深入自制操作系统(一、Bootloader的实现)

我们的操作系统启动流程:

boot.asm -> loader.asm -> kernel.asm -> shell.asm

直接上代码

Boot.asm

; Plain Kernel
; boot.asm


    org 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 start
    nop

    ; 下面的...咱也不知道,咱也不敢问,厂家说啥就是啥

    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          ; 介质描述符,固定为0xF0
    BPB_FATSz16    dw 9             ; 一个FAT表所占的扇区数FAT12文件系统固定为9个扇区
    BPB_SecPerTrk  dw 18            ; 每磁道扇区数,固定为18
    BPB_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          ; 扩展引导标记,固定为0x29
    BS_VolID       dd 0             ; 卷序列号,由于只挂载一个软盘所以为0
    BS_VolLab      db 'Plain - OS ' ; 卷标,11个字节
    BS_FileSysType db 'FAT12   '    ; 由于是 FAT12 文件系统,所以写入 FAT12 后补齐8个字节

start:


    
	mov ax, 0x0600
	mov bx, 0x0700
	mov cx, 0
	mov dx, 0x184F
	int 0x10
	mov ah, 0x02
	xor bh, bh
	mov dh, 0
	mov dl, 0
	int 0x10
    xor ax, ax ; 相当于 mov ax, 0
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, StackBase
    
    
    mov dh, 0
    mov si, msg
    call print_string

read_main:

    mov word [wSectorNo], SectorNoRootDirectory
    cmp word [wRootDirLoopSize], 0
    jz no_loader
    dec word [wRootDirLoopSize] ; 减一个扇区
    mov ax, StackBase
    mov es, ax
    mov bx, OffsetLoader
    mov ax, [wSectorNo] ; now
    mov cl, 1
    call read_sector

    mov si, LoaderFileName
    mov di, OffsetLoader
    cld
    mov dx, 0x10

loader_search:
    cmp dx, 0
    jz next_sector
    dec dx
    mov cx, 11

cmp_name:
    cmp cx, 0
    jz loader_found
    dec cx
    lodsb
    cmp al, byte [es:di]
    jz next_str
    jmp different

next_str:
    inc di
    jmp cmp_name

different:
    and di, 0xFFE0
    add di, 0x20
    mov si, LoaderFileName
    jmp loader_search

next_sector:
    add word [wSectorNo], 1
    jmp read_main

no_loader:
    mov si, Message2
    call print_string
    jmp $

loader_found:
    mov ax, RootDirSectors
    and di, 0xFFE0
    add di, 0x1A
    mov cx, word [es:di]
    push cx
    add cx,ax
    add cx, DeltaSectorNo
    mov ax, LoaderBase
    mov es, ax
    mov bx, OffsetLoader
    mov ax, cx

load_file:
    mov cl, 1
    call read_sector
    pop ax
    call FAT_entry
    cmp ax, 0x0FFF
    jz loader_file
    push ax
    mov dx, RootDirSectors
    add ax, dx
    add ax, DeltaSectorNo
    add bx, [BPB_BytsPerSec]
    jmp load_file

loader_file:
    mov dh, 1
    mov si, Message1
    call print_string
    jmp LoaderBase:OffsetLoader ; Loader!

print_string:
    lodsb
    or al, al ; 检查是否为0
    jz .done
    mov ah, 0x0E
    mov bx, 0x0007
    int 0x10
    jmp print_string

.done:
    ret

read_sector:
    push bp
    mov bp, sp
    sub esp, 2
    mov byte [bp-2], cl
    push bx
    mov bl, [BPB_SecPerTrk]
    div bl
    inc ah
    mov cl, ah
    mov dh, al
    shr al, 1
    mov ch, al
    and dh, 1 ; 磁头
    pop bx
    mov dl, [BS_DrvNum]

.read_start:
    mov ah, 2
    mov al, byte [bp-2]
    int 0x13
    jc .read_start
    add esp, 2
    pop bp
    ret

FAT_entry:
    push es
    push bx
    push ax
    mov ax, LoaderBase
    sub ax, 0x0100
    mov es, ax ; 缓冲区的基址
    pop ax
    mov byte [bOdd], 0
    mov bx, 3
    mul bx
    mov bx, 2
    div bx
    cmp dx, 0
    jz FAT_next
    mov byte [bOdd], 1

FAT_next:
    xor dx, dx
    mov bx, [BPB_BytsPerSec]
    div bx
    push dx
    xor bx, bx
    add ax, SectorNoFAT1
    mov cl, 2
    call read_sector
    pop dx
    add bx, dx
    mov ax, [es:bx]
    cmp byte [bOdd], 1
    jnz FAT_next2
    shr ax, 4

FAT_next2:
    and ax, 0x0FFF

all_OK:
    pop bx
    pop es
    ret

msg              db "Loading Loader...", 0

wRootDirLoopSize 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", 0


times 510-($-$$) db 0
db 0x55, 0xAA

Loader.asm

org 0100h
BaseOfStack equ 0100h

jmp 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_DPL3

GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen - 1
       dd BaseOfLoaderPhyAddr + LABEL_GDT

SelectorFlatC  equ LABEL_DESC_FLAT_C - LABEL_GDT
SelectorFlatRW equ LABEL_DESC_FLAT_RW - LABEL_GDT
SelectorVideo  equ LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3

LABEL_START:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, BaseOfStack
    
    mov si, msg
    call print_string

    mov word [wSectorNo], SectorNoOfRootDirectory
    xor ah, ah
    xor dl, dl
    int 13h

SearchRootDir:
    cmp word [wRootDirSizeForLoop], 0
    jz NoKernelFound
    dec word [wRootDirSizeForLoop]
    mov ax, BaseOfKernelFile
    mov es, ax
    mov bx, OffsetOfKernelFile
    mov ax, [wSectorNo]
    mov cl, 1
    call ReadSector

    mov si, KernelFileName
    mov di, OffsetOfKernelFile
    cld
    mov dx, 10h

SearchFile:
    cmp dx, 0
    jz NextSector
    dec dx
    mov cx, 11
CmpFilename:
    cmp cx, 0
    jz FileFound
    dec cx
    lodsb
    cmp al, byte [es:di]
    jz NextChar
    jmp DifferentFile
NextChar:
    inc di
    jmp CmpFilename

DifferentFile:
    and di, 0FFE0h
    add di, 20h
    mov si, KernelFileName
    jmp SearchFile

NextSector:
    add word [wSectorNo], 1
    jmp SearchRootDir

NoKernelFound:
    mov si, Message2
    call print_string
    jmp $

FileFound:
    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

LoadFile:
    mov cl, 1
    call ReadSector
    pop ax
    call GetFATEntry
    cmp ax, 0FFFh
    jz FileLoaded
    push ax
    mov dx, RootDirSectors
    add ax, dx
    add ax, DeltaSectorNo
    add bx, [BPB_BytsPerSec]
    jmp LoadFile

FileLoaded:
    call KillMotor
    mov si, Message1
    call print_string
    lgdt [GdtPtr]
    cli
    in al, 92h
    or al, 00000010b
    out 92h, al
    mov eax, cr0
    or eax, 1
    mov cr0, eax
    jmp dword SelectorFlatC:(BaseOfLoaderPhyAddr+LABEL_PM_START)

print_string:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    mov bx, 0x0007
    int 0x10
    jmp print_string

.done:
    ret


ReadSector:
    push bp
    mov bp, sp
    sub esp, 2
    mov byte [bp-2], cl
    push bx
    mov bl, [BPB_SecPerTrk]
    div bl
    inc ah
    mov cl, ah
    mov dh, al
    shr al, 1
    mov ch, al
    and dh, 1
    pop bx
    mov dl, [BS_DrvNum]
ReadLoop:
    mov ah, 2
    mov al, byte [bp-2]
    int 13h
    jc ReadLoop
    add esp, 2
    pop bp
    ret

GetFATEntry:
    push es
    push bx
    push ax
    mov ax, BaseOfKernelFile
    sub ax, 0100h
    mov es, ax
    pop ax
    mov byte [bOdd], 0
    mov bx, 3
    mul bx
    mov bx, 2
    div bx
    cmp dx, 0
    jz EvenEntry
    mov byte [bOdd], 1
EvenEntry:
    xor dx, dx
    mov bx, [BPB_BytsPerSec]
    div bx
    push dx
    mov bx, 0
    add ax, SectorNoOfFAT1
    mov cl, 2
    call ReadSector
    pop dx
    add bx, dx
    mov ax, [es:bx]
    cmp byte [bOdd], 1
    jnz EvenEntry2
    shr ax, 4
EvenEntry2:
    and ax, 0FFFh
    pop bx
    pop es
    ret

KillMotor:
    push dx
    mov dx, 03F2h
    mov al, 0
    out dx, al
    pop dx
    ret

[section .s32]
align 32
[bits 32]
LABEL_PM_START:
    mov ax, SelectorVideo
    mov gs, ax
    mov ax, SelectorFlatRW
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov ss, ax
    mov esp, TopOfStack
    call InitKernel
    jmp SelectorFlatC:KernelEntryPointPhyAddr

MemCpy:
    push ebp
    mov ebp, esp
    push esi
    push edi
    push ecx
    mov edi, [ebp + 8]
    mov esi, [ebp + 12]
    mov ecx, [ebp + 16]
CopyLoop:
    cmp ecx, 0
    jz CopyDone
    mov al, [ds:esi]
    inc esi
    mov byte [es:edi], al
    inc edi
    dec ecx
    jmp CopyLoop
CopyDone:
    mov eax, [ebp + 8]
    pop ecx
    pop edi
    pop esi
    mov esp, ebp
    pop ebp
    ret

InitKernel:
    xor esi, esi
    mov cx, word [BaseOfKernelFilePhyAddr + 2Ch]
    movzx ecx, cx
    mov esi, [BaseOfKernelFilePhyAddr + 1Ch]
    add esi, BaseOfKernelFilePhyAddr
ProcessHeader:
    mov eax, [esi]
    cmp eax, 0
    jz NextHeader
    push dword [esi + 010h]
    mov eax, [esi + 04h]
    add eax, BaseOfKernelFilePhyAddr
    push eax
    push dword [esi + 08h]
    call MemCpy
    add esp, 12

NextHeader:
    add esi, 020h
    dec ecx
    jnz ProcessHeader
    ret

[section .data1]
StackSpace: times 1024 db 0
TopOfStack equ $ - StackSpace

dwKernelSize        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.inc


    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          ; 介质描述符,固定为0xF0
    BPB_FATSz16    dw 9             ; 一个FAT表所占的扇区数FAT12文件系统固定为9个扇区
    BPB_SecPerTrk  dw 18            ; 每磁道扇区数,固定为18
    BPB_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          ; 扩展引导标记,固定为0x29
    BS_VolID       dd 0             ; 卷序列号,由于只挂载一个软盘所以为0
    BS_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 0x8000

DA_DPL0     equ 0x00
DA_DPL1     equ 0x20
DA_DPL2     equ 0x40
DA_DPL3     equ 0x60

DA_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 0x9E

DA_LDT      equ 0x82
DA_TaskGate equ 0x85
DA_386TSS   equ 0x89
DA_386CGate equ 0x8C
DA_386IGate equ 0x8E
DA_386TGate equ 0x8F

SA_RPL0     equ 0
SA_RPL1     equ 1
SA_RPL2     equ 2
SA_RPL3     equ 3

SA_TIG      equ 0
SA_TIL      equ 4

PG_P        equ 1
PG_RWR      equ 0
PG_RWW      equ 2
PG_USS      equ 0
PG_USU      equ 4

%macro Descriptor 3
    dw %2 & 0xFFFF
    dw %1 & 0xFFFF
    db (%1 >> 16) & 0xFF
    dw ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF)
    db (%1 >> 24) & 0xFF
%endmacro

%macro Gate 4
    dw (%2 & 0xFFFF)
    dw %1
    dw (%3 & 0x1F) | ((%4 << 8) & 0xFF00)
    dw ((%2 >> 16) & 0xFFFF)
%endmacro

BaseOfLoader            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 bin
	make img
	make run

bin:
	nasm boot.asm -o boot.bin
	nasm 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.o

img : 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.py
	edimg   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.img


run : a.img
	qemu-system-i386 -fda a.img

一个make直接编译运行

显示 Kernel Not Found 即成功

至于1.img

这是ztools工具链edimg必须的,具体可以参考30day里的

为什么要有这种恶心的东西不要问我,问edimg作者

文件内容如图

依赖:

mtools (用于编译)

edimg (30day的写盘工具)

qemu  (虚拟机)

i686-elf-mtools (后面用来链接)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值