linux0.11內(nèi)核分析-setup

章節(jié)目錄
上一節(jié)講解了bootsect,由bootsect加載setup進入內(nèi)存,最后jmpi 0,SETUPSEG跳轉(zhuǎn)到setup程序處。

INITSEG  = 0x9000   ! we move boot here - out of the way
SYSSEG   = 0x1000   ! system loaded at 0x10000 (65536).
SETUPSEG = 0x9020   ! this is the current segment
    mov ax,#INITSEG ! this is done in bootsect already, but...
    mov ds,ax
    mov ah,#0x03    ! read cursor pos
    xor bh,bh
    int 0x10        ! save it in known place, con_init fetches
    mov [0],dx      ! it from 0x90000.

先把ds設(shè)置成 0x9000,這個段地址在加載bootsect時候講過,就是bootsect從0x07c00地址復制到 0x90000地址,
bios 10號中斷,功能ah=0x03表示讀取光標,
輸入?yún)?shù)是bh,
返回參數(shù)ch=掃描開始線,cl=掃描結(jié)束線
dh=行號,dl=列號
mov [0],dx如同mov ds:[0],dx,保存了行號和列號,也就是說0x90000處保存了行號和列號。

mov ah,#0x88
int 0x15
mov [2],ax

bios 15號中斷,功能號ah=0x88表示獲取擴展內(nèi)存的大?。↘B)
返回參數(shù):ax=從0x100000(1MB)處開始的擴展內(nèi)存大?。↘B)
如果出錯則cf=1,ax=出錯碼


    mov ah,#0x0f
    int 0x10
    mov [4],bx      ! bh = display page
    mov [6],ax      ! al = video mode, ah = window width

bios 10號中斷,功能號ah=0x0f表示獲取顯卡顯示模式
返回參數(shù):ah=字符列數(shù),al=顯示模式,hb=當前顯示頁
0x9000:0x0004存放當前顯示頁
0x9000:0x0006存放顯示模式
0x9000:0x0007存放字符列數(shù)

    mov ah,#0x12
    mov bl,#0x10
    int 0x10
    mov [8],ax
    mov [10],bx
    mov [12],cx

檢查顯示方式(EGA/VGA)并獲取參數(shù)
bios 10號中斷
ah=0x12 ,bl = 0x10

返回參數(shù):
bh=顯示狀態(tài), 0x00-彩色模式,I/O端口=0x3dX,0x01-單色模式,I/O端口=0x3bX
bl = 安裝的顯示內(nèi)存,0x00-64K,0x01-128K,0x02-192K,0x03-256K
cx=顯卡特性參數(shù)

程序執(zhí)行后
0x9000:0x000A = 安裝的顯示內(nèi)存
0x9000:0x000B = 顯示狀態(tài)
0x9000:0x000C = 顯卡特性參數(shù)

! Get hd0 data

    mov ax,#0x0000
    mov ds,ax
    lds si,[4*0x41]
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0080
    mov cx,#0x10
    rep
    movsb

獲取第一個硬盤的信息,這里用到41號中斷。
bios中斷向量表中的每個中斷向量大小是4字節(jié)。這4字節(jié)描述了一個中斷處理例程(程序)的段基址和段內(nèi)偏移地址。因為中斷向量表的長度為1024字節(jié),故該表最多容納256個中斷向量處理程序。計算機啟動之初,中斷向量表中的中斷例程是由BIOS建立的,它從物理內(nèi)存地址0x0000處初始化并在中斷向量表中添加各種處理例程。
[4*0x41]這個就表示41號中斷的物理偏移地址,段地址就是ds。
lds si,[4*0x41] 取4個字節(jié),前2個字節(jié)放在si寄存器中,后2個字節(jié)放在ds寄存器中。
rep movsb前一節(jié)已經(jīng)講過類似的,這里不再詳說。
movsb的功能是將ds:si指向的內(nèi)存單元中的字節(jié)送入es:di中,注意這里是字節(jié),這樣就是把ds:si地址的數(shù)據(jù)復制到0x9000:0x0080地址,復制10個字節(jié)數(shù)據(jù)。

! Check that there IS a hd1 :-)

    mov ax,#0x01500
    mov dl,#0x81
    int 0x13
    jc  no_disk1
    cmp ah,#3
    je  is_disk1
no_disk1:
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0090
    mov cx,#0x10
    mov ax,#0x00
    rep
    stosb

檢查系統(tǒng)是否存在第2個硬盤,如果不存在則第2個表清0。
利用13號中斷
功能號:ah = 0x15
輸入:dl = 驅(qū)動號(0x8X是硬盤:0x80指第1個硬盤,0x81第2個硬盤)
輸出:ah=類型碼,00-沒有這個盤,CF置位;01-軟驅(qū),沒有change-line支持,03-軟驅(qū)(或者其他可移動設(shè)備),有change-line支持;03-硬盤。

is_disk1:

! now we want to move to protected mode ...

    cli         ! no interrupts allowed !

! first we move the system to it's rightful place

    mov ax,#0x0000
    cld         ! 'direction'=0, movs moves forward
do_move:
    mov es,ax       ! destination segment
    add ax,#0x1000
    cmp ax,#0x9000
    jz  end_move
    mov ds,ax       ! source segment
    sub di,di
    sub si,si
    mov     cx,#0x8000
    rep
    movsw
    jmp do_move

執(zhí)行到標號is_disk1處,就要進入保護模式了。
cli關(guān)中斷,此時不再響應(yīng)中斷信號,因為要把system復制到0x0000位置,會覆蓋bios中斷。
cld cld用來操作方向標志位DF(Direction Flag)。cld使DF 復位,即是讓DF=0,std使DF置位,即DF=1.這兩個指令用于串操作指令中。通過執(zhí)行cld或std指令可以控制方向標志DF,決定內(nèi)存地址是增大(DF=0,向高地址增加)還是減?。―F=1,向地地址減小)。

在實模式下,尋址一個內(nèi)存地址主要是使用段和偏移值,段值被存放在段寄存器中,并且段的最大長度被固定為64KB,段內(nèi)偏移地址存放在任意一個可用尋址的寄存器中,因此根據(jù)段寄存器和偏移寄存器中的值,就可以算出實際內(nèi)存的地址。

實模式下尋址

在保護模式下,段寄存器中存放的不再是尋址段的基地址,而是一個一個索引,稱為段選擇符,由段選擇符從全局描述符表或者局部描述符表中找到8個字節(jié)長的段描述符,從而確定關(guān)于這個段的全部描述信息。


保護模式下尋址
段選擇符

RPL(RequestedPrivilege Level): 請求特權(quán)級,表示將要訪問的特權(quán)級,取值范圍0~3。
TI(TableIndicator): TI=0指示從全局描述符表GDT中讀取描述符;TI=1指示從局部描述符表LDT中讀取描述符。
index: 索引,指出要訪問描述符在段描述符表中的順序號,index占13位,因為順序號的范圍是0~8191,每個段描述符表中最多有8192個描述符。
有一個特殊的選擇子稱為空(Null)選擇子,它的Index=0,TI=0,而RPL字段可以為任意值??者x擇子有特定的用途,當用空選擇子進行存儲訪問時會引起異常。空選擇子是特別定義的,它不對應(yīng)于全局描述符表GDT中的第0個描述符,因此處理器中的第0個描述符總不被處理器訪問,一般把它置成全0。但當TI=1時,Index為0的選擇子不是空選擇子,它指定了當前任務(wù)局部描述符表LDT中的第0個描述符。

因此。在進入保護模式之前,必須首先設(shè)置好將要用到的段描述符,然后使用指令lgdt把描述符表的基地址告知CPU(gdt表的基地址存入gdtr寄存器),再將機器狀態(tài)字的保護模式標志置位即可進入32位保護運行模式。

end_move:
    mov ax,#SETUPSEG    ! right, forgot this at first. didn't work :-)
    mov ds,ax
    !idt剛被加載,一條數(shù)據(jù)也沒有
    lidt    idt_48      ! load idt with 0,0
    lgdt    gdt_48      ! load gdt with whatever appropriate

! that was painless, now we enable A20

    call    empty_8042
    mov al,#0xD1        ! command write
    out #0x64,al
    call    empty_8042
    mov al,#0xDF        ! A20 on
    out #0x60,al
    call    empty_8042

IDTR(中斷描述符表寄存器)和IDT(中斷描述符表)的關(guān)系

圖片來源英特爾? 64 位和 IA-32 架構(gòu)開發(fā)人員手冊:卷 3A 第196頁
idt limit是描述符表的長度值(字節(jié))。
idt base Address 是描述符表的32位線性基地址。
中斷描述符表中的每一個表項(8字節(jié))指出發(fā)生中斷時需要調(diào)用的代碼信息,與中斷向量有些相似,但要包含更多的信息。

圖片來源英特爾? 64 位和 IA-32 架構(gòu)開發(fā)人員手冊:卷 3A 第103頁

idt_48:
    .word   0           ! idt limit=0
    .word   0,0         ! idt base=0L
gdt_48:
    .word   0x800       ! gdt limit=2048, 256 GDT entries
    .word   512+gdt,0x9 ! gdt base = 0X9xxxx

此時此刻內(nèi)核尚未真正運行起來,還沒有進程,所以現(xiàn)在創(chuàng)建的GDT表的第一項為空,第二項為內(nèi)核代碼段描述符,第三項為內(nèi)核數(shù)據(jù)段描述符,其余項皆為空。
idt表雖然已經(jīng)設(shè)置,實為一張空表,原因是目前已經(jīng)關(guān)中斷,無需調(diào)用中斷服務(wù)程序。

打開A20,意味著CPU可以進行32位尋址,最大尋址空間為4GB。
CPU 在保護模式下,int 0x00~0x1F 被 Intel 保留作為內(nèi)部(不可屏蔽)中斷和異常中斷。如果不對 8259A 進行重新編程,int 0x00~0x1F 將被覆蓋。例如,IRQ0 (時鐘中斷)為 8 號(int 0x08)中斷,但在保護模式下此中斷號是 Intel 保留的“Double Fault”(雙重故障)。因此,必須對 8259A 編程將原來的 IRQ0x00~IRQ0x0F 對應(yīng)的中斷號重新分布,即在保護模式下,IRQ0x00~IRQ0x0F 的中斷號是 int 0x20~int 0x2F。

mov al,#0x11        ! initialization sequence
   out #0x20,al        ! send it to 8259A-1
   .word   0x00eb,0x00eb       ! jmp $+2, jmp $+2
   out #0xA0,al        ! and to 8259A-2
   .word   0x00eb,0x00eb
   mov al,#0x20        ! start of hardware int's (0x20)
   out #0x21,al
   .word   0x00eb,0x00eb
   mov al,#0x28        ! start of hardware int's 2 (0x28)
   out #0xA1,al
   .word   0x00eb,0x00eb
   mov al,#0x04        ! 8259-1 is master
   out #0x21,al
   .word   0x00eb,0x00eb
   mov al,#0x02        ! 8259-2 is slave
   out #0xA1,al
   .word   0x00eb,0x00eb
   mov al,#0x01        ! 8086 mode for both
   out #0x21,al
   .word   0x00eb,0x00eb
   out #0xA1,al
   .word   0x00eb,0x00eb
   mov al,#0xFF        ! mask off all interrupts for now
   out #0x21,al
   .word   0x00eb,0x00eb
   out #0xA1,al
! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
! need no steenking BIOS anyway (except for the initial loading :-).
! The BIOS-routine wants lots of unnecessary data, and it's less
! "interesting" anyway. This is how REAL programmers do it.
!
! Well, now's the time to actually move into protected mode. To make
! things as simple as possible, we do no register set-up or anything,
! we let the gnu-compiled 32-bit programs do that. We just jump to
! absolute address 0x00000, in 32-bit protected mode.
    mov ax,#0x0001  ! protected mode (PE) bit
    lmsw    ax      ! This is it!
    jmpi    0,8     ! jmp offset 0 of segment 8 (cs)

這個時候,正式進入保護模式,CR0控制寄存器的PE置位。
段寄存器cs的值是8,不過此時不再是基地址,而是保護模式下的段選擇符,段選擇符是16位的數(shù)據(jù),8用二進制表示就是0b0000 0000 0000 1000,第0到1位表示特權(quán)級別,第2位的0表示使用全局描述符表,第3位的1表示索引,所以jmpi 0,8就是請求特權(quán)級0,使用全局描述符表中的第一項,由于上面我們設(shè)置過gdt,這個基地址就是0,也就是跳轉(zhuǎn)去執(zhí)行system中的代碼,system的頭部就是head,下一節(jié)繼續(xù)講解head.s


圖片來源英特爾? 64 位和 IA-32 架構(gòu)開發(fā)人員手冊:卷 3A 第76頁

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容