终于到了第3天,这次作者太坑。搞的我差点放弃。这次是真的一天,一整天,拼起来24小时。今天我也终于实现了第一个内核,不再是简单的引导了。而且也进入CPU保护模式,这个就吊了。我还要吐槽下,为何其他很多中文博客TMD都是直接复制粘帖代码?坑啊。
IPL:
IPL(Initial Program Load),就是把系统导入内存然后跳入系统的入口。跟Booting是一个意思我才知道。这个阶段BIOS读入磁盘上的第一个扇区,一共512Bytes。在这个阶段,就是所谓的实模式。在这个阶段,只有16位,但是借助段寄存器使用超过16位的内存。主要任务,是读取操作系统所在扇区的内存并载入到相应内存位置。我后来用的是0x1000部分的内存(Memory Map)。
读取磁盘:
读取磁盘只要知道INT 0x13就行了。CH是柱面,DH是磁头,CL是要读的扇区位置, AL是要读的扇区数, AH=02表示读取。关键这里有的DL很坑,代表哪个磁盘,当然是0x00,但是我就傻逼了。我是直接生成img然后当作磁盘来跑,然后死也读不了。原因这时候要设成0x80。还有寄存器地址,主要跟ES和BX有关,具体地址是 ES:BX = ES*16 + BX, 一共fffff位,131072Bytes。还是不够用,这就是BIOS实模式的一个缺点。具体代码都在github。
1 2 3 4 5 6 7 8 | MOV AH,0x02 ; AH=0x02: bios read sector MOV AL,0x01 ; Read one MOV CL,0x02 ; sector 2 MOV CH,0x00 ; cylinder 0 MOV DH,0x00 MOV BX,0x1000 ;mem 0x1000 MOV DL,0x00 ; A driver INT 0x13 ; call |
载入内核:
这里的内核只是让CPU空转。具体步骤是,读入磁盘内容到内存,找到函数入口的地址(编译后找img中相应字段),然后再修改读完磁盘内容后jump的地址。我的地址是0x4400,作者的是0x4200,然后开始的位置是对应内存0x8000,加起来就是入口地址就是0xc400。jump即可。具体代码github。这个不是重点,重点是我学会了怎么挂载镜像,就像虚拟光驱一样的。并可以写入文件到这个img。
1 2 3 4 5 6 7 8 | #!/bin/bash nasm ipl.nas -o ipl.img nasm slef.nas -o slef.sys sudo mkdir /run/media/ipl # mnt sudo mount -o loop ipl.img /run/media/ipl/ sudo cp slef.sys /run/media/ipl/ # write file sudo umount /run/media/ipl # umount and file is in img xxd ipl.img > ipl.hex |
32位保护模式:
进入保护模式。这个是最复杂的部分,作者挖了个大坑,GDT,这个太复杂了。今天也不想说明了。然后作者的引导方式很奇怪,是要根据编译后的函数入口地址来调节的,很蛋疼,而且好像跟COFF和ELF的格式有关,我试了很久都没办法不用作者的工具成功实验。(我做这个系统的原则是不使用作者的工具)。 后来实在没有办法,我是看了这个指导,重新写了个逻辑结构好的内核。直接调用,编译后直接拼接,免去了上一步,挂载磁盘的麻烦,而且还要读那么多扇区麻烦。这个版本只要读第二个扇区。代码我新建了一个Scratch,说是Scratch,但我感觉要在这个基础上进行下一步了。看看结果。我超前了一点点,把图像显示了一下。
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 | [org 0x7c00] VMODE equ 0x0ff2 ;保留启动信息 SCRNX equ 0x0ff4 SCRNY equ 0x0ff6 VRAM equ 0x0ff8 ;显卡内存 MOV AL, 0x13 MOV AH, 0x00 INT 0x10 MOV BYTE [VMODE],8 MOV WORD [SCRNX],320 MOV WORD [SCRNY],200 MOV DWORD [VRAM],0x000a0000 ;内存地址 KERNEL_OFFSET equ 0x1000 ;kernel 载入内存位置 mov [BOOT_DRIVE], dl ;读磁盘 mov bp, 0x9000 ;设置stack地址,主要是push mov sp, bp mov bx, MSG_REAL_MODE call print_string call load_kernel call switch_to_pm ;切换到保护模式 jmp $ %include "print_string.asm" %include "print_string_pm.asm" %include "disk_load.asm" %include "gdt.asm" %include "switch_to_pm.asm" [bits 16] load_kernel: mov bx, MSG_LOAD_KERNEL call print_string mov bx, KERNEL_OFFSET mov dh, 15 mov dl, [BOOT_DRIVE] call disk_load ret [bits 32] BEGIN_PM: ;成功载入,执行内核程序 mov ebx, MSG_PROT_MODE ; call print_string_pm call KERNEL_OFFSET jmp $ BOOT_DRIVE db 0 MSG_REAL_MODE db "Started in 16-bit Real Mode",0 MSG_PROT_MODE db "Successfully landed in 32-bit Protected Mode",0 MSG_LOAD_KERNEL db "Loading kernel into memory.",0 times 510 -( $ - $$ ) db 0 dw 0xaa55 |
链接: