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.5KB

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