40 操作系统-从bootloader到内核雏形
参考
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | ############################################################### # 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | ; 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | 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
1 2 3 4 5 6 7 8 9 10 11 12 | global _start extern KMain [section .text] [bits 32] _start: mov ebp, 0 call KMain jmp $ |
kmain.c
1 2 3 4 5 6 | #include "kernel.h" void KMain() { } |
kernel.h暂时为空文件,无内容
makefile修改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | .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 |
posted on 2021-04-04 17:14 lh03061238 阅读(244) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2020-04-04 08.泛型编程简介
2020-04-04 C语言中的指针和内存泄漏