自己动手写操作系统系列之----------加载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
这个代码很简单,就是显示一个字符串,当然了,上面这个加载内核的程序还不能让内核执行,所以这里不会显示这个字符串。我们看内核加载后的执行结果
大神出品 学习学习技术,加油! 感谢楼主的分享~ 感谢楼主的分享~ 还是不错的哦,顶了 还是不错的哦,顶了 支持,看起来不错呢! 支持中国红客联盟(ihonker.org)