1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2008-2011
4 * Graeme Russ, <graeme.russ@gmail.com>
5 *
6 * (C) Copyright 2002
7 * Daniel Engstr��m, Omicron Ceti AB, <daniel@omicron.se>
8 *
9 * Portions of this file are derived from the Linux kernel source
10 *  Copyright (C) 1991, 1992  Linus Torvalds
11 */
12
13#include <common.h>
14#include <dm.h>
15#include <efi_loader.h>
16#include <hang.h>
17#include <init.h>
18#include <irq.h>
19#include <irq_func.h>
20#include <asm/control_regs.h>
21#include <asm/global_data.h>
22#include <asm/i8259.h>
23#include <asm/interrupt.h>
24#include <asm/io.h>
25#include <asm/lapic.h>
26#include <asm/processor-flags.h>
27#include <asm/ptrace.h>
28
29DECLARE_GLOBAL_DATA_PTR;
30
31#define DECLARE_INTERRUPT(x) \
32	".globl irq_"#x"\n" \
33	".hidden irq_"#x"\n" \
34	".type irq_"#x", @function\n" \
35	"irq_"#x":\n" \
36	"pushl $"#x"\n" \
37	"jmp.d32 irq_common_entry\n"
38
39static char *exceptions[] = {
40	"Divide Error",
41	"Debug",
42	"NMI Interrupt",
43	"Breakpoint",
44	"Overflow",
45	"BOUND Range Exceeded",
46	"Invalid Opcode (Undefined Opcode)",
47	"Device Not Available (No Math Coprocessor)",
48	"Double Fault",
49	"Coprocessor Segment Overrun",
50	"Invalid TSS",
51	"Segment Not Present",
52	"Stack Segment Fault",
53	"General Protection",
54	"Page Fault",
55	"Reserved",
56	"x87 FPU Floating-Point Error",
57	"Alignment Check",
58	"Machine Check",
59	"SIMD Floating-Point Exception",
60	"Virtualization Exception",
61	"Reserved",
62	"Reserved",
63	"Reserved",
64	"Reserved",
65	"Reserved",
66	"Reserved",
67	"Reserved",
68	"Reserved",
69	"Reserved",
70	"Reserved",
71	"Reserved"
72};
73
74/**
75 * show_efi_loaded_images() - show loaded UEFI images
76 *
77 * List all loaded UEFI images.
78 *
79 * @eip:	instruction pointer
80 */
81static void show_efi_loaded_images(uintptr_t eip)
82{
83	efi_print_image_infos((void *)eip);
84}
85
86static void dump_regs(struct irq_regs *regs)
87{
88	unsigned long cs, eip, eflags;
89	unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L;
90	unsigned long d0, d1, d2, d3, d6, d7;
91	unsigned long sp;
92
93	/*
94	 * Some exceptions cause an error code to be saved on the current stack
95	 * after the EIP value. We should extract CS/EIP/EFLAGS from different
96	 * position on the stack based on the exception number.
97	 */
98	switch (regs->irq_id) {
99	case EXC_DF:
100	case EXC_TS:
101	case EXC_NP:
102	case EXC_SS:
103	case EXC_GP:
104	case EXC_PF:
105	case EXC_AC:
106		cs = regs->context.ctx2.xcs;
107		eip = regs->context.ctx2.eip;
108		eflags = regs->context.ctx2.eflags;
109		/* We should fix up the ESP due to error code */
110		regs->esp += 4;
111		break;
112	default:
113		cs = regs->context.ctx1.xcs;
114		eip = regs->context.ctx1.eip;
115		eflags = regs->context.ctx1.eflags;
116		break;
117	}
118
119	printf("EIP: %04x:[<%08lx>] EFLAGS: %08lx\n",
120			(u16)cs, eip, eflags);
121	if (gd->flags & GD_FLG_RELOC)
122		printf("Original EIP :[<%08lx>]\n", eip - gd->reloc_off);
123
124	printf("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
125		regs->eax, regs->ebx, regs->ecx, regs->edx);
126	printf("ESI: %08lx EDI: %08lx EBP: %08lx ESP: %08lx\n",
127		regs->esi, regs->edi, regs->ebp, regs->esp);
128	printf(" DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x\n",
129	       (u16)regs->xds, (u16)regs->xes, (u16)regs->xfs,
130	       (u16)regs->xgs, (u16)regs->xss);
131
132	cr0 = read_cr0();
133	cr2 = read_cr2();
134	cr3 = read_cr3();
135	cr4 = read_cr4();
136
137	printf("CR0: %08lx CR2: %08lx CR3: %08lx CR4: %08lx\n",
138			cr0, cr2, cr3, cr4);
139
140	d0 = get_debugreg(0);
141	d1 = get_debugreg(1);
142	d2 = get_debugreg(2);
143	d3 = get_debugreg(3);
144
145	printf("DR0: %08lx DR1: %08lx DR2: %08lx DR3: %08lx\n",
146			d0, d1, d2, d3);
147
148	d6 = get_debugreg(6);
149	d7 = get_debugreg(7);
150	printf("DR6: %08lx DR7: %08lx\n",
151			d6, d7);
152
153	printf("Stack:\n");
154	sp = regs->esp;
155
156	sp += 64;
157
158	while (sp > (regs->esp - 16)) {
159		if (sp == regs->esp)
160			printf("--->");
161		else
162			printf("    ");
163		printf("0x%8.8lx : 0x%8.8lx\n", sp, (ulong)readl(sp));
164		sp -= 4;
165	}
166	show_efi_loaded_images(eip);
167}
168
169static void do_exception(struct irq_regs *regs)
170{
171	printf("%s\n", exceptions[regs->irq_id]);
172	dump_regs(regs);
173	hang();
174}
175
176struct idt_entry {
177	u16	base_low;
178	u16	selector;
179	u8	res;
180	u8	access;
181	u16	base_high;
182} __packed;
183
184struct idt_entry idt[256] __aligned(16);
185
186struct idt_ptr idt_ptr;
187
188static inline void load_idt(const struct idt_ptr *dtr)
189{
190	asm volatile("cs lidt %0" : : "m" (*dtr));
191}
192
193void set_vector(u8 intnum, void *routine)
194{
195	idt[intnum].base_high = (u16)((ulong)(routine) >> 16);
196	idt[intnum].base_low = (u16)((ulong)(routine) & 0xffff);
197}
198
199/*
200 * Ideally these would be defined static to avoid a checkpatch warning, but
201 * the compiler cannot see them in the inline asm and complains that they
202 * aren't defined
203 */
204void irq_0(void);
205void irq_1(void);
206
207int cpu_init_interrupts(void)
208{
209	int i;
210
211	int irq_entry_size = irq_1 - irq_0;
212	void *irq_entry = (void *)irq_0;
213
214	/* Setup the IDT */
215	for (i = 0; i < 256; i++) {
216		idt[i].access = 0x8e;
217		idt[i].res = 0;
218		idt[i].selector = X86_GDT_ENTRY_32BIT_CS * X86_GDT_ENTRY_SIZE;
219		set_vector(i, irq_entry);
220		irq_entry += irq_entry_size;
221	}
222
223	idt_ptr.size = 256 * 8 - 1;
224	idt_ptr.address = (unsigned long) idt;
225
226	load_idt(&idt_ptr);
227
228	return 0;
229}
230
231void interrupt_read_idt(struct idt_ptr *ptr)
232{
233	asm volatile("sidt %0" : : "m" (*ptr));
234}
235
236void *x86_get_idt(void)
237{
238	return &idt_ptr;
239}
240
241void __do_irq(int irq)
242{
243	printf("Unhandled IRQ : %d\n", irq);
244}
245void do_irq(int irq) __attribute__((weak, alias("__do_irq")));
246
247void enable_interrupts(void)
248{
249	asm("sti\n");
250}
251
252int disable_interrupts(void)
253{
254	long flags;
255
256#if CONFIG_IS_ENABLED(X86_64)
257	asm volatile ("pushfq ; popq %0 ; cli\n" : "=g" (flags) : );
258#else
259	asm volatile ("pushfl ; popl %0 ; cli\n" : "=g" (flags) : );
260#endif
261	return flags & X86_EFLAGS_IF;
262}
263
264int interrupt_init(void)
265{
266	struct udevice *dev;
267	int ret;
268
269	/*
270	 * When running as an EFI application we are not in control of
271	 * interrupts and should leave them alone.
272	 */
273	if (!ll_boot_init())
274		return 0;
275
276	/* Try to set up the interrupt router, but don't require one */
277	ret = irq_first_device_type(X86_IRQT_BASE, &dev);
278	if (ret && ret != -ENODEV)
279		return ret;
280
281	/* Just in case... */
282	disable_interrupts();
283
284#ifdef CONFIG_I8259_PIC
285	/* Initialize the master/slave i8259 pic */
286	i8259_init();
287#endif
288
289#ifdef CONFIG_APIC
290	lapic_setup();
291#endif
292
293	/* Initialize core interrupt and exception functionality of CPU */
294	cpu_init_interrupts();
295
296	/* It is now safe to enable interrupts */
297	enable_interrupts();
298
299	return 0;
300}
301
302/* IRQ Low-Level Service Routine */
303void irq_llsr(struct irq_regs *regs)
304{
305	/*
306	 * For detailed description of each exception, refer to:
307	 * Intel�� 64 and IA-32 Architectures Software Developer's Manual
308	 * Volume 1: Basic Architecture
309	 * Order Number: 253665-029US, November 2008
310	 * Table 6-1. Exceptions and Interrupts
311	 */
312	if (regs->irq_id < 32) {
313		/* Architecture defined exception */
314		do_exception(regs);
315	} else {
316		/* Hardware or User IRQ */
317		do_irq(regs->irq_id);
318	}
319}
320
321/*
322 * OK - This looks really horrible, but it serves a purpose - It helps create
323 * fully relocatable code.
324 *  - The call to irq_llsr will be a relative jump
325 *  - The IRQ entries will be guaranteed to be in order
326 *  Interrupt entries are now very small (a push and a jump) but they are
327 *  now slower (all registers pushed on stack which provides complete
328 *  crash dumps in the low level handlers
329 *
330 * Interrupt Entry Point:
331 *  - Interrupt has caused eflags, CS and EIP to be pushed
332 *  - Interrupt Vector Handler has pushed orig_eax
333 *  - pt_regs.esp needs to be adjusted by 40 bytes:
334 *      12 bytes pushed by CPU (EFLAGSF, CS, EIP)
335 *      4 bytes pushed by vector handler (irq_id)
336 *      24 bytes pushed before SP (SS, GS, FS, ES, DS, EAX)
337 *      NOTE: Only longs are pushed on/popped off the stack!
338 */
339asm(".globl irq_common_entry\n" \
340	".hidden irq_common_entry\n" \
341	".type irq_common_entry, @function\n" \
342	"irq_common_entry:\n" \
343	"cld\n" \
344	"pushl %ss\n" \
345	"pushl %gs\n" \
346	"pushl %fs\n" \
347	"pushl %es\n" \
348	"pushl %ds\n" \
349	"pushl %eax\n" \
350	"movl  %esp, %eax\n" \
351	"addl  $40, %eax\n" \
352	"pushl %eax\n" \
353	"pushl %ebp\n" \
354	"pushl %edi\n" \
355	"pushl %esi\n" \
356	"pushl %edx\n" \
357	"pushl %ecx\n" \
358	"pushl %ebx\n" \
359	"mov   %esp, %eax\n" \
360	"call irq_llsr\n" \
361	"popl %ebx\n" \
362	"popl %ecx\n" \
363	"popl %edx\n" \
364	"popl %esi\n" \
365	"popl %edi\n" \
366	"popl %ebp\n" \
367	"popl %eax\n" \
368	"popl %eax\n" \
369	"popl %ds\n" \
370	"popl %es\n" \
371	"popl %fs\n" \
372	"popl %gs\n" \
373	"popl %ss\n" \
374	"add  $4, %esp\n" \
375	"iret\n" \
376	DECLARE_INTERRUPT(0) \
377	DECLARE_INTERRUPT(1) \
378	DECLARE_INTERRUPT(2) \
379	DECLARE_INTERRUPT(3) \
380	DECLARE_INTERRUPT(4) \
381	DECLARE_INTERRUPT(5) \
382	DECLARE_INTERRUPT(6) \
383	DECLARE_INTERRUPT(7) \
384	DECLARE_INTERRUPT(8) \
385	DECLARE_INTERRUPT(9) \
386	DECLARE_INTERRUPT(10) \
387	DECLARE_INTERRUPT(11) \
388	DECLARE_INTERRUPT(12) \
389	DECLARE_INTERRUPT(13) \
390	DECLARE_INTERRUPT(14) \
391	DECLARE_INTERRUPT(15) \
392	DECLARE_INTERRUPT(16) \
393	DECLARE_INTERRUPT(17) \
394	DECLARE_INTERRUPT(18) \
395	DECLARE_INTERRUPT(19) \
396	DECLARE_INTERRUPT(20) \
397	DECLARE_INTERRUPT(21) \
398	DECLARE_INTERRUPT(22) \
399	DECLARE_INTERRUPT(23) \
400	DECLARE_INTERRUPT(24) \
401	DECLARE_INTERRUPT(25) \
402	DECLARE_INTERRUPT(26) \
403	DECLARE_INTERRUPT(27) \
404	DECLARE_INTERRUPT(28) \
405	DECLARE_INTERRUPT(29) \
406	DECLARE_INTERRUPT(30) \
407	DECLARE_INTERRUPT(31) \
408	DECLARE_INTERRUPT(32) \
409	DECLARE_INTERRUPT(33) \
410	DECLARE_INTERRUPT(34) \
411	DECLARE_INTERRUPT(35) \
412	DECLARE_INTERRUPT(36) \
413	DECLARE_INTERRUPT(37) \
414	DECLARE_INTERRUPT(38) \
415	DECLARE_INTERRUPT(39) \
416	DECLARE_INTERRUPT(40) \
417	DECLARE_INTERRUPT(41) \
418	DECLARE_INTERRUPT(42) \
419	DECLARE_INTERRUPT(43) \
420	DECLARE_INTERRUPT(44) \
421	DECLARE_INTERRUPT(45) \
422	DECLARE_INTERRUPT(46) \
423	DECLARE_INTERRUPT(47) \
424	DECLARE_INTERRUPT(48) \
425	DECLARE_INTERRUPT(49) \
426	DECLARE_INTERRUPT(50) \
427	DECLARE_INTERRUPT(51) \
428	DECLARE_INTERRUPT(52) \
429	DECLARE_INTERRUPT(53) \
430	DECLARE_INTERRUPT(54) \
431	DECLARE_INTERRUPT(55) \
432	DECLARE_INTERRUPT(56) \
433	DECLARE_INTERRUPT(57) \
434	DECLARE_INTERRUPT(58) \
435	DECLARE_INTERRUPT(59) \
436	DECLARE_INTERRUPT(60) \
437	DECLARE_INTERRUPT(61) \
438	DECLARE_INTERRUPT(62) \
439	DECLARE_INTERRUPT(63) \
440	DECLARE_INTERRUPT(64) \
441	DECLARE_INTERRUPT(65) \
442	DECLARE_INTERRUPT(66) \
443	DECLARE_INTERRUPT(67) \
444	DECLARE_INTERRUPT(68) \
445	DECLARE_INTERRUPT(69) \
446	DECLARE_INTERRUPT(70) \
447	DECLARE_INTERRUPT(71) \
448	DECLARE_INTERRUPT(72) \
449	DECLARE_INTERRUPT(73) \
450	DECLARE_INTERRUPT(74) \
451	DECLARE_INTERRUPT(75) \
452	DECLARE_INTERRUPT(76) \
453	DECLARE_INTERRUPT(77) \
454	DECLARE_INTERRUPT(78) \
455	DECLARE_INTERRUPT(79) \
456	DECLARE_INTERRUPT(80) \
457	DECLARE_INTERRUPT(81) \
458	DECLARE_INTERRUPT(82) \
459	DECLARE_INTERRUPT(83) \
460	DECLARE_INTERRUPT(84) \
461	DECLARE_INTERRUPT(85) \
462	DECLARE_INTERRUPT(86) \
463	DECLARE_INTERRUPT(87) \
464	DECLARE_INTERRUPT(88) \
465	DECLARE_INTERRUPT(89) \
466	DECLARE_INTERRUPT(90) \
467	DECLARE_INTERRUPT(91) \
468	DECLARE_INTERRUPT(92) \
469	DECLARE_INTERRUPT(93) \
470	DECLARE_INTERRUPT(94) \
471	DECLARE_INTERRUPT(95) \
472	DECLARE_INTERRUPT(97) \
473	DECLARE_INTERRUPT(96) \
474	DECLARE_INTERRUPT(98) \
475	DECLARE_INTERRUPT(99) \
476	DECLARE_INTERRUPT(100) \
477	DECLARE_INTERRUPT(101) \
478	DECLARE_INTERRUPT(102) \
479	DECLARE_INTERRUPT(103) \
480	DECLARE_INTERRUPT(104) \
481	DECLARE_INTERRUPT(105) \
482	DECLARE_INTERRUPT(106) \
483	DECLARE_INTERRUPT(107) \
484	DECLARE_INTERRUPT(108) \
485	DECLARE_INTERRUPT(109) \
486	DECLARE_INTERRUPT(110) \
487	DECLARE_INTERRUPT(111) \
488	DECLARE_INTERRUPT(112) \
489	DECLARE_INTERRUPT(113) \
490	DECLARE_INTERRUPT(114) \
491	DECLARE_INTERRUPT(115) \
492	DECLARE_INTERRUPT(116) \
493	DECLARE_INTERRUPT(117) \
494	DECLARE_INTERRUPT(118) \
495	DECLARE_INTERRUPT(119) \
496	DECLARE_INTERRUPT(120) \
497	DECLARE_INTERRUPT(121) \
498	DECLARE_INTERRUPT(122) \
499	DECLARE_INTERRUPT(123) \
500	DECLARE_INTERRUPT(124) \
501	DECLARE_INTERRUPT(125) \
502	DECLARE_INTERRUPT(126) \
503	DECLARE_INTERRUPT(127) \
504	DECLARE_INTERRUPT(128) \
505	DECLARE_INTERRUPT(129) \
506	DECLARE_INTERRUPT(130) \
507	DECLARE_INTERRUPT(131) \
508	DECLARE_INTERRUPT(132) \
509	DECLARE_INTERRUPT(133) \
510	DECLARE_INTERRUPT(134) \
511	DECLARE_INTERRUPT(135) \
512	DECLARE_INTERRUPT(136) \
513	DECLARE_INTERRUPT(137) \
514	DECLARE_INTERRUPT(138) \
515	DECLARE_INTERRUPT(139) \
516	DECLARE_INTERRUPT(140) \
517	DECLARE_INTERRUPT(141) \
518	DECLARE_INTERRUPT(142) \
519	DECLARE_INTERRUPT(143) \
520	DECLARE_INTERRUPT(144) \
521	DECLARE_INTERRUPT(145) \
522	DECLARE_INTERRUPT(146) \
523	DECLARE_INTERRUPT(147) \
524	DECLARE_INTERRUPT(148) \
525	DECLARE_INTERRUPT(149) \
526	DECLARE_INTERRUPT(150) \
527	DECLARE_INTERRUPT(151) \
528	DECLARE_INTERRUPT(152) \
529	DECLARE_INTERRUPT(153) \
530	DECLARE_INTERRUPT(154) \
531	DECLARE_INTERRUPT(155) \
532	DECLARE_INTERRUPT(156) \
533	DECLARE_INTERRUPT(157) \
534	DECLARE_INTERRUPT(158) \
535	DECLARE_INTERRUPT(159) \
536	DECLARE_INTERRUPT(160) \
537	DECLARE_INTERRUPT(161) \
538	DECLARE_INTERRUPT(162) \
539	DECLARE_INTERRUPT(163) \
540	DECLARE_INTERRUPT(164) \
541	DECLARE_INTERRUPT(165) \
542	DECLARE_INTERRUPT(166) \
543	DECLARE_INTERRUPT(167) \
544	DECLARE_INTERRUPT(168) \
545	DECLARE_INTERRUPT(169) \
546	DECLARE_INTERRUPT(170) \
547	DECLARE_INTERRUPT(171) \
548	DECLARE_INTERRUPT(172) \
549	DECLARE_INTERRUPT(173) \
550	DECLARE_INTERRUPT(174) \
551	DECLARE_INTERRUPT(175) \
552	DECLARE_INTERRUPT(176) \
553	DECLARE_INTERRUPT(177) \
554	DECLARE_INTERRUPT(178) \
555	DECLARE_INTERRUPT(179) \
556	DECLARE_INTERRUPT(180) \
557	DECLARE_INTERRUPT(181) \
558	DECLARE_INTERRUPT(182) \
559	DECLARE_INTERRUPT(183) \
560	DECLARE_INTERRUPT(184) \
561	DECLARE_INTERRUPT(185) \
562	DECLARE_INTERRUPT(186) \
563	DECLARE_INTERRUPT(187) \
564	DECLARE_INTERRUPT(188) \
565	DECLARE_INTERRUPT(189) \
566	DECLARE_INTERRUPT(190) \
567	DECLARE_INTERRUPT(191) \
568	DECLARE_INTERRUPT(192) \
569	DECLARE_INTERRUPT(193) \
570	DECLARE_INTERRUPT(194) \
571	DECLARE_INTERRUPT(195) \
572	DECLARE_INTERRUPT(196) \
573	DECLARE_INTERRUPT(197) \
574	DECLARE_INTERRUPT(198) \
575	DECLARE_INTERRUPT(199) \
576	DECLARE_INTERRUPT(200) \
577	DECLARE_INTERRUPT(201) \
578	DECLARE_INTERRUPT(202) \
579	DECLARE_INTERRUPT(203) \
580	DECLARE_INTERRUPT(204) \
581	DECLARE_INTERRUPT(205) \
582	DECLARE_INTERRUPT(206) \
583	DECLARE_INTERRUPT(207) \
584	DECLARE_INTERRUPT(208) \
585	DECLARE_INTERRUPT(209) \
586	DECLARE_INTERRUPT(210) \
587	DECLARE_INTERRUPT(211) \
588	DECLARE_INTERRUPT(212) \
589	DECLARE_INTERRUPT(213) \
590	DECLARE_INTERRUPT(214) \
591	DECLARE_INTERRUPT(215) \
592	DECLARE_INTERRUPT(216) \
593	DECLARE_INTERRUPT(217) \
594	DECLARE_INTERRUPT(218) \
595	DECLARE_INTERRUPT(219) \
596	DECLARE_INTERRUPT(220) \
597	DECLARE_INTERRUPT(221) \
598	DECLARE_INTERRUPT(222) \
599	DECLARE_INTERRUPT(223) \
600	DECLARE_INTERRUPT(224) \
601	DECLARE_INTERRUPT(225) \
602	DECLARE_INTERRUPT(226) \
603	DECLARE_INTERRUPT(227) \
604	DECLARE_INTERRUPT(228) \
605	DECLARE_INTERRUPT(229) \
606	DECLARE_INTERRUPT(230) \
607	DECLARE_INTERRUPT(231) \
608	DECLARE_INTERRUPT(232) \
609	DECLARE_INTERRUPT(233) \
610	DECLARE_INTERRUPT(234) \
611	DECLARE_INTERRUPT(235) \
612	DECLARE_INTERRUPT(236) \
613	DECLARE_INTERRUPT(237) \
614	DECLARE_INTERRUPT(238) \
615	DECLARE_INTERRUPT(239) \
616	DECLARE_INTERRUPT(240) \
617	DECLARE_INTERRUPT(241) \
618	DECLARE_INTERRUPT(242) \
619	DECLARE_INTERRUPT(243) \
620	DECLARE_INTERRUPT(244) \
621	DECLARE_INTERRUPT(245) \
622	DECLARE_INTERRUPT(246) \
623	DECLARE_INTERRUPT(247) \
624	DECLARE_INTERRUPT(248) \
625	DECLARE_INTERRUPT(249) \
626	DECLARE_INTERRUPT(250) \
627	DECLARE_INTERRUPT(251) \
628	DECLARE_INTERRUPT(252) \
629	DECLARE_INTERRUPT(253) \
630	DECLARE_INTERRUPT(254) \
631	DECLARE_INTERRUPT(255));
632