深入自制操作系统(一、Bootloader的实现)
我们的操作系统启动流程:
boot.asm -> loader.asm -> kernel.asm -> shell.asm
直接上代码
Boot.asm
; Plain Kernel
; boot.asmorg 0x07C00
StackBase equ 0x07C00 ; 栈基址
LoaderBase equ 0x09000 ; Loader基址
OffsetLoader equ 0x0100 ; Loader偏移
RootDirSectors equ 14 ; 根目录大小
SectorNoRootDirectory equ 19 ; 根目录起始扇区
SectorNoFAT1 equ 1 ; 第一个FAT表开始扇区
DeltaSectorNo equ 17 jmp short startnop; 下面的...咱也不知道,咱也不敢问,厂家说啥就是啥BS_OEMName db 'Plain ' ; 8个字节BPB_BytsPerSec dw 512 ; 每扇区512个字节BPB_SecPerClus db 1 ; 每簇固定1个扇区BPB_RsvdSecCnt dw 1 ; MBR固定占用1个扇区BPB_NumFATs db 2 ; FAT12文件系统固定2个FAT表BPB_RootEntCnt dw 224 ; FAT12文件系统中根目录最大224个文件BPB_TotSec16 dw 2880 ; 1.44MB磁盘固定2880个扇区BPB_Media db 0xF0 ; 介质描述符,固定为0xF0BPB_FATSz16 dw 9 ; 一个FAT表所占的扇区数FAT12文件系统固定为9个扇区BPB_SecPerTrk dw 18 ; 每磁道扇区数,固定为18BPB_NumHeads dw 2 ; 磁头数BPB_HiddSec dd 0 ; 隐藏扇区数,没有BPB_TotSec32 dd 0 ; 直接置0即可BS_DrvNum db 0 ; int 13h 调用时所读取的驱动器号,由于只有一个软盘所以是0 BS_Reserved1 db 0 ; 未使用,预留BS_BootSig db 0x29 ; 扩展引导标记,固定为0x29BS_VolID dd 0 ; 卷序列号,由于只挂载一个软盘所以为0BS_VolLab db 'Plain - OS ' ; 卷标,11个字节BS_FileSysType db 'FAT12 ' ; 由于是 FAT12 文件系统,所以写入 FAT12 后补齐8个字节start:mov ax, 0x0600mov bx, 0x0700mov cx, 0mov dx, 0x184Fint 0x10mov ah, 0x02xor bh, bhmov dh, 0mov dl, 0int 0x10xor ax, ax ; 相当于 mov ax, 0mov ds, axmov es, axmov ss, axmov sp, StackBasemov dh, 0mov si, msgcall print_stringread_main:mov word [wSectorNo], SectorNoRootDirectorycmp word [wRootDirLoopSize], 0jz no_loaderdec word [wRootDirLoopSize] ; 减一个扇区mov ax, StackBasemov es, axmov bx, OffsetLoadermov ax, [wSectorNo] ; nowmov cl, 1call read_sectormov si, LoaderFileNamemov di, OffsetLoadercldmov dx, 0x10loader_search:cmp dx, 0jz next_sectordec dxmov cx, 11cmp_name:cmp cx, 0jz loader_founddec cxlodsbcmp al, byte [es:di]jz next_strjmp differentnext_str:inc dijmp cmp_namedifferent:and di, 0xFFE0add di, 0x20mov si, LoaderFileNamejmp loader_searchnext_sector:add word [wSectorNo], 1jmp read_mainno_loader:mov si, Message2call print_stringjmp $loader_found:mov ax, RootDirSectorsand di, 0xFFE0add di, 0x1Amov cx, word [es:di]push cxadd cx,axadd cx, DeltaSectorNomov ax, LoaderBasemov es, axmov bx, OffsetLoadermov ax, cxload_file:mov cl, 1call read_sectorpop axcall FAT_entrycmp ax, 0x0FFFjz loader_filepush axmov dx, RootDirSectorsadd ax, dxadd ax, DeltaSectorNoadd bx, [BPB_BytsPerSec]jmp load_fileloader_file:mov dh, 1mov si, Message1call print_stringjmp LoaderBase:OffsetLoader ; Loader!print_string:lodsbor al, al ; 检查是否为0jz .donemov ah, 0x0Emov bx, 0x0007int 0x10jmp print_string.done:retread_sector:push bpmov bp, spsub esp, 2mov byte [bp-2], clpush bxmov bl, [BPB_SecPerTrk]div blinc ahmov cl, ahmov dh, alshr al, 1mov ch, aland dh, 1 ; 磁头pop bxmov dl, [BS_DrvNum].read_start:mov ah, 2mov al, byte [bp-2]int 0x13jc .read_startadd esp, 2pop bpretFAT_entry:push espush bxpush axmov ax, LoaderBasesub ax, 0x0100mov es, ax ; 缓冲区的基址pop axmov byte [bOdd], 0mov bx, 3mul bxmov bx, 2div bxcmp dx, 0jz FAT_nextmov byte [bOdd], 1FAT_next:xor dx, dxmov bx, [BPB_BytsPerSec]div bxpush dxxor bx, bxadd ax, SectorNoFAT1mov cl, 2call read_sectorpop dxadd bx, dxmov ax, [es:bx]cmp byte [bOdd], 1jnz FAT_next2shr ax, 4FAT_next2:and ax, 0x0FFFall_OK:pop bxpop esretmsg db "Loading Loader...", 0wRootDirLoopSize dw RootDirSectors
wSectorNo dw 0 ; 当前扇区数
bOdd db 0 LoaderFileName db "LOADER BIN", 0 ; loader的文件名Message1 db "OK!", 0x0A, 0x0D, 0
Message2 db "Loader Not Find", 0times 510-($-$$) db 0
db 0x55, 0xAA
Loader.asm
org 0100h
BaseOfStack equ 0100hjmp LABEL_START%include "load.inc"LABEL_GDT: Descriptor 0, 0, 0
LABEL_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_C|DA_32|DA_LIMIT_4K
LABEL_DESC_FLAT_RW: Descriptor 0, 0fffffh, DA_DRW|DA_32|DA_LIMIT_4K
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW|DA_DPL3GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen - 1dd BaseOfLoaderPhyAddr + LABEL_GDTSelectorFlatC equ LABEL_DESC_FLAT_C - LABEL_GDT
SelectorFlatRW equ LABEL_DESC_FLAT_RW - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3LABEL_START:mov ax, csmov ds, axmov es, axmov ss, axmov sp, BaseOfStackmov si, msgcall print_stringmov word [wSectorNo], SectorNoOfRootDirectoryxor ah, ahxor dl, dlint 13hSearchRootDir:cmp word [wRootDirSizeForLoop], 0jz NoKernelFounddec word [wRootDirSizeForLoop]mov ax, BaseOfKernelFilemov es, axmov bx, OffsetOfKernelFilemov ax, [wSectorNo]mov cl, 1call ReadSectormov si, KernelFileNamemov di, OffsetOfKernelFilecldmov dx, 10hSearchFile:cmp dx, 0jz NextSectordec dxmov cx, 11
CmpFilename:cmp cx, 0jz FileFounddec cxlodsbcmp al, byte [es:di]jz NextCharjmp DifferentFile
NextChar:inc dijmp CmpFilenameDifferentFile:and di, 0FFE0hadd di, 20hmov si, KernelFileNamejmp SearchFileNextSector:add word [wSectorNo], 1jmp SearchRootDirNoKernelFound:mov si, Message2call print_stringjmp $FileFound:mov ax, RootDirSectorsand di, 0FFF0hpush eaxmov eax, [es:di + 01Ch]mov dword [dwKernelSize], eaxpop eaxadd di, 01Ahmov cx, word [es:di]push cxadd cx, axadd cx, DeltaSectorNomov ax, BaseOfKernelFilemov es, axmov bx, OffsetOfKernelFilemov ax, cxLoadFile:mov cl, 1call ReadSectorpop axcall GetFATEntrycmp ax, 0FFFhjz FileLoadedpush axmov dx, RootDirSectorsadd ax, dxadd ax, DeltaSectorNoadd bx, [BPB_BytsPerSec]jmp LoadFileFileLoaded:call KillMotormov si, Message1call print_stringlgdt [GdtPtr]cliin al, 92hor al, 00000010bout 92h, almov eax, cr0or eax, 1mov cr0, eaxjmp dword SelectorFlatC:(BaseOfLoaderPhyAddr+LABEL_PM_START)print_string:lodsbor al, aljz .donemov ah, 0x0Emov bx, 0x0007int 0x10jmp print_string.done:retReadSector:push bpmov bp, spsub esp, 2mov byte [bp-2], clpush bxmov bl, [BPB_SecPerTrk]div blinc ahmov cl, ahmov dh, alshr al, 1mov ch, aland dh, 1pop bxmov dl, [BS_DrvNum]
ReadLoop:mov ah, 2mov al, byte [bp-2]int 13hjc ReadLoopadd esp, 2pop bpretGetFATEntry:push espush bxpush axmov ax, BaseOfKernelFilesub ax, 0100hmov es, axpop axmov byte [bOdd], 0mov bx, 3mul bxmov bx, 2div bxcmp dx, 0jz EvenEntrymov byte [bOdd], 1
EvenEntry:xor dx, dxmov bx, [BPB_BytsPerSec]div bxpush dxmov bx, 0add ax, SectorNoOfFAT1mov cl, 2call ReadSectorpop dxadd bx, dxmov ax, [es:bx]cmp byte [bOdd], 1jnz EvenEntry2shr ax, 4
EvenEntry2:and ax, 0FFFhpop bxpop esretKillMotor:push dxmov dx, 03F2hmov al, 0out dx, alpop dxret[section .s32]
align 32
[bits 32]
LABEL_PM_START:mov ax, SelectorVideomov gs, axmov ax, SelectorFlatRWmov ds, axmov es, axmov fs, axmov ss, axmov esp, TopOfStackcall InitKerneljmp SelectorFlatC:KernelEntryPointPhyAddrMemCpy:push ebpmov ebp, esppush esipush edipush ecxmov edi, [ebp + 8]mov esi, [ebp + 12]mov ecx, [ebp + 16]
CopyLoop:cmp ecx, 0jz CopyDonemov al, [ds:esi]inc esimov byte [es:edi], alinc edidec ecxjmp CopyLoop
CopyDone:mov eax, [ebp + 8]pop ecxpop edipop esimov esp, ebppop ebpretInitKernel:xor esi, esimov cx, word [BaseOfKernelFilePhyAddr + 2Ch]movzx ecx, cxmov esi, [BaseOfKernelFilePhyAddr + 1Ch]add esi, BaseOfKernelFilePhyAddr
ProcessHeader:mov eax, [esi]cmp eax, 0jz NextHeaderpush dword [esi + 010h]mov eax, [esi + 04h]add eax, BaseOfKernelFilePhyAddrpush eaxpush dword [esi + 08h]call MemCpyadd esp, 12NextHeader:add esi, 020hdec ecxjnz ProcessHeaderret[section .data1]
StackSpace: times 1024 db 0
TopOfStack equ $ - StackSpacedwKernelSize dd 0
wRootDirSizeForLoop dw RootDirSectors
wSectorNo dw 0
bOdd db 0
KernelFileName db "KERNEL BIN", 0
msg db "Loading Kernel...", 0
Message1 db "OK!", 0x0A, 0x0D, 0
Message2 db "Kernel Not Find", 0
loader.asm
BS_OEMName db 'Plain ' ; 8个字节BPB_BytsPerSec dw 512 ; 每扇区512个字节BPB_SecPerClus db 1 ; 每簇固定1个扇区BPB_RsvdSecCnt dw 1 ; MBR固定占用1个扇区BPB_NumFATs db 2 ; FAT12文件系统固定2个FAT表BPB_RootEntCnt dw 224 ; FAT12文件系统中根目录最大224个文件BPB_TotSec16 dw 2880 ; 1.44MB磁盘固定2880个扇区BPB_Media db 0xF0 ; 介质描述符,固定为0xF0BPB_FATSz16 dw 9 ; 一个FAT表所占的扇区数FAT12文件系统固定为9个扇区BPB_SecPerTrk dw 18 ; 每磁道扇区数,固定为18BPB_NumHeads dw 2 ; 磁头数BPB_HiddSec dd 0 ; 隐藏扇区数,没有BPB_TotSec32 dd 0 ; 直接置0即可BS_DrvNum db 0 ; int 13h 调用时所读取的驱动器号,由于只有一个软盘所以是0 BS_Reserved1 db 0 ; 未使用,预留BS_BootSig db 0x29 ; 扩展引导标记,固定为0x29BS_VolID dd 0 ; 卷序列号,由于只挂载一个软盘所以为0BS_VolLab db 'Plain - OS ' ; 卷标,11个字节BS_FileSysType db 'FAT12 ' ; 由于是 FAT12 文件系统,所以写入 FAT12 后补齐8个字节FATSz equ 9 ; BPB_FATSz16
RootDirSectors equ 14 ; 根目录大小
SectorNoOfRootDirectory equ 19 ; 根目录起始扇区
SectorNoOfFAT1 equ 1 ; 第一个FAT表的开始扇区
DeltaSectorNo equ 17 ; 由于第一个簇不用,所以RootDirSectors要-2再加上根目录区首扇区和偏移才能得到真正的地址,故把RootDirSectors-2封装成一个常量(17)DA_32 equ 0x4000
DA_LIMIT_4K equ 0x8000DA_DPL0 equ 0x00
DA_DPL1 equ 0x20
DA_DPL2 equ 0x40
DA_DPL3 equ 0x60DA_DR equ 0x90
DA_DRW equ 0x92
DA_DRWA equ 0x93
DA_C equ 0x98
DA_CR equ 0x9A
DA_CCO equ 0x9C
DA_CCOR equ 0x9EDA_LDT equ 0x82
DA_TaskGate equ 0x85
DA_386TSS equ 0x89
DA_386CGate equ 0x8C
DA_386IGate equ 0x8E
DA_386TGate equ 0x8FSA_RPL0 equ 0
SA_RPL1 equ 1
SA_RPL2 equ 2
SA_RPL3 equ 3SA_TIG equ 0
SA_TIL equ 4PG_P equ 1
PG_RWR equ 0
PG_RWW equ 2
PG_USS equ 0
PG_USU equ 4%macro Descriptor 3dw %2 & 0xFFFFdw %1 & 0xFFFFdb (%1 >> 16) & 0xFFdw ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF)db (%1 >> 24) & 0xFF
%endmacro%macro Gate 4dw (%2 & 0xFFFF)dw %1dw (%3 & 0x1F) | ((%4 << 8) & 0xFF00)dw ((%2 >> 16) & 0xFFFF)
%endmacroBaseOfLoader equ 09000h ; Loader的基址
OffsetOfLoader equ 0100h ; Loader的偏移BaseOfLoaderPhyAddr equ BaseOfLoader * 10h ; Loader被装载到的物理地址BaseOfKernelFile equ 08000h ; Kernel的基址
OffsetOfKernelFile equ 0h ; Kernel的偏移BaseOfKernelFilePhyAddr equ BaseOfKernelFile * 10h ; Kernel被装载到的物理地址
KernelEntryPointPhyAddr equ 0x100000 ; Kernel入口点,一定要与编译命令一致!!!
Makefile
all:make binmake imgmake runbin:nasm boot.asm -o boot.binnasm loader.asm -o loader.bin#gcc -c -O0 -fno-builtin -m32 -fno-stack-protector -o monitor.o monitor.c#gcc -c -O0 -fno-builtin -m32 -fno-stack-protector -o common.o common.c#gcc -c -O0 -fno-builtin -m32 -fno-stack-protector -o main.o main.c#nasm -f elf -o kernel.o kernel.asm#nasm -f elf -o io.o io.asm#i686-elf-ld -s -Ttext 0x100000 -o kernel.bin kernel.o io.oimg : boot.bin loader.bin kernel.bin#dd if=boot.bin of=a.img bs=512 count=1#dd if=loader.bin of=a.img bs=512 seek=1 conv=notrunc#python img.pyedimg imgin:1.img \wbinimg src:boot.bin len:512 from:0 to:0 \copy from:loader.bin to:@: \#copy from:kernel.bin to:@: \imgout:a.imgrun : a.imgqemu-system-i386 -fda a.img
一个make直接编译运行
显示 Kernel Not Found 即成功
至于1.img
这是ztools工具链edimg必须的,具体可以参考30day里的
为什么要有这种恶心的东西不要问我,问edimg作者
文件内容如图
依赖:
mtools (用于编译)
edimg (30day的写盘工具)
qemu (虚拟机)
i686-elf-mtools (后面用来链接)