Updated to REAL_CODE()
[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         unsigned int discard_a;
54         unsigned int flags;
55
56         if ( reentry_guard )
57                 return;
58         reentry_guard = 1;
59
60         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
61                                            "stc\n\t"
62                                            "int $0x15\n\t"
63                                            "pushfw\n\t"
64                                            "popw %w0\n\t"
65                                            "cli\n\t" )
66                                : "=r" ( flags ), "=a" ( discard_a )
67                                : "a" ( Enable_A20 ) );
68
69         if ( flags & CF ) {
70                 /* INT 15 method failed, try alternatives */
71 #ifdef  IBM_L40
72                 outb(0x2, 0x92);
73 #else   /* IBM_L40 */
74                 empty_8042();
75                 outb(KC_CMD_WOUT, K_CMD);
76                 empty_8042();
77                 outb(KB_SET_A20, K_RDWR);
78                 empty_8042();
79 #endif  /* IBM_L40 */
80         }
81         
82         reentry_guard = 0;
83 }
84
85 void gateA20_unset ( void ) {
86         /* Not currently implemented */
87 }