You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gateA20.c 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #include <stdio.h>
  2. #include <realmode.h>
  3. #include <bios.h>
  4. #include <gpxe/timer.h>
  5. #define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
  6. #define K_STATUS 0x64 /* keyboard status */
  7. #define K_CMD 0x64 /* keybd ctlr command (write-only) */
  8. #define K_OBUF_FUL 0x01 /* output buffer full */
  9. #define K_IBUF_FUL 0x02 /* input buffer full */
  10. #define KC_CMD_WIN 0xd0 /* read output port */
  11. #define KC_CMD_WOUT 0xd1 /* write output port */
  12. #define KC_CMD_NULL 0xff /* null command ("pulse nothing") */
  13. #define KB_SET_A20 0xdf /* enable A20,
  14. enable output buffer full interrupt
  15. enable data line
  16. disable clock line */
  17. #define KB_UNSET_A20 0xdd /* enable A20,
  18. enable output buffer full interrupt
  19. enable data line
  20. disable clock line */
  21. #define SCP_A 0x92 /* System Control Port A */
  22. enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402,
  23. Query_A20_Support = 0x2403 };
  24. enum a20_methods {
  25. A20_UNKNOWN = 0,
  26. A20_INT15,
  27. A20_KBC,
  28. A20_SCPA,
  29. };
  30. #define A20_MAX_RETRIES 32
  31. #define A20_INT15_RETRIES 32
  32. #define A20_KBC_RETRIES (2^21)
  33. #define A20_SCPA_RETRIES (2^21)
  34. /**
  35. * Drain keyboard controller
  36. */
  37. static void empty_8042 ( void ) {
  38. unsigned long time;
  39. time = currticks() + TICKS_PER_SEC; /* max wait of 1 second */
  40. while ( ( inb ( K_CMD ) & ( K_IBUF_FUL | K_OBUF_FUL ) ) &&
  41. currticks() < time ) {
  42. SLOW_DOWN_IO;
  43. ( void ) inb ( K_RDWR );
  44. SLOW_DOWN_IO;
  45. }
  46. }
  47. /**
  48. * Fast test to see if gate A20 is already set
  49. *
  50. * @v retries Number of times to retry before giving up
  51. * @ret set Gate A20 is set
  52. */
  53. static int gateA20_is_set ( int retries ) {
  54. static uint32_t test_pattern = 0xdeadbeef;
  55. physaddr_t test_pattern_phys = virt_to_phys ( &test_pattern );
  56. physaddr_t verify_pattern_phys = ( test_pattern_phys ^ 0x100000 );
  57. userptr_t verify_pattern_user = phys_to_user ( verify_pattern_phys );
  58. uint32_t verify_pattern;
  59. do {
  60. /* Check for difference */
  61. copy_from_user ( &verify_pattern, verify_pattern_user, 0,
  62. sizeof ( verify_pattern ) );
  63. if ( verify_pattern != test_pattern )
  64. return 1;
  65. /* Avoid false negatives */
  66. test_pattern++;
  67. SLOW_DOWN_IO;
  68. /* Always retry at least once, to avoid false negatives */
  69. } while ( retries-- >= 0 );
  70. /* Pattern matched every time; gate A20 is not set */
  71. return 0;
  72. }
  73. /*
  74. * Gate A20 for high memory
  75. *
  76. * Note that this function gets called as part of the return path from
  77. * librm's real_call, which is used to make the int15 call if librm is
  78. * being used. To avoid an infinite recursion, we make gateA20_set
  79. * return immediately if it is already part of the call stack.
  80. */
  81. void gateA20_set ( void ) {
  82. static char reentry_guard = 0;
  83. static int a20_method = A20_UNKNOWN;
  84. unsigned int discard_a;
  85. unsigned int scp_a;
  86. int retries = 0;
  87. /* Avoid potential infinite recursion */
  88. if ( reentry_guard )
  89. return;
  90. reentry_guard = 1;
  91. /* Fast check to see if gate A20 is already enabled */
  92. if ( gateA20_is_set ( 0 ) )
  93. goto out;
  94. for ( ; retries < A20_MAX_RETRIES ; retries++ ) {
  95. switch ( a20_method ) {
  96. case A20_UNKNOWN:
  97. case A20_INT15:
  98. /* Try INT 15 method */
  99. __asm__ __volatile__ ( REAL_CODE ( "int $0x15" )
  100. : "=a" ( discard_a )
  101. : "a" ( Enable_A20 ) );
  102. if ( gateA20_is_set ( A20_INT15_RETRIES ) ) {
  103. DBG ( "Enabled gate A20 using BIOS\n" );
  104. a20_method = A20_INT15;
  105. goto out;
  106. }
  107. /* fall through */
  108. case A20_KBC:
  109. /* Try keyboard controller method */
  110. empty_8042();
  111. outb ( KC_CMD_WOUT, K_CMD );
  112. empty_8042();
  113. outb ( KB_SET_A20, K_RDWR );
  114. empty_8042();
  115. outb ( KC_CMD_NULL, K_CMD );
  116. empty_8042();
  117. if ( gateA20_is_set ( A20_KBC_RETRIES ) ) {
  118. DBG ( "Enabled gate A20 using "
  119. "keyboard controller\n" );
  120. a20_method = A20_KBC;
  121. goto out;
  122. }
  123. /* fall through */
  124. case A20_SCPA:
  125. /* Try "Fast gate A20" method */
  126. scp_a = inb ( SCP_A );
  127. scp_a &= ~0x01; /* Avoid triggering a reset */
  128. scp_a |= 0x02; /* Enable A20 */
  129. SLOW_DOWN_IO;
  130. outb ( scp_a, SCP_A );
  131. SLOW_DOWN_IO;
  132. if ( gateA20_is_set ( A20_SCPA_RETRIES ) ) {
  133. DBG ( "Enabled gate A20 using "
  134. "Fast Gate A20\n" );
  135. a20_method = A20_SCPA;
  136. goto out;
  137. }
  138. }
  139. }
  140. /* Better to die now than corrupt memory later */
  141. printf ( "FATAL: Gate A20 stuck\n" );
  142. while ( 1 ) {}
  143. out:
  144. if ( retries )
  145. DBG ( "%d attempts were required to enable A20\n",
  146. ( retries + 1 ) );
  147. reentry_guard = 0;
  148. }
  149. void gateA20_unset ( void ) {
  150. /* Not currently implemented */
  151. }