--------------------------------------------------------
打印内存信息
开启分页
jmp to kernle
--------------------------------------------------------
上节我们说过,在进入保护模式之后紧接着就要 jmp 到 kernel ,jmp 之前我们还要做几件事情...
一,显示可用内存并开启分页
; ----------------------------
; <loader.asm>
; Jack Zheng 11.26
; ----------------------------
....
; ----------------------------
; ----
; 在实模式下获得可用内存信息
; ----
mov ebx, 0 ; ebx = 后续值, 开始时需为 0
mov di, _MemChkBuf ; es:di 指向一个地址范围描述符结构(Address Range Descriptor Structure)
.MemChkLoop:
mov eax, 0E820h ; eax = 0000E820h
mov ecx, 20 ; ecx = 地址范围描述符结构的大小
mov edx, 0534D4150h ; edx = 'SMAP'
int 15h ; int 15h
jc .MemChkFail
add di, 20
inc dword [_dwMCRNumber] ; dwMCRNumber = ARDS 的个数
cmp ebx, 0
jne .MemChkLoop
jmp .MemChkOK
.MemChkFail:
mov dword [_dwMCRNumber], 0
.MemChkOK:
; ----------------------------
....
; ----------------------------
[SECTION .s32]
ALIGN 32
[BITS 32]
LABEL_PM_START:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorFlatRW
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
mov esp, TopOfStack
; ----
; 显示可用内存信息
; ----
push szMemChkTitle
call DispStr
add esp, 4
call DispMemInfo
; ----
; 开启分页
; ----
call SetupPaging
jmp $
; ----------------------------
....
; ----------------------------
DispMemInfo:
push esi
push edi
push ecx
mov esi, MemChkBuf
mov ecx, [dwMCRNumber]
.loop:
mov edx, 5
mov edi, ARDStruct
.1:
push dword [esi]
call DispInt
pop eax
stosd
add esi, 4
dec edx
cmp edx, 0
jnz .1
call DispReturn
cmp dword [dwType], 1
jne .2
mov eax, [dwBaseAddrLow]
add eax, [dwLengthLow]
cmp eax, [dwMemSize]
jb .2
mov [dwMemSize], eax
.2:
loop .loop
call DispReturn
push szRAMSize
call DispStr
add esp, 4
push dword [dwMemSize]
call DispInt
add esp, 4
pop ecx
pop edi
pop esi
ret
; ----------------------------
....
; ----------------------------
SetupPaging:
; ----
; 根据内存大小计算应初始化多少PDE以及多少页表
; ----
xor edx, edx
mov eax, [dwMemSize]
mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
div ebx
mov ecx, eax ; 此时 ecx 为页表的个数,也即 PDE 应该的个数
test edx, edx
jz .no_remainder
inc ecx ; 如果余数不为 0 就需增加一个页表
.no_remainder:
push ecx ; 暂存页表个数
; ----
; 页目录初始化
; ----
mov ax, SelectorFlatRW
mov es, ax
mov edi, PageDirBase ; 此段首地址为 PageDirBase
xor eax, eax
mov eax, PageTblBase | PG_P | PG_USU | PG_RWW
.1:
stosd
add eax, 4096 ; 为了简化, 所有页表在内存中是连续的.
loop .1
; ----
; 页表初始化
; ----
pop eax ; 页表个数
mov ebx, 1024 ; 每个页表 1024 个 PTE
mul ebx
mov ecx, eax ; PTE个数 = 页表个数 * 1024
mov edi, PageTblBase ; 此段首地址为 PageTblBase
xor eax, eax
mov eax, PG_P | PG_USU | PG_RWW
.2:
stosd
add eax, 4096 ; 每一页指向 4K 的空间
loop .2
mov eax, PageDirBase
mov cr3, eax
mov eax, cr0
or eax, 80000000h
mov cr0, eax
jmp short .3
.3:
nop
ret
; ----------------------------
....
二,转移 kernel 位置 & jmp to kernel
因为我们不想使用 ld 命令默认生成的 elf 程序起始的执行地址,因此我们在编译的时候指定 kernel 起始的执行地址,然后再将 kenel 移动到那个位置
; ----------------------------
; <loader.asm>
; Jack Zheng 11.26
; ----------------------------
...
; ----------------------------
[SECTION .s32]
ALIGN 32
[BITS 32]
LABEL_PM_START:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorFlatRW
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
mov esp, TopOfStack
; ----
; 显示可用内存信息
; ----
push szMemChkTitle
call DispStr
add esp, 4
call DispMemInfo
; ----
; 开启分页
; ----
call SetupPaging
; ----
; 调整内核位置
; ----
call InitKernel
jmp SelectorFlatC:KernelEntryPointPhyAddr
jmp $
; ----------------------------
....
; ----------------------------
InitKernel:
; ----
; 遍历每一个 Program Header,根据 Program Header 中的信息来确定把什么放进内存,放到什么位置,以及放多少。
; ----
xor esi, esi
mov cx, word [BaseOfKernelFilePhyAddr + 2Ch] ; ┓ ecx <- pELFHdr->e_phnum
movzx ecx, cx ; ┛
mov esi, [BaseOfKernelFilePhyAddr + 1Ch] ; esi <- pELFHdr->e_phoff
add esi, BaseOfKernelFilePhyAddr ; esi <- OffsetOfKernel + pELFHdr->e_phoff
.Begin:
mov eax, [esi + 0]
cmp eax, 0 ; PT_NULL
jz .NoAction
push dword [esi + 010h] ; size ┓
mov eax, [esi + 04h] ; ┃
add eax, BaseOfKernelFilePhyAddr ; ┣ ::memcpy((void*)(pPHdr->p_vaddr),
push eax ; src ┃ uchCode + pPHdr->p_offset,
push dword [esi + 08h] ; dst ┃ pPHdr->p_filesz);
call MemCpy ; ┃
add esp, 12 ; ┛
.NoAction:
add esi, 020h ; esi += pELFHdr->e_phentsize
dec ecx
jnz .Begin
ret
; ----------------------------
....
; ----------------------------
; <kernel>
; Jack Zheng 11.26
; ----------------------------
[section .text]
global _start
_start:
mov ax, 0fh
mov al, 'K'
mov [gs:(80 * 17)], ax
jmp $
OK!jmp to kernel 之后就正式进入内核开发阶段了,下节我们将引入 C 语言和 汇编语言 联合编译开发内核!
来源:CSDN
作者:Jack Zheng~
链接:https://blog.csdn.net/qq_42306471/article/details/103319962