当前位置: 首页 > java >正文

简单记录一下Debug的折磨历程

问题描述

书接上文,Linux内核ioremap函数问题小记,这篇里我们加载镜像文件成功并成功执行 ret 指令返回内核模块。

本文则是开始执行镜像入口函数之后,一直崩。一路 Debug 的过程记录。

先贴图:执行真正的镜像代码之后,内核直接无输出,崩掉了,没有任何打印,借助 QEMU 看到各寄存器状态如下:

在这里插入图片描述

问题很明确:有指令触发了 instruction page fault,代码进入 stvec 指向的地址执行,但该地址没有映射,不可执行。


小 插曲

原本镜像加载的虚拟地址固定为 IOREMAP 的区间中的:0xff20000000c00000。这个地址是 RISCV Ubuntu 系统在 SV57 分页模式下的高地址,读者可验证符号扩展的正确性。

但老遇到问题,时而加载不了,时而可以加载。也就是这段虚拟地址空间时而可用,时而不可用。

非常的烦人,不可用就要重启重新尝试。这个错误还是类似奇偶出现的形式,重启一次就好了,再来就不行,然后又可以这样。

遂,打印 [VMALLOC_START, VMALLOC_END] 这段区间的内容,具体如下:[VMALLOC_START = ff20000000000000, VMALLOC_END = ff60000000000000]

尝试探究偶发失败的原因,遂执行命令:cat /proc/vmallocinfo 部分截图如下所示:

在这里插入图片描述
说明从 0xff20000000c00000 开始的这段区间是内核常用分配 IOREMAP 的位置。

遂把映射地址改为:0xff30000000c00000。这个问题解决了。

原因分析:

回到原来的问题:既然镜像经常崩溃,在镜像中又无法打印,那么我们只能通过在镜像代码中加入死循环的方式来进行非常原始的单步行为。

中间繁杂的尝试、暂停、打印寄存器组的内容等等行为就不一一展示了。最终初步定位到问题如下。

在镜像入口的设置页表前的代码中加入死循环,如下:

temp_entry:j temp_entry/** 设置SATP寄存器启用页表* MODE=10 (Sv57), ASID=0, PPN=bootstrap_pt_l0的物理地址>>12*/la      x12, bootstrap_pt_l0virt2phys x12                   // 转换为物理地址srli    x12, x12, 12            // 右移12位得到PPNli      x10, 10                  // Sv57模式  slli    x10, x10, 60            // MODE字段位于[63:60]or      x12, x12, x10           // 组合MODE和PPNcsrw    satp, x12               // 写入SATP寄存器sfence.vma                      // 刷新TLBfence.i

重新加载镜像并执行,暂停 CPU 打印寄存器组的内容如下:
请添加图片描述
随后,利用 objdump 反汇编镜像文件的内容并对应,可以看到:

在这里插入图片描述
那么,在设置页表之前,是没有问题的。同样的,把死循环放在这段设置页表的代码之后,崩了。

更细致的来说,我们把死循环放在:

    csrw    satp, x12               // 写入SATP寄存器
temp_entry:j temp_entrysfence.vma                      // 刷新TLBfence.i

还是崩。

但我们把死循环换一个位置:

    or      x12, x12, x10           // 组合MODE和PPN
temp_entry:j temp_entrycsrw    satp, x12               // 写入SATP寄存器sfence.vma                      // 刷新TLBfence.i

这就不崩了。CPU info 如下所示,对应到反汇编的循环位置:

在这里插入图片描述

问题就出在页表设置的代码中,是页表内容错了


解决方案:

今天还没有解决方案,明天慢慢 Debug 补上。

昨天分析到,页表内容错了。具体 Debug 原因如下:

.macro set_block table, index, addr, lvl# RISC-V Sv57: 每级9位,第lvl级的页面大小为2^(12+(4-lvl)*9)# 地址对齐掩码计算li      t0, 9li      t1, 4addi     t1, t1, -\lvl      # t1 = 4 - lvlmul     t1, t1, t0        # t1 = (4 - lvl) * 9li      t0, 12add     t0, t0, t1        # t0 = 12 + (4 - lvl) * 9# 创建对齐掩码li      t1, 1sll     t1, t1, t0        # t1 = 1 << (12 + (4 - lvl) * 9)addi    t1, t1, -1        # t1 = (1 << (12 + (4 - lvl) * 9)) - 1not     t1, t1            # t1 = ~((1 << (12 + (4 - lvl) * 9)) - 1)and     t0, \addr, t1     # t0 = addr & 对齐掩码set_pte \table, \index, t0, PAGE_DEFAULT_FLAGS
.endm

这是原本设置大页映射的代码。这里错了,在调用 set_pte 宏的时候应当传入 PTE 的值,PTE中PPN需要左移 10bit,即便做了对齐但也不是物理地址 4k 一页的 12bit。故需要做移位。修改为如下内容:

.macro set_block table, index, addr, lvl/* -------- 1. 计算 shift = 12 + (4-lvl)*9 -------- */li      t0, 9li      t1, 4addi    t1, t1, -\lvl      /* t1 = 4-lvl              */mul     t1, t1, t0         /* t1 = (4-lvl)*9          */li      t0, 12add     t0, t0, t1         /* t0 = shift              *//* -------- 2. 用掩码把物理地址对齐到大页边界 -------- */li      t1, -1             /* 全 1                    */sll     t1, t1, t0         /* t1 = ~((1 << shift)-1)  */and     t1, \addr, t1      /* t1 = aligned phys base  *//* -------- 3. 把 PPN 移到 bit10,并加上叶子标志 -------- */srli    t1, t1, 12         /* strip page offset       */slli    t1, t1, 10         /* PPN ↦ bits 53:10        */ori     t1, t1, PAGE_DEFAULT_FLAGS/* -------- 4. 写入 table[index] -------- */la      t2, \tableslli    t0, \index, 3      /* t0 = index * 8          */add     t2, t2, t0sd      t1, 0(t2)
.endm

总结

没完结,不能撒花。

完结撒花!!!

http://www.xdnf.cn/news/15109.html

相关文章:

  • 汽车级MCU选型新方向:eVTOL垂桨控制监控芯片的替代选型技术分析
  • 巨人网络持续加强AI工业化管线,Lovart国内版有望协同互补
  • UI前端大数据可视化实战技巧:如何利用数据故事化提升用户参与度?
  • 云暴露面分析完整指南
  • Qt:布局管理器Layout
  • [Meetily后端框架] 多模型-Pydantic AI 代理-统一抽象 | SQLite管理
  • React 核心知识点速览:从基础到关键概念
  • 7.12 卷积 | 最小生成树 prim
  • 手把手一起使用Miniforge3+mamba平替Anaconda(Win10)
  • mac电脑的usr/libexec目录是干什么的?
  • 基于redis的分布式session共享管理之销毁事件不生效问题
  • JavaSE——面向对象基础
  • 分布式系统高可用性设计-负载均衡与容错机制深度解析
  • Rust基础-part3-函数
  • 【硬核】6节串联锂电池均衡系统仿真_组内双向cuk均衡_组间双向反激式变压器
  • Go 编译报错排查:vendor/golang.org/x/crypto/cryptobyte/asn1 no Go source files
  • Android原生TabLayout使用技巧
  • Telnet远程连接实验(Cisco)
  • jenkins部署springboot+Docker项目
  • 数据结构:栈、队列、链表
  • OpenCV实现感知哈希(Perceptual Hash)算法的类cv::img_hash::PHash
  • 亿级流量下的缓存架构设计:Redis+Caffeine多级缓存实战
  • C#中的设计模式:构建更加优雅的代码
  • 深入探究编程拷贝
  • 【Spring Boot】Spring Boot 4.0 的颠覆性AI特性全景解析,结合智能编码实战案例、底层架构革新及Prompt工程手册
  • Vue 表单开发优化实践:如何优雅地合并 `data()` 与 `resetForm()` 中的重复对象
  • 两台电脑通过网线直连形成局域网,共享一台wifi网络实现上网
  • 排序算法(一):冒泡排序
  • nginx 负载均衡配置(加解决重复登录问题)
  • 没有管理员权限,在服务器安装使用 Jupyter + R 内核