473 lines
22 KiB
Plaintext
473 lines
22 KiB
Plaintext
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[t.CUInt64T, 512]
|
||
|
||
PT_POOL_PAGES: t.CDefine = 512
|
||
pt_pool_raw: t.CVoid | t.CPtr = None
|
||
pt_pool_start: t.CUInt64T = 0
|
||
pt_pool_used: t.CUInt64T = 0
|
||
|
||
AllocatedPages: t.CUInt64T = 0
|
||
pml4: _p | t.CPtr = None
|
||
pdp: _p | t.CPtr
|
||
pd: _p | t.CPtr
|
||
pt: _p | t.CPtr
|
||
|
||
def init(MemmapAddr: t.CUInt64T, MemmapSize: t.CUInt64T):
|
||
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.CVoid | t.CPtr, buf_size: t.CUInt64T):
|
||
global pt_pool_raw, pt_pool_start, pt_pool_used
|
||
pt_pool_raw = buf
|
||
if buf is None: return
|
||
raw_addr: t.CUInt64T = t.CUInt64T(buf)
|
||
aligned_addr: t.CUInt64T = (raw_addr + t.CUInt64T(0xFFF)) & ~t.CUInt64T(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", t.CUInt64T(pt_pool_raw), pt_pool_start)
|
||
serial.puts(c.Addr(_dip))
|
||
|
||
def AllocPage() -> t.CVoid | t.CPtr:
|
||
global AllocatedPages, pt_pool_used, pml4, pdp, pd, pt
|
||
if pt_pool_raw is None: return None
|
||
if pt_pool_used >= t.CUInt64T(PT_POOL_PAGES): return None
|
||
page: t.CVoid | t.CPtr = t.CVoid(pt_pool_start + pt_pool_used * t.CUInt64T(4096), t.CPtr)
|
||
pt_pool_used = pt_pool_used + t.CUInt64T(1)
|
||
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_AL, t.ASM_DESCR.CLOBBER_ECX, t.ASM_DESCR.CLOBBER_RDI])
|
||
AllocatedPages = AllocatedPages + t.CUInt64T(1)
|
||
return page
|
||
|
||
def FreePage(page: t.CVoid | t.CPtr):
|
||
global AllocatedPages, pml4, pdp, pd, pt
|
||
if page is None: return
|
||
if AllocatedPages > 0: AllocatedPages = AllocatedPages - t.CUInt64T(1)
|
||
|
||
def MapPage(VirtAddr: t.CUInt64T, PhysAddr: t.CUInt64T, flags: t.CUInt64T):
|
||
global pml4, pdp, pd, pt
|
||
if VirtAddr == 0 or PhysAddr == 0: return
|
||
VirtAddr = VirtAddr & ~t.CUInt64T(0xFFF)
|
||
PhysAddr = PhysAddr & ~t.CUInt64T(0xFFF)
|
||
Pml4Index: t.CUInt64T = (VirtAddr >> 39) & 0x1FF
|
||
PdpIndex: t.CUInt64T = (VirtAddr >> 30) & 0x1FF
|
||
PdIndex: t.CUInt64T = (VirtAddr >> 21) & 0x1FF
|
||
PtIndex: t.CUInt64T = (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] = (t.CUInt64T(pdp) & ~t.CUInt64T(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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
|
||
if not (pdp.entries[PdpIndex] & PTE_PRESENT):
|
||
pd: _p | t.CPtr = AllocPage()
|
||
if not pd: return
|
||
pdp.entries[PdpIndex] = (t.CUInt64T(pd) & ~t.CUInt64T(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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
|
||
if pd.entries[PdIndex] & 1:
|
||
if pd.entries[PdIndex] & 0x80:
|
||
big_phys: t.CUInt64T = pd.entries[PdIndex] & ~t.CUInt64T(0x1FFFFF)
|
||
inherit_flags: t.CUInt64T = pd.entries[PdIndex] & (PTE_PCD | PTE_PWT)
|
||
pt: _p | t.CPtr = AllocPage()
|
||
if not pt: return
|
||
split_i: t.CInt = 0
|
||
while split_i < 512:
|
||
pt.entries[split_i] = (big_phys + t.CUInt64T(split_i) * 0x1000) | PTE_PRESENT | PTE_WRITABLE | inherit_flags
|
||
split_i += 1
|
||
pd.entries[PdIndex] = (t.CUInt64T(pt) & ~t.CUInt64T(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] = (t.CUInt64T(pt) & ~t.CUInt64T(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER)
|
||
pt: _p | t.CPtr = t.CType(pd.entries[PdIndex] & ~t.CUInt64T(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: t.CUInt64T, PhysAddr: t.CUInt64T, flags: t.CUInt64T) -> t.CVoid:
|
||
global pml4, pdp, pd, pt
|
||
VirtAddr = VirtAddr & ~t.CUInt64T(0x1FFFFF)
|
||
PhysAddr = PhysAddr & ~t.CUInt64T(0x1FFFFF)
|
||
Pml4Index: t.CUInt64T = (VirtAddr >> 39) & 0x1FF
|
||
PdpIndex: t.CUInt64T = (VirtAddr >> 30) & 0x1FF
|
||
PdIndex: t.CUInt64T = (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] = (t.CUInt64T(pdp) & ~t.CUInt64T(0xFFF)) | PTE_PRESENT | PTE_WRITABLE
|
||
pdp: _p | t.CPtr = t.CType(pml4.entries[Pml4Index] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
|
||
if not (pdp.entries[PdpIndex] & PTE_PRESENT):
|
||
pd: _p | t.CPtr = AllocPage()
|
||
if not pd: return
|
||
pdp.entries[PdpIndex] = (t.CUInt64T(pd) & ~t.CUInt64T(0xFFF)) | PTE_PRESENT | PTE_WRITABLE
|
||
pd: _p | t.CPtr = t.CType(pdp.entries[PdpIndex] & ~t.CUInt64T(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: t.CUInt64T):
|
||
global pml4, pdp, pd, pt
|
||
if VirtAddr == 0: return
|
||
if not pml4: return
|
||
Pml4Index: t.CUInt64T = (VirtAddr >> 39) & 0x1FF
|
||
PdpIndex: t.CUInt64T = (VirtAddr >> 30) & 0x1FF
|
||
PdIndex: t.CUInt64T = (VirtAddr >> 21) & 0x1FF
|
||
PtIndex: t.CUInt64T = (VirtAddr >> 12) & 0x1FF
|
||
|
||
if not pml4.entries[Pml4Index] & PTE_PRESENT: return
|
||
|
||
pdp: _p | t.CPtr = t.CType(pml4.entries[Pml4Index] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
if not pdp.entries[PdpIndex] & PTE_PRESENT: return
|
||
|
||
pd: _p | t.CPtr = t.CType(pdp.entries[PdpIndex] & ~t.CUInt64T(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] & ~t.CUInt64T(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(t.CUInt64T(pml4), t.ASM_DESCR.REG_ANY)}")
|
||
|
||
def _set_page_writable(phys_addr: t.CUInt64T):
|
||
"""把物理页 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: t.CUInt64T = phys_addr & ~t.CUInt64T(0xFFF)
|
||
pml4_idx: t.CUInt64T = (va >> 39) & 0x1FF
|
||
if not (pml4.entries[pml4_idx] & PTE_PRESENT): return
|
||
pdp_s: _p | t.CPtr = t.CType(pml4.entries[pml4_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pdp_idx: t.CUInt64T = (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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pd_idx: t.CUInt64T = (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] | t.CUInt64T(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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pt_idx: t.CUInt64T = (va >> 12) & 0x1FF
|
||
pt_s.entries[pt_idx] = pt_s.entries[pt_idx] | t.CUInt64T(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(t.CUInt64T(pml4))
|
||
pi: t.CUInt64T
|
||
for pi in range(512):
|
||
if not (pml4.entries[pi] & PTE_PRESENT): continue
|
||
pdp_m: _p | t.CPtr = t.CType(pml4.entries[pi] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
_set_page_writable(t.CUInt64T(pdp_m))
|
||
pj: t.CUInt64T
|
||
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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
_set_page_writable(t.CUInt64T(pd_m))
|
||
pk: t.CUInt64T
|
||
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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
_set_page_writable(t.CUInt64T(pt_m))
|
||
|
||
def MakeRangeRO(vaddr_start: t.CUInt64T, vaddr_end: t.CUInt64T):
|
||
"""把 [vaddr_start, vaddr_end) 范围内所有页的 PTE 清 W 位(只读)
|
||
|
||
定位越界写源:内核态写只读页在 CR0.WP=1 下触发 #PF(int=14),
|
||
isrCommonhandler 打印 cr2/rip → 写源立即暴露,不再静默破坏 .rodata。
|
||
"""
|
||
global pml4
|
||
if not pml4: return
|
||
va: t.CUInt64T = vaddr_start & ~t.CUInt64T(0xFFF)
|
||
while va < vaddr_end:
|
||
pml4_idx: t.CUInt64T = (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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pdp_idx: t.CUInt64T = (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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pd_idx: t.CUInt64T = (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: t.CUInt64T = pd_r.entries[pd_idx] & ~t.CUInt64T(0x1FFFFF)
|
||
inherit_flags: t.CUInt64T = pd_r.entries[pd_idx] & (PTE_PCD | PTE_PWT | PTE_USER | t.CUInt64T(0x8000000000000000))
|
||
pt_split: _p | t.CPtr = AllocPage()
|
||
if not pt_split:
|
||
va += 0x1000
|
||
continue
|
||
split_i: t.CInt = 0
|
||
while split_i < 512:
|
||
pt_split.entries[split_i] = (big_phys + t.CUInt64T(split_i) * t.CUInt64T(0x1000)) \
|
||
| PTE_PRESENT | PTE_WRITABLE | inherit_flags
|
||
split_i += 1
|
||
pd_r.entries[pd_idx] = (t.CUInt64T(pt_split) & ~t.CUInt64T(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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pt_idx: t.CUInt64T = (va >> 12) & 0x1FF
|
||
pt_r.entries[pt_idx] = pt_r.entries[pt_idx] & ~t.CUInt64T(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: t.CUInt64T = 0
|
||
ro_end: t.CUInt64T = 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: t.CUInt64T, 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: t.CUInt64T = (va >> 39) & 0x1FF
|
||
pdp_i: t.CUInt64T = (va >> 30) & 0x1FF
|
||
pd_i: t.CUInt64T = (va >> 21) & 0x1FF
|
||
pt_i: t.CUInt64T = (va >> 12) & 0x1FF
|
||
pml4_e: t.CUInt64T = pml4.entries[pml4_i]
|
||
pdp_e: t.CUInt64T = 0
|
||
pd_e: t.CUInt64T = 0
|
||
pt_e: t.CUInt64T = 0
|
||
if pml4_e & PTE_PRESENT:
|
||
pdp_r: _p | t.CPtr = t.CType(pml4_e & ~t.CUInt64T(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 & ~t.CUInt64T(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 & ~t.CUInt64T(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: t.CUInt64T
|
||
c.Asm(f"mov {c.AsmOut(cr3, t.ASM_DESCR.OUTPUT_REG)}, cr3")
|
||
return (_p | t.CPtr)(cr3 & ~t.CUInt64T(0xFFF))
|
||
|
||
def create_process_page_table() -> t.CUInt64T:
|
||
global pml4
|
||
if not pml4: return 0
|
||
new_pml4: _p | t.CPtr = AllocPage()
|
||
if not new_pml4: return 0
|
||
i: t.CUInt64T
|
||
for i in range(512):
|
||
entry: t.CUInt64T = pml4.entries[i]
|
||
if entry & PTE_PRESENT:
|
||
new_pml4.entries[i] = entry
|
||
return t.CUInt64T(new_pml4)
|
||
|
||
def clone_for_process(isolate_vaddr: t.CUInt64T) -> t.CUInt64T:
|
||
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", t.CUInt64T(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",
|
||
t.CUInt64T(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", t.CUInt64T(new_pml4_page))
|
||
serial.puts(_dbg2)
|
||
i: t.CUInt64T
|
||
for i in range(512):
|
||
new_pml4_page.entries[i] = pml4.entries[i]
|
||
pml4_idx: t.CUInt64T = (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 t.CUInt64T(new_pml4_page)
|
||
kernel_pdp: _p | t.CPtr = t.CType(pml4.entries[pml4_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
new_pdp_page: _p | t.CPtr = AllocPage()
|
||
if not new_pdp_page: return t.CUInt64T(new_pml4_page)
|
||
for i in range(512):
|
||
new_pdp_page.entries[i] = kernel_pdp.entries[i]
|
||
new_pml4_page.entries[pml4_idx] = (t.CUInt64T(new_pdp_page) & ~t.CUInt64T(0xFFF)) | (pml4.entries[pml4_idx] & t.CUInt64T(0xFFF))
|
||
pdp_idx: t.CUInt64T = (isolate_vaddr >> 30) & 0x1FF
|
||
if not (kernel_pdp.entries[pdp_idx] & PTE_PRESENT): return t.CUInt64T(new_pml4_page)
|
||
kernel_pd: _p | t.CPtr = t.CType(kernel_pdp.entries[pdp_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
new_pd_page: _p | t.CPtr = AllocPage()
|
||
if not new_pd_page: return t.CUInt64T(new_pml4_page)
|
||
for i in range(512):
|
||
new_pd_page.entries[i] = kernel_pd.entries[i]
|
||
new_pdp_page.entries[pdp_idx] = (t.CUInt64T(new_pd_page) & ~t.CUInt64T(0xFFF)) | (kernel_pdp.entries[pdp_idx] & t.CUInt64T(0xFFF))
|
||
pd_idx: t.CUInt64T = (isolate_vaddr >> 21) & 0x1FF
|
||
if not (kernel_pd.entries[pd_idx] & PTE_PRESENT): return t.CUInt64T(new_pml4_page)
|
||
if kernel_pd.entries[pd_idx] & PTE_PS: return t.CUInt64T(new_pml4_page)
|
||
kernel_pt: _p | t.CPtr = t.CType(kernel_pd.entries[pd_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
new_pt_page: _p | t.CPtr = AllocPage()
|
||
if not new_pt_page: return t.CUInt64T(new_pml4_page)
|
||
for i in range(512):
|
||
new_pt_page.entries[i] = kernel_pt.entries[i]
|
||
new_pd_page.entries[pd_idx] = (t.CUInt64T(new_pt_page) & ~t.CUInt64T(0xFFF)) | (kernel_pd.entries[pd_idx] & t.CUInt64T(0xFFF))
|
||
return t.CUInt64T(new_pml4_page)
|
||
|
||
def save_pt_range(vaddr_start: t.CUInt64T, vaddr_end: t.CUInt64T, save_buf: t.CUInt64T | t.CPtr, save_count: t.CUInt32T | t.CPtr) -> t.CInt:
|
||
global pml4
|
||
if not pml4: return -1
|
||
c.Set(save_count, t.CUInt32T(0))
|
||
va: t.CUInt64T = vaddr_start & ~t.CUInt64T(0xFFF)
|
||
while va < vaddr_end:
|
||
pml4_idx: t.CUInt64T = (va >> 39) & 0x1FF
|
||
if not (pml4.entries[pml4_idx] & PTE_PRESENT): va += 0x1000; continue
|
||
pdp_: _p | t.CPtr = t.CType(pml4.entries[pml4_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pdp_idx: t.CUInt64T = (va >> 30) & 0x1FF
|
||
if not (pdp_.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue
|
||
pd_: _p | t.CPtr = t.CType(pdp_.entries[pdp_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pd_idx: t.CUInt64T = (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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pt_idx: t.CUInt64T = (va >> 12) & 0x1FF
|
||
saved: t.CUInt32T = c.DerefAs(save_count)
|
||
entry_ptr: t.CUInt64T | t.CPtr = save_buf + t.CUInt64T(saved) * 16
|
||
c.DerefAs(entry_ptr, va)
|
||
c.DerefAs(entry_ptr + 8, pt_.entries[pt_idx])
|
||
c.Set(save_count, t.CUInt32T(saved + 1))
|
||
va += 0x1000
|
||
return 0
|
||
|
||
def restore_pt_range(save_buf: t.CUInt64T | t.CPtr, save_count: t.CUInt32T):
|
||
global pml4
|
||
if not pml4: return
|
||
i: t.CUInt32T = 0
|
||
while i < save_count:
|
||
entry_ptr: t.CUInt64T | t.CPtr = save_buf + t.CUInt64T(i) * 16
|
||
va: t.CUInt64T = c.DerefAs(entry_ptr)
|
||
orig_pte: t.CUInt64T = c.DerefAs(entry_ptr + 8)
|
||
pml4_idx: t.CUInt64T = (va >> 39) & 0x1FF
|
||
if not (pml4.entries[pml4_idx] & PTE_PRESENT): i += 1; continue
|
||
pdp_: _p | t.CPtr = t.CType(pml4.entries[pml4_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pdp_idx: t.CUInt64T = (va >> 30) & 0x1FF
|
||
if not (pdp_.entries[pdp_idx] & PTE_PRESENT): i += 1; continue
|
||
pd_: _p | t.CPtr = t.CType(pdp_.entries[pdp_idx] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pd_idx: t.CUInt64T = (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] & ~t.CUInt64T(0xFFF), _p, t.CPtr)
|
||
pt_idx: t.CUInt64T = (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: t.CUInt64T):
|
||
global pml4
|
||
pml4 = (_p | t.CPtr)(cr3_val & ~t.CUInt64T(0xFFF))
|