点击上方蓝字 江湖评谈设为关注/星标
前言
上一篇:Linux内核Arm64用户态,详细叙述了下Arm64的Linux内核调用的用户态。本篇看下Riscv64的内核态到用户态切换。
Linux-Riscv64 To User
依旧通过EA(EntryPoint Address)地址:0x10554来看下这个过程的切换
# file hello
hello: ELF 64-bit LSB executable, UCB RISC-V, RVC, double-float ABI, version 1 (SYSV), statically linked, BuildID[sha1]=549830b5e1cfea28b21ab9cbcd1955f257390d54, for GNU/Linux 4.15.0, with debug_info, not stripped
# readelf -h hello
ELF 头: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
Version: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: EXEC (可执行文件)
系统架构: RISC-V
版本: 0x1
入口点地址: 0x10554
断点和Qemu-asm如下:
(lldb) b 0x10554
Breakpoint 1: address = 0x0000000000010554
(lldb) c
Process 1 resuming
Process 1 stopped
* thread #1, stop reason = breakpoint 1.1
frame #0: 0x0000000000010554
error: memory read failed for 0x10400
//上条Qemu-asm地址
0xffffffff8099f6a2: 10200073 sret
(lldb) di -s 0xffffffff8099f6a2 -b
vmlinux`ret_from_exception:
0xffffffff8099f6a2 <+96>: 73 00 20 10 sret
0xffffffff8099f6a6: 01 00 nop
vmlinux`handle_kernel_stack_overflow:
0xffffffff8099f6a8 <+0>: f3 9f 0f 14 csrrw t6, sscratch, t6
0xffffffff8099f6ac <+4>: 83 3f 02 02 ld t6, 0x20(tp)
0xffffffff8099f6b0 <+8>: 8e 0f slli t6, t6, 0x3
0xffffffff8099f6b2 <+10>: 17 41 bb 00 auipc sp, 0xbb4
0xffffffff8099f6b6 <+14>: 13 01 61 e6 addi sp, sp, -0x19a
0xffffffff8099f6ba <+18>: 7e 91 add sp, sp, t6
0xffffffff8099f6bc <+20>: 82 6f ld t6, 0x0(sp)
0xffffffff8099f6be <+22>: 17 81 28 00 auipc sp, 0x288
STVEC保存异常或者中断陷入的入口地址)
:
(lldb) re re SEPC SSTATUS STVEC
sepc = 0x0000000000010554
sstatus = 0x0000000200004020
stvec = 0xffffffff8099f4f0 vmlinux`__kprobes_text_start
SYM_CODE_START(handle_exception)
csrrw tp, CSR_SCRATCH, tp
bnez tp, .Lsave_context
.Lsave_context:
//中级部分省略,便于观看
REG_L t1, 0(t0)2:
jalr t1 //会跳到do_page_fault函数
j ret_from_exception
do_page_fault如下:
//arch/riscv/kernel/traps.c:359:1
#ifdef CONFIG_MMU
asmlinkage __visible noinstr void do_page_fault(struct pt_regs *regs)
{
irqentry_state_t state = irqentry_enter(regs);
handle_page_fault(regs);
local_irq_disable();
irqentry_exit(regs, state);
}
#endif
(lldb) p/x regs->epc
(unsigned long) 0x0000000000010554
irqentry_exit会返回到__kprobes_text_start函数的执行点:j ret_from_exception
//
SYM_CODE_START_NOALIGN(ret_from_exception)
REG_L s0, PT_STATUS(sp)
//中省略便于观看
REG_L x1, PT_RA(sp)
REG_L x3, PT_GP(sp)
REG_L x4, PT_TP(sp)
REG_L x5, PT_T0(sp)
restore_from_x6_to_x31
REG_L x2, PT_SP(sp)
#ifdef CONFIG_RISCV_M_MODE
mret
#else
sret
#endif
通过SRET返回陷入到spec:
(lldb) re re stvec sepc sstatus
stvec = 0xffffffff8099f4f0 vmlinux`__kprobes_text_start
sepc = 0x0000000000010554
sstatus = 0x0000000200004020
此时我们真正的来到了用户态入口EA:
(lldb) si
Process 1 stopped
* thread #1, stop reason = breakpoint 1.1
frame #0: 0x0000000000010554
-> 0x10554: jal 0x10576
0x10558: mv a5, a0
0x1055a: auipc a0, 0x0
0x1055e: addi a0, a0, 0xd8
结尾
总结下,EA通过ret_from_exception陷入用户态异常,调用页面处理错误(do_page_fault),处理完成之后通过SSTATUS和STVEC设置用户态状态,然后跳转到用户态执行EA入口。
往期精彩回顾
Riscv级的指令编写
Linux内核Arm64用户态
关注公众号↑↑↑:江湖评谈