Skip to content
  • Yinary's avatar
    feat(mm): 建立内核页表,细化镜像权限并替换启动粗表 (#10) · 75af5ff2
    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: default avatar一鸣 张 <3025189089@qq.com>
    75af5ff2