其他分享
首页 > 其他分享> > OP-TEE学习记录(1)

OP-TEE学习记录(1)

作者:互联网

OP-TEE文件结构

启动过程理解

Qemu 模拟计算机加电启动后的运行流程可以分成若干个阶段,每个阶段均由一层软件负责,每一层软件的功能是进行它应当承担的初始化工作,并在此之后跳转到下一层软件的入口地址,也就是将计算机的控制权移交给了下一层软件。第一个阶段由固化在 Qemu 内的一小段汇编程序负责。在optee的makefile文件中将-bios 指定bl1.bin,因此第二阶段会跳转到ATF中的bl1中执行。设置

此处讲一下我对启动流程的基本理解。我认为不管是启动固件,内核,还是应用程序,他们的加载流程都很类似,他们本质上都是实现特定功能的代码,然后通过具体的规则进行编译和链接得到镜像文件(应用程序可能是ELF格式的文件),这些文件通常能给我们提供一些信息,例如入口地址(ELF文件还可以获得每段起始地址,大小等信息)。然后当将这些文件中加载到固定地址时,我们同时设置特定寄存器的值,例如将指令寄存器设置为入口地址的值,然后程序就可以自动运行了。同时,运行环境,硬件初始化等操作应该都是设置相关寄存器的值使得程序能正常运行,不同硬件架构有不同的要求。

OP-TEE软件框架理解设置

CA使用libteec库中提供的接口来实现对TEE侧TA中具体命令的调用。libteec库都在optee_client/libteec目录下。

tee_supplicant作为守护进程,在linux启动时会被作为后台程序启动,然后进入一个无限循环,监控、处理、回复TEE侧的RPC请求。

OP-TEE驱动是REE侧和TEE侧之间进行数据交互的桥梁。tee_supplicant和libteec库中提供的接口都会通过系统调用的方式陷入Linux内核空间,然后Linux内核根据传递的参数找到OP-TEE驱动,并命中驱动中的operation结构体中具体处理函数来完成实际操作。对于OP-TEE驱动,一般所会触发安全监控模式(SMC),并将参数带入Monitor模式或者EL3,在Monitor中执行正常世界状态与安全世界状态的切换,待状态切换完成后,会将驱动段带入的参数传递给OP-TEE中的线程进行进一步处理。

系统调用和SMC理解

在此,我简单说明一下我对系统调用的理解因为不同的体系结构可能存在不同的设置,并且认为SMC的原理可由系统调用类比理解。

为确保操作系统的安全,对应用程处理处理序而言,需要限制的主要有两个方面:

假设有了这样的限制,我们还需要确保应用程序能够得到操作系统的服务,即应用程序和操作系统还需要有交互的手段。使得低特权级软件只能做高特权级软件允许它做的,且超出低特权级软件能力的功能必须寻求高特权级软件的帮助。这样,高特权级软件(操作系统)就成为低特权级软件(一般应用)的软件执行环境的重要组成部分。

为了实现这样的特权级机制,需要进行软硬件协同设计。一个比较简洁的方法就是,处理器设置两个不同安全等级的执行环境:用户态特权级的执行环境和内核态特权级的执行环境。且明确指出可能破坏计算机系统的内核态特权级指令子集,规定内核态特权级指令子集中的指令只能在内核态特权级的执行环境中执行。处理器在执行指令前会进行特权级安全检查,如果在用户态执行环境中执行这些内核态特权级指令,会产生异常。

为了让应用程序获得操作系统的函数服务,采用传统的函数调用方式(即通常的 callret 指令或指令组合)将会直接绕过硬件的特权级保护检查。所以可以设计新和来咯的机器指令:执行环境调用(Execution Environment Call,简称 ecall )和执行环境返回(Execution Environment Return,简称 eret )):

硬件具有了这样的机制后,还需要操作系统的配合才能最终完成对操作系统自身的保护。首先,操作系统需要提供相应的功能代码,能在执行 eret 前准备和恢复用户态执行应用程序的上下文。其次,在应用程序调用 ecall 指令后,能够检查应用程序的系统调用参数,确保参数不会破坏操作系统。

那系统调用的具体实现方式是什么呢?我举个例子,在 RISC-V 调用规范中,约定寄存器 a0~a6 保存系统调用的参数, a0 保存系统调用的返回值。有些许不同的是寄存器 a7 用来传递 syscall ID,这是因为所有的 syscall 都是通过 ecall 指令触发的,除了各输入参数之外我们还额外需要一个寄存器来保存要请求哪个系统调用。示例代码如下。

llvm_asm!("ecall"
            : "={x10}" (ret)
            : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
            : "memory"
            : "volatile"
        );

当 CPU 执行完一条指令(如 ecall )并准备从用户特权级 陷入( Trap )到 S 特权级的时候,硬件会自动完成如下这些事情:

这个trap的入口地址需要我们指定,可以用一段汇编完成,主要的功能就是保存上下文信息,然后跳转到一个异常处理的函数handler, handler一般通过x17寄存器的值得到系统调用号,做相应的处理。

CA调用流程

具体分析案例是optee_example中的hello_world.

可以看出主要调用流程为在client端的open和ioctl函数,这两个函数会陷入Linux内核空间调用具体tee驱动的相应函数:tee_open和tee_ioctl。在tee_ioctl中通过参数cmd来进行命令的switch从而调用相应的处理函数tee_ioctl_XXX,比如例中的tee_ioctl_version。在相应的处理函数tee_ioctl_XXX中会调用optee设备的相应函数,比如例中的optee_get_version来进行具体的操作,在例中为初始化ctx上下文中参数。

标签:调用,记录,特权,tee,TEE,内核,OP
来源: https://www.cnblogs.com/ppan-y/p/16381354.html