init.c 2.6 KB

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