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