操作系統(tǒng)實(shí)驗(yàn):Lab1

清華大學(xué)操作系統(tǒng)Lab1實(shí)驗(yàn)報告
課程主頁:http://os.cs.tsinghua.edu.cn/oscourse/OS2018spring
實(shí)驗(yàn)指導(dǎo)書:https://chyyuu.gitbooks.io/ucore_os_docs/content/
github:https://github.com/chyyuu/ucore_os_lab

練習(xí)1:理解通過make生成執(zhí)行文件的過程

操作系統(tǒng)鏡像文件ucore.img是如何一步一步生成的撕捍?

運(yùn)行make "=V"署驻,可以得到如下編譯過程油狂。

+ cc kern/init/init.c
gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c -o obj/kern/init/init.o
+ cc kern/libs/stdio.c
gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/stdio.c -o obj/kern/libs/stdio.o
+ cc kern/libs/readline.c
gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/readline.c -o obj/kern/libs/readline.o
+ cc kern/debug/panic.c
gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/panic.c -o obj/kern/debug/panic.o
+ cc kern/debug/kdebug.c
gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kdebug.c -o obj/kern/debug/kdebug.o
+ cc kern/debug/kmonitor.c
gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kmonitor.c -o obj/kern/debug/kmonitor.o
+ cc kern/driver/clock.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/clock.c -o obj/kern/driver/clock.o
+ cc kern/driver/console.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/console.c -o obj/kern/driver/console.o
+ cc kern/driver/picirq.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/picirq.c -o obj/kern/driver/picirq.o
+ cc kern/driver/intr.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/intr.c -o obj/kern/driver/intr.o
+ cc kern/trap/trap.c
gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trap.c -o obj/kern/trap/trap.o
+ cc kern/trap/vectors.S
gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/vectors.S -o obj/kern/trap/vectors.o
+ cc kern/trap/trapentry.S
gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trapentry.S -o obj/kern/trap/trapentry.o
+ cc kern/mm/pmm.c
gcc -Ikern/mm/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/mm/pmm.c -o obj/kern/mm/pmm.o
+ cc libs/string.c
gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/  -c libs/string.c -o obj/libs/string.o
+ cc libs/printfmt.c
gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/  -c libs/printfmt.c -o obj/libs/printfmt.o
+ ld bin/kernel
ld -m    elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel  obj/kern/init/init.o obj/kern/libs/stdio.o obj/kern/libs/readline.o obj/kern/debug/panic.o obj/kern/debug/kdebug.o obj/kern/debug/kmonitor.o obj/kern/driver/clock.o obj/kern/driver/console.o obj/kern/driver/picirq.o obj/kern/driver/intr.o obj/kern/trap/trap.o obj/kern/trap/vectors.o obj/kern/trap/trapentry.o obj/kern/mm/pmm.o  obj/libs/string.o obj/libs/printfmt.o
+ cc boot/bootasm.S
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o obj/boot/bootasm.o
+ cc boot/bootmain.c
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.o
+ cc tools/sign.c
gcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.o
gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
+ ld bin/bootblock
ld -m    elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
'obj/bootblock.out' size: 488 bytes
build 512 bytes boot sector: 'bin/bootblock' success!
dd if=/dev/zero of=bin/ucore.img count=10000
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc

根據(jù)實(shí)際命令斤程,回到Makefile文件中,可以看到對應(yīng)的生成ucore.img的過程及相應(yīng)語句如下乖酬,

# create ucore.img
UCOREIMG    := $(call totarget,ucore.img)

$(UCOREIMG): $(kernel) $(bootblock)
    $(V)dd if=/dev/zero of=$@ count=10000
    $(V)dd if=$(bootblock) of=$@ conv=notrunc
    $(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc

$(call create_target,ucore.img)

逐條分析:

  • $(kernel):生成kernel顷编。需要以下兩步:
    • 編譯kern/目錄下的C程序,生成kernel需要的.o文件:

      $(call add_files_cc,$(call listf_cc,$(KSRCDIR)),kernel,$(KCFLAGS))
      

      執(zhí)行的實(shí)際命令為

      + cc kern/init/init.c
      gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c -o obj/kern/init/init.o
      + cc kern/libs/stdio.c
      gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/stdio.c -o obj/kern/libs/stdio.o
      + cc kern/libs/readline.c
      gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/readline.c -o obj/kern/libs/readline.o
      + cc kern/debug/panic.c
      gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/panic.c -o obj/kern/debug/panic.o
      + cc kern/debug/kdebug.c
      gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kdebug.c -o obj/kern/debug/kdebug.o
      + cc kern/debug/kmonitor.c
      gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kmonitor.c -o obj/kern/debug/kmonitor.o
      + cc kern/driver/clock.c
      gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/clock.c -o obj/kern/driver/clock.o
      + cc kern/driver/console.c
      gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/console.c -o obj/kern/driver/console.o
      + cc kern/driver/picirq.c
      gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/picirq.c -o obj/kern/driver/picirq.o
      + cc kern/driver/intr.c
      gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/intr.c -o obj/kern/driver/intr.o
      + cc kern/trap/trap.c
      gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trap.c -o obj/kern/trap/trap.o
      + cc kern/trap/vectors.S
      gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/vectors.S -o obj/kern/trap/vectors.o
      + cc kern/trap/trapentry.S
      gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trapentry.S -o obj/kern/trap/trapentry.o
      + cc kern/mm/pmm.c
      gcc -Ikern/mm/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/mm/pmm.c -o obj/kern/mm/pmm.o
      + cc libs/string.c
      gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/  -c libs/string.c -o obj/libs/string.o
      + cc libs/printfmt.c
      gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/  -c libs/printfmt.c -o obj/libs/printfmt.o
      
    • 鏈接這些.o文件剑刑,生成kernel媳纬。

      # create kernel target
      kernel = $(call totarget,kernel)
      
      $(kernel): tools/kernel.ld
      
      $(kernel): $(KOBJS)
          @echo + ld $@
          $(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS)
          @$(OBJDUMP) -S $@ > $(call asmfile,kernel)
          @$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel)
      
      $(call create_target,kernel)
      

      執(zhí)行的實(shí)際命令為

      + ld bin/kernel
      ld -m    elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel  obj/kern/init/init.o obj/kern/libs/stdio.o obj/kern/libs/readline.o obj/kern/debug/panic.o obj/kern/debug/kdebug.o obj/kern/debug/kmonitor.o obj/kern/driver/clock.o obj/kern/driver/console.o obj/kern/driver/picirq.o obj/kern/driver/intr.o obj/kern/trap/trap.o obj/kern/trap/vectors.o obj/kern/trap/trapentry.o obj/kern/mm/pmm.o  obj/libs/string.o obj/libs/printfmt.o
      
  • $(bootblock):生成binblock。需要以下三步:
    • 生成bootmain.o和bootasm.o施掏。
      bootfiles = $(call listf_cc,boot)
      $(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc))
      
      執(zhí)行的實(shí)際命令為
      + cc boot/bootasm.S
      gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o         obj/boot/bootasm.o
      + cc boot/bootmain.c
      gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.o
      
    • 編譯tools/sign.c钮惠,生成sign.o。
      # create 'sign' tools
      $(call add_files_host,tools/sign.c,sign,sign)
      $(call create_target_host,sign,sign)
      
      執(zhí)行的實(shí)際命令為
      + cc tools/sign.c
      gcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.o
      gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
      
    • 鏈接以上的.o文件七芭。
      bootblock = $(call totarget,bootblock)
      
      $(bootblock): $(call toobj,$(bootfiles)) | $(call totarget,sign)
          @echo + ld $@
          $(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ -o $(call toobj,bootblock)
          @$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock)
          @$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock)
          @$(call totarget,sign) $(call outfile,bootblock) $(bootblock)
      
      $(call create_target,bootblock)
      
      執(zhí)行的實(shí)際命令為
      + ld bin/bootblock
      ld -m    elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
      
  • $(V)dd if=/dev/zero of=$@ count=10000:生成一個有10000個塊的文件素挽,每個塊默認(rèn)512字節(jié),用0填充狸驳。執(zhí)行的實(shí)際命令為
    dd if=/dev/zero of=bin/ucore.img count=10000
    
  • $(V)dd if=$(bootblock) of=$@ conv=notrunc:把bootblock中的內(nèi)容寫到第一個塊预明。執(zhí)行的實(shí)際命令為
    dd if=bin/bootblock of=bin/ucore.img conv=notrunc
    
  • $(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc:從第二個塊開始寫kernel中的內(nèi)容。執(zhí)行的實(shí)際命令為
    dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
    

實(shí)際執(zhí)行的命令分為三類:

  1. gcc命令:將.c或.S命令編譯生成.o目標(biāo)文件耙箍。
  2. ld命令:鏈接.o文件生成新的.o文件或可執(zhí)行文件撰糠。
  3. dd命令:用指定大小的塊拷貝一個文件,并在拷貝的同時進(jìn)行指定的轉(zhuǎn)換辩昆。

gcc命令的參數(shù)和含義如下表:

參數(shù) 含義
-Idir 把dir 加入到搜索頭文件的路徑列表中
-fno-builtin 不接受沒有 _builtin 前綴的函數(shù)作為內(nèi)建函數(shù)
-Wall 開啟警告
-ggdb 生成gdb專 用的調(diào)試信息,使用最適合的格式(DWARF 2,stabs等)會有一些gdb專用的擴(kuò)展,可能造成其他調(diào)試器無法運(yùn)行
-m32 生成32位機(jī)器上的代碼
-gstabs 使用 stabs格式,不包含gdb擴(kuò)展,stabs常用于BSD系統(tǒng)的DBX調(diào)試器
-nostdinc 不使用標(biāo)準(zhǔn)庫
-fno-stack-protector
-O2 編譯時開啟O2優(yōu)化

ld命令的參數(shù)和含義如下表:

參數(shù) 含義
-m <emulation> 模擬為i386上的連接器
-nostdlib 不使用標(biāo)準(zhǔn)庫
-N 設(shè)置代碼段和數(shù)據(jù)段均可讀寫
-e <entry> 指定入口
-Ttext 制定代碼段開始位置
-T <scriptfile> 讓連接器使用指定的腳本
一個被系統(tǒng)認(rèn)為是符合規(guī)范的硬盤主引導(dǎo)扇區(qū)的特征是什么阅酪?

在tools/sign.c中,

    char buf[512];
    memset(buf, 0, sizeof(buf));
    FILE *ifp = fopen(argv[1], "rb");
    int size = fread(buf, 1, st.st_size, ifp);
    if (size != st.st_size) {
        fprintf(stderr, "read '%s' error, size is %d.\n", argv[1], size);
        return -1;
    }
    fclose(ifp);
    buf[510] = 0x55;
    buf[511] = 0xAA;

可以看到汁针,符合規(guī)范的硬盤主引導(dǎo)扇區(qū)必須含有512個字節(jié)术辐,并且最后兩個字節(jié)分別是0x55和0xAA。

練習(xí)2:使用qemu執(zhí)行并調(diào)試lab1中的軟件

從CPU加電后執(zhí)行的第一條指令開始施无,單步跟蹤BIOS的執(zhí)行

將tools/gitinit文件改為如下命令:

file bin/kernel
target remote :1234
set architecture i8086

隨后執(zhí)行make debug將彈出gdb窗口辉词,在gdb窗口中使用si命令即可單步追蹤。截圖如下:

從CPU加電后第一條指令開始執(zhí)行猾骡,執(zhí)行三步后輸出當(dāng)前指令

在初始化位置0x7c00設(shè)置實(shí)地址斷點(diǎn),測試斷點(diǎn)正常

將tools/gitinit文件改為如下命令:

file bin/kernel
target remote :1234
set architecture i8086
b *0x7c00
c
x/10i $pc

執(zhí)行make debug后瑞躺,測試結(jié)果如圖:

在0x7c00處設(shè)斷點(diǎn)

從0x7c00開始跟蹤代碼運(yùn)行,將單步跟蹤反匯編得到的代碼與bootasm.S和bootblock.asm進(jìn)行比較

為了方便比較隧魄,我將Makefile做如下更改以使得跟蹤代碼的匯編代碼輸出到文件中:

debug: $(UCOREIMG)
    $(V)$(QEMU) -S -s -d in_asm -D $(BINDIR)/q.log -parallel stdio -hda $< -serial null &
    $(V)sleep 2
    $(V)$(TERMINAL) -e "gdb -q -tui -x tools/gdbinit"

將tools/gitinit文件改為如下命令以使得每一步si都可以將匯編代碼輸出出來:

file bin/kernel
target remote :1234
set architecture i8086
b *0x7c00
c
x/i $pc
set architecture i386
define hook-stop
x/i $pc
end

單步跟蹤的得到的部分代碼如下:

----------------
IN: 
0x00007c00:  cli    
----------------
IN: 
0x00007c00:  cli    
----------------
IN: 
0x00007c01:  cld    
----------------
IN: 
0x00007c02:  xor    %ax,%ax
----------------
IN: 
0x00007c04:  mov    %ax,%ds
----------------
IN: 
0x00007c06:  mov    %ax,%es
----------------
IN: 
0x00007c08:  mov    %ax,%ss
----------------
IN: 
0x00007c0a:  in     $0x64,%al
----------------
IN: 
0x00007c0c:  test   $0x2,%al
----------------
IN: 
0x00007c0e:  jne    0x7c0a
----------------
IN: 
0x00007c10:  mov    $0xd1,%al
----------------
IN: 
0x00007c12:  out    %al,$0x64
----------------
IN: 
0x00007c14:  in     $0x64,%al
----------------
IN: 
0x00007c16:  test   $0x2,%al
----------------
IN: 
0x00007c18:  jne    0x7c14
----------------
IN: 
0x00007c1a:  mov    $0xdf,%al

可以看出,和bootloader.S中0x7c00起始的匯編代碼相同隘蝎。

自己找一個bootloader或內(nèi)核中的代碼位置,設(shè)置斷點(diǎn)并進(jìn)行測試

將端點(diǎn)設(shè)在0x7d10襟企,tools/gitinit文件改為如下命令:

file bin/kernel
target remote :1234
set architecture i8086
b *0x7d10
c
x/i $pc
set architecture i386
define hook-stop
x/i $pc
end

效果如圖:


斷點(diǎn)為0x7de0

練習(xí)3:分析bootloader進(jìn)入保護(hù)模式的過程

見注釋Step1-6.

start:
# Step1:清理環(huán)境嘱么,射中重要段寄存器的初值。
.code16
    cli     
    cld  

    xorw %ax, %ax  
    movw %ax, %ds
    movw %ax, %es
    movw %ax, %ss

# Step2:開啟A20顽悼。通過將鍵盤控制器上的A20線置于高電位曼振,全部32條地址線可用, 可以訪問4G的內(nèi)存空間蔚龙。
seta20.1:
    inb $0x64, %al  # 等待8042鍵盤控制器不忙
    testb $0x2, %al
    jnz seta20.1

    movb $0xd1, %al  # 向8042端口發(fā)送0xd1
    outb %al, $0x64                                 

seta20.2:
    inb $0x64, %al  # 等待8042鍵盤控制器不忙input buffer empty).
    testb $0x2, %al
    jnz seta20.2

    movb $0xdf, %al  # 開啟A20
    outb %al, $0x60                                

# Step3:初始化GDT冰评,從實(shí)模式切換至保護(hù)模式
    lgdt gdtdesc
    movl %cr0, %eax
    orl $CR0_PE_ON, %eax
    movl %eax, %cr0

# Step4:將處理器轉(zhuǎn)至32位模式,跳轉(zhuǎn)
    ljmp $PROT_MODE_CSEG, $protcseg

.code32                                             # Assemble for 32-bit mode
protcseg:

# Step5:設(shè)置段寄存器木羹,并建立堆棧
    movw $PROT_MODE_DSEG, %ax                       # Our data segment selector
    movw %ax, %ds                                   # -> DS: Data Segment
    movw %ax, %es                                   # -> ES: Extra Segment
    movw %ax, %fs                                   # -> FS
    movw %ax, %gs                                   # -> GS
    movw %ax, %ss                                   # -> SS: Stack Segment
    movl $0x0, %ebp
    movl $start, %esp
# Step6:進(jìn)入bootmain
    call bootmain

練習(xí)4:分析bootloader加載ELF格式的OS的過程

在bootmain函數(shù)中甲雅,包含了加載OS的過程,見Step1-4:

void
bootmain(void) {
    // Step1:讀磁盤中的第一頁
    readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0);

    // Step2:檢查是否為ELF
    if (ELFHDR->e_magic != ELF_MAGIC) {
        goto bad;
    }

    struct proghdr *ph, *eph;

    // Step3:加載描述表坑填,并按照描述表讀入ELF文件中的數(shù)據(jù)
    ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff);
    eph = ph + ELFHDR->e_phnum;
    for (; ph < eph; ph ++) {
        readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset);
    }

    // Step4:根據(jù)ELF頭部儲存的入口信息抛人,找到內(nèi)核的入口。
    ((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))();

bad:
    outw(0x8A00, 0x8A00);
    outw(0x8A00, 0x8E00);

    /* do nothing */
    while (1);
}

其中脐瑰,readseg可以從磁盤讀取任意長度的內(nèi)容妖枚。

練習(xí)5:實(shí)現(xiàn)函數(shù)調(diào)用堆棧跟蹤函數(shù)

補(bǔ)充kern/debug/kdebug.c:

void
print_stackframe(void) {
    uint32_t ebp = read_ebp();
    uint32_t eip = read_eip();
    for (int i = 0; i < STACKFRAME_DEPTH && ebp != 0; ++i) {
        cprintf("ebp:0x%08x eip:0x%08x ", ebp, eip);
        uint32_t arg1, arg2, arg3, arg4;
        arg1 = *((uint32_t *)ebp + 2);
        arg2 = *((uint32_t *)ebp + 3);
        arg3 = *((uint32_t *)ebp + 4);
        arg4 = *((uint32_t *)ebp + 5);
        cprintf("args:0x%08x 0x%08x 0x%08x 0x%08x\n", arg1, arg2, arg3, arg4);
        print_debuginfo(eip - 1);
        eip = *((uint32_t *)ebp + 1);
        ebp = *((uint32_t *)ebp);
    }
}

運(yùn)行make qemu后結(jié)果見“練習(xí)5、6的結(jié)果”一圖苍在。
最后一行的內(nèi)容是bootmain.c中的bootmain函數(shù)绝页,也即第一個使用該堆棧的函數(shù)。bootloader設(shè)置的堆棧從0x7c00開始寂恬,使用“call bootmain”轉(zhuǎn)入bootmain函數(shù)续誉。 call指令壓棧,所以bootmain中ebp為0x7bf8初肉。

練習(xí)5屈芜、6的結(jié)果

練習(xí)6:完善中斷初始化和處理

中斷描述符表( 也可簡稱為保護(hù)模式下的中斷向量表) 中一個表項(xiàng)占多少字節(jié)?其中哪幾位代表中斷處理代碼的入口朴译?

中斷向量表一個表項(xiàng)占用8字節(jié)井佑,2、3字節(jié)是段選擇子眠寿,0躬翁、1字節(jié)和6、7字節(jié)拼成位移盯拱, 兩者聯(lián)合便是中斷處理程序的入口地址盒发。

請編程完善kern/trap/trap.c中對中斷向量表進(jìn)行初始化的函數(shù)idt_init例嘱。
void
idt_init(void) {
    extern uintptr_t __vectors[];
    for (int i = 0; i < 256; ++i) {
        SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
    }
    SETGATE(idt[T_SWITCH_TOK], 0, GD_KTEXT, __vectors[T_SWITCH_TOK], DPL_USER);
    lidt(&idt_pd);
}
請編程完善trap.c中的中斷處理函數(shù)trap,在對時鐘中斷進(jìn)行處理的部分填寫trap函數(shù)中處理時鐘中斷的部分宁舰,使操作系統(tǒng)每遇到100次時鐘中斷后拼卵,調(diào)用print_ticks子程序,向屏幕上打印一行文字”100 ticks”蛮艰。
    case IRQ_OFFSET + IRQ_TIMER:
        ticks++;
        if (ticks== TICK_NUM) {
            ticks= 0;
            print_ticks();
        }
        break;

Challenge1

  • Note:極大的參考了答案腋腮,還沒有完全理解。正試圖將syscall相關(guān)的部分寫進(jìn)去壤蚜。
  • lab1_switch_to_userlab1_switch_to_kernel中嵌入?yún)R編即寡,調(diào)用T_SWITCH_TOUT_SWITCH_TOK兩種中斷。
    static void
    lab1_switch_to_user(void) {
      //LAB1 CHALLENGE 1 : TODO
      cprintf("1");
      asm volatile (
          "sub $0x8, %%esp \n"
          "int %0 \n"
          "movl %%ebp, %%esp":: "i"(T_SWITCH_TOU)
      );
      cprintf("1");
    }
    
    static void
    lab1_switch_to_kernel(void) {
      //LAB1 CHALLENGE 1 :  TODO
      asm volatile (
          "int %0 \n"
          "movl %%ebp, %%esp" :: "i"(T_SWITCH_TOK)
      );
    }
    
  • 發(fā)生中斷后袜刷,在中斷處理程序中需要修改段選擇子聪富,并在棧中保存原來的段選擇子。
      case T_SWITCH_TOU:
          if (tf->tf_cs != USER_CS) {
              k2u = *tf;
              k2u.tf_cs = USER_CS;
              k2u.tf_ds = USER_DS;
              k2u.tf_es = USER_DS;
              k2u.tf_ss = USER_DS;
              k2u.tf_esp = (uint32_t)tf + sizeof(struct trapframe) - 8;
              k2u.tf_eflags |= FL_IOPL_MASK;
              *((uint32_t *)tf - 1) = (uint32_t)&k2u;
          }
          break;
      case T_SWITCH_TOK:
          if (tf->tf_cs != KERNEL_CS) {
              tf->tf_cs = KERNEL_CS;
              tf->tf_ds = KERNEL_DS;
              tf->tf_es = KERNEL_DS;
              tf->tf_eflags &= ~FL_IOPL_MASK;
              u2k = (struct trapframe *)(tf->tf_esp - (sizeof(struct trapframe) - 8));
              memmove(u2k, tf, sizeof(struct trapframe) - 8);
              *((uint32_t *)tf - 1) = (uint32_t)u2k;
          }
          break;
    

與參考答案的區(qū)別

  • 練習(xí)五:最開始使用的是內(nèi)嵌匯編的形式獲得對應(yīng)地址的值著蟹,但是由于發(fā)現(xiàn)答案中的方法更好墩蔓,因此改為了使用指針來獲取值。
  • 練習(xí)六:與答案類似萧豆,但是自己寫的钢拧。沒有理解為什么前32個idt的is_trap也設(shè)為0。
  • Challenge:參考了答案炕横。

所覆蓋知識點(diǎn)

  • BIOS啟動過程
  • bootloader啟動過程
  • 保護(hù)模式和分段機(jī)制
  • 硬盤訪問
  • 操作系統(tǒng)啟動過程
  • 函數(shù)堆棧
  • 中斷和異常

思考

在實(shí)驗(yàn)過程中源内,我發(fā)現(xiàn)我對OS的理解不夠透徹。最重要的是份殿,我沒有完全分清哪些由軟件操作膜钓,哪些由硬件操作,這給我的實(shí)驗(yàn)造成了一些困難卿嘲。所幸最后在閱讀匯編代碼的時候大致理清了思路颂斜。在接下來的學(xué)習(xí)中,我需要更深入的理解理論拾枣,才能更順利地進(jìn)行接下來的實(shí)驗(yàn)沃疮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市梅肤,隨后出現(xiàn)的幾起案子司蔬,更是在濱河造成了極大的恐慌,老刑警劉巖姨蝴,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俊啼,死亡現(xiàn)場離奇詭異,居然都是意外死亡左医,警方通過查閱死者的電腦和手機(jī)授帕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門同木,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人跛十,你說我怎么就攤上這事彤路。” “怎么了芥映?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵洲尊,是天一觀的道長。 經(jīng)常有香客問我屏轰,道長,這世上最難降的妖魔是什么朋腋? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任诫舅,我火速辦了婚禮咧擂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唁盏。我一直安慰自己,他們只是感情好检眯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布厘擂。 她就那樣靜靜地躺著,像睡著了一般锰瘸。 火紅的嫁衣襯著肌膚如雪刽严。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天避凝,我揣著相機(jī)與錄音舞萄,去河邊找鬼。 笑死管削,一個胖子當(dāng)著我的面吹牛倒脓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播含思,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼崎弃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了含潘?” 一聲冷哼從身側(cè)響起饲做,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎遏弱,沒想到半個月后艇炎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腾窝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年缀踪,在試婚紗的時候發(fā)現(xiàn)自己被綠了居砖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡驴娃,死狀恐怖奏候,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唇敞,我是刑警寧澤蔗草,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站疆柔,受9級特大地震影響咒精,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜旷档,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一模叙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鞋屈,春花似錦范咨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至权旷,卻和暖如春替蛉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拄氯。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工灭返, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坤邪。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓熙含,卻偏偏與公主長得像,于是被迫代替她去往敵國和親艇纺。 傳聞我的和親對象是個殘疾皇子怎静,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354