《真象还原》读书笔记——第五章 保护模式进阶,向内核迈进(加载内核)(代码部分,更新)

mbr.asm 没有什么改变

%include "boot.inc"
section MBR vstart=0x7c00
	mov ax,cs
	mov ds,ax
	mov es,ax
	mov ss,ax
	mov fs,ax
	mov sp,0x7c00 
	mov ax,0xb800 
	mov gs,ax 

;使用 int 0x10 功能号 0x06 清屏
	mov ah,0x06
	mov al,0x00		;上卷行数 0->全部
	mov bl,0x00
	mov bh,0x07
	mov cx,0x0000 ;左上( 0, 0)
	mov dx,0x184f ;右下(80,25)
	int 0x10

; ---------- 在显存中输入信息 ---------
; 输出背景为 黄 色,前景 红 色,并且跳动的字符串 "HELLO WORLD"
	mov bx,0
	mov cx,14
	mov si,0
print:
	mov al,[s+si]
	mov [gs:bx],al
	inc bx
	mov byte [gs:bx],0x24
	inc bx
	add si,1
loop print
	
	mov eax, LOADER_START_SECTOR
	mov bx, LOADER_BASE_ADDR
	mov cx, 4

	call rd_disk_m_16
	jmp LOADER_BASE_ADDR+0x300

rd_disk_m_16:
	mov esi,eax
	mov di,cx

	;指定扇区数0x1f2 8bit
	mov dx,0x1f2
	mov al,cl 
	out dx,al

	mov eax,esi 
	;LBA 0-7 8bit
	mov dx,0x1f3
	out dx,al 
	;LBA 8-15 8bit
	mov cl,8
	shr eax,cl
	mov dx,0x1f4
	out dx,al
	;LBA 16-23
	shr eax,cl 
	mov dx,0x1f5
	out dx,al 
	;LBA Device | 24-27
	shr eax,cl 
	and al,0000_1111b
	or  al,1110_0000b ;LBA, DEV主盘
	mov dx,0x1f6
	out dx,al 

	;写入命令
	mov dx,0x1f7
	mov al,0x20
	out dx,al 

	;检测硬盘状态
.not_ready:
	nop ;相当于个停顿
	in al,dx 
	and al,1000_1000b ;忙/准备好
	cmp al,0000_1000b
jnz .not_ready

	;读数据
	mov ax,di
	mov dx,256 ;512/2
	mul dx 
	mov cx,ax ;一共ax个字节

	mov dx,0x1f0
.read_data:
	in ax,dx 
	mov [bx],ax 
	add bx,2
	loop .read_data
ret



s db "1 MBR_start!!!"
times 510 - ($-$$) db 0
db 0x55, 0xaa

loader

  1. 增加了读硬盘32位模式
  2. 将内核从硬盘中读到内存中
  3. 将内存中的kernel中的段 cpy到指定的虚拟地址去
  4. 通过跳转指令进入内核
%include "boot.inc"

section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP    equ LOADER_BASE_ADDR
;jmp loader_start

GDT_BASE:   dd 0x00000000
            dd 0x00000000

CODE_DESC:  dd 0x0000FFFF
            dd DESC_CODE_HIGH4

DATA_STACK_DESC:    dd  0x0000FFFF
                    dd  DESC_DATA_HIGH4

VIDEO_DESC: dd 0x80000007
            dd DESC_VIDEO_HIGH4

GDT_SIZE    equ $-GDT_BASE
GDT_LIMIT   equ GDT_SIZE-1
times 60 dq 0   ;预留60个描述符的位子

;选择子
SELECTOR_CODE equ  (0x0001<<3) + TI_GDT + RPL0
SELECTOR_DATA equ  (0x0002<<3) + TI_GDT + RPL0
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0

total_mem_bytes dd 0 ;4 保存内存容量
;gdt指针
gdt_ptr dw  GDT_LIMIT ;6
        dd  GDT_BASE
ards_buf times 244 db 0 ;244 ards结构体缓存
ards_nr dw 0    ;2 ards结构体数量,一共 4+6+244+2 = 256字节

loader_start:
;==== 读取内存数
    ;int 15h eax,ebx,es:di,ecx,edx
    xor ebx,ebx  ;ARDS后续值,第一次为0
    mov edx,0x534d4150  ;固定为签名标记
    mov di,ards_buf     ;ards结构缓冲区
    .e820_mem_get_loop:
        mov eax,0x0000e820
        mov ecx,20      ;ards 地址范围描述符结构是20字节
        int 0x15

        jc .e820_failed_so_try_e801 ;若cf为1,则发生错误,常识0xe801

        add di,cx   ;cx是读到的结构缓冲区中数据的字节数
        inc word [ards_nr]
        cmp ebx,0   ;如果是0说明这是最后一个ARDS结构
        jnz .e820_mem_get_loop

        ;找出(base_add_low + length_low)的最大值,也就是内存容量
        mov cx,[ards_nr]
        mov ebx,ards_buf
        xor edx,edx
        .find_max_mem_area:
            mov eax,[ebx]
            add eax,[ebx+8]
            add ebx,20  ;ebx存放 ARDS缓存内容地址
            cmp edx,eax ;比较 edx 与 eax大小
            jge .next_ards
            mov edx,eax
            .next_ards:
            loop .find_max_mem_area
        jmp .mem_get_ok


    ;0x15 0xe801
    .e820_failed_so_try_e801:
        mov ax,0xe801
        int 0x15
        jc .e801_failed_so_try88
        ;1 先算出低15MB的内存
        mov cx,0x400;ax KB单位2^10 ,0x400-> 1024 
        mul cx
        shl edx,16
        and eax,0x0000FFFF
        or edx,eax 
        add edx, 0x100000 ;补上1MB
        mov esi,edx ;先放在esi中
        ;2 算>16MB的内存 bx 64KB为单位
        xor eax,eax
        mov ax,bx
        mov ecx,0x10000 ;将64KB单位转为byte
        mul ecx
        add esi,eax ;<=15 + >16

        jmp .mem_get_ok


    .e801_failed_so_try88:
        mov ah,0x88
        int 0x15
        jc .error_hlt
        and eax,0x0000FFFF ;ax*1024+1MB
        mov cx,0x400
        mul cx          ;ax*1024->dx,ax
        shl edx,16
        or edx,eax
        add edx,0x100000 ;+1MB
        
    .mem_get_ok:
        mov [total_mem_bytes],edx 
    .error_hlt:
    mov byte [gs:160],'2'
    mov byte [gs:162],' '
    mov byte [gs:164],'0'
    mov byte [gs:166],'x'
    mov byte [gs:168],'1'
    mov byte [gs:170],'5'
;====

;1 打开 A20
    in al,0x92
    or al,0x02
    out 0x92,al 

;2 加载 GDT
lgdt [gdt_ptr]

;3 cr0 pe置1
mov eax,cr0
or eax,0x01
mov cr0,eax

jmp dword SELECTOR_CODE:p_mode_start

[bits 32]
p_mode_start:
    mov ax,SELECTOR_DATA
    mov ds,ax
    mov es,ax
    mov ss,ax 
    mov esp,LOADER_STACK_TOP
    mov ax,SELECTOR_VIDEO
    mov gs,ax 

    mov byte [gs:320],'3'
    mov byte [gs:322],' '
    mov byte [gs:324],'P'
    mov byte [gs:326],'r'
    mov byte [gs:328],'o'
    mov byte [gs:330],'t'
    mov byte [gs:332],'e'
    mov byte [gs:334],'c'
    mov byte [gs:336],'t'
    mov byte [gs:338],'i'
    mov byte [gs:340],'o'
    mov byte [gs:342],'n'
    mov byte [gs:344],' '
    mov byte [gs:346],'m'
    mov byte [gs:348],'o'
    mov byte [gs:350],'d'
    mov byte [gs:352],'e'
    mov byte [gs:354],'.'

;---读取硬盘---
mov eax,KERNEL_START_SECTOR
mov ebx,KERNEL_BIN_BASE_ADDR
mov ecx,200
call rd_disk_m_32

;---创建页表---
call setup_page

sgdt[gdt_ptr]
mov ebx,[gdt_ptr+2] ;ebx = GDT_BASE
or dword [ebx+0x18+4],0xc0000000 ; GDT_BASE+3*8+4,到了VIDEO的高4字节

add dword [gdt_ptr+2],0xc0000000; 为之后的高系统区1GB映射做准备
add esp,0xc0000000

mov eax,PAGE_DIR_TABLE_POS
mov cr3,eax 

mov eax,cr0
or eax,0x80000000
mov cr0,eax ;开启cr0 31位

lgdt [gdt_ptr] ;重新加载GDT

mov byte [gs:480],'4'
mov byte [gs:482],' '
mov byte [gs:484],'P'
mov byte [gs:486],'D'
mov byte [gs:488],'E'


;--- 强刷流水线 进入内核 ---
jmp SELECTOR_CODE:enter_kernel
enter_kernel:
    call kernel_init
    mov esp,0xc009f000
    mov byte [gs:640],'5'
    mov byte [gs:642],' '
    mov byte [gs:644],'K'
    jmp KERNEL_ENTRY_POINT

;----- 创建页目录及页表 -----
setup_page:
    mov ecx,4096 ;1024*4 1024个页目录项
    mov esi,0
    .clear_page_dir:
        mov byte [PAGE_DIR_TABLE_POS+esi],0
        inc esi 
        loop .clear_page_dir

    .create_pde:
        mov eax,PAGE_DIR_TABLE_POS
        add eax,0x1000
        mov ebx,eax 

        or eax,PG_US_U | PG_RW_W | PG_P
        mov [PAGE_DIR_TABLE_POS+0x0],eax 
        mov [PAGE_DIR_TABLE_POS+0xc00],eax 

        sub eax, 0x1000
        mov [PAGE_DIR_TABLE_POS+4092],eax ;将目录页首地址放在最后一个目录项中

        ;创建页表项
        mov ecx,256
        mov esi,0
        mov edx,PG_US_U | PG_RW_W | PG_P
        .create_pte:
            mov [ebx+esi*4],edx 
            add edx,4096
            inc esi 
            loop .create_pte

        ;创建高1GB中的其他页目录项
        mov eax,PAGE_DIR_TABLE_POS
        add eax,0x2000
        or eax, PG_US_U | PG_RW_W | PG_P
        mov ebx, PAGE_DIR_TABLE_POS
        mov ecx,254
        mov esi,769 
        ;768是1G中的第一个目录项
        .create_kernel_pde:
            mov [ebx+esi*4],eax
            inc esi
            add eax,0x1000
            loop .create_kernel_pde
ret 

;----- kernel.bin 中的 segment 拷贝到编译的地址
kernel_init:
    xor eax,eax
    xor ebx,ebx
    xor ecx,ecx
    xor edx,edx 

    mov dx,[KERNEL_BIN_BASE_ADDR+42] ;program header大小
    mov ebx,[KERNEL_BIN_BASE_ADDR+28];program header偏移量
    add ebx,KERNEL_BIN_BASE_ADDR
    mov cx,[KERNEL_BIN_BASE_ADDR+44] ;program header数量
.each_segment:
    cmp byte [ebx+0],PT_NULL
    je .PTNULL
    
    ;memcpy(dst, src, size)
    push dword[ebx+16];size
    mov eax, [ebx+4]
    add eax, KERNEL_BIN_BASE_ADDR
    push eax ;src 源地址
    push dword [ebx+8] ; dst 目的地址
    call mem_cpy
    add esp,12
.PTNULL:
    add ebx,edx 
    loop .each_segment
ret 
;------ mem_cpy (dst, src, size) 逐字节cpy---
mem_cpy:
    cld 
    push ebp
    mov ebp,esp 
    push ecx 
    
    mov edi,[ebp+8]
    mov esi,[ebp+12]
    mov ecx,[ebp+16]
    rep movsb

    pop ecx 
    pop ebp 
ret 
;--- rd_disk_m_32 ---
rd_disk_m_32:
	mov esi,eax
	mov di,cx 

	;指定扇区数0x1f2 8bit
	mov dx,0x1f2
	mov al,cl 
	out dx,al

	mov eax,esi 
	;LBA 0-7 8bit
	mov dx,0x1f3
	out dx,al 
	;LBA 8-15 8bit
	mov cl,8
	shr eax,cl
	mov dx,0x1f4
	out dx,al
	;LBA 16-23
	shr eax,cl 
	mov dx,0x1f5
	out dx,al 
	;LBA Device | 24-27
	shr eax,cl 
	and al,0000_1111b
	or  al,1110_0000b ;LBA, DEV主盘
	mov dx,0x1f6
	out dx,al 

	;写入命令
	mov dx,0x1f7
	mov al,0x20
	out dx,al 

	;检测硬盘状态
.not_ready_32:
	nop ;相当于个停顿
	in al,dx 
	and al,1000_1000b ;忙/准备好
	cmp al,0000_1000b
    jnz .not_ready_32

	;读数据
	mov ax,di
	mov dx,256 ;512/2
	mul dx 
	mov cx,ax ;一共ax个字节

	mov dx,0x1f0
.read_data_32:
	in ax,dx 
	mov [ebx],ax 
	add ebx,2
	loop .read_data_32
ret

boot.inc

  1. 增加了内核部分的4条宏定义
; loader and kernel
LOADER_BASE_ADDR    equ 0x900
LOADER_START_SECTOR equ 0x2

; gdt 属性 
DESC_G_4K   equ 0x800000
DESC_D_32   equ 0x400000
DESC_L      equ 0x000000 ;是否开启64位代码
DESC_AVL    equ 0x000000 ;可用的

DESC_LIMIT_CODE2    equ 0xF0000
DESC_LIMIT_DATA2    equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2   equ 0x00000

DESC_P      equ 0x8000
DESC_DPL_0  equ 0x0000
DESC_DPL_1  equ 0x2000
DESC_DPL_2  equ 0x4000
DESC_DPL_3  equ 0x6000

DESC_S_CODE equ 0x1000
DESC_S_DATA equ DESC_S_CODE ;相同的

DESC_S_SYS  equ 0x0000
DESC_TYPE_CODE  equ 0x800 ;x1 e0 w0 a0
DESC_TYPE_DATA  equ 0x200 ;x0 e0 w1 a0
DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
    DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
    DESC_P + DESC_DPL_0 + DESC_S_CODE + \
    DESC_TYPE_CODE + 0x00

DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
    DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
    DESC_P + DESC_DPL_0 + DESC_S_DATA + \
    DESC_TYPE_DATA + 0x00

DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
    DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
    DESC_P + DESC_DPL_0 + DESC_S_DATA + \
    DESC_TYPE_DATA + 0x0b

;选择子 属性
RPL0    equ 00b 
RPL1    equ 01b
RPL2    equ 10b
RPL3    equ 11b

TI_GDT  equ 000b
TI_LDT  equ 100b

;------ loader and kernel ------
PAGE_DIR_TABLE_POS equ 0x100000
PG_P    equ 1b 
PG_RW_R equ 00b
PG_RW_W equ 10b
PG_US_S equ 000b 
PG_US_U equ 100b 

;---kernel ---
KERNEL_START_SECTOR     equ 0x9
KERNEL_BIN_BASE_ADDR    equ 0x70000
PT_NULL equ 0
KERNEL_ENTRY_POINT      equ 0xc0001500

kernel.bin编译,还是因为gcc 编译太大了,所以用clang编译。同时还要注意,编译环境可能是64位下,所以要指定编译后的代码为 32 位的。

clang -m32 -c -o main.o main.c && ld -m elf_i386 main.o -Ttext 0xc0001500 -e main -o kernel.bin && dd if=kernel.bin of=hd60M.img bs=512 count=200 seek=9 conv=notrunc

运行结果:

  1. MBR运行
  2. 获取内存容量
  3. 进入32位安全模式
  4. 进入页表分页
  5. 加载内核并运行
    在这里插入图片描述
    2024/2/24日记录
    loader.asm,这里的mbr.asm中已经jmp到了指定地点+0x3000,其他没有改变什么,所以,GDT在宏定义的位置。我要说明的就只有这一点。
%include "boot.inc"

section loader vstart=LOADER_BASE_ADDR


GDT_BASE:
    dd  0x00000000
    dd  0x00000000
CODE_DESC:
    dd  0x0000FFFF
    dd  DESC_CODE_HIGH4
DATA_STACK_DESC:
    dd  0x0000FFFF
    dd  DESC_DATA_HIGH4
VIDEO_DESC:
    dd  0x80000007
    dd  DESC_VIDEO_HIGH4
GDT_SIZE            equ $-GDT_BASE
GDT_LIMIT           equ GDT_SIZE-1
times   60 dq   0;预留60个描述符的空位
;选择子
SELECTOR_CODE   equ (0x0001 << 3) + TI_GDT + RPL0
SELECTOR_DATA   equ (0x0002 << 3) + TI_GDT + RPL0
SELECTOR_VIDEO  equ (0x0003 << 3) + TI_GDT + RPL0

;gdt 指针
gdt_ptr dw  GDT_LIMIT
        dd  GDT_BASE

;============= 读取内存数 
total_mem_bytes     dd  0;4 保存内存容量
ards_buf    times   244 db 0
ards_nr     dw      0

jmp loader_start

def_read_card_num:
    xor ebx,ebx
    mov edx,0x534d4150
    mov di,ards_buf
.e820_mem_get_loop:
    mov eax,0x0000e820
    mov ecx,20
    int 0x15

    jc .e820_failed_so_try_e801
    add di,cx
    inc word[ards_nr]
    cmp ebx,0
    jnz .e820_mem_get_loop
    ;找出(base_add_low + length_low)的最大值,也就是内存容量
    mov cx,[ards_nr]
    mov ebx,ards_buf
    xor edx,edx
.find_max_mem_area:
    mov eax,[ebx]
    add eax,[ebx+8]
    add ebx,20
    cmp edx,eax
    jge .next_ards
    mov edx,eax
.next_ards:
    loop .find_max_mem_area
    jmp .mem_get_ok
;0x15 0xe801
.e820_failed_so_try_e801:
    mov ax,0xe801
    int 0x15
    jc .e801_failed_so_try88
    ;1 先算出低15MB的内存
    mov cx,0x400 ;ax KB单位2^10, 0x400->1024
    mul cx
    shl edx,16
    and eax,0x0000FFFF
    or edx,eax
    add edx,0x100000
    mov esi,edx
    ;2 算 > 16MB 的内存 bx 64KB位单位
    xor eax,eax
    mov ax,bx
    mov ecx,0x10000;将64KB单位转为byte
    mul ecx
    add esi,eax ; <=15 + >16
    jmp .mem_get_ok
.e801_failed_so_try88:
    mov ah,0x88
    int 0x15
    jc .error_hlt
    and eax,0x0000FFFF
    mov cx,0x400
    mul cx
    shl edx,16
    or edx,eax
    add edx,0x10000 ;+1MB

.mem_get_ok:
    mov [total_mem_bytes], edx
.error_hlt:
    mov byte [gs:480],'['
    mov byte [gs:482],'4'
    mov byte [gs:484],']'
    mov byte [gs:486],' '
    
    mov al,[total_mem_bytes+3]
    add al,'0'
    mov byte [gs:488],'0'
    mov byte [gs:490],al
    
    mov al,[total_mem_bytes+2]
    add al,'0'
    mov byte [gs:492],'0'
    mov byte [gs:494],al
    
    mov al,[total_mem_bytes+1]
    add al,'0'
    mov byte [gs:496],'0'
    mov byte [gs:498],al
    
    mov al,[total_mem_bytes+0]
    add al,'0'
    mov byte [gs:500],'0'
    mov byte [gs:502],al
    ret

def_print_loader:
    mov bx,160
    mov cx,loader_str_len
    mov si,0
print_loader_loop:
    mov al, [loader_str + si]
    mov [gs:bx],al
    inc bx
    mov al, [loader_str_color]
    mov [gs:bx],al
    inc bx
    add si,1
    loop print_loader_loop
    ret
loader_str db '[2] This is loader code'
loader_str_len equ $-loader_str
loader_str_color db 0000_0111b

def_open_A20:
    in al,0x92
    or al,0x02
    out 0x92,al
    ret

def_loader_GDT_set_PE:
    lgdt[gdt_ptr]
    
    mov eax,cr0
    or eax,0x01
    mov cr0,eax
    ret


; =================== loader_start =======================
loader_start:
    call def_print_loader
    call def_read_card_num
    call def_open_A20
    call def_loader_GDT_set_PE
    jmp dword SELECTOR_CODE:p_mode_start

LOADER_STACK_TOP    equ LOADER_BASE_ADDR

    [bits 32]
p_mode_start:
    mov ax,SELECTOR_DATA
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov esp,LOADER_STACK_TOP
    mov ax,SELECTOR_VIDEO
    mov gs,ax
    
    mov eax,KERNEL_START_SECTOR
    mov ebx,KERNEL_BIN_BASE_ADDR
    mov ecx,200
    call def_rd_disk_m_32
    call def_print_into32
    call def_setup_page
    jmp SELECTOR_CODE:enter_kernel

enter_kernel:
    call kernel_init
    mov esp,0xc009f000
    call def_6print_kernel
    jmp KERNEL_ENTRY_POINT
    ;jmp $
; ======================= [bits 32 code] =======================
def_6print_kernel:
    mov ebx,800
    mov ecx,kernel_str_len
    mov di,0
.kernel_loop:
    mov al,[kernel_str+di]
    mov byte [gs:ebx],al
    add ebx,2
    inc di
    loop .kernel_loop
    ret
kernel_str db '[6] This is kernel'
kernel_str_len equ  $-kernel_str

def_print_into32:
    mov ebx,320
    mov ecx,into32_len
    mov di,0
.into32_loop:
    mov al,[into32_str+di]
    mov byte [gs:ebx],al
    add ebx,2
    inc di
    loop .into32_loop
    ret
into32_str db '[3] This is into32'
into32_len  equ $-into32_str

def_setup_page:
    mov ecx,4096 ;1024*4个页目录项
    mov esi,0
.clear_page_dir:
    mov byte [PAGE_DIR_TABLE_POS + esi],0
    inc esi
    loop .clear_page_dir

.create_pde:
    mov eax,PAGE_DIR_TABLE_POS
    add eax,0x1000
    mov ebx,eax

    or eax,PG_US_U | PG_RW_W | PG_P
    mov [PAGE_DIR_TABLE_POS +0x0],eax;第一个页表放入第一个页目录项
    mov [PAGE_DIR_TABLE_POS + 0xc00],eax;第一个页表放入最高1G的页目录项

    sub eax,0x1000;得到页目录首地址
    mov [PAGE_DIR_TABLE_POS + 4092], eax;将页目录首地址放到最后一个目录项中
    
    ;创建页表项,前256个
    mov ecx,256
    mov esi,0
    mov edx,PG_US_U | PG_RW_W | PG_P
.create_pte:
    mov [ebx+esi*4],edx
    add edx,4096
    inc esi
    loop .create_pte


    ;创建高1GB中的其他页目录项
    mov eax,PAGE_DIR_TABLE_POS
    add eax,0x2000
    or eax, PG_US_U | PG_RW_W | PG_P
    mov ebx, PAGE_DIR_TABLE_POS
    mov ecx,254
    mov esi,769 ;769是1G中的第一个目录项
.create_kernel_pde:
    mov [ebx+esi*4],eax
    inc esi
    add eax,0x1000
    loop .create_kernel_pde
    
    ;设置配置
    sgdt[gdt_ptr]
    mov ebx,[gdt_ptr + 2]; ebx = GDT_BASE
    or dword [ebx + 0x18 + 4], 0xc0000000; GDT_BASE + 3*8 + 4,到了VIDEO的高4字节
    
    add dword[gdt_ptr + 2], 0xc0000000;为高系统区1GB映射做准备
    

    add esp, 0xc0000000;将栈指针映射到内核
    mov eax,PAGE_DIR_TABLE_POS
    mov cr3, eax
    mov eax,cr0
    or eax,0x80000000
    mov cr0,eax;开启cr0 31位
    lgdt [gdt_ptr]; 重新加载GDT
    call def_print_va
    ret

def_print_va:
    mov ebx,640
    mov ecx,va_len
    mov di,0
.virtual_addr_loop:
    mov al,[virtual_addr+di]
    mov byte [gs:ebx],al
    add ebx,2
    inc di
    loop .virtual_addr_loop
    ret
virtual_addr db '[5] virtual addr'
va_len  equ $-virtual_addr

;----------- 将kernel.bin 中的segment拷贝到编译的地址 ----------
kernel_init:
    xor eax,eax
    xor ebx,ebx
    xor ecx,ecx
    xor edx,edx

    mov dx, [KERNEL_BIN_BASE_ADDR + 42]
    mov ebx,[KERNEL_BIN_BASE_ADDR + 28]
    add ebx, KERNEL_BIN_BASE_ADDR
    mov cx, [KERNEL_BIN_BASE_ADDR + 44]
.each_segment:
    cmp byte [ebx + 0], PT_NULL
    je .PTNULL

    push dword [ebx + 16]
    mov eax,[ebx + 4]
    add eax,KERNEL_BIN_BASE_ADDR
    push eax
    push dword [ebx + 8]
    call mem_cpy
    add esp,12
.PTNULL:
    add ebx,edx
    loop .each_segment
    ret

;--------------------- mem_cpy ------------------------|
;-------- 逐字节拷贝 void mem_cpy(dst, src, size) -----|
;------------------------------------------------------|
mem_cpy:
    cld
    push ebp
    mov ebp,esp
    push ecx
    mov edi, [ebp + 8]
    mov esi, [ebp + 12]
    mov ecx, [ebp + 16]
    rep movsb
    pop ecx
    pop ebp
    ret

;------------------------ rd_disk_m_32 ----------------
def_rd_disk_m_32:
    mov esi,eax
    mov di,cx
    ;指定扇区 0x1f2 8bit
    mov dx,0x1f2
    mov al,cl
    out dx,al

    mov eax,esi
    ;LBA 0-7 8bit
    mov dx,0x1f3
    out dx,al
    ;LBA 8-15 8bit
    mov cl,8
    shr eax,cl
    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
    or al,0xe0
    mov dx,0x1f6
    out dx,al

    ;第3步:向0x1f7端口写入读命令,0x20
    mov dx,0x1f7
    mov al,0x20
    out dx,al

    ;至此,硬盘控制器便从指定的 lba 地址(eax)处,读出连续的cx个扇区,下面检查硬盘状态,不忙就把 cx 个扇区的数据读出来。
    ;第4步,检查硬盘状态
.not_ready:
    nop
    in al,dx
    and al,0x88
    cmp al,0x08
    jnz .not_ready
    ;第5步,从0x1f0端口读数据
    mov ax,di
    mov dx,256
    mul dx
    mov cx,ax
    mov dx,0x1f0
.go_on_read:
    in ax,dx
    mov [ebx],ax
    add ebx,2

    loop .go_on_read
    ret

结果图:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值