冬天OS(三):jmp to kernel

99封情书 提交于 2019-12-11 07:59:16

--------------------------------------------------------

打印内存信息

开启分页

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 语言和 汇编语言 联合编译开发内核!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!