Explorar o código

将内核载入内存

runningwater hai 1 ano
pai
achega
604e2b192b
Modificáronse 8 ficheiros con 159 adicións e 13 borrados
  1. 1 1
      .gitignore
  2. 15 2
      boot/include/boot.inc
  3. 136 2
      boot/loader.S
  4. 1 1
      boot/mbr.S
  5. 4 6
      build.sh
  6. 1 1
      clean.sh
  7. BIN=BIN
      kernel/doc/layout_memory.png
  8. 1 0
      kernel/main.c

+ 1 - 1
.gitignore

@@ -68,6 +68,6 @@ Icon
 Network Trash Folder
 Temporary Items
 .apdisk
-bochsout.txt
+bochs_log.txt
 a.img
 *.bin

+ 15 - 2
boot/include/boot.inc

@@ -1,9 +1,13 @@
-;--------------------------- loader和kernel --------------------------------------------------------------------------------------------------------------------
-MBR_BASE_ADDR equ 0x7c00   ; mbr 在内存中的位置
+; --------------------------- loader和kernel --------------------------------------------------------------------------------------------------------------------
 LOADER_BASE_ADDR equ 0x900 ; loader 在内存中的位置 
 LOADER_START_SECTOR equ 0x2 ; loader 在硬盘上的逻辑扇区地址,即 LBA 地址 
+
 PAGE_DIR_TABLE_POS equ 0x100000 ; 页目录表物理地址
 
+KERNEL_START_SECTOR equ 0x9   ; kernel 在硬盘上的逻辑扇区地址
+KERNEL_BIN_BASE_ADDR equ 0x70000 ; kernel 在内存中的位置
+KERNEL_ENTRY_POINT equ 0xc0001500
+
 ; ---------------------------- GDT 描述符属性 -----------------------------------------------------------------------------------------------------------------------
 ; 段描述符格式
 ;  31~24  23  22  21  20  19~16   15  14~13 12  11~8   7~0
@@ -58,3 +62,12 @@ PG_RW_R equ 00b
 PG_RW_W equ 10b 
 PG_US_S equ 000b
 PG_US_U equ 100b
+
+; -------------------------------- 程序中的段类型 p_type -----------------------------------------------------------------------------------------------------------
+PT_NULL equ 0
+PT_LOAD equ 1      ; 可加载程序段
+PT_DYNAMIC equ 2   ; 动态链接信息
+PT_INTERP equ 3    ; 动态加载器名称
+PT_NOTE equ 4      ; 一些辅助的附加信息
+PT_SHLIB equ 5     ; 保留
+PT_PHDR equ 6      ; 程序头表

+ 136 - 2
boot/loader.S

@@ -185,6 +185,14 @@ p_mode_start:
     mov ax, SELECTOR_VIDEO
     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
 
@@ -214,7 +222,13 @@ p_mode_start:
     ; 在开启分页后,用 gdt 新的地址重新加载
     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 $
 
@@ -285,4 +299,124 @@ setup_page:
         inc esi
         add eax, 0x1000
         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

+ 1 - 1
boot/mbr.S

@@ -1,7 +1,7 @@
 ; 主引导程序
 ; ----------------------------------------------------------------
 %include "boot.inc"
-SECTION MBR vstart=MBR_BASE_ADDR
+SECTION MBR vstart=0x7c00
     mov ax, cs ; 0x00000000
     mov ds, ax
     mov es, ax

+ 4 - 6
build.sh

@@ -1,22 +1,20 @@
 #!/bin/sh
 
-echo "Creating disk image..."
-bximage -q -func=create -hd=30M hd30M.img
-
 echo "Compiling..."
 nasm -I boot/include/ -o boot/mbr.bin boot/mbr.S
 nasm -I boot/include/ -o boot/loader.bin boot/loader.S
 i386-elf-gcc -c -o kernel/main.o kernel/main.c && i386-elf-ld kernel/main.o -Ttext 0xc0001500 -e main -o kernel/kernel.bin
 
+echo "Creating disk image..."
+bximage -q -func=create -hd=30M hd30M.img
+
 echo "Installing to disk image..."
 echo " 0. Writing mbr to disk..."
 dd if=boot/mbr.bin of=hd30M.img bs=512 count=1 conv=notrunc
 echo " 1. Writing loader to disk..."
 dd if=boot/loader.bin of=hd30M.img bs=512 count=4 seek=2 conv=notrunc
 echo " 2. Writing kernel to disk..."
-dd if=kernel/kernel.bin of=hd30M.img bs=512 count=2000 seek=9 conv=notrunc
-# echo " 2. Writing kernel to disk..."
-# TODO: Add code to write the kernel to the disk image
+dd if=kernel/kernel.bin of=hd30M.img bs=512 count=200 seek=9 conv=notrunc
 echo "Disk image created successfully."
 
 # echo "Now starting bochs ..."

+ 1 - 1
clean.sh

@@ -1,3 +1,3 @@
 #!/bin/sh
 
-rm -rf bochsout.txt hd30M.img *.bin boot/*.bin kernel/kernel.bin kernel/main.o
+rm -rf bochs_log.txt hd30M.img *.bin boot/*.bin kernel/kernel.bin kernel/main.o

BIN=BIN
kernel/doc/layout_memory.png


+ 1 - 0
kernel/main.c

@@ -1,3 +1,4 @@
+
 int main(void)
 {
     while (1)