From patchwork Wed Oct 6 07:08:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jens Wiklander X-Patchwork-Id: 12538745 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9AE73C433F5 for ; Wed, 6 Oct 2021 07:11:21 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 6C96F60FD8 for ; Wed, 6 Oct 2021 07:11:21 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 6C96F60FD8 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=orOYHQUEIxWph7CyjTMUjMmkCvqjwyFQdZB224Ahtww=; b=kx5z57hMhVZNxk 2I6V2s2ezhVAw86CffRZA9lGeI33KbRfXdwMRpGFLDdmtLVCQXI3zce8VQN/Wl8d75uR8/xN0UIds rVqth+SLR92hi5+iOoWAxPLdLomBD5EqAleJvyagSrRJLiME4pzn9xUh+yg8KVPbwWEmuOFBkDCPT b/VK2Z7cj3kWUqFNIyQjiJ7XeQZX9VMmtaHkACTWeoWkA88gmOy8XO+MytKh1SAQ2aEwN3NZGS7mb BQwYPUucOHSsLYD2ZEagCIYORyvtw1r6vcl2v+hkIg+gvHTSDEW0wDJXIyNMU84ABjRMvhGVBRPXp Us9AWAWXEZL0DqcZGjXA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mY13D-00D8Gd-K4; Wed, 06 Oct 2021 07:09:15 +0000 Received: from mail-lf1-x131.google.com ([2a00:1450:4864:20::131]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mY139-00D8F1-Cp for linux-arm-kernel@lists.infradead.org; Wed, 06 Oct 2021 07:09:12 +0000 Received: by mail-lf1-x131.google.com with SMTP id m3so6355645lfu.2 for ; Wed, 06 Oct 2021 00:09:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=NvXatvGat/zwzJqSmrQCEyhrxQWhLfa0q1L0IoYWuTg=; b=OUMXFkVRlHjmPfLG01X7bZWCgFjToKccbZtsyf9eZ8/OEK+CvHpiA5aPvlz2z9mRSD +ZKfg2U/a2An2ygd2VpvvsmPX7uCKkAC+Hxc41yuQTj+uePVLiOU+haODZ/ApYHH/8f2 3NtIrioLve9QELz1V3TrL3NotEHS9iQA8y4r+cJqKg9I7OGLndz+S6fBgkpjg+gxWis/ iblOzSOrQ8Yhakjl26lrMc4NPO5mWRL6Lu+Ut/JZLmsyhsPdF2RGgMFETpmAdABz0j4D Hk3mvPCK7lhVyyi3R3FFdaF92xiAeEqYtq86fH+kS+L6FtG3jJD0zGAJattBFE05YAj5 RNJQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=NvXatvGat/zwzJqSmrQCEyhrxQWhLfa0q1L0IoYWuTg=; b=RIIPVEfMSokoVYafqdvSI95yvseUYyuxfeOeZJDfHprYmSyoH68ShCSN2itNP635ZX ykCmQlgUkQ9KH9OTVTByP/BD2TNV0xBpNVzmtkumR5Wt5CHSDroy2EomciFTaEy7ya8g i8p45UI3FOb0QOnSU3fXfVq7OIkuMS+FU+Tv3RAlZ+PeuOKo+JCyM56t72vedPysQANd l8fbSm9c8NmpzNpOuBFpMsiCHkKAMrTdM1e2xXBbCp6hWoN03n5Q8uq3kt5Q9o1AK0ir XcEFrjjGXnwJAPPwgZJpdqg09Rp30qmovYLfJGiQL4MFn4baj6o+DfffI2skZ4M17C+u MKzw== X-Gm-Message-State: AOAM532nXu8PDiZYlL9tjGQvHIrYoizLjBA89YrVd8Rx1mlr7WmD3oJv reZ2sILQVEhtvmqz8rrFO3ibVQ== X-Google-Smtp-Source: ABdhPJwiWckAjDP0oEVOp7O6I+2xdWl6g3KHBDRUZLJcWAjzuNr4/EViv0KcghFJmprudeewbUSrZg== X-Received: by 2002:a05:6512:108f:: with SMTP id j15mr7531991lfg.337.1633504149074; Wed, 06 Oct 2021 00:09:09 -0700 (PDT) Received: from jade.ideon.se ([85.235.10.227]) by smtp.gmail.com with ESMTPSA id u15sm2381032lfl.20.2021.10.06.00.09.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Oct 2021 00:09:08 -0700 (PDT) From: Jens Wiklander To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, op-tee@lists.trustedfirmware.org Cc: Sumit Garg , Sudeep Holla , Marc Bonnici , Jerome Forissier , sughosh.ganu@linaro.org, Jens Wiklander Subject: [PATCH v6 1/5] tee: add sec_world_id to struct tee_shm Date: Wed, 6 Oct 2021 09:08:58 +0200 Message-Id: <20211006070902.2531311-2-jens.wiklander@linaro.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211006070902.2531311-1-jens.wiklander@linaro.org> References: <20211006070902.2531311-1-jens.wiklander@linaro.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20211006_000911_455779_E517A16D X-CRM114-Status: GOOD ( 13.57 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Adds sec_world_id to struct tee_shm which describes a shared memory object. sec_world_id can be used by a driver to store an id assigned by secure world. Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- include/linux/tee_drv.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 3ebfea0781f1..a1f03461369b 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -197,7 +197,11 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, * @num_pages: number of locked pages * @dmabuf: dmabuf used to for exporting to user space * @flags: defined by TEE_SHM_* in tee_drv.h - * @id: unique id of a shared memory object on this device + * @id: unique id of a shared memory object on this device, shared + * with user space + * @sec_world_id: + * secure world assigned id of this shared memory object, not + * used by all drivers * * This pool is only supposed to be accessed directly from the TEE * subsystem and from drivers that implements their own shm pool manager. @@ -213,6 +217,7 @@ struct tee_shm { struct dma_buf *dmabuf; u32 flags; int id; + u64 sec_world_id; }; /** From patchwork Wed Oct 6 07:08:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jens Wiklander X-Patchwork-Id: 12538749 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4562AC4332F for ; Wed, 6 Oct 2021 07:11:32 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1A16560FD8 for ; Wed, 6 Oct 2021 07:11:32 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 1A16560FD8 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=2jO/Y9FBx0jtVPSHj0WIrpID6txL/yu1Ub49SwygGZQ=; b=Z9v4LVEPGOzmnt FZJBwqt6yI57r3pVPNY1qJVuu6lefaFpJEyJiZ/G17Jkx4+tTFefawMkV2AU+PMo9f39QXhd1/b3N 5JPFH+iknz7K6jIcN3JSUwZC2CiBD59CKQrsONtUhiIVERDjdGYEXtCcDUdwALQgFSYpTfw9htu3j 8PzL0mHXzlkrO/fvtElwt3X9HcAb8JaD6aXh0HIwaGswp2Ob36AgA6OYSyM+Z4nSGPZT6bG0Cxuue 5VOII0VGwZ46EKRBRKKWxrMIrb2kPpEPFBTeIloLkfALODjeMj3wr2Hiq7moWJxGMZBv6WxFPT/01 7mQV0i9VwRt9YjMLq9Fg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mY13a-00D8Ka-9w; Wed, 06 Oct 2021 07:09:38 +0000 Received: from mail-lf1-x130.google.com ([2a00:1450:4864:20::130]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mY13A-00D8F9-8r for linux-arm-kernel@lists.infradead.org; Wed, 06 Oct 2021 07:09:14 +0000 Received: by mail-lf1-x130.google.com with SMTP id y23so6508445lfb.0 for ; Wed, 06 Oct 2021 00:09:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=vBdWBM0UW4kI+oJm3DDC8ZI0wyWMqjtOzMb/smR/vC8=; b=xN2xFYNdBFo9f+Eig16FNlM+M08j6F57PkLcV7EAZIgFkAWu+fva33L0Nb9zG3Aq3k yx3bJtzkoVAOcKJc/m/PGRadK8EhB2OROz30ZPI0gcMxYTdt7j81rKRvr6crmMfNvk6+ 81hDxyfjqMPyDG9O2SAQbDnZCBt47+C11nDvPkV891tL9tLwBu74BswcnOvK4OhzB+C3 PVdKxhxur6MKkzYmaVZsyLHBxyCYzPftjXRPvq97eoLj0ZD8aitEMrQTVPppIZ/AfxR8 NJI3lQtQIRxOUvuJAOqCR0VPUqK+cDU14k6J1znQRFayW01RF1wJGbbrvrOmBOiY94LW RdQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=vBdWBM0UW4kI+oJm3DDC8ZI0wyWMqjtOzMb/smR/vC8=; b=HiRO6ULI9aQJUer9RLKo89DfnIB8NP3Yk9Qgr7g/jmNyl+Gh1JisE+FFel2q4Rmjd7 nnw52xv4lCwEA+rJMKK4QezdMaFDxa4aeskQRdck8NkNrkdDDAaQ9D43hgYy5iX24ZYF vvsg3oc7cxgyBKW4soUhfJwJ2x8YfPftp1nViVz8YrkL50Heo/745ZBZBNiCRyO8QJIG htMVGrxOndmNiUVjanPcJvdfLXgjLbkPVxsaIcMFBL/zaQ2Ug9cmXMWDgLhSGuBhzAhh xAa5Xf7y4S4Ge526gMnt3KlIrKL0DDaivQeXZV2i5rZePRmC13Z2pJw+1V69sXStRMG7 r1iA== X-Gm-Message-State: AOAM532XlOcGko08Cu2ZDRpyt1oDlyuybwP/6w6zXj6oSnyYvZmnVLZy G4JQjnNkN93NjHqieA6aQInnOSvaY5ax7A== X-Google-Smtp-Source: ABdhPJwUJWaPZSsc43Fp2TAhFNvB3UDPKp+t8SByGt3pbW91TKh13hnVBpIUsnDqItQQMcddB3Q1nA== X-Received: by 2002:a2e:9ad7:: with SMTP id p23mr26778632ljj.527.1633504149826; Wed, 06 Oct 2021 00:09:09 -0700 (PDT) Received: from jade.ideon.se ([85.235.10.227]) by smtp.gmail.com with ESMTPSA id u15sm2381032lfl.20.2021.10.06.00.09.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Oct 2021 00:09:09 -0700 (PDT) From: Jens Wiklander To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, op-tee@lists.trustedfirmware.org Cc: Sumit Garg , Sudeep Holla , Marc Bonnici , Jerome Forissier , sughosh.ganu@linaro.org, Jens Wiklander Subject: [PATCH v6 2/5] optee: simplify optee_release() Date: Wed, 6 Oct 2021 09:08:59 +0200 Message-Id: <20211006070902.2531311-3-jens.wiklander@linaro.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211006070902.2531311-1-jens.wiklander@linaro.org> References: <20211006070902.2531311-1-jens.wiklander@linaro.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20211006_000912_337185_4C3CE256 X-CRM114-Status: GOOD ( 19.05 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Simplifies optee_release() with a new helper function, optee_close_session_helper() which has been factored out from optee_close_session(). A separate optee_release_supp() is added for the supplicant device. Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/call.c | 31 ++++++++++------- drivers/tee/optee/core.c | 56 +++++++++++-------------------- drivers/tee/optee/optee_private.h | 1 + 3 files changed, 39 insertions(+), 49 deletions(-) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 945f03da0223..103976df2062 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -288,12 +288,28 @@ int optee_open_session(struct tee_context *ctx, return rc; } -int optee_close_session(struct tee_context *ctx, u32 session) +int optee_close_session_helper(struct tee_context *ctx, u32 session) { - struct optee_context_data *ctxdata = ctx->data; struct tee_shm *shm; struct optee_msg_arg *msg_arg; phys_addr_t msg_parg; + + shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; + msg_arg->session = session; + optee_do_call_with_arg(ctx, msg_parg); + + tee_shm_free(shm); + + return 0; +} + +int optee_close_session(struct tee_context *ctx, u32 session) +{ + struct optee_context_data *ctxdata = ctx->data; struct optee_session *sess; /* Check that the session is valid and remove it from the list */ @@ -306,16 +322,7 @@ int optee_close_session(struct tee_context *ctx, u32 session) return -EINVAL; kfree(sess); - shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); - if (IS_ERR(shm)) - return PTR_ERR(shm); - - msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; - msg_arg->session = session; - optee_do_call_with_arg(ctx, msg_parg); - - tee_shm_free(shm); - return 0; + return optee_close_session_helper(ctx, session); } int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 5ce13b099d7d..86a1ae8d296d 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -264,60 +264,42 @@ static int optee_open(struct tee_context *ctx) return 0; } -static void optee_release(struct tee_context *ctx) +static void optee_release_helper(struct tee_context *ctx, + int (*close_session)(struct tee_context *ctx, + u32 session)) { struct optee_context_data *ctxdata = ctx->data; - struct tee_device *teedev = ctx->teedev; - struct optee *optee = tee_get_drvdata(teedev); - struct tee_shm *shm; - struct optee_msg_arg *arg = NULL; - phys_addr_t parg; struct optee_session *sess; struct optee_session *sess_tmp; if (!ctxdata) return; - shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), - TEE_SHM_MAPPED | TEE_SHM_PRIV); - if (!IS_ERR(shm)) { - arg = tee_shm_get_va(shm, 0); - /* - * If va2pa fails for some reason, we can't call into - * secure world, only free the memory. Secure OS will leak - * sessions and finally refuse more sessions, but we will - * at least let normal world reclaim its memory. - */ - if (!IS_ERR(arg)) - if (tee_shm_va2pa(shm, arg, &parg)) - arg = NULL; /* prevent usage of parg below */ - } - list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list, list_node) { list_del(&sess->list_node); - if (!IS_ERR_OR_NULL(arg)) { - memset(arg, 0, sizeof(*arg)); - arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; - arg->session = sess->session_id; - optee_do_call_with_arg(ctx, parg); - } + close_session(ctx, sess->session_id); kfree(sess); } kfree(ctxdata); + ctx->data = NULL; +} - if (!IS_ERR(shm)) - tee_shm_free(shm); +static void optee_release(struct tee_context *ctx) +{ + optee_release_helper(ctx, optee_close_session_helper); +} - ctx->data = NULL; +static void optee_release_supp(struct tee_context *ctx) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); - if (teedev == optee->supp_teedev) { - if (optee->scan_bus_wq) { - destroy_workqueue(optee->scan_bus_wq); - optee->scan_bus_wq = NULL; - } - optee_supp_release(&optee->supp); + optee_release_helper(ctx, optee_close_session_helper); + if (optee->scan_bus_wq) { + destroy_workqueue(optee->scan_bus_wq); + optee->scan_bus_wq = NULL; } + optee_supp_release(&optee->supp); } static const struct tee_driver_ops optee_ops = { @@ -341,7 +323,7 @@ static const struct tee_desc optee_desc = { static const struct tee_driver_ops optee_supp_ops = { .get_version = optee_get_version, .open = optee_open, - .release = optee_release, + .release = optee_release_supp, .supp_recv = optee_supp_recv, .supp_send = optee_supp_send, .shm_register = optee_shm_register_supp, diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index dbdd367be156..d9f5f3f7ba58 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -152,6 +152,7 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg); int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *param); +int optee_close_session_helper(struct tee_context *ctx, u32 session); int optee_close_session(struct tee_context *ctx, u32 session); int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param); From patchwork Wed Oct 6 07:09:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jens Wiklander X-Patchwork-Id: 12538751 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B6508C433F5 for ; Wed, 6 Oct 2021 07:12:09 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 77033610C7 for ; Wed, 6 Oct 2021 07:12:09 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 77033610C7 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=j9dpdCUJ1yn79rv+UkcoMdUGN4X6TSnsTGxOrEZChC4=; b=lxppRTb5xdzm+U grE2zigNGRqWdY8XrTfEOvFbwyYPIylybTPqzzSI9YyOEmRGenDw88h3MaNCqxyijciB8QKmJUY73 cTf3gDdBPTxuIA/eweIhiDBLJY+W1gU0D9bNuUfA6HeFl8lereyQr7NhpS8EQlD/Ia0LsnPMjk740 vFIwWH2NQZIWTIcaANOj49Z9RsjxtsWMvVnSvYyATMLAfLg7ocRMtKA9QdFaO/dGePG1z8eqC8twA Gnt59No41m590jBbfi1ZaXk1729L+jj2bwTByGyJ6EuES+y8/Yy1BT+WZtbnVM99XEKOmi7v34ihI q6QxUdkVUiEOaW6uBHzw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mY13l-00D8Na-K7; Wed, 06 Oct 2021 07:09:49 +0000 Received: from mail-lf1-x12d.google.com ([2a00:1450:4864:20::12d]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mY13B-00D8Fd-9I for linux-arm-kernel@lists.infradead.org; Wed, 06 Oct 2021 07:09:16 +0000 Received: by mail-lf1-x12d.google.com with SMTP id t9so6356812lfd.1 for ; Wed, 06 Oct 2021 00:09:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=o/KK/P8maHQ0VnrYiPfvtpWT4pJDMNsRhKrjhgYtThI=; b=fqsKfsUH/T8GDrKpgll8GTFk7RBaJw8dHTpfeLkuviVeeddN7CDmmhYixIGf8vPOzX wV7YTW2dERht2EFYPv3JuHYczyDAjWu4jxoNMwtv+ZlMYKuYdR3YdfiPej4by5+bNzmX QZMlr4vrUnJlR8nk3EEyLXMQjKPplYY2lvMEPFR9rFdIW6uMzRFQMpNzMhuicVMNYXB2 WdjW02yzKTeMGISMIHlPeOsL1+uRMn0enKYTjgCUBKPHfs86VqiQxDhJiEXnqIjqBJPq 7tBEKUrUsvFO5FkNgSsGnAjQc7pQlfo9QCcWGO/vYLq6dGsz1w6dCsXheydQuCH60XYB cTlw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=o/KK/P8maHQ0VnrYiPfvtpWT4pJDMNsRhKrjhgYtThI=; b=VUX1QJ8xTrSD+OUL47M0AjrGNBGy/7XmEAeLd+IMtCR+irFgkEXl35JIfvCLhehlxQ /MpywihHZ61Tme8fDIm5w5hE+9nCAmHW4A2TR8uqyHeg09laiHzgYSgjzgNjhSTGFhr1 5nBzwQpKBfHdfBGSSSU6/ZAu4qT4+KwVoIjU/mBzlXgDK6l664Qz6fgP9SWl6lyMN/XM hbuWJbPsi08X29l8YlvOE0HXk2YBcS1MXkdlE1XYcZ2RGfc+7pWajIjFpzE9W/4WLw54 h/BETIsf4VqTdLY6ieoEu3lK+NIigupm8EJRFKTEnI8kOU45ueLOgRNX4I7GPCkC7uXB 3cwg== X-Gm-Message-State: AOAM530XqPvFNmJt2zkvs2G9cxIYdYIF9KlUqDw2a0bZZ/GISXBsAlmB 3OBcuPvyq3sKf09d7dFATstvOA== X-Google-Smtp-Source: ABdhPJylprop7LynwAhRtgtGWBi1cdCvSkynKm9EMhiO6HQ0y3fScpdDUzHWbVp65/aqYFdUdOETog== X-Received: by 2002:a2e:7d03:: with SMTP id y3mr6467613ljc.221.1633504150688; Wed, 06 Oct 2021 00:09:10 -0700 (PDT) Received: from jade.ideon.se ([85.235.10.227]) by smtp.gmail.com with ESMTPSA id u15sm2381032lfl.20.2021.10.06.00.09.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Oct 2021 00:09:10 -0700 (PDT) From: Jens Wiklander To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, op-tee@lists.trustedfirmware.org Cc: Sumit Garg , Sudeep Holla , Marc Bonnici , Jerome Forissier , sughosh.ganu@linaro.org, Jens Wiklander Subject: [PATCH v6 3/5] optee: refactor driver with internal callbacks Date: Wed, 6 Oct 2021 09:09:00 +0200 Message-Id: <20211006070902.2531311-4-jens.wiklander@linaro.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211006070902.2531311-1-jens.wiklander@linaro.org> References: <20211006070902.2531311-1-jens.wiklander@linaro.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20211006_000913_368705_DDC734B7 X-CRM114-Status: GOOD ( 22.26 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org The OP-TEE driver is refactored with three internal callbacks replacing direct calls to optee_from_msg_param(), optee_to_msg_param() and optee_do_call_with_arg(). These functions a central to communicating with OP-TEE in secure world by using the SMC Calling Convention directly. This refactoring makes room for using other primitives to communicate with OP-TEE in secure world while being able to reuse as much as possible from the present driver. Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/call.c | 86 +++++++++-------- drivers/tee/optee/core.c | 148 ++++++++++++++++++++---------- drivers/tee/optee/optee_private.h | 35 +++++-- drivers/tee/optee/rpc.c | 19 ++-- 4 files changed, 182 insertions(+), 106 deletions(-) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 103976df2062..ddedde45f1ee 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2015-2021, Linaro Limited */ #include #include @@ -118,20 +118,25 @@ static struct optee_session *find_session(struct optee_context_data *ctxdata, /** * optee_do_call_with_arg() - Do an SMC to OP-TEE in secure world * @ctx: calling context - * @parg: physical address of message to pass to secure world + * @arg: shared memory holding the message to pass to secure world * * Does and SMC to OP-TEE in secure world and handles eventual resulting * Remote Procedure Calls (RPC) from OP-TEE. * * Returns return code from secure world, 0 is OK */ -u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) +int optee_do_call_with_arg(struct tee_context *ctx, struct tee_shm *arg) { struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_call_waiter w; struct optee_rpc_param param = { }; struct optee_call_ctx call_ctx = { }; - u32 ret; + phys_addr_t parg; + int rc; + + rc = tee_shm_get_pa(arg, 0, &parg); + if (rc) + return rc; param.a0 = OPTEE_SMC_CALL_WITH_ARG; reg_pair_from_64(¶m.a1, ¶m.a2, parg); @@ -160,7 +165,7 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) param.a3 = res.a3; optee_handle_rpc(ctx, ¶m, &call_ctx); } else { - ret = res.a0; + rc = res.a0; break; } } @@ -172,14 +177,12 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) */ optee_cq_wait_final(&optee->call_queue, &w); - return ret; + return rc; } static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, - struct optee_msg_arg **msg_arg, - phys_addr_t *msg_parg) + struct optee_msg_arg **msg_arg) { - int rc; struct tee_shm *shm; struct optee_msg_arg *ma; @@ -190,22 +193,13 @@ static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, ma = tee_shm_get_va(shm, 0); if (IS_ERR(ma)) { - rc = PTR_ERR(ma); - goto out; + tee_shm_free(shm); + return (void *)ma; } - rc = tee_shm_get_pa(shm, 0, msg_parg); - if (rc) - goto out; - memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params)); ma->num_params = num_params; *msg_arg = ma; -out: - if (rc) { - tee_shm_free(shm); - return ERR_PTR(rc); - } return shm; } @@ -214,16 +208,16 @@ int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *param) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; int rc; struct tee_shm *shm; struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess = NULL; uuid_t client_uuid; /* +2 for the meta parameters added below */ - shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg, &msg_parg); + shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm); @@ -247,7 +241,8 @@ int optee_open_session(struct tee_context *ctx, goto out; export_uuid(msg_arg->params[1].u.octets, &client_uuid); - rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params, param); + rc = optee->ops->to_msg_param(optee, msg_arg->params + 2, + arg->num_params, param); if (rc) goto out; @@ -257,7 +252,7 @@ int optee_open_session(struct tee_context *ctx, goto out; } - if (optee_do_call_with_arg(ctx, msg_parg)) { + if (optee->ops->do_call_with_arg(ctx, shm)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } @@ -272,7 +267,8 @@ int optee_open_session(struct tee_context *ctx, kfree(sess); } - if (optee_from_msg_param(param, arg->num_params, msg_arg->params + 2)) { + if (optee->ops->from_msg_param(optee, param, arg->num_params, + msg_arg->params + 2)) { arg->ret = TEEC_ERROR_COMMUNICATION; arg->ret_origin = TEEC_ORIGIN_COMMS; /* Close session again to avoid leakage */ @@ -291,16 +287,16 @@ int optee_open_session(struct tee_context *ctx, int optee_close_session_helper(struct tee_context *ctx, u32 session) { struct tee_shm *shm; + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; - shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); + shm = get_msg_arg(ctx, 0, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm); msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; msg_arg->session = session; - optee_do_call_with_arg(ctx, msg_parg); + optee->ops->do_call_with_arg(ctx, shm); tee_shm_free(shm); @@ -328,10 +324,10 @@ int optee_close_session(struct tee_context *ctx, u32 session) int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; struct tee_shm *shm; struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess; int rc; @@ -342,7 +338,7 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, if (!sess) return -EINVAL; - shm = get_msg_arg(ctx, arg->num_params, &msg_arg, &msg_parg); + shm = get_msg_arg(ctx, arg->num_params, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm); msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; @@ -350,16 +346,18 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, msg_arg->session = arg->session; msg_arg->cancel_id = arg->cancel_id; - rc = optee_to_msg_param(msg_arg->params, arg->num_params, param); + rc = optee->ops->to_msg_param(optee, msg_arg->params, arg->num_params, + param); if (rc) goto out; - if (optee_do_call_with_arg(ctx, msg_parg)) { + if (optee->ops->do_call_with_arg(ctx, shm)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } - if (optee_from_msg_param(param, arg->num_params, msg_arg->params)) { + if (optee->ops->from_msg_param(optee, param, arg->num_params, + msg_arg->params)) { msg_arg->ret = TEEC_ERROR_COMMUNICATION; msg_arg->ret_origin = TEEC_ORIGIN_COMMS; } @@ -373,10 +371,10 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_context_data *ctxdata = ctx->data; struct tee_shm *shm; struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; struct optee_session *sess; /* Check that the session is valid */ @@ -386,14 +384,14 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) if (!sess) return -EINVAL; - shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); + shm = get_msg_arg(ctx, 0, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm); msg_arg->cmd = OPTEE_MSG_CMD_CANCEL; msg_arg->session = session; msg_arg->cancel_id = cancel_id; - optee_do_call_with_arg(ctx, msg_parg); + optee->ops->do_call_with_arg(ctx, shm); tee_shm_free(shm); return 0; @@ -622,10 +620,10 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, struct page **pages, size_t num_pages, unsigned long start) { - struct tee_shm *shm_arg = NULL; + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_msg_arg *msg_arg; + struct tee_shm *shm_arg; u64 *pages_list; - phys_addr_t msg_parg; int rc; if (!num_pages) @@ -639,7 +637,7 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, if (!pages_list) return -ENOMEM; - shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg); + shm_arg = get_msg_arg(ctx, 1, &msg_arg); if (IS_ERR(shm_arg)) { rc = PTR_ERR(shm_arg); goto out; @@ -660,7 +658,7 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) | (tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); - if (optee_do_call_with_arg(ctx, msg_parg) || + if (optee->ops->do_call_with_arg(ctx, shm_arg) || msg_arg->ret != TEEC_SUCCESS) rc = -EINVAL; @@ -672,12 +670,12 @@ int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) { - struct tee_shm *shm_arg; + struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_msg_arg *msg_arg; - phys_addr_t msg_parg; + struct tee_shm *shm_arg; int rc = 0; - shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg); + shm_arg = get_msg_arg(ctx, 1, &msg_arg); if (IS_ERR(shm_arg)) return PTR_ERR(shm_arg); @@ -686,7 +684,7 @@ int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm; - if (optee_do_call_with_arg(ctx, msg_parg) || + if (optee->ops->do_call_with_arg(ctx, shm_arg) || msg_arg->ret != TEEC_SUCCESS) rc = -EINVAL; tee_shm_free(shm_arg); diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 86a1ae8d296d..50650224e311 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2015-2021, Linaro Limited */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -27,21 +27,87 @@ #define OPTEE_SHM_NUM_PRIV_PAGES CONFIG_OPTEE_SHM_NUM_PRIV_PAGES +static void from_msg_param_value(struct tee_param *p, u32 attr, + const struct optee_msg_param *mp) +{ + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; + p->u.value.a = mp->u.value.a; + p->u.value.b = mp->u.value.b; + p->u.value.c = mp->u.value.c; +} + +static int from_msg_param_tmp_mem(struct tee_param *p, u32 attr, + const struct optee_msg_param *mp) +{ + struct tee_shm *shm; + phys_addr_t pa; + int rc; + + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; + p->u.memref.size = mp->u.tmem.size; + shm = (struct tee_shm *)(unsigned long)mp->u.tmem.shm_ref; + if (!shm) { + p->u.memref.shm_offs = 0; + p->u.memref.shm = NULL; + return 0; + } + + rc = tee_shm_get_pa(shm, 0, &pa); + if (rc) + return rc; + + p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; + p->u.memref.shm = shm; + + /* Check that the memref is covered by the shm object */ + if (p->u.memref.size) { + size_t o = p->u.memref.shm_offs + + p->u.memref.size - 1; + + rc = tee_shm_get_pa(shm, o, NULL); + if (rc) + return rc; + } + + return 0; +} + +static void from_msg_param_reg_mem(struct tee_param *p, u32 attr, + const struct optee_msg_param *mp) +{ + struct tee_shm *shm; + + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; + p->u.memref.size = mp->u.rmem.size; + shm = (struct tee_shm *)(unsigned long)mp->u.rmem.shm_ref; + + if (shm) { + p->u.memref.shm_offs = mp->u.rmem.offs; + p->u.memref.shm = shm; + } else { + p->u.memref.shm_offs = 0; + p->u.memref.shm = NULL; + } +} + /** * optee_from_msg_param() - convert from OPTEE_MSG parameters to * struct tee_param + * @optee: main service struct * @params: subsystem internal parameter representation * @num_params: number of elements in the parameter arrays * @msg_params: OPTEE_MSG parameters * Returns 0 on success or <0 on failure */ -int optee_from_msg_param(struct tee_param *params, size_t num_params, - const struct optee_msg_param *msg_params) +static int optee_from_msg_param(struct optee *optee, struct tee_param *params, + size_t num_params, + const struct optee_msg_param *msg_params) { int rc; size_t n; - struct tee_shm *shm; - phys_addr_t pa; for (n = 0; n < num_params; n++) { struct tee_param *p = params + n; @@ -56,48 +122,19 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params, case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: - p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + - attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; - p->u.value.a = mp->u.value.a; - p->u.value.b = mp->u.value.b; - p->u.value.c = mp->u.value.c; + from_msg_param_value(p, attr, mp); break; case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: - p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + - attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; - p->u.memref.size = mp->u.tmem.size; - shm = (struct tee_shm *)(unsigned long) - mp->u.tmem.shm_ref; - if (!shm) { - p->u.memref.shm_offs = 0; - p->u.memref.shm = NULL; - break; - } - rc = tee_shm_get_pa(shm, 0, &pa); + rc = from_msg_param_tmp_mem(p, attr, mp); if (rc) return rc; - p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; - p->u.memref.shm = shm; break; case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: - p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + - attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; - p->u.memref.size = mp->u.rmem.size; - shm = (struct tee_shm *)(unsigned long) - mp->u.rmem.shm_ref; - - if (!shm) { - p->u.memref.shm_offs = 0; - p->u.memref.shm = NULL; - break; - } - p->u.memref.shm_offs = mp->u.rmem.offs; - p->u.memref.shm = shm; - + from_msg_param_reg_mem(p, attr, mp); break; default: @@ -107,6 +144,16 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params, return 0; } +static void to_msg_param_value(struct optee_msg_param *mp, + const struct tee_param *p) +{ + mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + mp->u.value.a = p->u.value.a; + mp->u.value.b = p->u.value.b; + mp->u.value.c = p->u.value.c; +} + static int to_msg_param_tmp_mem(struct optee_msg_param *mp, const struct tee_param *p) { @@ -149,13 +196,15 @@ static int to_msg_param_reg_mem(struct optee_msg_param *mp, /** * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters + * @optee: main service struct * @msg_params: OPTEE_MSG parameters * @num_params: number of elements in the parameter arrays * @params: subsystem itnernal parameter representation * Returns 0 on success or <0 on failure */ -int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, - const struct tee_param *params) +static int optee_to_msg_param(struct optee *optee, + struct optee_msg_param *msg_params, + size_t num_params, const struct tee_param *params) { int rc; size_t n; @@ -172,11 +221,7 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: - mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - - TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; - mp->u.value.a = p->u.value.a; - mp->u.value.b = p->u.value.b; - mp->u.value.c = p->u.value.c; + to_msg_param_value(mp, p); break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: @@ -302,7 +347,7 @@ static void optee_release_supp(struct tee_context *ctx) optee_supp_release(&optee->supp); } -static const struct tee_driver_ops optee_ops = { +static const struct tee_driver_ops optee_clnt_ops = { .get_version = optee_get_version, .open = optee_open, .release = optee_release, @@ -314,9 +359,9 @@ static const struct tee_driver_ops optee_ops = { .shm_unregister = optee_shm_unregister, }; -static const struct tee_desc optee_desc = { +static const struct tee_desc optee_clnt_desc = { .name = DRIVER_NAME "-clnt", - .ops = &optee_ops, + .ops = &optee_clnt_ops, .owner = THIS_MODULE, }; @@ -337,6 +382,12 @@ static const struct tee_desc optee_supp_desc = { .flags = TEE_DESC_PRIVILEGED, }; +static const struct optee_ops optee_ops = { + .do_call_with_arg = optee_do_call_with_arg, + .to_msg_param = optee_to_msg_param, + .from_msg_param = optee_from_msg_param, +}; + static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) { struct arm_smccc_res res; @@ -667,10 +718,11 @@ static int optee_probe(struct platform_device *pdev) goto err; } + optee->ops = &optee_ops; optee->invoke_fn = invoke_fn; optee->sec_caps = sec_caps; - teedev = tee_device_alloc(&optee_desc, NULL, pool, optee); + teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); if (IS_ERR(teedev)) { rc = PTR_ERR(teedev); goto err; diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index d9f5f3f7ba58..92d59c145d4a 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2015-2021, Linaro Limited */ #ifndef OPTEE_PRIVATE_H @@ -66,9 +66,34 @@ struct optee_supp { struct completion reqs_c; }; +struct optee; + +/** + * struct optee_ops - OP-TEE driver internal operations + * @do_call_with_arg: enters OP-TEE in secure world + * @to_msg_param: converts from struct tee_param to OPTEE_MSG parameters + * @from_msg_param: converts from OPTEE_MSG parameters to struct tee_param + * + * These OPs are only supposed to be used internally in the OP-TEE driver + * as a way of abstracting the different methogs of entering OP-TEE in + * secure world. + */ +struct optee_ops { + int (*do_call_with_arg)(struct tee_context *ctx, + struct tee_shm *shm_arg); + int (*to_msg_param)(struct optee *optee, + struct optee_msg_param *msg_params, + size_t num_params, const struct tee_param *params); + int (*from_msg_param)(struct optee *optee, struct tee_param *params, + size_t num_params, + const struct optee_msg_param *msg_params); +}; + /** * struct optee - main service struct * @supp_teedev: supplicant device + * @ops: internal callbacks for different ways to reach secure + * world * @teedev: client device * @invoke_fn: function to issue smc or hvc * @call_queue: queue of threads waiting to call @invoke_fn @@ -86,6 +111,7 @@ struct optee_supp { struct optee { struct tee_device *supp_teedev; struct tee_device *teedev; + const struct optee_ops *ops; optee_invoke_fn *invoke_fn; struct optee_call_queue call_queue; struct optee_wait_queue wait_queue; @@ -148,7 +174,7 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, struct tee_param *param); -u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg); +int optee_do_call_with_arg(struct tee_context *ctx, struct tee_shm *arg); int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *param); @@ -172,11 +198,6 @@ int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, unsigned long start); int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm); -int optee_from_msg_param(struct tee_param *params, size_t num_params, - const struct optee_msg_param *msg_params); -int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, - const struct tee_param *params); - u64 *optee_allocate_pages_list(size_t num_entries); void optee_free_pages_list(void *array, size_t num_entries); void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index efbaff7ad7e5..309258d47790 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2016, Linaro Limited + * Copyright (c) 2015-2021, Linaro Limited */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -55,6 +55,7 @@ static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg) static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, struct optee_msg_arg *arg) { + struct optee *optee = tee_get_drvdata(ctx->teedev); struct tee_param *params; struct i2c_adapter *adapter; struct i2c_msg msg = { }; @@ -79,7 +80,8 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, return; } - if (optee_from_msg_param(params, arg->num_params, arg->params)) + if (optee->ops->from_msg_param(optee, params, arg->num_params, + arg->params)) goto bad; for (i = 0; i < arg->num_params; i++) { @@ -122,7 +124,8 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, arg->ret = TEEC_ERROR_COMMUNICATION; } else { params[3].u.value.a = msg.len; - if (optee_to_msg_param(arg->params, arg->num_params, params)) + if (optee->ops->to_msg_param(optee, arg->params, + arg->num_params, params)) arg->ret = TEEC_ERROR_BAD_PARAMETERS; else arg->ret = TEEC_SUCCESS; @@ -234,7 +237,7 @@ static void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg) arg->ret = TEEC_ERROR_BAD_PARAMETERS; } -static void handle_rpc_supp_cmd(struct tee_context *ctx, +static void handle_rpc_supp_cmd(struct tee_context *ctx, struct optee *optee, struct optee_msg_arg *arg) { struct tee_param *params; @@ -248,14 +251,16 @@ static void handle_rpc_supp_cmd(struct tee_context *ctx, return; } - if (optee_from_msg_param(params, arg->num_params, arg->params)) { + if (optee->ops->from_msg_param(optee, params, arg->num_params, + arg->params)) { arg->ret = TEEC_ERROR_BAD_PARAMETERS; goto out; } arg->ret = optee_supp_thrd_req(ctx, arg->cmd, arg->num_params, params); - if (optee_to_msg_param(arg->params, arg->num_params, params)) + if (optee->ops->to_msg_param(optee, arg->params, arg->num_params, + params)) arg->ret = TEEC_ERROR_BAD_PARAMETERS; out: kfree(params); @@ -480,7 +485,7 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, handle_rpc_func_cmd_i2c_transfer(ctx, arg); break; default: - handle_rpc_supp_cmd(ctx, arg); + handle_rpc_supp_cmd(ctx, optee, arg); } } From patchwork Wed Oct 6 07:09:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jens Wiklander X-Patchwork-Id: 12538757 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BF451C433EF for ; Wed, 6 Oct 2021 07:12:57 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8A8E0610E6 for ; Wed, 6 Oct 2021 07:12:57 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 8A8E0610E6 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=5PD7r3tQ8Kh4QRuRKGrIfpeL/8LYloIU0TJB0eOUhs8=; b=MqaRSD3JLQgEQh 82bG1jprHgyU3pewzDwq56nZrDEdHHZ0zDtBYjTOcsNg8jhMl6PzF76kSVxUXUyZBO9AlUPg05jz0 o2XmkBz/3g3Zs4k3ZKNofOa1OyTz9y6sy5To1bd5UGng+4SQ7hnLwADgnbRf7bsA9WaGnzLutRwGr mRUmmr3ggawNU4A1+B8jxiNoZoE2Y+TiElZh25L0KyNHgj5SeMuJJD7J2DJlP7oEyRRV9MQE17Dj2 eG0SOH/WYHKtgVmrmEnXggkhTnmHcxJMjvkAL0RoHORxV2eh0hiHswiJhELD0z1NTte3HKrB7HlMp ds6DAWZZO4KS5kNpjnPg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mY14I-00D8aO-CJ; Wed, 06 Oct 2021 07:10:22 +0000 Received: from mail-lf1-x129.google.com ([2a00:1450:4864:20::129]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mY13D-00D8Gf-Ph for linux-arm-kernel@lists.infradead.org; Wed, 06 Oct 2021 07:09:21 +0000 Received: by mail-lf1-x129.google.com with SMTP id y15so6268558lfk.7 for ; Wed, 06 Oct 2021 00:09:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Ofz2wEpHsX/yC8KPgBn2mZcBE5gsU5GJ+93ibL+V9Xo=; b=SqOntF244O+NmdtW/jVb4s0ZLZKvzqhatZvbR88+rW5CKffoskbiNLkHc+HQiXIGg8 PAHYa70WTM8DZSQRKN2pVzflWts6WkOm/fGvGMuqOBcuCd+vKJ72GQsXvKvswmoHSSrA InWwTxJZ+xUGQMkHdSpJfRxZML+OiO6QGKtZtgzJJ0nUgcWgulwbJmZJbEgY+qVtCZ98 SMNKBxHeE1f/WHUJWWtEl4mp9Dv8EK069iNQz31DycaP7i7l0Il4wiB3defa+HbrAaVJ ytwGVmUdP1d5ajMVhXooyrIm0ePMJuB37IRXj/NzO00v04JvdfHqbfw7JHn1gwDmH43x 2BsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Ofz2wEpHsX/yC8KPgBn2mZcBE5gsU5GJ+93ibL+V9Xo=; b=0H86+n4IKWs/4Zx3hIsv1xmNKrxaLK/YJzR80pYCLTufA/r719GTs9eVgQOlRaTQtN vJmODx7jPg9zHUx28j7YFlyTBUJPuXyjoTjep/K1rrjHC8IsGFFDpSn7RD5/adIzdZp3 xe7Nog/R4CSCuaAL0iZGrpdryUWc4cLKzhDuICtDPOIzpz44pHZyHHJiN4uc9bDrWI9p H/ZuHYcVE/HltSCGHaDRzk9CUnrl5Xmud29Qf0BmjcbuZJv/Iad1odx5uSOeoAFeWXWF W1LNJvmFrQnoRlMNFHTM4xM8eWsmJfBod3Wr0Yu1abx3A2JQ3Fz7H93+nSlWLIecfKVt +mbw== X-Gm-Message-State: AOAM531L2+IRbqjWTo0OKYI5E4vqaZ8h1PEPwiLL52XqdbNl5grvSHY7 0aLmucJHzaJ5YqfmuIVeGNuORQ== X-Google-Smtp-Source: ABdhPJx86J/Q2FuHD8s8YAJiDxCJonG0vBBJk8bhkWZ9Pds9LzxAJGULGDDL+S6NMhf6VuHVq5jzxQ== X-Received: by 2002:a05:6512:3084:: with SMTP id z4mr7800230lfd.195.1633504152177; Wed, 06 Oct 2021 00:09:12 -0700 (PDT) Received: from jade.ideon.se ([85.235.10.227]) by smtp.gmail.com with ESMTPSA id u15sm2381032lfl.20.2021.10.06.00.09.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Oct 2021 00:09:11 -0700 (PDT) From: Jens Wiklander To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, op-tee@lists.trustedfirmware.org Cc: Sumit Garg , Sudeep Holla , Marc Bonnici , Jerome Forissier , sughosh.ganu@linaro.org, Jens Wiklander Subject: [PATCH v6 4/5] optee: isolate smc abi Date: Wed, 6 Oct 2021 09:09:01 +0200 Message-Id: <20211006070902.2531311-5-jens.wiklander@linaro.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211006070902.2531311-1-jens.wiklander@linaro.org> References: <20211006070902.2531311-1-jens.wiklander@linaro.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20211006_000916_353563_08CFB829 X-CRM114-Status: GOOD ( 33.55 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Isolate the ABI based on raw SMCs. Code specific to the raw SMC ABI is moved into smc_abi.c. This makes room for other ABIs with a clear separation. The driver changes to use module_init()/module_exit() instead of module_platform_driver(). The platform_driver_register() and platform_driver_unregister() functions called directly to keep the same behavior. This is needed because module_platform_driver() is based on module_driver() which can only be used once in a module. A function optee_rpc_cmd() is factored out from the function handle_rpc_func_cmd() to handle the ABI independent part of RPC processing. This patch is not supposed to change the driver behavior, it's only a matter of reorganizing the code. Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/Makefile | 4 +- drivers/tee/optee/call.c | 369 +------- drivers/tee/optee/core.c | 721 ++------------- drivers/tee/optee/optee_private.h | 104 ++- drivers/tee/optee/rpc.c | 218 +---- drivers/tee/optee/shm_pool.c | 101 --- drivers/tee/optee/shm_pool.h | 14 - drivers/tee/optee/smc_abi.c | 1360 +++++++++++++++++++++++++++++ 8 files changed, 1504 insertions(+), 1387 deletions(-) delete mode 100644 drivers/tee/optee/shm_pool.c delete mode 100644 drivers/tee/optee/shm_pool.h create mode 100644 drivers/tee/optee/smc_abi.c diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 3aa33ea9e6a6..97ac3ab3e1c0 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -4,8 +4,8 @@ optee-objs += core.o optee-objs += call.o optee-objs += rpc.o optee-objs += supp.o -optee-objs += shm_pool.o optee-objs += device.o +optee-objs += smc_abi.o # for tracing framework to find optee_trace.h -CFLAGS_call.o := -I$(src) +CFLAGS_smc_abi.o := -I$(src) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index ddedde45f1ee..9ff4f0812825 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -2,28 +2,17 @@ /* * Copyright (c) 2015-2021, Linaro Limited */ -#include #include #include #include #include -#include #include #include #include -#include #include "optee_private.h" -#include "optee_smc.h" -#define CREATE_TRACE_POINTS -#include "optee_trace.h" -struct optee_call_waiter { - struct list_head list_node; - struct completion c; -}; - -static void optee_cq_wait_init(struct optee_call_queue *cq, - struct optee_call_waiter *w) +void optee_cq_wait_init(struct optee_call_queue *cq, + struct optee_call_waiter *w) { /* * We're preparing to make a call to secure world. In case we can't @@ -47,8 +36,8 @@ static void optee_cq_wait_init(struct optee_call_queue *cq, mutex_unlock(&cq->mutex); } -static void optee_cq_wait_for_completion(struct optee_call_queue *cq, - struct optee_call_waiter *w) +void optee_cq_wait_for_completion(struct optee_call_queue *cq, + struct optee_call_waiter *w) { wait_for_completion(&w->c); @@ -74,8 +63,8 @@ static void optee_cq_complete_one(struct optee_call_queue *cq) } } -static void optee_cq_wait_final(struct optee_call_queue *cq, - struct optee_call_waiter *w) +void optee_cq_wait_final(struct optee_call_queue *cq, + struct optee_call_waiter *w) { /* * We're done with the call to secure world. The thread in secure @@ -115,73 +104,8 @@ static struct optee_session *find_session(struct optee_context_data *ctxdata, return NULL; } -/** - * optee_do_call_with_arg() - Do an SMC to OP-TEE in secure world - * @ctx: calling context - * @arg: shared memory holding the message to pass to secure world - * - * Does and SMC to OP-TEE in secure world and handles eventual resulting - * Remote Procedure Calls (RPC) from OP-TEE. - * - * Returns return code from secure world, 0 is OK - */ -int optee_do_call_with_arg(struct tee_context *ctx, struct tee_shm *arg) -{ - struct optee *optee = tee_get_drvdata(ctx->teedev); - struct optee_call_waiter w; - struct optee_rpc_param param = { }; - struct optee_call_ctx call_ctx = { }; - phys_addr_t parg; - int rc; - - rc = tee_shm_get_pa(arg, 0, &parg); - if (rc) - return rc; - - param.a0 = OPTEE_SMC_CALL_WITH_ARG; - reg_pair_from_64(¶m.a1, ¶m.a2, parg); - /* Initialize waiter */ - optee_cq_wait_init(&optee->call_queue, &w); - while (true) { - struct arm_smccc_res res; - - trace_optee_invoke_fn_begin(¶m); - optee->invoke_fn(param.a0, param.a1, param.a2, param.a3, - param.a4, param.a5, param.a6, param.a7, - &res); - trace_optee_invoke_fn_end(¶m, &res); - - if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) { - /* - * Out of threads in secure world, wait for a thread - * become available. - */ - optee_cq_wait_for_completion(&optee->call_queue, &w); - } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { - cond_resched(); - param.a0 = res.a0; - param.a1 = res.a1; - param.a2 = res.a2; - param.a3 = res.a3; - optee_handle_rpc(ctx, ¶m, &call_ctx); - } else { - rc = res.a0; - break; - } - } - - optee_rpc_finalize_call(&call_ctx); - /* - * We're done with our thread in secure world, if there's any - * thread waiters wake up one. - */ - optee_cq_wait_final(&optee->call_queue, &w); - - return rc; -} - -static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, - struct optee_msg_arg **msg_arg) +struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, + struct optee_msg_arg **msg_arg) { struct tee_shm *shm; struct optee_msg_arg *ma; @@ -217,7 +141,7 @@ int optee_open_session(struct tee_context *ctx, uuid_t client_uuid; /* +2 for the meta parameters added below */ - shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg); + shm = optee_get_msg_arg(ctx, arg->num_params + 2, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm); @@ -290,7 +214,7 @@ int optee_close_session_helper(struct tee_context *ctx, u32 session) struct optee *optee = tee_get_drvdata(ctx->teedev); struct optee_msg_arg *msg_arg; - shm = get_msg_arg(ctx, 0, &msg_arg); + shm = optee_get_msg_arg(ctx, 0, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm); @@ -338,7 +262,7 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, if (!sess) return -EINVAL; - shm = get_msg_arg(ctx, arg->num_params, &msg_arg); + shm = optee_get_msg_arg(ctx, arg->num_params, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm); msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; @@ -384,7 +308,7 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) if (!sess) return -EINVAL; - shm = get_msg_arg(ctx, 0, &msg_arg); + shm = optee_get_msg_arg(ctx, 0, &msg_arg); if (IS_ERR(shm)) return PTR_ERR(shm); @@ -397,182 +321,6 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) return 0; } -/** - * optee_enable_shm_cache() - Enables caching of some shared memory allocation - * in OP-TEE - * @optee: main service struct - */ -void optee_enable_shm_cache(struct optee *optee) -{ - struct optee_call_waiter w; - - /* We need to retry until secure world isn't busy. */ - optee_cq_wait_init(&optee->call_queue, &w); - while (true) { - struct arm_smccc_res res; - - optee->invoke_fn(OPTEE_SMC_ENABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, - 0, &res); - if (res.a0 == OPTEE_SMC_RETURN_OK) - break; - optee_cq_wait_for_completion(&optee->call_queue, &w); - } - optee_cq_wait_final(&optee->call_queue, &w); -} - -/** - * __optee_disable_shm_cache() - Disables caching of some shared memory - * allocation in OP-TEE - * @optee: main service struct - * @is_mapped: true if the cached shared memory addresses were mapped by this - * kernel, are safe to dereference, and should be freed - */ -static void __optee_disable_shm_cache(struct optee *optee, bool is_mapped) -{ - struct optee_call_waiter w; - - /* We need to retry until secure world isn't busy. */ - optee_cq_wait_init(&optee->call_queue, &w); - while (true) { - union { - struct arm_smccc_res smccc; - struct optee_smc_disable_shm_cache_result result; - } res; - - optee->invoke_fn(OPTEE_SMC_DISABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, - 0, &res.smccc); - if (res.result.status == OPTEE_SMC_RETURN_ENOTAVAIL) - break; /* All shm's freed */ - if (res.result.status == OPTEE_SMC_RETURN_OK) { - struct tee_shm *shm; - - /* - * Shared memory references that were not mapped by - * this kernel must be ignored to prevent a crash. - */ - if (!is_mapped) - continue; - - shm = reg_pair_to_ptr(res.result.shm_upper32, - res.result.shm_lower32); - tee_shm_free(shm); - } else { - optee_cq_wait_for_completion(&optee->call_queue, &w); - } - } - optee_cq_wait_final(&optee->call_queue, &w); -} - -/** - * optee_disable_shm_cache() - Disables caching of mapped shared memory - * allocations in OP-TEE - * @optee: main service struct - */ -void optee_disable_shm_cache(struct optee *optee) -{ - return __optee_disable_shm_cache(optee, true); -} - -/** - * optee_disable_unmapped_shm_cache() - Disables caching of shared memory - * allocations in OP-TEE which are not - * currently mapped - * @optee: main service struct - */ -void optee_disable_unmapped_shm_cache(struct optee *optee) -{ - return __optee_disable_shm_cache(optee, false); -} - -#define PAGELIST_ENTRIES_PER_PAGE \ - ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1) - -/** - * optee_fill_pages_list() - write list of user pages to given shared - * buffer. - * - * @dst: page-aligned buffer where list of pages will be stored - * @pages: array of pages that represents shared buffer - * @num_pages: number of entries in @pages - * @page_offset: offset of user buffer from page start - * - * @dst should be big enough to hold list of user page addresses and - * links to the next pages of buffer - */ -void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, - size_t page_offset) -{ - int n = 0; - phys_addr_t optee_page; - /* - * Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h - * for details. - */ - struct { - u64 pages_list[PAGELIST_ENTRIES_PER_PAGE]; - u64 next_page_data; - } *pages_data; - - /* - * Currently OP-TEE uses 4k page size and it does not looks - * like this will change in the future. On other hand, there are - * no know ARM architectures with page size < 4k. - * Thus the next built assert looks redundant. But the following - * code heavily relies on this assumption, so it is better be - * safe than sorry. - */ - BUILD_BUG_ON(PAGE_SIZE < OPTEE_MSG_NONCONTIG_PAGE_SIZE); - - pages_data = (void *)dst; - /* - * If linux page is bigger than 4k, and user buffer offset is - * larger than 4k/8k/12k/etc this will skip first 4k pages, - * because they bear no value data for OP-TEE. - */ - optee_page = page_to_phys(*pages) + - round_down(page_offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE); - - while (true) { - pages_data->pages_list[n++] = optee_page; - - if (n == PAGELIST_ENTRIES_PER_PAGE) { - pages_data->next_page_data = - virt_to_phys(pages_data + 1); - pages_data++; - n = 0; - } - - optee_page += OPTEE_MSG_NONCONTIG_PAGE_SIZE; - if (!(optee_page & ~PAGE_MASK)) { - if (!--num_pages) - break; - pages++; - optee_page = page_to_phys(*pages); - } - } -} - -/* - * The final entry in each pagelist page is a pointer to the next - * pagelist page. - */ -static size_t get_pages_list_size(size_t num_entries) -{ - int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE); - - return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE; -} - -u64 *optee_allocate_pages_list(size_t num_entries) -{ - return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL); -} - -void optee_free_pages_list(void *list, size_t num_entries) -{ - free_pages_exact(list, get_pages_list_size(num_entries)); -} - static bool is_normal_memory(pgprot_t p) { #if defined(CONFIG_ARM) @@ -596,7 +344,7 @@ static int __check_mem_type(struct vm_area_struct *vma, unsigned long end) return -EINVAL; } -static int check_mem_type(unsigned long start, size_t num_pages) +int optee_check_mem_type(unsigned long start, size_t num_pages) { struct mm_struct *mm = current->mm; int rc; @@ -615,94 +363,3 @@ static int check_mem_type(unsigned long start, size_t num_pages) return rc; } - -int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, - struct page **pages, size_t num_pages, - unsigned long start) -{ - struct optee *optee = tee_get_drvdata(ctx->teedev); - struct optee_msg_arg *msg_arg; - struct tee_shm *shm_arg; - u64 *pages_list; - int rc; - - if (!num_pages) - return -EINVAL; - - rc = check_mem_type(start, num_pages); - if (rc) - return rc; - - pages_list = optee_allocate_pages_list(num_pages); - if (!pages_list) - return -ENOMEM; - - shm_arg = get_msg_arg(ctx, 1, &msg_arg); - if (IS_ERR(shm_arg)) { - rc = PTR_ERR(shm_arg); - goto out; - } - - optee_fill_pages_list(pages_list, pages, num_pages, - tee_shm_get_page_offset(shm)); - - msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM; - msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | - OPTEE_MSG_ATTR_NONCONTIG; - msg_arg->params->u.tmem.shm_ref = (unsigned long)shm; - msg_arg->params->u.tmem.size = tee_shm_get_size(shm); - /* - * In the least bits of msg_arg->params->u.tmem.buf_ptr we - * store buffer offset from 4k page, as described in OP-TEE ABI. - */ - msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) | - (tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); - - if (optee->ops->do_call_with_arg(ctx, shm_arg) || - msg_arg->ret != TEEC_SUCCESS) - rc = -EINVAL; - - tee_shm_free(shm_arg); -out: - optee_free_pages_list(pages_list, num_pages); - return rc; -} - -int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) -{ - struct optee *optee = tee_get_drvdata(ctx->teedev); - struct optee_msg_arg *msg_arg; - struct tee_shm *shm_arg; - int rc = 0; - - shm_arg = get_msg_arg(ctx, 1, &msg_arg); - if (IS_ERR(shm_arg)) - return PTR_ERR(shm_arg); - - msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM; - - msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; - msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm; - - if (optee->ops->do_call_with_arg(ctx, shm_arg) || - msg_arg->ret != TEEC_SUCCESS) - rc = -EINVAL; - tee_shm_free(shm_arg); - return rc; -} - -int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, - struct page **pages, size_t num_pages, - unsigned long start) -{ - /* - * We don't want to register supplicant memory in OP-TEE. - * Instead information about it will be passed in RPC code. - */ - return check_mem_type(start, num_pages); -} - -int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm) -{ - return 0; -} diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 50650224e311..35d66ac383b7 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -1,260 +1,71 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2021, Linaro Limited + * Copyright (c) 2016, EPAM Systems */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include #include #include #include +#include #include -#include -#include -#include #include #include #include #include -#include #include #include "optee_private.h" -#include "optee_smc.h" -#include "shm_pool.h" -#define DRIVER_NAME "optee" - -#define OPTEE_SHM_NUM_PRIV_PAGES CONFIG_OPTEE_SHM_NUM_PRIV_PAGES - -static void from_msg_param_value(struct tee_param *p, u32 attr, - const struct optee_msg_param *mp) -{ - p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + - attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; - p->u.value.a = mp->u.value.a; - p->u.value.b = mp->u.value.b; - p->u.value.c = mp->u.value.c; -} - -static int from_msg_param_tmp_mem(struct tee_param *p, u32 attr, - const struct optee_msg_param *mp) -{ - struct tee_shm *shm; - phys_addr_t pa; - int rc; - - p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + - attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; - p->u.memref.size = mp->u.tmem.size; - shm = (struct tee_shm *)(unsigned long)mp->u.tmem.shm_ref; - if (!shm) { - p->u.memref.shm_offs = 0; - p->u.memref.shm = NULL; - return 0; - } - - rc = tee_shm_get_pa(shm, 0, &pa); - if (rc) - return rc; - - p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; - p->u.memref.shm = shm; - - /* Check that the memref is covered by the shm object */ - if (p->u.memref.size) { - size_t o = p->u.memref.shm_offs + - p->u.memref.size - 1; - - rc = tee_shm_get_pa(shm, o, NULL); - if (rc) - return rc; - } - - return 0; -} - -static void from_msg_param_reg_mem(struct tee_param *p, u32 attr, - const struct optee_msg_param *mp) +int optee_pool_op_alloc_helper(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size, + int (*shm_register)(struct tee_context *ctx, + struct tee_shm *shm, + struct page **pages, + size_t num_pages, + unsigned long start)) { - struct tee_shm *shm; + unsigned int order = get_order(size); + struct page *page; + int rc = 0; - p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + - attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; - p->u.memref.size = mp->u.rmem.size; - shm = (struct tee_shm *)(unsigned long)mp->u.rmem.shm_ref; - - if (shm) { - p->u.memref.shm_offs = mp->u.rmem.offs; - p->u.memref.shm = shm; - } else { - p->u.memref.shm_offs = 0; - p->u.memref.shm = NULL; - } -} - -/** - * optee_from_msg_param() - convert from OPTEE_MSG parameters to - * struct tee_param - * @optee: main service struct - * @params: subsystem internal parameter representation - * @num_params: number of elements in the parameter arrays - * @msg_params: OPTEE_MSG parameters - * Returns 0 on success or <0 on failure - */ -static int optee_from_msg_param(struct optee *optee, struct tee_param *params, - size_t num_params, - const struct optee_msg_param *msg_params) -{ - int rc; - size_t n; + page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); + if (!page) + return -ENOMEM; - for (n = 0; n < num_params; n++) { - struct tee_param *p = params + n; - const struct optee_msg_param *mp = msg_params + n; - u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; + shm->kaddr = page_address(page); + shm->paddr = page_to_phys(page); + shm->size = PAGE_SIZE << order; - switch (attr) { - case OPTEE_MSG_ATTR_TYPE_NONE: - p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; - memset(&p->u, 0, sizeof(p->u)); - break; - case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: - case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: - case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: - from_msg_param_value(p, attr, mp); - break; - case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: - case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: - case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: - rc = from_msg_param_tmp_mem(p, attr, mp); - if (rc) - return rc; - break; - case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: - case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: - case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: - from_msg_param_reg_mem(p, attr, mp); - break; + if (shm_register) { + unsigned int nr_pages = 1 << order, i; + struct page **pages; - default: - return -EINVAL; + pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL); + if (!pages) { + rc = -ENOMEM; + goto err; } - } - return 0; -} - -static void to_msg_param_value(struct optee_msg_param *mp, - const struct tee_param *p) -{ - mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - - TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; - mp->u.value.a = p->u.value.a; - mp->u.value.b = p->u.value.b; - mp->u.value.c = p->u.value.c; -} - -static int to_msg_param_tmp_mem(struct optee_msg_param *mp, - const struct tee_param *p) -{ - int rc; - phys_addr_t pa; - mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr - - TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; - - mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm; - mp->u.tmem.size = p->u.memref.size; + for (i = 0; i < nr_pages; i++) { + pages[i] = page; + page++; + } - if (!p->u.memref.shm) { - mp->u.tmem.buf_ptr = 0; - return 0; + shm->flags |= TEE_SHM_REGISTER; + rc = shm_register(shm->ctx, shm, pages, nr_pages, + (unsigned long)shm->kaddr); + kfree(pages); + if (rc) + goto err; } - rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa); - if (rc) - return rc; - - mp->u.tmem.buf_ptr = pa; - mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED << - OPTEE_MSG_ATTR_CACHE_SHIFT; - - return 0; -} - -static int to_msg_param_reg_mem(struct optee_msg_param *mp, - const struct tee_param *p) -{ - mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr - - TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; - - mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm; - mp->u.rmem.size = p->u.memref.size; - mp->u.rmem.offs = p->u.memref.shm_offs; - return 0; -} - -/** - * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters - * @optee: main service struct - * @msg_params: OPTEE_MSG parameters - * @num_params: number of elements in the parameter arrays - * @params: subsystem itnernal parameter representation - * Returns 0 on success or <0 on failure - */ -static int optee_to_msg_param(struct optee *optee, - struct optee_msg_param *msg_params, - size_t num_params, const struct tee_param *params) -{ - int rc; - size_t n; - - for (n = 0; n < num_params; n++) { - const struct tee_param *p = params + n; - struct optee_msg_param *mp = msg_params + n; - - switch (p->attr) { - case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: - mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; - memset(&mp->u, 0, sizeof(mp->u)); - break; - case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: - case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: - case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: - to_msg_param_value(mp, p); - break; - case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: - case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: - case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: - if (tee_shm_is_registered(p->u.memref.shm)) - rc = to_msg_param_reg_mem(mp, p); - else - rc = to_msg_param_tmp_mem(mp, p); - if (rc) - return rc; - break; - default: - return -EINVAL; - } - } return 0; -} -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; - if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) - v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL; - *vers = v; +err: + __free_pages(page, order); + return rc; } static void optee_bus_scan(struct work_struct *work) @@ -262,7 +73,7 @@ static void optee_bus_scan(struct work_struct *work) WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP)); } -static int optee_open(struct tee_context *ctx) +int optee_open(struct tee_context *ctx, bool cap_memref_null) { struct optee_context_data *ctxdata; struct tee_device *teedev = ctx->teedev; @@ -300,11 +111,7 @@ static int optee_open(struct tee_context *ctx) mutex_init(&ctxdata->mutex); INIT_LIST_HEAD(&ctxdata->sess_list); - if (optee->sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) - ctx->cap_memref_null = true; - else - ctx->cap_memref_null = false; - + ctx->cap_memref_null = cap_memref_null; ctx->data = ctxdata; return 0; } @@ -330,12 +137,12 @@ static void optee_release_helper(struct tee_context *ctx, ctx->data = NULL; } -static void optee_release(struct tee_context *ctx) +void optee_release(struct tee_context *ctx) { optee_release_helper(ctx, optee_close_session_helper); } -static void optee_release_supp(struct tee_context *ctx) +void optee_release_supp(struct tee_context *ctx) { struct optee *optee = tee_get_drvdata(ctx->teedev); @@ -347,284 +154,8 @@ static void optee_release_supp(struct tee_context *ctx) optee_supp_release(&optee->supp); } -static const struct tee_driver_ops optee_clnt_ops = { - .get_version = optee_get_version, - .open = optee_open, - .release = optee_release, - .open_session = optee_open_session, - .close_session = optee_close_session, - .invoke_func = optee_invoke_func, - .cancel_req = optee_cancel_req, - .shm_register = optee_shm_register, - .shm_unregister = optee_shm_unregister, -}; - -static const struct tee_desc optee_clnt_desc = { - .name = DRIVER_NAME "-clnt", - .ops = &optee_clnt_ops, - .owner = THIS_MODULE, -}; - -static const struct tee_driver_ops optee_supp_ops = { - .get_version = optee_get_version, - .open = optee_open, - .release = optee_release_supp, - .supp_recv = optee_supp_recv, - .supp_send = optee_supp_send, - .shm_register = optee_shm_register_supp, - .shm_unregister = optee_shm_unregister_supp, -}; - -static const struct tee_desc optee_supp_desc = { - .name = DRIVER_NAME "-supp", - .ops = &optee_supp_ops, - .owner = THIS_MODULE, - .flags = TEE_DESC_PRIVILEGED, -}; - -static const struct optee_ops optee_ops = { - .do_call_with_arg = optee_do_call_with_arg, - .to_msg_param = optee_to_msg_param, - .from_msg_param = optee_from_msg_param, -}; - -static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) -{ - struct arm_smccc_res res; - - invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); - - if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && - res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) - return true; - return false; -} - -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) -{ - union { - struct arm_smccc_res smccc; - struct optee_smc_call_get_os_revision_result result; - } res = { - .result = { - .build_id = 0 - } - }; - - invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, - &res.smccc); - - if (res.result.build_id) - pr_info("revision %lu.%lu (%08lx)", res.result.major, - res.result.minor, res.result.build_id); - else - pr_info("revision %lu.%lu", res.result.major, res.result.minor); -} - -static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) -{ - union { - struct arm_smccc_res smccc; - struct optee_smc_calls_revision_result result; - } res; - - invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); - - if (res.result.major == OPTEE_MSG_REVISION_MAJOR && - (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) - return true; - return false; -} - -static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, - u32 *sec_caps) -{ - union { - struct arm_smccc_res smccc; - struct optee_smc_exchange_capabilities_result result; - } res; - u32 a1 = 0; - - /* - * TODO This isn't enough to tell if it's UP system (from kernel - * point of view) or not, is_smp() returns the the information - * needed, but can't be called directly from here. - */ - if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) - a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; - - invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, - &res.smccc); - - if (res.result.status != OPTEE_SMC_RETURN_OK) - return false; - - *sec_caps = res.result.capabilities; - return true; -} - -static struct tee_shm_pool *optee_config_dyn_shm(void) -{ - struct tee_shm_pool_mgr *priv_mgr; - struct tee_shm_pool_mgr *dmabuf_mgr; - void *rc; - - rc = optee_shm_pool_alloc_pages(); - if (IS_ERR(rc)) - return rc; - priv_mgr = rc; - - rc = optee_shm_pool_alloc_pages(); - if (IS_ERR(rc)) { - tee_shm_pool_mgr_destroy(priv_mgr); - return rc; - } - dmabuf_mgr = rc; - - rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); - if (IS_ERR(rc)) { - tee_shm_pool_mgr_destroy(priv_mgr); - tee_shm_pool_mgr_destroy(dmabuf_mgr); - } - - return rc; -} - -static struct tee_shm_pool * -optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) +void optee_remove_common(struct optee *optee) { - union { - struct arm_smccc_res smccc; - struct optee_smc_get_shm_config_result result; - } res; - unsigned long vaddr; - phys_addr_t paddr; - size_t size; - phys_addr_t begin; - phys_addr_t end; - void *va; - struct tee_shm_pool_mgr *priv_mgr; - struct tee_shm_pool_mgr *dmabuf_mgr; - void *rc; - const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; - - invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); - if (res.result.status != OPTEE_SMC_RETURN_OK) { - pr_err("static shm service not available\n"); - return ERR_PTR(-ENOENT); - } - - if (res.result.settings != OPTEE_SMC_SHM_CACHED) { - pr_err("only normal cached shared memory supported\n"); - return ERR_PTR(-EINVAL); - } - - begin = roundup(res.result.start, PAGE_SIZE); - end = rounddown(res.result.start + res.result.size, PAGE_SIZE); - paddr = begin; - size = end - begin; - - if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) { - pr_err("too small shared memory area\n"); - return ERR_PTR(-EINVAL); - } - - va = memremap(paddr, size, MEMREMAP_WB); - if (!va) { - pr_err("shared memory ioremap failed\n"); - return ERR_PTR(-EINVAL); - } - vaddr = (unsigned long)va; - - rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, - 3 /* 8 bytes aligned */); - if (IS_ERR(rc)) - goto err_memunmap; - priv_mgr = rc; - - vaddr += sz; - paddr += sz; - size -= sz; - - rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT); - if (IS_ERR(rc)) - goto err_free_priv_mgr; - dmabuf_mgr = rc; - - rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); - if (IS_ERR(rc)) - goto err_free_dmabuf_mgr; - - *memremaped_shm = va; - - return rc; - -err_free_dmabuf_mgr: - tee_shm_pool_mgr_destroy(dmabuf_mgr); -err_free_priv_mgr: - tee_shm_pool_mgr_destroy(priv_mgr); -err_memunmap: - memunmap(va); - return rc; -} - -/* Simple wrapper functions to be able to use a function pointer */ -static void optee_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) -{ - arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); -} - -static void optee_smccc_hvc(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) -{ - arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); -} - -static optee_invoke_fn *get_invoke_func(struct device *dev) -{ - const char *method; - - pr_info("probing for conduit method.\n"); - - if (device_property_read_string(dev, "method", &method)) { - pr_warn("missing \"method\" property\n"); - return ERR_PTR(-ENXIO); - } - - if (!strcmp("hvc", method)) - return optee_smccc_hvc; - else if (!strcmp("smc", method)) - return optee_smccc_smc; - - pr_warn("invalid \"method\" property: %s\n", method); - return ERR_PTR(-EINVAL); -} - -/* optee_remove - Device Removal Routine - * @pdev: platform device information struct - * - * optee_remove is called by platform subsystem to alert the driver - * that it should release the device - */ - -static int optee_remove(struct platform_device *pdev) -{ - struct optee *optee = platform_get_drvdata(pdev); - - /* - * Ask OP-TEE to free all cached shared memory objects to decrease - * reference counters and also avoid wild pointers in secure world - * into the old shared memory range. - */ - optee_disable_shm_cache(optee); - /* * The two devices have to be unregistered before we can free the * other resources. @@ -633,39 +164,13 @@ static int optee_remove(struct platform_device *pdev) tee_device_unregister(optee->teedev); tee_shm_pool_free(optee->pool); - if (optee->memremaped_shm) - memunmap(optee->memremaped_shm); optee_wait_queue_exit(&optee->wait_queue); optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex); - - kfree(optee); - - return 0; -} - -/* optee_shutdown - Device Removal Routine - * @pdev: platform device information struct - * - * platform_shutdown is called by the platform subsystem to alert - * the driver that a shutdown, reboot, or kexec is happening and - * device must be disabled. - */ -static void optee_shutdown(struct platform_device *pdev) -{ - optee_disable_shm_cache(platform_get_drvdata(pdev)); } -static int optee_probe(struct platform_device *pdev) +static int optee_core_init(void) { - optee_invoke_fn *invoke_fn; - struct tee_shm_pool *pool = ERR_PTR(-EINVAL); - struct optee *optee = NULL; - void *memremaped_shm = NULL; - struct tee_device *teedev; - u32 sec_caps; - int rc; - /* * The kernel may have crashed at the same time that all available * secure world threads were suspended and we cannot reschedule the @@ -676,139 +181,15 @@ static int optee_probe(struct platform_device *pdev) if (is_kdump_kernel()) return -ENODEV; - invoke_fn = get_invoke_func(&pdev->dev); - if (IS_ERR(invoke_fn)) - return PTR_ERR(invoke_fn); - - if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { - pr_warn("api uid mismatch\n"); - return -EINVAL; - } - - optee_msg_get_os_revision(invoke_fn); - - if (!optee_msg_api_revision_is_compatible(invoke_fn)) { - pr_warn("api revision mismatch\n"); - return -EINVAL; - } - - if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { - pr_warn("capabilities mismatch\n"); - return -EINVAL; - } - - /* - * Try to use dynamic shared memory if possible - */ - if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) - pool = optee_config_dyn_shm(); - - /* - * If dynamic shared memory is not available or failed - try static one - */ - if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) - pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); - - if (IS_ERR(pool)) - return PTR_ERR(pool); - - optee = kzalloc(sizeof(*optee), GFP_KERNEL); - if (!optee) { - rc = -ENOMEM; - goto err; - } - - optee->ops = &optee_ops; - optee->invoke_fn = invoke_fn; - optee->sec_caps = sec_caps; - - teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); - if (IS_ERR(teedev)) { - rc = PTR_ERR(teedev); - goto err; - } - optee->teedev = teedev; - - teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); - if (IS_ERR(teedev)) { - rc = PTR_ERR(teedev); - goto err; - } - optee->supp_teedev = teedev; - - rc = tee_device_register(optee->teedev); - if (rc) - goto err; - - rc = tee_device_register(optee->supp_teedev); - if (rc) - goto err; - - mutex_init(&optee->call_queue.mutex); - INIT_LIST_HEAD(&optee->call_queue.waiters); - optee_wait_queue_init(&optee->wait_queue); - optee_supp_init(&optee->supp); - optee->memremaped_shm = memremaped_shm; - optee->pool = pool; - - /* - * Ensure that there are no pre-existing shm objects before enabling - * the shm cache so that there's no chance of receiving an invalid - * address during shutdown. This could occur, for example, if we're - * kexec booting from an older kernel that did not properly cleanup the - * shm cache. - */ - optee_disable_unmapped_shm_cache(optee); - - optee_enable_shm_cache(optee); - - if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) - pr_info("dynamic shared memory is enabled\n"); - - platform_set_drvdata(pdev, optee); - - rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); - if (rc) { - optee_remove(pdev); - return rc; - } - - pr_info("initialized driver\n"); - return 0; -err: - if (optee) { - /* - * tee_device_unregister() is safe to call even if the - * devices hasn't been registered with - * tee_device_register() yet. - */ - tee_device_unregister(optee->supp_teedev); - tee_device_unregister(optee->teedev); - kfree(optee); - } - if (pool) - tee_shm_pool_free(pool); - if (memremaped_shm) - memunmap(memremaped_shm); - return rc; + return optee_smc_abi_register(); } +module_init(optee_core_init); -static const struct of_device_id optee_dt_match[] = { - { .compatible = "linaro,optee-tz" }, - {}, -}; -MODULE_DEVICE_TABLE(of, optee_dt_match); - -static struct platform_driver optee_driver = { - .probe = optee_probe, - .remove = optee_remove, - .shutdown = optee_shutdown, - .driver = { - .name = "optee", - .of_match_table = optee_dt_match, - }, -}; -module_platform_driver(optee_driver); +static void optee_core_exit(void) +{ + optee_smc_abi_unregister(); +} +module_exit(optee_core_exit); MODULE_AUTHOR("Linaro"); MODULE_DESCRIPTION("OP-TEE driver"); diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 92d59c145d4a..ca0213e330b5 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -12,6 +12,8 @@ #include #include "optee_msg.h" +#define DRIVER_NAME "optee" + #define OPTEE_MAX_ARG_SIZE 1024 /* Some Global Platform error codes used in this driver */ @@ -29,6 +31,11 @@ typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, struct arm_smccc_res *); +struct optee_call_waiter { + struct list_head list_node; + struct completion c; +}; + struct optee_call_queue { /* Serializes access to this struct */ struct mutex mutex; @@ -66,6 +73,19 @@ struct optee_supp { struct completion reqs_c; }; +/** + * struct optee_smc - SMC ABI specifics + * @invoke_fn: function to issue smc or hvc + * @memremaped_shm virtual address of memory in shared memory pool + * @sec_caps: secure world capabilities defined by + * OPTEE_SMC_SEC_CAP_* in optee_smc.h + */ +struct optee_smc { + optee_invoke_fn *invoke_fn; + void *memremaped_shm; + u32 sec_caps; +}; + struct optee; /** @@ -95,15 +115,12 @@ struct optee_ops { * @ops: internal callbacks for different ways to reach secure * world * @teedev: client device - * @invoke_fn: function to issue smc or hvc + * @smc: specific to SMC ABI * @call_queue: queue of threads waiting to call @invoke_fn * @wait_queue: queue of threads from secure world waiting for a * secure world sync object * @supp: supplicant synchronization struct for RPC to supplicant * @pool: shared memory pool - * @memremaped_shm virtual address of memory in shared memory pool - * @sec_caps: secure world capabilities defined by - * OPTEE_SMC_SEC_CAP_* in optee_smc.h * @scan_bus_done flag if device registation was already done. * @scan_bus_wq workqueue to scan optee bus and register optee drivers * @scan_bus_work workq to scan optee bus and register optee drivers @@ -112,13 +129,11 @@ struct optee { struct tee_device *supp_teedev; struct tee_device *teedev; const struct optee_ops *ops; - optee_invoke_fn *invoke_fn; + struct optee_smc smc; struct optee_call_queue call_queue; struct optee_wait_queue wait_queue; struct optee_supp supp; struct tee_shm_pool *pool; - void *memremaped_shm; - u32 sec_caps; bool scan_bus_done; struct workqueue_struct *scan_bus_wq; struct work_struct scan_bus_work; @@ -153,10 +168,6 @@ struct optee_call_ctx { size_t num_entries; }; -void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param, - struct optee_call_ctx *call_ctx); -void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx); - void optee_wait_queue_init(struct optee_wait_queue *wq); void optee_wait_queue_exit(struct optee_wait_queue *wq); @@ -174,7 +185,6 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, struct tee_param *param); -int optee_do_call_with_arg(struct tee_context *ctx, struct tee_shm *arg); int optee_open_session(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *param); @@ -184,28 +194,58 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param); int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session); -void optee_enable_shm_cache(struct optee *optee); -void optee_disable_shm_cache(struct optee *optee); -void optee_disable_unmapped_shm_cache(struct optee *optee); +#define PTA_CMD_GET_DEVICES 0x0 +#define PTA_CMD_GET_DEVICES_SUPP 0x1 +int optee_enumerate_devices(u32 func); -int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, - struct page **pages, size_t num_pages, - unsigned long start); -int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm); +int optee_pool_op_alloc_helper(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size, + int (*shm_register)(struct tee_context *ctx, + struct tee_shm *shm, + struct page **pages, + size_t num_pages, + unsigned long start)); -int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, - struct page **pages, size_t num_pages, - unsigned long start); -int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm); -u64 *optee_allocate_pages_list(size_t num_entries); -void optee_free_pages_list(void *array, size_t num_entries); -void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, - size_t page_offset); +void optee_remove_common(struct optee *optee); +int optee_open(struct tee_context *ctx, bool cap_memref_null); +void optee_release(struct tee_context *ctx); +void optee_release_supp(struct tee_context *ctx); -#define PTA_CMD_GET_DEVICES 0x0 -#define PTA_CMD_GET_DEVICES_SUPP 0x1 -int optee_enumerate_devices(u32 func); +static inline void optee_from_msg_param_value(struct tee_param *p, u32 attr, + const struct optee_msg_param *mp) +{ + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; + p->u.value.a = mp->u.value.a; + p->u.value.b = mp->u.value.b; + p->u.value.c = mp->u.value.c; +} + +static inline void optee_to_msg_param_value(struct optee_msg_param *mp, + const struct tee_param *p) +{ + mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + mp->u.value.a = p->u.value.a; + mp->u.value.b = p->u.value.b; + mp->u.value.c = p->u.value.c; +} + +void optee_cq_wait_init(struct optee_call_queue *cq, + struct optee_call_waiter *w); +void optee_cq_wait_for_completion(struct optee_call_queue *cq, + struct optee_call_waiter *w); +void optee_cq_wait_final(struct optee_call_queue *cq, + struct optee_call_waiter *w); +int optee_check_mem_type(unsigned long start, size_t num_pages); +struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, + struct optee_msg_arg **msg_arg); + +struct tee_shm *optee_rpc_cmd_alloc_suppl(struct tee_context *ctx, size_t sz); +void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm); +void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee, + struct optee_msg_arg *arg); /* * Small helpers @@ -222,4 +262,8 @@ static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val) *reg1 = val; } +/* Registration of the ABIs */ +int optee_smc_abi_register(void); +void optee_smc_abi_unregister(void); + #endif /*OPTEE_PRIVATE_H*/ diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 309258d47790..cd642e340eaf 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -6,12 +6,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include #include #include #include #include "optee_private.h" -#include "optee_smc.h" #include "optee_rpc_cmd.h" struct wq_entry { @@ -266,7 +264,7 @@ static void handle_rpc_supp_cmd(struct tee_context *ctx, struct optee *optee, kfree(params); } -static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) +struct tee_shm *optee_rpc_cmd_alloc_suppl(struct tee_context *ctx, size_t sz) { u32 ret; struct tee_param param; @@ -289,103 +287,7 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) return shm; } -static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, - struct optee_msg_arg *arg, - struct optee_call_ctx *call_ctx) -{ - phys_addr_t pa; - struct tee_shm *shm; - size_t sz; - size_t n; - - arg->ret_origin = TEEC_ORIGIN_COMMS; - - if (!arg->num_params || - arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { - arg->ret = TEEC_ERROR_BAD_PARAMETERS; - return; - } - - for (n = 1; n < arg->num_params; n++) { - if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE) { - arg->ret = TEEC_ERROR_BAD_PARAMETERS; - return; - } - } - - sz = arg->params[0].u.value.b; - switch (arg->params[0].u.value.a) { - case OPTEE_RPC_SHM_TYPE_APPL: - shm = cmd_alloc_suppl(ctx, sz); - break; - case OPTEE_RPC_SHM_TYPE_KERNEL: - shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED | TEE_SHM_PRIV); - break; - default: - arg->ret = TEEC_ERROR_BAD_PARAMETERS; - return; - } - - if (IS_ERR(shm)) { - arg->ret = TEEC_ERROR_OUT_OF_MEMORY; - return; - } - - if (tee_shm_get_pa(shm, 0, &pa)) { - arg->ret = TEEC_ERROR_BAD_PARAMETERS; - goto bad; - } - - sz = tee_shm_get_size(shm); - - if (tee_shm_is_registered(shm)) { - struct page **pages; - u64 *pages_list; - size_t page_num; - - pages = tee_shm_get_pages(shm, &page_num); - if (!pages || !page_num) { - arg->ret = TEEC_ERROR_OUT_OF_MEMORY; - goto bad; - } - - pages_list = optee_allocate_pages_list(page_num); - if (!pages_list) { - arg->ret = TEEC_ERROR_OUT_OF_MEMORY; - goto bad; - } - - call_ctx->pages_list = pages_list; - call_ctx->num_entries = page_num; - - arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | - OPTEE_MSG_ATTR_NONCONTIG; - /* - * In the least bits of u.tmem.buf_ptr we store buffer offset - * from 4k page, as described in OP-TEE ABI. - */ - arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_list) | - (tee_shm_get_page_offset(shm) & - (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); - arg->params[0].u.tmem.size = tee_shm_get_size(shm); - arg->params[0].u.tmem.shm_ref = (unsigned long)shm; - - optee_fill_pages_list(pages_list, pages, page_num, - tee_shm_get_page_offset(shm)); - } else { - arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT; - arg->params[0].u.tmem.buf_ptr = pa; - arg->params[0].u.tmem.size = sz; - arg->params[0].u.tmem.shm_ref = (unsigned long)shm; - } - - arg->ret = TEEC_SUCCESS; - return; -bad: - tee_shm_free(shm); -} - -static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) +void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) { struct tee_param param; @@ -410,60 +312,9 @@ static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_FREE, 1, ¶m); } -static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, - struct optee_msg_arg *arg) -{ - struct tee_shm *shm; - - arg->ret_origin = TEEC_ORIGIN_COMMS; - - if (arg->num_params != 1 || - arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { - arg->ret = TEEC_ERROR_BAD_PARAMETERS; - return; - } - - shm = (struct tee_shm *)(unsigned long)arg->params[0].u.value.b; - switch (arg->params[0].u.value.a) { - case OPTEE_RPC_SHM_TYPE_APPL: - cmd_free_suppl(ctx, shm); - break; - case OPTEE_RPC_SHM_TYPE_KERNEL: - tee_shm_free(shm); - break; - default: - arg->ret = TEEC_ERROR_BAD_PARAMETERS; - } - arg->ret = TEEC_SUCCESS; -} - -static void free_pages_list(struct optee_call_ctx *call_ctx) -{ - if (call_ctx->pages_list) { - optee_free_pages_list(call_ctx->pages_list, - call_ctx->num_entries); - call_ctx->pages_list = NULL; - call_ctx->num_entries = 0; - } -} - -void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx) -{ - free_pages_list(call_ctx); -} - -static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, - struct tee_shm *shm, - struct optee_call_ctx *call_ctx) +void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee, + struct optee_msg_arg *arg) { - struct optee_msg_arg *arg; - - arg = tee_shm_get_va(shm, 0); - if (IS_ERR(arg)) { - pr_err("%s: tee_shm_get_va %p failed\n", __func__, shm); - return; - } - switch (arg->cmd) { case OPTEE_RPC_CMD_GET_TIME: handle_rpc_func_cmd_get_time(arg); @@ -474,13 +325,6 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, case OPTEE_RPC_CMD_SUSPEND: handle_rpc_func_cmd_wait(arg); break; - case OPTEE_RPC_CMD_SHM_ALLOC: - free_pages_list(call_ctx); - handle_rpc_func_cmd_shm_alloc(ctx, arg, call_ctx); - break; - case OPTEE_RPC_CMD_SHM_FREE: - handle_rpc_func_cmd_shm_free(ctx, arg); - break; case OPTEE_RPC_CMD_I2C_TRANSFER: handle_rpc_func_cmd_i2c_transfer(ctx, arg); break; @@ -489,58 +333,4 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, } } -/** - * optee_handle_rpc() - handle RPC from secure world - * @ctx: context doing the RPC - * @param: value of registers for the RPC - * @call_ctx: call context. Preserved during one OP-TEE invocation - * - * Result of RPC is written back into @param. - */ -void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param, - struct optee_call_ctx *call_ctx) -{ - struct tee_device *teedev = ctx->teedev; - struct optee *optee = tee_get_drvdata(teedev); - struct tee_shm *shm; - phys_addr_t pa; - - switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) { - case OPTEE_SMC_RPC_FUNC_ALLOC: - shm = tee_shm_alloc(ctx, param->a1, - TEE_SHM_MAPPED | TEE_SHM_PRIV); - if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) { - reg_pair_from_64(¶m->a1, ¶m->a2, pa); - reg_pair_from_64(¶m->a4, ¶m->a5, - (unsigned long)shm); - } else { - param->a1 = 0; - param->a2 = 0; - param->a4 = 0; - param->a5 = 0; - } - break; - case OPTEE_SMC_RPC_FUNC_FREE: - shm = reg_pair_to_ptr(param->a1, param->a2); - tee_shm_free(shm); - break; - case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: - /* - * A foreign interrupt was raised while secure world was - * executing, since they are handled in Linux a dummy RPC is - * performed to let Linux take the interrupt through the normal - * vector. - */ - break; - case OPTEE_SMC_RPC_FUNC_CMD: - shm = reg_pair_to_ptr(param->a1, param->a2); - handle_rpc_func_cmd(ctx, optee, shm, call_ctx); - break; - default: - pr_warn("Unknown RPC func 0x%x\n", - (u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)); - break; - } - param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; -} diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c deleted file mode 100644 index d167039af519..000000000000 --- a/drivers/tee/optee/shm_pool.c +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2015, Linaro Limited - * Copyright (c) 2017, EPAM Systems - */ -#include -#include -#include -#include -#include -#include "optee_private.h" -#include "optee_smc.h" -#include "shm_pool.h" - -static int pool_op_alloc(struct tee_shm_pool_mgr *poolm, - struct tee_shm *shm, size_t size) -{ - unsigned int order = get_order(size); - struct page *page; - int rc = 0; - - page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); - if (!page) - return -ENOMEM; - - shm->kaddr = page_address(page); - shm->paddr = page_to_phys(page); - shm->size = PAGE_SIZE << order; - - /* - * Shared memory private to the OP-TEE driver doesn't need - * to be registered with OP-TEE. - */ - if (!(shm->flags & TEE_SHM_PRIV)) { - unsigned int nr_pages = 1 << order, i; - struct page **pages; - - pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL); - if (!pages) { - rc = -ENOMEM; - goto err; - } - - for (i = 0; i < nr_pages; i++) { - pages[i] = page; - page++; - } - - shm->flags |= TEE_SHM_REGISTER; - rc = optee_shm_register(shm->ctx, shm, pages, nr_pages, - (unsigned long)shm->kaddr); - kfree(pages); - if (rc) - goto err; - } - - return 0; - -err: - __free_pages(page, order); - return rc; -} - -static void pool_op_free(struct tee_shm_pool_mgr *poolm, - struct tee_shm *shm) -{ - if (!(shm->flags & TEE_SHM_PRIV)) - optee_shm_unregister(shm->ctx, shm); - - free_pages((unsigned long)shm->kaddr, get_order(shm->size)); - shm->kaddr = NULL; -} - -static void pool_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm) -{ - kfree(poolm); -} - -static const struct tee_shm_pool_mgr_ops pool_ops = { - .alloc = pool_op_alloc, - .free = pool_op_free, - .destroy_poolmgr = pool_op_destroy_poolmgr, -}; - -/** - * optee_shm_pool_alloc_pages() - create page-based allocator pool - * - * This pool is used when OP-TEE supports dymanic SHM. In this case - * command buffers and such are allocated from kernel's own memory. - */ -struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void) -{ - struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); - - if (!mgr) - return ERR_PTR(-ENOMEM); - - mgr->ops = &pool_ops; - - return mgr; -} diff --git a/drivers/tee/optee/shm_pool.h b/drivers/tee/optee/shm_pool.h deleted file mode 100644 index 28109d991c4b..000000000000 --- a/drivers/tee/optee/shm_pool.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2015, Linaro Limited - * Copyright (c) 2016, EPAM Systems - */ - -#ifndef SHM_POOL_H -#define SHM_POOL_H - -#include - -struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void); - -#endif diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c new file mode 100644 index 000000000000..914f287e9acc --- /dev/null +++ b/drivers/tee/optee/smc_abi.c @@ -0,0 +1,1360 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, Linaro Limited + * Copyright (c) 2016, EPAM Systems + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "optee_private.h" +#include "optee_smc.h" +#include "optee_rpc_cmd.h" +#define CREATE_TRACE_POINTS +#include "optee_trace.h" + +/* + * This file implement the SMC ABI used when communicating with secure world + * OP-TEE OS via raw SMCs. + * This file is divided into the following sections: + * 1. Convert between struct tee_param and struct optee_msg_param + * 2. Low level support functions to register shared memory in secure world + * 3. Dynamic shared memory pool based on alloc_pages() + * 4. Do a normal scheduled call into secure world + * 5. Driver initialization. + */ + +#define OPTEE_SHM_NUM_PRIV_PAGES CONFIG_OPTEE_SHM_NUM_PRIV_PAGES + +/* + * 1. Convert between struct tee_param and struct optee_msg_param + * + * optee_from_msg_param() and optee_to_msg_param() are the main + * functions. + */ + +static int from_msg_param_tmp_mem(struct tee_param *p, u32 attr, + const struct optee_msg_param *mp) +{ + struct tee_shm *shm; + phys_addr_t pa; + int rc; + + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; + p->u.memref.size = mp->u.tmem.size; + shm = (struct tee_shm *)(unsigned long)mp->u.tmem.shm_ref; + if (!shm) { + p->u.memref.shm_offs = 0; + p->u.memref.shm = NULL; + return 0; + } + + rc = tee_shm_get_pa(shm, 0, &pa); + if (rc) + return rc; + + p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; + p->u.memref.shm = shm; + + /* Check that the memref is covered by the shm object */ + if (p->u.memref.size) { + size_t o = p->u.memref.shm_offs + + p->u.memref.size - 1; + + rc = tee_shm_get_pa(shm, o, NULL); + if (rc) + return rc; + } + + return 0; +} + +static void from_msg_param_reg_mem(struct tee_param *p, u32 attr, + const struct optee_msg_param *mp) +{ + struct tee_shm *shm; + + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; + p->u.memref.size = mp->u.rmem.size; + shm = (struct tee_shm *)(unsigned long)mp->u.rmem.shm_ref; + + if (shm) { + p->u.memref.shm_offs = mp->u.rmem.offs; + p->u.memref.shm = shm; + } else { + p->u.memref.shm_offs = 0; + p->u.memref.shm = NULL; + } +} + +/** + * optee_from_msg_param() - convert from OPTEE_MSG parameters to + * struct tee_param + * @optee: main service struct + * @params: subsystem internal parameter representation + * @num_params: number of elements in the parameter arrays + * @msg_params: OPTEE_MSG parameters + * Returns 0 on success or <0 on failure + */ +static int optee_from_msg_param(struct optee *optee, struct tee_param *params, + size_t num_params, + const struct optee_msg_param *msg_params) +{ + int rc; + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_param *p = params + n; + const struct optee_msg_param *mp = msg_params + n; + u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; + + switch (attr) { + case OPTEE_MSG_ATTR_TYPE_NONE: + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; + memset(&p->u, 0, sizeof(p->u)); + break; + case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: + optee_from_msg_param_value(p, attr, mp); + break; + case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: + rc = from_msg_param_tmp_mem(p, attr, mp); + if (rc) + return rc; + break; + case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: + from_msg_param_reg_mem(p, attr, mp); + break; + + default: + return -EINVAL; + } + } + return 0; +} + +static int to_msg_param_tmp_mem(struct optee_msg_param *mp, + const struct tee_param *p) +{ + int rc; + phys_addr_t pa; + + mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + + mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm; + mp->u.tmem.size = p->u.memref.size; + + if (!p->u.memref.shm) { + mp->u.tmem.buf_ptr = 0; + return 0; + } + + rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa); + if (rc) + return rc; + + mp->u.tmem.buf_ptr = pa; + mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED << + OPTEE_MSG_ATTR_CACHE_SHIFT; + + return 0; +} + +static int to_msg_param_reg_mem(struct optee_msg_param *mp, + const struct tee_param *p) +{ + mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + + mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm; + mp->u.rmem.size = p->u.memref.size; + mp->u.rmem.offs = p->u.memref.shm_offs; + return 0; +} + +/** + * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters + * @optee: main service struct + * @msg_params: OPTEE_MSG parameters + * @num_params: number of elements in the parameter arrays + * @params: subsystem itnernal parameter representation + * Returns 0 on success or <0 on failure + */ +static int optee_to_msg_param(struct optee *optee, + struct optee_msg_param *msg_params, + size_t num_params, const struct tee_param *params) +{ + int rc; + size_t n; + + for (n = 0; n < num_params; n++) { + const struct tee_param *p = params + n; + struct optee_msg_param *mp = msg_params + n; + + switch (p->attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: + mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; + memset(&mp->u, 0, sizeof(mp->u)); + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + optee_to_msg_param_value(mp, p); + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + if (tee_shm_is_registered(p->u.memref.shm)) + rc = to_msg_param_reg_mem(mp, p); + else + rc = to_msg_param_tmp_mem(mp, p); + if (rc) + return rc; + break; + default: + return -EINVAL; + } + } + return 0; +} + +/* + * 2. Low level support functions to register shared memory in secure world + * + * Functions to enable/disable shared memory caching in secure world, that + * is, lazy freeing of previously allocated shared memory. Freeing is + * performed when a request has been compled. + * + * Functions to register and unregister shared memory both for normal + * clients and for tee-supplicant. + */ + +/** + * optee_enable_shm_cache() - Enables caching of some shared memory allocation + * in OP-TEE + * @optee: main service struct + */ +static void optee_enable_shm_cache(struct optee *optee) +{ + struct optee_call_waiter w; + + /* We need to retry until secure world isn't busy. */ + optee_cq_wait_init(&optee->call_queue, &w); + while (true) { + struct arm_smccc_res res; + + optee->smc.invoke_fn(OPTEE_SMC_ENABLE_SHM_CACHE, + 0, 0, 0, 0, 0, 0, 0, &res); + if (res.a0 == OPTEE_SMC_RETURN_OK) + break; + optee_cq_wait_for_completion(&optee->call_queue, &w); + } + optee_cq_wait_final(&optee->call_queue, &w); +} + +/** + * __optee_disable_shm_cache() - Disables caching of some shared memory + * allocation in OP-TEE + * @optee: main service struct + * @is_mapped: true if the cached shared memory addresses were mapped by this + * kernel, are safe to dereference, and should be freed + */ +static void __optee_disable_shm_cache(struct optee *optee, bool is_mapped) +{ + struct optee_call_waiter w; + + /* We need to retry until secure world isn't busy. */ + optee_cq_wait_init(&optee->call_queue, &w); + while (true) { + union { + struct arm_smccc_res smccc; + struct optee_smc_disable_shm_cache_result result; + } res; + + optee->smc.invoke_fn(OPTEE_SMC_DISABLE_SHM_CACHE, + 0, 0, 0, 0, 0, 0, 0, &res.smccc); + if (res.result.status == OPTEE_SMC_RETURN_ENOTAVAIL) + break; /* All shm's freed */ + if (res.result.status == OPTEE_SMC_RETURN_OK) { + struct tee_shm *shm; + + /* + * Shared memory references that were not mapped by + * this kernel must be ignored to prevent a crash. + */ + if (!is_mapped) + continue; + + shm = reg_pair_to_ptr(res.result.shm_upper32, + res.result.shm_lower32); + tee_shm_free(shm); + } else { + optee_cq_wait_for_completion(&optee->call_queue, &w); + } + } + optee_cq_wait_final(&optee->call_queue, &w); +} + +/** + * optee_disable_shm_cache() - Disables caching of mapped shared memory + * allocations in OP-TEE + * @optee: main service struct + */ +static void optee_disable_shm_cache(struct optee *optee) +{ + return __optee_disable_shm_cache(optee, true); +} + +/** + * optee_disable_unmapped_shm_cache() - Disables caching of shared memory + * allocations in OP-TEE which are not + * currently mapped + * @optee: main service struct + */ +static void optee_disable_unmapped_shm_cache(struct optee *optee) +{ + return __optee_disable_shm_cache(optee, false); +} + +#define PAGELIST_ENTRIES_PER_PAGE \ + ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1) + +/* + * The final entry in each pagelist page is a pointer to the next + * pagelist page. + */ +static size_t get_pages_list_size(size_t num_entries) +{ + int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE); + + return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE; +} + +static u64 *optee_allocate_pages_list(size_t num_entries) +{ + return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL); +} + +static void optee_free_pages_list(void *list, size_t num_entries) +{ + free_pages_exact(list, get_pages_list_size(num_entries)); +} + +/** + * optee_fill_pages_list() - write list of user pages to given shared + * buffer. + * + * @dst: page-aligned buffer where list of pages will be stored + * @pages: array of pages that represents shared buffer + * @num_pages: number of entries in @pages + * @page_offset: offset of user buffer from page start + * + * @dst should be big enough to hold list of user page addresses and + * links to the next pages of buffer + */ +static void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, + size_t page_offset) +{ + int n = 0; + phys_addr_t optee_page; + /* + * Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h + * for details. + */ + struct { + u64 pages_list[PAGELIST_ENTRIES_PER_PAGE]; + u64 next_page_data; + } *pages_data; + + /* + * Currently OP-TEE uses 4k page size and it does not looks + * like this will change in the future. On other hand, there are + * no know ARM architectures with page size < 4k. + * Thus the next built assert looks redundant. But the following + * code heavily relies on this assumption, so it is better be + * safe than sorry. + */ + BUILD_BUG_ON(PAGE_SIZE < OPTEE_MSG_NONCONTIG_PAGE_SIZE); + + pages_data = (void *)dst; + /* + * If linux page is bigger than 4k, and user buffer offset is + * larger than 4k/8k/12k/etc this will skip first 4k pages, + * because they bear no value data for OP-TEE. + */ + optee_page = page_to_phys(*pages) + + round_down(page_offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE); + + while (true) { + pages_data->pages_list[n++] = optee_page; + + if (n == PAGELIST_ENTRIES_PER_PAGE) { + pages_data->next_page_data = + virt_to_phys(pages_data + 1); + pages_data++; + n = 0; + } + + optee_page += OPTEE_MSG_NONCONTIG_PAGE_SIZE; + if (!(optee_page & ~PAGE_MASK)) { + if (!--num_pages) + break; + pages++; + optee_page = page_to_phys(*pages); + } + } +} + +static int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages, + unsigned long start) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + struct optee_msg_arg *msg_arg; + struct tee_shm *shm_arg; + u64 *pages_list; + int rc; + + if (!num_pages) + return -EINVAL; + + rc = optee_check_mem_type(start, num_pages); + if (rc) + return rc; + + pages_list = optee_allocate_pages_list(num_pages); + if (!pages_list) + return -ENOMEM; + + shm_arg = optee_get_msg_arg(ctx, 1, &msg_arg); + if (IS_ERR(shm_arg)) { + rc = PTR_ERR(shm_arg); + goto out; + } + + optee_fill_pages_list(pages_list, pages, num_pages, + tee_shm_get_page_offset(shm)); + + msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM; + msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | + OPTEE_MSG_ATTR_NONCONTIG; + msg_arg->params->u.tmem.shm_ref = (unsigned long)shm; + msg_arg->params->u.tmem.size = tee_shm_get_size(shm); + /* + * In the least bits of msg_arg->params->u.tmem.buf_ptr we + * store buffer offset from 4k page, as described in OP-TEE ABI. + */ + msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) | + (tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); + + if (optee->ops->do_call_with_arg(ctx, shm_arg) || + msg_arg->ret != TEEC_SUCCESS) + rc = -EINVAL; + + tee_shm_free(shm_arg); +out: + optee_free_pages_list(pages_list, num_pages); + return rc; +} + +static int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + struct optee_msg_arg *msg_arg; + struct tee_shm *shm_arg; + int rc = 0; + + shm_arg = optee_get_msg_arg(ctx, 1, &msg_arg); + if (IS_ERR(shm_arg)) + return PTR_ERR(shm_arg); + + msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM; + + msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; + msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm; + + if (optee->ops->do_call_with_arg(ctx, shm_arg) || + msg_arg->ret != TEEC_SUCCESS) + rc = -EINVAL; + tee_shm_free(shm_arg); + return rc; +} + +static int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages, + unsigned long start) +{ + /* + * We don't want to register supplicant memory in OP-TEE. + * Instead information about it will be passed in RPC code. + */ + return optee_check_mem_type(start, num_pages); +} + +static int optee_shm_unregister_supp(struct tee_context *ctx, + struct tee_shm *shm) +{ + return 0; +} + +/* + * 3. Dynamic shared memory pool based on alloc_pages() + * + * Implements an OP-TEE specific shared memory pool which is used + * when dynamic shared memory is supported by secure world. + * + * The main function is optee_shm_pool_alloc_pages(). + */ + +static int pool_op_alloc(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size) +{ + /* + * Shared memory private to the OP-TEE driver doesn't need + * to be registered with OP-TEE. + */ + if (shm->flags & TEE_SHM_PRIV) + return optee_pool_op_alloc_helper(poolm, shm, size, NULL); + + return optee_pool_op_alloc_helper(poolm, shm, size, optee_shm_register); +} + +static void pool_op_free(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm) +{ + if (!(shm->flags & TEE_SHM_PRIV)) + optee_shm_unregister(shm->ctx, shm); + + free_pages((unsigned long)shm->kaddr, get_order(shm->size)); + shm->kaddr = NULL; +} + +static void pool_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm) +{ + kfree(poolm); +} + +static const struct tee_shm_pool_mgr_ops pool_ops = { + .alloc = pool_op_alloc, + .free = pool_op_free, + .destroy_poolmgr = pool_op_destroy_poolmgr, +}; + +/** + * optee_shm_pool_alloc_pages() - create page-based allocator pool + * + * This pool is used when OP-TEE supports dymanic SHM. In this case + * command buffers and such are allocated from kernel's own memory. + */ +static struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void) +{ + struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + + if (!mgr) + return ERR_PTR(-ENOMEM); + + mgr->ops = &pool_ops; + + return mgr; +} + +/* + * 4. Do a normal scheduled call into secure world + * + * The function optee_smc_do_call_with_arg() performs a normal scheduled + * call into secure world. During this call may normal world request help + * from normal world using RPCs, Remote Procedure Calls. This includes + * delivery of non-secure interrupts to for instance allow rescheduling of + * the current task. + */ + +static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, + struct optee_msg_arg *arg) +{ + struct tee_shm *shm; + + arg->ret_origin = TEEC_ORIGIN_COMMS; + + if (arg->num_params != 1 || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + shm = (struct tee_shm *)(unsigned long)arg->params[0].u.value.b; + switch (arg->params[0].u.value.a) { + case OPTEE_RPC_SHM_TYPE_APPL: + optee_rpc_cmd_free_suppl(ctx, shm); + break; + case OPTEE_RPC_SHM_TYPE_KERNEL: + tee_shm_free(shm); + break; + default: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + } + arg->ret = TEEC_SUCCESS; +} + +static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, + struct optee_msg_arg *arg, + struct optee_call_ctx *call_ctx) +{ + phys_addr_t pa; + struct tee_shm *shm; + size_t sz; + size_t n; + + arg->ret_origin = TEEC_ORIGIN_COMMS; + + if (!arg->num_params || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + for (n = 1; n < arg->num_params; n++) { + if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + } + + sz = arg->params[0].u.value.b; + switch (arg->params[0].u.value.a) { + case OPTEE_RPC_SHM_TYPE_APPL: + shm = optee_rpc_cmd_alloc_suppl(ctx, sz); + break; + case OPTEE_RPC_SHM_TYPE_KERNEL: + shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED | TEE_SHM_PRIV); + break; + default: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + if (IS_ERR(shm)) { + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + return; + } + + if (tee_shm_get_pa(shm, 0, &pa)) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + goto bad; + } + + sz = tee_shm_get_size(shm); + + if (tee_shm_is_registered(shm)) { + struct page **pages; + u64 *pages_list; + size_t page_num; + + pages = tee_shm_get_pages(shm, &page_num); + if (!pages || !page_num) { + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + goto bad; + } + + pages_list = optee_allocate_pages_list(page_num); + if (!pages_list) { + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + goto bad; + } + + call_ctx->pages_list = pages_list; + call_ctx->num_entries = page_num; + + arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | + OPTEE_MSG_ATTR_NONCONTIG; + /* + * In the least bits of u.tmem.buf_ptr we store buffer offset + * from 4k page, as described in OP-TEE ABI. + */ + arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_list) | + (tee_shm_get_page_offset(shm) & + (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); + arg->params[0].u.tmem.size = tee_shm_get_size(shm); + arg->params[0].u.tmem.shm_ref = (unsigned long)shm; + + optee_fill_pages_list(pages_list, pages, page_num, + tee_shm_get_page_offset(shm)); + } else { + arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT; + arg->params[0].u.tmem.buf_ptr = pa; + arg->params[0].u.tmem.size = sz; + arg->params[0].u.tmem.shm_ref = (unsigned long)shm; + } + + arg->ret = TEEC_SUCCESS; + return; +bad: + tee_shm_free(shm); +} + +static void free_pages_list(struct optee_call_ctx *call_ctx) +{ + if (call_ctx->pages_list) { + optee_free_pages_list(call_ctx->pages_list, + call_ctx->num_entries); + call_ctx->pages_list = NULL; + call_ctx->num_entries = 0; + } +} + +static void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx) +{ + free_pages_list(call_ctx); +} + +static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, + struct tee_shm *shm, + struct optee_call_ctx *call_ctx) +{ + struct optee_msg_arg *arg; + + arg = tee_shm_get_va(shm, 0); + if (IS_ERR(arg)) { + pr_err("%s: tee_shm_get_va %p failed\n", __func__, shm); + return; + } + + switch (arg->cmd) { + case OPTEE_RPC_CMD_SHM_ALLOC: + free_pages_list(call_ctx); + handle_rpc_func_cmd_shm_alloc(ctx, arg, call_ctx); + break; + case OPTEE_RPC_CMD_SHM_FREE: + handle_rpc_func_cmd_shm_free(ctx, arg); + break; + default: + optee_rpc_cmd(ctx, optee, arg); + } +} + +/** + * optee_handle_rpc() - handle RPC from secure world + * @ctx: context doing the RPC + * @param: value of registers for the RPC + * @call_ctx: call context. Preserved during one OP-TEE invocation + * + * Result of RPC is written back into @param. + */ +static void optee_handle_rpc(struct tee_context *ctx, + struct optee_rpc_param *param, + struct optee_call_ctx *call_ctx) +{ + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct tee_shm *shm; + phys_addr_t pa; + + switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) { + case OPTEE_SMC_RPC_FUNC_ALLOC: + shm = tee_shm_alloc(ctx, param->a1, TEE_SHM_MAPPED); + if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) { + reg_pair_from_64(¶m->a1, ¶m->a2, pa); + reg_pair_from_64(¶m->a4, ¶m->a5, + (unsigned long)shm); + } else { + param->a1 = 0; + param->a2 = 0; + param->a4 = 0; + param->a5 = 0; + } + break; + case OPTEE_SMC_RPC_FUNC_FREE: + shm = reg_pair_to_ptr(param->a1, param->a2); + tee_shm_free(shm); + break; + case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: + /* + * A foreign interrupt was raised while secure world was + * executing, since they are handled in Linux a dummy RPC is + * performed to let Linux take the interrupt through the normal + * vector. + */ + break; + case OPTEE_SMC_RPC_FUNC_CMD: + shm = reg_pair_to_ptr(param->a1, param->a2); + handle_rpc_func_cmd(ctx, optee, shm, call_ctx); + break; + default: + pr_warn("Unknown RPC func 0x%x\n", + (u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)); + break; + } + + param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; +} + +/** + * optee_smc_do_call_with_arg() - Do an SMC to OP-TEE in secure world + * @ctx: calling context + * @arg: shared memory holding the message to pass to secure world + * + * Does and SMC to OP-TEE in secure world and handles eventual resulting + * Remote Procedure Calls (RPC) from OP-TEE. + * + * Returns return code from secure world, 0 is OK + */ +static int optee_smc_do_call_with_arg(struct tee_context *ctx, + struct tee_shm *arg) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + struct optee_call_waiter w; + struct optee_rpc_param param = { }; + struct optee_call_ctx call_ctx = { }; + phys_addr_t parg; + int rc; + + rc = tee_shm_get_pa(arg, 0, &parg); + if (rc) + return rc; + + param.a0 = OPTEE_SMC_CALL_WITH_ARG; + reg_pair_from_64(¶m.a1, ¶m.a2, parg); + /* Initialize waiter */ + optee_cq_wait_init(&optee->call_queue, &w); + while (true) { + struct arm_smccc_res res; + + trace_optee_invoke_fn_begin(¶m); + optee->smc.invoke_fn(param.a0, param.a1, param.a2, param.a3, + param.a4, param.a5, param.a6, param.a7, + &res); + trace_optee_invoke_fn_end(¶m, &res); + + if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) { + /* + * Out of threads in secure world, wait for a thread + * become available. + */ + optee_cq_wait_for_completion(&optee->call_queue, &w); + } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { + cond_resched(); + param.a0 = res.a0; + param.a1 = res.a1; + param.a2 = res.a2; + param.a3 = res.a3; + optee_handle_rpc(ctx, ¶m, &call_ctx); + } else { + rc = res.a0; + break; + } + } + + optee_rpc_finalize_call(&call_ctx); + /* + * We're done with our thread in secure world, if there's any + * thread waiters wake up one. + */ + optee_cq_wait_final(&optee->call_queue, &w); + + return rc; +} + +/* + * 5. Driver initialization + * + * During driver inititialization is secure world probed to find out which + * features it supports so the driver can be initialized with a matching + * configuration. This involves for instance support for dynamic shared + * memory instead of a static memory carvout. + */ + +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->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) + v.gen_caps |= TEE_GEN_CAP_REG_MEM; + if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL) + v.gen_caps |= TEE_GEN_CAP_MEMREF_NULL; + *vers = v; +} + +static int optee_smc_open(struct tee_context *ctx) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + u32 sec_caps = optee->smc.sec_caps; + + return optee_open(ctx, sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL); +} + +static const struct tee_driver_ops optee_clnt_ops = { + .get_version = optee_get_version, + .open = optee_smc_open, + .release = optee_release, + .open_session = optee_open_session, + .close_session = optee_close_session, + .invoke_func = optee_invoke_func, + .cancel_req = optee_cancel_req, + .shm_register = optee_shm_register, + .shm_unregister = optee_shm_unregister, +}; + +static const struct tee_desc optee_clnt_desc = { + .name = DRIVER_NAME "-clnt", + .ops = &optee_clnt_ops, + .owner = THIS_MODULE, +}; + +static const struct tee_driver_ops optee_supp_ops = { + .get_version = optee_get_version, + .open = optee_smc_open, + .release = optee_release_supp, + .supp_recv = optee_supp_recv, + .supp_send = optee_supp_send, + .shm_register = optee_shm_register_supp, + .shm_unregister = optee_shm_unregister_supp, +}; + +static const struct tee_desc optee_supp_desc = { + .name = DRIVER_NAME "-supp", + .ops = &optee_supp_ops, + .owner = THIS_MODULE, + .flags = TEE_DESC_PRIVILEGED, +}; + +static const struct optee_ops optee_ops = { + .do_call_with_arg = optee_smc_do_call_with_arg, + .to_msg_param = optee_to_msg_param, + .from_msg_param = optee_from_msg_param, +}; + +static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) +{ + struct arm_smccc_res res; + + invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); + + if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && + res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) + return true; + return false; +} + +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) +{ + union { + struct arm_smccc_res smccc; + struct optee_smc_call_get_os_revision_result result; + } res = { + .result = { + .build_id = 0 + } + }; + + invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, + &res.smccc); + + if (res.result.build_id) + pr_info("revision %lu.%lu (%08lx)", res.result.major, + res.result.minor, res.result.build_id); + else + pr_info("revision %lu.%lu", res.result.major, res.result.minor); +} + +static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) +{ + union { + struct arm_smccc_res smccc; + struct optee_smc_calls_revision_result result; + } res; + + invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); + + if (res.result.major == OPTEE_MSG_REVISION_MAJOR && + (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) + return true; + return false; +} + +static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, + u32 *sec_caps) +{ + union { + struct arm_smccc_res smccc; + struct optee_smc_exchange_capabilities_result result; + } res; + u32 a1 = 0; + + /* + * TODO This isn't enough to tell if it's UP system (from kernel + * point of view) or not, is_smp() returns the information + * needed, but can't be called directly from here. + */ + if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) + a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; + + invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, + &res.smccc); + + if (res.result.status != OPTEE_SMC_RETURN_OK) + return false; + + *sec_caps = res.result.capabilities; + return true; +} + +static struct tee_shm_pool *optee_config_dyn_shm(void) +{ + struct tee_shm_pool_mgr *priv_mgr; + struct tee_shm_pool_mgr *dmabuf_mgr; + void *rc; + + rc = optee_shm_pool_alloc_pages(); + if (IS_ERR(rc)) + return rc; + priv_mgr = rc; + + rc = optee_shm_pool_alloc_pages(); + if (IS_ERR(rc)) { + tee_shm_pool_mgr_destroy(priv_mgr); + return rc; + } + dmabuf_mgr = rc; + + rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); + if (IS_ERR(rc)) { + tee_shm_pool_mgr_destroy(priv_mgr); + tee_shm_pool_mgr_destroy(dmabuf_mgr); + } + + return rc; +} + +static struct tee_shm_pool * +optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) +{ + union { + struct arm_smccc_res smccc; + struct optee_smc_get_shm_config_result result; + } res; + unsigned long vaddr; + phys_addr_t paddr; + size_t size; + phys_addr_t begin; + phys_addr_t end; + void *va; + struct tee_shm_pool_mgr *priv_mgr; + struct tee_shm_pool_mgr *dmabuf_mgr; + void *rc; + const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; + + invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); + if (res.result.status != OPTEE_SMC_RETURN_OK) { + pr_err("static shm service not available\n"); + return ERR_PTR(-ENOENT); + } + + if (res.result.settings != OPTEE_SMC_SHM_CACHED) { + pr_err("only normal cached shared memory supported\n"); + return ERR_PTR(-EINVAL); + } + + begin = roundup(res.result.start, PAGE_SIZE); + end = rounddown(res.result.start + res.result.size, PAGE_SIZE); + paddr = begin; + size = end - begin; + + if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) { + pr_err("too small shared memory area\n"); + return ERR_PTR(-EINVAL); + } + + va = memremap(paddr, size, MEMREMAP_WB); + if (!va) { + pr_err("shared memory ioremap failed\n"); + return ERR_PTR(-EINVAL); + } + vaddr = (unsigned long)va; + + rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, + 3 /* 8 bytes aligned */); + if (IS_ERR(rc)) + goto err_memunmap; + priv_mgr = rc; + + vaddr += sz; + paddr += sz; + size -= sz; + + rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT); + if (IS_ERR(rc)) + goto err_free_priv_mgr; + dmabuf_mgr = rc; + + rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); + if (IS_ERR(rc)) + goto err_free_dmabuf_mgr; + + *memremaped_shm = va; + + return rc; + +err_free_dmabuf_mgr: + tee_shm_pool_mgr_destroy(dmabuf_mgr); +err_free_priv_mgr: + tee_shm_pool_mgr_destroy(priv_mgr); +err_memunmap: + memunmap(va); + return rc; +} + +/* Simple wrapper functions to be able to use a function pointer */ +static void optee_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) +{ + arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); +} + +static void optee_smccc_hvc(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) +{ + arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); +} + +static optee_invoke_fn *get_invoke_func(struct device *dev) +{ + const char *method; + + pr_info("probing for conduit method.\n"); + + if (device_property_read_string(dev, "method", &method)) { + pr_warn("missing \"method\" property\n"); + return ERR_PTR(-ENXIO); + } + + if (!strcmp("hvc", method)) + return optee_smccc_hvc; + else if (!strcmp("smc", method)) + return optee_smccc_smc; + + pr_warn("invalid \"method\" property: %s\n", method); + return ERR_PTR(-EINVAL); +} + +/* optee_remove - Device Removal Routine + * @pdev: platform device information struct + * + * optee_remove is called by platform subsystem to alert the driver + * that it should release the device + */ +static int optee_smc_remove(struct platform_device *pdev) +{ + struct optee *optee = platform_get_drvdata(pdev); + + /* + * Ask OP-TEE to free all cached shared memory objects to decrease + * reference counters and also avoid wild pointers in secure world + * into the old shared memory range. + */ + optee_disable_shm_cache(optee); + + optee_remove_common(optee); + + if (optee->smc.memremaped_shm) + memunmap(optee->smc.memremaped_shm); + + kfree(optee); + + return 0; +} + +/* optee_shutdown - Device Removal Routine + * @pdev: platform device information struct + * + * platform_shutdown is called by the platform subsystem to alert + * the driver that a shutdown, reboot, or kexec is happening and + * device must be disabled. + */ +static void optee_shutdown(struct platform_device *pdev) +{ + optee_disable_shm_cache(platform_get_drvdata(pdev)); +} + +static int optee_probe(struct platform_device *pdev) +{ + optee_invoke_fn *invoke_fn; + struct tee_shm_pool *pool = ERR_PTR(-EINVAL); + struct optee *optee = NULL; + void *memremaped_shm = NULL; + struct tee_device *teedev; + u32 sec_caps; + int rc; + + invoke_fn = get_invoke_func(&pdev->dev); + if (IS_ERR(invoke_fn)) + return PTR_ERR(invoke_fn); + + if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { + pr_warn("api uid mismatch\n"); + return -EINVAL; + } + + optee_msg_get_os_revision(invoke_fn); + + if (!optee_msg_api_revision_is_compatible(invoke_fn)) { + pr_warn("api revision mismatch\n"); + return -EINVAL; + } + + if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { + pr_warn("capabilities mismatch\n"); + return -EINVAL; + } + + /* + * Try to use dynamic shared memory if possible + */ + if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) + pool = optee_config_dyn_shm(); + + /* + * If dynamic shared memory is not available or failed - try static one + */ + if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) + pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); + + if (IS_ERR(pool)) + return PTR_ERR(pool); + + optee = kzalloc(sizeof(*optee), GFP_KERNEL); + if (!optee) { + rc = -ENOMEM; + goto err; + } + + optee->ops = &optee_ops; + optee->smc.invoke_fn = invoke_fn; + optee->smc.sec_caps = sec_caps; + + teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); + if (IS_ERR(teedev)) { + rc = PTR_ERR(teedev); + goto err; + } + optee->teedev = teedev; + + teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); + if (IS_ERR(teedev)) { + rc = PTR_ERR(teedev); + goto err; + } + optee->supp_teedev = teedev; + + rc = tee_device_register(optee->teedev); + if (rc) + goto err; + + rc = tee_device_register(optee->supp_teedev); + if (rc) + goto err; + + mutex_init(&optee->call_queue.mutex); + INIT_LIST_HEAD(&optee->call_queue.waiters); + optee_wait_queue_init(&optee->wait_queue); + optee_supp_init(&optee->supp); + optee->smc.memremaped_shm = memremaped_shm; + optee->pool = pool; + + /* + * Ensure that there are no pre-existing shm objects before enabling + * the shm cache so that there's no chance of receiving an invalid + * address during shutdown. This could occur, for example, if we're + * kexec booting from an older kernel that did not properly cleanup the + * shm cache. + */ + optee_disable_unmapped_shm_cache(optee); + + optee_enable_shm_cache(optee); + + if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) + pr_info("dynamic shared memory is enabled\n"); + + platform_set_drvdata(pdev, optee); + + rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); + if (rc) { + optee_smc_remove(pdev); + return rc; + } + + pr_info("initialized driver\n"); + return 0; +err: + if (optee) { + /* + * tee_device_unregister() is safe to call even if the + * devices hasn't been registered with + * tee_device_register() yet. + */ + tee_device_unregister(optee->supp_teedev); + tee_device_unregister(optee->teedev); + kfree(optee); + } + if (pool) + tee_shm_pool_free(pool); + if (memremaped_shm) + memunmap(memremaped_shm); + return rc; +} + +static const struct of_device_id optee_dt_match[] = { + { .compatible = "linaro,optee-tz" }, + {}, +}; +MODULE_DEVICE_TABLE(of, optee_dt_match); + +static struct platform_driver optee_driver = { + .probe = optee_probe, + .remove = optee_smc_remove, + .shutdown = optee_shutdown, + .driver = { + .name = "optee", + .of_match_table = optee_dt_match, + }, +}; + +int optee_smc_abi_register(void) +{ + return platform_driver_register(&optee_driver); +} + +void optee_smc_abi_unregister(void) +{ + platform_driver_unregister(&optee_driver); +} From patchwork Wed Oct 6 07:09:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jens Wiklander X-Patchwork-Id: 12538753 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4EFBAC433EF for ; Wed, 6 Oct 2021 07:12:10 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1C55A610C7 for ; Wed, 6 Oct 2021 07:12:10 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 1C55A610C7 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linaro.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=xIhAnqO9cUNMb+MkTH9+gyl7xnKThZF4C/+MuEhB/zY=; b=ZQ7o/qCOg+sSc/ 5jnvVr7t2Xi7uSvNUHlWN23DBEMP5x9Qt02Ju4+4qarfCDZ6hC6tNCSQetZXeShzWr3laeE2bBYpT rjHDkhq60n/67tUaYzLZKyFW9lV/7h1UpYlsobtaTKYOvGmQZHJi/8/aeTRGMdV58DptThV8H2rj1 81/h5cjBUmrsw/loa52Azw2xPl3nIaMdWRwlJOW9Fnu95RofKR5GpcKsFKAmDjvFY8HTzFIACAkpf DqjNqTGLNaOJPrwpVskWqZzukEYnVVuaBsGu15Z0p/psz1tJ+llxT/rUQcDclWI7/+aiM+gyJtQOZ VYPzmF6y8colKth/i3lA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mY141-00D8T2-5Z; Wed, 06 Oct 2021 07:10:05 +0000 Received: from mail-lf1-x12d.google.com ([2a00:1450:4864:20::12d]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mY13C-00D8GN-Rm for linux-arm-kernel@lists.infradead.org; Wed, 06 Oct 2021 07:09:19 +0000 Received: by mail-lf1-x12d.google.com with SMTP id j5so6249411lfg.8 for ; Wed, 06 Oct 2021 00:09:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=tYpPD7h4eUX3Vx3Vn+9wItsk7SJ1RmvHRPgdj19EVYY=; b=Py1ocog10mZEKj9OTNNOM9SS7n5Fy8nazNs75dzyD7oRR+lhCmUS8pRSid3Tmrj1UW FH3vPFEqKqSCswBYW7bTUAakcI/YhpLKNtRjd8shlphTdpQdXIZOTCJg+cnotZlO0gpn EItHvVFUDLTWZ5XtUtwsbNfCMqJx4B8FjFLnTRqf4u7gS8DLO0tnT0QazJzx9MKU0AGe fah6LwHPkhOmPmYw/BaTUtaE2sWlUaGCDuHc76fV7qwsFcWhwpzjXQ0FCMQnOSnusSkU d1a0TGk5ZpFvChkStZMSOUcWtoe3aJoYyfF355gc4jP8OKR1DP8IFhtbDkOHG+eClR5I zKFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=tYpPD7h4eUX3Vx3Vn+9wItsk7SJ1RmvHRPgdj19EVYY=; b=D9cccf9LqRbJ07TVdbT+R188SAJv1a59leuXJVpMDh+hHsGWsfaq7LcXjz67ODJ0OI dWHyoDHOnxT0pDMN2dpJx+IQCBG6Auyj6U5yvj/Gc8N5GKUYk7azb8dJNcsqGyQbXDQC 5XGdzV5pxMba0zuHqsujffC3cB1jc23sZauKLgbNRXhDOLqBkfI9sdmNY4wZlF7ubeEs CgmeX3fmYuqenMwM3vQDwghU2LdUbtr5Hk/B1nLvDbt+cZCgoPROhL66s6WWEO4WrmsF Uv83g+Qun3tAAcjnLX9YlMT2SSpRASZ+NsNnhiueJ6k0O0B17fgGswyFi0Q9uuVU9Zzn lgGg== X-Gm-Message-State: AOAM530UfIQvd2HoK24xSj42+1gXINi48FVGDqoesguLQNydJx26s6zZ 3bpAJoKEMbSgvvJgYEhjSubXng== X-Google-Smtp-Source: ABdhPJyLpOwb/VDspCjFJlB6JaSWd/r3xnlokciv59jr2urENIccMuC7YP2xpmj0EeGrsuLdyJhfbg== X-Received: by 2002:ac2:4835:: with SMTP id 21mr4482518lft.108.1633504152971; Wed, 06 Oct 2021 00:09:12 -0700 (PDT) Received: from jade.ideon.se ([85.235.10.227]) by smtp.gmail.com with ESMTPSA id u15sm2381032lfl.20.2021.10.06.00.09.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Oct 2021 00:09:12 -0700 (PDT) From: Jens Wiklander To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, op-tee@lists.trustedfirmware.org Cc: Sumit Garg , Sudeep Holla , Marc Bonnici , Jerome Forissier , sughosh.ganu@linaro.org, Jens Wiklander Subject: [PATCH v6 5/5] optee: add FF-A support Date: Wed, 6 Oct 2021 09:09:02 +0200 Message-Id: <20211006070902.2531311-6-jens.wiklander@linaro.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211006070902.2531311-1-jens.wiklander@linaro.org> References: <20211006070902.2531311-1-jens.wiklander@linaro.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20211006_000915_225425_9A6C9D89 X-CRM114-Status: GOOD ( 33.10 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Adds support for using FF-A [1] as transport to the OP-TEE driver. Introduces struct optee_msg_param_fmem which carries all information needed when OP-TEE is calling FFA_MEM_RETRIEVE_REQ to get the shared memory reference mapped by the hypervisor in S-EL2. Register usage is also updated to include the information needed. The FF-A part of this driver is enabled if CONFIG_ARM_FFA_TRANSPORT is enabled. [1] https://developer.arm.com/documentation/den0077/latest Acked-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/Makefile | 1 + drivers/tee/optee/call.c | 13 +- drivers/tee/optee/core.c | 16 +- drivers/tee/optee/ffa_abi.c | 911 ++++++++++++++++++++++++++++++ drivers/tee/optee/optee_ffa.h | 153 +++++ drivers/tee/optee/optee_msg.h | 27 +- drivers/tee/optee/optee_private.h | 35 +- 7 files changed, 1143 insertions(+), 13 deletions(-) create mode 100644 drivers/tee/optee/ffa_abi.c create mode 100644 drivers/tee/optee/optee_ffa.h diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 97ac3ab3e1c0..66b8a17f14c4 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -6,6 +6,7 @@ optee-objs += rpc.o optee-objs += supp.o optee-objs += device.o optee-objs += smc_abi.o +optee-objs += ffa_abi.o # for tracing framework to find optee_trace.h CFLAGS_smc_abi.o := -I$(src) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index 9ff4f0812825..b25cc1fac945 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -107,11 +107,20 @@ static struct optee_session *find_session(struct optee_context_data *ctxdata, struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params, struct optee_msg_arg **msg_arg) { + struct optee *optee = tee_get_drvdata(ctx->teedev); + size_t sz = OPTEE_MSG_GET_ARG_SIZE(num_params); struct tee_shm *shm; struct optee_msg_arg *ma; - shm = tee_shm_alloc(ctx, OPTEE_MSG_GET_ARG_SIZE(num_params), - TEE_SHM_MAPPED | TEE_SHM_PRIV); + /* + * rpc_arg_count is set to the number of allocated parameters in + * the RPC argument struct if a second MSG arg struct is expected. + * The second arg struct will then be used for RPC. + */ + if (optee->rpc_arg_count) + sz += OPTEE_MSG_GET_ARG_SIZE(optee->rpc_arg_count); + + shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED | TEE_SHM_PRIV); if (IS_ERR(shm)) return shm; diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 35d66ac383b7..faf215369e2f 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -169,6 +169,9 @@ void optee_remove_common(struct optee *optee) mutex_destroy(&optee->call_queue.mutex); } +static int smc_abi_rc; +static int ffa_abi_rc; + static int optee_core_init(void) { /* @@ -181,13 +184,22 @@ static int optee_core_init(void) if (is_kdump_kernel()) return -ENODEV; - return optee_smc_abi_register(); + smc_abi_rc = optee_smc_abi_register(); + ffa_abi_rc = optee_ffa_abi_register(); + + /* If both failed there's no point with this module */ + if (smc_abi_rc && ffa_abi_rc) + return smc_abi_rc; + return 0; } module_init(optee_core_init); static void optee_core_exit(void) { - optee_smc_abi_unregister(); + if (!smc_abi_rc) + optee_smc_abi_unregister(); + if (!ffa_abi_rc) + optee_ffa_abi_unregister(); } module_exit(optee_core_exit); diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c new file mode 100644 index 000000000000..6defd1ec982a --- /dev/null +++ b/drivers/tee/optee/ffa_abi.c @@ -0,0 +1,911 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, Linaro Limited + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "optee_private.h" +#include "optee_ffa.h" +#include "optee_rpc_cmd.h" + +/* + * This file implement the FF-A ABI used when communicating with secure world + * OP-TEE OS via FF-A. + * This file is divided into the following sections: + * 1. Maintain a hash table for lookup of a global FF-A memory handle + * 2. Convert between struct tee_param and struct optee_msg_param + * 3. Low level support functions to register shared memory in secure world + * 4. Dynamic shared memory pool based on alloc_pages() + * 5. Do a normal scheduled call into secure world + * 6. Driver initialization. + */ + +/* + * 1. Maintain a hash table for lookup of a global FF-A memory handle + * + * FF-A assigns a global memory handle for each piece shared memory. + * This handle is then used when communicating with secure world. + * + * Main functions are optee_shm_add_ffa_handle() and optee_shm_rem_ffa_handle() + */ +struct shm_rhash { + struct tee_shm *shm; + u64 global_id; + struct rhash_head linkage; +}; + +static void rh_free_fn(void *ptr, void *arg) +{ + kfree(ptr); +} + +static const struct rhashtable_params shm_rhash_params = { + .head_offset = offsetof(struct shm_rhash, linkage), + .key_len = sizeof(u64), + .key_offset = offsetof(struct shm_rhash, global_id), + .automatic_shrinking = true, +}; + +static struct tee_shm *optee_shm_from_ffa_handle(struct optee *optee, + u64 global_id) +{ + struct tee_shm *shm = NULL; + struct shm_rhash *r; + + mutex_lock(&optee->ffa.mutex); + r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id, + shm_rhash_params); + if (r) + shm = r->shm; + mutex_unlock(&optee->ffa.mutex); + + return shm; +} + +static int optee_shm_add_ffa_handle(struct optee *optee, struct tee_shm *shm, + u64 global_id) +{ + struct shm_rhash *r; + int rc; + + r = kmalloc(sizeof(*r), GFP_KERNEL); + if (!r) + return -ENOMEM; + r->shm = shm; + r->global_id = global_id; + + mutex_lock(&optee->ffa.mutex); + rc = rhashtable_lookup_insert_fast(&optee->ffa.global_ids, &r->linkage, + shm_rhash_params); + mutex_unlock(&optee->ffa.mutex); + + if (rc) + kfree(r); + + return rc; +} + +static int optee_shm_rem_ffa_handle(struct optee *optee, u64 global_id) +{ + struct shm_rhash *r; + int rc = -ENOENT; + + mutex_lock(&optee->ffa.mutex); + r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id, + shm_rhash_params); + if (r) + rc = rhashtable_remove_fast(&optee->ffa.global_ids, + &r->linkage, shm_rhash_params); + mutex_unlock(&optee->ffa.mutex); + + if (!rc) + kfree(r); + + return rc; +} + +/* + * 2. Convert between struct tee_param and struct optee_msg_param + * + * optee_ffa_from_msg_param() and optee_ffa_to_msg_param() are the main + * functions. + */ + +static void from_msg_param_ffa_mem(struct optee *optee, struct tee_param *p, + u32 attr, const struct optee_msg_param *mp) +{ + struct tee_shm *shm = NULL; + u64 offs_high = 0; + u64 offs_low = 0; + + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_FMEM_INPUT; + p->u.memref.size = mp->u.fmem.size; + + if (mp->u.fmem.global_id != OPTEE_MSG_FMEM_INVALID_GLOBAL_ID) + shm = optee_shm_from_ffa_handle(optee, mp->u.fmem.global_id); + p->u.memref.shm = shm; + + if (shm) { + offs_low = mp->u.fmem.offs_low; + offs_high = mp->u.fmem.offs_high; + } + p->u.memref.shm_offs = offs_low | offs_high << 32; +} + +/** + * optee_ffa_from_msg_param() - convert from OPTEE_MSG parameters to + * struct tee_param + * @optee: main service struct + * @params: subsystem internal parameter representation + * @num_params: number of elements in the parameter arrays + * @msg_params: OPTEE_MSG parameters + * + * Returns 0 on success or <0 on failure + */ +static int optee_ffa_from_msg_param(struct optee *optee, + struct tee_param *params, size_t num_params, + const struct optee_msg_param *msg_params) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_param *p = params + n; + const struct optee_msg_param *mp = msg_params + n; + u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; + + switch (attr) { + case OPTEE_MSG_ATTR_TYPE_NONE: + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; + memset(&p->u, 0, sizeof(p->u)); + break; + case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: + optee_from_msg_param_value(p, attr, mp); + break; + case OPTEE_MSG_ATTR_TYPE_FMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_FMEM_INOUT: + from_msg_param_ffa_mem(optee, p, attr, mp); + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static int to_msg_param_ffa_mem(struct optee_msg_param *mp, + const struct tee_param *p) +{ + struct tee_shm *shm = p->u.memref.shm; + + mp->attr = OPTEE_MSG_ATTR_TYPE_FMEM_INPUT + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + + if (shm) { + u64 shm_offs = p->u.memref.shm_offs; + + mp->u.fmem.internal_offs = shm->offset; + + mp->u.fmem.offs_low = shm_offs; + mp->u.fmem.offs_high = shm_offs >> 32; + /* Check that the entire offset could be stored. */ + if (mp->u.fmem.offs_high != shm_offs >> 32) + return -EINVAL; + + mp->u.fmem.global_id = shm->sec_world_id; + } else { + memset(&mp->u, 0, sizeof(mp->u)); + mp->u.fmem.global_id = OPTEE_MSG_FMEM_INVALID_GLOBAL_ID; + } + mp->u.fmem.size = p->u.memref.size; + + return 0; +} + +/** + * optee_ffa_to_msg_param() - convert from struct tee_params to OPTEE_MSG + * parameters + * @optee: main service struct + * @msg_params: OPTEE_MSG parameters + * @num_params: number of elements in the parameter arrays + * @params: subsystem itnernal parameter representation + * Returns 0 on success or <0 on failure + */ +static int optee_ffa_to_msg_param(struct optee *optee, + struct optee_msg_param *msg_params, + size_t num_params, + const struct tee_param *params) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + const struct tee_param *p = params + n; + struct optee_msg_param *mp = msg_params + n; + + switch (p->attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: + mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; + memset(&mp->u, 0, sizeof(mp->u)); + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + optee_to_msg_param_value(mp, p); + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + if (to_msg_param_ffa_mem(mp, p)) + return -EINVAL; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +/* + * 3. Low level support functions to register shared memory in secure world + * + * Functions to register and unregister shared memory both for normal + * clients and for tee-supplicant. + */ + +static int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm, + struct page **pages, size_t num_pages, + unsigned long start) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops; + struct ffa_device *ffa_dev = optee->ffa.ffa_dev; + struct ffa_mem_region_attributes mem_attr = { + .receiver = ffa_dev->vm_id, + .attrs = FFA_MEM_RW, + }; + struct ffa_mem_ops_args args = { + .use_txbuf = true, + .attrs = &mem_attr, + .nattrs = 1, + }; + struct sg_table sgt; + int rc; + + rc = optee_check_mem_type(start, num_pages); + if (rc) + return rc; + + rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, 0, + num_pages * PAGE_SIZE, GFP_KERNEL); + if (rc) + return rc; + args.sg = sgt.sgl; + rc = ffa_ops->memory_share(ffa_dev, &args); + sg_free_table(&sgt); + if (rc) + return rc; + + rc = optee_shm_add_ffa_handle(optee, shm, args.g_handle); + if (rc) { + ffa_ops->memory_reclaim(args.g_handle, 0); + return rc; + } + + shm->sec_world_id = args.g_handle; + + return 0; +} + +static int optee_ffa_shm_unregister(struct tee_context *ctx, + struct tee_shm *shm) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops; + struct ffa_device *ffa_dev = optee->ffa.ffa_dev; + u64 global_handle = shm->sec_world_id; + struct ffa_send_direct_data data = { + .data0 = OPTEE_FFA_UNREGISTER_SHM, + .data1 = (u32)global_handle, + .data2 = (u32)(global_handle >> 32) + }; + int rc; + + optee_shm_rem_ffa_handle(optee, global_handle); + shm->sec_world_id = 0; + + rc = ffa_ops->sync_send_receive(ffa_dev, &data); + if (rc) + pr_err("Unregister SHM id 0x%llx rc %d\n", global_handle, rc); + + rc = ffa_ops->memory_reclaim(global_handle, 0); + if (rc) + pr_err("mem_reclain: 0x%llx %d", global_handle, rc); + + return rc; +} + +static int optee_ffa_shm_unregister_supp(struct tee_context *ctx, + struct tee_shm *shm) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops; + u64 global_handle = shm->sec_world_id; + int rc; + + /* + * We're skipping the OPTEE_FFA_YIELDING_CALL_UNREGISTER_SHM call + * since this is OP-TEE freeing via RPC so it has already retired + * this ID. + */ + + optee_shm_rem_ffa_handle(optee, global_handle); + rc = ffa_ops->memory_reclaim(global_handle, 0); + if (rc) + pr_err("mem_reclain: 0x%llx %d", global_handle, rc); + + shm->sec_world_id = 0; + + return rc; +} + +/* + * 4. Dynamic shared memory pool based on alloc_pages() + * + * Implements an OP-TEE specific shared memory pool. + * The main function is optee_ffa_shm_pool_alloc_pages(). + */ + +static int pool_ffa_op_alloc(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size) +{ + return optee_pool_op_alloc_helper(poolm, shm, size, + optee_ffa_shm_register); +} + +static void pool_ffa_op_free(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm) +{ + optee_ffa_shm_unregister(shm->ctx, shm); + free_pages((unsigned long)shm->kaddr, get_order(shm->size)); + shm->kaddr = NULL; +} + +static void pool_ffa_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm) +{ + kfree(poolm); +} + +static const struct tee_shm_pool_mgr_ops pool_ffa_ops = { + .alloc = pool_ffa_op_alloc, + .free = pool_ffa_op_free, + .destroy_poolmgr = pool_ffa_op_destroy_poolmgr, +}; + +/** + * optee_ffa_shm_pool_alloc_pages() - create page-based allocator pool + * + * This pool is used with OP-TEE over FF-A. In this case command buffers + * and such are allocated from kernel's own memory. + */ +static struct tee_shm_pool_mgr *optee_ffa_shm_pool_alloc_pages(void) +{ + struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + + if (!mgr) + return ERR_PTR(-ENOMEM); + + mgr->ops = &pool_ffa_ops; + + return mgr; +} + +/* + * 5. Do a normal scheduled call into secure world + * + * The function optee_ffa_do_call_with_arg() performs a normal scheduled + * call into secure world. During this call may normal world request help + * from normal world using RPCs, Remote Procedure Calls. This includes + * delivery of non-secure interrupts to for instance allow rescheduling of + * the current task. + */ + +static void handle_ffa_rpc_func_cmd_shm_alloc(struct tee_context *ctx, + struct optee_msg_arg *arg) +{ + struct tee_shm *shm; + + if (arg->num_params != 1 || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + switch (arg->params[0].u.value.a) { + case OPTEE_RPC_SHM_TYPE_APPL: + shm = optee_rpc_cmd_alloc_suppl(ctx, arg->params[0].u.value.b); + break; + case OPTEE_RPC_SHM_TYPE_KERNEL: + shm = tee_shm_alloc(ctx, arg->params[0].u.value.b, + TEE_SHM_MAPPED | TEE_SHM_PRIV); + break; + default: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + if (IS_ERR(shm)) { + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + return; + } + + arg->params[0] = (struct optee_msg_param){ + .attr = OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT, + .u.fmem.size = tee_shm_get_size(shm), + .u.fmem.global_id = shm->sec_world_id, + .u.fmem.internal_offs = shm->offset, + }; + + arg->ret = TEEC_SUCCESS; +} + +static void handle_ffa_rpc_func_cmd_shm_free(struct tee_context *ctx, + struct optee *optee, + struct optee_msg_arg *arg) +{ + struct tee_shm *shm; + + if (arg->num_params != 1 || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) + goto err_bad_param; + + shm = optee_shm_from_ffa_handle(optee, arg->params[0].u.value.b); + if (!shm) + goto err_bad_param; + switch (arg->params[0].u.value.a) { + case OPTEE_RPC_SHM_TYPE_APPL: + optee_rpc_cmd_free_suppl(ctx, shm); + break; + case OPTEE_RPC_SHM_TYPE_KERNEL: + tee_shm_free(shm); + break; + default: + goto err_bad_param; + } + arg->ret = TEEC_SUCCESS; + return; + +err_bad_param: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; +} + +static void handle_ffa_rpc_func_cmd(struct tee_context *ctx, + struct optee_msg_arg *arg) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + + arg->ret_origin = TEEC_ORIGIN_COMMS; + switch (arg->cmd) { + case OPTEE_RPC_CMD_SHM_ALLOC: + handle_ffa_rpc_func_cmd_shm_alloc(ctx, arg); + break; + case OPTEE_RPC_CMD_SHM_FREE: + handle_ffa_rpc_func_cmd_shm_free(ctx, optee, arg); + break; + default: + optee_rpc_cmd(ctx, optee, arg); + } +} + +static void optee_handle_ffa_rpc(struct tee_context *ctx, u32 cmd, + struct optee_msg_arg *arg) +{ + switch (cmd) { + case OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD: + handle_ffa_rpc_func_cmd(ctx, arg); + break; + case OPTEE_FFA_YIELDING_CALL_RETURN_INTERRUPT: + /* Interrupt delivered by now */ + break; + default: + pr_warn("Unknown RPC func 0x%x\n", cmd); + break; + } +} + +static int optee_ffa_yielding_call(struct tee_context *ctx, + struct ffa_send_direct_data *data, + struct optee_msg_arg *rpc_arg) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops; + struct ffa_device *ffa_dev = optee->ffa.ffa_dev; + struct optee_call_waiter w; + u32 cmd = data->data0; + u32 w4 = data->data1; + u32 w5 = data->data2; + u32 w6 = data->data3; + int rc; + + /* Initialize waiter */ + optee_cq_wait_init(&optee->call_queue, &w); + while (true) { + rc = ffa_ops->sync_send_receive(ffa_dev, data); + if (rc) + goto done; + + switch ((int)data->data0) { + case TEEC_SUCCESS: + break; + case TEEC_ERROR_BUSY: + if (cmd == OPTEE_FFA_YIELDING_CALL_RESUME) { + rc = -EIO; + goto done; + } + + /* + * Out of threads in secure world, wait for a thread + * become available. + */ + optee_cq_wait_for_completion(&optee->call_queue, &w); + data->data0 = cmd; + data->data1 = w4; + data->data2 = w5; + data->data3 = w6; + continue; + default: + rc = -EIO; + goto done; + } + + if (data->data1 == OPTEE_FFA_YIELDING_CALL_RETURN_DONE) + goto done; + + /* + * OP-TEE has returned with a RPC request. + * + * Note that data->data4 (passed in register w7) is already + * filled in by ffa_ops->sync_send_receive() returning + * above. + */ + cond_resched(); + optee_handle_ffa_rpc(ctx, data->data1, rpc_arg); + cmd = OPTEE_FFA_YIELDING_CALL_RESUME; + data->data0 = cmd; + data->data1 = 0; + data->data2 = 0; + data->data3 = 0; + } +done: + /* + * We're done with our thread in secure world, if there's any + * thread waiters wake up one. + */ + optee_cq_wait_final(&optee->call_queue, &w); + + return rc; +} + +/** + * optee_ffa_do_call_with_arg() - Do a FF-A call to enter OP-TEE in secure world + * @ctx: calling context + * @shm: shared memory holding the message to pass to secure world + * + * Does a FF-A call to OP-TEE in secure world and handles eventual resulting + * Remote Procedure Calls (RPC) from OP-TEE. + * + * Returns return code from FF-A, 0 is OK + */ + +static int optee_ffa_do_call_with_arg(struct tee_context *ctx, + struct tee_shm *shm) +{ + struct ffa_send_direct_data data = { + .data0 = OPTEE_FFA_YIELDING_CALL_WITH_ARG, + .data1 = (u32)shm->sec_world_id, + .data2 = (u32)(shm->sec_world_id >> 32), + .data3 = shm->offset, + }; + struct optee_msg_arg *arg = tee_shm_get_va(shm, 0); + unsigned int rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params); + struct optee_msg_arg *rpc_arg = tee_shm_get_va(shm, rpc_arg_offs); + + return optee_ffa_yielding_call(ctx, &data, rpc_arg); +} + +/* + * 6. Driver initialization + * + * During driver inititialization is the OP-TEE Secure Partition is probed + * to find out which features it supports so the driver can be initialized + * with a matching configuration. + */ + +static bool optee_ffa_api_is_compatbile(struct ffa_device *ffa_dev, + const struct ffa_dev_ops *ops) +{ + struct ffa_send_direct_data data = { OPTEE_FFA_GET_API_VERSION }; + int rc; + + ops->mode_32bit_set(ffa_dev); + + rc = ops->sync_send_receive(ffa_dev, &data); + if (rc) { + pr_err("Unexpected error %d\n", rc); + return false; + } + if (data.data0 != OPTEE_FFA_VERSION_MAJOR || + data.data1 < OPTEE_FFA_VERSION_MINOR) { + pr_err("Incompatible OP-TEE API version %lu.%lu", + data.data0, data.data1); + return false; + } + + data = (struct ffa_send_direct_data){ OPTEE_FFA_GET_OS_VERSION }; + rc = ops->sync_send_receive(ffa_dev, &data); + if (rc) { + pr_err("Unexpected error %d\n", rc); + return false; + } + if (data.data2) + pr_info("revision %lu.%lu (%08lx)", + data.data0, data.data1, data.data2); + else + pr_info("revision %lu.%lu", data.data0, data.data1); + + return true; +} + +static bool optee_ffa_exchange_caps(struct ffa_device *ffa_dev, + const struct ffa_dev_ops *ops, + unsigned int *rpc_arg_count) +{ + struct ffa_send_direct_data data = { OPTEE_FFA_EXCHANGE_CAPABILITIES }; + int rc; + + rc = ops->sync_send_receive(ffa_dev, &data); + if (rc) { + pr_err("Unexpected error %d", rc); + return false; + } + if (data.data0) { + pr_err("Unexpected exchange error %lu", data.data0); + return false; + } + + *rpc_arg_count = (u8)data.data1; + + return true; +} + +static struct tee_shm_pool *optee_ffa_config_dyn_shm(void) +{ + struct tee_shm_pool_mgr *priv_mgr; + struct tee_shm_pool_mgr *dmabuf_mgr; + void *rc; + + rc = optee_ffa_shm_pool_alloc_pages(); + if (IS_ERR(rc)) + return rc; + priv_mgr = rc; + + rc = optee_ffa_shm_pool_alloc_pages(); + if (IS_ERR(rc)) { + tee_shm_pool_mgr_destroy(priv_mgr); + return rc; + } + dmabuf_mgr = rc; + + rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); + if (IS_ERR(rc)) { + tee_shm_pool_mgr_destroy(priv_mgr); + tee_shm_pool_mgr_destroy(dmabuf_mgr); + } + + return rc; +} + +static void optee_ffa_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 | TEE_GEN_CAP_REG_MEM | + TEE_GEN_CAP_MEMREF_NULL, + }; + + *vers = v; +} + +static int optee_ffa_open(struct tee_context *ctx) +{ + return optee_open(ctx, true); +} + +static const struct tee_driver_ops optee_ffa_clnt_ops = { + .get_version = optee_ffa_get_version, + .open = optee_ffa_open, + .release = optee_release, + .open_session = optee_open_session, + .close_session = optee_close_session, + .invoke_func = optee_invoke_func, + .cancel_req = optee_cancel_req, + .shm_register = optee_ffa_shm_register, + .shm_unregister = optee_ffa_shm_unregister, +}; + +static const struct tee_desc optee_ffa_clnt_desc = { + .name = DRIVER_NAME "-ffa-clnt", + .ops = &optee_ffa_clnt_ops, + .owner = THIS_MODULE, +}; + +static const struct tee_driver_ops optee_ffa_supp_ops = { + .get_version = optee_ffa_get_version, + .open = optee_ffa_open, + .release = optee_release_supp, + .supp_recv = optee_supp_recv, + .supp_send = optee_supp_send, + .shm_register = optee_ffa_shm_register, /* same as for clnt ops */ + .shm_unregister = optee_ffa_shm_unregister_supp, +}; + +static const struct tee_desc optee_ffa_supp_desc = { + .name = DRIVER_NAME "-ffa-supp", + .ops = &optee_ffa_supp_ops, + .owner = THIS_MODULE, + .flags = TEE_DESC_PRIVILEGED, +}; + +static const struct optee_ops optee_ffa_ops = { + .do_call_with_arg = optee_ffa_do_call_with_arg, + .to_msg_param = optee_ffa_to_msg_param, + .from_msg_param = optee_ffa_from_msg_param, +}; + +static void optee_ffa_remove(struct ffa_device *ffa_dev) +{ + struct optee *optee = ffa_dev->dev.driver_data; + + optee_remove_common(optee); + + mutex_destroy(&optee->ffa.mutex); + rhashtable_free_and_destroy(&optee->ffa.global_ids, rh_free_fn, NULL); + + kfree(optee); +} + +static int optee_ffa_probe(struct ffa_device *ffa_dev) +{ + const struct ffa_dev_ops *ffa_ops; + unsigned int rpc_arg_count; + struct tee_device *teedev; + struct optee *optee; + int rc; + + ffa_ops = ffa_dev_ops_get(ffa_dev); + if (!ffa_ops) { + pr_warn("failed \"method\" init: ffa\n"); + return -ENOENT; + } + + if (!optee_ffa_api_is_compatbile(ffa_dev, ffa_ops)) + return -EINVAL; + + if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &rpc_arg_count)) + return -EINVAL; + + optee = kzalloc(sizeof(*optee), GFP_KERNEL); + if (!optee) { + rc = -ENOMEM; + goto err; + } + optee->pool = optee_ffa_config_dyn_shm(); + if (IS_ERR(optee->pool)) { + rc = PTR_ERR(optee->pool); + optee->pool = NULL; + goto err; + } + + optee->ops = &optee_ffa_ops; + optee->ffa.ffa_dev = ffa_dev; + optee->ffa.ffa_ops = ffa_ops; + optee->rpc_arg_count = rpc_arg_count; + + teedev = tee_device_alloc(&optee_ffa_clnt_desc, NULL, optee->pool, + optee); + if (IS_ERR(teedev)) { + rc = PTR_ERR(teedev); + goto err; + } + optee->teedev = teedev; + + teedev = tee_device_alloc(&optee_ffa_supp_desc, NULL, optee->pool, + optee); + if (IS_ERR(teedev)) { + rc = PTR_ERR(teedev); + goto err; + } + optee->supp_teedev = teedev; + + rc = tee_device_register(optee->teedev); + if (rc) + goto err; + + rc = tee_device_register(optee->supp_teedev); + if (rc) + goto err; + + rc = rhashtable_init(&optee->ffa.global_ids, &shm_rhash_params); + if (rc) + goto err; + mutex_init(&optee->ffa.mutex); + mutex_init(&optee->call_queue.mutex); + INIT_LIST_HEAD(&optee->call_queue.waiters); + optee_wait_queue_init(&optee->wait_queue); + optee_supp_init(&optee->supp); + ffa_dev_set_drvdata(ffa_dev, optee); + + rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); + if (rc) { + optee_ffa_remove(ffa_dev); + return rc; + } + + pr_info("initialized driver\n"); + return 0; +err: + /* + * tee_device_unregister() is safe to call even if the + * devices hasn't been registered with + * tee_device_register() yet. + */ + tee_device_unregister(optee->supp_teedev); + tee_device_unregister(optee->teedev); + if (optee->pool) + tee_shm_pool_free(optee->pool); + kfree(optee); + return rc; +} + +static const struct ffa_device_id optee_ffa_device_id[] = { + /* 486178e0-e7f8-11e3-bc5e0002a5d5c51b */ + { UUID_INIT(0x486178e0, 0xe7f8, 0x11e3, + 0xbc, 0x5e, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b) }, + {} +}; + +static struct ffa_driver optee_ffa_driver = { + .name = "optee", + .probe = optee_ffa_probe, + .remove = optee_ffa_remove, + .id_table = optee_ffa_device_id, +}; + +int optee_ffa_abi_register(void) +{ + if (IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT)) + return ffa_register(&optee_ffa_driver); + else + return -EOPNOTSUPP; +} + +void optee_ffa_abi_unregister(void) +{ + if (IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT)) + ffa_unregister(&optee_ffa_driver); +} diff --git a/drivers/tee/optee/optee_ffa.h b/drivers/tee/optee/optee_ffa.h new file mode 100644 index 000000000000..ee3a03fc392c --- /dev/null +++ b/drivers/tee/optee/optee_ffa.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2019-2021, Linaro Limited + */ + +/* + * This file is exported by OP-TEE and is kept in sync between secure world + * and normal world drivers. We're using ARM FF-A 1.0 specification. + */ + +#ifndef __OPTEE_FFA_H +#define __OPTEE_FFA_H + +#include + +/* + * Normal world sends requests with FFA_MSG_SEND_DIRECT_REQ and + * responses are returned with FFA_MSG_SEND_DIRECT_RESP for normal + * messages. + * + * All requests with FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP + * are using the AArch32 SMC calling convention with register usage as + * defined in FF-A specification: + * w0: Function ID (0x8400006F or 0x84000070) + * w1: Source/Destination IDs + * w2: Reserved (MBZ) + * w3-w7: Implementation defined, free to be used below + */ + +#define OPTEE_FFA_VERSION_MAJOR 1 +#define OPTEE_FFA_VERSION_MINOR 0 + +#define OPTEE_FFA_BLOCKING_CALL(id) (id) +#define OPTEE_FFA_YIELDING_CALL_BIT 31 +#define OPTEE_FFA_YIELDING_CALL(id) ((id) | BIT(OPTEE_FFA_YIELDING_CALL_BIT)) + +/* + * Returns the API version implemented, currently follows the FF-A version. + * Call register usage: + * w3: Service ID, OPTEE_FFA_GET_API_VERSION + * w4-w7: Not used (MBZ) + * + * Return register usage: + * w3: OPTEE_FFA_VERSION_MAJOR + * w4: OPTEE_FFA_VERSION_MINOR + * w5-w7: Not used (MBZ) + */ +#define OPTEE_FFA_GET_API_VERSION OPTEE_FFA_BLOCKING_CALL(0) + +/* + * Returns the revision of OP-TEE. + * + * Used by non-secure world to figure out which version of the Trusted OS + * is installed. Note that the returned revision is the revision of the + * Trusted OS, not of the API. + * + * Call register usage: + * w3: Service ID, OPTEE_FFA_GET_OS_VERSION + * w4-w7: Unused (MBZ) + * + * Return register usage: + * w3: CFG_OPTEE_REVISION_MAJOR + * w4: CFG_OPTEE_REVISION_MINOR + * w5: TEE_IMPL_GIT_SHA1 (or zero if not supported) + */ +#define OPTEE_FFA_GET_OS_VERSION OPTEE_FFA_BLOCKING_CALL(1) + +/* + * Exchange capabilities between normal world and secure world. + * + * Currently there are no defined capabilities. When features are added new + * capabilities may be added. + * + * Call register usage: + * w3: Service ID, OPTEE_FFA_EXCHANGE_CAPABILITIES + * w4-w7: Note used (MBZ) + * + * Return register usage: + * w3: Error code, 0 on success + * w4: Bit[7:0]: Number of parameters needed for RPC to be supplied + * as the second MSG arg struct for + * OPTEE_FFA_YIELDING_CALL_WITH_ARG. + * Bit[31:8]: Reserved (MBZ) + * w5-w7: Note used (MBZ) + */ +#define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2) + +/* + * Unregister shared memory + * + * Call register usage: + * w3: Service ID, OPTEE_FFA_YIELDING_CALL_UNREGISTER_SHM + * w4: Shared memory handle, lower bits + * w5: Shared memory handle, higher bits + * w6-w7: Not used (MBZ) + * + * Return register usage: + * w3: Error code, 0 on success + * w4-w7: Note used (MBZ) + */ +#define OPTEE_FFA_UNREGISTER_SHM OPTEE_FFA_BLOCKING_CALL(3) + +/* + * Call with struct optee_msg_arg as argument in the supplied shared memory + * with a zero internal offset and normal cached memory attributes. + * Register usage: + * w3: Service ID, OPTEE_FFA_YIELDING_CALL_WITH_ARG + * w4: Lower 32 bits of a 64-bit Shared memory handle + * w5: Upper 32 bits of a 64-bit Shared memory handle + * w6: Offset into shared memory pointing to a struct optee_msg_arg + * right after the parameters of this struct (at offset + * OPTEE_MSG_GET_ARG_SIZE(num_params) follows a struct optee_msg_arg + * for RPC, this struct has reserved space for the number of RPC + * parameters as returned by OPTEE_FFA_EXCHANGE_CAPABILITIES. + * w7: Not used (MBZ) + * Resume from RPC. Register usage: + * w3: Service ID, OPTEE_FFA_YIELDING_CALL_RESUME + * w4-w6: Not used (MBZ) + * w7: Resume info + * + * Normal return (yielding call is completed). Register usage: + * w3: Error code, 0 on success + * w4: OPTEE_FFA_YIELDING_CALL_RETURN_DONE + * w5-w7: Not used (MBZ) + * + * RPC interrupt return (RPC from secure world). Register usage: + * w3: Error code == 0 + * w4: Any defined RPC code but OPTEE_FFA_YIELDING_CALL_RETURN_DONE + * w5-w6: Not used (MBZ) + * w7: Resume info + * + * Possible error codes in register w3: + * 0: Success + * FFA_DENIED: w4 isn't one of OPTEE_FFA_YIELDING_CALL_START + * OPTEE_FFA_YIELDING_CALL_RESUME + * + * Possible error codes for OPTEE_FFA_YIELDING_CALL_START, + * FFA_BUSY: Number of OP-TEE OS threads exceeded, + * try again later + * FFA_DENIED: RPC shared memory object not found + * FFA_INVALID_PARAMETER: Bad shared memory handle or offset into the memory + * + * Possible error codes for OPTEE_FFA_YIELDING_CALL_RESUME + * FFA_INVALID_PARAMETER: Bad resume info + */ +#define OPTEE_FFA_YIELDING_CALL_WITH_ARG OPTEE_FFA_YIELDING_CALL(0) +#define OPTEE_FFA_YIELDING_CALL_RESUME OPTEE_FFA_YIELDING_CALL(1) + +#define OPTEE_FFA_YIELDING_CALL_RETURN_DONE 0 +#define OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD 1 +#define OPTEE_FFA_YIELDING_CALL_RETURN_INTERRUPT 2 + +#endif /*__OPTEE_FFA_H*/ diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index e3d72d09c484..2422e185d400 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -28,6 +28,9 @@ #define OPTEE_MSG_ATTR_TYPE_RMEM_INPUT 0x5 #define OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT 0x6 #define OPTEE_MSG_ATTR_TYPE_RMEM_INOUT 0x7 +#define OPTEE_MSG_ATTR_TYPE_FMEM_INPUT OPTEE_MSG_ATTR_TYPE_RMEM_INPUT +#define OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT +#define OPTEE_MSG_ATTR_TYPE_FMEM_INOUT OPTEE_MSG_ATTR_TYPE_RMEM_INOUT #define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT 0x9 #define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT 0xa #define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT 0xb @@ -96,6 +99,8 @@ */ #define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096 +#define OPTEE_MSG_FMEM_INVALID_GLOBAL_ID 0xffffffffffffffff + /** * struct optee_msg_param_tmem - temporary memory reference parameter * @buf_ptr: Address of the buffer @@ -127,6 +132,23 @@ struct optee_msg_param_rmem { u64 shm_ref; }; +/** + * struct optee_msg_param_fmem - ffa memory reference parameter + * @offs_lower: Lower bits of offset into shared memory reference + * @offs_upper: Upper bits of offset into shared memory reference + * @internal_offs: Internal offset into the first page of shared memory + * reference + * @size: Size of the buffer + * @global_id: Global identifier of Shared memory + */ +struct optee_msg_param_fmem { + u32 offs_low; + u16 offs_high; + u16 internal_offs; + u64 size; + u64 global_id; +}; + /** * struct optee_msg_param_value - opaque value parameter * @@ -143,13 +165,15 @@ struct optee_msg_param_value { * @attr: attributes * @tmem: parameter by temporary memory reference * @rmem: parameter by registered memory reference + * @fmem: parameter by ffa registered memory reference * @value: parameter by opaque value * @octets: parameter by octet string * * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value or octets, * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and - * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem, + * OPTEE_MSG_ATTR_TYPE_RMEM_* or the alias PTEE_MSG_ATTR_TYPE_FMEM_* indicates + * @rmem or @fmem depending on the conduit. * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used. */ struct optee_msg_param { @@ -157,6 +181,7 @@ struct optee_msg_param { union { struct optee_msg_param_tmem tmem; struct optee_msg_param_rmem rmem; + struct optee_msg_param_fmem fmem; struct optee_msg_param_value value; u8 octets[24]; } u; diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index ca0213e330b5..b8c7dd468705 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -7,6 +7,7 @@ #define OPTEE_PRIVATE_H #include +#include #include #include #include @@ -22,6 +23,7 @@ #define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A #define TEEC_ERROR_COMMUNICATION 0xFFFF000E #define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C +#define TEEC_ERROR_BUSY 0xFFFF000D #define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010 #define TEEC_ORIGIN_COMMS 0x00000002 @@ -73,19 +75,28 @@ struct optee_supp { struct completion reqs_c; }; -/** - * struct optee_smc - SMC ABI specifics - * @invoke_fn: function to issue smc or hvc - * @memremaped_shm virtual address of memory in shared memory pool - * @sec_caps: secure world capabilities defined by - * OPTEE_SMC_SEC_CAP_* in optee_smc.h - */ struct optee_smc { optee_invoke_fn *invoke_fn; void *memremaped_shm; u32 sec_caps; }; +/** + * struct optee_ffa_data - FFA communication struct + * @ffa_dev FFA device, contains the destination id, the id of + * OP-TEE in secure world + * @ffa_ops FFA operations + * @mutex Serializes access to @global_ids + * @global_ids FF-A shared memory global handle translation + */ +struct optee_ffa { + struct ffa_device *ffa_dev; + const struct ffa_dev_ops *ffa_ops; + /* Serializes access to @global_ids */ + struct mutex mutex; + struct rhashtable global_ids; +}; + struct optee; /** @@ -116,11 +127,13 @@ struct optee_ops { * world * @teedev: client device * @smc: specific to SMC ABI + * @ffa: specific to FF-A ABI * @call_queue: queue of threads waiting to call @invoke_fn * @wait_queue: queue of threads from secure world waiting for a * secure world sync object * @supp: supplicant synchronization struct for RPC to supplicant * @pool: shared memory pool + * @rpc_arg_count: If > 0 number of RPC parameters to make room for * @scan_bus_done flag if device registation was already done. * @scan_bus_wq workqueue to scan optee bus and register optee drivers * @scan_bus_work workq to scan optee bus and register optee drivers @@ -129,11 +142,15 @@ struct optee { struct tee_device *supp_teedev; struct tee_device *teedev; const struct optee_ops *ops; - struct optee_smc smc; + union { + struct optee_smc smc; + struct optee_ffa ffa; + }; struct optee_call_queue call_queue; struct optee_wait_queue wait_queue; struct optee_supp supp; struct tee_shm_pool *pool; + unsigned int rpc_arg_count; bool scan_bus_done; struct workqueue_struct *scan_bus_wq; struct work_struct scan_bus_work; @@ -265,5 +282,7 @@ static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val) /* Registration of the ABIs */ int optee_smc_abi_register(void); void optee_smc_abi_unregister(void); +int optee_ffa_abi_register(void); +void optee_ffa_abi_unregister(void); #endif /*OPTEE_PRIVATE_H*/