#Booting
##Boot sector
Boot sector is implemented in setctor.S
, check partition to find the first bootable linux partition.
Then call int 0x13
to load the bootloader from C:H:S = 0:0:2
. Finally
jump to bootloader.
##Bootloader Bootloader passes control to the kernel. We do this in a C-assembly-C manner.
First run main
from loader_16bit.c
. In this file we
- enable A20 line to access memory higher than 1M
- call
int 0x15
to detect memory map - set up GDT
- one 32-bit read/executable code segment, 4K granularity, DPL 0
- one 32-bit read/write data segment, 4K granularity, DPL 0
- call
enter_protected_mode
Then in loader.S
, we
- enter the protected mode
- call
loader_main
to the loader code
Finally in loader.c
, we
- clear the text-based console
- initialize the IDE device
- call
load_kernel
to load the kernel image- first we locate the ext2 file system start
- then we call
loader_ext2_find_file
to locate the image in the file system - finally we read the ELF image and load the required segments into memory
- return the kernel image entry address
- copy these data to
KERNEL_SAVED_DATA
(0x7e00
) for kernel useboot_gdt
: GDT set during bootgdtptr
: pointer to boot GDTe820map
: e820 memory map obtained in 16-bit C code
- jump to the kernel image entry address
##Kernel entry
This is linked to .text.entry
section.
The first thing is to set up the mappings for paging.
We'd like to see
- the kernel running in the higher 1 GB of virtual memory
- the lower 3 GB virtual memory are for the user space
We map both regions to the lower 8MB of physical memory
- lower 8 MB of virtual memory
- lower 8 MB of virtual memory starting from the kernel
Then load the base address of the page directory and enable paging.
Finally jump to main function of kernel C code.
##Kernel main function First initialize the base address of video memory for printing later.
Initialize memory with meminit()
.
##Memory initialization First, copy the saved kernel data to kernel memory space, later we'll access memory all through virtual addresses.
Second, scan_e820map()
, to get the max and min available page frame number (PFN) maxpfn
and minpfn
.
Third, init_bootmem()
, initialize the bootmen allocator.
Fourth, init_mapping()
, build the new mapping since we do not need the lower 8 MB identity mapping any more,
we access memory only through virtual addresses. The new page directory starts from pagedir
.
We set up a new mapping, which directly map the lower 896 MB physical memory to the kernel virtual memory space,
which is above 0xc0000000
.
Then, init_pages()
, allocate a page
structure for each physical page in the range of minpfn
to maxpfn
, saved in mem_map
.
Then, init_buddy()
, initializes buddy allocator. We maintain 10 orders of continuous pages, each has a
linked list holding all the 2^order -sized page blocks on it.
Then, free_all_bootmem()
, bootmem allocator handles all the free pages to the buddy allocator.
Then, init_slab()
, initialize slab allocator. We maintain 4-512 KB -sized objects with the slab allocator.
Since the creation of slabs needs kmalloc()
, we need to bootstrap the slabs for struct slab_cache
and struct slab
to make kmalloc()
available
for the rest slab sizes. kmalloc()
has an array called kmalloc_sizes
which records the pointers to the slab of each size.
All slab caches are stored in the linked list slab_caches
.
#Memory layout
+----------------------+
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
+--- +----------------------+ end of direct mapping area, min of maxpfn and DIRECTMAP_PHYS_MAX
| | |
| | |
| | |
| | |
| | |
| | managed by bootmem |
| | |
| +----------------------+ <-- bdata.bitmap + bdata.size
| bootmem bitmap |
direct +______________________+ <-- bdata.bitmap
mapping | |
area +----------------------+ _end
| ... | --------------------------------+
| +----------------------+ |
| | pagedir | 4 KB page directory |
| +----------------------+ |
| | stack | 8 KB stack |
| +----------------------+ +-- .bss
| | pagetable1 | 4 KB page table 1 |
| +----------------------+ |
| | pagetable0 | 4 KB page table 2 |
| +----------------------+ |
| | init_pagedir | 4 KB initial page directory |
| +----------------------+ --------------------------------+
| | .data |
| +----------------------+
| | .rodata |
| +----------------------+ ---+
| | .stabstr | |
| +----------------------+ +-- for debugging
| | .stab | |
| +----------------------+ ---+
| | .text |
| +----------------------+
| | .text.entry |
| +----------------------+ KERNEL_PHYS_START 0x100000, MIN_PA, 1 MB
| | low memory |
+--- +----------------------+ 0x0
#Test ##Test if mapping is set up correctly Read the first byte of each physical page and see if #GP triggers:
char byte;
for (pfn = minpfn; pfn <= pte_max; pfn++) {
byte = *((char *)(pfn << PAGE_SHIFT));
byte += 0; /* eliminate compiler warning */
printk("physical page %d/%d ok\n", pfn, pte_max);
}
/* bad: not mapped page */
byte = *((char *)((pte_max + 1) << PAGE_SHIFT));
/* ok: last byte of the last mapped page */
byte = *((char *)((pte_max + 1) << PAGE_SHIFT) - 1);