40 操作系统-从bootloader到内核雏形

参考

https://blog.51cto.com/13475106/category6.html及狄泰软件相关课程

 

一.整体的设计

40 操作系统-从bootloader到内核雏形
从上图可以得到一个问题,为什么不能从boot直接加载kernnel,并跳转运行?
该设计的思路
1.boot必须小于512字节,无法完成过多功能
2.kernel需要运行于32位保护模式(汇编+c语言)
3.使用loader中转:获取必要硬件信息,进入保护模式
进行的重构方案如下所示
40 操作系统-从bootloader到内核雏形
文件功能的定义
common.asm--常量定义,宏定义
blfunc.asm--实模式下的文件加载功能定义
boot.asm--加载loader并跳转[引导扇区]
loader.asm--在这里需要进行的操作是必要硬件初始化,加载kernel,进入保护模式,跳转到kernel执行

需要进行的修改
1.将之前定义的inc.asm修改成common.asm,并在makefile中的链接关系将其相关的名字进行修改
2.在loader.asm中相关的头文件进行修改
3.函数重构的实现--将其实现不同的接口[将之前的代码进行改写,细分模块
40 操作系统-从bootloader到内核雏形
blfunc.asm注意事项
1.%include "blfunc.asm"必须是第一条"包含"语句
2.%include "blfunc.asm"强制从BLMain标签处开始执行
3.Buffer为必要的内存缓冲区必须在代码末尾定义

内核雏形构造
40 操作系统-从bootloader到内核雏形
通过上图可知,kernel.out加载进内存后不能直接被x86处理器运行,产生的原因有三种
1.kernel.out是Linux系统中的可执行程序
2.Linux中的可执行程序为elf格式的文件-固定数据格式
3.处理器只认代码和数据,无法正确执行elf可执行程序
改进方法
1.提取elf文件中的代码段与数据段-删除elf文件格式信息
2.重定位提取后的代码和数据段,得到内核文件
3.加载内核文到内存-起始地址可自定义
4.跳转到内核入口地址处执行

在这里介绍一个工具,会有三种数据处理格式-如下图所示
40 操作系统-从bootloader到内核雏形
40 操作系统-从bootloader到内核雏形
所需的实验文件链接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

  

  

  

 

 

 

 

上一篇:20.有效的括号(栈L)


下一篇:8086汇编实验:寻址方式在结构化数据访问中的应用