Files
ViperOS/VKernel/Kernel/paging/paging.vp
T
2026-09-16 16:07:04 +08:00

483 lines
22 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import drivers.serial.uart.serial as serial
import viperlib
import t, c
PTE_PRESENT: t.CDefine = (1 << 0)
PTE_WRITABLE: t.CDefine = (1 << 1)
PTE_USER: t.CDefine = (1 << 2)
PTE_PWT: t.CDefine = (1 << 3)
PTE_PCD: t.CDefine = (1 << 4)
PTE_ACCESSED: t.CDefine = (1 << 5)
PTE_DIRTY: t.CDefine = (1 << 6)
PTE_PS: t.CDefine = (1 << 7)
PTE_GLOBAL: t.CDefine = (1 << 8)
class _p:
entries: t.CArray[uint64, 512]
PT_POOL_PAGES: t.CDefine = 512
pt_pool_raw: t.CPtr = None
pt_pool_start: uint64 = 0
pt_pool_used: uint64 = 0
AllocatedPages: uint64 = 0
pml4: _p | t.CPtr = None
pdp: _p | t.CPtr
pd: _p | t.CPtr
pt: _p | t.CPtr
def init(MemmapAddr: uint64, MemmapSize: uint64):
global pml4
pml4 = GetCurrentPml4()
# bootloader/UEFI 的 CR0.WP 通常已是 1(原代码清 WP 正因此)。页表页
# (pml4/pdp/pd/pt,0xf8xxxx)默认只读,而内核运行时 MapPage/clone 需
# 修改页表。顺序:先清 WP(允许写只读页表页)→ MakePTWritable 放开
# 全部页表页 → 再置 WP=1(此后写只读页即 #PF 暴露越界写源)。
c.Asm(f"""
mov rax, cr0
and rax, ~0x10000
mov cr0, rax
""")
MakePTWritable()
c.Asm(f"""
mov rax, cr0
or rax, 0x10000
mov cr0, rax
""")
def init_pool(buf: t.CPtr, buf_size: uint64):
global pt_pool_raw, pt_pool_start, pt_pool_used
pt_pool_raw = buf
if buf is None: return
raw_addr: uint64 = uint64(buf)
aligned_addr: uint64 = (raw_addr + uint64(0xFFF)) & ~uint64(0xFFF)
pt_pool_start = aligned_addr
pt_pool_used = 0
# [DIAG] 定位 clone 时 pt_pool_raw=0x0(init 后被覆盖?还是 init 收到 0)
_dip: t.CArray[t.CChar, 80]
viperlib.snprintf(c.Addr(_dip), 80, "[paging] init_pool raw=0x%lx start=0x%lx\n", uint64(pt_pool_raw), pt_pool_start)
serial.puts(c.Addr(_dip))
def AllocPage() -> t.CPtr:
global AllocatedPages, pt_pool_used, pml4, pdp, pd, pt
if pt_pool_raw is None: return None
if pt_pool_used >= uint64(PT_POOL_PAGES): return None
page: t.CPtr = t.CPtr(pt_pool_start + pt_pool_used * uint64(4096))
pt_pool_used = pt_pool_used + uint64(1)
# rep stosb 向 [rdi] 写 4096 字节:必须声明 CLOBBER_MEMORY,否则优化器
# 无法得知本块改写了内存,可能把其前后的内存读写重排/消除(N8)。
c.Asm(f"""mov al, 0
mov ecx, 4096
mov rdi, {c.AsmInp(page, t.ASM_DESCR.REG_ANY)}
rep stosb""",
op = [t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_AL,
t.ASM_DESCR.CLOBBER_ECX, t.ASM_DESCR.CLOBBER_RDI])
AllocatedPages = AllocatedPages + uint64(1)
return page
def FreePage(page: t.CPtr):
global AllocatedPages, pml4, pdp, pd, pt
if page is None: return
if AllocatedPages > 0: AllocatedPages = AllocatedPages - uint64(1)
def MapPage(VirtAddr: uint64, PhysAddr: uint64, flags: uint64):
global pml4, pdp, pd, pt
if VirtAddr == 0 or PhysAddr == 0: return
VirtAddr = VirtAddr & ~uint64(0xFFF)
PhysAddr = PhysAddr & ~uint64(0xFFF)
Pml4Index: uint64 = (VirtAddr >> 39) & 0x1FF
PdpIndex: uint64 = (VirtAddr >> 30) & 0x1FF
PdIndex: uint64 = (VirtAddr >> 21) & 0x1FF
PtIndex: uint64 = (VirtAddr >> 12) & 0x1FF
if not pml4:
pml4 = AllocPage()
if not pml4: return
if not (pml4.entries[Pml4Index] & PTE_PRESENT):
pdp: _p | t.CPtr = AllocPage()
if not pdp: return
pml4.entries[Pml4Index] = (uint64(pdp) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER)
else:
if (flags & PTE_USER) and not (pml4.entries[Pml4Index] & PTE_USER):
pml4.entries[Pml4Index] = pml4.entries[Pml4Index] | PTE_USER
pdp: _p | t.CPtr = t.CType(pml4.entries[Pml4Index] & ~uint64(0xFFF), _p, t.CPtr)
if not (pdp.entries[PdpIndex] & PTE_PRESENT):
pd: _p | t.CPtr = AllocPage()
if not pd: return
pdp.entries[PdpIndex] = (uint64(pd) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER)
else:
if (flags & PTE_USER) and not (pdp.entries[PdpIndex] & PTE_USER):
pdp.entries[PdpIndex] = pdp.entries[PdpIndex] | PTE_USER
pd: _p | t.CPtr = t.CType(pdp.entries[PdpIndex] & ~uint64(0xFFF), _p, t.CPtr)
if pd.entries[PdIndex] & 1:
if pd.entries[PdIndex] & 0x80:
big_phys: uint64 = pd.entries[PdIndex] & ~uint64(0x1FFFFF)
inherit_flags: uint64 = pd.entries[PdIndex] & (PTE_PCD | PTE_PWT)
pt: _p | t.CPtr = AllocPage()
if not pt: return
split_i: int = 0
while split_i < 512:
pt.entries[split_i] = (big_phys + uint64(split_i) * 0x1000) | PTE_PRESENT | PTE_WRITABLE | inherit_flags
split_i += 1
pd.entries[PdIndex] = (uint64(pt) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER)
else:
# PD entry exists as a regular PT (e.g. cloned identity mapping).
# Must propagate PTE_USER to the PD entry so user-mode access is
# allowed at all paging levels, otherwise #PF err=0x5.
if (flags & PTE_USER) and not (pd.entries[PdIndex] & PTE_USER):
pd.entries[PdIndex] = pd.entries[PdIndex] | PTE_USER
else:
pt: _p | t.CPtr = AllocPage()
if not pt: return
pd.entries[PdIndex] = (uint64(pt) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER)
pt: _p | t.CPtr = t.CType(pd.entries[PdIndex] & ~uint64(0xFFF), _p, t.CPtr)
pt.entries[PtIndex] = PhysAddr | flags | PTE_PRESENT
c.Asm(f"invlpg [{c.AsmInp(VirtAddr, t.ASM_DESCR.REG_ANY)}]")
def MapLargePage(VirtAddr: uint64, PhysAddr: uint64, flags: uint64):
global pml4, pdp, pd, pt
VirtAddr = VirtAddr & ~uint64(0x1FFFFF)
PhysAddr = PhysAddr & ~uint64(0x1FFFFF)
Pml4Index: uint64 = (VirtAddr >> 39) & 0x1FF
PdpIndex: uint64 = (VirtAddr >> 30) & 0x1FF
PdIndex: uint64 = (VirtAddr >> 21) & 0x1FF
if not pml4:
pml4 = AllocPage()
if not pml4: return
if not pml4.entries[Pml4Index] & PTE_PRESENT:
pdp: _p | t.CPtr = AllocPage()
if not pdp: return
pml4.entries[Pml4Index] = (uint64(pdp) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE
pdp: _p | t.CPtr = t.CType(pml4.entries[Pml4Index] & ~uint64(0xFFF), _p, t.CPtr)
if not (pdp.entries[PdpIndex] & PTE_PRESENT):
pd: _p | t.CPtr = AllocPage()
if not pd: return
pdp.entries[PdpIndex] = (uint64(pd) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE
pd: _p | t.CPtr = t.CType(pdp.entries[PdpIndex] & ~uint64(0xFFF), _p, t.CPtr)
pd.entries[PdIndex] = PhysAddr | flags | PTE_PRESENT | PTE_PS
c.Asm(f"invlpg [{c.AsmInp(VirtAddr, t.ASM_DESCR.REG_ANY)}]")
def UnMapPage(VirtAddr: uint64):
global pml4, pdp, pd, pt
if VirtAddr == 0: return
if not pml4: return
Pml4Index: uint64 = (VirtAddr >> 39) & 0x1FF
PdpIndex: uint64 = (VirtAddr >> 30) & 0x1FF
PdIndex: uint64 = (VirtAddr >> 21) & 0x1FF
PtIndex: uint64 = (VirtAddr >> 12) & 0x1FF
if not pml4.entries[Pml4Index] & PTE_PRESENT: return
pdp: _p | t.CPtr = t.CType(pml4.entries[Pml4Index] & ~uint64(0xFFF), _p, t.CPtr)
if not pdp.entries[PdpIndex] & PTE_PRESENT: return
pd: _p | t.CPtr = t.CType(pdp.entries[PdpIndex] & ~uint64(0xFFF), _p, t.CPtr)
if not pd.entries[PdIndex] & PTE_PRESENT: return
if pd.entries[PdIndex] & PTE_PS:
pd.entries[PdIndex] = 0
else:
pt: _p | t.CPtr = t.CType(pd.entries[PdIndex] & ~uint64(0xFFF), _p, t.CPtr)
pt.entries[PtIndex] = 0
c.Asm(f"invlpg [{c.AsmInp(VirtAddr, t.ASM_DESCR.REG_ANY)}]")
def EnablePaging():
global pml4, pdp, pd, pt
c.Asm(f"mov cr3, {c.AsmInp(uint64(pml4), t.ASM_DESCR.REG_ANY)}")
def _set_page_writable(phys_addr: uint64):
"""把物理页 phys_addr(identity 映射,虚拟=物理)的 PTE 置 W 位
页表页(bootloader 建立的 pml4/pdp/pd/pt 位于 0xf8xxxx)默认只读,
而内核运行时 MapPage/clone 需修改页表 → 必须先把这些页表页自身设为可写,
否则 CR0.WP=1 下写页表触发 #PF(slog.so 映射 0x600000 新建页表项时
cr2=0xf801000 err=0x3 根因)。
"""
global pml4
if not pml4: return
va: uint64 = phys_addr & ~uint64(0xFFF)
pml4_idx: uint64 = (va >> 39) & 0x1FF
if not (pml4.entries[pml4_idx] & PTE_PRESENT): return
pdp_s: _p | t.CPtr = t.CType(pml4.entries[pml4_idx] & ~uint64(0xFFF), _p, t.CPtr)
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_s.entries[pdp_idx] & PTE_PRESENT): return
pd_s: _p | t.CPtr = t.CType(pdp_s.entries[pdp_idx] & ~uint64(0xFFF), _p, t.CPtr)
pd_idx: uint64 = (va >> 21) & 0x1FF
if not (pd_s.entries[pd_idx] & PTE_PRESENT): return
if pd_s.entries[pd_idx] & PTE_PS:
# 2MB 大页:W 位在 PD 条目自身(无下层 PT),直接置位
pd_s.entries[pd_idx] = pd_s.entries[pd_idx] | uint64(PTE_WRITABLE)
c.Asm(f"invlpg [{c.AsmInp(va, t.ASM_DESCR.REG_ANY)}]")
return
pt_s: _p | t.CPtr = t.CType(pd_s.entries[pd_idx] & ~uint64(0xFFF), _p, t.CPtr)
pt_idx: uint64 = (va >> 12) & 0x1FF
pt_s.entries[pt_idx] = pt_s.entries[pt_idx] | uint64(PTE_WRITABLE)
c.Asm(f"invlpg [{c.AsmInp(va, t.ASM_DESCR.REG_ANY)}]")
def MakePTWritable():
"""把当前页表层次所有页表页(pml4 自身 + pdp/pd/pt)设为可写
内核运行时需修改页表(MapPage/clone_for_process),而 bootloader 建立的
页表页映射为只读——CR0.WP=1 下必须先放开页表页自身,否则写页表即 #PF。
"""
global pml4
if not pml4: return
_set_page_writable(uint64(pml4))
pi: uint64
for pi in range(512):
if not (pml4.entries[pi] & PTE_PRESENT): continue
pdp_m: _p | t.CPtr = t.CType(pml4.entries[pi] & ~uint64(0xFFF), _p, t.CPtr)
_set_page_writable(uint64(pdp_m))
pj: uint64
for pj in range(512):
if not (pdp_m.entries[pj] & PTE_PRESENT): continue
pd_m: _p | t.CPtr = t.CType(pdp_m.entries[pj] & ~uint64(0xFFF), _p, t.CPtr)
_set_page_writable(uint64(pd_m))
pk: uint64
for pk in range(512):
if not (pd_m.entries[pk] & PTE_PRESENT): continue
if pd_m.entries[pk] & PTE_PS: continue
pt_m: _p | t.CPtr = t.CType(pd_m.entries[pk] & ~uint64(0xFFF), _p, t.CPtr)
_set_page_writable(uint64(pt_m))
def MakeRangeRO(vaddr_start: uint64, vaddr_end: uint64):
"""把 [vaddr_start, vaddr_end) 范围内所有页的 PTE 清 W 位(只读)
定位越界写源:内核态写只读页在 CR0.WP=1 下触发 #PF(int=14),
isrCommonhandler 打印 cr2/rip → 写源立即暴露,不再静默破坏 .rodata。
"""
global pml4
if not pml4: return
va: uint64 = vaddr_start & ~uint64(0xFFF)
# ro_end 下取整到页:不保护含 ro_end 的未对齐尾页。链接脚本输出段未页对齐,
# .data 紧跟 .rodata(__rodata_end 未对齐时 .data 起始落在 rodata 尾页内),
# 整页置只读会把 .data 全局(如 devfs @DevFSObject=0x141630)误保护 →
# devfs_init 写全局 #PF(cr2=0x141630 rip=0x101aaa mov [r15],rax 根因)。
# 尾页含少量 rodata 尾部(对齐填充),保持可写可容忍。
ro_end_align: uint64 = vaddr_end & ~uint64(0xFFF)
while va < ro_end_align:
pml4_idx: uint64 = (va >> 39) & 0x1FF
if not (pml4.entries[pml4_idx] & PTE_PRESENT): va += 0x1000; continue
pdp_r: _p | t.CPtr = t.CType(pml4.entries[pml4_idx] & ~uint64(0xFFF), _p, t.CPtr)
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_r.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue
pd_r: _p | t.CPtr = t.CType(pdp_r.entries[pdp_idx] & ~uint64(0xFFF), _p, t.CPtr)
pd_idx: uint64 = (va >> 21) & 0x1FF
if not (pd_r.entries[pd_idx] & PTE_PRESENT): va += 0x1000; continue
if pd_r.entries[pd_idx] & PTE_PS:
# 2MB 大页:直接跳过会让 .rodata 保持可写——越界写被静默吞掉
# (perf 格式串 win=%lu/work=%lu 被逐步破坏的根因)。拆分为 512
# 个 4KB 页(复用 MapPage 的分裂逻辑),再走下方常规路径清 W 位。
# WP=1 后内核态写只读页即 #PF → isrCommonhandler 打印 cr2/rip 暴露写源。
big_phys: uint64 = pd_r.entries[pd_idx] & ~uint64(0x1FFFFF)
inherit_flags: uint64 = pd_r.entries[pd_idx] & (PTE_PCD | PTE_PWT | PTE_USER | uint64(0x8000000000000000))
pt_split: _p | t.CPtr = AllocPage()
if not pt_split:
va += 0x1000
continue
split_i: int = 0
while split_i < 512:
pt_split.entries[split_i] = (big_phys + uint64(split_i) * uint64(0x1000)) \
| PTE_PRESENT | PTE_WRITABLE | inherit_flags
split_i += 1
pd_r.entries[pd_idx] = (uint64(pt_split) & ~uint64(0xFFF)) \
| PTE_PRESENT | PTE_WRITABLE | (pd_r.entries[pd_idx] & PTE_USER)
c.Asm(f"invlpg [{c.AsmInp(va, t.ASM_DESCR.REG_ANY)}]")
# 分裂后落入下方 4KB 常规路径,继续清当前页 W 位
pt_r: _p | t.CPtr = t.CType(pd_r.entries[pd_idx] & ~uint64(0xFFF), _p, t.CPtr)
pt_idx: uint64 = (va >> 12) & 0x1FF
pt_r.entries[pt_idx] = pt_r.entries[pt_idx] & ~uint64(PTE_WRITABLE)
c.Asm(f"invlpg [{c.AsmInp(va, t.ASM_DESCR.REG_ANY)}]")
va += 0x1000
def ProtectKernelRO():
"""把内核镜像 .rodata 段(字符串常量/vtable 等)映射为只读
地址经 c.Asm 内联引用链接器符号 __rodata_start/__rodata_end
(linker.ld 定义,TPV 不声明 extern——避免生成定义与链接器符号冲突)。
"""
global pml4
if not pml4: return
# 页表页可写已在 paging.init(WP=0 阶段)由 MakePTWritable 完成,
# 此处只保护 .rodata 内容页(WP=1 后写只读页即 #PF 暴露写源)。
ro_start: uint64 = 0
ro_end: uint64 = 0
c.Asm(f"""lea rax, [rip + __rodata_start]
mov {c.AsmOut(ro_start, t.ASM_DESCR.OUTPUT_REG)}, rax
lea rax, [rip + __rodata_end]
mov {c.AsmOut(ro_end, t.ASM_DESCR.OUTPUT_REG)}, rax""",
op=[t.ASM_DESCR.CLOBBER_RAX])
MakeRangeRO(ro_start, ro_end)
# [PG] 打印 .rodata 起始页映射:确认只读真正生效。
# ps=1 说明仍在 2MB 大页(分裂失败)→ .rodata 保持可写 → 越界写继续被静默吞掉。
DumpMapping(ro_start, "ro_s")
def DumpMapping(va: uint64, tag: t.CConst | str):
"""[PG] 打印指定 VA 的 pml4/pdp/pd/pt 条目,定位大页(PS)与权限位
内核区域若为 UEFI 2MB 大页,MakeRangeRO 无法只读 .rodata(越界写静默破坏
格式串);1GB 页同理(PS 位在 pdpe)。W=1 且 PS=1 → 该页可写且是大页。
"""
global pml4
if not pml4: return
pml4_i: uint64 = (va >> 39) & 0x1FF
pdp_i: uint64 = (va >> 30) & 0x1FF
pd_i: uint64 = (va >> 21) & 0x1FF
pt_i: uint64 = (va >> 12) & 0x1FF
pml4_e: uint64 = pml4.entries[pml4_i]
pdp_e: uint64 = 0
pd_e: uint64 = 0
pt_e: uint64 = 0
if pml4_e & PTE_PRESENT:
pdp_r: _p | t.CPtr = t.CType(pml4_e & ~uint64(0xFFF), _p, t.CPtr)
pdp_e = pdp_r.entries[pdp_i]
if pdp_e & PTE_PRESENT:
if pdp_e & PTE_PS:
pd_e = pdp_e
else:
pd_r: _p | t.CPtr = t.CType(pdp_e & ~uint64(0xFFF), _p, t.CPtr)
pd_e = pd_r.entries[pd_i]
if (pd_e & PTE_PRESENT) and not (pd_e & PTE_PS):
pt_r: _p | t.CPtr = t.CType(pd_e & ~uint64(0xFFF), _p, t.CPtr)
pt_e = pt_r.entries[pt_i]
buf_d: t.CArray[t.CChar, 200]
viperlib.snprintf(c.Addr(buf_d), 200, "[PG] %s va=0x%lx pdpe=0x%lx pde=0x%lx pte=0x%lx (ps=%d W=%d)\n",
tag, va, pdp_e, pd_e, pt_e,
1 if (pd_e & PTE_PS) else 0,
1 if (pt_e & PTE_WRITABLE) or (pd_e & PTE_WRITABLE) or (pdp_e & PTE_WRITABLE) else 0)
serial.puts(c.Addr(buf_d))
def GetCurrentPml4() -> _p | t.CPtr:
global pml4, pdp, pd, pt
cr3: uint64
c.Asm(f"mov {c.AsmOut(cr3, t.ASM_DESCR.OUTPUT_REG)}, cr3")
return (_p | t.CPtr)(cr3 & ~uint64(0xFFF))
def create_process_page_table() -> uint64:
global pml4
if not pml4: return 0
new_pml4: _p | t.CPtr = AllocPage()
if not new_pml4: return 0
i: uint64
for i in range(512):
entry: uint64 = pml4.entries[i]
if entry & PTE_PRESENT:
new_pml4.entries[i] = entry
return uint64(new_pml4)
def clone_for_process(isolate_vaddr: uint64) -> uint64:
global pml4
_dbg: t.CArray[t.CChar, 120]
viperlib.snprintf(c.Addr(_dbg), 120, "[clone] pml4=0x%lx pool_used=%lu iso_va=0x%lx\n", uint64(pml4), pt_pool_used, isolate_vaddr)
serial.puts(_dbg)
if not pml4: return 0
new_pml4_page: _p | t.CPtr = AllocPage()
if not new_pml4_page:
# [DIAG] 定位 AllocPage 失败:pool_used 打印 0 但分配失败 → BSS 全局
# (pt_pool_raw/pt_pool_used)疑似被内存越界写覆盖。
_dbgf: t.CArray[t.CChar, 120]
viperlib.snprintf(c.Addr(_dbgf), 120, "[clone] AllocPage FAIL raw=0x%lx used=%lu PP=%d\n",
uint64(pt_pool_raw), pt_pool_used, PT_POOL_PAGES)
serial.puts(c.Addr(_dbgf))
serial.puts("[clone] AllocPage pml4 failed\n")
return 0
_dbg2: t.CArray[t.CChar, 120]
viperlib.snprintf(c.Addr(_dbg2), 120, "[clone] new_pml4=0x%lx\n", uint64(new_pml4_page))
serial.puts(_dbg2)
i: uint64
for i in range(512):
new_pml4_page.entries[i] = pml4.entries[i]
pml4_idx: uint64 = (isolate_vaddr >> 39) & 0x1FF
_dbg3: t.CArray[t.CChar, 120]
viperlib.snprintf(c.Addr(_dbg3), 120, "[clone] pml4_idx=%lu entry0=0x%lx\n", pml4_idx, pml4.entries[pml4_idx])
serial.puts(_dbg3)
if not (pml4.entries[pml4_idx] & PTE_PRESENT): return uint64(new_pml4_page)
kernel_pdp: _p | t.CPtr = t.CType(pml4.entries[pml4_idx] & ~uint64(0xFFF), _p, t.CPtr)
new_pdp_page: _p | t.CPtr = AllocPage()
if not new_pdp_page: return uint64(new_pml4_page)
for i in range(512):
new_pdp_page.entries[i] = kernel_pdp.entries[i]
new_pml4_page.entries[pml4_idx] = (uint64(new_pdp_page) & ~uint64(0xFFF)) | (pml4.entries[pml4_idx] & uint64(0xFFF))
pdp_idx: uint64 = (isolate_vaddr >> 30) & 0x1FF
if not (kernel_pdp.entries[pdp_idx] & PTE_PRESENT): return uint64(new_pml4_page)
kernel_pd: _p | t.CPtr = t.CType(kernel_pdp.entries[pdp_idx] & ~uint64(0xFFF), _p, t.CPtr)
new_pd_page: _p | t.CPtr = AllocPage()
if not new_pd_page: return uint64(new_pml4_page)
for i in range(512):
new_pd_page.entries[i] = kernel_pd.entries[i]
new_pdp_page.entries[pdp_idx] = (uint64(new_pd_page) & ~uint64(0xFFF)) | (kernel_pdp.entries[pdp_idx] & uint64(0xFFF))
pd_idx: uint64 = (isolate_vaddr >> 21) & 0x1FF
if not (kernel_pd.entries[pd_idx] & PTE_PRESENT): return uint64(new_pml4_page)
if kernel_pd.entries[pd_idx] & PTE_PS: return uint64(new_pml4_page)
kernel_pt: _p | t.CPtr = t.CType(kernel_pd.entries[pd_idx] & ~uint64(0xFFF), _p, t.CPtr)
new_pt_page: _p | t.CPtr = AllocPage()
if not new_pt_page: return uint64(new_pml4_page)
for i in range(512):
new_pt_page.entries[i] = kernel_pt.entries[i]
new_pd_page.entries[pd_idx] = (uint64(new_pt_page) & ~uint64(0xFFF)) | (kernel_pd.entries[pd_idx] & uint64(0xFFF))
return uint64(new_pml4_page)
def save_pt_range(vaddr_start: uint64, vaddr_end: uint64, save_buf: uint64 | t.CPtr, save_count: uint32 | t.CPtr) -> int:
global pml4
if not pml4: return -1
# c.Set(a,b)=a=b(改指针变量本身);写所指地址必须 c.Set(c.Deref(a),b)
c.Set(c.Deref(save_count), uint32(0))
va: uint64 = vaddr_start & ~uint64(0xFFF)
while va < vaddr_end:
pml4_idx: uint64 = (va >> 39) & 0x1FF
if not (pml4.entries[pml4_idx] & PTE_PRESENT): va += 0x1000; continue
pdp_: _p | t.CPtr = t.CType(pml4.entries[pml4_idx] & ~uint64(0xFFF), _p, t.CPtr)
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue
pd_: _p | t.CPtr = t.CType(pdp_.entries[pdp_idx] & ~uint64(0xFFF), _p, t.CPtr)
pd_idx: uint64 = (va >> 21) & 0x1FF
if not (pd_.entries[pd_idx] & PTE_PRESENT): va += 0x1000; continue
if pd_.entries[pd_idx] & PTE_PS: va += 0x200000; continue
pt_: _p | t.CPtr = t.CType(pd_.entries[pd_idx] & ~uint64(0xFFF), _p, t.CPtr)
pt_idx: uint64 = (va >> 12) & 0x1FF
saved: uint32 = c.DerefAs(save_count)
entry_ptr: uint64 | t.CPtr = save_buf + uint64(saved) * 16
c.DerefAs(entry_ptr, va)
c.DerefAs(entry_ptr + 8, pt_.entries[pt_idx])
c.Set(c.Deref(save_count), uint32(saved + 1))
va += 0x1000
return 0
def restore_pt_range(save_buf: uint64 | t.CPtr, save_count: uint32):
global pml4
if not pml4: return
i: uint32 = 0
while i < save_count:
entry_ptr: uint64 | t.CPtr = save_buf + uint64(i) * 16
va: uint64 = c.DerefAs(entry_ptr)
orig_pte: uint64 = c.DerefAs(entry_ptr + 8)
pml4_idx: uint64 = (va >> 39) & 0x1FF
if not (pml4.entries[pml4_idx] & PTE_PRESENT): i += 1; continue
pdp_: _p | t.CPtr = t.CType(pml4.entries[pml4_idx] & ~uint64(0xFFF), _p, t.CPtr)
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_.entries[pdp_idx] & PTE_PRESENT): i += 1; continue
pd_: _p | t.CPtr = t.CType(pdp_.entries[pdp_idx] & ~uint64(0xFFF), _p, t.CPtr)
pd_idx: uint64 = (va >> 21) & 0x1FF
if not (pd_.entries[pd_idx] & PTE_PRESENT): i += 1; continue
if pd_.entries[pd_idx] & PTE_PS: i += 1; continue
pt_: _p | t.CPtr = t.CType(pd_.entries[pd_idx] & ~uint64(0xFFF), _p, t.CPtr)
pt_idx: uint64 = (va >> 12) & 0x1FF
pt_.entries[pt_idx] = orig_pte
c.Asm(f"invlpg [{c.AsmInp(va, t.ASM_DESCR.REG_ANY)}]", op=[t.ASM_DESCR.CLOBBER_MEMORY])
i += 1
def set_active_pml4(cr3_val: uint64):
global pml4
pml4 = (_p | t.CPtr)(cr3_val & ~uint64(0xFFF))