1. How launched by the memory page table quantities
assume that memory space is 32MB, since each page is 4KB, then we need to 8K page tables, and each page table actually accounts for 4 bytes, so a total share of memory page table 32KB
Allocating memory still starts from scratch, but some of the memory is unusable. We can only say that the memory pointed to by our linear address is partially available. The maximum available address is 01FF0000h, so the usable memory is Point to
;======================================
;pmtest7.asm
;编译方法:nasm pmtest7.asm -o pmtest7.bin
;======================================
%include "pm.inc"; 常量,宏 以及一些说明
;org 07c00h
PageDirBase equ 200000h; 页目录开始地址2M
PageTblBase equ 201000h; 页表开始地址 2M+4K
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;GDT 段基址 段界限 属性
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ;NORMAL描述符
LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, DA_DRW ; page directory 4k
LABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 4096*8-1, DA_DRW ;32KB的页表
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段
LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C;
LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW; Data
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32;
LABEL_DESC_VIDEO : Descriptor 0B8000h, 0ffffh, DA_DRW; 显存首地址
;GDT 就是一个数组结构
GdtLen equ $-LABEL_GDT ;GDT长度
GdtPtr dw GdtLen - 1 ;GDT 界限
dd 0 ; GDT基地址
;GdtPtr也是一个数据结构 前2字节是GDT界限 后4字节是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 .gdt]
[SECTION .data1] ;数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
;实模式下使用这些符号
;字符串
_szPMMessage: db "In Protect Mode now.^_^", 0Ah, 0Ah, 0; 在保护模式中显示
_szMemChkTitle db "BaseAddrL BaseAddrH LengthLow LengthHigh Type", 0Ah, 0; 进入保护模式后显示
_szRAMSize db "RAM size:", 0
_szReturn db 0Ah, 0
; 变量
_wSPValueInRealMode dw 0
_dwMCRNumber: dd 0 ; Memory Check Result
_dwDispPos: dd (80 * 6 + 0) * 2 ; 屏幕第 6 行, 第 0 列。
_dwMemSize: dd 0
_ARDStruct: ; Address Range Descriptor Structure
_dwBaseAddrLow: dd 0
_dwBaseAddrHigh: dd 0
_dwLengthLow: dd 0
_dwLengthHigh: dd 0
_dwType: dd 0
_MemChkBuf: times 256 db 0
; 保护模式下使用这些符号
szPMMessage equ _szPMMessage - $$
szMemChkTitle equ _szMemChkTitle - $$
szRAMSize equ _szRAMSize - $$
szReturn equ _szReturn - $$
dwDispPos equ _dwDispPos - $$
dwMemSize equ _dwMemSize - $$
dwMCRNumber equ _dwMCRNumber - $$
ARDStruct equ _ARDStruct - $$
dwBaseAddrLow equ _dwBaseAddrLow - $$
dwBaseAddrHigh equ _dwBaseAddrHigh - $$
dwLengthLow equ _dwLengthLow - $$
dwLengthHigh equ _dwLengthHigh - $$
dwType equ _dwType - $$
MemChkBuf equ _MemChkBuf - $$
DataLen equ $ - LABEL_DATA
; END of [SECTION .data1]
;全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0
TopOfStack equ $-LABEL_STACK-1
;end of [section.gs]
;这是一个16位代码段 这个程序修改了gdt中的一些值 然后执行跳转到第三个section
[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 [_wSPValueInRealMode],sp ;是从零开始的?
; 得到内存数
mov ebx, 0
mov di, _MemChkBuf
.loop:
mov eax, 0E820h
mov ecx, 20
mov edx, 0534D4150h
int 15h
jc LABEL_MEM_CHK_FAIL ;CF=1 发生了错误
add di, 20
inc dword [_dwMCRNumber] ;_dwMCRNumber每次循环加一 作为循环次数
cmp ebx, 0
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
; xor eax, eax
; mov ax, cs
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
;初始化32位代码段描述符
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
mov ax, ds
shl eax, 4
add eax, LABEL_GDT; eax <- gdt基地址
mov dword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址
;加载GDTR
lgdt [GdtPtr]
;关中断
cli
;打开地址线A20
in al, 92h
or al, 00000010b
out 92h, al
;准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
;真正进入保护模式
jmp dword SelectorCode32:0 ; 执行这句会把SelectorCode32装入CS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LABEL_REAL_ENTRY: ;从保护模式跳回到实模式就到了这里
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, [_wSPValueInRealMode] ;指针调到了堆栈中 返回到实模式
in al, 92h
and al, 11111101b; 关闭A20地址线
out 92h, al
sti; 开中断
mov ax, 4c00h;
int 21h; 回到dos
;end of section .s16
[SECTION .s32]
[BITS 32]
LABEL_SEG_CODE32:
;call SetupPaging
mov ax, SelectorData
mov ds, ax; 数据段选择子
mov ax, SelectorData
mov es, ax
mov ax, SelectorVideo; 视频段选择子
mov gs, ax
mov ax, SelectorStack
mov ss, ax; 堆栈段选择子
mov esp, TopOfStack
;下面显示一个字符串
push szPMMessage
call DispStr
add esp, 4
push szMemChkTitle
call DispStr
add esp, 4
call DispMemSize ; 显示内存信息
call SetupPaging ; 启动分页机制
; 到此停止
jmp SelectorCode16:0
;-------------------------------------------------------------------------
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 ; 测试edx 是否为空 edx是放余数的位置
jz .no_remainder
inc ecx ; 如果余数不为 0 就需增加一个页表
.no_remainder:
push ecx ; 暂存页表个数
; 为简化处理 所有线性地址对应相等的物理地址
;首先初始化页目录
mov ax, SelectorPageDir;此段首地址为pagedirbase
mov es, ax
mov ecx, 1024 ; 共1K个表项
xor edi, edi
xor eax, eax
mov eax, PageTblBase|PG_P|PG_USU|PG_RWW
.1:
stosd ;取得双字mov [edi], eax, edi = edi+4;
add eax, 4096; 为了简化 所有页表在内存中都是连续的
loop .1
;再初始化所有页表(1K个 4M内存空间)
mov ax, SelectorPageTbl; 此段首地址为 pagetblbase
mov es, ax
pop eax ; 页表个数从堆栈里弹出来
mov ebx, 1024
mul ebx
mov ecx, eax; PTE个数 1024*页表个数
xor edi, edi
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 ;cr3 指向页目录表
mov eax, cr0
or eax, 80000000h
mov cr0, eax ;PG = 1 分页机制生效
jmp short .3
.3:
nop
ret
;
;分页机制启动完毕
DispMemSize:
push esi
push edi
push ecx
mov esi, MemChkBuf
mov ecx, [dwMCRNumber];for(int i=0;i<[MCRNumber];i++)//每次得到一个ARDS
.loop: ;{
mov edx, 5 ; for(int j=0;j<5;j++) //每次得到一个ARDS中的成员 每个四字节 共20字节
mov edi, ARDStruct ; {//依次显示BaseAddrLow,BaseAddrHigh,LengthLow,
.1: ; LengthHigh,Type
push dword [esi] ;
call DispInt ; DispInt(MemChkBuf[j*4]); //显示一个成员
pop eax ;
stosd ; ARDStruct[j*4] = MemChkBuf[j*4];
add esi, 4 ;
dec edx ;
cmp edx, 0 ;
jnz .1 ; }
call DispReturn ; printf("\n");
cmp dword [dwType], 1 ; if(Type == AddressRangeMemory)
jne .2 ; {
mov eax, [dwBaseAddrLow];
add eax, [dwLengthLow];
cmp eax, [dwMemSize] ; if(BaseAddrLow + LengthLow > MemSize)
jb .2 ;
mov [dwMemSize], eax ; MemSize = BaseAddrLow + LengthLow;
.2: ; }
loop .loop ;}
;
call DispReturn ;printf("\n");
push szRAMSize ;
call DispStr ;printf("RAM size:");
add esp, 4 ;
;
push dword [dwMemSize] ;
call DispInt ;DispInt(MemSize);
add esp, 4 ;
pop ecx
pop edi
pop esi
ret
%include "lib.inc"
SegCode32Len equ $-LABEL_SEG_CODE32
;end of section .s32
;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 al, 11111110b
and eax, 7ffffffeh
mov cr0, eax
LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY; 段地址会在程序开始处被设置为正确的值
Code16Len equ $ - LABEL_SEG_CODE16
;end of section .s16code