chore: 改名同步 TransPyV -> ViperC(源码/配置内的纯替换)

This commit is contained in:
TermiNexus committed 2026-10-12 15:06:50 +08:00
1 parent c34bac0455
commit d093d3fc8b
10 files changed
+1543 -1543

No files matched your search

+30 -30
View File
@@ -1,30 +1,30 @@
{
"name": "Gargantua",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"--strip-all"
],
"output": "gargantua.elf"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
{
"name": "Gargantua",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./__vpcbuild__",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"--strip-all"
],
"output": "gargantua.elf"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
+30 -30
View File
@@ -1,30 +1,30 @@
{
"name": "HelloWorld",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"--strip-all"
],
"output": "helloworld.elf"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
{
"name": "HelloWorld",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./__vpcbuild__",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"--strip-all"
],
"output": "helloworld.elf"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
+30 -30
View File
@@ -1,30 +1,30 @@
{
"name": "Scene3D",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"--strip-all"
],
"output": "scene3d.elf"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
{
"name": "Scene3D",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./__vpcbuild__",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"--strip-all"
],
"output": "scene3d.elf"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
+30 -30
View File
@@ -1,30 +1,30 @@
{
"name": "Terminal",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"--strip-all"
],
"output": "terminal.elf"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
{
"name": "Terminal",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./__vpcbuild__",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"--strip-all"
],
"output": "terminal.elf"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
+33 -33
View File
@@ -1,33 +1,33 @@
{
"name": "SerialLogger",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=pic", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"-shared",
"-z", "notext",
"--no-relax",
"--strip-all"
],
"output": "slog.so"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
{
"name": "SerialLogger",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./__vpcbuild__",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=pic", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"-shared",
"-z", "notext",
"--no-relax",
"--strip-all"
],
"output": "slog.so"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
+33 -33
View File
@@ -1,33 +1,33 @@
{
"name": "VPUI64",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=pic", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"-shared",
"-z", "notext",
"--no-relax",
"--strip-all"
],
"output": "vpui64.so"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
{
"name": "VPUI64",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./__vpcbuild__",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=pic", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"-shared",
"-z", "notext",
"--no-relax",
"--strip-all"
],
"output": "vpui64.so"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
File diff suppressed because it is too large. Load diff
+476 -476
View File
@@ -1,476 +1,476 @@
import drivers.serial.uart.serial as serial
import viperlib
define PTE_PRESENT = (1 << 0)
define PTE_WRITABLE = (1 << 1)
define PTE_USER = (1 << 2)
define PTE_PWT = (1 << 3)
define PTE_PCD = (1 << 4)
define PTE_ACCESSED = (1 << 5)
define PTE_DIRTY = (1 << 6)
define PTE_PS = (1 << 7)
define PTE_GLOBAL = (1 << 8)
class _p:
entries: [512]uint64
define PT_POOL_PAGES = 512
pt_pool_raw: rawptr = None
pt_pool_start: uint64 = 0
pt_pool_used: uint64 = 0
AllocatedPages: uint64 = 0
pml4: _p* = None
pdp: _p*
pd: _p*
pt: _p*
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 暴露越界写源)。
__asm__(f"""
mov rax, cr0
and rax, ~0x10000
mov cr0, rax
""")
MakePTWritable()
__asm__(f"""
mov rax, cr0
or rax, 0x10000
mov cr0, rax
""")
def init_pool(buf: rawptr, 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
def AllocPage() -> rawptr:
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: rawptr = rawptr(pt_pool_start + pt_pool_used * uint64(4096))
pt_pool_used = pt_pool_used + uint64(1)
# rep stosb 向 [rdi] 写 4096 字节:必须声明 CLOBBER_MEMORY,否则优化器
# 无法得知本块改写了内存,可能把其前后的内存读写重排/消除(N8)。
__asm__(f"""mov al, 0
mov ecx, 4096
mov rdi, {asminp(page, ASM_DESCR.REG_ANY)}
rep stosb""",
op = [ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_AL,
ASM_DESCR.CLOBBER_ECX, ASM_DESCR.CLOBBER_RDI])
AllocatedPages = AllocatedPages + uint64(1)
return page
def FreePage(page: rawptr):
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 = 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*(pml4.entries[Pml4Index] & ~uint64(0xFFF))
if not (pdp.entries[PdpIndex] & PTE_PRESENT):
pd = 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*(pdp.entries[PdpIndex] & ~uint64(0xFFF))
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 = AllocPage()
if not pt: return
for split_i in range(512):
pt.entries[split_i] = (big_phys + uint64(split_i) * 0x1000) | PTE_PRESENT | PTE_WRITABLE | inherit_flags
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 = AllocPage()
if not pt: return
pd.entries[PdIndex] = (uint64(pt) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER)
pt = _p*(pd.entries[PdIndex] & ~uint64(0xFFF))
pt.entries[PtIndex] = PhysAddr | flags | PTE_PRESENT
__asm__(f"invlpg [{asminp(VirtAddr, 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 = AllocPage()
if not pdp: return
pml4.entries[Pml4Index] = (uint64(pdp) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE
pdp = _p*(pml4.entries[Pml4Index] & ~uint64(0xFFF))
if not (pdp.entries[PdpIndex] & PTE_PRESENT):
pd = AllocPage()
if not pd: return
pdp.entries[PdpIndex] = (uint64(pd) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE
pd = _p*(pdp.entries[PdpIndex] & ~uint64(0xFFF))
pd.entries[PdIndex] = PhysAddr | flags | PTE_PRESENT | PTE_PS
__asm__(f"invlpg [{asminp(VirtAddr, 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*(pml4.entries[Pml4Index] & ~uint64(0xFFF))
if not pdp.entries[PdpIndex] & PTE_PRESENT: return
pd = _p*(pdp.entries[PdpIndex] & ~uint64(0xFFF))
if not pd.entries[PdIndex] & PTE_PRESENT: return
if pd.entries[PdIndex] & PTE_PS:
pd.entries[PdIndex] = 0
else:
pt = _p*(pd.entries[PdIndex] & ~uint64(0xFFF))
pt.entries[PtIndex] = 0
__asm__(f"invlpg [{asminp(VirtAddr, ASM_DESCR.REG_ANY)}]")
def EnablePaging():
global pml4, pdp, pd, pt
__asm__(f"mov cr3, {asminp(uint64(pml4), 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* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF))
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_s.entries[pdp_idx] & PTE_PRESENT): return
pd_s: _p* = _p*(pdp_s.entries[pdp_idx] & ~uint64(0xFFF))
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)
__asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]")
return
pt_s: _p* = _p*(pd_s.entries[pd_idx] & ~uint64(0xFFF))
pt_idx: uint64 = (va >> 12) & 0x1FF
pt_s.entries[pt_idx] = pt_s.entries[pt_idx] | uint64(PTE_WRITABLE)
__asm__(f"invlpg [{asminp(va, 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))
for pi in range(512):
if not (pml4.entries[pi] & PTE_PRESENT): continue
pdp_m: _p* = _p*(pml4.entries[pi] & ~uint64(0xFFF))
_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* = _p*(pdp_m.entries[pj] & ~uint64(0xFFF))
_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* = _p*(pd_m.entries[pk] & ~uint64(0xFFF))
_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* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF))
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_r.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue
pd_r: _p* = _p*(pdp_r.entries[pdp_idx] & ~uint64(0xFFF))
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* = AllocPage()
if not pt_split:
va += 0x1000
continue
for split_i in range(512):
pt_split.entries[split_i] = (big_phys + uint64(split_i) * uint64(0x1000)) | PTE_PRESENT | PTE_WRITABLE | inherit_flags
pd_r.entries[pd_idx] = (uint64(pt_split) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (pd_r.entries[pd_idx] & PTE_USER)
__asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]")
# 分裂后落入下方 4KB 常规路径,继续清当前页 W 位
pt_r: _p* = _p*(pd_r.entries[pd_idx] & ~uint64(0xFFF))
pt_idx: uint64 = (va >> 12) & 0x1FF
pt_r.entries[pt_idx] = pt_r.entries[pt_idx] & ~uint64(PTE_WRITABLE)
__asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]")
va += 0x1000
def ProtectKernelRO():
"""把内核镜像 .rodata 段(字符串常量/vtable 等)映射为只读
地址经 __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
__asm__(f"""lea rax, [rip + __rodata_start]
mov {asmout(ro_start, ASM_DESCR.OUTPUT_REG)}, rax
lea rax, [rip + __rodata_end]
mov {asmout(ro_end, ASM_DESCR.OUTPUT_REG)}, rax""",
op=[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: const 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* = _p*(pml4_e & ~uint64(0xFFF))
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* = _p*(pdp_e & ~uint64(0xFFF))
pd_e = pd_r.entries[pd_i]
if (pd_e & PTE_PRESENT) and not (pd_e & PTE_PS):
pt_r: _p* = _p*(pd_e & ~uint64(0xFFF))
pt_e = pt_r.entries[pt_i]
buf_d: [200]char
viperlib.snprintf(&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(&buf_d)
def GetCurrentPml4() -> _p*:
global pml4, pdp, pd, pt
cr3: uint64
__asm__(f"mov {asmout(cr3, ASM_DESCR.OUTPUT_REG)}, cr3")
return _p*(cr3 & ~uint64(0xFFF))
def create_process_page_table() -> uint64:
global pml4
if not pml4: return 0
new_pml4: _p* = 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: [120]char
viperlib.snprintf(&_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* = AllocPage()
if not new_pml4_page:
# [DIAG] 定位 AllocPage 失败:pool_used 打印 0 但分配失败 → BSS 全局
# (pt_pool_raw/pt_pool_used)疑似被内存越界写覆盖。
_dbgf: [120]char
viperlib.snprintf(&_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(&_dbgf)
serial.puts("[clone] AllocPage pml4 failed\n")
return 0
_dbg2: [120]char
viperlib.snprintf(&_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: [120]char
viperlib.snprintf(&_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* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF))
new_pdp_page: _p* = 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* = _p*(kernel_pdp.entries[pdp_idx] & ~uint64(0xFFF))
new_pd_page: _p* = 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* = _p*(kernel_pd.entries[pd_idx] & ~uint64(0xFFF))
new_pt_page: _p* = 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*, save_count: uint32*) -> int:
global pml4
if not pml4: return -1
*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* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF))
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue
pd_: _p* = _p*(pdp_.entries[pdp_idx] & ~uint64(0xFFF))
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* = _p*(pd_.entries[pd_idx] & ~uint64(0xFFF))
pt_idx: uint64 = (va >> 12) & 0x1FF
saved: uint32 = *save_count
# [待复核·指针缩放] save_buf 是 `uint64*`;若该联合解析为 Ptr(u64),则下面的
# `* 16` 与 `+ 8` 都会再乘 8(GEP 按元素缩放)⇒ 记录步长 128 字节、字段偏移 64 字节,
# 与本处"16 字节一条记录(va 在 +0、pte 在 +8)"的设计不符。若本意是字节偏移,
# 需改用整数加法推进地址(或显式 rawptr/字节指针)——迁移到 rawptr[T] 时一并复核,
# 见 Debugger/rawptr-migration-and-fastfail.md。结论待定(需一次 IR 取证 GEP 的元素类型)。
entry_ptr: uint64* = save_buf + uint64(saved) * 16
*(entry_ptr + 0) = va
*(entry_ptr + 8) = pt_.entries[pt_idx]
*save_count = uint32(saved + 1)
va += 0x1000
return 0
def restore_pt_range(save_buf: uint64*, save_count: uint32):
global pml4
if not pml4: return
i: uint32 = 0
while i < save_count:
# [待复核·指针缩放] 同 save_pt_range:`* 16` / `+ 8` 若按 Ptr(u64) 缩放会变成 128/64 字节
# (该函数必须与 save_pt_range 的写入布局严格一致,两者要一起复核/一起改)。
entry_ptr: uint64* = save_buf + uint64(i) * 16
va: uint64 = *(entry_ptr + 0)
orig_pte: uint64 = *(entry_ptr + 8)
pml4_idx: uint64 = (va >> 39) & 0x1FF
if not (pml4.entries[pml4_idx] & PTE_PRESENT): i += 1; continue
pdp_: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF))
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_.entries[pdp_idx] & PTE_PRESENT): i += 1; continue
pd_: _p* = _p*(pdp_.entries[pdp_idx] & ~uint64(0xFFF))
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* = _p*(pd_.entries[pd_idx] & ~uint64(0xFFF))
pt_idx: uint64 = (va >> 12) & 0x1FF
pt_.entries[pt_idx] = orig_pte
__asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]", op=[ASM_DESCR.CLOBBER_MEMORY])
i += 1
def set_active_pml4(cr3_val: uint64):
global pml4
pml4 = _p*(cr3_val & ~uint64(0xFFF))
import drivers.serial.uart.serial as serial
import viperlib
define PTE_PRESENT = (1 << 0)
define PTE_WRITABLE = (1 << 1)
define PTE_USER = (1 << 2)
define PTE_PWT = (1 << 3)
define PTE_PCD = (1 << 4)
define PTE_ACCESSED = (1 << 5)
define PTE_DIRTY = (1 << 6)
define PTE_PS = (1 << 7)
define PTE_GLOBAL = (1 << 8)
class _p:
entries: [512]uint64
define PT_POOL_PAGES = 512
pt_pool_raw: rawptr = None
pt_pool_start: uint64 = 0
pt_pool_used: uint64 = 0
AllocatedPages: uint64 = 0
pml4: _p* = None
pdp: _p*
pd: _p*
pt: _p*
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 暴露越界写源)。
__asm__(f"""
mov rax, cr0
and rax, ~0x10000
mov cr0, rax
""")
MakePTWritable()
__asm__(f"""
mov rax, cr0
or rax, 0x10000
mov cr0, rax
""")
def init_pool(buf: rawptr, 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
def AllocPage() -> rawptr:
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: rawptr = rawptr(pt_pool_start + pt_pool_used * uint64(4096))
pt_pool_used = pt_pool_used + uint64(1)
# rep stosb 向 [rdi] 写 4096 字节:必须声明 CLOBBER_MEMORY,否则优化器
# 无法得知本块改写了内存,可能把其前后的内存读写重排/消除(N8)。
__asm__(f"""mov al, 0
mov ecx, 4096
mov rdi, {asminp(page, ASM_DESCR.REG_ANY)}
rep stosb""",
op = [ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_AL,
ASM_DESCR.CLOBBER_ECX, ASM_DESCR.CLOBBER_RDI])
AllocatedPages = AllocatedPages + uint64(1)
return page
def FreePage(page: rawptr):
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 = 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*(pml4.entries[Pml4Index] & ~uint64(0xFFF))
if not (pdp.entries[PdpIndex] & PTE_PRESENT):
pd = 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*(pdp.entries[PdpIndex] & ~uint64(0xFFF))
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 = AllocPage()
if not pt: return
for split_i in range(512):
pt.entries[split_i] = (big_phys + uint64(split_i) * 0x1000) | PTE_PRESENT | PTE_WRITABLE | inherit_flags
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 = AllocPage()
if not pt: return
pd.entries[PdIndex] = (uint64(pt) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (flags & PTE_USER)
pt = _p*(pd.entries[PdIndex] & ~uint64(0xFFF))
pt.entries[PtIndex] = PhysAddr | flags | PTE_PRESENT
__asm__(f"invlpg [{asminp(VirtAddr, 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 = AllocPage()
if not pdp: return
pml4.entries[Pml4Index] = (uint64(pdp) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE
pdp = _p*(pml4.entries[Pml4Index] & ~uint64(0xFFF))
if not (pdp.entries[PdpIndex] & PTE_PRESENT):
pd = AllocPage()
if not pd: return
pdp.entries[PdpIndex] = (uint64(pd) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE
pd = _p*(pdp.entries[PdpIndex] & ~uint64(0xFFF))
pd.entries[PdIndex] = PhysAddr | flags | PTE_PRESENT | PTE_PS
__asm__(f"invlpg [{asminp(VirtAddr, 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*(pml4.entries[Pml4Index] & ~uint64(0xFFF))
if not pdp.entries[PdpIndex] & PTE_PRESENT: return
pd = _p*(pdp.entries[PdpIndex] & ~uint64(0xFFF))
if not pd.entries[PdIndex] & PTE_PRESENT: return
if pd.entries[PdIndex] & PTE_PS:
pd.entries[PdIndex] = 0
else:
pt = _p*(pd.entries[PdIndex] & ~uint64(0xFFF))
pt.entries[PtIndex] = 0
__asm__(f"invlpg [{asminp(VirtAddr, ASM_DESCR.REG_ANY)}]")
def EnablePaging():
global pml4, pdp, pd, pt
__asm__(f"mov cr3, {asminp(uint64(pml4), 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* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF))
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_s.entries[pdp_idx] & PTE_PRESENT): return
pd_s: _p* = _p*(pdp_s.entries[pdp_idx] & ~uint64(0xFFF))
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)
__asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]")
return
pt_s: _p* = _p*(pd_s.entries[pd_idx] & ~uint64(0xFFF))
pt_idx: uint64 = (va >> 12) & 0x1FF
pt_s.entries[pt_idx] = pt_s.entries[pt_idx] | uint64(PTE_WRITABLE)
__asm__(f"invlpg [{asminp(va, 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))
for pi in range(512):
if not (pml4.entries[pi] & PTE_PRESENT): continue
pdp_m: _p* = _p*(pml4.entries[pi] & ~uint64(0xFFF))
_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* = _p*(pdp_m.entries[pj] & ~uint64(0xFFF))
_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* = _p*(pd_m.entries[pk] & ~uint64(0xFFF))
_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* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF))
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_r.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue
pd_r: _p* = _p*(pdp_r.entries[pdp_idx] & ~uint64(0xFFF))
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* = AllocPage()
if not pt_split:
va += 0x1000
continue
for split_i in range(512):
pt_split.entries[split_i] = (big_phys + uint64(split_i) * uint64(0x1000)) | PTE_PRESENT | PTE_WRITABLE | inherit_flags
pd_r.entries[pd_idx] = (uint64(pt_split) & ~uint64(0xFFF)) | PTE_PRESENT | PTE_WRITABLE | (pd_r.entries[pd_idx] & PTE_USER)
__asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]")
# 分裂后落入下方 4KB 常规路径,继续清当前页 W 位
pt_r: _p* = _p*(pd_r.entries[pd_idx] & ~uint64(0xFFF))
pt_idx: uint64 = (va >> 12) & 0x1FF
pt_r.entries[pt_idx] = pt_r.entries[pt_idx] & ~uint64(PTE_WRITABLE)
__asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]")
va += 0x1000
def ProtectKernelRO():
"""把内核镜像 .rodata 段(字符串常量/vtable 等)映射为只读
地址经 __asm__ 内联引用链接器符号 __rodata_start/__rodata_end
(linker.ld 定义,VPC 不声明 extern——避免生成定义与链接器符号冲突)。
"""
global pml4
if not pml4: return
# 页表页可写已在 paging.init(WP=0 阶段)由 MakePTWritable 完成,
# 此处只保护 .rodata 内容页(WP=1 后写只读页即 #PF 暴露写源)。
ro_start: uint64 = 0
ro_end: uint64 = 0
__asm__(f"""lea rax, [rip + __rodata_start]
mov {asmout(ro_start, ASM_DESCR.OUTPUT_REG)}, rax
lea rax, [rip + __rodata_end]
mov {asmout(ro_end, ASM_DESCR.OUTPUT_REG)}, rax""",
op=[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: const 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* = _p*(pml4_e & ~uint64(0xFFF))
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* = _p*(pdp_e & ~uint64(0xFFF))
pd_e = pd_r.entries[pd_i]
if (pd_e & PTE_PRESENT) and not (pd_e & PTE_PS):
pt_r: _p* = _p*(pd_e & ~uint64(0xFFF))
pt_e = pt_r.entries[pt_i]
buf_d: [200]char
viperlib.snprintf(&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(&buf_d)
def GetCurrentPml4() -> _p*:
global pml4, pdp, pd, pt
cr3: uint64
__asm__(f"mov {asmout(cr3, ASM_DESCR.OUTPUT_REG)}, cr3")
return _p*(cr3 & ~uint64(0xFFF))
def create_process_page_table() -> uint64:
global pml4
if not pml4: return 0
new_pml4: _p* = 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: [120]char
viperlib.snprintf(&_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* = AllocPage()
if not new_pml4_page:
# [DIAG] 定位 AllocPage 失败:pool_used 打印 0 但分配失败 → BSS 全局
# (pt_pool_raw/pt_pool_used)疑似被内存越界写覆盖。
_dbgf: [120]char
viperlib.snprintf(&_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(&_dbgf)
serial.puts("[clone] AllocPage pml4 failed\n")
return 0
_dbg2: [120]char
viperlib.snprintf(&_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: [120]char
viperlib.snprintf(&_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* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF))
new_pdp_page: _p* = 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* = _p*(kernel_pdp.entries[pdp_idx] & ~uint64(0xFFF))
new_pd_page: _p* = 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* = _p*(kernel_pd.entries[pd_idx] & ~uint64(0xFFF))
new_pt_page: _p* = 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*, save_count: uint32*) -> int:
global pml4
if not pml4: return -1
*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* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF))
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_.entries[pdp_idx] & PTE_PRESENT): va += 0x1000; continue
pd_: _p* = _p*(pdp_.entries[pdp_idx] & ~uint64(0xFFF))
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* = _p*(pd_.entries[pd_idx] & ~uint64(0xFFF))
pt_idx: uint64 = (va >> 12) & 0x1FF
saved: uint32 = *save_count
# [待复核·指针缩放] save_buf 是 `uint64*`;若该联合解析为 Ptr(u64),则下面的
# `* 16` 与 `+ 8` 都会再乘 8(GEP 按元素缩放)⇒ 记录步长 128 字节、字段偏移 64 字节,
# 与本处"16 字节一条记录(va 在 +0、pte 在 +8)"的设计不符。若本意是字节偏移,
# 需改用整数加法推进地址(或显式 rawptr/字节指针)——迁移到 rawptr[T] 时一并复核,
# 见 Debugger/rawptr-migration-and-fastfail.md。结论待定(需一次 IR 取证 GEP 的元素类型)。
entry_ptr: uint64* = save_buf + uint64(saved) * 16
*(entry_ptr + 0) = va
*(entry_ptr + 8) = pt_.entries[pt_idx]
*save_count = uint32(saved + 1)
va += 0x1000
return 0
def restore_pt_range(save_buf: uint64*, save_count: uint32):
global pml4
if not pml4: return
i: uint32 = 0
while i < save_count:
# [待复核·指针缩放] 同 save_pt_range:`* 16` / `+ 8` 若按 Ptr(u64) 缩放会变成 128/64 字节
# (该函数必须与 save_pt_range 的写入布局严格一致,两者要一起复核/一起改)。
entry_ptr: uint64* = save_buf + uint64(i) * 16
va: uint64 = *(entry_ptr + 0)
orig_pte: uint64 = *(entry_ptr + 8)
pml4_idx: uint64 = (va >> 39) & 0x1FF
if not (pml4.entries[pml4_idx] & PTE_PRESENT): i += 1; continue
pdp_: _p* = _p*(pml4.entries[pml4_idx] & ~uint64(0xFFF))
pdp_idx: uint64 = (va >> 30) & 0x1FF
if not (pdp_.entries[pdp_idx] & PTE_PRESENT): i += 1; continue
pd_: _p* = _p*(pdp_.entries[pdp_idx] & ~uint64(0xFFF))
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* = _p*(pd_.entries[pd_idx] & ~uint64(0xFFF))
pt_idx: uint64 = (va >> 12) & 0x1FF
pt_.entries[pt_idx] = orig_pte
__asm__(f"invlpg [{asminp(va, ASM_DESCR.REG_ANY)}]", op=[ASM_DESCR.CLOBBER_MEMORY])
i += 1
def set_active_pml4(cr3_val: uint64):
global pml4
pml4 = _p*(cr3_val & ~uint64(0xFFF))
+39 -39
View File
@@ -1,40 +1,40 @@
{
"name": "Viper OS Kernel",
"version": "2.3.7",
"source_dir": "./Kernel",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": [
"-filetype=obj",
"-mtriple=x86_64-none-elf",
"-relocation-model=static",
"-O2"
]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m",
"elf_x86_64",
"-T",
"linker.ld",
"--oformat",
"binary",
"--strip-all"
],
"output": "kernel.bin"
},
"includes": [
"../../../includes"
],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
{
"name": "Viper OS Kernel",
"version": "2.3.7",
"source_dir": "./Kernel",
"build_dir": "./__vpcbuild__",
"compiler": {
"cmd": "llc",
"flags": [
"-filetype=obj",
"-mtriple=x86_64-none-elf",
"-relocation-model=static",
"-O2"
]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m",
"elf_x86_64",
"-T",
"linker.ld",
"--oformat",
"binary",
"--strip-all"
],
"output": "kernel.bin"
},
"includes": [
"../../../includes"
],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
+20 -20
View File
@@ -12,13 +12,13 @@ noapp = '--noapp' in sys.argv
# 编译内核
cmd = r'..\..\TransPyV\output\TransPyV.exe --project ./VKernel/project.vpj --clear-cache --rebuild-includes'
cmd = r'..\..\ViperC\output\viperc.exe --project ./VKernel/project.vpj --clear-cache --rebuild-includes'
if clean:
cmd += ' --clean'
os.system(cmd)
shutil.copy("VKernel/.tpv_build/output/kernel.bin", "./disk/sys/kernel/x86_64/kernel.bin")
shutil.copy("VKernel/__vpcbuild__/output/kernel.bin", "./disk/sys/kernel/x86_64/kernel.bin")
# --noapp 优先级高于 --kernel-only:只编译内核,不编译所有库和应用
@@ -26,14 +26,14 @@ if noapp:
print("[build] --noapp: 跳过所有库与应用编译,复用已有缓存文件")
# 检查必要文件是否存在
required_libs = [
("Libs/SerialLogger/.tpv_build/output/slog.so", "./disk/libs/slog.so"),
("Libs/VPUI64/.tpv_build/output/vpui64.so", "./disk/libs/vpui64.so"),
("Libs/SerialLogger/__vpcbuild__/output/slog.so", "./disk/libs/slog.so"),
("Libs/VPUI64/__vpcbuild__/output/vpui64.so", "./disk/libs/vpui64.so"),
]
required_apps = [
("Apps/HelloWorld/.tpv_build/output/helloworld.elf", "./disk/apps/hello.elf"),
("Apps/Terminal/.tpv_build/output/terminal.elf", "./disk/apps/terminal.elf"),
("Apps/Gargantua/.tpv_build/output/gargantua.elf", "./disk/apps/gargantua.elf"),
("Apps/Scene3D/.tpv_build/output/scene3d.elf", "./disk/apps/scene3d.elf"),
("Apps/HelloWorld/__vpcbuild__/output/helloworld.elf", "./disk/apps/hello.elf"),
("Apps/Terminal/__vpcbuild__/output/terminal.elf", "./disk/apps/terminal.elf"),
("Apps/Gargantua/__vpcbuild__/output/gargantua.elf", "./disk/apps/gargantua.elf"),
("Apps/Scene3D/__vpcbuild__/output/scene3d.elf", "./disk/apps/scene3d.elf"),
]
os.makedirs("./disk/apps", exist_ok=True)
os.makedirs("./disk/libs", exist_ok=True)
@@ -49,48 +49,48 @@ elif kernel_only:
print("[build] 警告: hello.elf 不存在,请先不带 --kernel-only 完整构建一次")
else:
# 完整编译所有库+应用
so_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Libs/SerialLogger/project.vpj --clear-cache'
so_cmd = r'..\..\ViperC\output\viperc.exe --project Libs/SerialLogger/project.vpj --clear-cache'
if clean:
so_cmd += ' --clean'
os.system(so_cmd)
vpui_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Libs/VPUI64/project.vpj --clear-cache'
vpui_cmd = r'..\..\ViperC\output\viperc.exe --project Libs/VPUI64/project.vpj --clear-cache'
if clean:
vpui_cmd += ' --clean'
os.system(vpui_cmd)
app_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Apps/HelloWorld/project.vpj --clear-cache'
app_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/HelloWorld/project.vpj --clear-cache'
if clean:
app_cmd += ' --clean'
os.system(app_cmd)
os.makedirs("./disk/apps", exist_ok=True)
shutil.copy("Apps/HelloWorld/.tpv_build/output/helloworld.elf", "./disk/apps/hello.elf")
shutil.copy("Apps/HelloWorld/__vpcbuild__/output/helloworld.elf", "./disk/apps/hello.elf")
term_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Apps/Terminal/project.vpj --clear-cache'
term_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/Terminal/project.vpj --clear-cache'
if clean:
term_cmd += ' --clean'
os.system(term_cmd)
shutil.copy("Apps/Terminal/.tpv_build/output/terminal.elf", "./disk/apps/terminal.elf")
shutil.copy("Apps/Terminal/__vpcbuild__/output/terminal.elf", "./disk/apps/terminal.elf")
garg_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Apps/Gargantua/project.vpj --clear-cache'
garg_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/Gargantua/project.vpj --clear-cache'
if clean:
garg_cmd += ' --clean'
os.system(garg_cmd)
shutil.copy("Apps/Gargantua/.tpv_build/output/gargantua.elf", "./disk/apps/gargantua.elf")
shutil.copy("Apps/Gargantua/__vpcbuild__/output/gargantua.elf", "./disk/apps/gargantua.elf")
scene3d_cmd = r'..\..\TransPyV\output\TransPyV.exe --project Apps/Scene3D/project.vpj --clear-cache'
scene3d_cmd = r'..\..\ViperC\output\viperc.exe --project Apps/Scene3D/project.vpj --clear-cache'
if clean:
scene3d_cmd += ' --clean'
os.system(scene3d_cmd)
shutil.copy("Apps/Scene3D/.tpv_build/output/scene3d.elf", "./disk/apps/scene3d.elf")
shutil.copy("Apps/Scene3D/__vpcbuild__/output/scene3d.elf", "./disk/apps/scene3d.elf")
os.makedirs("./disk/libs", exist_ok=True)
so_path = os.path.abspath("Libs/SerialLogger/.tpv_build/output/slog.so")
vpui_path = os.path.abspath("Libs/VPUI64/.tpv_build/output/vpui64.so")
so_path = os.path.abspath("Libs/SerialLogger/__vpcbuild__/output/slog.so")
vpui_path = os.path.abspath("Libs/VPUI64/__vpcbuild__/output/vpui64.so")
if os.path.exists(so_path):
shutil.copy(so_path, "./disk/libs/slog.so")
if os.path.exists(vpui_path):