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