1. 如何让内核突破512字节?
boot.asm
和setup.asm
编译后生成对应的boot.o
和setup.o
文件,boot.o
由dd
命令写入硬盘的0磁道0柱面1扇区
,由BIOS例程
自动读入内存的0x7c00
处;setup.o
通过dd
命令写入硬盘的0磁道0柱面2扇区
,并且写两个扇区,然后由boot
读入内存。
2. 对 CMOS、BIOS、MBR、GPT、GRUB、UEFI基本认识。
总结:
- mbr是老版,gpt是新版
- mbr仅支持4个分区,gpt支持128个分区
- mbr支持最大2TB,gpt最大支持无限制
- 一般搭配:
bios
+mbr
、uefi
+gpt
3. 硬盘工作原理、汇编实现同步读写硬盘。
文件系统、OS如何操作硬盘,如下图:
lba28以及lab48
LBA28 和 LBA48 是指逻辑块寻址(Logical Block Addressing, LBA)的两种模式,它们用于硬盘驱动器的数据寻址。这些模式定义了硬盘可以寻址的最大范围。
- LBA28可以寻址的最大存储容量是 2^28×512 字节,即128GB。
- LBA28可以寻址的最大存储容量是 2^48×512 字节,即128PB(Petabytes,1PB 等于 1024 TB)。
- IO端口分配地址以及硬盘控制器
除了0x1F0
为两个字节外,其余的都为一个字节,其中0x1F3 ~ 0x1F5
分别为lab28
低八位、中八位以及高八位,再加上0x1F6
的低四位,总共28位。
- 读写硬盘的一般流程
- 操作多少个扇区
- 往0x1f2写入操作的扇区数
- 从哪开始操作?
- 通过
0x1F3 ~ 0x1F6
四个寄存器拼起来的
- 给硬盘发送读命令
- 读0x1F7,判断第4位和7位
- 读:获取硬盘的状态 0x20 读
写:给硬盘发命令 0x30 写
0xec 获取硬盘信息
- 读0x1F0端口,读256次,一次2个字节,总共512字节一个扇区。
- 汇编实现同步写硬件
1)基本框架
;boot.asm ;0磁道0柱面1扇区 [ORG 0x7c00] [SECTION .data] BOOT_MAIN_ADDR equ 0x500 [SECTION .text] [BITS 16] global _start _start: ;设置屏幕输出文本模式,清除屏幕 mov ax, 3 int 0x10 ;跳过去 mov si, jmp_to_setup call print jmp $ ;如何调用 ;mov si, msg ;1.传入字符串 ;call print ;2 调用 print: mov ah, 0x0e mov bh, 0 mov bl, 0x10 .loop: mov al, [si] cmp al, 0 jz .done int 0x10 inc si jmp .loop .done: ret jmp_to_setup: db "jump to setup...", 10, 13, 0 times 510 - ($ - $$) db 0 db 0x55, 0xaa
2)增加汇编代码实现对硬件的同步读
列出0x1F7寄存器图:
下图分析是对的
在基本框架基础上增加寄存器操作实现boot.asm:
;0磁道0柱面1扇区 [ORG 0x7c00] [SECTION .data] BOOT_MAIN_ADDR equ 0x500 ;BOOT_MAIN_ADDR值为0x500 [SECTION .text] [BITS 16] global _start _start: ;设置屏幕输出文本模式,清除屏幕 mov ax, 3 int 0x10 mov ecx, 2 ; 从硬盘哪个扇区开始读 mov bl, 2 ;读取扇区的数量 ; 0x1f2 8bit 指定读取或写入的扇区数 mov dx, 0x1f2 mov al, bl out dx, al ;out指令仅能与累加寄存器使用al、ax、eax ; 0x1f3 8bit iba地址的第八位 0-7 inc dx ;增加DX寄存器的值(指向下一个端口0x1f3) mov al, cl out dx, al ; 0x1f4 8bit iba地址的中八位 8-15 inc dx mov al, ch out dx, al ; 0x1f5 8bit iba地址的高八位 16-23 inc dx shr ecx, 16 mov al, cl out dx, al ; 0x1f6 8bit ; 0-3 位iba地址的24-27 ; 4 0表示主盘 1表示从盘 ; 5、7位固定为1 ; 6 0表示CHS模式,1表示LAB模式 inc dx mov al, ch and al, 0b1110_1111 out dx, al ; 0x1f7 8bit 命令或状态端口 inc dx mov al, 0x20 ;读 out dx, al ; 验证状态 ; 3 0表示硬盘未准备好与主机交换数据 1表示准备好了 ; 7 0表示硬盘不忙 1表示硬盘忙 ; 0 0表示前一条指令正常执行 1表示执行出错 出错信息通过0x1f1端口获得 .read_check: mov dx, 0x1f7 in al,dx and al, 0b1000_0100 ; 取硬盘状态的第3、7位 cmp al, 0b0000_0100 ;硬盘数据准备好了且不忙了 jnz .read_check ;读数据 mov dx, 0x1f0 mov cx, 256 ;loop次数 mov edi, BOOT_MAIN_ADDR ;将EDI寄存器设置为0x500 .read_data: in ax, dx mov [edi], ax ;把ax的值给edi所指向的内存地址 add edi, 2 loop .read_data ;跳过去 mov si, jmp_to_setup call print jmp BOOT_MAIN_ADDR ;如何调用 ;mov si, msg ;1.传入字符串 ;call print ;2 调用 print: mov ah, 0x0e mov bh, 0 mov bl, 0x10 .loop: mov al, [si] cmp al, 0 jz .done int 0x10 inc si jmp .loop .done: ret jmp_to_setup: db "jump to setup...", 10, 13, 0 times 510 - ($ - $$) db 0 db 0x55, 0xaa
setup.asm实现:
; 0柱面0磁道2扇區 [ORG 0x500] [SECTION .text] [BITS 16] global _start _start: ;寄存器初始化操作 mov ax, 0 mov ss, ax mov ds, ax mov es, ax mov fs, ax mov gs, ax mov si, ax mov si, msg call print jmp $ ; 如何调用 ; mov si, msg ; 1 传入字符串 ; call print ; 2 调用 print: mov ah, 0x0e mov bh, 0 mov bl, 0x01 .loop: mov al, [si] cmp al, 0 jz .done int 0x10 inc si jmp .loop .done: ret msg: db "hello", 10, 13, 0
3)运行成功效果图
运行完boot
后打印输出jump to setup...
,打印结束后通过jmp BOOT_MAIN_ADDR
跳到setup
执行。