自制操作系统Antz(7)——实现内核 (上)

时间:2022-07-01 01:15:42

Antz系统更新地址: https://www.cnblogs.com/LexMoon/category/1262287.html

Linux内核源码分析地址:https://www.cnblogs.com/LexMoon/category/1267413.html

  在前几天的任务中,我们已经简单实现了MBR,直接操作显示器和硬盘操作来加载其他扇区的程序,如今已经可以进入保护模式了,简单引入了C语言,接下来我们编写自己的内核。

0. 汇编生成ELF

  完成实模式到保护模式跳转的这一任务是由loader进行的,而我们不应该用loader做太多的事,loader只需要完成跳转就好了,剩下的工作交给内核。

  为了加载内核到内存,需要使用ELF格式,如何编译这种格式呢?

  来看看下面这个例子。

[section .data]

strHello    db    "Hello, Antz !", 0Ah
STRLEN equ $ - strHello [section .text] global _start _start:
mov edx, STRLEN
mov ecx, strHello
mov ebx,
mov eax,
int 0x80
mov ebx,
mov eax,
int 0x80

  global _start定义了程序的入口地址,相当于c/c++中main。

  接下来使用NASM编译。

  自制操作系统Antz(7)——实现内核 (上)

  -f elf 指定了输出文件的格式为ELF格式。

  编译完成之后要进行链接,链接指令如下:

  自制操作系统Antz(7)——实现内核 (上)

    (出现警告,在linux无警告,Windows下会有,可以无视)

  -s是strip的简写,可以去掉符号表等内容,对生成的可执行代码进行减肥。

1. 汇编与C语言共同使用

  我们已经可以生成一个支持载入内存的内核格式文件了,现在我们要学会把汇编程序和c语言程序链接在一起。这是我们编写内核的一大步。

  先来看看c代码:

    其中定义了一个choose函数,声明了一个myprint函数。

 void myprint(char* msg, int len);

 int choose(int a, int b)
{
if(a >= b){
myprint("the 1st one\n", );
}
else{
myprint("the 2nd one\n", );
} return ;
}

  再来看看汇编代码:

 extern choose

 [section .data]    ; 数据在此

 num1st        dd
num2nd dd [section .text] ; 代码在此 global _start
global myprint _start:
push num2nd
push num1st
call choose
add esp, mov ebx,
mov eax,
int 0x80 ; void myprint(char* msg, int len)
myprint:
mov edx, [esp + ] ; len
mov ecx, [esp + ] ; msg
mov ebx,
mov eax, ; sys_write
int 0x80
ret

  如果你懂汇编,这个代码一定没有如何问题。

  第一行的extern choose就是指c代码中定义的choose函数。

  后面的global导出了函数入口,_start和在c文件中没有定义的myprint函数,myprint在汇编代码中完成了定义。

  _start中调用了choose,choose中又调用了myprint函数。

  这样这两个代码文件内容就很清晰了吧。

  在Linux终端下查看结果:

 nasm -f elf foo.asm -o foo.o
gcc -c bar.c -o bar.o
ld -s hello.o bar.o -o foobar
./foobar

2. 从Loader到内核

  加载内核到内存和引导扇区的工作很相似,只是处理内核时我们要根据ELF文件结构中的值将内核中相应段放入相应位置。

  fat12hdr.inc :

 BS_OEMName    DB 'Antz__Os'

 BPB_BytsPerSec    DW
BPB_SecPerClus DB
BPB_RsvdSecCnt DW
BPB_NumFATs DB
BPB_RootEntCnt DW
BPB_TotSec16 DW
BPB_Media DB 0xF0
BPB_FATSz16 DW
BPB_SecPerTrk DW
BPB_NumHeads DW
BPB_HiddSec DD
BPB_TotSec32 DD BS_DrvNum DB
BS_Reserved1 DB
BS_BootSig DB 29h
BS_VolID DD
BS_VolLab DB 'Tinix0.01 '
BS_FileSysType DB 'FAT12 ' FATSz equ
RootDirSectors equ SectorNoOfRootDirectory equ
SectorNoOfFAT1 equ
DeltaSectorNo equ

  boot.asm :

 org  07c00h        

 BaseOfStack        equ    07c00h
BaseOfLoader equ 09000h
OffsetOfLoader equ 0100h jmp short LABEL_START
nop %include "fat12hdr.inc" LABEL_START:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack mov ax, 0600h
mov bx, 0700h
mov cx,
mov dx, 0184fh
int 10h mov dh,
call DispStr xor ah, ah
xor dl, dl
int 13h mov word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
cmp word [wRootDirSizeForLoop],
jz LABEL_NO_LOADERBIN
dec word [wRootDirSizeForLoop]
mov ax, BaseOfLoader
mov es, ax
mov bx, OffsetOfLoader
mov ax, [wSectorNo]
mov cl,
call ReadSector mov si, LoaderFileName
mov di, OffsetOfLoader
cld
mov dx, 10h
LABEL_SEARCH_FOR_LOADERBIN:
cmp dx,
jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR
dec dx
mov cx,
LABEL_CMP_FILENAME:
cmp cx,
jz LABEL_FILENAME_FOUND
dec cx
lodsb
cmp al, byte [es:di]
jz LABEL_GO_ON
jmp LABEL_DIFFERENT LABEL_GO_ON:
inc di
jmp LABEL_CMP_FILENAME LABEL_DIFFERENT:
and di, 0FFE0h
add di, 20h
mov si, LoaderFileName
jmp LABEL_SEARCH_FOR_LOADERBIN LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
add word [wSectorNo],
jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN LABEL_NO_LOADERBIN:
mov dh,
call DispStr
%ifdef _BOOT_DEBUG_
mov ax, 4c00h
int 21h
%else
jmp $
%endif LABEL_FILENAME_FOUND:
mov ax, RootDirSectors
and di, 0FFE0h
add di, 01Ah
mov cx, word [es:di]
push cx
add cx, ax
add cx, DeltaSectorNo
mov ax, BaseOfLoader
mov es, ax
mov bx, OffsetOfLoader
mov ax, cx LABEL_GOON_LOADING_FILE:
push ax
push bx
mov ah, 0Eh
mov al, '.'
mov bl, 0Fh
int 10h
pop bx
pop ax mov cl,
call ReadSector
pop ax
call GetFATEntry
cmp ax, 0FFFh
jz LABEL_FILE_LOADED
push ax
mov dx, RootDirSectors
add ax, dx
add ax, DeltaSectorNo
add bx, [BPB_BytsPerSec]
jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED: mov dh,
call DispStr jmp BaseOfLoader:OffsetOfLoader wRootDirSizeForLoop dw RootDirSectors
wSectorNo dw
bOdd db LoaderFileName db "LOADER BIN", MessageLength equ
BootMessage: db "Booting ";
Message1 db "Ready. ";
Message2 db "No LOADER"; DispStr:
mov ax, MessageLength
mul dh
add ax, BootMessage
mov bp, ax
mov ax, ds
mov es, ax
mov cx, MessageLength
mov ax, 01301h
mov bx, 0007h
mov dl,
int 10h
ret ReadSector:
push bp
mov bp, sp
sub esp, mov byte [bp-], cl
push bx
mov bl, [BPB_SecPerTrk]
div bl
inc ah
mov cl, ah
mov dh, al
shr al,
mov ch, al
and dh,
pop bx mov dl, [BS_DrvNum]
.GoOnReading:
mov ah,
mov al, byte [bp-]
int 13h
jc .GoOnReading add esp,
pop bp ret GetFATEntry:
push es
push bx
push ax
mov ax, BaseOfLoader
sub ax, 0100h
mov es, ax
pop ax
mov byte [bOdd],
mov bx,
mul bx
mov bx,
div bx
cmp dx,
jz LABEL_EVEN
mov byte [bOdd],
LABEL_EVEN:
xor dx, dx
mov bx, [BPB_BytsPerSec]
div bx push dx
mov bx,
add ax, SectorNoOfFAT1
mov cl,
call ReadSector
pop dx
add bx, dx
mov ax, [es:bx]
cmp byte [bOdd],
jnz LABEL_EVEN_2
shr ax,
LABEL_EVEN_2:
and ax, 0FFFh LABEL_GET_FAT_ENRY_OK: pop bx
pop es
ret times -($-$$) db
dw 0xaa55

  loader.asm:

 org  0100h

 BaseOfStack        equ    0100h

 BaseOfKernelFile    equ     08000h
OffsetOfKernelFile equ 0h jmp LABEL_START %include "fat12hdr.inc" LABEL_START:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack mov dh,
call DispStr mov word [wSectorNo], SectorNoOfRootDirectory
xor ah, ah
xor dl, dl
int 13h
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
cmp word [wRootDirSizeForLoop],
jz LABEL_NO_KERNELBIN
dec word [wRootDirSizeForLoop]
mov ax, BaseOfKernelFile
mov es, ax
mov bx, OffsetOfKernelFile mov ax, [wSectorNo]
mov cl,
call ReadSector mov si, KernelFileName
mov di, OffsetOfKernelFile
cld
mov dx, 10h
LABEL_SEARCH_FOR_KERNELBIN:
cmp dx,
jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR
dec dx
mov cx,
LABEL_CMP_FILENAME:
cmp cx,
jz LABEL_FILENAME_FOUND
dec cx
lodsb
cmp al, byte [es:di]
jz LABEL_GO_ON
jmp LABEL_DIFFERENT
LABEL_GO_ON:
inc di
jmp LABEL_CMP_FILENAME LABEL_DIFFERENT:
and di, 0FFE0h
add di, 20h
mov si, KernelFileName
jmp LABEL_SEARCH_FOR_KERNELBIN LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
add word [wSectorNo],
jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN LABEL_NO_KERNELBIN:
mov dh,
call DispStr
%ifdef _LOADER_DEBUG_
mov ax, 4c00h
int 21h
%else
jmp $
%endif LABEL_FILENAME_FOUND:
mov ax, RootDirSectors
and di, 0FFF0h push eax
mov eax, [es : di + 01Ch]
mov dword [dwKernelSize], eax
pop eax add di, 01Ah
mov cx, word [es:di]
push cx
add cx, ax
add cx, DeltaSectorNo
mov ax, BaseOfKernelFile
mov es, ax
mov bx, OffsetOfKernelFile
mov ax, cx LABEL_GOON_LOADING_FILE:
push ax
push bx
mov ah, 0Eh
mov al, '.'
mov bl, 0Fh
int 10h
pop bx
pop ax mov cl,
call ReadSector
pop ax
call GetFATEntry
cmp ax, 0FFFh
jz LABEL_FILE_LOADED
push ax
mov dx, RootDirSectors
add ax, dx
add ax, DeltaSectorNo
add bx, [BPB_BytsPerSec]
jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED: call KillMotor mov dh,
call DispStr jmp $ wRootDirSizeForLoop dw RootDirSectors
wSectorNo dw
bOdd db
dwKernelSize dd KernelFileName db "KERNEL BIN",
MessageLength equ
LoadMessage: db "Loading "
Message1 db "Ready. "
Message2 db "No KERNEL" DispStr:
mov ax, MessageLength
mul dh
add ax, LoadMessage
mov bp, ax
mov ax, ds
mov es, ax
mov cx, MessageLength
mov ax, 01301h
mov bx, 0007h
mov dl,
add dh,
int 10h
ret ReadSector: push bp
mov bp, sp
sub esp, mov byte [bp-], cl
push bx
mov bl, [BPB_SecPerTrk]
div bl
inc ah
mov cl, ah
mov dh, al
shr al,
mov ch, al
and dh,
pop bx mov dl, [BS_DrvNum]
.GoOnReading:
mov ah,
mov al, byte [bp-]
int 13h
jc .GoOnReading add esp,
pop bp ret GetFATEntry:
push es
push bx
push ax
mov ax, BaseOfKernelFile
sub ax, 0100h
mov es, ax
pop ax
mov byte [bOdd],
mov bx,
mul bx
mov bx,
div bx
cmp dx,
jz LABEL_EVEN
mov byte [bOdd],
LABEL_EVEN:
xor dx, dx
mov bx, [BPB_BytsPerSec]
div bx push dx
mov bx,
add ax, SectorNoOfFAT1
mov cl,
call ReadSector
pop dx
add bx, dx
mov ax, [es:bx]
cmp byte [bOdd],
jnz LABEL_EVEN_2
shr ax,
LABEL_EVEN_2:
and ax, 0FFFh LABEL_GET_FAT_ENRY_OK: pop bx
pop es
ret KillMotor:
push dx
mov dx, 03F2h
mov al,
out dx, al
pop dx
ret

  加载功能已经有了,但是还没有内核给以上程序拿来加载。

  我们来实现一个最简单的内核,以后会基于此扩展。

  kernel.asm :

    此处的K不会打印,因为这里只是假设gs指向了显存。

 [section .text]

 global _start     

 _start:
mov ah, 0Fh ; 0000: 黑底 1111: 白字
mov al, 'K'
mov [gs:(( * + ) * )], ax ; 屏幕第 1 行, 第 39 列。
jmp $

  自制操作系统Antz(7)——实现内核 (上)

  出现了Ready,说明我们的kernel内核已经加载成功了。