linux-5.17
echo "CONFIG_DEBUG_INFO=y" >> ./arch/riscv/configs/defconfig
make ARCH=riscv CROSS_COMPILE=${CROSS_COMPILE} defconfig
qemu-system-riscv64 -M virt -m 512M -kernel arch/riscv/boot/Image -nographic -S -s
riscv64-unknown-linux-gnu-gdb -x gdb_init -tui
set logging file log_gdb.txt
set logging on
set architecture riscv:rv64
target remote localhost:1234
启动过程中第一段 为 opensbi , 所以我们想单步调试,肯定少不了 opensbi
但是我不想调试opensbi
但是我又不知道 Image 被加载到了哪里.所以采取这种方式
改代码来确定 Linux 的加载地址
在 arch/riscv/kernel/head.S 40 行之后加入如下代码
47 loop_test:
48 mv x1,x1
49 mv x2,x2
50 j loop_test
通过c ,ctrl-c 来 确定 pc 的范围, 最小的地址就是 arch/riscv/kernel/head.S __HEAD _start 的地址
通过实践,有三个pc , 0x80200000 , 0x80200002 0x80200004
然后计算符号偏差,然后加载符号,然后修改pc
计算偏差
ffffffff80000000 => 0x80200000
$ riscv64-unknown-linux-gnu-readelf -S vmlinux
There are 39 section headers, starting at offset 0x9f3f900:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .head.text PROGBITS ffffffff80000000 00200000
0000000000001e86 0000000000000000 AX 0 0 4096
[ 2] .text PROGBITS ffffffff80002000 00202000
00000000005fd46c 0000000000000000 AX 0 0 4
[ 3] .init.text PROGBITS ffffffff80600000 00800000
0000000000031eaa 0000000000000000 AX 0 0 2097152
[ 4] .exit.text PROGBITS ffffffff80631eb0 00831eb0
0000000000001926 0000000000000000 AX 0 0 2
[ 5] .init.data PROGBITS ffffffff80800000 00834000
0000000000012c58 0000000000000000 WA 0 0 4096
[ 6] .data..percpu PROGBITS ffffffff80813000 00847000
0000000000007868 0000000000000000 WA 0 0 64
[ 7] .alternative PROGBITS ffffffff8081a868 0084e868
0000000000000240 0000000000000000 A 0 0 1
[ 8] .rodata PROGBITS ffffffff80a00000 0084f000
0000000000183278 0000000000000000 WA 0 0 64
add-symbol-file vmlinux -s .text 0x80202000 -s .head.text 0x80200000 -s .rodata 0x80c00000 -s .init.text 0x80800000 -s .init.data 0x80a00000
print $pc=0x80200006
单步到 call relocate , relocate 里面 最后一部分代码 就 出错了,注意: 不是失去符号信息
但是 我用 add-symbol-file vmlinux 最早可以断在 call relocate 的下一句
call setup_trap_vector , 也可以断在 start_kernel
rv64-linux boot 过程,如果要用gdb调试,需要换多少次符号信息?
1. Image 过程中
1. gdb attach 上去 , 需要加载一次 symbol
2. 由于开mmu , 需要更新一次
setup_vm 和 relocate 的运行过程
https:
https:
relocate
函数的参数
个数:1个
含义:early_pg_dir的物理地址
值 :0x80a06000
1.根据_start的虚拟地址(通过kernel_map算出)和物理地址(通过_start),算出 ra寄存器中的物理地址 对应的虚拟地址,写入ra
2.计算 标号1的虚拟地址(0xffffffff 80001044),写入 CSR_TVEC
3.计算 early_pg_dir&48bit 对应的 SATP寄存器值,先放入a2寄存器
4.计算 trampoline_pg_dir&48bit 对应的 SATP寄存器值,写入CSR_SATP寄存器
5.标号1的物理地址是0x80201044,标号1的虚拟地址是CSR_TVEC的值
6.以该物理地址来取指"标号1的指令",异常
7.PC跳转到"CSR_TVEC的值",以标号1的虚拟地址来取指"标号1的指令",正常,往下执行
8.将 .Lsecondary_park 地址(虚拟地址)写入 CSR_TVEC
9.将 early_pg_dir&48bit 写入 CSR_SATP
10. ret 指令, 会返回到 ra ,之前 ra 已经是 虚拟地址的值了(参考过程1),所以会跳到 "call relocate" 下一句
setup_vm 做了什么
1.计算 kernel_map 成员,memory_limit,riscv_pfn_base
2.检测SATP_MODE_48是否支持
3.设置 页表alloc函数
4.固定页表early_pg_dir->fixmap_pud->fixmap_pmd 映射 FIXADDR_START -> fixmap_pte
5.固定页表trampoline_pg_dir->trampoline_pud->trampoline_pmd 映射 kernel_map.virt_addr -> kernel_map.phys_addr
6.非固定页表early_pg_dir->early_pud->early_pmd 映射 kernel_map.virt_addr->kernel_map.phys_addr
7.固定页表early_pg_dir->early_dtb_pud->early_dtb_pmd 映射 DTB_EARLY_BASE_VA->dtb_pa
8.重新设置 页表alloc函数
过程1的结果
kernel_map = {
page_offset = 0xffffaf8000000000,
virt_addr = 0xffffffff80000000,
phys_addr = 0x80200000,
size = 0xf2d000,
va_pa_offset = 0xffffaf7f7fe00000,
va_kernel_pa_offset = 0xfffffffeffe00000,
va_kernel_xip_pa_offset = 0x0
}
0x90000000000810e2 => trampoline page tables => 48bit | 810e2000 => trampoline_pg_dir =>0xffffffff80ee2000
0x9000000000080a06 => kernel page tables => 48bit | 80a06000 => early_pg_dir =>0xffffffff80806000
思考
- trampoline_pg_dir 有没有必要存在
https:
The trampoline_pg_dir is to handle the case when RAM is
large enough such that RAM physical address range overlaps
kernel virtual address range (i.e. VA >= PAGE_OFFSET). This
is overlap of virtual address range and physical address range
can be problematic for low-level code which is trying to enable
MMU (such as the relocate() function).
trampoline_pg_dir 用于处理RAM足够大的情况,以便RAM物理地址范围与内核虚拟地址范围重叠(即VA>=页偏移量)。
虚拟地址范围和物理地址范围的重叠,对于试图启用MMU的低级代码(如relocate()函数)可能会有问题。
Here's a old kernel thread which tries to summarize this:
https:
在其他架构中 , 都是叫 swapper_pg_dir .
而在riscv 临时内核页表中,swapper_pg_dir 在临时页表中存在过,后来改名了early_pg_dir
5.2.21 trampoline_pg_dir + swapper_pg_dir
5.3-rc1 trampoline_pg_dir + early_pg_dir
两次
1. set_satp_mode 中检测 SATP_MODE_48 是否支持 开关了一次
2. relocate 中 以 trampoline_pg_dir&48bit 开了一次,就没关过
---------
3. relocate 中 以 early_pg_dir&48bit 无缝切换了一次.
注意:以后都是无缝切换pgd
4. 之后还会切换到 swapper_pg_dir&48bit
调试过程中的问题
现在用gdb + qemu 调试 relocate 代码 , 会根据 不同厂家释放的gdb ,有不同的效果
总而言之是 gdb的bug
2022-5-30 07:12:01