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.

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