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 512 M - 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
00000000005f d46c 0000000000000000 AX 0 0 4
[ 3 ] . init. text PROGBITS ffffffff80600000 00800000
0000000000031 eaa 0000000000000000 AX 0 0 2097152
[ 4 ] . exit. text PROGBITS ffffffff80631eb0 00831 eb0
0000000000001926 0000000000000000 AX 0 0 2
[ 5 ] . init. data PROGBITS ffffffff80800000 00834000
0000000000012 c58 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 0084f 000
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& 48 bit 对应的 SATP寄存器值, 先放入a2寄存器
4. 计算 trampoline_pg_dir& 48 bit 对应的 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& 48 bit 写入 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 = > 48 bit | 810e2000 = > trampoline_pg_dir = > 0xffffffff80ee2000
0x9000000000080a06 = > kernel page tables = > 48 bit | 80 a06000 = > 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& 48 bit 开了一次, 就没关过
-- -- -- -- -
3. relocate 中 以 early_pg_dir& 48 bit 无缝切换了一次.
注意: 以后都是无缝切换pgd
4. 之后还会切换到 swapper_pg_dir& 48 bit
调试过程中的问题
现在用gdb + qemu 调试 relocate 代码 , 会根据 不同厂家释放的gdb , 有不同的效果
总而言之是 gdb的bug
2022 - 5 - 30 07 : 12 : 01