This article mainly talks about the details of booting process of Linux 0.11. It focuses on the initiation work in the Real Mode and some prepared work in the Protected Mode. In this part, all the code is written by assembly language
When we turn on the computer, the CPU is in Real Mode, so the addressing range is 1MB and there is nothing in the RAM. So this part is done by the hardware. The memory 640KB~1MB is used for the ROM and the BIOS is in the ROM. The the CS register is set as 0xFFFF, and IP is set as 0x0000. At this time the CS:IP is 0xFFFF0, and this is the entry of BIOS.
Bios will create its IVT (Interrupt Vector Table) and ISR (Interrupt Service Routine) and read the information of the computer and store them in certain address of the memory. At last BIOS will load the first 512 byte of the disk to 0x07C000 and strat to execute from this address.
Bootsect is the 512 bytes BIOS just read from the disk. Here, Bootsect will copy itself from 0x07C000 to 0x90000 and execute from the new address. And set the SS (Stack Segment Register) as 0x9000 and SP (Stack Pointer) as 0xFF00. Setting SS and SP as these values can reserve enough space for stack operation (push and pop) and later interrupt operations will take advantages of the stack.
Then, Bootsect will read the Setup program into the memory 0x90200 (the 0x200 is just 512 bytes). To read the Setup, Bootsect will use the IVT and ISR created by BIOS to read from the disk. Use the interrupt “int 0x13” to read Setup (4 sectors, 2KB). Then loadd the other parts of the system to 0x10000 in the same way (240 sectors, 120KB). As it takes some time to load the system, “Loading system …” is printed on the screen by using the 0x10 interrupt. Then Bootsect will load the root device number and store it in 0x901FC, in case load the root file system later.
Setup will fisrt use the interrupt to get the computer information and store them in 0x90000~0x901FD, and this operation will cover some parts of Bootsect. Then Setup disables all the interrupts by setting the IF bit of EFLAGS Register as 0. Because the later operation will cover the memory of the IVT and ISR created by BIOS, if interrupts occurs at that time, unexpected error may happen.
Then Setup will move the system (240 sectors loaded by Bootsect) from 0x10000 to 0x00000, which will cover the IVT and ISR created by BIOS. Because the next step will turn to Protected Mode, new IDT and GDT should be set ahead. Here, the GDTR is set as 0x90200 and write the data to this address. Based on the structure of GDT, the zero item is empty, first item is the kernel code segment descriptor, second item is the kernel data segment descriptor. The base address of the kernel code segment and kernel data segment are 0x00000000 and IDTR is also 0x00000000 (because no ISR has been added and the system is still in Real mode, so it does not cover the system in the 0x00000).
The Setup will open the A20, which means the CPU can do 32-bit addressing. Before opening the A20, only 0~19 pins of CPU can be used for addressing. When the address is out of 0xFFFFF, it will return from 0x00000. After opening A20, the 20~31 pins of CPU can be used for addressing. But this does not means, we are in Protected Mode now. Then the Setup will reprogram the interrupt controller (8259A), which will not change any data in the memory and just do some initiation work for the 8259A.
Set the CR0 register as 0 that the system has entered the Protected Mode. As the system is using a new addressing method now, the code below means select the first item of the GDT and the offset is 0. In another words, the next operation will start from the address 0x00000000 as the base address of the first item in the GDT is 0x00000000.
jmpi 0, 8
Remember the Setup has moved the system’s code to this address.
Just comparing the code in head.s and setup.s, we can find the code style has changed a lot. Because they are executed in different mode. The first work of Head is resetting the register used in Read mode, setting the DS, ES, FS, GS as 0x10 which means they will point to the second item of the GDT (kernel data segment descriptor). As the SS and SP can not work in Protected Mode, SS is also set as 0x10, ESP is the new Stack Poniter point to the end of user_stack (an array defined in sched.c exists in the kernel’s data segment).
Then Head will reset the IDT by setting the IDTR as idt (an structure array defined in head.h, also exists in the kernel’s data segment) and set all the ISR in IDT as ignore_int, unknown iterrupt. Then reset the GDT by setting GDTR as gdt (an structure array defined in head.h, also exists in the kernel’s data segment), Because the memory of the old GDT will be used as Buffer. Compared to the old GDT, the content in the new one does not change, except the limit becomes 16MB.
Last, the Head will build a Paging system, that it will create a page content at the begin of the physical memory and 4 page tables after the directory. The first 4 items in the directory point to the 4 tables and 4 tables manage the first 16MB memory (4096*4KB). Set CR3 as 0x00000000 (address of page directory) and set the PG bit of CR0 as 1, which means enable Paging. The code later will use the Paging system. Because for the kernel the linear address is the same to the physical address, so we can still find the code like directly reading 0x901FC to get the root device number.