千家信息网

操作系统--主引导程序控制权的转移

发表于:2025-01-24 作者:千家信息网编辑
千家信息网最后更新 2025年01月24日,一.主引导程序控制权的转移首先需要了解的是BootLoader内存布局,在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件
千家信息网最后更新 2025年01月24日操作系统--主引导程序控制权的转移

一.主引导程序控制权的转移

首先需要了解的是BootLoader内存布局,在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。在一个基于ARM7TDMI core的嵌入式系统中,系统在上电或复位时通常都从地址0x00000000处开始执行,而在这个地址处安排的通常就是系统的BootLoader程序。


0x7c00主引导程序的起始地址,之前为栈空间,主要是函数调用。,最终主引导程序是从boot程序跳转到0x9000loader,中间部分为Fat表,占用4个字节
1.通过FAT表加载文件内容--流程图

实验步骤
1.在虚拟软盘中创建体积较大的文本文件(Loader)
2.将Loader的内容加载到BaseOfLoader地址处
3.打印Loader中的文本(判断加载是否完全)

org 0x7c00jmp short startnopdefine:    BaseOfStack      equ 0x7c00    BaseOfLoader     equ 0x9000    RootEntryOffset  equ 19    RootEntryLength  equ 14    EntryItemLength  equ 32    FatEntryOffset   equ 1    FatEntryLength   equ 9header:    BS_OEMName     db "D.T.Soft"    BPB_BytsPerSec dw 512    BPB_SecPerClus db 1    BPB_RsvdSecCnt dw 1    BPB_NumFATs    db 2    BPB_RootEntCnt dw 224    BPB_TotSec16   dw 2880    BPB_Media      db 0xF0    BPB_FATSz16    dw 9    BPB_SecPerTrk  dw 18    BPB_NumHeads   dw 2    BPB_HiddSec    dd 0    BPB_TotSec32   dd 0    BS_DrvNum      db 0    BS_Reserved1   db 0    BS_BootSig     db 0x29    BS_VolID       dd 0    BS_VolLab      db "D.T.OS-0.01"    BS_FileSysType db "FAT12   "start:    mov ax, cs    mov ss, ax    mov ds, ax    mov es, ax    mov sp, BaseOfStack    mov ax, RootEntryOffset    mov cx, RootEntryLength    mov bx, Buf    call ReadSector    mov si, Target    mov cx, TarLen    mov dx, 0    call FindEntry    cmp dx, 0    jz output    mov si, bx    mov di, EntryItem    mov cx, EntryItemLength    call MemCpy    mov ax, FatEntryLength    mov cx, [BPB_BytsPerSec]    mul cx    mov bx, BaseOfLoader    sub bx, ax    mov ax, FatEntryOffset    mov cx, FatEntryLength    call ReadSector    mov cx, [EntryItem + 0x1A]    mov si, BaseOfLoaderloading:    mov ax, dx    add ax, 31    mov cx, 1    push dx    push bx    mov bx, si    call ReadSector    pop bx    pop cx    call FatVec    cmp dx, 0xFF7    jnb BaseOfLoader    add si, 512    jmp loadingoutput:     mov bp, MsgStr    mov cx, MsgLen    call Printlast:    hlt    jmp last    ; cx --> index; bx --> fat table address;; return:;     dx --> fat[index]FatVec:    mov ax, cx    mov cl, 2    div cl    push ax    mov ah, 0    mov cx, 3    mul cx    mov cx, ax    pop ax    cmp ah, 0    jz even    jmp oddeven:    ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i];    mov dx, cx    add dx, 1    add dx, bx    mov bp, dx    mov dl, byte [bp]    and dl, 0x0F    shl dx, 8    add cx, bx    mov bp, cx    or  dl, byte [bp]    jmp returnodd:     ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F );    mov dx, cx    add dx, 2    add dx, bx    mov bp, dx    mov dl, byte [bp]    mov dh, 0    shl dx, 4    add cx, 1    add cx, bx    mov bp, cx    mov cl, byte [bp]    shr cl, 4    and cl, 0x0F    mov ch, 0    or  dx, cxreturn:     ret; ds:si --> source; es:di --> destination; cx    --> lengthMemCpy:    push si    push di    push cx    push ax    cmp si, di    ja btoe    add si, cx    add di, cx    dec si    dec di    jmp etobbtoe:    cmp cx, 0    jz done    mov al, [si]    mov byte [di], al    inc si    inc di    dec cx    jmp btoeetob:     cmp cx, 0    jz done    mov al, [si]    mov byte [di], al    dec si    dec di    dec cx    jmp etobdone:       pop ax    pop cx    pop di    pop si    ret; es:bx --> root entry offset address; ds:si --> target string; cx    --> target length;; return:;     (dx !=0 ) ? exist : noexist;        exist --> bx is the target entryFindEntry:    push di    push bp    push cx    mov dx, [BPB_RootEntCnt]    mov bp, spfind:    cmp dx, 0    jz noexist    mov di, bx    mov cx, [bp]    call MemCmp    cmp cx, 0    jz exist    add bx, 32    dec dx    jmp findexist:noexist:     pop cx    pop bp    pop di    ret; ds:si --> source; es:di --> destination; cx    --> length;; return:;        (cx == 0) ? equal : noequalMemCmp:    push si    push di    push axcompare:    cmp cx, 0    jz equal    mov al, [si]    cmp al, byte [di]    jz goon    jmp noequalgoon:    inc si    inc di    dec cx    jmp compareequal: noequal:       pop ax    pop di    pop si    ret; es:bp --> string address; cx    --> string lengthPrint:    mov dx, 0    mov ax, 0x1301    mov bx, 0x0007    int 0x10    ret; no parameterResetFloppy:    push ax    push dx    mov ah, 0x00    mov dl, [BS_DrvNum]    int 0x13    pop dx    pop ax    ret; ax    --> logic sector number; cx    --> number of sector; es:bx --> target addressReadSector:    push bx    push cx    push dx    push ax    call ResetFloppy    push bx    push cx    mov bl, [BPB_SecPerTrk]    div bl    mov cl, ah    add cl, 1    mov ch, al    shr ch, 1    mov dh, al    and dh, 1    mov dl, [BS_DrvNum]    pop ax    pop bx    mov ah, 0x02read:        int 0x13    jc read    pop ax    pop dx    pop cx    pop bx    retMsgStr db  "No LOADER ..."  MsgLen equ ($-MsgStr)Target db  "LOADER     "TarLen equ ($-Target)EntryItem times EntryItemLength db 0x00Buf:    times 510-($-$$) db 0x00    db 0x55, 0xaa

对此进行make的结果

从该结果可以看出,TIME的值为负数了,说明主引导程序的大小大于了512,我们需要将其减小,将之前不重要的入栈与出栈的操作进行删除,以免占用空间,那么我们之前为何要这样做呢?是为了遵守汇编代码的约定,有操作相关寄存器的值就要进行入栈出栈操作。那么我们这块内存已经不够,因此没必要进行这个操作了。我们将下面的入栈出栈操作进行删除,但是要在 FindEntry 这个函数保留 cx 寄存器的入栈出栈的操作,原因是下面不停在改变 cx 寄存器的值。我们在 find 操作中,call MemCmp 操作前后有必要再加上 si 寄存器的入栈出栈操作
将其改正后的make以及在bochs上实现的结果为会打印loader中的字符串内容

B.第一个loader程序
1.起始地址0x9000
2.通过int0x10在屏幕上打印字符串


a.零标志位--判断运算的结果是否为0,当运算结果为0时,ZF位的值为1
b.同时jxx代表了一个指令族,功能是根据标志位进行调整
jo当OF为1则跳转,jc当CF为1则跳转,jns当SF不为1则跳转,jz当ZF为1则跳转,je比较结果为相等则跳转
loader.asm代码实现

org 0x9000begin:    mov si, msgprint:    mov al, [si]    add si, 1    cmp al, 0x00    je end    mov ah, 0x0E    mov bx, 0x0F    int 0x10    jmp printend:    hlt    jmp endmsg:    db 0x0a, 0x0a    db "Hello, D.T.OS!"    db 0x0a, 0x0a    db 0x00

将loader.asm进行反编译得出的结果

可以看到在这里的jz对应的是loader.asm中的je命令
接下来将loader拷贝到软盘中去,然后从Boot跳转到loader进行执行,我们将虚拟软盘先在linux中进行挂载,然后进行拷贝,最后进行运行

从打印的结果可以看出,控制权从boot已经转移到loader程序了
将其打印结果进行修改看在bochs上的实现结果是否也修改了

在这里我们需要将makefile文件进行修改保证后期的运行简便

.PHONY : all clean rebuildBOOT_SRC := boot.asmBOOT_OUT := bootLOADER_SRC := loader.asmLOADER_OUT := loaderIMG := data.imgIMG_PATH := /mnt/hgfsRM := rm -frall : $(IMG) $(BOOT_OUT) $(LOADER_OUT)    @echo "Build Success ==> D.T.OS!"$(IMG) :    bximage $@ -q -fd -size=1.44$(BOOT_OUT) : $(BOOT_SRC)    nasm $^ -o $@    dd if=$@ of=$(IMG) bs=512 count=1 conv=notrunc$(LOADER_OUT) : $(LOADER_SRC)    nasm $^ -o $@    sudo mount -o loop $(IMG) $(IMG_PATH)    sudo cp $@ $(IMG_PATH)/$@    sudo umount $(IMG_PATH)clean :    $(RM) $(IMG) $(BOOT_OUT) $(LOADER_OUT)rebuild :    @$(MAKE) clean    @$(MAKE) all

最后将data.img在window下实现

小结
1.Boot需要进行重构保证在512字节内完成功能
2.在汇编程序中尽量确保函数调用前后通用寄存器的状态不变
3.Boot成功加载Loader后将控制权转移
4.Loader程序没有代码体积上的限制

0