| #include "libcflat.h" |
| #include "apic.h" |
| #include "asm/io.h" |
| |
| #define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */ |
| #define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */ |
| #define KBD_CCMD_RESET 0xFE /* CPU reset */ |
| |
| static inline void kbd_cmd(u8 val) |
| { |
| while (inb(0x64) & 2); |
| outb(val, 0x64); |
| } |
| |
| static inline u8 kbd_in(void) |
| { |
| kbd_cmd(KBD_CCMD_READ_OUTPORT); |
| while (inb(0x64) & 2); |
| return inb(0x60); |
| } |
| |
| static inline void kbd_out(u8 val) |
| { |
| kbd_cmd(KBD_CCMD_WRITE_OUTPORT); |
| while (inb(0x64) & 2); |
| outb(val, 0x60); |
| } |
| |
| static inline void rtc_out(u8 reg, u8 val) |
| { |
| outb(reg, 0x70); |
| outb(val, 0x71); |
| } |
| |
| extern char resume_start, resume_end; |
| |
| #define state (*(volatile int *)0x2000) |
| #define bad (*(volatile int *)0x2004) |
| #define resumed (*(volatile int *)0x2008) |
| |
| int main(int argc, char **argv) |
| { |
| volatile u16 *resume_vector_ptr = (u16 *)0x467L; |
| char *addr, *resume_vec = (void*)0x1000; |
| |
| /* resume execution by indirect jump via 40h:0067h */ |
| rtc_out(0x0f, 0x0a); |
| resume_vector_ptr[0] = ((u32)(ulong)resume_vec); |
| resume_vector_ptr[1] = 0; |
| |
| for (addr = &resume_start; addr < &resume_end; addr++) |
| *resume_vec++ = *addr; |
| |
| if (state != 0) { |
| /* |
| * Strictly speaking this is a firmware problem, but let's check |
| * for it as well... |
| */ |
| if (resumed != 1) { |
| printf("Uh, resume vector visited %d times?\n", resumed); |
| bad |= 2; |
| } |
| /* |
| * Port 92 bit 0 is cleared on system reset. On a soft reset it |
| * is left to 1. Use this to distinguish INIT from hard reset. |
| */ |
| if (resumed != 0 && (inb(0x92) & 1) == 0) { |
| printf("Uh, hard reset!\n"); |
| bad |= 1; |
| } |
| } |
| |
| resumed = 0; |
| |
| switch (state++) { |
| case 0: |
| printf("testing port 92 init... "); |
| outb(inb(0x92) & ~1, 0x92); |
| outb(inb(0x92) | 1, 0x92); |
| break; |
| |
| case 1: |
| printf("testing kbd controller reset... "); |
| kbd_cmd(KBD_CCMD_RESET); |
| break; |
| |
| case 2: |
| printf("testing kbd controller init... "); |
| kbd_out(kbd_in() & ~1); |
| break; |
| |
| case 3: |
| printf("testing 0xcf9h init... "); |
| outb(0, 0xcf9); |
| outb(4, 0xcf9); |
| break; |
| |
| case 4: |
| printf("testing init to BSP... "); |
| apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL |
| | APIC_DM_INIT, 0); |
| break; |
| |
| case 5: |
| exit(bad); |
| } |
| |
| /* The resume code will get us back to main. */ |
| asm("cli; hlt"); |
| __builtin_unreachable(); |
| } |
| |
| asm ( |
| ".global resume_start\n" |
| ".global resume_end\n" |
| ".code16\n" |
| "resume_start:\n" |
| "incb %cs:0x2008\n" // resumed++; |
| "mov $0x0f, %al\n" // rtc_out(0x0f, 0x00); |
| "out %al, $0x70\n" |
| "mov $0x00, %al\n" |
| "out %al, $0x71\n" |
| "jmp $0xffff, $0x0000\n" // BIOS reset |
| "resume_end:\n" |
| #ifdef __i386__ |
| ".code32\n" |
| #else |
| ".code64\n" |
| #endif |
| ); |