Merged mcb30-realmode-redesign back to HEAD
[gpxe.git] / src / arch / i386 / firmware / pcbios / gateA20.c
1 #include "realmode.h"
2 #include "timer.h"
3 #include "latch.h"
4 #include "bios.h"
5
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
10 #define K_OBUF_FUL      0x01            /* output buffer full */
11 #define K_IBUF_FUL      0x02            /* input buffer full */
12
13 #define KC_CMD_WIN      0xd0            /* read  output port */
14 #define KC_CMD_WOUT     0xd1            /* write output port */
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
24 enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402,
25         Query_A20_Support = 0x2403 };
26
27 #define CF ( 1 << 0 )
28
29 #ifndef IBM_L40
30 static void empty_8042 ( void )
31 {
32         unsigned long time;
33         char st;
34
35         time = currticks() + TICKS_PER_SEC;     /* max wait of 1 second */
36         while ((((st = inb(K_CMD)) & K_OBUF_FUL) ||
37                (st & K_IBUF_FUL)) &&
38                currticks() < time)
39                 inb(K_RDWR);
40 }
41 #endif  /* IBM_L40 */
42
43 /*
44  * Gate A20 for high memory
45  *
46  * Note that this function gets called as part of the return path from
47  * librm's real_call, which is used to make the int15 call if librm is
48  * being used.  To avoid an infinite recursion, we make gateA20_set
49  * return immediately if it is already part of the call stack.
50  */
51 void gateA20_set ( void ) {
52         static char reentry_guard = 0;
53         uint16_t flags, status;
54
55         if ( reentry_guard )
56                 return;
57         reentry_guard = 1;
58
59         REAL_EXEC ( rm_enableA20,
60                     "sti\n\t"
61                     "stc\n\t"
62                     "int $0x15\n\t"
63                     "pushfw\n\t"
64                     "popw %%bx\n\t"
65                     "cli\n\t",
66                     2,
67                     OUT_CONSTRAINTS ( "=a" ( status ), "=b" ( flags ) ),
68                     IN_CONSTRAINTS ( "a" ( Enable_A20 ) ),
69                     CLOBBER ( "ecx", "edx", "ebp", "esi", "edi" ) );
70
71         if ( ( flags & CF ) ||
72              ( ( status >> 8 ) & 0xff ) ) {
73                 /* INT 15 method failed, try alternatives */
74 #ifdef  IBM_L40
75                 outb(0x2, 0x92);
76 #else   /* IBM_L40 */
77                 empty_8042();
78                 outb(KC_CMD_WOUT, K_CMD);
79                 empty_8042();
80                 outb(KB_SET_A20, K_RDWR);
81                 empty_8042();
82 #endif  /* IBM_L40 */
83         }
84         
85         reentry_guard = 0;
86 }
87
88 void gateA20_unset ( void ) {
89         /* Not currently implemented */
90 }