参考
https://blog.51cto.com/13475106/category6.html及狄泰软件相关课程
一.整体的设计
从上图可以得到一个问题,为什么不能从boot直接加载kernnel,并跳转运行?
该设计的思路
1.boot必须小于512字节,无法完成过多功能
2.kernel需要运行于32位保护模式(汇编+c语言)
3.使用loader中转:获取必要硬件信息,进入保护模式
进行的重构方案如下所示
文件功能的定义
common.asm--常量定义,宏定义
blfunc.asm--实模式下的文件加载功能定义
boot.asm--加载loader并跳转[引导扇区]
loader.asm--在这里需要进行的操作是必要硬件初始化,加载kernel,进入保护模式,跳转到kernel执行
需要进行的修改
1.将之前定义的inc.asm修改成common.asm,并在makefile中的链接关系将其相关的名字进行修改
2.在loader.asm中相关的头文件进行修改
3.函数重构的实现--将其实现不同的接口[将之前的代码进行改写,细分模块
blfunc.asm注意事项
1.%include "blfunc.asm"必须是第一条"包含"语句
2.%include "blfunc.asm"强制从BLMain标签处开始执行
3.Buffer为必要的内存缓冲区必须在代码末尾定义
内核雏形构造
通过上图可知,kernel.out加载进内存后不能直接被x86处理器运行,产生的原因有三种
1.kernel.out是Linux系统中的可执行程序
2.Linux中的可执行程序为elf格式的文件-固定数据格式
3.处理器只认代码和数据,无法正确执行elf可执行程序
改进方法
1.提取elf文件中的代码段与数据段-删除elf文件格式信息
2.重定位提取后的代码和数据段,得到内核文件
3.加载内核文到内存-起始地址可自定义
4.跳转到内核入口地址处执行
在这里介绍一个工具,会有三种数据处理格式-如下图所示
所需的实验文件链接https://down.51cto.com/data/2468702
小结
1.实验nasm和gcc编译得到的是elf目标文件
2.ld将elf目标文件装配成为elf可执行程序
3.实验elf2kobj将可执行程序转换为内核文件
4.在实模式下加载转换得到的内核文件
5.进入保护模式后执行跳转内核起始位置处执行
参考代码分别如下:
bochsrc
############################################################### # Configuration file for Bochs ############################################################### # how much memory the emulated machine will have megs: 32 # filename of ROM images romimage: file=/usr/local/share/bochs/BIOS-bochs-latest vgaromimage: file=/usr/share/vgabios/vgabios.bin # what disk images will be used # floppya: 1_44=freedos.img, status=inserted floppya: 1_44=D.T.OS, status=inserted # choose the boot disk. boot: a # where do we send log messages? # log: bochsout.txt # disable the mouse mouse: enabled=0 # enable key mapping, using US layout as default. keyboard_mapping: enabled=1, map=/usr/local/share/bochs/keymaps/x11-pc-us.map
common.asm
; PIC-8259A Ports MASTER_ICW1_PORT equ 0x20 MASTER_ICW2_PORT equ 0x21 MASTER_ICW3_PORT equ 0x21 MASTER_ICW4_PORT equ 0x21 MASTER_OCW1_PORT equ 0x21 MASTER_OCW2_PORT equ 0x20 MASTER_OCW3_PORT equ 0x20 SLAVE_ICW1_PORT equ 0xA0 SLAVE_ICW2_PORT equ 0xA1 SLAVE_ICW3_PORT equ 0xA1 SLAVE_ICW4_PORT equ 0xA1 SLAVE_OCW1_PORT equ 0xA1 SLAVE_OCW2_PORT equ 0xA0 SLAVE_OCW3_PORT equ 0xA0 MASTER_EOI_PORT equ 0x20 MASTER_IMR_PORT equ 0x21 MASTER_IRR_PORT equ 0x20 MASTER_ISR_PORT equ 0x20 SLAVE_EOI_PORT equ 0xA0 SLAVE_IMR_PORT equ 0xA1 SLAVE_IRR_PORT equ 0xA0 SLAVE_ISR_PORT equ 0xA0 ; Segment Attribute DA_32 equ 0x4000 DA_LIMIT_4K EQU 0x8000 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 ; Segment Privilege DA_DPL0 equ 0x00 ; DPL = 0 DA_DPL1 equ 0x20 ; DPL = 1 DA_DPL2 equ 0x40 ; DPL = 2 DA_DPL3 equ 0x60 ; DPL = 3 ; Special Attribute DA_LDT equ 0x82 DA_TaskGate equ 0x85 ; 任务门类型值 DA_386TSS equ 0x89 ; 可用 386 任务状态段类型值 DA_386CGate equ 0x8C ; 386 调用门类型值 DA_386IGate equ 0x8E ; 386 中断门类型值 DA_386TGate equ 0x8F ; 386 陷阱门类型值 ; Selector Attribute 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 ; R/W 属性位值, 读/执行 PG_RWW equ 2 ; R/W 属性位值, 读/写/执行 PG_USS equ 0 ; U/S 属性位值, 系统级 PG_USU equ 4 ; U/S 属性位值, 用户级 ; 描述符 ; usage: Descriptor Base, Limit, Attr ; Base: dd ; Limit: dd (low 20 bits available) ; Attr: dw (lower 4 bits of higher byte are always 0) %macro Descriptor 3 ; 段基址, 段界限, 段属性 dw %2 & 0xFFFF ; 段界限1 dw %1 & 0xFFFF ; 段基址1 db (%1 >> 16) & 0xFF ; 段基址2 dw ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 属性1 + 段界限2 + 属性2 db (%1 >> 24) & 0xFF ; 段基址3 %endmacro ; 共 8 字节 ; 门 ; usage: Gate Selector, Offset, DCount, Attr ; Selector: dw ; Offset: dd ; DCount: db ; Attr: db %macro Gate 4 dw (%2 & 0xFFFF) ; 偏移地址1 dw %1 ; 选择子 dw (%3 & 0x1F) | ((%4 << 8) & 0xFF00) ; 属性 dw ((%2 >> 16) & 0xFFFF) ; 偏移地址2 %endmacro
blfunc.asm
jmp short _start nop header: BS_OEMName db "D.T.Soft" BPB_BytsPerSec dw 512 BPB_SecPerClus db 1 BPB_RsvdSecCnt dw 1 BPB_NumFATs db 2 BPB_RootEntCnt dw 224 BPB_TotSec16 dw 2880 BPB_Media db 0xF0 BPB_FATSz16 dw 9 BPB_SecPerTrk dw 18 BPB_NumHeads dw 2 BPB_HiddSec dd 0 BPB_TotSec32 dd 0 BS_DrvNum db 0 BS_Reserved1 db 0 BS_BootSig db 0x29 BS_VolID dd 0 BS_VolLab db "D.T.OS-0.01" BS_FileSysType db "FAT12 " const: RootEntryOffset equ 19 RootEntryLength equ 14 SPInitValue equ BaseOfStack - EntryItemLength EntryItem equ SPInitValue EntryItemLength equ 32 FatEntryOffset equ 1 FatEntryLength equ 9 _start: jmp BLMain ; ; return: ; dx --> (dx != 0) ? success : failure LoadTarget: mov ax, RootEntryOffset mov cx, RootEntryLength mov bx, Buffer call ReadSector mov si, Target mov cx, TarLen mov dx, 0 call FindEntry cmp dx, 0 jz finish mov si, bx mov di, EntryItem mov cx, EntryItemLength call MemCpy mov ax, FatEntryLength mov cx, [BPB_BytsPerSec] mul cx mov bx, BaseOfTarget sub bx, ax mov ax, FatEntryOffset mov cx, FatEntryLength call ReadSector mov dx, [EntryItem + 0x1A] mov si, BaseOfTarget / 0x10 mov es, si mov si, 0 loading: mov ax, dx add ax, 31 mov cx, 1 push dx push bx mov bx, si call ReadSector pop bx pop cx call FatVec cmp dx, 0xFF7 jnb finish add si, 512 cmp si, 0 jnz continue mov si, es add si, 0x1000 mov es, si mov si, 0 continue: jmp loading finish: ret ; cx --> index ; bx --> fat table address ; ; return: ; dx --> fat[index] FatVec: push cx mov ax, cx shr ax, 1 mov cx, 3 mul cx mov cx, ax pop ax and ax, 1 jz even jmp odd even: ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i]; mov dx, cx add dx, 1 add dx, bx mov bp, dx mov dl, byte [bp] and dl, 0x0F shl dx, 8 add cx, bx mov bp, cx or dl, byte [bp] jmp return odd: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F ); mov dx, cx add dx, 2 add dx, bx mov bp, dx mov dl, byte [bp] mov dh, 0 shl dx, 4 add cx, 1 add cx, bx mov bp, cx mov cl, byte [bp] shr cl, 4 and cl, 0x0F mov ch, 0 or dx, cx return: ret ; ds:si --> source ; es:di --> destination ; cx --> length MemCpy: cmp si, di ja btoe add si, cx add di, cx dec si dec di jmp etob btoe: cmp cx, 0 jz done mov al, [si] mov byte [di], al inc si inc di dec cx jmp btoe etob: cmp cx, 0 jz done mov al, [si] mov byte [di], al dec si dec di dec cx jmp etob done: ret ; es:bx --> root entry offset address ; ds:si --> target string ; cx --> target length ; ; return: ; (dx !=0 ) ? exist : noexist ; exist --> bx is the target entry FindEntry: push cx mov dx, [BPB_RootEntCnt] mov bp, sp find: cmp dx, 0 jz noexist mov di, bx mov cx, [bp] push si call MemCmp pop si cmp cx, 0 jz exist add bx, 32 dec dx jmp find exist: noexist: pop cx ret ; ds:si --> source ; es:di --> destination ; cx --> length ; ; return: ; (cx == 0) ? equal : noequal MemCmp: compare: cmp cx, 0 jz equal mov al, [si] cmp al, byte [di] jz goon jmp noequal goon: inc si inc di dec cx jmp compare equal: noequal: ret ; es:bp --> string address ; cx --> string length Print: mov dx, 0 mov ax, 0x1301 mov bx, 0x0007 int 0x10 ret ; no parameter ResetFloppy: push ax mov ah, 0x00 mov dl, [BS_DrvNum] int 0x13 pop ax ret ; ax --> logic sector number ; cx --> number of sector ; es:bx --> target address ReadSector: call ResetFloppy push bx push cx mov bl, [BPB_SecPerTrk] div bl mov cl, ah add cl, 1 mov ch, al shr ch, 1 mov dh, al and dh, 1 mov dl, [BS_DrvNum] pop ax pop bx mov ah, 0x02 read: int 0x13 jc read ret
boot.asm
BaseOfBoot equ 0x7C00 org BaseOfBoot %include "blfunc.asm" interface: BaseOfStack equ BaseOfBoot BaseOfTarget equ 0x9000 Target db "LOADER " TarLen equ ($-Target) BLMain: mov ax, cs mov ss, ax mov ds, ax mov es, ax mov sp, SPInitValue call LoadTarget cmp dx, 0 jz output jmp BaseOfTarget output: mov bp, ErrStr mov cx, ErrLen call Print jmp $ ErrStr db "No LOADER" ErrLen equ ($-ErrStr) Buffer: times 510-($-$$) db 0x00 db 0x55, 0xaa
loader.asm
BaseOfLoader equ 0x9000 org BaseOfLoader %include "blfunc.asm" %include "common.asm" interface: BaseOfStack equ BaseOfLoader BaseOfTarget equ 0xB000 Target db "KERNEL " TarLen equ ($-Target) [section .gdt] ; GDT definition ; Base, Limit, Attribute GDT_ENTRY : Descriptor 0, 0, 0 CODE32_FLAT_DESC : Descriptor 0, 0xFFFFF, DA_C + DA_32 CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32 ; GDT end GdtLen equ $ - GDT_ENTRY GdtPtr: dw GdtLen - 1 dd 0 ; GDT Selector Code32FlatSelector equ (0x0001 << 3) + SA_TIG + SA_RPL0 Code32Selector equ (0x0002 << 3) + SA_TIG + SA_RPL0 ; end of [section .gdt] [section .s16] [bits 16] BLMain: mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, SPInitValue ; initialize GDT for 32 bits code segment mov esi, CODE32_SEGMENT mov edi, CODE32_DESC call InitDescItem ; initialize GDT pointer struct mov eax, 0 mov ax, ds shl eax, 4 add eax, GDT_ENTRY mov dword [GdtPtr + 2], eax call LoadTarget cmp dx, 0 jz output ; 1. load GDT lgdt [GdtPtr] ; 2. close interrupt ; set IOPL to 3 cli pushf pop eax or eax, 0x3000 push eax popf ; 3. open A20 in al, 0x92 or al, 00000010b out 0x92, al ; 4. enter protect mode mov eax, cr0 or eax, 0x01 mov cr0, eax ; 5. jump to 32 bits code jmp dword Code32Selector : 0 output: mov bp, ErrStr mov cx, ErrLen call Print jmp $ ; esi --> code segment label ; edi --> descriptor label InitDescItem: push eax mov eax, 0 mov ax, cs shl eax, 4 add eax, esi mov word [edi + 2], ax shr eax, 16 mov byte [edi + 4], al mov byte [edi + 7], ah pop eax ret [section .s32] [bits 32] CODE32_SEGMENT: jmp dword Code32FlatSelector : BaseOfTarget Code32SegLen equ $ - CODE32_SEGMENT ErrStr db "No KERNEL" ErrLen equ ($-ErrStr) Buffer db 0
kentry.asm
global _start extern KMain [section .text] [bits 32] _start: mov ebp, 0 call KMain jmp $
kmain.c
#include "kernel.h" void KMain() { }
kernel.h暂时为空文件,无内容
makefile修改为:
.PHONY : all clean rebuild KERNEL_ADDR := B000 IMG := D.T.OS IMG_PATH := /mnt/hgfs DIR_DEPS := deps DIR_EXES := exes DIR_OBJS := objs DIRS := $(DIR_DEPS) $(DIR_EXES) $(DIR_OBJS) KENTRY_SRC := kentry.asm BLFUNC_SRC := blfunc.asm BOOT_SRC := boot.asm LOADER_SRC := loader.asm COMMON_SRC := common.asm KERNEL_SRC := kmain.c BOOT_OUT := boot LOADER_OUT := loader KERNEL_OUT := kernel KENTRY_OUT := $(DIR_OBJS)/kentry.o EXE := kernel.out EXE := $(addprefix $(DIR_EXES)/, $(EXE)) SRCS := $(wildcard *.c) OBJS := $(SRCS:.c=.o) OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS)) DEPS := $(SRCS:.c=.dep) DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS)) all : $(DIR_OBJS) $(DIR_EXES) $(IMG) $(BOOT_OUT) $(LOADER_OUT) $(KERNEL_OUT) @echo "Build Success ==> D.T.OS!" ifeq ("$(MAKECMDGOALS)", "all") -include $(DEPS) endif ifeq ("$(MAKECMDGOALS)", "") -include $(DEPS) endif $(IMG) : bximage $@ -q -fd -size=1.44 $(BOOT_OUT) : $(BOOT_SRC) $(BLFUNC_SRC) nasm $< -o $@ dd if=$@ of=$(IMG) bs=512 count=1 conv=notrunc $(LOADER_OUT) : $(LOADER_SRC) $(COMMON_SRC) $(BLFUNC_SRC) nasm $< -o $@ sudo mount -o loop $(IMG) $(IMG_PATH) sudo cp $@ $(IMG_PATH)/$@ sudo umount $(IMG_PATH) $(KENTRY_OUT) : $(KENTRY_SRC) $(COMMON_SRC) nasm -f elf $< -o $@ $(KERNEL_OUT) : $(EXE) ./elf2kobj -c$(KERNEL_ADDR) $< $@ sudo mount -o loop $(IMG) $(IMG_PATH) sudo cp $@ $(IMG_PATH)/$@ sudo umount $(IMG_PATH) $(EXE) : $(KENTRY_OUT) $(OBJS) ld -s $^ -o $@ $(DIR_OBJS)/%.o : %.c gcc -fno-builtin -fno-stack-protector -o $@ -c $(filter %.c, $^) $(DIRS) : mkdir $@ ifeq ("$(wildcard $(DIR_DEPS))", "") $(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c else $(DIR_DEPS)/%.dep : %.c endif @echo "Creating $@ ..." @set -e; \ gcc -MM -E $(filter %.c, $^) | sed 's,\(.*\)\.o[ :]*,objs/\1.o $@ : ,g' > $@ clean : rm -fr $(IMG) $(BOOT_OUT) $(LOADER_OUT) $(KERNEL_OUT) $(DIRS) rebuild : @$(MAKE) clean @$(MAKE) all