冰琥珀 发表于 2015-11-8 09:46:39

自己动手写操作系统系列之----------加载Kernel

本帖最后由 冰琥珀 于 2015-11-8 10:13 编辑

       上一篇文章写了Loader.bin文件的加载,我们也成功的将控制权转交给Loader.bin,接下来就通过它来加载内核,在这里,我们将要完成的只是将内核的数据加载到内存,加载完了之后,我们进行一些其他的处理之后,再在内存中调整内核,为啥要调整内核,因为内核是一个elf文件(就像windows下的pe文件),要使其能够运行,就需要按照elf的文件格式,将各个段映射到内存中,这个我们在加载完内核之后会做处理。也许有人会问,为啥Loader.bin不用做处理,因为Loader.bin我们是将其编译成com文件,所以不需要像elf文件那样按段加载到内存中。现在先来完成内核的加载。
        内核的加载方式和Loader.bin的方式一样,都是在根目录中查找到”KERNEL   BIN”的目录项,然后读取处初始簇号,再根据簇号来加载数据,同时在FAT中查找下一个簇号,如果还有下一个簇号,则继续加载,否则加载结束。流程上跟Loader.bin的加载是一样的,我们先来看代码
org 0x100
       
        jmp                LABEL_START                ;跳转

        BaseOfStack                equ                0x100
        BaseOfKernel        equ                0x8000
        OffsetOfKernel        equ                0
        wRootSecCnt                dw                0
        wRootCurSecNum        dw                0
        wReadOffset                dw                0
        wFileNameCnt        db                0
        bInHighByte                db                0                       
        LoaderFileName        db                'KERNELBIN', 0        ;LOADER.BIN (文件名)
        MessageLength        equ                9
        BootMessage:        db                "Loading"
        Message1:                db                "Ready.   "
        Message2:                db                "No KERNEL"

        %include "Include/FAT12Info.inc"               

LABEL_START:
        mov ax, cs
        mov ds, ax
        mov es, ax
        mov ss, ax
        mov sp, BaseOfStack

        ;显示Loading
        mov dh, 0
        call DispStr

        mov word , RootDirSecNum
        mov word , RootDirSecCnt
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
        cmp word , 0
        jz        LABEL_SEARCH_IN_ROOT_DIR_END        ;这里跳转表示整个根目录都搜索完毕,没有找到Loader.bin

       
        dec word
        mov        ax, BaseOfKernel
        mov es, ax
        mov bx, OffsetOfKernel
        mov ax, word
        mov cl, 1
        call ReadSector                                ;读取扇区数据
        mov di, OffsetOfKernel
        cld
        mov dl, 0x10                                ;每个扇区中目录个数为16

LABEL_CMP_NEXT_FILE_NAME:
        mov si, LoaderFileName
        mov cl, 0x0b                                ;文件名长度为11个字节

LABEL_CMP_FILE_NAME:
        test cl, cl
        jz LABEL_FOUND_LOADER
        dec cl
        lodsb
        test al, al
        jz LABEL_CMP_NAME_END                ;文件名比较完毕
        cmp al,
        jz LABEL_GO_ON                ;从根目录中读取到的文件名字符和目标文件名字符相等,则继续比较下一个字符

        jmp LABEL_CMP_NAME_END

LABEL_GO_ON:
        inc di
        jmp LABEL_CMP_FILE_NAME

LABEL_CMP_NAME_END:
        dec dl
        jz LABEL_GO_TO_NEXT_SECTOR        ;16组目录都比较完毕,则读取下一个扇区
        and di, 0xffe0                                ;这里把di指针重置到当前目录的起始处
        add di, 0x20                                ;指针指向下一组目录(每组目录长度为32个字节)
        jmp        LABEL_CMP_NEXT_FILE_NAME        ;比较下一组文件名

LABEL_GO_TO_NEXT_SECTOR:
        inc word
        jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN

LABEL_SEARCH_IN_ROOT_DIR_END:        ;没找到LOADER.BIN
        mov dh, 2
        call DispStr

        jmp $

LABEL_FOUND_LOADER:                                ;找到LOADER.BIN文件
        xor cx, cx
        and di, 0xffe0                                ;这里把di设置为指向目录项的首地址
        add di, 0x1a                                ;目录项中偏移0x1a处为初始簇号
        mov cx, word                 ;将初始簇号存入cx
        mov word , OffsetOfKernel

LABEL_GO_ON_LOADING:
        push ax
        push bx
        mov ah, 0x0e
        mov al, '.'
        mov bl, 0x0f
        int 0x10
        pop bx
        pop ax

        push cx
        mov ax, cx
        sub ax, 2
        xor cx, cx
        mov cl,
        mul cl
        mov bx, DataOffsetSec
        add ax, bx                                                ;得到当前簇号的数据偏移(单位:扇区)
        push ax
        mov ax, BaseOfKernel
        mov es, ax                                        ;存放数据的段地址
        pop ax
        mov bx, word                 ;存放数据的偏移
        mov cl, 1
        call ReadSector                                ;读取数据
       
        pop ax                                                ;取出当前簇号
        mov bx, 3                                       
        mul bx                                                ;簇号 * 3
        mov bx, 2
        div bx                                                ;再除2,这里得到簇号的偏移(单位:字节)                               
        test dx, dx
        jz LABEL_IN_HIGH_BYTE
        mov byte , 1        ;该簇号的在当前字节的高4位

LABEL_IN_HIGH_BYTE:
        xor dx, dx
        mov bx,
        add word , bx       
        div bx
        inc ax                                                ;FAT       
        push dx
        push ax
        mov ax, BaseOfKernel
        sub ax, 0x100
        mov es, ax                                        ;设置读的位置的段地址
        pop ax                                                ;ax中存放要读取的扇区号
        xor bx, bx
        mov cl, 2                                        ;一次读取2个扇区
        call ReadSector
        pop dx
        add bx, dx
        mov ax, word
        cmp byte , 0                ;看簇号是不是在当前字节的高4位
        jz LABEL_EVEN
        shr ax, 4                                        ;如果是则ax右移4位
LABEL_EVEN:
        and ax, 0xfff
        cmp ax, 0xfff                                ;如果ax的值为0xfff表示文件结束
        jz        LABEL_FILE_LOADED
        mov cx, ax
        jmp LABEL_GO_ON_LOADING

LABEL_FILE_LOADED:
        call KillMotor                ;关闭软驱马达
        mov dh, 1
        call DispStr
        jmp $

ReadSector:
        push bp
        mov bp, sp
        sub sp, 2
        mov byte , cl
        push bx
        mov bl,                        
        div bl                                                ;计算出当前扇区所处的磁道
        inc ah                                                ;除得的商放在al,余数放在ah(al中放的是磁道号,ah中放的是扇区号)
        mov cl, ah
        mov dh, al
        shr al, 1                                        ;磁道号除以2得到磁头号
        mov ch, al
        and ch, 1
        pop bx
        mov dl,
.GoOnReading:
        mov ah, 2                                        ;读扇区
        mov al, byte                 ;要读的扇区数
        int 0x13
        jc .GoOnReading                                ;读扇区失败则继续读
        add sp, 2
        pop bp,
        ret

DispStr:
        mov ax, MessageLength
        mul dh
        add ax, BootMessage
        mov bp, ax
        mov ax, ds
        mov es, ax
        mov cx, MessageLength
        mov ax, 0x1301
        mov bx, 0x0007
        mov dl, 0
        add dh, 3
        int 0x10
        ret

KillMotor:
        push dx
        mov dx, 0x3f2
        mov al, 0
        out dx, al
        pop dx
        ret
        从上面的代码可知,相比于Loader.bin的加载,只是多了KillMotor函数,这个函数的作用就是关闭软驱马达,否则软驱的灯会一直亮着。
        因为这里是内核的加载,所以我们需要一个内核,代码如下

        Message   db"In the Kernel"
        StrLen                equ                $ - Message


        global _start

_start:
        mov edx, StrLen
        mov ecx, Message
        mov ebx, 1
        mov eax, 4
        int 0x80
        mov ebx, 0
        mov eax, 1
        int 0x80
        这个代码很简单,就是显示一个字符串,当然了,上面这个加载内核的程序还不能让内核执行,所以这里不会显示这个字符串。我们看内核加载后的执行结果

wuyan 发表于 2015-11-8 10:32:50

大神出品

若冰 发表于 2015-11-8 13:29:06

学习学习技术,加油!

fireworld 发表于 2015-11-8 21:48:22

感谢楼主的分享~

08-wh 发表于 2015-11-8 22:09:48

感谢楼主的分享~

yusiii 发表于 2015-11-9 11:55:46

Lucifer 发表于 2015-11-10 21:39:54

还是不错的哦,顶了

xiaoqqf4 发表于 2015-11-11 20:19:07

还是不错的哦,顶了

54hacker 发表于 2015-11-11 22:18:01

支持,看起来不错呢!

小路 发表于 2015-11-12 00:07:26

支持中国红客联盟(ihonker.org)
页: [1] 2 3 4 5 6 7
查看完整版本: 自己动手写操作系统系列之----------加载Kernel