如何在 assembly/C 内核中管理分页?
How to manage paging in assembly/C kernel?
我的项目是在一小块内核上执行我自己的 JVM,我正在尝试用 C 和汇编语言编写代码。
所以,我仍在学习内核如何完成其主要工作。
现在,按照一些指南,我设置了分页
.map
mov eax , p3_t
or eax , 0b11
mov dword [p4_t] , eax
mov eax , p2_t
or eax , 0b11
mov dword [p3_t] , eax
mov ecx , 0
.map_pd:
mov eax, 0x200000
mul ecx
or eax , 0b10000011
mov [p2_t+ecx*8] , eax
inc ecx
cmp ecx, 512
jne .map_pd
mov eax, p4_t
mov cr3, eax
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
mov eax, cr0
or eax, 1 << 31
or eax , 1 << 16
mov cr0, eax
应该设置并启用分页。
不幸的是,这些指南提供的信息很少。
现在,我如何管理 C 内核中的分页和虚拟地址?
你们能给我一些其他关于分页的指南吗?
听起来你启用分页只是因为 x86-64 需要它,你真的不想将它用于任何事情,比如为不同的进程提供它们自己的虚拟地址 space:
一个简单的选择是标识映射所有 RAM:物理 = 虚拟地址。 https://wiki.osdev.org/Setting_Up_Long_Mode#Setting_up_the_Paging
另见 https://wiki.osdev.org/Paging and https://wiki.osdev.org/Setting_Up_Paging_With_PAE (the 与 32 位 PAE 相同,但使用更多级别。)
1GB 大页面提供更好的性能(更少的 TLB 未命中,并且当您发生 TLB 未命中时页面遍历更短)并且意味着更少的页表级别和您必须设置的总 PTE。在较旧的硬件上,2MB 大页面可能是一个不错的选择,但现代 CPU 对 1G 页面也有很好的 TLB 支持。
我在您的源代码中看到的一个问题是您隐含地假设 虚拟 和 物理 地址相同。例如,你 link 你的内核到 运行 在 0x1000000,并复制到内存在 0x1000000。使其可重定位有一定的优雅,也许有对齐限制。
这些说明显示了您的 V=P 假设:
mov eax , p3_t
or eax , 0b11
mov dword [p4_t] , eax
如果您正在写入 V != P,则必须根据 link 和 运行 时间之间的差异调整标签 p3_t、p4_t地址。通常人们喜欢为此使用 %ebx;假设您使用的是基于 0 的细分:
call l1 ; get actual ip.
l1:
pop ebx
sub ebx, offset l1 ; adjust symbols by P - V.
...
mov eax, pl_t[ebx]
我的项目是在一小块内核上执行我自己的 JVM,我正在尝试用 C 和汇编语言编写代码。 所以,我仍在学习内核如何完成其主要工作。
现在,按照一些指南,我设置了分页
.map
mov eax , p3_t
or eax , 0b11
mov dword [p4_t] , eax
mov eax , p2_t
or eax , 0b11
mov dword [p3_t] , eax
mov ecx , 0
.map_pd:
mov eax, 0x200000
mul ecx
or eax , 0b10000011
mov [p2_t+ecx*8] , eax
inc ecx
cmp ecx, 512
jne .map_pd
mov eax, p4_t
mov cr3, eax
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
mov eax, cr0
or eax, 1 << 31
or eax , 1 << 16
mov cr0, eax
应该设置并启用分页。 不幸的是,这些指南提供的信息很少。
现在,我如何管理 C 内核中的分页和虚拟地址? 你们能给我一些其他关于分页的指南吗?
听起来你启用分页只是因为 x86-64 需要它,你真的不想将它用于任何事情,比如为不同的进程提供它们自己的虚拟地址 space:
一个简单的选择是标识映射所有 RAM:物理 = 虚拟地址。 https://wiki.osdev.org/Setting_Up_Long_Mode#Setting_up_the_Paging
另见 https://wiki.osdev.org/Paging and https://wiki.osdev.org/Setting_Up_Paging_With_PAE (the
1GB 大页面提供更好的性能(更少的 TLB 未命中,并且当您发生 TLB 未命中时页面遍历更短)并且意味着更少的页表级别和您必须设置的总 PTE。在较旧的硬件上,2MB 大页面可能是一个不错的选择,但现代 CPU 对 1G 页面也有很好的 TLB 支持。
我在您的源代码中看到的一个问题是您隐含地假设 虚拟 和 物理 地址相同。例如,你 link 你的内核到 运行 在 0x1000000,并复制到内存在 0x1000000。使其可重定位有一定的优雅,也许有对齐限制。
这些说明显示了您的 V=P 假设:
mov eax , p3_t
or eax , 0b11
mov dword [p4_t] , eax
如果您正在写入 V != P,则必须根据 link 和 运行 时间之间的差异调整标签 p3_t、p4_t地址。通常人们喜欢为此使用 %ebx;假设您使用的是基于 0 的细分:
call l1 ; get actual ip.
l1:
pop ebx
sub ebx, offset l1 ; adjust symbols by P - V.
...
mov eax, pl_t[ebx]