1.linux 0.11-boot/setup.s

吳建興
13 min readMar 16, 2019

--

HELLO!

因為有幸選了交大OSDI這堂課,所以來紀錄一下trace code的歷程。假如有人能指出錯誤的話,希望能回應一下,非常感謝< _ _ >。

其實應該從boot/bootsect.s開始寫的(或甚至從BIOS開始寫),但是因為在trace boot/bootsect.s的時候沒有想到要紀錄一下,所以就這樣吧~

TRACE CODE

  1. 利用BIOS提供的中斷服務程式,分析核心執行所需的機器系統資料
....equ INITSEG, 0x9000
...
# ok, the read went well so we get current cursor position and save it for
# posterity.
mov $INITSEG, %ax
mov %ax, %ds
mov $0x3, %ah
xor %bh, %bh
int $0x10
mov %dx, %ds:0//將dx放到0x90000(0x9000:0)的地方。
....

int 0x10 , ah=0x3所使用的服務是取得游標位置,return的值詳細可以看refernece [3],這裡將dx(dh是row, dl是column)放到0x90000(0x9000:0)的地方。

...# Get memory size (extended mem, kB)
mov $0x88, %ah
int $0x15
mov %ax, %ds:2
...

int 0x15 , ah=0x88 ,獲取擴充記憶體的大小,從1MB開始的擴充記憶體數值(KB)

...# Get video-card data
mov $0x0f, %ah
int $0x10
mov %bx, %ds:4 # bh = display page
mov %ax, %ds:6 # al = video mode, ah = window width
...

int 0x10 , ah==0x0f ,Get current video mode。

bh:顯示頁面

al:顯示模式

ah:字元列數

...# check for EGA/VGA and some config parameters
mov $0x12, %ah
mov $0x10, %bl
int $0x10
mov %ax, %ds:8
mov %bx, %ds:10
mov %cx, %ds:12
...

int 0x12 , ah==0x12, bl==0x10, 附加功能選擇資訊

ax==??

bh==顯示狀態。0x00=>彩色模式,I/O埠=>0x3dX。0x01=>單色模式,I/O埠=>0x3bX。

bl==安裝的顯示卡記憶體。(0x00–64k, 0x01–128k, 0x02–192k, 0x03–256k)

...
# Get hd0 data
mov $0x0000, %ax
mov %ax, %ds
lds %ds:4*0x41, %si #取中斷向量0x41的值(一個中斷向量在16bit,也就是real mode的時候是4bytes,所以在記憶體從0開始,取第0x41個entry就是取中斷向量0x41的值。而這時候這裡存的值是hd0參數表)
mov $INITSEG, %ax
mov %ax, %es
#這裡開始是一個迴圈,從ds:si的東西搬到es:di,因為cx值設為0x10,所以會搬16bytes
mov $0x0080, %di
mov $0x10, %cx
rep
movsb
...

把hd0的參數表從4*0x41(ds==0, si==4*0x41, ds:si)搬到0x90080(es==0x9000, di==0x80, es:di)的位址。

...
# Get hd1 data
mov $0x0000, %ax
mov %ax, %ds
lds %ds:4*0x46, %si
mov $INITSEG, %ax
mov %ax, %es
mov $0x0090, %di
mov $0x10, %cx
rep
movsb
...

做的事情跟上面很像,把hd1的參數表從4*0x41(ds==0, si==4*0x46, ds:si)搬到0x90090(es==0x9000, di==0x90, es:di)的位址。

# Check that there IS a hd1 :-)        mov     $0x01500, %ax
mov $0x81, %dl
int $0x13
jc no_disk1
cmp $3, %ah
je is_disk1
no_disk1:
mov $INITSEG, %ax
mov %ax, %es
mov $0x0090, %di
mov $0x10, %cx
mov $0x00, %ax
#一個迴圈,從(es==0x9000, di==0x90, es:di)的地方,給0x10(cx)次al的值
rep
stosb
is_disk1:

int 0x13,ah==0x15,這功能是取磁碟的類型。

dl=0x81(代表第幾個硬碟,0x80代表第一個,0x81代表第二個)

回傳值是ah(0x00代表沒有這個磁碟。0x01代表是軟碟機,沒有change-line的支持。0x02代表是軟碟機,有change-line的支持。0x03代表是硬碟。)

當int 0x13, ah==0x15執行失敗的時候,carry flag會等於1,這時候就會jc到no_disk1,把剛剛0x90090的地方清空(默認為沒有hd1)。

當int 0x13, ah==0x15 的回傳值不是3的時候,表示沒有hd1,一樣會走到下面no_disk1的地方,把0x90090清空。

當int 0x13, ah==0x15 的回傳值是3,表示有hd1,跳到下面is_disk1執行。

jc: jump if carry flag is set

je: jump if equal

stosb:將 AL 存放到 ES:DI 的位置

2.關中斷,移動system

# now we want to move to protected mode ...cli                            # no interrupts allowed !# first we move the system to it's rightful placemov     $0x0000, %ax
cld # 'direction'=0, movs moves forward
do_move:
mov %ax, %es # destination segment
add $0x1000, %ax
cmp $0x9000, %ax
jz end_move
mov %ax, %ds # source segment
sub %di, %di
sub %si, %si
mov $0x8000, %cx
#ax=0x1000, ds=0x1000, si=0, es=0, di=0
rep
#從ds:si的東西搬到es:di,一次搬一個word(2 bytes),每次會把si, di的值各加二
movsw
jmp do_move

移動的過程中,會把BIOS的中斷向量表(interrupt vector table)給蓋過去,所以這時後是沒辦法進行中斷的,要把中斷關掉(cli)。

因為目前假設system不會大於0x80000(512KB),所以移動的總量就是0x80000。

1輪移動0x10000(0x8000*2, 64KB),總共移動8輪(0x80000, 512KB)

jz: jump if zero
cld: 原本每移動一次si, di各加1,設cld後會變成si, di各減1
cmp: subtracts its two operands, and sets flags accordingly
movsw:原本只能執行一次,但只要加了rep前綴,就可以執行cx次。

3.設定IDT與GDT

...
.equ SETUPSEG, 0x9020 #this is the current segment
...
end_move:
mov $SETUPSEG, %ax # right, forgot this at first. didn't work :-)
mov %ax, %ds
lidt idt_48 # load idt with 0,0
lgdt gdt_48 # load gdt with whatever appropriate
...#每個entry是8 bytes(4 words)gdt:
.word 0,0,0,0 # dummy,第一個entry先不用
#內核代碼段
.word 0x07FF # 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 # base address=0
.word 0x9A00 # code read/exec
.word 0x00C0 # granularity=4096, 386
#內核資料段
.word 0x07FF # 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 # base address=0
.word 0x9200 # data read/write
.word 0x00C0 # granularity=4096, 386
#.word 0:idt的長度限制是0 #.word 0,0:idt的基底位址也是0,這裡只是先標示出位址,還不需要中斷機制
idt_48:
.word 0 # idt limit=0
.word 0,0 # idt base=0L
#.word 0x800:指的是gdt表的長度,總共2048個bytes,每個entry需要8 bytes,所以總共是256個GDT entries。
#.word 512+gdt, 0x9:512==0x200,0x90000+0x200就是setup的起始點。而這裡的gdt代表的是gdt這個label在setup裡面的偏移量。0x90000+0x200+gdt offset,就是gdt在memory裡的位址。
gdt_48:
.word 0x800 # gdt limit=2048, 256 GDT entries
.word 512+gdt, 0x9 # gdt base = 0X9xxxx,
# 512+gdt is the real gdt after setup is moved to 0x9020 * 0x10

lidt(load IDTR): 把東西load進IDTR,一次load 48 bytes。
lgdt(load GDTR): 把東西load進GDTR,一次load 48 bytes。

4.開啟A20

# that was painless, now we enable A20        #call   empty_8042      # 8042 is the keyboard controller
#mov $0xD1, %al # command write
#out %al, $0x64
#call empty_8042
#mov $0xDF, %al # A20 on
#out %al, $0x60
#call empty_8042
inb $0x92, %al # open A20 line(Fast Gate A20).
orb $0b00000010, %al
outb %al, $0x92

開啟A20,表示CPU可以開始32bits的定址,最大的定址空間是4GB。

5.為在保護模式下執行head.s做準備

# well, that went ok, I hope. Now we have to reprogram the interrupts :-(
# we put them right after the intel-reserved hardware interrupts, at
# int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
# messed this up with the original PC, and they haven't been able to
# rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
# which is used for the internal hardware interrupts as well. We just
# have to reprogram the 8259's, and it isn't fun.
mov $0x11, %al # initialization sequence(ICW1)
# ICW4 needed(1),CASCADE mode,Level-triggered
out %al, $0x20 # send it to 8259A-1
.word 0x00eb,0x00eb # jmp $+2, jmp $+2
out %al, $0xA0 # and to 8259A-2
.word 0x00eb,0x00eb
mov $0x20, %al # start of hardware int's (0x20)(ICW2)
out %al, $0x21 # from 0x20-0x27
.word 0x00eb,0x00eb
mov $0x28, %al # start of hardware int's 2 (0x28)
out %al, $0xA1 # from 0x28-0x2F
.word 0x00eb,0x00eb # IR 7654 3210
mov $0x04, %al # 8259-1 is master(0000 0100) --\
out %al, $0x21 # |
.word 0x00eb,0x00eb # INT /
mov $0x02, %al # 8259-2 is slave( 010 --> 2)
out %al, $0xA1
.word 0x00eb,0x00eb
mov $0x01, %al # 8086 mode for both
out %al, $0x21
.word 0x00eb,0x00eb
out %al, $0xA1
.word 0x00eb,0x00eb
mov $0xFF, %al # mask off all interrupts for now
out %al, $0x21
.word 0x00eb,0x00eb
out %al, $0xA1

對8259A進行重新程式設計。

TERM

真實模式(real mode)

保護模式(protected mode)

中斷服務程式(Interrupt Service)

中斷向量表(Interrupt Vector Table)

GDT(Global Descriptor Table, 全局描述符號表)

LDT(Local Descriptor Table, 局部描述符號表)

TSS(Task Structure Segment, 工作狀態區段)

GDTR(Global Descriptor Table Register, GDT基底位址暫存器)

IDT(Interrupt Descriptor Table, 中斷描述符號表)

IDTR(Interrupt Descriptor Table Register, IDT基底位址暫存器)

A20

REFERENCE

[1]Linux Kernel 設計的藝術

[2]Linux Kernel 完全剖析

[3]INT 10H https://en.wikipedia.org/wiki/INT_10H

[4]INT 0x15, AH=0x88 https://www.google.com/search?client=ubuntu&hs=rPs&channel=fs&ei=qs2LXKa2O4Tm8wXfwYDIBA&q=int+0x15+ah%3D0x88&oq=int+0x15+ah%3D0x88&gs_l=psy-ab.3...4300.10159..10400...4.0..0.79.645.12......0....1..gws-wiz.......0i19j0i30i19j0i5i30i19j33i160.buvDU3ACKkc

[5]INT 0x15, AH=0x88 http://www.delorie.com/djgpp/doc/rbinter/ix/15/88.html

[6]讀setup.s http://www.voidcn.com/article/p-kqisgnmi-vp.html

[7]int 0x13 ah=0x15 https://www.datadoctor.biz/data_recovery_programming_book_chapter8-page36.html

QUESTION

[1]BIOS的中斷,會因為BIOS版本或是CPU版本的不同而不同嗎?

To Do List & Future Work

陸續補完TERM,A20,與8259A控制器的部份。

--

--

吳建興
吳建興

Written by 吳建興

I want to be a good programmer.(´・ω・`)

No responses yet