Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* -*- linux-c -*- ------------------------------------------------------- *
0003  *
0004  *   Copyright (C) 1991, 1992 Linus Torvalds
0005  *   Copyright 2007-2008 rPath, Inc. - All Rights Reserved
0006  *   Copyright 2009 Intel Corporation; author H. Peter Anvin
0007  *
0008  * ----------------------------------------------------------------------- */
0009 
0010 /*
0011  * Enable A20 gate (return -1 on failure)
0012  */
0013 
0014 #include "boot.h"
0015 
0016 #define MAX_8042_LOOPS  100000
0017 #define MAX_8042_FF 32
0018 
0019 static int empty_8042(void)
0020 {
0021     u8 status;
0022     int loops = MAX_8042_LOOPS;
0023     int ffs   = MAX_8042_FF;
0024 
0025     while (loops--) {
0026         io_delay();
0027 
0028         status = inb(0x64);
0029         if (status == 0xff) {
0030             /* FF is a plausible, but very unlikely status */
0031             if (!--ffs)
0032                 return -1; /* Assume no KBC present */
0033         }
0034         if (status & 1) {
0035             /* Read and discard input data */
0036             io_delay();
0037             (void)inb(0x60);
0038         } else if (!(status & 2)) {
0039             /* Buffers empty, finished! */
0040             return 0;
0041         }
0042     }
0043 
0044     return -1;
0045 }
0046 
0047 /* Returns nonzero if the A20 line is enabled.  The memory address
0048    used as a test is the int $0x80 vector, which should be safe. */
0049 
0050 #define A20_TEST_ADDR   (4*0x80)
0051 #define A20_TEST_SHORT  32
0052 #define A20_TEST_LONG   2097152 /* 2^21 */
0053 
0054 static int a20_test(int loops)
0055 {
0056     int ok = 0;
0057     int saved, ctr;
0058 
0059     set_fs(0x0000);
0060     set_gs(0xffff);
0061 
0062     saved = ctr = rdfs32(A20_TEST_ADDR);
0063 
0064     while (loops--) {
0065         wrfs32(++ctr, A20_TEST_ADDR);
0066         io_delay(); /* Serialize and make delay constant */
0067         ok = rdgs32(A20_TEST_ADDR+0x10) ^ ctr;
0068         if (ok)
0069             break;
0070     }
0071 
0072     wrfs32(saved, A20_TEST_ADDR);
0073     return ok;
0074 }
0075 
0076 /* Quick test to see if A20 is already enabled */
0077 static int a20_test_short(void)
0078 {
0079     return a20_test(A20_TEST_SHORT);
0080 }
0081 
0082 /* Longer test that actually waits for A20 to come on line; this
0083    is useful when dealing with the KBC or other slow external circuitry. */
0084 static int a20_test_long(void)
0085 {
0086     return a20_test(A20_TEST_LONG);
0087 }
0088 
0089 static void enable_a20_bios(void)
0090 {
0091     struct biosregs ireg;
0092 
0093     initregs(&ireg);
0094     ireg.ax = 0x2401;
0095     intcall(0x15, &ireg, NULL);
0096 }
0097 
0098 static void enable_a20_kbc(void)
0099 {
0100     empty_8042();
0101 
0102     outb(0xd1, 0x64);   /* Command write */
0103     empty_8042();
0104 
0105     outb(0xdf, 0x60);   /* A20 on */
0106     empty_8042();
0107 
0108     outb(0xff, 0x64);   /* Null command, but UHCI wants it */
0109     empty_8042();
0110 }
0111 
0112 static void enable_a20_fast(void)
0113 {
0114     u8 port_a;
0115 
0116     port_a = inb(0x92); /* Configuration port A */
0117     port_a |=  0x02;    /* Enable A20 */
0118     port_a &= ~0x01;    /* Do not reset machine */
0119     outb(port_a, 0x92);
0120 }
0121 
0122 /*
0123  * Actual routine to enable A20; return 0 on ok, -1 on failure
0124  */
0125 
0126 #define A20_ENABLE_LOOPS 255    /* Number of times to try */
0127 
0128 int enable_a20(void)
0129 {
0130        int loops = A20_ENABLE_LOOPS;
0131        int kbc_err;
0132 
0133        while (loops--) {
0134            /* First, check to see if A20 is already enabled
0135           (legacy free, etc.) */
0136            if (a20_test_short())
0137                return 0;
0138            
0139            /* Next, try the BIOS (INT 0x15, AX=0x2401) */
0140            enable_a20_bios();
0141            if (a20_test_short())
0142                return 0;
0143            
0144            /* Try enabling A20 through the keyboard controller */
0145            kbc_err = empty_8042();
0146 
0147            if (a20_test_short())
0148                return 0; /* BIOS worked, but with delayed reaction */
0149     
0150            if (!kbc_err) {
0151                enable_a20_kbc();
0152                if (a20_test_long())
0153                    return 0;
0154            }
0155            
0156            /* Finally, try enabling the "fast A20 gate" */
0157            enable_a20_fast();
0158            if (a20_test_long())
0159                return 0;
0160        }
0161        
0162        return -1;
0163 }