-
Yinary authored
### 改动原因 启动阶段由 polyhal-boot 建立的粗页表(BOOT_PT)以 1 GiB 大页把整个 256 GiB 虚拟空间映射为 RWX,存在三个问题: 1. 内核镜像无任何权限隔离(代码段可写、数据段可执行); 2. 映射了不存在的物理地址,越界访问命中垃圾而非干净的 page fault; 3. MMIO 区(UART/PLIC 等)对内核全程可见可写。 另含两类配套改动:项目定位为仅单核,Makefile 默认 `-smp 1` 并统一口径; trap 断点自检恢复为可观测形式(计数 + 断言)。 ### 改动思路 新建 `mm/kernel_pt`,启动期构建一张内核页表(表 B)并切换 satp: - 镜像区间 4 KiB 叶子按段分权:text RX / rodata R / data 起往后 RW; - 镜像所在 GiB 的其余部分与后续 RAM 用 2 MiB / 1 GiB 大页 RWX(按 FDT 实际 RAM 顶逐 GiB 展开,非整 GiB 尾部降为 2 MiB 叶子); - 低半区(无恒等窗口)、MMIO 区、RAM 之外不映射,越界访问被页表拦截; - 切换前对表 B 做软件走表自检(分段权限与物理地址精确回读、MMIO 与 恒等窗口断言无效),失败时仍在旧表上 panic;BOOT_PT 物理地址留存备诊断; - la 侧为空实现:内核映射由 DMW 窗口承担,无页表可建。 备选方案与否决理由: - 原地分裂 BOOT_PT:不新建整表、逐区间降粒度。否决:无法去掉恒等窗口, 且正确性依赖"先填满新层再替换指针"的顺序纪律,半成品状态难审计; - xv6 式内核/用户页表分离 + trampoline:否决:本项目不做 KPTI,且当前 无 ASID 支持,每次 trap 换表的全量 TLB 刷新代价不可接受。 ### 测试结果 - `make all` 双架构编译通过;`cargo fmt` 通过;clippy 无新增 warning (la 侧仅剩 RUSTFLAGS_LA 既有的 unstable 提示,基线原有); - rv 单核:boot ok + 内存子系统启动自检 + kthread 演示 + hello/check 全通过; - rv SMP=4 回归通过;MEM=2G 回归通过(验证逐 GiB 填表路径); - W^X 负向验证:临时写 .text 首页触发 StorePageFault @ 0xffffffc080200000, 权限拒绝生效; - loongarch64:编译通过,运行未测试。 ### 影响与风险 - 切表为单向操作:新表映射错误会导致切换后立即失去取指,无法回切。 缓解:切换前全量走表自检,失败停留在旧表上; - 表 B 的表页为启动期一次性分配、永不释放,未纳入 OwnedFrame 体系 - 内核态异常现场 dump(pc/寄存器)尚未实现,W^X 误配置的定位手段仍有限 - 借鉴声明:表项构造方式参照 polyhal-boot 0.4.0 引导页表(MIT)的 做法,思路借鉴、无代码复制; --------- Co-authored-by:
一鸣 张 <3025189089@qq.com>