实战分页机制实现 -- 通过实际内存大小动态调整页表个数

2020-02-22 21:39:13   最后更新: 2020-02-22 21:39:13   访问数量:74




上一篇文章中,我们详细讲解了 32 位保护模式下的分页机制,最终,我们将 4GB 的内存区域划分为了连续的 1023 个分页,页表保存在 4MB 的空间中

详解操作系统分页机制与实战

 

但是我们的内存大小到底是多少呢?如果内存总共只要 8MB,那上面的分页程序执行完,光是页表就占用了 4MB,空间已经所剩无几,可见,按需使用内存,合理规划页表的大小是非常重要的,而这一切的前提是必须要搞清楚内存总共有多少

本文我们就来通过一个程序获取计算机的内存信息

 

 

我们曾经通过 BIOS 的 10H 硬件中断实现向显示器输出一行文字

计算机是如何启动的?如何制作自己的操作系统

 

通过 15H 号硬件终端,我们可以获取系统多个不同方面的信息,这其中就包括内存信息的获取

 

原理

用于获取内存信息的 10H 中断将内存信息拼装为一个 20 个字节的数据结构 -- 地址范围描述符结构,写入到指定的内存中

每一次中断生成一个描述符结构,用来表示一段连续可用的内存,经过若干次中断调用,即可获取整个内存中若干段的可用内存

 

地址范围描述符 ARDS

ARDS 是 Address Range Descriptor Structure 的简称,也就是地址范围描述符

他描述了内存中连续可用的一段,共有 20 个字节:

ARDS 结构
偏移名称说明
0BaseAddrLow初始物理地址低 32 位
4BaseAddrHigh初始物理地址高 32 位
8LengthLow内存段长度字节数低 32 位
12LengthHigh内存段长度字节数高 32 位
16Type地址范围类型,位 1 表示可以被 OS 使用,其他值表示 OS 不可用

 

准备工作

利用 BIOS INT 15H 获取内存信息前,需要填充以下寄存器:

  • EAX -- 设置为 0E820h,表示获取内存信息
  • EBX -- 设置为 0
  • ES:DI -- 信息写入的内存区域首地址
  • ECX -- 内存区域大小字节数,通常系统需要写入的数据是 20 字节,如果 ECX 值小于 20,那么 BIOS 会写入 ECX 字节,但有些实现中 BIOS 没有考虑 ECX 的值,总是写入 20 字节
  • EDX -- 0534D4150h,用于校验的固定值,中断执行后会被填充到 EAX

 

INT 15H 中断完成后寄存器的值

  • EFLAGS -- EFLAGS 的 CF 位表示中断执行是否出错,位 0 表示没有出错,为 1 表示出错
  • EAX -- 0534D4150h
  • ECX -- BIOS 实际写入字节数
  • EBX -- 剩余需要写入的地址描述符数量

 

下面,我们就在实地址模式下通过 INT 15H 获取内存信息保存在内存上,然后到保护模式下,通过 8025 彩色字符模式打印出内存的信息

 

分配 ARDS 存储空间

我们需要在数据段中开辟出一块内存用来存储若干个 ARDS 结构,同时为了能够在保护模式下使用,需要创建一个存储偏移的指针

与之对应的,我们还需要一个变量,用于存储 ARDS 结构的个数,以及保护模式下使用的段内偏移指针变量

_MemChkBuf: times 256 db 0 ; ARDS 缓冲区 _dwMCRNumber: dd 0 ; ARDS 个数 MemChkBuf equ _MemChkBuf - $$ dwMCRNumber equ _dwMCRNumber - $$

 

 

在实模式下循环获取 ARDS

; 循环获取 ARDS xor ebx, ebx ; 清零 EBX mov di, _MemChkBuf ; DI 寄存器中保存写入地址偏移 .loop: mov eax, 0E820h ; 初始化 EAX 为固定值 mov ecx, 20 ; ARDS 字节数 mov edx, 0534D4150h ; 初始化 EDX 为固定值 int 15h ; 触发 15H 中断 jc LABEL_MEM_CHK_FAIL ; EFLAGS 寄存器 CF 位为 1 则跳转,表示失败 add di, 20 ; DI 寄存器指向下一个待写入位置偏移 inc dword [_dwMCRNumber] ; 计数变量 + 1 cmp ebx, 0 ; 比较 EBX 判断是否完成 ARDS 获取 jne .loop jmp LABEL_MEM_CHK_OK LABEL_MEM_CHK_FAIL: mov dword [_dwMCRNumber], 0 LABEL_MEM_CHK_OK:

 

 

内存变量分配

为了打印 ARDS 并获取最大连续内存,除了上面我们已经定义并填充的内存缓冲区,我们还需要定义连续内存大小值的存储变量

同时,需要一个 ARDS 结构变量来进行比较

_dwMemSize: dd 0 ; 最大连续内存大小 _ARDStruct: _dwBaseAddrLow: dd 0 _dwBaseAddrHigh: dd 0 _dwLengthLow: dd 0 _dwLengthHigh: dd 0 _dwType: dd 0 dwMemSize equ _dwMemSize - $$ ARDStruct equ _ARDStruct - $$ ; 用于存储 ARDS 结构的临时变量 dwBaseAddrLow equ _dwBaseAddrLow - $$ dwBaseAddrHigh equ _dwBaseAddrHigh - $$ dwLengthLow equ _dwLengthLow - $$ dwLengthHigh equ _dwLengthHigh - $$ dwType equ _dwType - $$ RAMSize db "RAM size:", 0 OffsetMemSize equ RAMSize - $$

 

 

打印函数定义

随着我们的程序越来越长,我们必须进行函数的封装和拆分,甚至进行文件的拆分,来让我们的程序可读性更强

 

打印字符串

; ------------------------------------------------------------------------ ; 显示一个字符串 ; ; params: ; db property ; dw display_index ; dw string_offset ; ------------------------------------------------------------------------ DisplayString: push ebp mov ebp, esp push eax push edi push esi mov ah, [ebp + 16] mov edi, [ebp + 12] mov esi, [ebp + 8] cld .loop_label: lodsb test al, al jz .over_print mov [gs:edi], ax add edi, 2 jmp .loop_label .over_print: pop esi pop edi pop eax pop ebp ret

 

 

打印一个字节中的数字

; ------------------------------------------------------------------------ ; 显示一个字节中的数字 ; ; params: ; db property ; dw display_index ; db number ; ; return: ; eax display_index ; ------------------------------------------------------------------------ DisplayByteNumber: push ebp mov ebp, esp push edx push edi push esi mov ah, [ebp + 16] mov edi, [ebp + 12] mov al, [ebp + 8] mov dl, al shr al, 4 mov ecx, 2 .begin: and al, 01111b cmp al, 9 ja .letter add al, '0' jmp .number .letter: sub al, 0Ah add al, 'A' .number: mov [gs:edi], ax add edi, 2 mov al, dl loop .begin mov eax, edi pop esi pop edi pop edx pop ebp ret

 

 

打印一个 dword 中的数字

在 32 位系统中,打印一个 32 位的数字是最为常用的功能,也是我们本次程序中所必须使用的

; ------------------------------------------------------------------------ ; 显示一个整形数 ; ; params: ; db property ; dw display_index ; db number ; ; return: ; eax display_index ; ------------------------------------------------------------------------ DisplayInt: push ebp mov ebp, esp push edx push edi push esi push ebx push ecx mov ah, [ebp + 16] mov edi, [ebp + 12] mov ecx, 4 mov ebx, 24 push ah .loop: mov eax, [ebp + 8] shr eax, ebx push edi push eax call DisplayByteNumber add esp, 8 add edi, 2 sub ebx, 8 loop .loop mov al, 'h' push edi mov [gs:edi], ax add edi, 4 mv eax, edi pop ecx pop ebx pop esi pop edi pop edx pop ebp ret

 

 

可以看到,他通过循环调用 DisplayByteNumber 实现了对32位数字的打印

 

显示内存信息并获取最大连续内存

; ---------------------- 显示内存信息 --------------------------- DispMemSize: push esi push edi push ebx push ecx push edx mov esi, MemChkBuf ; esi 指向 ARDS 缓冲器首地址 mov ecx, [dwMCRNumber] ; 循环次数复制 mov ebx, (80 * 3) * 2 push ebx .dispmemsizeloop: mov edx, 5 ; 依次打印 ARDS 中的 5 个成员 mov edi, ARDStruct .printloop: mov eax, 0Fh push eax mov eax, ebx push eax push dword [esi] call DisplayInt add esp, 12 mov ebx, eax add ebx, 4 add esi, 4 dec edx cmp edx, 0 jnz .printloop pop ebx add ebx, 160 push ebx cmp dword [dwType], 1 ; dwType 不为 1 表示操作系统不可用 jne .finishgetmem mov eax, [dwBaseAddrLow] add eax, [dwLengthLow] ; 获取当前结构对应的连续内存长度 cmp eax, [dwMemSize] ; 与已获取到的最大内存大小比较,如果大于则更新 jb .finishgetmem mov [dwMemSize], eax .finishgetmem: loop .dispmemsizeloop pop ebx add ebx, 160 mov eax, 0Fh push eax mov eax, ebx push eax push OffsetMemSize call DisplayString add esp, 12 mov eax, 0Fh push eax mov eax, ebx push eax push dword [dwMemSize] call DisplayInt add esp, 12 pop edx pop ecx pop ebx pop edi pop esi ret

 

 

接下来,我们就要对上一篇文章中的分页机制进行改造,实现在有限的最大连续内存中分配我们的页目录表和页表

 

变量分配

我们需要动态计算页表个数,因此需要一个变量来存储页表个数

_PageTableNumber dd 0 ; 页表个数 PageTableNumber equ _PageTableNumber - $$

 

 

启动分页机制

下面,我们就让我们的程序通过上面计算出的最大可用连续内存来动态决定页表个数,分配可用内存

; ---------------------- 分页机制启动 --------------------------- SetupPaging: ; 根据内存大小计算应初始化多少PDE以及多少页表 xor edx, edx mov eax, [dwMemSize] mov ebx, 400000h ; 一个页表对应的内存 4MB div ebx mov ecx, eax ; ecx 保存页表个数 test edx, edx jz .no_remainder inc ecx ; 余数不为 0 则增加一个页表 .no_remainder: mov [PageTableNumber], ecx ; 暂存页表个数 ; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞. ; 初始化页目录 mov ax, SelectorFlatRW mov es, ax mov edi, PageDirBase ; 此段首地址为 PageDirBase xor eax, eax mov eax, PageTblBase0 | 7 ; 用户级,存在于内存,可读写 .filter_pde: stosd add eax, 4096 ; 为了简化, 所有页表在内存中是连续的. loop .filter_pde ; 再初始化所有页表 mov eax, [PageTableNumber] ; 页表个数 mov ebx, 1024 ; 每个页表 1024 个 PTE mul ebx mov ecx, eax ; PTE 个数 = 页表个数 * 1024 mov edi, PageTblBase0 xor eax, eax mov eax, 7 ; 用户级、存在于内存、可读写 .filter_pte: stosd add eax, 4096 ; 每一页指向 4K 的空间 loop .filter_pte ; 设置页目录表起始地址 mov eax, PageDirBase mov cr3, eax ; 开启分页机制 mov eax, cr0 or eax, 80000000h mov cr0, eax ret

 

 

经过一系列的工作,我们终于完成了我们的程序,让我们的“操作系统”可以获取实际可用的连续内存大小,并在其中分配页表来启动我们的程序,那接下来就让我们执行看看:

 

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤

 

 

函数封装 -- lib.asm

; ------------------------------------------------------------------------ ; 显示一个字节中的数字 ; ; params: ; db property ; dw display_index ; db number ; ; return: ; eax display_index ; ------------------------------------------------------------------------ DisplayByteNumber: push ebp mov ebp, esp push edx push edi push esi mov ah, [ebp + 16] mov edi, [ebp + 12] mov al, [ebp + 8] mov dl, al shr al, 4 mov ecx, 2 .begin: and al, 01111b cmp al, 9 ja .letter add al, '0' jmp .number .letter: sub al, 0Ah add al, 'A' .number: mov [gs:edi], ax add edi, 2 mov al, dl loop .begin mov eax, edi pop esi pop edi pop edx pop ebp ret ; ------------------------------------------------------------------------ ; 显示一个整形数 ; ; params: ; db property ; dw display_index ; db number ; ; return: ; eax display_index ; ------------------------------------------------------------------------ DisplayInt: push ebp mov ebp, esp push edx push edi push esi push ebx push ecx mov ah, [ebp + 16] mov edi, [ebp + 12] mov ecx, 4 mov ebx, 24 push ah .loop: mov eax, [ebp + 8] shr eax, ebx push edi push eax call DisplayByteNumber add esp, 8 add edi, 2 sub ebx, 8 loop .loop mov al, 'h' push edi mov [gs:edi], ax add edi, 4 mv eax, edi pop ecx pop ebx pop esi pop edi pop edx pop ebp ret ; ------------------------------------------------------------------------ ; 显示一个字符串 ; ; params: ; db property ; dw display_index ; dw string_offset ; ------------------------------------------------------------------------ DisplayString: push ebp mov ebp, esp push eax push edi push esi mov ah, [ebp + 16] mov edi, [ebp + 12] mov esi, [ebp + 8] cld .loop_label: lodsb test al, al jz .over_print mov [gs:edi], ax add edi, 2 jmp .loop_label .over_print: pop esi pop edi pop eax pop ebp ret

 

 

主程序 -- protect.asm

; ---------------- 内存段描述符宏 ------------- ; usage: Descriptor Base, Limit, Attr ; Base: dd ; Limit: dd (low 20 bits available) ; Attr: dw (lower 4 bits of higher byte are always 0) %macro Descriptor 3 dw %2 & 0FFFFh ; 段界限1 dw %1 & 0FFFFh ; 段基址1 db (%1 >> 16) & 0FFh ; 段基址2 dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2 db (%1 >> 24) & 0FFh ; 段基址3 %endmacro PageDirBase equ 200000h ; 页目录开始地址: 2M PageTblBase equ 201000h ; 页表开始地址: 2M+4K ; ------------ DOS 加载初始内存地址 ----------- org 0100h jmp LABEL_BEGIN ; ------------------- GDT --------------------- [SECTION .gdt] ; GDT ; 段基址, 段界限, 属性 LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符 LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, 92h ; Normal 描述符 LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, 92h ; Page Directory,可读写 LABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 1023, 8092h ; Page Tables,段界限为 1023 * 4096 字节 LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, 4098h ; 非一致代码段 LABEL_DESC_CODE16: Descriptor 0, 0ffffh, 98h ; 非一致代码段, 用于跳回 16 BITS 模式 LABEL_DESC_DATA: Descriptor 0, DataLen-1, 92h ; 可读写数据段,界限 64KB LABEL_DESC_STACK: Descriptor 0, TopOfStack, 4093h ; 32 位全局堆栈段,可读写数据段,且栈指针默认使用 esp 寄存器 LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, 92h ; 显存首地址 ; ------------------ END OF GDT ---------------- GdtLen equ $ - LABEL_GDT ; GDT长度 GdtPtr dw GdtLen - 1 ; GDT界限 dd 0 ; GDT基地址 ; ------------------ GDT 选择子 ----------------- SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT SelectorPageDir equ LABEL_DESC_PAGE_DIR - LABEL_GDT SelectorPageTbl equ LABEL_DESC_PAGE_TBL - LABEL_GDT SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT SelectorData equ LABEL_DESC_DATA - LABEL_GDT SelectorStack equ LABEL_DESC_STACK - LABEL_GDT SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT ; --------------- END OF 段选择子 ---------------- [SECTION .data1] ; 数据段 ALIGN 32 [BITS 32] LABEL_DATA: SPValueInRealMode dw 0 BootMessage: db "Hello World my OS, techlog.cn!", 0 OffsetBootMessage equ BootMessage - $$ RAMSize db "RAM size:", 0 OffsetMemSize equ RAMSize - $$ _MemChkBuf: times 256 db 0 ; ARDS 缓冲区 _dwMCRNumber: dd 0 ; ARDS 个数 _PageTableNumber dd 0 ; 页表个数 _dwMemSize: dd 0 ; 最大连续内存大小 _ARDStruct: _dwBaseAddrLow: dd 0 _dwBaseAddrHigh: dd 0 _dwLengthLow: dd 0 _dwLengthHigh: dd 0 _dwType: dd 0 MemChkBuf equ _MemChkBuf - $$ dwMCRNumber equ _dwMCRNumber - $$ PageTableNumber equ _PageTableNumber - $$ dwMemSize equ _dwMemSize - $$ ARDStruct equ _ARDStruct - $$ ; 用于存储 ARDS 结构的临时变量 dwBaseAddrLow equ _dwBaseAddrLow - $$ dwBaseAddrHigh equ _dwBaseAddrHigh - $$ dwLengthLow equ _dwLengthLow - $$ dwLengthHigh equ _dwLengthHigh - $$ dwType equ _dwType - $$ DataLen equ $ - LABEL_DATA ; 全局堆栈段 [SECTION .gs] ALIGN 32 [BITS 32] LABEL_STACK: times 512 db 0 TopOfStack equ $ - LABEL_STACK - 1 [SECTION .s16] [BITS 16] LABEL_BEGIN: ; 初始化段基址寄存器 mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, 0100h mov [LABEL_GO_BACK_TO_REAL+3], ax mov [SPValueInRealMode], sp ; 循环获取 ARDS xor ebx, ebx ; 清零 EBX mov di, _MemChkBuf ; DI 寄存器中保存写入地址偏移 .loop: mov eax, 0E820h ; 初始化 EAX 为固定值 mov ecx, 20 ; ARDS 字节数 mov edx, 0534D4150h ; 初始化 EDX 为固定值 int 15h ; 触发 15H 中断 jc LABEL_MEM_CHK_FAIL ; EFLAGS 寄存器 CF 位为 1 则跳转,表示失败 add di, 20 ; DI 寄存器指向下一个待写入位置偏移 inc dword [_dwMCRNumber] ; 计数变量 + 1 cmp ebx, 0 ; 比较 EBX 判断是否完成 ARDS 获取 jne .loop jmp LABEL_MEM_CHK_OK LABEL_MEM_CHK_FAIL: mov dword [_dwMCRNumber], 0 LABEL_MEM_CHK_OK: ; 初始化 16 位代码段描述符 mov ax, cs movzx eax, ax shl eax, 4 add eax, LABEL_SEG_CODE16 mov word [LABEL_DESC_CODE16 + 2], ax shr eax, 16 mov byte [LABEL_DESC_CODE16 + 4], al mov byte [LABEL_DESC_CODE16 + 7], ah ; 初始化非一致代码段描述符 xor eax, eax mov ax, cs shl eax, 4 add eax, LABEL_SEG_CODE32 ; 计算非一致代码段基地址物理地址 mov word [LABEL_DESC_CODE32 + 2], ax shr eax, 16 mov byte [LABEL_DESC_CODE32 + 4], al mov byte [LABEL_DESC_CODE32 + 7], ah ; 初始化数据段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_DATA mov word [LABEL_DESC_DATA + 2], ax shr eax, 16 mov byte [LABEL_DESC_DATA + 4], al mov byte [LABEL_DESC_DATA + 7], ah ; 初始化堆栈段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_STACK mov word [LABEL_DESC_STACK + 2], ax shr eax, 16 mov byte [LABEL_DESC_STACK + 4], al mov byte [LABEL_DESC_STACK + 7], ah ; 准备加载 GDTR xor eax, eax ; 清空 eax 寄存器 mov ax, ds shl eax, 4 add eax, LABEL_GDT ; 计算出 GDT 基地址的物理地址 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址 ; 加载 GDTR lgdt [GdtPtr] ; 关闭硬件中断 cli ; 打开 A20 地址总线 in al, 92h or al, 00000010b out 92h, al ; 置位 PE 标志位,打开保护模式 mov eax, cr0 or eax, 1 mov cr0, eax ; 跳转进入保护模式 jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, ; 并跳转到 Code32Selector:0 处 ; 从保护模式跳回到实模式 LABEL_REAL_ENTRY: mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, [SPValueInRealMode] ; 关闭 A20 地址线 in al, 92h and al, 0fdh out 92h, al ; 打开硬件中断 sti ; 触发 BIOS int 21h 中断,回到实地址模式 mov ax, 4c00h int 21h [SECTION .s32] ; 32 位代码段. 由实模式跳入. [BITS 32] LABEL_SEG_CODE32: mov ax, SelectorData mov ds, ax ; 数据段选择子 mov ax, SelectorVideo mov gs, ax ; 赋值视频段选择子 mov ax, SelectorStack mov ss, ax ; 堆栈段选择子 mov esp, TopOfStack mov eax, 0Fh push eax mov eax, 80 * 2 * 2 push eax push OffsetBootMessage call DisplayString add esp 12 call DispMemSize ; 显示内存信息 call SetupPaging ; 启动分页机制 jmp SelectorCode16:0 ; ---------------------- 显示内存信息 --------------------------- DispMemSize: push esi push edi push ebx push ecx push edx mov esi, MemChkBuf ; esi 指向 ARDS 缓冲器首地址 mov ecx, [dwMCRNumber] ; 循环次数复制 mov ebx, (80 * 3) * 2 push ebx .dispmemsizeloop: mov edx, 5 ; 依次打印 ARDS 中的 5 个成员 mov edi, ARDStruct .printloop: mov eax, 0Fh push eax mov eax, ebx push eax push dword [esi] call DisplayInt add esp, 12 mov ebx, eax add ebx, 4 add esi, 4 dec edx cmp edx, 0 jnz .printloop pop ebx add ebx, 160 push ebx cmp dword [dwType], 1 ; dwType 不为 1 表示操作系统不可用 jne .finishgetmem mov eax, [dwBaseAddrLow] add eax, [dwLengthLow] ; 获取当前结构对应的连续内存长度 cmp eax, [dwMemSize] ; 与已获取到的最大内存大小比较,如果大于则更新 jb .finishgetmem mov [dwMemSize], eax .finishgetmem: loop .dispmemsizeloop pop ebx add ebx, 160 mov eax, 0Fh push eax mov eax, ebx push eax push OffsetMemSize call DisplayString add esp, 12 mov eax, 0Fh push eax mov eax, ebx push eax push dword [dwMemSize] call DisplayInt add esp, 12 pop edx pop ecx pop ebx pop edi pop esi ret ; ---------------------- 分页机制启动 --------------------------- SetupPaging: ; 根据内存大小计算应初始化多少PDE以及多少页表 xor edx, edx mov eax, [dwMemSize] mov ebx, 400000h ; 一个页表对应的内存 4MB div ebx mov ecx, eax ; ecx 保存页表个数 test edx, edx jz .no_remainder inc ecx ; 余数不为 0 则增加一个页表 .no_remainder: mov [PageTableNumber], ecx ; 暂存页表个数 ; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞. ; 初始化页目录 mov ax, SelectorFlatRW mov es, ax mov edi, PageDirBase ; 此段首地址为 PageDirBase xor eax, eax mov eax, PageTblBase0 | 7 ; 用户级,存在于内存,可读写 .filter_pde: stosd add eax, 4096 ; 为了简化, 所有页表在内存中是连续的. loop .filter_pde ; 再初始化所有页表 mov eax, [PageTableNumber] ; 页表个数 mov ebx, 1024 ; 每个页表 1024 个 PTE mul ebx mov ecx, eax ; PTE 个数 = 页表个数 * 1024 mov edi, PageTblBase0 xor eax, eax mov eax, 7 ; 用户级、存在于内存、可读写 .filter_pte: stosd add eax, 4096 ; 每一页指向 4K 的空间 loop .filter_pte ; 设置页目录表起始地址 mov eax, PageDirBase mov cr3, eax ; 开启分页机制 mov eax, cr0 or eax, 80000000h mov cr0, eax ret %include "lib.asm" ; 库函数 SegCode32Len equ $ - LABEL_SEG_CODE32 ; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式 [SECTION .s16code] ALIGN 32 [BITS 16] LABEL_SEG_CODE16: ; 跳回实模式: mov ax, SelectorNormal mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov eax, cr0 and eax, 7FFFFFFEh ; PE=0, PG=0 mov cr0, eax LABEL_GO_BACK_TO_REAL: jmp word 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值 Code16Len equ $ - LABEL_SEG_CODE16 ; END of [SECTION .s16code]

 

 

http://www.uruk.org/orig-grub/mem64mb.html

https://en.wikipedia.org/wiki/BIOS_interrupt_call

 






操作系统      os      分页      内存      paging      页表      bios      中断     


京ICP备15018585号