|
@@ -185,6 +185,14 @@ p_mode_start:
|
|
|
mov ax, SELECTOR_VIDEO
|
|
mov ax, SELECTOR_VIDEO
|
|
|
mov gs, ax
|
|
mov gs, ax
|
|
|
|
|
|
|
|
|
|
+ ; ------------------------ 加载内核 kernel ------------------------
|
|
|
|
|
+ ;!! 加载到 0x70000~0x9fbff, 190KB 的字节空间,kernel 不超过 100KB
|
|
|
|
|
+ mov eax, KERNEL_START_SECTOR ; kernel.bin 所在的扇区号
|
|
|
|
|
+ mov ebx, KERNEL_BIN_BASE_ADDR
|
|
|
|
|
+ mov ecx, 200 ; 读入的扇区数
|
|
|
|
|
+
|
|
|
|
|
+ call rd_disk_m_32
|
|
|
|
|
+
|
|
|
; 创建页目录及页表并初始化内存位图
|
|
; 创建页目录及页表并初始化内存位图
|
|
|
call setup_page
|
|
call setup_page
|
|
|
|
|
|
|
@@ -214,7 +222,13 @@ p_mode_start:
|
|
|
; 在开启分页后,用 gdt 新的地址重新加载
|
|
; 在开启分页后,用 gdt 新的地址重新加载
|
|
|
lgdt [gdt_ptr]
|
|
lgdt [gdt_ptr]
|
|
|
|
|
|
|
|
- mov byte [gs:160], 'V' ; 视频段段基址已经被更新,用字符 V 表示 virtual addr
|
|
|
|
|
|
|
+ ; 初始化 kernel
|
|
|
|
|
+ jmp SELECTOR_CODE:enter_kernel
|
|
|
|
|
+
|
|
|
|
|
+ enter_kernel:
|
|
|
|
|
+ call kernel_init
|
|
|
|
|
+ mov esp, 0xc009f000
|
|
|
|
|
+ jmp KERNEL_ENTRY_POINT
|
|
|
|
|
|
|
|
jmp $
|
|
jmp $
|
|
|
|
|
|
|
@@ -285,4 +299,124 @@ setup_page:
|
|
|
inc esi
|
|
inc esi
|
|
|
add eax, 0x1000
|
|
add eax, 0x1000
|
|
|
loop .create_kernel_pde
|
|
loop .create_kernel_pde
|
|
|
- ret
|
|
|
|
|
|
|
+ ret
|
|
|
|
|
+
|
|
|
|
|
+; ----------------------------------------------------------------
|
|
|
|
|
+; 读取硬盘数据
|
|
|
|
|
+; EAX=LBA 地址
|
|
|
|
|
+; EBX=将数据写入的内存地址
|
|
|
|
|
+; ECX=读取的扇区数
|
|
|
|
|
+rd_disk_m_32:
|
|
|
|
|
+; ----------------------------------------------------------------
|
|
|
|
|
+ mov esi, eax ; 备份 eax
|
|
|
|
|
+ mov edi, ecx ; 备份 ecx
|
|
|
|
|
+
|
|
|
|
|
+ ; 1. 设置要读取的扇区数
|
|
|
|
|
+ mov dx, 0x1f2
|
|
|
|
|
+ mov al, cl
|
|
|
|
|
+ out dx, al
|
|
|
|
|
+
|
|
|
|
|
+ mov eax, esi ; 恢复 eax
|
|
|
|
|
+
|
|
|
|
|
+ ; 2. 设置 LBA 地址,存入 0x1f3 - 0x1f6 端口中
|
|
|
|
|
+ ; LBA 地址 7 ~ 0 位写入端口 0x1f3
|
|
|
|
|
+ mov dx, 0x1f3
|
|
|
|
|
+ out dx, al
|
|
|
|
|
+
|
|
|
|
|
+ ; LBA 地址 15 ~ 8 位写入端口 0x1f4
|
|
|
|
|
+ mov cl, 8
|
|
|
|
|
+ shr eax, cl ; 右移 8 位
|
|
|
|
|
+ mov dx, 0x1f4
|
|
|
|
|
+ out dx, al
|
|
|
|
|
+
|
|
|
|
|
+ ; LBA 地址 23 ~ 16 位写入端口 0x1f5
|
|
|
|
|
+ shr eax, cl
|
|
|
|
|
+ mov dx, 0x1f5
|
|
|
|
|
+ out dx, al
|
|
|
|
|
+
|
|
|
|
|
+ shr eax, cl
|
|
|
|
|
+ and al, 0x0f ; 设置 LBA 地址 24~27 位
|
|
|
|
|
+ or al, 0xe0 ; 设置 7~4 位为 1110, 表示 LBA 模式
|
|
|
|
|
+ mov dx, 0x1f6 ; Device
|
|
|
|
|
+ out dx, al
|
|
|
|
|
+
|
|
|
|
|
+ ; 3. 向 0x1f7 端口写入命令 0x20
|
|
|
|
|
+ mov dx, 0x1f7 ; Command
|
|
|
|
|
+ mov al, 0x20
|
|
|
|
|
+ out dx, al
|
|
|
|
|
+
|
|
|
|
|
+ ; 4. 检测硬盘状态
|
|
|
|
|
+ ; 使用同一端口0x1f7,写时表示写入命令字,读时表示读入硬盘状态
|
|
|
|
|
+ .not_ready:
|
|
|
|
|
+ nop ; 什么也不做,目的是为了减少对硬盘的打扰
|
|
|
|
|
+ in al, dx ; 读入硬盘状态
|
|
|
|
|
+ and al, 0x88 ; 第 4 位为 1 表示硬盘控制器已准备好数据传输,第 7 位为 1 表示硬盘忙
|
|
|
|
|
+ cmp al, 0x08
|
|
|
|
|
+ jnz .not_ready ; 硬盘没有准备数据传输,继续等待
|
|
|
|
|
+
|
|
|
|
|
+ ; 5. 从 0x1f0 端口读取数据
|
|
|
|
|
+ ; di 为要读取的扇区数,一个扇区 512 字节,每次读入一个字,共需要 di*512/2 次,所以 di*256
|
|
|
|
|
+ mov ax, di
|
|
|
|
|
+ mov dx, 256
|
|
|
|
|
+ mul dx ; dx:ax = ax * 256
|
|
|
|
|
+ mov cx, ax
|
|
|
|
|
+
|
|
|
|
|
+ mov dx, 0x1f0
|
|
|
|
|
+ .go_on_read:
|
|
|
|
|
+ in ax, dx
|
|
|
|
|
+ mov [ebx], ax
|
|
|
|
|
+ add ebx, 2
|
|
|
|
|
+ loop .go_on_read
|
|
|
|
|
+
|
|
|
|
|
+ ret
|
|
|
|
|
+
|
|
|
|
|
+; ------------------------ 将 kernel.bin 中的 segment 拷贝到编译的地址 ------------------------
|
|
|
|
|
+kernel_init:
|
|
|
|
|
+ xor eax, eax
|
|
|
|
|
+ xor ebx, ebx ; ebx 记录程序头表地址
|
|
|
|
|
+ xor ecx, ecx ; cx 记录程序头表的 program header 数量
|
|
|
|
|
+ xor edx, edx ; dx 记录 program header 尺寸,即 e_phentsize
|
|
|
|
|
+
|
|
|
|
|
+ mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件 42 字节处的属性是 e_phentsize,表示 program header 大小
|
|
|
|
|
+ mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件 28 字节的地方是 e_phoff, 表示第 1 个 program header 在文件中的偏移量
|
|
|
|
|
+ add ebx, KERNEL_BIN_BASE_ADDR
|
|
|
|
|
+ mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分 44 字节的地方是 e_phnum, 表示有几个 program header
|
|
|
|
|
+
|
|
|
|
|
+ .each_segment:
|
|
|
|
|
+ cmp byte [ebx], PT_NULL ; 若 p_type 等于 PT_NULL,说明此 program header 未使用
|
|
|
|
|
+ je .PTNULL
|
|
|
|
|
+
|
|
|
|
|
+ ; 为函数 memycpy 压入参数, 参数是从右到左依然压入
|
|
|
|
|
+ ; 函数原型 memycpy(dst, src, size)
|
|
|
|
|
+ push dword [ebx + 16] ; program header 中偏移 16 字节的地方是 p_filesz 第三个参数: size
|
|
|
|
|
+ mov eax, [ebx + 4] ; 距程序头偏移量为 4 字节的位置是 p_offset
|
|
|
|
|
+ add eax, KERNEL_BIN_BASE_ADDR ; 加上 kernel.bin 被加载的物理地址, eax 为该段的物理地址
|
|
|
|
|
+ push eax ; 第二个参数: 源地址 src
|
|
|
|
|
+ push dword [ebx + 8] ; 偏移程序头 8 字节位置是p_vaddr, 目的地址
|
|
|
|
|
+ call mem_cpy ; 调用 mem_cpy 完成段自制
|
|
|
|
|
+ add esp, 12 ; 清理栈中压入的三个参数
|
|
|
|
|
+
|
|
|
|
|
+ .PTNULL
|
|
|
|
|
+ add ebx, edx ; edx 为 program header 大小,此时 ebx 指向下一个 program header
|
|
|
|
|
+ loop .each_segment
|
|
|
|
|
+ ret
|
|
|
|
|
+
|
|
|
|
|
+; -------------------------------- 逐字节拷贝 mem_cpy(dst, src, size) --------------------------------
|
|
|
|
|
+; 输入:栈中三个参数 (dst, src, size)
|
|
|
|
|
+; 输出: 无
|
|
|
|
|
+; --------------------------------------------------------------------------------------------------
|
|
|
|
|
+mem_cpy:
|
|
|
|
|
+ cld ; mem_cpy 使 DF (Direction Flag) 复位 DF = 0
|
|
|
|
|
+ push ebp
|
|
|
|
|
+ mov ebp, esp
|
|
|
|
|
+ push ecx ; rep 指令用到了 ecx, 故先备份
|
|
|
|
|
+
|
|
|
|
|
+ mov edi, [ebp + 8] ; dst ES:DI
|
|
|
|
|
+ mov esi, [ebp + 12] ; src DS:SI
|
|
|
|
|
+ mov ecx, [ebp + 16] ; size
|
|
|
|
|
+ rep movsb ; 逐字节拷贝
|
|
|
|
|
+
|
|
|
|
|
+ ; 恢复环境
|
|
|
|
|
+ pop ecx
|
|
|
|
|
+ pop ebp
|
|
|
|
|
+ ret
|