驱动的加载和初始化可以参考https://blog.csdn.net/shuaifengyun/article/details/72934531。已经做出较为详细的说明,这里对于其中一些更细节的内容做出一些记录,仅供学习参考。
文中提到了optee_probe是建立optee驱动的最后一步,其中的操作大多数型如下:
invoke_fn = get_invoke_func(np);
if (IS_ERR(invoke_fn))
return (void *)invoke_fn;
if (!optee_msg_api_uid_is_optee_api(invoke_fn)) {
pr_warn("api uid mismatch\n");
return ERR_PTR(-EINVAL);
}
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
return ERR_PTR(-EINVAL);
}
if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) {
pr_warn("capabilities mismatch\n");
return ERR_PTR(-EINVAL);
}
可以看到,首先获取到一个Invoke_fn的函数,然后后面都是调用这个函数与optee进行通信,获取到optee的系统信息,下面就对这个函数如何与optee进行通信做出说明。
Invoke_fn函数实际上获取的是optee_smccc_smc函数,函数实现如下
/*
* Wrap c macros in asm macros to delay expansion until after the
* SMCCC asm macro is expanded.
*/
/*SMCCC_SMC宏,触发smc*/
.macro SMCCC_SMC
__SMC(0)
.endm
/*SMCCC_HVC宏,触发hvc*/
.macro SMCCC_HVC
__HVC(0)
.endm
/* 定义SMCCC宏,其参数为instr */
.macro SMCCC instr
/* 将normal world中的寄存器入栈,保存现场 */
UNWIND( .fnstart)
mov r12, sp /* r12指向老的sp地址 */
push {r4-r7} /* 推r4 - r7入栈,则sp = sp - 4 * 4 */
UNWIND( .save {r4-r7})
ldm r12, {r4-r7} /* 把r12指向的内容的刷入r4 - r7,其实就是把参数a4 - a7存入r4 - r7
\instr /* 执行instr参数的内容,即执行smc切换 */
pop {r4-r7} /* 出栈操作,恢复现场 */
ldr r12, [sp, #(4 * 4)]
stm r12, {r0-r3}
bx lr
UNWIND( .fnend)
.endm
/*
* void smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
* unsigned long a3, unsigned long a4, unsigned long a5,
* unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
*/
ENTRY(arm_smccc_smc)
SMCCC SMCCC_SMC
ENDPROC(arm_smccc_smc)
由于例子中是arm32的平台,因此调用约定满足ATPCS调用约定。由于smccc_smc函数的入参有9个参数,因此在执行instr前的一些操作后,寄存器和栈空间分布如下。
res |
a7 |
a6 |
a5 |
a4 |
r7 |
r6 |
r5 |
r4 |
<- r12
<- sp
r0=a0, r1=a1, r2=a2, r3=a3, r4=a4, r5=a5, r6=a6, r7=a7