xref: /kvm-unit-tests/x86/init.c (revision 55601383cca6221889c5641e4bf6cfdbb855b213)
1 #include "libcflat.h"
2 #include "apic.h"
3 #include "asm/io.h"
4 
5 #define KBD_CCMD_READ_OUTPORT   0xD0    /* read output port */
6 #define KBD_CCMD_WRITE_OUTPORT  0xD1    /* write output port */
7 #define KBD_CCMD_RESET          0xFE    /* CPU reset */
8 
kbd_cmd(u8 val)9 static inline void kbd_cmd(u8 val)
10 {
11     while (inb(0x64) & 2);
12     outb(val, 0x64);
13 }
14 
kbd_in(void)15 static inline u8 kbd_in(void)
16 {
17     kbd_cmd(KBD_CCMD_READ_OUTPORT);
18     while (inb(0x64) & 2);
19     return inb(0x60);
20 }
21 
kbd_out(u8 val)22 static inline void kbd_out(u8 val)
23 {
24     kbd_cmd(KBD_CCMD_WRITE_OUTPORT);
25     while (inb(0x64) & 2);
26     outb(val, 0x60);
27 }
28 
rtc_out(u8 reg,u8 val)29 static inline void rtc_out(u8 reg, u8 val)
30 {
31     outb(reg, 0x70);
32     outb(val, 0x71);
33 }
34 
35 extern char resume_start, resume_end;
36 
37 #define state (*(volatile int *)0x2000)
38 #define bad (*(volatile int *)0x2004)
39 #define resumed (*(volatile int *)0x2008)
40 
main(int argc,char ** argv)41 int main(int argc, char **argv)
42 {
43 	volatile u16 *resume_vector_ptr = (u16 *)0x467L;
44 	char *addr, *resume_vec = (void*)0x1000;
45 
46 	/* resume execution by indirect jump via 40h:0067h */
47 	rtc_out(0x0f, 0x0a);
48 	resume_vector_ptr[0] = ((u32)(ulong)resume_vec);
49 	resume_vector_ptr[1] = 0;
50 
51 	for (addr = &resume_start; addr < &resume_end; addr++)
52 		*resume_vec++ = *addr;
53 
54 	if (state != 0) {
55 		/*
56 		 * Strictly speaking this is a firmware problem, but let's check
57 		 * for it as well...
58 		 */
59 		if (resumed != 1) {
60 			printf("Uh, resume vector visited %d times?\n", resumed);
61 			bad |= 2;
62 		}
63 		/*
64 		 * Port 92 bit 0 is cleared on system reset.  On a soft reset it
65 		 * is left to 1.  Use this to distinguish INIT from hard reset.
66 		 */
67 		if (resumed != 0 && (inb(0x92) & 1) == 0) {
68 			printf("Uh, hard reset!\n");
69 			bad |= 1;
70 		}
71 	}
72 
73 	resumed = 0;
74 
75 	switch (state++) {
76 	case 0:
77 		printf("testing port 92 init... ");
78 		outb(inb(0x92) & ~1, 0x92);
79 		outb(inb(0x92) | 1, 0x92);
80 		break;
81 
82 	case 1:
83 		printf("testing kbd controller reset... ");
84 		kbd_cmd(KBD_CCMD_RESET);
85 		break;
86 
87 	case 2:
88 		printf("testing kbd controller init... ");
89 		kbd_out(kbd_in() & ~1);
90 		break;
91 
92 	case 3:
93 		printf("testing 0xcf9h init... ");
94 		outb(0, 0xcf9);
95 		outb(4, 0xcf9);
96 		break;
97 
98 	case 4:
99 		printf("testing init to BSP... ");
100 		apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL
101 			      | APIC_DM_INIT, 0);
102 		break;
103 
104 	case 5:
105 		exit(bad);
106 	}
107 
108 	/* The resume code will get us back to main.  */
109 	asm("cli; hlt");
110 	__builtin_unreachable();
111 }
112 
113 asm (
114 	".global resume_start\n"
115 	".global resume_end\n"
116 	".code16\n"
117 	"resume_start:\n"
118 	"incb %cs:0x2008\n"		// resumed++;
119 	"mov $0x0f, %al\n"		// rtc_out(0x0f, 0x00);
120 	"out %al, $0x70\n"
121 	"mov $0x00, %al\n"
122 	"out %al, $0x71\n"
123 	"jmp $0xffff, $0x0000\n"	// BIOS reset
124 	"resume_end:\n"
125 #ifdef __i386__
126 	".code32\n"
127 #else
128 	".code64\n"
129 #endif
130     );
131