The Linux Kernel Documentation Project
Amnesiac
February 2024
2
0.1 Introduction
This book/manual documents every single line of code in the Linux Kernel’s
source code.
Specifically this book/manual documents every single file and line of code in the
Linux Kernel - Version 6.7.5
0.2 x86 Architecture
/arch/x86/boot/a20.c
The a20.c file enables the A20 line in the Intel 80286 and later processors.
The A20 line is important because it enables a processor in real mode to access
memory beyond the 1 MB limit in real mode. This is because the maximum
addressable memory in real mode is 1 MB.
The reason for this is that real mode was designed to maintain backward com-
patibility with earlier Intel processors such as the 8086 and 8088.
These processors had a 20-bit address bus which mean they could address up
to 220 or 1,048,576 memory locations, corresponding to 1 MB of memory.
The A20 line enables us to access more the 1 MB of memory in Real Mode. Let
us have a look at the code of a20.c part by part.
Listing 1: a20.c, Lines 1-17
/*
* Enable A20 gate ( return -1 on failure )
*/
# include " boot . h "
# define MAX_8042_LOOPS 100000
# define MAX_8042_FF 32
The first line #include "boot.h" includes the header file boot.h located at
/arch/x86/boot/boot.h
The constant MAX_8042_LOOPS defines the maximum number of loops to wait
for the 8042 PS/2 keyboard controller to acknowledge a command.
To reduce costs some of the general purpose I/O capabilities of the 8042
controller was used to control various functions unrelated to the keybaord such
as the A20 Gate, or System Reset.
When the CPU sends any command to the 8042 controller it expects a
acknowledgement(ACK) from the controller to make sure the messgae has been
recieved.
The variable MAX_8042_LOOPS defines the number of times one must check for
acknowledgement from the 8042 controller. Which is in this case 10, 000.
0.2. X86 ARCHITECTURE 3
The constant MAX_8042_FF defines the number of times the code will check the
8042 controller’s status register for the 0xFF flag this is because the 0xFF flag
usually means something is wrong with the controller.
The 8042 controllers input buffer is where commands sent to it by the CPU are
stored until the controller processes them. The code checks this buffer 32 times
in this case to see if the command has been [Link] us look at the next
few lines
Listing 2: a20.c, Lines 19-45
static int empty_8042 ( void )
{
u8 status ;
int loops = MAX_8042_LOOPS ;
int ffs = MAX_8042_FF ;
while ( loops - -) {
io_delay ();
status = inb (0 x64 );
if ( status == 0 xff ) {
/* FF is a plausible , but very unlikely status */
if (! - - ffs )
return -1; /* Assume no KBC present */
}
if ( status & 1) {
/* Read and discard input data */
io_delay ();
( void ) inb (0 x60 );
} else if (!( status & 2)) {
/* Buffers empty , finished ! */
return 0;
}
}
return -1;
}
The above function executes 9, 999 times as it is implemented using a while loop
which decrements the loops variable which has been set to 10, 000.
The io_delay(); function has been defined in /arch/x86/boot/boot.h as fol-
lows
Listing 3: boot.h Lines 39-43
static inline void io_delay ( void )
{
const u16 DELAY_PORT = 0 x80 ;
outb (0 , DELAY_PORT );
}
4
This above function writes an output to the 0x80 IO port which is a debugging
port and is used to display POST(Power On Self Test) codes on motherboard.
Writing to this port also causes a 1 microsecond delay in program exeuction,
this is done for synchronization purposes.
The status variable stores the value of the status register corresponding to the
8042 controller. The inb function is defined in arch/x86/boot/io.h as follows
# define inb pio_ops . f_inb
# define outb pio_ops . f_outb
# define outw pio_ops . f_outw
pio_ops is an instance of the struct port_io_ops which is itself defined in io.h
as follows
struct port_io_ops {
u8 (* f_inb )( u16 port );
void (* f_outb )( u8 v , u16 port );
void (* f_outw )( u16 v , u16 port );
};
*f_inb, *f_outb and *f_outv are function pointers which point to functions
with the inputs defined in the structure itself, such as u16 port for *f_inb.
However what are these functions?.
The functions *f_inb and the others have to be given a value after creating
a instance of the structure. Earlier mentioned the instance created is pio_ops.
The values given to the structure are as follows
static inline void init_default_io_ops ( void )
{
pio_ops . f_inb = __inb ;
pio_ops . f_outb = __outb ;
pio_ops . f_outw = __outw ;
}
Where do these __inb, __outb and __outw functions come from? They are
defined in /arch/x86/include/asm/shared/io.h. They are generated using
macros and are defined as follows
# define BUILDIO ( bwl , bw , type ) \
static __always_inline void __out ## bwl ( type value , u16 port ) \
{ \
asm volatile (" out " # bwl " %" # bw "0 , % w1 " \
: : " a "( value ) , " Nd "( port )); \
} \
\
static __always_inline type __in ## bwl ( u16 port ) \
{ \
type value ; \
asm volatile (" in " # bwl " % w1 , %" # bw "0" \
: "= a "( value ) : " Nd "( port )); \
return value ; \
}
0.2. X86 ARCHITECTURE 5
These functions use inline and assemblly and are used in other places for writing
and reading from I/O ports. So now getting back to the following line from
a20.c
status = inb (0 x64 );
The 0x64 I/O port is used to access the status register of the PS/2 controller.
This register contains various flags which show the state of the PS/2 controller,
these flags is stored in the status variable.
The next few lines in the code are
if ( status == 0 xff ) {
/* FF is a plausible , but very unlikely status */
if (! - - ffs )
return -1; /* Assume no KBC present */
}
These lines of code check if the status is 0xFF, if it is the program decrements
ffs and checks if it is 0, if it is 0 it means we have checked the status register
32 times and the status is still 0xFF which something is wrong and we return -1
which indicates failure. This is where the MAX_8042_FF variable is used.
The next lines of code(a20.c) are as follows
if ( status & 1) {
/* Read and discard input data */
io_delay ();
( void ) inb (0 x60 );
}
The above code performs a bitwise AND with the data in the status variable.
This extracts the Least Significant Bit in the status variable’s byte. The Least
Significant Bit in the status stands for Output Buffer Status and is 1 if the
output buffer is full and 0 if the output buffer is empty.
If the output buffer is full i.e. the LSB is 1, then we read data from the 0x60 I/O
port which is the Read/Write Data port, we discard this data by typecasting it
to (void).
The io_delay(); is again used for synchronization purposes.
The next few lines of code(a20.c) are as follows
} else if (!( status & 2)) {
/* Buffers empty , finished ! */
return 0;
}
The above code checks if the 2 Least Significant Bit is not set i.e. if it is not
equal to 0. The 2nd bit in the status is reponsible for telling is the Input
Buffer Status of the controller. If it 0 the buffer is empty and if it is 1 the
buffer is full. Only if it is 0 we can give the controller command via the input
buffer.
When the buffer is empty the program returns 0. Indicating the input buffer
6
is empty and we can send commands to the 8042 controller to enable the A20
Line.
If this does not happen the loop exits and −1 is returned indicating failure.
Hoof!, Poof! that was just one function quite some info! isn’t it.