armkernelandroid-kerneltrustzone

How to interpret ARM's SMC calls?


I have been reading Android's kernel to see how dynamic power management for CPU cores (aka DVFS, DCVS) is being done. The code I found here makes some calls to the following function (defined here) which in turn calls the SMC assembly instruction.

ARM has a document that explains SMC calling convention, but I haven't been able to use it to make sense of the following function. How can I track down the SMC instruction further to see what it actually does based on its input operands?

s32 scm_call_atomic4_3(u32 svc, u32 cmd, u32 arg1, u32 arg2,
        u32 arg3, u32 arg4, u32 *ret1, u32 *ret2)
{
    int ret;
    int context_id;
    register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 4);
    register u32 r1 asm("r1") = (u32)&context_id;
    register u32 r2 asm("r2") = arg1;
    register u32 r3 asm("r3") = arg2;
    register u32 r4 asm("r4") = arg3;
    register u32 r5 asm("r5") = arg4;
    asm volatile(
        __asmeq("%0", "r0")
        __asmeq("%1", "r1")
        __asmeq("%2", "r2")
        __asmeq("%3", "r0")
        __asmeq("%4", "r1")
        __asmeq("%5", "r2")
        __asmeq("%6", "r3")
#ifdef REQUIRES_SEC
            ".arch_extension sec\n"
#endif
        "smc    #0  @ switch to secure world\n"
        : "=r" (r0), "=r" (r1), "=r" (r2)
        : "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4), "r" (r5));
    ret = r0;
    if (ret1)
        *ret1 = r1;
    if (ret2)
        *ret2 = r2;
    return r0;
}
EXPORT_SYMBOL(scm_call_atomic4_3);

Solution

  • The SMC calling conventions is a suggestion from ARM on how to implement cross world API, so that multiple vendors can write code in either world and co-exist with a minimum of incompatibility. At least that is the intent. A vendor (Android/Linux in your case) does not have to do this and may not be able to do this if the secure world does not follow it.

    but I haven't been able to use it to make sense of the following function

    The SMC instruction is a controlled change from the normal world to the secure monitor. The monitor has it's own vector table and the entry for svc is the SMC call. Registers are 'shared' information between worlds. Normally on a world switch the monitor may swap out all register to some context store. In the SMC case, the register can transfer parameter and return results. This is all this function is doing.

    register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 4);
    

    This is a bit field like table 2-1 in the SMC calling conventions document. It is from the normal world Linux to the secure monitor to tell which function (and number of arguments to marshal/keep of four).

    register u32 r2 asm("r2") = arg1;
    register u32 r3 asm("r3") = arg2;
    register u32 r4 asm("r4") = arg3;
    register u32 r5 asm("r5") = arg4;
    

    If the ARM ABI is fortuitous, the parameters arg1-arg4 will already be in these register, so no code will be generated. The global context_id (to Linux/Android kernel) is also passed.

    How can I track down the SMC instruction further to see what it actually does based on its input operands?

    To do that, you need the code to the secure world. My guess is that this is some cell phone and base band, etc. are in the secure world and the code is not available. If you have the secure world code, the monitor vector table will have an SMC entry. This will have a switch (or if/then) based on the r0 or 'cmd' value which selects the particular smc function. A generic smc handler may restore secure world registers/context but keep the four arguements as specified in an r0 bit field.

    So probably the answer is you can't trace down what it does. You have to rely on the vendor documents or be lucky enough to have code to the secure world.