1. 程式人生 > >OPTEE學習筆記 - CA呼叫TA流程

OPTEE學習筆記 - CA呼叫TA流程

今天開始學習optee相關的程式碼,目的是弄清optee的執行邏輯,學習optee。以下是雜記,僅用做學習記錄。學習程式碼均取自github和《手機安全和可信應用開發指南》(帥峰雲,黃騰,宋洋)

學習程式碼示例:

void g_CryptoVerifyCa_Helloworld(void)
{
    TEEC_Session   l_session;    /* Define the session of TA&CA */
    TEEC_Operation l_operation;  /* Define the operation for communicating between TA&CA */
    int l_RetVal = FAIL;       /* Define the return value of function */

    /**1) Initialize this task */
    l_RetVal = l_CryptoVerifyCa_TaskInit();
    if(FAIL == l_RetVal)
    {
        goto cleanup_1;
    }

    /**2) Open session */
    l_RetVal = l_CryptoVerifyCa_OpenSession(&l_session);
    if(FAIL == l_RetVal)
    {
        goto cleanup_2;
    }

    /* Clear the TEEC_Operation struct */
    memset(&l_operation, 0, sizeof(TEEC_Operation));

    /*
     * Prepare the argument. Pass a value in the first parameter,
     * the remaining three parameters are unused.
     */
    l_operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE,
                     TEEC_NONE, TEEC_NONE);
    l_operation.params[0].value.a = 42;

    /**4) Send command to TA */
    l_RetVal = l_CryptoVerifyCa_SendCommand(&l_operation, &l_session, TA_MY_TEST_CMD_INC_VALUE);
    if(FAIL == l_RetVal)
    {
        goto cleanup_3;
    }

    /**5) The clean up operation */
    cleanup_3:
        TEEC_CloseSession(&l_session);
    cleanup_2:
        TEEC_FinalizeContext(&g_TaskContext);
    cleanup_1:
        printf("over\n");

}

TaskInit流程分析

l_CryptoVerifyCa_TaskInit()函式主要是呼叫了TEEC_InitializeContext(NULL, &g_TaskContext)來初始化context。傳入的name是NULL,根據註釋可知,這裡只能傳NULL

/**
 * TEEC_InitializeContext() - Initializes a context holding connection
 * information on the specific TEE, designated by the name string.

 * @param name    A zero-terminated string identifying the TEE to connect to.
 *                If name is set to NULL, the default TEE is connected to. NULL
 *                is the only supported value in this version of the API
 *                implementation.
 *
 * @param context The context structure which is to be initialized.
 *
 * @return TEEC_SUCCESS  The initialization was successful.
 * @return TEEC_Result   Something failed.
 */

TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *ctx)
{
	char devname[PATH_MAX];
	int fd;
	size_t n;

	if (!ctx)
		return TEEC_ERROR_BAD_PARAMETERS;

	for (n = 0; n < TEEC_MAX_DEV_SEQ; n++) { //TEEC_MAX_DEV_SEQ=10,只使用前10個tee裝置
		uint32_t gen_caps;

		snprintf(devname, sizeof(devname), "/dev/tee%zu", n);
		fd = teec_open_dev(devname, name, &gen_caps); //開啟節點,並取的caps,cap是什麼意思後面需要研究
		if (fd >= 0) {
			ctx->fd = fd;
			ctx->reg_mem = gen_caps & TEE_GEN_CAP_REG_MEM;
			return TEEC_SUCCESS;
		}
	}

	return TEEC_ERROR_ITEM_NOT_FOUND;
}
/**
 * struct tee_ioctl_version_data - TEE version
 * @impl_id:	[out] TEE implementation id
 * @impl_caps:	[out] Implementation specific capabilities
 * @gen_caps:	[out] Generic capabilities, defined by TEE_GEN_CAPS_* above
 *
 * Identifies the TEE implementation, @impl_id is one of TEE_IMPL_ID_* above.
 * @impl_caps is implementation specific, for example TEE_OPTEE_CAP_*
 * is valid when @impl_id == TEE_IMPL_ID_OPTEE.
 */

//下面的成員變數都是幹什麼的
struct tee_ioctl_version_data {
	__u32 impl_id;
	__u32 impl_caps;
	__u32 gen_caps;
};

static int teec_open_dev(const char *devname, const char *capabilities,
			 uint32_t *gen_caps)
{
	struct tee_ioctl_version_data vers; //
	int fd;

	fd = open(devname, O_RDWR);
	if (fd < 0)
		return -1;

	if (ioctl(fd, TEE_IOC_VERSION, &vers)) { //ioctl開啟/dev/tee0,獲取TEE VERSION DATA
		EMSG("TEE_IOC_VERSION failed");
		goto err;
	}

	/* We can only handle GP TEEs */
	if (!(vers.gen_caps & TEE_GEN_CAP_GP))
		goto err;

	if (capabilities) {
		if (strcmp(capabilities, "optee-tz") == 0) {
			if (vers.impl_id != TEE_IMPL_ID_OPTEE)
				goto err;
			if (!(vers.impl_caps & TEE_OPTEE_CAP_TZ))
				goto err;
		} else {
			/* Unrecognized capability requested */
			goto err;
		}
	}

	*gen_caps = vers.gen_caps;
	return fd;
err:
	close(fd);
	return -1;
}

ioctl會呼叫到kernel的tee driver:kernel/drivers/tee/tee_core.c

OPTEE的驅動的載入和初始化可以參考https://blog.csdn.net/shuaifengyun/article/details/72934531

static const struct file_operations tee_fops = {
	.owner = THIS_MODULE,
	.open = tee_open,
	.release = tee_release,
	.unlocked_ioctl = tee_ioctl,
	.compat_ioctl = tee_ioctl,
};

static int tee_ioctl_version(struct tee_context *ctx,
			     struct tee_ioctl_version_data __user *uvers)
{
	struct tee_ioctl_version_data vers;

	ctx->teedev->desc->ops->get_version(ctx->teedev, &vers);

	if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED)
		vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED;

	if (copy_to_user(uvers, &vers, sizeof(vers))) //把資料copy到userspace需要研究
		return -EFAULT;

	return 0;
}

static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct tee_context *ctx = filp->private_data;
	void __user *uarg = (void __user *)arg;

	switch (cmd) {
	case TEE_IOC_VERSION:
		return tee_ioctl_version(ctx, uarg);
	case TEE_IOC_SHM_ALLOC:
		return tee_ioctl_shm_alloc(ctx, uarg);
	case TEE_IOC_SHM_REGISTER:
		return tee_ioctl_shm_register(ctx, uarg);
	case TEE_IOC_SHM_REGISTER_FD:
		return tee_ioctl_shm_register_fd(ctx, uarg);
	case TEE_IOC_OPEN_SESSION:
		return tee_ioctl_open_session(ctx, uarg);
	case TEE_IOC_INVOKE:
		return tee_ioctl_invoke(ctx, uarg);
	case TEE_IOC_CANCEL:
		return tee_ioctl_cancel(ctx, uarg);
	case TEE_IOC_CLOSE_SESSION:
		return tee_ioctl_close_session(ctx, uarg);
	case TEE_IOC_SUPPL_RECV:
		return tee_ioctl_supp_recv(ctx, uarg);
	case TEE_IOC_SUPPL_SEND:
		return tee_ioctl_supp_send(ctx, uarg);
	default:
		return -EINVAL;
	}
}

get_version最後呼叫的是optee_get_version函式:kernel/drivers/tee/optee/core.c

static void optee_get_version(struct tee_device *teedev,
			      struct tee_ioctl_version_data *vers)
{
	struct tee_ioctl_version_data v = {
		.impl_id = TEE_IMPL_ID_OPTEE,
		.impl_caps = TEE_OPTEE_CAP_TZ,
		.gen_caps = TEE_GEN_CAP_GP,
	};
	struct optee *optee = tee_get_drvdata(teedev);

	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
		v.gen_caps |= TEE_GEN_CAP_REG_MEM;
	*vers = v;
}