0%

问题:
在 guest shell 中发起 reboot, guest os 完成关机后挂死.
guest os 无 log 输出, kvm 无异常 log.

1
2
3
4
5
6
7
8
9
The system is going down NOW!
Sent SIGTERM to all processes
logout
Sent SIGKILL to all processes
Requesting system reboot




从现象上看, 首先需要查 vcpu 在 kvm 中的调度是否正常.
需要的关键信息即 vcpu 的信息.

Guest 运行情况

通过 vcpu->arch. Guest_csr 和 vcpu->arch. Guest_context 可以得到 guest 陷入到 kvm 前的 csr 和通用寄存器状态.

1
2
3
4
5
6
7
8
9
10
11
12
guest_csr = {
vsstatus = 0x200000020,
vsie = 0x222,
vstvec = 0xffffffff800031f4,
vsscratch = 0x0,
vsepc = 0xffffffff805b107a,
vscause = 0x8000000000000005,
vstval = 0x0,
hvip = 0x0,
vsatp = 0xa000100000082924,
scounteren = 0x7
},

如果 host kernel 和 guest kernel 使用的同一份 Image, 其虚拟地址映射的符号应该是一致的, 因为 kernel va 的映射起始地址是一样的.
如果 host kernel 和 guest kernel 使用的 Image 不同, 可以查看 qemu 映射的 memory-layout

Qemu 命令行 ctrl+a+c, 切换到 qemu 命令行

1
2
# info mtree
0000000080000000-0000000087ffffff (prio 0, ram): riscv_virt_board.ram "GPA 地址"

Kernel 的 log

阅读全文 »

编译内核子模块

submodule

1
2
make SUBDIRS=./kmodules/01_oops O=virt_build modules "5.3之前的kernel版本"
make M=./kmodules/01_oops O=virt_build modules "之后的版本"

编写一个简单的可以导致 load page fault 的内核模块触发内核 panic.

Kernel sysrq

Documentation/translations/zh_CN/admin-guide/sysrq.rst

启用 CONFIG_MAGIC_SYSRQ

1
echo c > /proc/sysrq-trigger "产生panic"

配置 crashdump

kexec-tools

阅读全文 »

sv39

satp: 0x8000000000087fff
va: 0x80000fac

求 pa

sv39
38…30 9bit
29…21 9bit
20…12 9bit
11…0 12bit

64bit 中一个页表项占 64bit,一个 double word

  • satp.PPN 给出了一级页表的基址, VA[38:30]给出了一级页号, 因此处理器会读取位于地址(satp.PPN × 4096 + VA[38:30] × 8)的页目录项 (此处的 8 为 8字节,即一个页表项占 64bit)
  • 该 PTE 包含二级页表的基址, VA[29:21]给出了二级页号, 因此处理器读取位于地址(PTE.PPN × 4096 + VA[29:21] × 8)的页目录项
  • 该 PTE 包含了三级页表的基址, VA[20:12] 给出了三级页号, 处理器读取位于地址(PTE.PPN × 4096 + VA[20:12] × 8)的页目录项
  • 该页表项的 PTE. PPN 就是物理地址对应的 PPN * 4096 + offset 得到物理地址

va: 0x80000fac

1
2
3
4
5
bit:   1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 
index: 31| 30| 29| 28| 27| 26| 25| 24| 23| 22| 21| 20| 19| 18| 17| 16|
----------------------------------------------------------------------
bit: 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 |
index: 15| 14| 13| 12| 11| 10| 09| 08| 07| 06| 05| 04| 03| 02| 01| 00|

satp.ppn = 0x87fff << 12 = 0x87fff000

阅读全文 »

调试riscv kernel

kernel 版本 5.19.16

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export ARCH=riscv
export CROSS_COMPILE=...bin/...gnu-
make defconfig O=build
make menuconfig O=build

Virtualization -->
<*> Kernel-based Virtual Machine (KVM) Support
Kernel hacking -->
-*- Kernel debugging
Compile-time checks and compiler options -->
Debug information (Generate DWARF Version 4 debuginfo) -->
[*] Compile the kernel with debug info
[*] Provide GDB scripts for kernel debugging
Kernel Features -->
[] Randomize the address of the kernel image "关闭kernel 地址随机化"

debug qemu

#qemu_debug
buildroot 中的qemu 编译时会产生build.ninja文件, 修改build.ninja文件
将全部的-Os -g0 修改为 -O1 -gdwarf-2-O0 -gdwarf-2 即可.

KVM 初始化

riscv kvm_init 的流程
kvm 主要的代码 在 virt/kvm
arch相关的驱动代码在 arch/riscv/kvm
这个部分涉及到 kvm.ko 加载的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
-+ module_init(riscv_kvm_init);
\ -+ kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE);
\ -+ kvm_arch_init(NULL); "riscv specific"
\ - check riscv_isa_extension_available(NULL, h) "必须打开h-extension"
| - check not sbi_spec_is_0_1() "sbi 版本必须是0.2以上"
| - check sbi_probe_extension(SBI_EXT_RFENCE) "sbi必须有RFENCE extension 支持"
| -+ kvm_riscv_gstage_mode_detect(); "g-stage 页表是用 sv57x4 还是 sv48x4, sv57 模式使用5级页表, sv48使用4级页表"
\ - gstage_mode = (HGATP_MODE_SV48X4 << HGATP_MODE_SHIFT); or SV57X4
| - gstage_pgd_levels = 4; or 5 "四级页表或五级页表"
| - kvm_riscv_local_hfence_gvma_all(); "HFENCE.GVMA 0 0 汇编指令
刷新 tlb 中 G-stage 和 VS-stage 且对最终的HPA 设置过PMP的cache entry
flush all G-stage or VS-stage address-translation cache entries that
have cached PMP settings corresponding to the final translated supervisor physical address."
| - kvm_riscv_gstage_vmid_detect(); "刷新guest tlb, 检查hgatp 寄存器的vmid bits 是否够用"
| - kvm_irqfd_init() "创建工作队列, 用于处理VM的shutdown操作"
| -+ cpuhp_setup_state_nocalls(CPUHP_AP_KVM_STARTING, "kvm/cpu:starting", kvm_starting_cpu, kvm_dying_cpu);
"设置物理cpu 热插拔时的回调函数"
\ - kvm_starting_cpu(unsigned int cpu) "startup 回调函数"
| - kvm_dying_cpu(unsigned int cpu) "teardown 回调函数"
| -+ register_reboot_notifier(&kvm_reboot_notifier); "注册重启时的回调函数"
\ - blocking_notifier_chain_register(&reboot_notifier_list, &kvm_reboot_notifier);
| -+ kvm_reboot_notifier.notifier_call = kvm_reboot "回调函数为 kvm_reboot"
\ - on_each_cpu(hardware_disable_nolock, NULL, 1);
| -+ hardware_disable_nolock(void *junk)
\ -+ kvm_arch_hardware_disable()
\ - csr_write(CSR_HEDELEG, 0);
| - csr_write(CSR_HIDELEG, 0);
| - csr_write(CSR_VSIE, 0);
| - csr_write(CSR_HVIP, 0);
| - kvm_vcpu_cache = kmem_cache_create_usercopy("kvm_vcpu", vcpu_size, vcpu_align, ...)
"创建用于分配kvm_vcpu的slab 高速缓存"
| - kvm_async_pf_init() "创建用于分配kvm_async_pf的slab 高速缓存"
| - misc_register(&kvm_dev); "注册misc字符设备 /dev/kvm 节点"
| -+ register_syscore_ops(&kvm_syscore_ops); "注册suspend/resume 时的回调, 与系统休眠唤醒有关"
\ -|+ kvm_suspend "休眠时挂起VM"
\ - hardware_disable_nolock(NULL); "前面已经讲了"
| -|+ kvm_resume "唤醒时恢复VM"
\ -+ hardware_enable_nolock(NULL)
\ - csr_write(CSR_HEDELEG, EXC_INST_MISALIGNED | EXC_BREAKPOINT | EXC_SYSCALL |
EXC_INST_PAGE_FAULT | EXC_LOAD_PAGE_FAULT | EXC_STORE_PAGE_FAULT);
"riscv 设置HEDELEG 虚拟异常代理"
| - csr_write(CSR_HIDELEG, IRQ_VS_SOFT | IRQ_VS_TIMER | IRQ_VS_EXT);
"riscv 设置 hideleg 虚拟中断代理"
| - csr_write(CSR_HVIP, 0);
| - kvm_vfio_ops_init(); "注册vfio 操作, riscv not implement"

QEMU-KVM 框架

阅读全文 »

KVM

KVM(Kernel-based Virtual Machine,基于内核的虚拟机)是一种用于 Linux 内核中的虚拟化基础设施。本质是一个嵌入到 Linux 内核中的虚拟化功能模块 kvm.ko,该模块在利用 Linux 内核所提供的部分操作系统能力(e.g. 任务调度、内存管理、硬件设备交互)的基础上,再加入了处理器和内存虚拟化的能力,使得 Linux 内核具备了成为 VMM 的条件。

KVM 内核模块本身只能提供 CPU 和内存的虚拟化
KVM 需要硬件虚拟化技术支持(Intel VT-x, AMD svm, ARM hypervisor, RISCV H-extension),所以 KVM 也被称之为硬件辅助的虚拟化实现。

严格来说 kvm 属于硬件辅助的全虚拟化 + type2 类虚拟化技术.

KVM 的功能清单:

  • 支持 CPU 和 Memory 超分(Overcommit)
  • 支持半虚拟化 I/O(virtio)
  • 支持热插拔 (CPU、块设备、网络设备等)
  • 支持 SMP(Symmetric Multi-Processing,对称多处理)处理器架构
  • 支持 NUMA (Non-Uniform Memory Access,非一致存储访问)处理器架构
  • 支持实时迁移(Live Migration)
  • 支持 PCI 设备直接分配(Pass-through)和单根 I/O 虚拟化 (SR-IOV)
  • 支持合并相同内存页 (KSM )

KVM 内核模块加载流程

当启动 Linux 操作系统并加载 KVM 内核模块时:

  • 初始化 KVM 模块内部的数据结构
  • KVM 模块检测当前的 CPU 体系结构, 打开虚拟化模式开关
  • KVM 模块创建特殊的接口设备文件 /dev/kvm 并等待来自用户空间(QEMU)的指令。

KVM 是运行在内核态的且本身不能进行任何io设备的模拟。所以,KVM 还必须借助于一个运行在用户态的应用程序来模拟出虚拟机所需要的虚拟设备(e.g. 网卡、显卡、存储控制器和硬盘)同时为用户提供操作入口。目前这个应用程序的最佳选择就是 QEMU。

阅读全文 »

问题背景

在使用最新版本 qemu (8.0) 搭配 linux kernel (5.19.16) 模拟出的 riscv 主机上, 使用 riscv 版本的 qemu 跑 kvm-mode 的 guest kernel 无法执行.
现象为没有 host kernel 的 log, 没有 guest kernel 的 log 输出.

问题初步分析

在 kvm_riscv_vcpu_enter_exit 处下断点看下 vcpu 的调度情况
发现 vcpu 调度是正常的

但在 kvm_riscv_vcpu_exit 处打 log 发现每次 guest 退出的原因都是因为
sbi 设置 timecmp

通过 qemu 的 command dump-guest-memory 命令导出 guest 的 coredump
guest 停在了通过 sbi 设置 timecmp 的地方.

根据这个现象, guest kernel 一直在处理 timer tick, 却不能走其他的流程, 对比正常的 log, 启动过程中从 guest 退出到 kvm 的原因是多种多样的, 所以可以首先怀疑是 guest timer tick 出了问题.

对比最新版本 qemu (8.0) 搭配 linux kernel (主线 6.x) 版本, 可以正常运行 guest.
对比 linux kernel kvm 中对 guest timer tick 的处理, 发现加入了 sstc 的 feature.

初步验证

sstc 可以作为第一个疑点, 需要首先排除下是否是这个 feature 导致的.

阅读全文 »

内存虚拟化

涉及到两个主题:

  1. GPA->HVA
  2. HVA->HPA

略过 GVA->GPA, 这个是 guest os 中的 mmu 地址翻译过程.

GPA->HVA

KVM-Qemu 方案中,GPA->HVA 的转换,是通过 ioctl 中的 KVM_SET_USER_MEMORY_REGION 命令来实现的.

数据结构

阅读全文 »

PMP

RISC-V架构提供了一种PMP物理内存保护机制,用于隔离M模式与S/U模式下的内存访问。只有M模式才有权限配置PMP。

PMP包含几组(通常是8到16个)地址寄存器以及相应的配置寄存器,这些配置寄存器可以授予或拒绝S/U模式的读、写和执行权限。

PMP同时也能保护内存映射I/O (MMIO),M模式可信固件可以通过配置PMP来约束处理器对外设I/O的访问。

配置cpu csr
物理内存保护设置寄存器(PMPCFG)
物理内存保护地址寄存器(PMPADDR)
PMP 共实现了 8 个地址寄存器 pmpaddr0-pmpaddr7,存放表项的物理地址

缺点:

仅适用于单一核心, 无法与其他核心同步, 需要添加同步控制.
只能够对内存进行保护

阅读全文 »

首先 probe 失败的问题

需要先添加 iommu capable 函数, 表明 IOMMU_CAP_CACHE_COHERENCY 能力为 true, 否则 probe 时会失败.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static struct iommu_ops xt_iommu_ops = {
.capable = xt_iommu_capable,
}
static bool xt_iommu_capable(struct device *dev, enum iommu_cap cap)
{
switch (cap) {
case IOMMU_CAP_CACHE_COHERENCY:
return true;
case IOMMU_CAP_NOEXEC:
return true;
default:
return false;
}
}

打开 option allow_unsafe_interrupts=1
编译为动态模块时, insmod 会读取 /etc/modprobe.d/iommu_unsafe_interrupts.conf 文件, 将 allow_unsafe_interrupts 设置为 1.

1
options vfio_iommu_type1 allow_unsafe_interrupts=1

如果不是动态模块, 包含在 kernel Image 中, 则需要强制打开

1
static bool allow_unsafe_interrupts = 1;

其次 xt_iommu 在 base 版本上使用的一些接口在升级版本上发生了变化:
iommu_device_set_ops iommu_device_set_fwnode bus_set_iommu 不见了, 取而代之的是总结成了一个接口 iommu_device_register, 该接口支持的参数也有变化.
其次某些函数的参数个数意义变化:
xt_iommu_map xt_iommu_unmap , size 变成了 pgsize 和 pgcount.

需要相应的适配修改.

kernel 的相关 config 需要打开:

阅读全文 »

sm_init 调用栈

1
2
3
4
5
6
7
8
#0  sm_init (cold_boot=cold_boot@entry=1) at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/lib/sbi/optee/sm.c:120
#1 0x00000000a00026aa in nuclei_final_init (cold_boot=<optimized out>)
at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/platform/nuclei/evalsoc/platform.c:66
#2 0x00000000a0000c18 in sbi_platform_final_init (cold_boot=1, plat=0xa00138a8 <platform>)
at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/include/sbi/sbi_platform.h:401
#3 init_coldboot (hartid=0, scratch=0xa002d000) at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/lib/sbi/sbi_init.c:295
#4 sbi_init (scratch=0xa002d000) at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/lib/sbi/sbi_init.c:425
#5 0x00000000a00004b6 in _start_warm () at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/firmware/fw_base.S:443

重点关注 sm_init 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
-+ void sm_init(bool cold_boot)
\ -|+ if cold_boot
\ -+ sbi_ecall_register_extension(&ecall_optee);
\ - .extid_start = SBI_EXT_OPTEE "0x4F505445"
| - .handle = sbi_ecall_optee_handler, "注册 optee ext handler"
| - sm_region_id = smm_init(); "添加 opensbi text 段 pmp entry, 并保存 id为 sm_region_id"
| - os_region_id = osm_init(); "添加 0xfffffffff bottom entry"
| - tee_region_id = teem_init(); "添加 FW_OPTEE_TZDRAM_BASE optee_os 的text 内存 pmp entry"
| - shm_region_id = shm_init(); "添加 share memory (secure与non sec 的共享内存的) pmp entry"
| - plicm_region_id = plicm_init(); "添加 plic 的 mmio的 pmp entry"
| - timer_region_id = timerm_init(); "添加 timer 的 mmio的pmp entry"
"没有判断 cold_boot 说明是所有核都会走"
| -+ pmp_init() "将所有可用的 pmpaddr pmpcfg 置0"
| -+ pmp_set_keystone(sm_region_id, PMP_NO_PERM); "设置 opensbi text 段不可读,这个是对 S-mode 有效,对 M-mode 不起作用"
| -+ pmp_set_keystone(os_region_id, PMP_ALL_PERM); "设置优先级最低的 osm entry 0xfffffffff 保底读写执行权限,排在它前面的 entry 如果设置了不一样的权限, 以前面的 entry 设置的为准"
| -+ pmp_set_keystone(tee_region_id, PMP_NO_PERM); "设置 optee text 无权限"
| -|+ if cold_boot
\ -+ opteed_init()
\ - sbi_memset(psci_ns_context, 0, sizeof(psci_ns_context)); "normal world context 初始化为 0"
| - sbi_memset(opteed_sp_context, 0, sizeof(opteed_sp_context)); "secure world context 初始化为 0"
| -+ img_entry_point.sec_attr = SECURE; "保存状态, 后面会用. 该结构体最终会被放到 当前 cpu context 中"
| -+ img_entry_point.pc = OPTEE_OS_LOAD_ADDR; "optee 的 text 段起始地址"
| - img_entry_point.arg0 = current_hartid(); "cpu core id"
| - img_entry_point.arg1 = 8*1024*1024;
| - img_entry_point.arg2 = FW_JUMP_FDT_ADDR; "fdt 的地址,optee 和 opensbi 共用一个 fdt"
| -+ cm_init_my_context(&img_entry_point);
\ -+ cm_init_context_by_index(current_hartid(), &img_entry_point);
\ -+ ctx = cm_get_context_by_index(cpu_idx, &img_entry_point->sec_attr); "根据 cpu id 和 sec_attr 属性找到 context 指针"
\ - if secure? &opteed_sp_context[cpu_idx].cpu_ctx : &psci_ns_context[cpu_idx];
"secure 和 non sec 分别有一个context 数组, 按cpuid 保存对应的 当前cpu的context"
| -+ cm_setup_context(ctx, ep); "填 context "
\ - ctx->sec_attr = (ep->sec_attr == SECURE) ? SECURE : NON_SECURE;
| - ctx->gp_regs.mepc = ep->pc;
| - ctx->gp_regs.mstatus = (1 << MSTATUS_MPP_SHIFT); "S-mode"
| - ctx->gp_regs.a0 = ep->arg0;
| - ctx->gp_regs.a1 = ep->arg1;
| - ctx->gp_regs.a2 = ep->arg2;
| - ctx->gp_regs.... 直到 arg7
| -|+ if ep->sec_attr == SECURE
\ - ctx->s_csrs.sie = 0; "初始 sie 为0, S-mode 中断关闭"
| - saved_mie = csr_read(CSR_MIE); "保存中断使能状态"
| - csr_write(CSR_MIE, 0); "关闭所有中断"
| - rc = opteed_synchronous_sp_entry(optee_ctx); "进入 sec world optee_os 的准备工作, 及要进入 optee_os, 下面拆解"
-----------------------------------------------------------------------------------------------------
"回到 M-mode的 opensbi"
-----------------------------------------------------------------------------------------------------
| -+ ... sbi_ecall_optee_handler 处理流程 -->1
| - csr_write(CSR_MIE, saved_mie); "恢复中断状态"
| - sbi_memset(&img_entry_point, 0, sizeof(entry_point_info_t));
| - img_entry_point.sec_attr = NON_SECURE; "下一个 image 为 non sec 的 u-boot"
| - cm_init_my_context(&img_entry_point); "初始化 non sec 的 context"
| - cm_set_next_eret_context(NON_SECURE);

下面对 opteed_synchronous_sp_entry 进行拆解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-+ uint64_t opteed_synchronous_sp_entry(optee_context_t *optee_ctx)
\ -+ cm_sysregs_context_restore(SECURE); "从context 中恢复 s 开头的 csr, 这里主要是 sepc sie "
| -+ cm_set_next_eret_context(SECURE);
\ -+ ctx = cm_get_context(security_state); "拿 当前 cpu的 context 指针"
| -+ cm_set_next_context(ctx);
\ - next_cpu_context_ptr[current_hartid()] = context; "设置 next_cpu_context_ptr 全局变量, 保存当前cpu的下一个 context"
| -|+ if secure_state == SECURE ?
\ -+ switch_plic_int_enable_mode(SECURE); "设置中断使能状态为 secure_state 方案"
| -+ switch_vector_sec();
\ - csr_write(CSR_MTVEC, &_trap_handler_sec); "设置 secure 方案的 trap handler"
| - osm_pmp_set(PMP_NO_PERM); "bottom pmp entry 设置所有内存无权限"
| - shm_pmp_set(PMP_ALL_PERM); "sec 与 non sec 的 share memory 设置 all 权限"
| - plicm_pmp_set(PMP_ALL_PERM);
| - timerm_pmp_set(PMP_ALL_PERM);
| - teem_pmp_set(PMP_ALL_PERM); "设置 tee 的 text data bss 等的 all 权限"
| -+ rc = opteed_enter_sp(&optee_ctx->c_rt_ctx); "进入汇编函数, 保存当前 opensbi的 cpu context,
恢复 context 中保存的上下文, 即为 optee_os 准备的初始的 cpu context"
\ - REG_L s1, SBI_TRAP_REGS_OFFSET(mepc)(s0)
| - csrw CSR_MEPC, s1
| - REG_L s1, SBI_TRAP_REGS_OFFSET(mstatus)(s0)
| - csrw CSR_MSTATUS, s1
| - mret "进入到 optee_os, M-mode 切换为 S-mode"

–>1 optee_os 初始化完毕后, 会发 ecall 调用, 调用号 SBI_EXT_OPTEE “0x4F505445” 回到 opensbi 中, opensbi 由前面设置的 _trap_handler_sec 处理, 进而由
opensbi sbi_ecall_optee_handler 处理该 ecall 调用,其中 funcid 为 TEESMC_OPTEED_RETURN_ENTRY_DONE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-+ static int sbi_ecall_optee_handler(unsigned long extid, unsigned long funcid,
const struct sbi_trap_regs *regs,
unsigned long *out_val,
struct sbi_trap_info *out_trap)
\ -+ switch (funcid)
\ -+ case TEESMC_OPTEED_RETURN_ENTRY_DONE
\ -+ cm_gpregs_context_save(SECURE, regs); "保存 optee_os 的上下文到 context 中"
\ - ctx = cm_get_context(security_state);
| - sbi_memcpy(&ctx->gp_regs, trap_reg, sizeof(struct sbi_trap_regs)); "regs 已经由 _trap_handler_sec 保存下来了, 这里转移到 context 下"
| -+ cm_sysregs_context_save(SECURE); "保存 s 开头的 csr 到 context 下, 这里不用参数, 因为 M-mode 基本上不会改动 S-mode 的寄存器, 直接读即可"
| - optee_vector_table = (optee_vectors_t *) regs->a1; "regs->a1锚点 optee_os的 thread_vector_table 基址在 ecall 时会保存到 a1 寄存器中, 进而转移到这里"
| - if optee_vector_table ? set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON) "optee_os的 power state, 这里on 表示正在运行"
| -+ opteed_synchronous_sp_exit(optee_ctx, regs->a1); "回到 进入 optee_os 时的地方"
\ -+ cm_sysregs_context_save(SECURE);
\ -+ opteed_exit_sp(optee_ctx->c_rt_ctx, ret);
\ - REG_L s0, (12-13) * __SIZEOF_POINTER__(sp)
| - ... "恢复 callee 即 s0-s11 寄存器"
| - REG_L ra, (0-13) * __SIZEOF_POINTER__(sp) "恢复 ra"
| - ret "回到 进入 optee_os 时的地方 接着往下执行"

optee_os 启动流程

上面讲到在调用 opteed_synchronous_sp_entry->opteed_enter_sp mret 后进入了 optee_os 中
下面就讲下 optee_os 的启动流程.

阅读全文 »