12 xv6 Boot Process
12 xv6 Boot Process
Abhijit A. M.
[email protected]
Credits:
xv6 book by Cox, Kaashoek, Morris
Notes by Prof. Sorav Bansal
A word of caution
We begin reading xv6 code
But it’s not possible to read this code in a “linear
fashion”
The dependency between knowing OS concepts
and reading/writing a kernel that is written using all
concepts
What we have seen ....
Compilation process, calling conventions
Basics of Memory Management by OS
Basics of x86 architecture
Registers, segments, memory management unit,
addressing, some basic machine instructions,
ELF files
Objdump, program headers
Symbol tables
Boot-process
Bootloader itself
Is loaded by the BIOS at a fixed location in
memory and BIOS makes it run
Our job, as OS programmers, is to write the
bootloader code
Bootloader does
Pick up code of OS from a ‘known’ location and
loads it in memory
Makes the OS run
Xv6 bootloader: bootasm.S bootmain.c (see
bootloader
BIOS Runs (automatically)
Loads boot sector into RAM at 0x7c00
Starts executing that code
Make sure that your bootloader is loaded at 0x7c00
Makefile has
bootblock: bootblock.S bootmain.c
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
.....
...
Processor starts in real mode
Processor starts in real mode – works like 16 bit 8088
eight 16-bit general-purpose registers,
Segment registers %cs, %ds, %es, and %ss -->
additional bits necessary to generate 20-bit memory
addresses from 16-bit registers.
addr = seg << 4 + addr
Virtual ddress = offset Address
%cs=0 %ip=7c00.
Paging scheme
PTBR
X86 paging
Page Directory Entry (PDE)
Page Table Entry (PTE)
CR3
CR4
Segmentation + Paging
asm.h
#define SEG_ASM(type,base,lim) \
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
Segment selector
Note in 16 bit mode, segment selector is 16 bit, here it’s 13 bit + 3 bits
EFLAGS register
lgdt gdtdesc lgdt
... load the processor’s
(GDT) register with the
# Bootstrap GDT
value gdtdesc which
.p2align 2 # force 4 byte points to the table gdt.
alignment
table gdt : The table has a
gdt: null entry, one entry for
SEG_NULLASM # executable code, and one
null seg entry to data.
all segments have a base
SEG_ASM(STA_X|STA_ address of zero and the
R, 0x0, 0xffffffff) # code maximum possible limit
seg The code segment
bootasm.S after “lgdt gdtdesc”
till jump to “entry”
Still
Logical Address = Physical addres
0 4GB Write
0 0 0
GDT
Prepare to enable protected mode
Prepare to enable movl %cr0, %eax
protected mode by orl $CR0_PE, %eax
setting the 1 bit
(CR0_PE) in register movl %eax, %cr0
%cr0
CR0
2 0 4GB Write
0 0 0 0
SS
CS GDT
bootmain(): already in memory, as
part of ‘bootblock’
bootmain.c , expects to void
find a copy of the kernel bootmain(void)
executable on the disk
starting at the second {
sector (sector = 1). struct elfhdr *elf;
Why? struct proghdr *ph,
The kernel is an ELF *eph;
format binary void (*entry)(void);
Bootmain loads the first uchar* pa;
4096 bytes of the ELF
binary. It places the in-
bootmain()
Check if it’s really ELF // Is this an ELF
or not executable?
Next load kernel code if(elf->magic !=
from ELF file “kernel” ELF_MAGIC)
into memory return; // let
bootasm.S handle error
struct elfhdr {
ELF uint magic; // must
equal ELF_MAGIC
uchar elf[12];
ushort type;
ushort machine;
uint version;
uint entry;
uint phoff; // where is
program header table
uint shoff;
uint flags;
// Program header
ELF struct proghdr {
uint type; // Loadable
segment , Dynamic
linking information ,
Interpreter information ,
Thread-Local Storage
template , etc.
uint off; //Offset of the
segment in the file image.
uint vaddr; //Virtual
address of the segment in
memory.
kernel: Run ‘objdump
file format elf32-i386 -x -a kernel | head -15’ & see this
kernel Diff between mems
Code to be loaded at KERNBASE + KE
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0010000c
Program Header:
LOAD off 0x00001000 vaddr 0x80100000 paddr 0x00100000 align 2**12
filesz 0x0000a516 memsz 0x000154a8 flags rwx
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4
filesz 0x00000000 memsz 0x00000000 flags rwx
Remember: 4
MB pages
are possible
From entry: RAM
Till: inside main(), before kvmalloc()
4MB
3 512 0 P,W,PS
2 0 4GB Write .
.
1 0 4GB Read, Execute
DS .
0 0 0 0
3
SS
GDT 2
CS 1
CR3
0 0 P,W,PS
entrypgdir
From entry: RAM
Till: inside main(), before kvmalloc()
Physical Addr
4MB
3 512 0 P,W,PS
0 4GB Write .
2
.
0 4GB Read, Execute
1
DS .
0 0 0
0 3
SS
GDT 2
1
CS Even now, every Logical address = Physical address, but through
CR3 Page dir
0 0 P,W,PS
entrypgdir
entrypgdir in main.c, is used by
entry()
__attribute__((__aligned__(PGSIZE)))
#define PTE_P 0x001 // Present
pde_t entrypgdir[NPDENTRIES] = {
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PS 0x080 // Page Size
// Map VA's [0, 4MB) to PA's [0, 4MB) #define PDXSHIFT 22 // offset of PDX in
[0] = (0) | PTE_P | PTE_W | PTE_PS,
// Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB). This is entry 512
};