Objective:
1.Build the customize BIOS and show some message on system startup
2.Understand the kernel booting flow and boot a customize program
3.Modify linux 0.11 bootsect to support multi booting
1.SeaBIOS is an open-source legacy BIOS implementation
which can be used as a coreboot payload.
$git clone git://git.seabios.org/seabios.git seabios $cd seabios $make menuconfig $make
In “make menuconfig” step you can change the debug level,
it will show the BIOS debug message when system booting.
After make step you will see the bios.bin rom file in the out folder.
open the src/bootsplash.c
and add the print message code in enable_vga_console function, then do make again.
[osdi@localhost seabios]$ vim src/bootsplash.c
/**************************************************************** * VGA text / graphics console ****************************************************************/ void enable_vga_console(void) { dprintf(1, "Turning on vga text mode console\n"); struct bregs br; /* Enable VGA text mode */ memset(&br, 0, sizeof(br)); br.ax = 0x0003; call16_int10(&br); printf("THIS IS OSDI LAB2!!!\n"); // Write to screen. printf("SeaBIOS (version %s)\n", VERSION); display_uuid(); }
Next you can use following command to use customize BIOS rom for QEMU.
$qemu -m 16M -boot a -fda linux0.11/Image -hda osdi.img -bios seabios/out/bios.bin -serial stdio
2.Kernel Booting
In linux 0.11 is use two phase boot sequence, first is bootsect called “boot sector”
use for load kernel into memory second is head it use for boot kernel and jump to main function.
1. BIOS load the bootsect from disk MBR to 0x7c00 memory address 2. boosect copy itself to 0x90000 memory address and jump to 0x90000. 3. boosect load setup from disk to 0x90200 memory address. 4. Get some system peripheral device parameters (video, root disk, keyboard,…,etc.) and jump to 0x90200. 5. Switch system into protected mode move kernel from 0x10000(64K) to 0x0000 6. Jump to 0x0000 and execute head.s for kernel boot
2.1. Boot the hello world program
modify the boot/bootsect.s boot loader program and make file
to load a hello word program and execute it.
Hello.s 程式碼
.code16 .global _start .text _start: mov %cs, %ax mov %ax, %ds mov %ax, %es mov $0x03, %ah # read cursor pos xor %bh, %bh int $0x10 mov $24, %cx mov $0x0007, %bx # page 0, attribute 7 (normal) #lea msg1, %bp mov $msg1, %bp mov $0x1301, %ax # write string, move cursor int $0x10 end_hello: jmp end_hello msg1: .byte 13,10 .ascii "Hello OSDI Lab2!" .byte 13,10
2.1-1 Modify the tools/build.sh
--- lab1/linux-0.11/tools/build.sh (revision 48) +++ lab1/linux-0.11/tools/build.sh (working copy) @@ -7,7 +7,8 @@ setup=$2 system=$3 IMAGE=$4 -root_dev=$5 +hello_img=$5 +root_dev=$6 # Set the biggest sys_size # Changes from 0x20000 to 0x30000 by tigercn to avoid oversized code. @@ -26,15 +27,18 @@ [ ! -f "$bootsect" ] && echo "there is no bootsect binary file there" && exit -1 dd if=$bootsect bs=512 count=1 of=$IMAGE 2>&1 >/dev/null +[ ! -f "$hello_img" ] && echo "there is no hello binary file there" && exit -1 +dd if=$hello_img seek=1 bs=512 count=1 of=$IMAGE 2>&1 >/dev/null + # Write setup(4 * 512bytes, four sectors) to stdout [ ! -f "$setup" ] && echo "there is no setup binary file there" && exit -1 -dd if=$setup seek=1 bs=512 count=4 of=$IMAGE 2>&1 >/dev/null +dd if=$setup seek=2 bs=512 count=4 of=$IMAGE 2>&1 >/dev/null # Write system(< SYS_SIZE) to stdout [ ! -f "$system" ] && echo "there is no system binary file there" && exit -1 system_size=`wc -c $system |cut -d" " -f1` [ $system_size -gt $SYS_SIZE ] && echo "the system binary is too big" && exit -1 -dd if=$system seek=5 bs=512 count=$((2888-1-4)) of=$IMAGE 2>&1 >/dev/null +dd if=$system seek=6 bs=512 count=$((2888-1-4)) of=$IMAGE 2>&1 >/dev/null
2.1-2 Modify the Makefile
Index: lab1/linux-0.11/boot/Makefile =================================================================== --- lab1/linux-0.11/boot/Makefile (revision 48) +++ lab1/linux-0.11/boot/Makefile (working copy) @@ -2,13 +2,17 @@ LDFLAGS += -Ttext 0 -all: bootsect setup +all: bootsect setup hello bootsect: bootsect.s @$(AS) -o bootsect.o bootsect.s @$(LD) $(LDFLAGS) -o bootsect bootsect.o @objcopy -R .pdr -R .comment -R.note -S -O binary bootsect +hello: hello.s + @$(AS) -o hello.o hello.s + @$(LD) $(LDFLAGS) -o hello hello.o + @objcopy -R .pdr -R .comment -R.note -S -O binary hello setup: setup.s @$(AS) -o setup.o setup.s @@ -19,4 +23,4 @@ @$(AS) -o head.o head.s clean: - @rm -f bootsect bootsect.o setup setup.o head.o + @rm -f bootsect bootsect.o setup setup.o head.o hello hello.o Index: lab1/linux-0.11/init/main.c
2.1-3 Modify the boot/bootsect.s
Use “int $0x13” to load the hello image to 0x1000 memory address.
注意:Intel和AT&T的差異
Intel mov ax, 0x10
AT&T mov $0x10, %ax
原始的boot/bootsect.s Trace
.equ SETUPLEN, 4 # nr of setup-sectors .equ BOOTSEG, 0x07c0 # original address of boot-sector .equ INITSEG, 0x9000 # we move boot here - out of the way .equ SETUPSEG, 0x9020 # setup starts here .equ SYSSEG, 0x1000 # system loaded at 0x10000 (65536). .equ ENDSEG, SYSSEG + SYSSIZE # where to stop loading # ROOT_DEV: 0x000 - same type of floppy as boot. # 0x301 - first partition on first drive etc .equ ROOT_DEV, 0x301 ljmp $BOOTSEG, $_start #跳至BOOTSEG(0x07C0)執行_start的區段 _start: # 把0x07c0的bootsect.s搬到0x9000 mov $BOOTSEG, %ax mov %ax, %ds # %ds指向0x07c0(要用ax當中介才能給ds值) mov $INITSEG, %ax mov %ax, %es # %es指向0x9000 mov $256, %cx # %cx初始化256 sub %si, %si # %si(來源地址)歸零 ds(0x07c0):si(0x0000) sub %di, %di # %di(目的地址)歸零 es(0x9000):di(0x0000) rep # 如果cx大於1,重複執行mov且cx-1,直到cx為0 movsw # Move word at address ds:si to address es:di,movsw和rep一起 # ds:si and es:di mean the segment:offset # the segment are offset are combined as segment * 16 + offset. ljmp $INITSEG, $go # 跳至INITSEG(0x9000)段執行go的標示(cs=0x9000) go: # 將其它segment registers包括DS,ES,SS都指向0x9000這個位置,與CS看齊。 # 節段或區段暫存區:CS、DS、ES、SS、FS、GS mov %cs, %ax # 程式區段CS:如IP所執行位址都是CS程式區段的內容,指向0x90000 mov %ax, %ds # 資料區段 DS:如 mov ax,[bx] 間接定址法所指都是資料段的資料 mov %ax, %es # 額外區段 ES:如 mov ax,es:[di] 利用間接定址法取其他區段記憶體資料時 # put stack at 0x9ff00. mov %ax, %ss # 堆疊區段 SS:如 SP 堆疊資料,都是指在堆疊段的 mov $0xFF00, %sp # arbitrary value >>512 # load the setup-sectors directly after the bootblock. # Note that 'es' is already set up. # 所以下面的位置都是相對es的值 load_setup: mov $0x0000, %dx # drive 0, head 0 mov $0x0002, %cx # sector 2, track 0 把 sec 2 讀出來(chs mode 是以sec 1 作為開始) mov $0x0200, %bx # address = es*0x10 + bx, es:bx 指向該service要存放在0x90200, # 由此可知這邊是要把data讀出來放到0x90200的位置 .equ AX, 0x0200+SETUPLEN # 這邊的值為0x0204, AH是0x02號 service (把data放到ram), AL 為讀幾個 sector mov $AX, %ax # service 2, nr of sectors int $0x13 # 執行中斷0x13 jnc ok_load_setup # ok - continue mov $0x0000, %dx mov $0x0000, %ax # reset the diskette int $0x13 jmp load_setup ... ok_load_setup: ... sread: .word 1+ SETUPLEN # sectors read of current track
3 Multi booting support
In this experiment you need implement simple keyboard character reader when user
Press ‘1’ to boot linux 0.11
Press ‘2’ to boot hello world
Hint: use the BIOS “int $0x16” service to read keystroke,
when press ‘1’ jump to load_setup, press ‘2’ jump to load_hello,
otherwise read keystroke again.
.equ SETUPLEN, 4 # nr of setup-sectors .equ BOOTSEG, 0x07c0 # original address of boot-sector .equ INITSEG, 0x9000 # we move boot here - out of the way .equ SETUPSEG, 0x9020 # setup starts here .equ SYSSEG, 0x1000 # system loaded at 0x10000 (65536). .equ ENDSEG, SYSSEG + SYSSIZE # where to stop loading # ROOT_DEV: 0x000 - same type of floppy as boot. # 0x301 - first partition on first drive etc .equ ROOT_DEV, 0x301 ljmp $BOOTSEG, $_start _start: mov $BOOTSEG, %ax mov %ax, %ds mov $INITSEG, %ax mov %ax, %es mov $256, %cx sub %si, %si sub %di, %di rep movsw ljmp $INITSEG, $go go: mov %cs, %ax mov %ax, %ds mov %ax, %es # put stack at 0x9ff00. mov %ax, %ss mov $0xFF00, %sp ###################################### # 寫法1 select: mov $0x03, %ah /* set mode INT 10 */ xor %bh, %bh int $0x10 /* 使用BIOS 顯示服務Video Service */ mov $58, %cx mov $0x0007, %bx # page 0, attribute 7 (normal) mov $msg, %bp mov $0x1301, %ax # write string, move cursor int $0x10 jmp read msg: .byte 13,10 # \r\n(字節) .ascii "##### boot memu #####" read: mov $0x00, %ah int $0x16 cmp $0x31, %al je load_setup cmp $0x32, %al je load_hello jmp select ###################################### # 寫法2 省略輸出 select: read_input: mov $0x00, %ah int $0x16 cmp $0x31, %al # ASCII的1 je load_setup cmp $0x32, %al # ASCII的2 je load_hello jmp select ###################################### # load hello image load_hello: mov $0x0000, %dx # drive 0, head 0 mov $0x0002, %cx # sector 2, track 0 mov $0x0200, %bx # address = 512, in INITSEG 2*16**2 .equ AX, 0x0200+1 mov $AX, %ax # service 2, nr of sectors int $0x13 # read it jnc ok_hello # ok - continue mov $0x0000, %dx mov $0x0000, %ax # reset the diskette int $0x13 jmp load_hello ok_hello: #ljmp $SETUPSEG,$0 # load the setup-sectors directly after the bootblock. # Note that 'es' is already set up. load_setup: mov $0x0000, %dx # drive 0, head 0 mov $0x0003, %cx # sector 3, track 0 mov $0x0200, %bx # address = 512, in INITSEG .equ AX, 0x0200+SETUPLEN mov $AX, %ax # service 2, nr of sectors int $0x13 # read it jnc ok_load_setup # ok - continue mov $0x0000, %dx mov $0x0000, %ax # reset the diskette int $0x13 jmp load_setup ok_load_setup: # Get disk drive parameters, specifically nr of sectors/track