
Hi,
This adds support for storing AVB rollback indexes in the RPMB partition. The RPMB partition (content and key) is managed by OP-TEE (https://www.op-tee.org/) which is a secure OS leveraging ARM TrustZone.
The Linux kernel can already support OP-TEE with reading and updating rollback indexes in the RPMB partition, the catch is that this is needed before the kernel has booted.
The design here is the same as what is in the Linux kernel, with the exception that the user space daemon tee-supplicant is integrated in the OP-TEE driver here (drivers/tee/optee/supplicant.c) instead. A new uclass (UCLASS_TEE) is introduced to provide an abstraction for interfacing with a Trusted Execution Environment (TEE). There's also the OP-TEE driver using UCLASS_TEE for registration.
A Trusted Application (TA) interface is added to be used by the AVB verify functions which are updated accordingly. The TA is managed by OP-TEE and is executed in a secure TrustZone protected environment.
The header files drivers/tee/optee/optee_{msg,msg_supplicant,smc}.h and include/tee/optee_ta_avb.h are copied from https://github.com/OP-TEE/optee_os/tree/master more or less unmodified. They may need to be updated from time to time in order to support new features.
In MMC there's a new function, mmc_rpmb_route_frames(), which as the name suggests is used to route RPMB frames to/from the MMC. This saves OP-TEE from implementing an MMC driver which would need to share resources with its counterpart here in U-boot.
This was tested on a Hikey (Kirin 620) board.
I've added myself as maintainer of the TEE stuff.
v2: * Added sandbox driver and a test in test/dm for the new TEE uclass: Commit ("test: tee: test TEE uclass") and the enabling commits ("sandbox: dt: add sandbox_tee node") and ("configs: sandbox: enable CONFIG_TEE (TEE uclass)") * Added descriptions of exported structs and functions * Added documentation for the TEE uclass and the OP-TEE driver with the new commit ("Documentation: tee uclass and op-tee driver") * Added documentation for the changes in avb_verify * Addressed review comments from Simon Glass * Added the commit ("cmd: avb: print error message if command fails") * Made a few functions static in the OP-TEE driver * Commit ("cmd: avb read_rb: print rb_idx in hexadecimal") and ("tee: optee: support AVB trusted application"); Reviewed-by: Igor Opaniuk igor.opaniuk@linaro.org
Thanks, Jens
Jens Wiklander (15): dm: fdt: scan for devices under /firmware too cmd: avb read_rb: print rb_idx in hexadecimal cmd: avb: print error message if command fails mmc: rpmb: add mmc_rpmb_route_frames() Add UCLASS_TEE for Trusted Execution Environment dt/bindings: add bindings for optee tee: add OP-TEE driver Documentation: tee uclass and op-tee driver test: tee: test TEE uclass sandbox: dt: add sandbox_tee node configs: sandbox: enable CONFIG_TEE (TEE uclass) arm: dt: hikey: Add optee node optee: support routing of rpmb data frames to mmc tee: optee: support AVB trusted application avb_verify: support using OP-TEE TA AVB
MAINTAINERS | 7 + arch/arm/dts/hi6220-hikey.dts | 7 + arch/sandbox/dts/sandbox.dts | 4 + arch/sandbox/dts/sandbox64.dts | 4 + arch/sandbox/dts/test.dts | 4 + cmd/avb.c | 19 +- common/avb_verify.c | 132 +++- configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + configs/sandbox_noblk_defconfig | 1 + configs/sandbox_spl_defconfig | 1 + doc/README.avb2 | 13 + doc/README.tee | 112 ++++ .../firmware/linaro,optee-tz.txt | 31 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/core/root.c | 15 +- drivers/mmc/rpmb.c | 160 +++++ drivers/tee/Kconfig | 17 + drivers/tee/Makefile | 4 + drivers/tee/optee/Kconfig | 23 + drivers/tee/optee/Makefile | 5 + drivers/tee/optee/core.c | 622 ++++++++++++++++++ drivers/tee/optee/optee_msg.h | 423 ++++++++++++ drivers/tee/optee/optee_msg_supplicant.h | 234 +++++++ drivers/tee/optee/optee_private.h | 41 ++ drivers/tee/optee/optee_smc.h | 444 +++++++++++++ drivers/tee/optee/rpmb.c | 184 ++++++ drivers/tee/optee/supplicant.c | 92 +++ drivers/tee/tee-uclass.c | 192 ++++++ include/avb_verify.h | 4 + include/dm/uclass-id.h | 1 + include/mmc.h | 2 + include/tee.h | 297 +++++++++ include/tee/optee_ta_avb.h | 48 ++ test/dm/Makefile | 1 + test/dm/tee.c | 182 +++++ 38 files changed, 3325 insertions(+), 7 deletions(-) create mode 100644 doc/README.tee create mode 100644 doc/device-tree-bindings/firmware/linaro,optee-tz.txt create mode 100644 drivers/tee/Kconfig create mode 100644 drivers/tee/Makefile create mode 100644 drivers/tee/optee/Kconfig create mode 100644 drivers/tee/optee/Makefile create mode 100644 drivers/tee/optee/core.c create mode 100644 drivers/tee/optee/optee_msg.h create mode 100644 drivers/tee/optee/optee_msg_supplicant.h create mode 100644 drivers/tee/optee/optee_private.h create mode 100644 drivers/tee/optee/optee_smc.h create mode 100644 drivers/tee/optee/rpmb.c create mode 100644 drivers/tee/optee/supplicant.c create mode 100644 drivers/tee/tee-uclass.c create mode 100644 include/tee.h create mode 100644 include/tee/optee_ta_avb.h create mode 100644 test/dm/tee.c

Just as /chosen may contain devices /firmware may contain devices, scan for devices under /firmware too.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/core/root.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/drivers/core/root.c b/drivers/core/root.c index 72bcc7d7f2a3..0dca507e1187 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -265,9 +265,15 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob, for (offset = fdt_first_subnode(blob, offset); offset > 0; offset = fdt_next_subnode(blob, offset)) { - /* "chosen" node isn't a device itself but may contain some: */ - if (!strcmp(fdt_get_name(blob, offset, NULL), "chosen")) { - pr_debug("parsing subnodes of "chosen"\n"); + const char *node_name = fdt_get_name(blob, offset, NULL); + + /* + * The "chosen" and "firmware" nodes aren't devices + * themselves but may contain some: + */ + if (!strcmp(node_name, "chosen") || + !strcmp(node_name, "firmware")) { + pr_debug("parsing subnodes of "%s"\n", node_name);
err = dm_scan_fdt_node(parent, blob, offset, pre_reloc_only); @@ -286,8 +292,7 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob, err = lists_bind_fdt(parent, offset_to_ofnode(offset), NULL); if (err && !ret) { ret = err; - debug("%s: ret=%d\n", fdt_get_name(blob, offset, NULL), - ret); + debug("%s: ret=%d\n", node_name, ret); } }

On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Just as /chosen may contain devices /firmware may contain devices, scan for devices under /firmware too.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
drivers/core/root.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

Hi Jens and Simon,
čt 23. 8. 2018 v 12:43 odesílatel Jens Wiklander jens.wiklander@linaro.org napsal:
Just as /chosen may contain devices /firmware may contain devices, scan for devices under /firmware too.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
drivers/core/root.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/drivers/core/root.c b/drivers/core/root.c index 72bcc7d7f2a3..0dca507e1187 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -265,9 +265,15 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob, for (offset = fdt_first_subnode(blob, offset); offset > 0; offset = fdt_next_subnode(blob, offset)) {
/* "chosen" node isn't a device itself but may contain some: */
if (!strcmp(fdt_get_name(blob, offset, NULL), "chosen")) {
pr_debug("parsing subnodes of \"chosen\"\n");
const char *node_name = fdt_get_name(blob, offset, NULL);
/*
* The "chosen" and "firmware" nodes aren't devices
* themselves but may contain some:
*/
if (!strcmp(node_name, "chosen") ||
!strcmp(node_name, "firmware")) {
pr_debug("parsing subnodes of \"%s\"\n", node_name); err = dm_scan_fdt_node(parent, blob, offset, pre_reloc_only);
@@ -286,8 +292,7 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob, err = lists_bind_fdt(parent, offset_to_ofnode(offset), NULL); if (err && !ret) { ret = err;
debug("%s: ret=%d\n", fdt_get_name(blob, offset, NULL),
ret);
debug("%s: ret=%d\n", node_name, ret); } }
-- 2.17.1
I have debug issue which I see on ZynqMP that firmware nodes are listed in dm tree twice. I was caused by this patch.
firmware 1 [ + ] zynqmp-firmware |-- zynqmp-firmware firmware 2 [ + ] zynqmp-power | `-- zynqmp-power .... firmware 3 [ ] zynqmp-firmware `-- zynqmp-firmware firmware 4 [ ] zynqmp-power `-- zynqmp-power
On ZynqMP firmware node needs to be populated early that's why u-boot,dm-pre-reloc flag is used. That's why I am curious how to fix this. Revert this patch or add flags to Jens case or do more checking to avoid creating duplicated entries of nodes present in firmware node.
Thanks, Michal

Hi Michal,
On Tue, Jan 7, 2020 at 9:49 AM Michal Simek monstr@monstr.eu wrote:
Hi Jens and Simon,
čt 23. 8. 2018 v 12:43 odesílatel Jens Wiklander jens.wiklander@linaro.org napsal:
Just as /chosen may contain devices /firmware may contain devices, scan for devices under /firmware too.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
drivers/core/root.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/drivers/core/root.c b/drivers/core/root.c index 72bcc7d7f2a3..0dca507e1187 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -265,9 +265,15 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob, for (offset = fdt_first_subnode(blob, offset); offset > 0; offset = fdt_next_subnode(blob, offset)) {
/* "chosen" node isn't a device itself but may contain some: */
if (!strcmp(fdt_get_name(blob, offset, NULL), "chosen")) {
pr_debug("parsing subnodes of \"chosen\"\n");
const char *node_name = fdt_get_name(blob, offset, NULL);
/*
* The "chosen" and "firmware" nodes aren't devices
* themselves but may contain some:
*/
if (!strcmp(node_name, "chosen") ||
!strcmp(node_name, "firmware")) {
pr_debug("parsing subnodes of \"%s\"\n", node_name); err = dm_scan_fdt_node(parent, blob, offset, pre_reloc_only);
@@ -286,8 +292,7 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob, err = lists_bind_fdt(parent, offset_to_ofnode(offset), NULL); if (err && !ret) { ret = err;
debug("%s: ret=%d\n", fdt_get_name(blob, offset, NULL),
ret);
debug("%s: ret=%d\n", node_name, ret); } }
-- 2.17.1
I have debug issue which I see on ZynqMP that firmware nodes are listed in dm tree twice. I was caused by this patch.
firmware 1 [ + ] zynqmp-firmware |-- zynqmp-firmware firmware 2 [ + ] zynqmp-power | `-- zynqmp-power .... firmware 3 [ ] zynqmp-firmware `-- zynqmp-firmware firmware 4 [ ] zynqmp-power `-- zynqmp-power
On ZynqMP firmware node needs to be populated early that's why u-boot,dm-pre-reloc flag is used. That's why I am curious how to fix this. Revert this patch or add flags to Jens case or do more checking to avoid creating duplicated entries of nodes present in firmware node.
Reverting may solve the ZynqMP problem, but it will cause new problems instead so I'd prefer fixing it properly. I'm not sure if this is best fixed by more flags or by extra checking when creating the nodes.
Thanks, Jens
Thanks, Michal
-- Michal Simek, Ing. (M.Eng), OpenPGP -> KeyID: FE3D1F91 w: www.monstr.eu p: +42-0-721842854 Maintainer of Linux kernel - Xilinx Microblaze Maintainer of Linux kernel - Xilinx Zynq ARM and ZynqMP ARM64 SoCs U-Boot custodian - Xilinx Microblaze/Zynq/ZynqMP/Versal SoCs

Prior to this patch was do_avb_write_rb() reading supplied rb_idx as a hexadecimal number while do_avb_read_rb() printed the read out rb_idx as decimal number. For consistency change do_avb_read_rb() to print rb_idx as a hexadecimal number too.
Reviewed-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- cmd/avb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmd/avb.c b/cmd/avb.c index f045a0c64c4a..ca4b26d5d7b3 100644 --- a/cmd/avb.c +++ b/cmd/avb.c @@ -158,7 +158,7 @@ int do_avb_read_rb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
if (avb_ops->read_rollback_index(avb_ops, index, &rb_idx) == AVB_IO_RESULT_OK) { - printf("Rollback index: %llu\n", rb_idx); + printf("Rollback index: %llx\n", rb_idx); return CMD_RET_SUCCESS; } return CMD_RET_FAILURE;

On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Prior to this patch was do_avb_write_rb() reading supplied rb_idx as a hexadecimal number while do_avb_read_rb() printed the read out rb_idx as decimal number. For consistency change do_avb_read_rb() to print rb_idx as a hexadecimal number too.
Reviewed-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
cmd/avb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Reviewed-by: Simon Glass sjg@chromium.org

Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- cmd/avb.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/cmd/avb.c b/cmd/avb.c index ca4b26d5d7b3..ff00be4cee38 100644 --- a/cmd/avb.c +++ b/cmd/avb.c @@ -35,6 +35,8 @@ int do_avb_init(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (avb_ops) return CMD_RET_SUCCESS;
+ printf("Failed to initialize avb2\n"); + return CMD_RET_FAILURE; }
@@ -65,6 +67,8 @@ int do_avb_read_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_SUCCESS; }
+ printf("Failed to read from partition\n"); + return CMD_RET_FAILURE; }
@@ -108,6 +112,8 @@ int do_avb_read_part_hex(cmd_tbl_t *cmdtp, int flag, int argc, return CMD_RET_SUCCESS; }
+ printf("Failed to read from partition\n"); + free(buffer); return CMD_RET_FAILURE; } @@ -138,6 +144,8 @@ int do_avb_write_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_SUCCESS; }
+ printf("Failed to write in partition\n"); + return CMD_RET_FAILURE; }
@@ -161,6 +169,9 @@ int do_avb_read_rb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("Rollback index: %llx\n", rb_idx); return CMD_RET_SUCCESS; } + + printf("Failed to read rollback index\n"); + return CMD_RET_FAILURE; }
@@ -184,6 +195,8 @@ int do_avb_write_rb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) AVB_IO_RESULT_OK) return CMD_RET_SUCCESS;
+ printf("Failed to write rollback index\n"); + return CMD_RET_FAILURE; }
@@ -210,6 +223,8 @@ int do_avb_get_uuid(cmd_tbl_t *cmdtp, int flag, return CMD_RET_SUCCESS; }
+ printf("Failed to read UUID\n"); + return CMD_RET_FAILURE; }
@@ -320,6 +335,8 @@ int do_avb_is_unlocked(cmd_tbl_t *cmdtp, int flag, return CMD_RET_SUCCESS; }
+ printf("Can't determine device lock state.\n"); + return CMD_RET_FAILURE; }

On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
cmd/avb.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

Adds mmc_rpmb_route_frames() to route RPMB data frames from/to an external entity.
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/mmc/rpmb.c | 160 +++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 2 + 2 files changed, 162 insertions(+)
diff --git a/drivers/mmc/rpmb.c b/drivers/mmc/rpmb.c index dfbdb0deb107..908f19208955 100644 --- a/drivers/mmc/rpmb.c +++ b/drivers/mmc/rpmb.c @@ -321,3 +321,163 @@ int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk, } return i; } + +static int send_write_mult_block(struct mmc *mmc, const struct s_rpmb *frm, + unsigned short cnt) +{ + struct mmc_cmd cmd = { + .cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK, + .resp_type = MMC_RSP_R1b, + }; + struct mmc_data data = { + .src = (const void *)frm, + .blocks = cnt, + .blocksize = sizeof(*frm), + .flags = MMC_DATA_WRITE, + }; + + return mmc_send_cmd(mmc, &cmd, &data); +} + +static int send_read_mult_block(struct mmc *mmc, struct s_rpmb *frm, + unsigned short cnt) +{ + struct mmc_cmd cmd = { + .cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK, + .resp_type = MMC_RSP_R1, + }; + struct mmc_data data = { + .dest = (void *)frm, + .blocks = cnt, + .blocksize = sizeof(*frm), + .flags = MMC_DATA_READ, + }; + + return mmc_send_cmd(mmc, &cmd, &data); +} + +static int rpmb_route_write_req(struct mmc *mmc, struct s_rpmb *req, + unsigned short req_cnt, struct s_rpmb *rsp, + unsigned short rsp_cnt) +{ + int ret; + + /* + * Send the write request. + */ + ret = mmc_set_blockcount(mmc, req_cnt, true); + if (ret) + return ret; + + ret = send_write_mult_block(mmc, req, req_cnt); + if (ret) + return ret; + + /* + * Read the result of the request. + */ + ret = mmc_set_blockcount(mmc, 1, false); + if (ret) + return ret; + + memset(rsp, 0, sizeof(*rsp)); + rsp->request = cpu_to_be16(RPMB_REQ_STATUS); + ret = send_write_mult_block(mmc, rsp, 1); + if (ret) + return ret; + + ret = mmc_set_blockcount(mmc, 1, false); + if (ret) + return ret; + + return send_read_mult_block(mmc, rsp, 1); +} + +static int rpmb_route_read_req(struct mmc *mmc, struct s_rpmb *req, + unsigned short req_cnt, struct s_rpmb *rsp, + unsigned short rsp_cnt) +{ + int ret; + + /* + * Send the read request. + */ + ret = mmc_set_blockcount(mmc, 1, false); + if (ret) + return ret; + + ret = send_write_mult_block(mmc, req, 1); + if (ret) + return ret; + + /* + * Read the result of the request. + */ + + ret = mmc_set_blockcount(mmc, rsp_cnt, false); + if (ret) + return ret; + + return send_read_mult_block(mmc, rsp, rsp_cnt); +} + +static int rpmb_route_frames(struct mmc *mmc, struct s_rpmb *req, + unsigned short req_cnt, struct s_rpmb *rsp, + unsigned short rsp_cnt) +{ + unsigned short n; + + /* + * If multiple request frames are provided, make sure that all are + * of the same type. + */ + for (n = 1; n < req_cnt; n++) + if (req[n].request != req->request) + return -EINVAL; + + switch (be16_to_cpu(req->request)) { + case RPMB_REQ_KEY: + if (req_cnt != 1 || rsp_cnt != 1) + return -EINVAL; + return rpmb_route_write_req(mmc, req, req_cnt, rsp, rsp_cnt); + + case RPMB_REQ_WRITE_DATA: + if (!req_cnt || rsp_cnt != 1) + return -EINVAL; + return rpmb_route_write_req(mmc, req, req_cnt, rsp, rsp_cnt); + + case RPMB_REQ_WCOUNTER: + if (req_cnt != 1 || rsp_cnt != 1) + return -EINVAL; + return rpmb_route_read_req(mmc, req, req_cnt, rsp, rsp_cnt); + + case RPMB_REQ_READ_DATA: + if (req_cnt != 1 || !req_cnt) + return -EINVAL; + return rpmb_route_read_req(mmc, req, req_cnt, rsp, rsp_cnt); + + default: + debug("Unsupported message type: %d\n", + be16_to_cpu(req->request)); + return -EINVAL; + } +} + +int mmc_rpmb_route_frames(struct mmc *mmc, void *req, unsigned long reqlen, + void *rsp, unsigned long rsplen) +{ + /* + * Whoever crafted the data supplied to this function knows how to + * format the PRMB frames and which response is expected. If + * there's some unexpected mismatch it's more helpful to report an + * error immediately than trying to guess what was the intention + * and possibly just delay an eventual error which will be harder + * to track down. + */ + + if (reqlen % sizeof(struct s_rpmb) || rsplen % sizeof(struct s_rpmb)) + return -EINVAL; + + return rpmb_route_frames(mmc, req, reqlen / sizeof(struct s_rpmb), + rsp, rsplen / sizeof(struct s_rpmb)); +} diff --git a/include/mmc.h b/include/mmc.h index df4255b828a7..d6e02af4edea 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -748,6 +748,8 @@ int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk, unsigned short cnt, unsigned char *key); int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk, unsigned short cnt, unsigned char *key); +int mmc_rpmb_route_frames(struct mmc *mmc, void *req, unsigned long reqlen, + void *rsp, unsigned long rsplen); #ifdef CONFIG_CMD_BKOPS_ENABLE int mmc_set_bkops_enable(struct mmc *mmc); #endif

Adds a uclass to interface with a TEE (Trusted Execution Environment).
A TEE driver is a driver that interfaces with a trusted OS running in some secure environment, for example, TrustZone on ARM cpus, or a separate secure co-processor etc.
The TEE subsystem can serve a TEE driver for a Global Platform compliant TEE, but it's not limited to only Global Platform TEEs.
The over all design is based on the TEE subsystem in the Linux kernel, tailored for U-boot.
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- MAINTAINERS | 6 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/tee/Kconfig | 8 ++ drivers/tee/Makefile | 3 + drivers/tee/tee-uclass.c | 192 ++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/tee.h | 290 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 503 insertions(+) create mode 100644 drivers/tee/Kconfig create mode 100644 drivers/tee/Makefile create mode 100644 drivers/tee/tee-uclass.c create mode 100644 include/tee.h
diff --git a/MAINTAINERS b/MAINTAINERS index 58b61ac05882..7458c606ee92 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -571,6 +571,12 @@ TQ GROUP S: Orphaned (Since 2016-02) T: git git://git.denx.de/u-boot-tq-group.git
+TEE +M: Jens Wiklander jens.wiklander@linaro.org +S: Maintained +F: drivers/tee/ +F: include/tee.h + UBI M: Kyungmin Park kmpark@infradead.org M: Heiko Schocher hs@denx.de diff --git a/drivers/Kconfig b/drivers/Kconfig index c72abf893297..f3249ab1d143 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -94,6 +94,8 @@ source "drivers/spmi/Kconfig"
source "drivers/sysreset/Kconfig"
+source "drivers/tee/Kconfig" + source "drivers/thermal/Kconfig"
source "drivers/timer/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index d53208540ea6..0fcae36f50f7 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -103,6 +103,7 @@ obj-y += smem/ obj-y += soc/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-y += thermal/ +obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ endif diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig new file mode 100644 index 000000000000..817ab331b0f8 --- /dev/null +++ b/drivers/tee/Kconfig @@ -0,0 +1,8 @@ +# Generic Trusted Execution Environment Configuration +config TEE + bool "Trusted Execution Environment support" + depends on ARM && (ARM64 || CPU_V7A) + select ARM_SMCCC + help + This implements a generic interface towards a Trusted Execution + Environment (TEE). diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile new file mode 100644 index 000000000000..b6d8e16e6211 --- /dev/null +++ b/drivers/tee/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += tee-uclass.o diff --git a/drivers/tee/tee-uclass.c b/drivers/tee/tee-uclass.c new file mode 100644 index 000000000000..0209983491a0 --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018 Linaro Limited + */ + +#include <common.h> +#include <dm.h> +#include <tee.h> + +/** + * struct tee_uclass_priv - information of a TEE, stored by the uclass + * + * @list_shm: list of structe tee_shm representing memory blocks shared + * with the TEE. + */ +struct tee_uclass_priv { + struct list_head list_shm; +}; + +static const struct tee_driver_ops *tee_get_ops(struct udevice *dev) +{ + return device_get_ops(dev); +} + +void tee_get_version(struct udevice *dev, struct tee_version_data *vers) +{ + tee_get_ops(dev)->get_version(dev, vers); +} + +int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg, + ulong num_param, struct tee_param *param) +{ + return tee_get_ops(dev)->open_session(dev, arg, num_param, param); +} + +int tee_close_session(struct udevice *dev, u32 session) +{ + return tee_get_ops(dev)->close_session(dev, session); +} + +int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg, + ulong num_param, struct tee_param *param) +{ + return tee_get_ops(dev)->invoke_func(dev, arg, num_param, param); +} + +struct tee_shm *__tee_shm_add(struct udevice *dev, ulong align, void *addr, + ulong size, u32 flags) +{ + struct tee_shm *shm; + void *p = addr; + + if (flags & TEE_SHM_ALLOC) { + if (align) + p = memalign(align, size); + else + p = malloc(size); + } + if (!p) + return NULL; + + shm = calloc(1, sizeof(*shm)); + if (!shm) + goto err; + + shm->dev = dev; + shm->addr = p; + shm->size = size; + shm->flags = flags; + + if ((flags & TEE_SHM_SEC_REGISTER) && + tee_get_ops(dev)->shm_register(dev, shm)) + goto err; + + if (flags & TEE_SHM_REGISTER) { + struct tee_uclass_priv *priv = dev_get_uclass_priv(dev); + + list_add(&shm->link, &priv->list_shm); + } + return shm; +err: + free(shm); + if (flags & TEE_SHM_ALLOC) + free(p); + return NULL; +} + +struct tee_shm *tee_shm_alloc(struct udevice *dev, ulong size, + u32 flags) +{ + u32 f = flags; + + f |= TEE_SHM_SEC_REGISTER | TEE_SHM_REGISTER | TEE_SHM_ALLOC; + return __tee_shm_add(dev, 0, NULL, size, f); +} + +struct tee_shm *tee_shm_register(struct udevice *dev, void *addr, + ulong size, u32 flags) +{ + u32 f = flags & ~TEE_SHM_ALLOC; + + f |= TEE_SHM_SEC_REGISTER | TEE_SHM_REGISTER; + return __tee_shm_add(dev, 0, addr, size, f); +} + +void tee_shm_free(struct tee_shm *shm) +{ + if (!shm) + return; + + if (shm->flags & TEE_SHM_SEC_REGISTER) + tee_get_ops(shm->dev)->shm_unregister(shm->dev, shm); + + if (shm->flags & TEE_SHM_REGISTER) + list_del(&shm->link); + + if (shm->flags & TEE_SHM_ALLOC) + free(shm->addr); + + free(shm); +} + +bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev) +{ + struct tee_uclass_priv *priv = dev_get_uclass_priv(dev); + struct tee_shm *s; + + list_for_each_entry(s, &priv->list_shm, link) + if (s == shm) + return true; + + return false; +} + +struct udevice *tee_find_device(struct udevice *start, + int (*match)(struct tee_version_data *vers, + const void *data), + const void *data, + struct tee_version_data *vers) +{ + struct udevice *dev = start; + struct tee_version_data lv; + struct tee_version_data *v = vers ? vers : &lv; + + if (!dev) + uclass_first_device(UCLASS_TEE, &dev); + + for (; dev; uclass_next_device(&dev)) { + tee_get_ops(dev)->get_version(dev, v); + if (!match || match(v, data)) + return dev; + } + + return NULL; +} + +static int tee_pre_probe(struct udevice *dev) +{ + struct tee_uclass_priv *priv = dev_get_uclass_priv(dev); + + INIT_LIST_HEAD(&priv->list_shm); + + return 0; +} + +static int tee_pre_remove(struct udevice *dev) +{ + struct tee_uclass_priv *priv = dev_get_uclass_priv(dev); + struct tee_shm *shm; + + /* + * Any remaining shared memory must be unregistered now as U-boot + * is about to hand over to the next stage and that memory will be + * reused. + */ + while (!list_empty(&priv->list_shm)) { + shm = list_first_entry(&priv->list_shm, struct tee_shm, link); + debug("%s: freeing leftover shm %p (size %lu, flags %#x)\n", + __func__, (void *)shm, shm->size, shm->flags); + tee_shm_free(shm); + } + + return 0; +} + +UCLASS_DRIVER(tee) = { + .id = UCLASS_TEE, + .name = "tee", + .per_device_auto_alloc_size = sizeof(struct tee_uclass_priv), + .pre_probe = tee_pre_probe, + .pre_remove = tee_pre_remove, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a39643ec5eef..955e0a915b87 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -81,6 +81,7 @@ enum uclass_id { UCLASS_SPI_GENERIC, /* Generic SPI flash target */ UCLASS_SYSCON, /* System configuration device */ UCLASS_SYSRESET, /* System reset device */ + UCLASS_TEE, /* Trusted Execution Environment device */ UCLASS_THERMAL, /* Thermal sensor */ UCLASS_TIMER, /* Timer device */ UCLASS_TPM, /* Trusted Platform Module TIS interface */ diff --git a/include/tee.h b/include/tee.h new file mode 100644 index 000000000000..3e6771123ef0 --- /dev/null +++ b/include/tee.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2018 Linaro Limited + */ + +#ifndef __TEE_H +#define __TEE_H + +#include <common.h> +#include <dm.h> + +#define TEE_UUID_LEN 16 + +#define TEE_GEN_CAP_GP BIT(0) /* GlobalPlatform compliant TEE */ +#define TEE_GEN_CAP_REG_MEM BIT(1) /* Supports registering shared memory */ + +#define TEE_SHM_REGISTER BIT(0) /* In list of shared memory */ +#define TEE_SHM_SEC_REGISTER BIT(1) /* TEE notified of this memory */ +#define TEE_SHM_ALLOC BIT(2) /* The memory is malloced() and must */ + /* be freed() */ + +#define TEE_PARAM_ATTR_TYPE_NONE 0 /* parameter not used */ +#define TEE_PARAM_ATTR_TYPE_VALUE_INPUT 1 +#define TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT 2 +#define TEE_PARAM_ATTR_TYPE_VALUE_INOUT 3 /* input and output */ +#define TEE_PARAM_ATTR_TYPE_MEMREF_INPUT 5 +#define TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT 6 +#define TEE_PARAM_ATTR_TYPE_MEMREF_INOUT 7 /* input and output */ +#define TEE_PARAM_ATTR_TYPE_MASK 0xff +#define TEE_PARAM_ATTR_META 0x100 +#define TEE_PARAM_ATTR_MASK (TEE_PARAM_ATTR_TYPE_MASK | \ + TEE_PARAM_ATTR_META) + +/* + * Some Global Platform error codes which has a meaning if the + * TEE_GEN_CAP_GP bit is returned by the driver in + * struct tee_version_data::gen_caps + */ +#define TEE_SUCCESS 0x00000000 +#define TEE_ERROR_GENERIC 0xffff0000 +#define TEE_ERROR_BAD_PARAMETERS 0xffff0006 +#define TEE_ERROR_ITEM_NOT_FOUND 0xffff0008 +#define TEE_ERROR_NOT_IMPLEMENTED 0xffff0009 +#define TEE_ERROR_NOT_SUPPORTED 0xffff000a +#define TEE_ERROR_COMMUNICATION 0xffff000e +#define TEE_ERROR_OUT_OF_MEMORY 0xffff000c +#define TEE_ERROR_TARGET_DEAD 0xffff3024 + +#define TEE_ORIGIN_COMMS 0x00000002 + +/** + * struct tee_shm - memory shared with the TEE + * @dev: The TEE device + * @link: List node in the list in struct struct tee_uclass_priv + * @addr: Pointer to the shared memory + * @size: Size of the the shared memory + * @flags: TEE_SHM_* above + */ +struct tee_shm { + struct udevice *dev; + struct list_head link; + void *addr; + ulong size; + u32 flags; +}; + +/** + * struct tee_param_memref - parameter memory reference + * @shm_offs: Offset in bytes into the shared memory object @shm + * @size: Size in bytes of the memory reference + * @shm: Pointer to a shared memory object for the buffer + */ +struct tee_param_memref { + ulong shm_offs; + ulong size; + struct tee_shm *shm; +}; + +/** + * struct tee_param_value - value parameter + * @a, @b, @c: Parameters passed by value + */ +struct tee_param_value { + u64 a; + u64 b; + u64 c; +}; + +/** + * struct tee_param - invoke parameter + * @attr: Attributes + * @u.memref: Memref parameter if (@attr & TEE_PARAM_ATTR_MASK) is one of + * TEE_PARAM_ATTR_TYPE_MEMREF_* above + * @u.value: Value parameter if (@attr & TEE_PARAM_ATTR_MASK) is one of + * TEE_PARAM_ATTR_TYPE_VALUE_* above + */ +struct tee_param { + u64 attr; + union { + struct tee_param_memref memref; + struct tee_param_value value; + } u; +}; + +/** + * struct tee_open_session_arg - extra arguments for tee_open_session() + * @uuid: [in] UUID of the Trusted Application + * @clnt_uuid: [in] Normally zeroes + * @clnt_login: [in] Normally 0 + * @session: [out] Session id + * @ret: [out] return value + * @ret_origin: [out] origin of the return value + */ +struct tee_open_session_arg { + u8 uuid[TEE_UUID_LEN]; + u8 clnt_uuid[TEE_UUID_LEN]; + u32 clnt_login; + u32 session; + u32 ret; + u32 ret_origin; +}; + +/** + * struct tee_invoke_arg - extra arguments for tee_invoke_func() + * @func: [in] Trusted Application function, specific to the TA + * @session: [in] Session id, from open session + * @ret: [out] return value + * @ret_origin: [out] origin of the return value + */ +struct tee_invoke_arg { + u32 func; + u32 session; + u32 ret; + u32 ret_origin; +}; + +/** + * struct tee_version_data - description of TEE + * @gen_caps: Generic capabilities, TEE_GEN_CAP_* above + */ +struct tee_version_data { + u32 gen_caps; +}; + +/** + * struct tee_driver_ops - TEE driver operations + * @get_version: Query capabilities of TEE device, + see tee_get_version() + * @open_session: Opens a session to a Trusted Application in the TEE, + * see tee_open_session(). + * @close_session: Closes a session to Trusted Application, + * see tee_close_session() + * @invoke_func: Invokes a function in a Trusted Application, + see tee_invoke_func() + */ +struct tee_driver_ops { + void (*get_version)(struct udevice *dev, struct tee_version_data *vers); + int (*open_session)(struct udevice *dev, + struct tee_open_session_arg *arg, ulong num_param, + struct tee_param *param); + int (*close_session)(struct udevice *dev, u32 session); + int (*invoke_func)(struct udevice *dev, struct tee_invoke_arg *arg, + ulong num_param, struct tee_param *param); + /** + * shm_register() - Registers memory shared with the TEE + * @dev: The TEE device + * @shm: Pointer to a shared memory object + * Returns 0 on success or < 0 on failure. + */ + int (*shm_register)(struct udevice *dev, struct tee_shm *shm); + /** + * shm_unregister() - Unregisters memory shared with the TEE + * @dev: The TEE device + * @shm: Pointer to a shared memory object + * Returns 0 on success or < 0 on failure. + */ + int (*shm_unregister)(struct udevice *dev, struct tee_shm *shm); +}; + +/** + * __tee_shm_add() - Internal helper function to register shared memory + * @dev: The TEE device + * @align: Required alignment of allocated memory block if + * (@flags & TEE_SHM_ALLOC) + * @addr: Address of memory block, ignored if (@flags & TEE_SHM_ALLOC) + * @size: Size of memory block + * @flags: TEE_SHM_* above + * + * returns pointer to struct tee_shm on success or NULL on error + */ +struct tee_shm *__tee_shm_add(struct udevice *dev, ulong align, void *addr, + ulong size, u32 flags); + +/** + * tee_shm_alloc() - Allocate shared memory + * @dev: The TEE device + * @size: Size of memory block + * @flags: TEE_SHM_* above + * + * returns pointer to struct tee_shm on success or NULL on error + */ +struct tee_shm *tee_shm_alloc(struct udevice *dev, ulong size, u32 flags); + +/** + * tee_shm_register() - Registers shared memory + * @dev: The TEE device + * @addr: Address of memory block + * @size: Size of memory block + * @flags: TEE_SHM_* above + * + * returns pointer to struct tee_shm on success or NULL on error + */ +struct tee_shm *tee_shm_register(struct udevice *dev, void *addr, + ulong length, u32 flags); + +/** + * tee_shm_free() - Frees shared memory + * @shm: Shared memory object + */ +void tee_shm_free(struct tee_shm *shm); + +/** + * tee_shm_is_registered() - Check register status of shared memory object + * @shm: Pointer to shared memory object + * @dev: The TEE device + * + * Returns true if the shared memory object is registered for the supplied + * TEE device + */ +bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev); + +/** + * tee_find_device() - Look up a TEE device + * @start: if not NULL, continue search after this device + * @match: function to check TEE device + * @data: data for match function + * @vers: if not NULL, version data of TEE device of the device returned + * + * Returns a probed TEE device of the first TEE device matched by the + * match() callback or NULL. + */ +struct udevice *tee_find_device(struct udevice *start, + int (*match)(struct tee_version_data *vers, + const void *data), + const void *data, + struct tee_version_data *vers); + +/** + * tee_get_version() - Query capabilities of TEE device + * @dev: The TEE device + * @vers: Pointer to version data + */ +void tee_get_version(struct udevice *dev, struct tee_version_data *vers); + +/** + * tee_open_session() - Open a session to a Trusted Application + * @dev: The TEE device + * @arg: Open session arguments + * @num_param: Number of elements in @param + * @param: Parameters for Trusted Application + * + * Returns < 0 on error else see @arg->ret for result. If @arg->ret is + * TEE_SUCCESS the session identifier is available in @arg->session. + */ +int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg, + ulong num_param, struct tee_param *param); + +/** + * tee_close_session() - Close a session to a Trusted Application + * @dev: The TEE device + * @session: Session id + * + * Return < 0 on error else 0, regardless the session will not be valid + * after this function has returned. + */ +int tee_close_session(struct udevice *dev, u32 session); + +/** + * tee_invoke_func() - Invoke a function in a Trusted Application + * @dev: The TEE device + * @arg: Invoke arguments + * @num_param: Number of elements in @param + * @param: Parameters for Trusted Application + * + * Returns < 0 on error else see @arg->ret for result. + */ +int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg, + ulong num_param, struct tee_param *param); + +#endif /*__TEE_H*/

Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds a uclass to interface with a TEE (Trusted Execution Environment).
A TEE driver is a driver that interfaces with a trusted OS running in some secure environment, for example, TrustZone on ARM cpus, or a separate secure co-processor etc.
The TEE subsystem can serve a TEE driver for a Global Platform compliant TEE, but it's not limited to only Global Platform TEEs.
The over all design is based on the TEE subsystem in the Linux kernel, tailored for U-boot.
U-Boot
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 6 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/tee/Kconfig | 8 ++ drivers/tee/Makefile | 3 + drivers/tee/tee-uclass.c | 192 ++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/tee.h | 290 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 503 insertions(+) create mode 100644 drivers/tee/Kconfig create mode 100644 drivers/tee/Makefile create mode 100644 drivers/tee/tee-uclass.c create mode 100644 include/tee.h
Reviewed-by: Simon Glass sjg@chromium.org
Various nits below.
diff --git a/MAINTAINERS b/MAINTAINERS index 58b61ac05882..7458c606ee92 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -571,6 +571,12 @@ TQ GROUP S: Orphaned (Since 2016-02) T: git git://git.denx.de/u-boot-tq-group.git
+TEE +M: Jens Wiklander jens.wiklander@linaro.org +S: Maintained +F: drivers/tee/ +F: include/tee.h
UBI M: Kyungmin Park kmpark@infradead.org M: Heiko Schocher hs@denx.de diff --git a/drivers/Kconfig b/drivers/Kconfig index c72abf893297..f3249ab1d143 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -94,6 +94,8 @@ source "drivers/spmi/Kconfig"
source "drivers/sysreset/Kconfig"
+source "drivers/tee/Kconfig"
source "drivers/thermal/Kconfig"
source "drivers/timer/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index d53208540ea6..0fcae36f50f7 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -103,6 +103,7 @@ obj-y += smem/ obj-y += soc/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-y += thermal/ +obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ endif diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig new file mode 100644 index 000000000000..817ab331b0f8 --- /dev/null +++ b/drivers/tee/Kconfig @@ -0,0 +1,8 @@ +# Generic Trusted Execution Environment Configuration +config TEE
bool "Trusted Execution Environment support"
depends on ARM && (ARM64 || CPU_V7A)
select ARM_SMCCC
help
This implements a generic interface towards a Trusted Execution
Environment (TEE).
Please expand this. What is it? Why would I use it? Also perhaps add a web link?
diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile new file mode 100644 index 000000000000..b6d8e16e6211 --- /dev/null +++ b/drivers/tee/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+
+obj-y += tee-uclass.o diff --git a/drivers/tee/tee-uclass.c b/drivers/tee/tee-uclass.c new file mode 100644 index 000000000000..0209983491a0 --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <dm.h> +#include <tee.h>
+/**
- struct tee_uclass_priv - information of a TEE, stored by the uclass
- @list_shm: list of structe tee_shm representing memory blocks shared
with the TEE.
- */
+struct tee_uclass_priv {
struct list_head list_shm;
+};
+static const struct tee_driver_ops *tee_get_ops(struct udevice *dev) +{
return device_get_ops(dev);
+}
+void tee_get_version(struct udevice *dev, struct tee_version_data *vers) +{
tee_get_ops(dev)->get_version(dev, vers);
+}
+int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg,
ulong num_param, struct tee_param *param)
+{
return tee_get_ops(dev)->open_session(dev, arg, num_param, param);
+}
+int tee_close_session(struct udevice *dev, u32 session) +{
return tee_get_ops(dev)->close_session(dev, session);
+}
+int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg,
ulong num_param, struct tee_param *param)
+{
return tee_get_ops(dev)->invoke_func(dev, arg, num_param, param);
+}
+struct tee_shm *__tee_shm_add(struct udevice *dev, ulong align, void *addr,
ulong size, u32 flags)
+{
struct tee_shm *shm;
void *p = addr;
if (flags & TEE_SHM_ALLOC) {
if (align)
p = memalign(align, size);
else
p = malloc(size);
}
if (!p)
return NULL;
shm = calloc(1, sizeof(*shm));
if (!shm)
goto err;
shm->dev = dev;
shm->addr = p;
shm->size = size;
shm->flags = flags;
if ((flags & TEE_SHM_SEC_REGISTER) &&
tee_get_ops(dev)->shm_register(dev, shm))
goto err;
if (flags & TEE_SHM_REGISTER) {
struct tee_uclass_priv *priv = dev_get_uclass_priv(dev);
list_add(&shm->link, &priv->list_shm);
}
return shm;
+err:
free(shm);
if (flags & TEE_SHM_ALLOC)
free(p);
return NULL;
+}
+struct tee_shm *tee_shm_alloc(struct udevice *dev, ulong size,
u32 flags)
+{
u32 f = flags;
f |= TEE_SHM_SEC_REGISTER | TEE_SHM_REGISTER | TEE_SHM_ALLOC;
blank line here
return __tee_shm_add(dev, 0, NULL, size, f);
+}
+struct tee_shm *tee_shm_register(struct udevice *dev, void *addr,
ulong size, u32 flags)
+{
u32 f = flags & ~TEE_SHM_ALLOC;
f |= TEE_SHM_SEC_REGISTER | TEE_SHM_REGISTER;
and here
return __tee_shm_add(dev, 0, addr, size, f);
+}
+void tee_shm_free(struct tee_shm *shm) +{
if (!shm)
return;
if (shm->flags & TEE_SHM_SEC_REGISTER)
tee_get_ops(shm->dev)->shm_unregister(shm->dev, shm);
if (shm->flags & TEE_SHM_REGISTER)
list_del(&shm->link);
if (shm->flags & TEE_SHM_ALLOC)
free(shm->addr);
free(shm);
+}
+bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev) +{
struct tee_uclass_priv *priv = dev_get_uclass_priv(dev);
struct tee_shm *s;
list_for_each_entry(s, &priv->list_shm, link)
if (s == shm)
return true;
return false;
+}
+struct udevice *tee_find_device(struct udevice *start,
int (*match)(struct tee_version_data *vers,
const void *data),
const void *data,
struct tee_version_data *vers)
Please add function comment
+{
struct udevice *dev = start;
struct tee_version_data lv;
struct tee_version_data *v = vers ? vers : &lv;
if (!dev)
uclass_first_device(UCLASS_TEE, &dev);
This can return an error if it finds a device but it tails to probe. If you don't care about that, I suppose it is OK.
for (; dev; uclass_next_device(&dev)) {
tee_get_ops(dev)->get_version(dev, v);
if (!match || match(v, data))
return dev;
}
return NULL;
+}
+static int tee_pre_probe(struct udevice *dev) +{
struct tee_uclass_priv *priv = dev_get_uclass_priv(dev);
INIT_LIST_HEAD(&priv->list_shm);
return 0;
+}
+static int tee_pre_remove(struct udevice *dev) +{
struct tee_uclass_priv *priv = dev_get_uclass_priv(dev);
struct tee_shm *shm;
/*
* Any remaining shared memory must be unregistered now as U-boot
U-Boot
* is about to hand over to the next stage and that memory will be
* reused.
*/
while (!list_empty(&priv->list_shm)) {
shm = list_first_entry(&priv->list_shm, struct tee_shm, link);
debug("%s: freeing leftover shm %p (size %lu, flags %#x)\n",
__func__, (void *)shm, shm->size, shm->flags);
tee_shm_free(shm);
}
return 0;
+}
+UCLASS_DRIVER(tee) = {
.id = UCLASS_TEE,
.name = "tee",
.per_device_auto_alloc_size = sizeof(struct tee_uclass_priv),
.pre_probe = tee_pre_probe,
.pre_remove = tee_pre_remove,
+}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a39643ec5eef..955e0a915b87 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -81,6 +81,7 @@ enum uclass_id { UCLASS_SPI_GENERIC, /* Generic SPI flash target */ UCLASS_SYSCON, /* System configuration device */ UCLASS_SYSRESET, /* System reset device */
UCLASS_TEE, /* Trusted Execution Environment device */ UCLASS_THERMAL, /* Thermal sensor */ UCLASS_TIMER, /* Timer device */ UCLASS_TPM, /* Trusted Platform Module TIS interface */
diff --git a/include/tee.h b/include/tee.h new file mode 100644 index 000000000000..3e6771123ef0 --- /dev/null +++ b/include/tee.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#ifndef __TEE_H +#define __TEE_H
+#include <common.h> +#include <dm.h>
Please drop both of those.
+#define TEE_UUID_LEN 16
+#define TEE_GEN_CAP_GP BIT(0) /* GlobalPlatform compliant TEE */ +#define TEE_GEN_CAP_REG_MEM BIT(1) /* Supports registering shared memory */
+#define TEE_SHM_REGISTER BIT(0) /* In list of shared memory */ +#define TEE_SHM_SEC_REGISTER BIT(1) /* TEE notified of this memory */ +#define TEE_SHM_ALLOC BIT(2) /* The memory is malloced() and must */
/* be freed() */
+#define TEE_PARAM_ATTR_TYPE_NONE 0 /* parameter not used */ +#define TEE_PARAM_ATTR_TYPE_VALUE_INPUT 1 +#define TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT 2 +#define TEE_PARAM_ATTR_TYPE_VALUE_INOUT 3 /* input and output */ +#define TEE_PARAM_ATTR_TYPE_MEMREF_INPUT 5 +#define TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT 6 +#define TEE_PARAM_ATTR_TYPE_MEMREF_INOUT 7 /* input and output */ +#define TEE_PARAM_ATTR_TYPE_MASK 0xff +#define TEE_PARAM_ATTR_META 0x100 +#define TEE_PARAM_ATTR_MASK (TEE_PARAM_ATTR_TYPE_MASK | \
TEE_PARAM_ATTR_META)
+/*
- Some Global Platform error codes which has a meaning if the
- TEE_GEN_CAP_GP bit is returned by the driver in
- struct tee_version_data::gen_caps
- */
+#define TEE_SUCCESS 0x00000000 +#define TEE_ERROR_GENERIC 0xffff0000 +#define TEE_ERROR_BAD_PARAMETERS 0xffff0006 +#define TEE_ERROR_ITEM_NOT_FOUND 0xffff0008 +#define TEE_ERROR_NOT_IMPLEMENTED 0xffff0009 +#define TEE_ERROR_NOT_SUPPORTED 0xffff000a +#define TEE_ERROR_COMMUNICATION 0xffff000e +#define TEE_ERROR_OUT_OF_MEMORY 0xffff000c +#define TEE_ERROR_TARGET_DEAD 0xffff3024
+#define TEE_ORIGIN_COMMS 0x00000002
+/**
- struct tee_shm - memory shared with the TEE
- @dev: The TEE device
- @link: List node in the list in struct struct tee_uclass_priv
- @addr: Pointer to the shared memory
- @size: Size of the the shared memory
- @flags: TEE_SHM_* above
- */
+struct tee_shm {
struct udevice *dev;
struct list_head link;
void *addr;
ulong size;
u32 flags;
+};
+/**
- struct tee_param_memref - parameter memory reference
Used for ...?
- @shm_offs: Offset in bytes into the shared memory object @shm
- @size: Size in bytes of the memory reference
- @shm: Pointer to a shared memory object for the buffer
- */
+struct tee_param_memref {
ulong shm_offs;
ulong size;
struct tee_shm *shm;
+};
+/**
- struct tee_param_value - value parameter
??
This doesn't really explain anything. What is it for?
- @a, @b, @c: Parameters passed by value
- */
+struct tee_param_value {
u64 a;
u64 b;
u64 c;
+};
+/**
- struct tee_param - invoke parameter
- @attr: Attributes
- @u.memref: Memref parameter if (@attr & TEE_PARAM_ATTR_MASK) is one of
TEE_PARAM_ATTR_TYPE_MEMREF_* above
- @u.value: Value parameter if (@attr & TEE_PARAM_ATTR_MASK) is one of
TEE_PARAM_ATTR_TYPE_VALUE_* above
- */
+struct tee_param {
u64 attr;
union {
struct tee_param_memref memref;
struct tee_param_value value;
} u;
+};
+/**
- struct tee_open_session_arg - extra arguments for tee_open_session()
- @uuid: [in] UUID of the Trusted Application
- @clnt_uuid: [in] Normally zeroes
- @clnt_login: [in] Normally 0
- @session: [out] Session id
- @ret: [out] return value
- @ret_origin: [out] origin of the return value
- */
+struct tee_open_session_arg {
u8 uuid[TEE_UUID_LEN];
u8 clnt_uuid[TEE_UUID_LEN];
u32 clnt_login;
u32 session;
u32 ret;
u32 ret_origin;
Do these use the sized u32 type because it is an API of some sort? Otherwise, why not uint?
+};
+/**
- struct tee_invoke_arg - extra arguments for tee_invoke_func()
- @func: [in] Trusted Application function, specific to the TA
- @session: [in] Session id, from open session
- @ret: [out] return value
- @ret_origin: [out] origin of the return value
- */
+struct tee_invoke_arg {
u32 func;
u32 session;
u32 ret;
u32 ret_origin;
+};
+/**
- struct tee_version_data - description of TEE
- @gen_caps: Generic capabilities, TEE_GEN_CAP_* above
- */
+struct tee_version_data {
u32 gen_caps;
+};
+/**
- struct tee_driver_ops - TEE driver operations
- @get_version: Query capabilities of TEE device,
see tee_get_version()
- @open_session: Opens a session to a Trusted Application in the TEE,
see tee_open_session().
- @close_session: Closes a session to Trusted Application,
see tee_close_session()
- @invoke_func: Invokes a function in a Trusted Application,
see tee_invoke_func()
These comments should be against each function in the struct below. You should repeat the comment from the exported functions there too.
- */
+struct tee_driver_ops {
void (*get_version)(struct udevice *dev, struct tee_version_data *vers);
int (*open_session)(struct udevice *dev,
struct tee_open_session_arg *arg, ulong num_param,
struct tee_param *param);
int (*close_session)(struct udevice *dev, u32 session);
int (*invoke_func)(struct udevice *dev, struct tee_invoke_arg *arg,
ulong num_param, struct tee_param *param);
/**
* shm_register() - Registers memory shared with the TEE
* @dev: The TEE device
* @shm: Pointer to a shared memory object
* Returns 0 on success or < 0 on failure.
*/
int (*shm_register)(struct udevice *dev, struct tee_shm *shm);
/**
* shm_unregister() - Unregisters memory shared with the TEE
* @dev: The TEE device
* @shm: Pointer to a shared memory object
* Returns 0 on success or < 0 on failure.
*/
int (*shm_unregister)(struct udevice *dev, struct tee_shm *shm);
+};
[...]
Regards, Simon

On Wed, Aug 29, 2018 at 06:28:48PM -0600, Simon Glass wrote:
Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds a uclass to interface with a TEE (Trusted Execution Environment).
A TEE driver is a driver that interfaces with a trusted OS running in some secure environment, for example, TrustZone on ARM cpus, or a separate secure co-processor etc.
The TEE subsystem can serve a TEE driver for a Global Platform compliant TEE, but it's not limited to only Global Platform TEEs.
The over all design is based on the TEE subsystem in the Linux kernel, tailored for U-boot.
U-Boot
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 6 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/tee/Kconfig | 8 ++ drivers/tee/Makefile | 3 + drivers/tee/tee-uclass.c | 192 ++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/tee.h | 290 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 503 insertions(+) create mode 100644 drivers/tee/Kconfig create mode 100644 drivers/tee/Makefile create mode 100644 drivers/tee/tee-uclass.c create mode 100644 include/tee.h
Reviewed-by: Simon Glass sjg@chromium.org
Various nits below.
diff --git a/MAINTAINERS b/MAINTAINERS index 58b61ac05882..7458c606ee92 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -571,6 +571,12 @@ TQ GROUP S: Orphaned (Since 2016-02) T: git git://git.denx.de/u-boot-tq-group.git
+TEE +M: Jens Wiklander jens.wiklander@linaro.org +S: Maintained +F: drivers/tee/ +F: include/tee.h
UBI M: Kyungmin Park kmpark@infradead.org M: Heiko Schocher hs@denx.de diff --git a/drivers/Kconfig b/drivers/Kconfig index c72abf893297..f3249ab1d143 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -94,6 +94,8 @@ source "drivers/spmi/Kconfig"
source "drivers/sysreset/Kconfig"
+source "drivers/tee/Kconfig"
source "drivers/thermal/Kconfig"
source "drivers/timer/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index d53208540ea6..0fcae36f50f7 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -103,6 +103,7 @@ obj-y += smem/ obj-y += soc/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-y += thermal/ +obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ endif diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig new file mode 100644 index 000000000000..817ab331b0f8 --- /dev/null +++ b/drivers/tee/Kconfig @@ -0,0 +1,8 @@ +# Generic Trusted Execution Environment Configuration +config TEE
bool "Trusted Execution Environment support"
depends on ARM && (ARM64 || CPU_V7A)
select ARM_SMCCC
help
This implements a generic interface towards a Trusted Execution
Environment (TEE).
Please expand this. What is it? Why would I use it? Also perhaps add a web link?
I'll expand it. I'll skip the why part though.
diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile new file mode 100644 index 000000000000..b6d8e16e6211 --- /dev/null +++ b/drivers/tee/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+
+obj-y += tee-uclass.o diff --git a/drivers/tee/tee-uclass.c b/drivers/tee/tee-uclass.c new file mode 100644 index 000000000000..0209983491a0 --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <dm.h> +#include <tee.h>
+/**
- struct tee_uclass_priv - information of a TEE, stored by the uclass
- @list_shm: list of structe tee_shm representing memory blocks shared
with the TEE.
- */
+struct tee_uclass_priv {
struct list_head list_shm;
+};
+static const struct tee_driver_ops *tee_get_ops(struct udevice *dev) +{
return device_get_ops(dev);
+}
+void tee_get_version(struct udevice *dev, struct tee_version_data *vers) +{
tee_get_ops(dev)->get_version(dev, vers);
+}
+int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg,
ulong num_param, struct tee_param *param)
+{
return tee_get_ops(dev)->open_session(dev, arg, num_param, param);
+}
+int tee_close_session(struct udevice *dev, u32 session) +{
return tee_get_ops(dev)->close_session(dev, session);
+}
+int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg,
ulong num_param, struct tee_param *param)
+{
return tee_get_ops(dev)->invoke_func(dev, arg, num_param, param);
+}
+struct tee_shm *__tee_shm_add(struct udevice *dev, ulong align, void *addr,
ulong size, u32 flags)
+{
struct tee_shm *shm;
void *p = addr;
if (flags & TEE_SHM_ALLOC) {
if (align)
p = memalign(align, size);
else
p = malloc(size);
}
if (!p)
return NULL;
shm = calloc(1, sizeof(*shm));
if (!shm)
goto err;
shm->dev = dev;
shm->addr = p;
shm->size = size;
shm->flags = flags;
if ((flags & TEE_SHM_SEC_REGISTER) &&
tee_get_ops(dev)->shm_register(dev, shm))
goto err;
if (flags & TEE_SHM_REGISTER) {
struct tee_uclass_priv *priv = dev_get_uclass_priv(dev);
list_add(&shm->link, &priv->list_shm);
}
return shm;
+err:
free(shm);
if (flags & TEE_SHM_ALLOC)
free(p);
return NULL;
+}
+struct tee_shm *tee_shm_alloc(struct udevice *dev, ulong size,
u32 flags)
+{
u32 f = flags;
f |= TEE_SHM_SEC_REGISTER | TEE_SHM_REGISTER | TEE_SHM_ALLOC;
blank line here
return __tee_shm_add(dev, 0, NULL, size, f);
+}
+struct tee_shm *tee_shm_register(struct udevice *dev, void *addr,
ulong size, u32 flags)
+{
u32 f = flags & ~TEE_SHM_ALLOC;
f |= TEE_SHM_SEC_REGISTER | TEE_SHM_REGISTER;
and here
return __tee_shm_add(dev, 0, addr, size, f);
+}
+void tee_shm_free(struct tee_shm *shm) +{
if (!shm)
return;
if (shm->flags & TEE_SHM_SEC_REGISTER)
tee_get_ops(shm->dev)->shm_unregister(shm->dev, shm);
if (shm->flags & TEE_SHM_REGISTER)
list_del(&shm->link);
if (shm->flags & TEE_SHM_ALLOC)
free(shm->addr);
free(shm);
+}
+bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev) +{
struct tee_uclass_priv *priv = dev_get_uclass_priv(dev);
struct tee_shm *s;
list_for_each_entry(s, &priv->list_shm, link)
if (s == shm)
return true;
return false;
+}
+struct udevice *tee_find_device(struct udevice *start,
int (*match)(struct tee_version_data *vers,
const void *data),
const void *data,
struct tee_version_data *vers)
Please add function comment
There is one in include/tee.h
+{
struct udevice *dev = start;
struct tee_version_data lv;
struct tee_version_data *v = vers ? vers : &lv;
if (!dev)
uclass_first_device(UCLASS_TEE, &dev);
This can return an error if it finds a device but it tails to probe. If you don't care about that, I suppose it is OK.
Thanks. I need the first device that probes OK and then the next devices that probes OK and so on. I'll need to make some small changes here.
for (; dev; uclass_next_device(&dev)) {
tee_get_ops(dev)->get_version(dev, v);
if (!match || match(v, data))
return dev;
}
return NULL;
+}
+static int tee_pre_probe(struct udevice *dev) +{
struct tee_uclass_priv *priv = dev_get_uclass_priv(dev);
INIT_LIST_HEAD(&priv->list_shm);
return 0;
+}
+static int tee_pre_remove(struct udevice *dev) +{
struct tee_uclass_priv *priv = dev_get_uclass_priv(dev);
struct tee_shm *shm;
/*
* Any remaining shared memory must be unregistered now as U-boot
U-Boot
* is about to hand over to the next stage and that memory will be
* reused.
*/
while (!list_empty(&priv->list_shm)) {
shm = list_first_entry(&priv->list_shm, struct tee_shm, link);
debug("%s: freeing leftover shm %p (size %lu, flags %#x)\n",
__func__, (void *)shm, shm->size, shm->flags);
tee_shm_free(shm);
}
return 0;
+}
+UCLASS_DRIVER(tee) = {
.id = UCLASS_TEE,
.name = "tee",
.per_device_auto_alloc_size = sizeof(struct tee_uclass_priv),
.pre_probe = tee_pre_probe,
.pre_remove = tee_pre_remove,
+}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a39643ec5eef..955e0a915b87 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -81,6 +81,7 @@ enum uclass_id { UCLASS_SPI_GENERIC, /* Generic SPI flash target */ UCLASS_SYSCON, /* System configuration device */ UCLASS_SYSRESET, /* System reset device */
UCLASS_TEE, /* Trusted Execution Environment device */ UCLASS_THERMAL, /* Thermal sensor */ UCLASS_TIMER, /* Timer device */ UCLASS_TPM, /* Trusted Platform Module TIS interface */
diff --git a/include/tee.h b/include/tee.h new file mode 100644 index 000000000000..3e6771123ef0 --- /dev/null +++ b/include/tee.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#ifndef __TEE_H +#define __TEE_H
+#include <common.h> +#include <dm.h>
Please drop both of those.
+#define TEE_UUID_LEN 16
+#define TEE_GEN_CAP_GP BIT(0) /* GlobalPlatform compliant TEE */ +#define TEE_GEN_CAP_REG_MEM BIT(1) /* Supports registering shared memory */
+#define TEE_SHM_REGISTER BIT(0) /* In list of shared memory */ +#define TEE_SHM_SEC_REGISTER BIT(1) /* TEE notified of this memory */ +#define TEE_SHM_ALLOC BIT(2) /* The memory is malloced() and must */
/* be freed() */
+#define TEE_PARAM_ATTR_TYPE_NONE 0 /* parameter not used */ +#define TEE_PARAM_ATTR_TYPE_VALUE_INPUT 1 +#define TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT 2 +#define TEE_PARAM_ATTR_TYPE_VALUE_INOUT 3 /* input and output */ +#define TEE_PARAM_ATTR_TYPE_MEMREF_INPUT 5 +#define TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT 6 +#define TEE_PARAM_ATTR_TYPE_MEMREF_INOUT 7 /* input and output */ +#define TEE_PARAM_ATTR_TYPE_MASK 0xff +#define TEE_PARAM_ATTR_META 0x100 +#define TEE_PARAM_ATTR_MASK (TEE_PARAM_ATTR_TYPE_MASK | \
TEE_PARAM_ATTR_META)
+/*
- Some Global Platform error codes which has a meaning if the
- TEE_GEN_CAP_GP bit is returned by the driver in
- struct tee_version_data::gen_caps
- */
+#define TEE_SUCCESS 0x00000000 +#define TEE_ERROR_GENERIC 0xffff0000 +#define TEE_ERROR_BAD_PARAMETERS 0xffff0006 +#define TEE_ERROR_ITEM_NOT_FOUND 0xffff0008 +#define TEE_ERROR_NOT_IMPLEMENTED 0xffff0009 +#define TEE_ERROR_NOT_SUPPORTED 0xffff000a +#define TEE_ERROR_COMMUNICATION 0xffff000e +#define TEE_ERROR_OUT_OF_MEMORY 0xffff000c +#define TEE_ERROR_TARGET_DEAD 0xffff3024
+#define TEE_ORIGIN_COMMS 0x00000002
+/**
- struct tee_shm - memory shared with the TEE
- @dev: The TEE device
- @link: List node in the list in struct struct tee_uclass_priv
- @addr: Pointer to the shared memory
- @size: Size of the the shared memory
- @flags: TEE_SHM_* above
- */
+struct tee_shm {
struct udevice *dev;
struct list_head link;
void *addr;
ulong size;
u32 flags;
+};
+/**
- struct tee_param_memref - parameter memory reference
Used for ...?
- @shm_offs: Offset in bytes into the shared memory object @shm
- @size: Size in bytes of the memory reference
- @shm: Pointer to a shared memory object for the buffer
- */
+struct tee_param_memref {
ulong shm_offs;
ulong size;
struct tee_shm *shm;
+};
+/**
- struct tee_param_value - value parameter
??
This doesn't really explain anything. What is it for?
I'll expand and refer to struct tee_param below.
- @a, @b, @c: Parameters passed by value
- */
+struct tee_param_value {
u64 a;
u64 b;
u64 c;
+};
+/**
- struct tee_param - invoke parameter
- @attr: Attributes
- @u.memref: Memref parameter if (@attr & TEE_PARAM_ATTR_MASK) is one of
TEE_PARAM_ATTR_TYPE_MEMREF_* above
- @u.value: Value parameter if (@attr & TEE_PARAM_ATTR_MASK) is one of
TEE_PARAM_ATTR_TYPE_VALUE_* above
- */
+struct tee_param {
u64 attr;
union {
struct tee_param_memref memref;
struct tee_param_value value;
} u;
+};
+/**
- struct tee_open_session_arg - extra arguments for tee_open_session()
- @uuid: [in] UUID of the Trusted Application
- @clnt_uuid: [in] Normally zeroes
- @clnt_login: [in] Normally 0
- @session: [out] Session id
- @ret: [out] return value
- @ret_origin: [out] origin of the return value
- */
+struct tee_open_session_arg {
u8 uuid[TEE_UUID_LEN];
u8 clnt_uuid[TEE_UUID_LEN];
u32 clnt_login;
u32 session;
u32 ret;
u32 ret_origin;
Do these use the sized u32 type because it is an API of some sort? Otherwise, why not uint?
Yes, the minimum width is 32-bits and wider than that is waste.
+};
+/**
- struct tee_invoke_arg - extra arguments for tee_invoke_func()
- @func: [in] Trusted Application function, specific to the TA
- @session: [in] Session id, from open session
- @ret: [out] return value
- @ret_origin: [out] origin of the return value
- */
+struct tee_invoke_arg {
u32 func;
u32 session;
u32 ret;
u32 ret_origin;
+};
+/**
- struct tee_version_data - description of TEE
- @gen_caps: Generic capabilities, TEE_GEN_CAP_* above
- */
+struct tee_version_data {
u32 gen_caps;
+};
+/**
- struct tee_driver_ops - TEE driver operations
- @get_version: Query capabilities of TEE device,
see tee_get_version()
- @open_session: Opens a session to a Trusted Application in the TEE,
see tee_open_session().
- @close_session: Closes a session to Trusted Application,
see tee_close_session()
- @invoke_func: Invokes a function in a Trusted Application,
see tee_invoke_func()
These comments should be against each function in the struct below. You should repeat the comment from the exported functions there too.
I'll fix.
- */
+struct tee_driver_ops {
void (*get_version)(struct udevice *dev, struct tee_version_data *vers);
int (*open_session)(struct udevice *dev,
struct tee_open_session_arg *arg, ulong num_param,
struct tee_param *param);
int (*close_session)(struct udevice *dev, u32 session);
int (*invoke_func)(struct udevice *dev, struct tee_invoke_arg *arg,
ulong num_param, struct tee_param *param);
/**
* shm_register() - Registers memory shared with the TEE
* @dev: The TEE device
* @shm: Pointer to a shared memory object
* Returns 0 on success or < 0 on failure.
*/
int (*shm_register)(struct udevice *dev, struct tee_shm *shm);
/**
* shm_unregister() - Unregisters memory shared with the TEE
* @dev: The TEE device
* @shm: Pointer to a shared memory object
* Returns 0 on success or < 0 on failure.
*/
int (*shm_unregister)(struct udevice *dev, struct tee_shm *shm);
+};
[...]
Thanks for the review. -- Jens

Sync with c8bfafb15944 ("dt/bindings: add bindings for optee") from Linux kernel.
Introduces linaro prefix and adds bindings for ARM TrustZone based OP-TEE implementation.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- .../firmware/linaro,optee-tz.txt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 doc/device-tree-bindings/firmware/linaro,optee-tz.txt
diff --git a/doc/device-tree-bindings/firmware/linaro,optee-tz.txt b/doc/device-tree-bindings/firmware/linaro,optee-tz.txt new file mode 100644 index 000000000000..d38834c67dff --- /dev/null +++ b/doc/device-tree-bindings/firmware/linaro,optee-tz.txt @@ -0,0 +1,31 @@ +OP-TEE Device Tree Bindings + +OP-TEE is a piece of software using hardware features to provide a Trusted +Execution Environment. The security can be provided with ARM TrustZone, but +also by virtualization or a separate chip. + +We're using "linaro" as the first part of the compatible property for +the reference implementation maintained by Linaro. + +* OP-TEE based on ARM TrustZone required properties: + +- compatible : should contain "linaro,optee-tz" + +- method : The method of calling the OP-TEE Trusted OS. Permitted + values are: + + "smc" : SMC #0, with the register assignments specified + in drivers/tee/optee/optee_smc.h + + "hvc" : HVC #0, with the register assignments specified + in drivers/tee/optee/optee_smc.h + + + +Example: + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + };

Adds a OP-TEE driver.
* Targets ARM and ARM64 * Supports using any u-boot memory as shared memory * Probes OP-TEE version using SMCs * Uses OPTEE message protocol version 2 to communicate with secure world
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/tee/Kconfig | 10 + drivers/tee/Makefile | 1 + drivers/tee/optee/Kconfig | 7 + drivers/tee/optee/Makefile | 4 + drivers/tee/optee/core.c | 614 +++++++++++++++++++++++ drivers/tee/optee/optee_msg.h | 423 ++++++++++++++++ drivers/tee/optee/optee_msg_supplicant.h | 234 +++++++++ drivers/tee/optee/optee_private.h | 12 + drivers/tee/optee/optee_smc.h | 444 ++++++++++++++++ drivers/tee/optee/supplicant.c | 89 ++++ 10 files changed, 1838 insertions(+) create mode 100644 drivers/tee/optee/Kconfig create mode 100644 drivers/tee/optee/Makefile create mode 100644 drivers/tee/optee/core.c create mode 100644 drivers/tee/optee/optee_msg.h create mode 100644 drivers/tee/optee/optee_msg_supplicant.h create mode 100644 drivers/tee/optee/optee_private.h create mode 100644 drivers/tee/optee/optee_smc.h create mode 100644 drivers/tee/optee/supplicant.c
diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig index 817ab331b0f8..3e7fe6ddcc5d 100644 --- a/drivers/tee/Kconfig +++ b/drivers/tee/Kconfig @@ -6,3 +6,13 @@ config TEE help This implements a generic interface towards a Trusted Execution Environment (TEE). + +if TEE + +menu "TEE drivers" + +source "drivers/tee/optee/Kconfig" + +endmenu + +endif diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile index b6d8e16e6211..19633b60f235 100644 --- a/drivers/tee/Makefile +++ b/drivers/tee/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+
obj-y += tee-uclass.o +obj-$(CONFIG_OPTEE) += optee/ diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig new file mode 100644 index 000000000000..8f7ebe161111 --- /dev/null +++ b/drivers/tee/optee/Kconfig @@ -0,0 +1,7 @@ +# OP-TEE Trusted Execution Environment Configuration +config OPTEE + bool "OP-TEE" + depends on ARM_SMCCC + help + This implements the OP-TEE Trusted Execution Environment (TEE) + driver. diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile new file mode 100644 index 000000000000..6148feb474a5 --- /dev/null +++ b/drivers/tee/optee/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += core.o +obj-y += supplicant.o diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c new file mode 100644 index 000000000000..f2d92d96551b --- /dev/null +++ b/drivers/tee/optee/core.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018 Linaro Limited + */ + +#include <common.h> +#include <dm.h> +#include <linux/arm-smccc.h> +#include <linux/io.h> +#include <log.h> +#include <tee.h> + +#include "optee_smc.h" +#include "optee_msg.h" +#include "optee_private.h" + +#define PAGELIST_ENTRIES_PER_PAGE \ + ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1) + +typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, + struct arm_smccc_res *); + +struct optee_pdata { + optee_invoke_fn *invoke_fn; +}; + +struct rpc_param { + u32 a0; + u32 a1; + u32 a2; + u32 a3; + u32 a4; + u32 a5; + u32 a6; + u32 a7; +}; + +static void *reg_pair_to_ptr(u32 reg0, u32 reg1) +{ + return (void *)(ulong)(((u64)reg0 << 32) | reg1); +} + +static void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val) +{ + *reg0 = val >> 32; + *reg1 = val; +} + +void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr) +{ + const unsigned int page_size = OPTEE_MSG_NONCONTIG_PAGE_SIZE; + const phys_addr_t page_mask = page_size - 1; + u8 *buf_base; + unsigned int page_offset; + unsigned int num_pages; + unsigned int list_size; + unsigned int n; + void *page_list; + struct { + u64 pages_list[PAGELIST_ENTRIES_PER_PAGE]; + u64 next_page_data; + } *pages_data; + + page_offset = (ulong)buf & page_mask; + num_pages = roundup(page_offset + len, page_size) / page_size; + list_size = DIV_ROUND_UP(num_pages, PAGELIST_ENTRIES_PER_PAGE) * + page_size; + page_list = memalign(page_size, list_size); + if (!page_list) + return NULL; + + pages_data = page_list; + buf_base = (u8 *)(rounddown((ulong)buf, page_size)); + n = 0; + while (num_pages) { + pages_data->pages_list[n] = virt_to_phys(buf_base); + n++; + buf_base += page_size; + num_pages--; + + if (n == PAGELIST_ENTRIES_PER_PAGE) { + pages_data->next_page_data = + virt_to_phys(pages_data + 1); + pages_data++; + n = 0; + } + } + + *phys_buf_ptr = virt_to_phys(page_list) | page_offset; + return page_list; +} + +static void optee_get_version(struct udevice *dev, + struct tee_version_data *vers) +{ + struct tee_version_data v = { + .gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM, + }; + + *vers = v; +} + +static struct tee_shm *get_msg_arg(struct udevice *dev, ulong num_params, + struct optee_msg_arg **msg_arg) +{ + struct tee_shm *shm; + struct optee_msg_arg *ma; + + shm = __tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL, + OPTEE_MSG_GET_ARG_SIZE(num_params), TEE_SHM_ALLOC); + if (!shm) + return NULL; + + ma = shm->addr; + memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params)); + ma->num_params = num_params; + *msg_arg = ma; + + return shm; +} + +static int to_msg_param(struct optee_msg_param *msg_params, ulong num_params, + const struct tee_param *params) +{ + ulong 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_PARAM_ATTR_TYPE_NONE: + mp->attr = OPTEE_MSG_ATTR_TYPE_NONE; + memset(&mp->u, 0, sizeof(mp->u)); + break; + case TEE_PARAM_ATTR_TYPE_VALUE_INPUT: + case TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_PARAM_ATTR_TYPE_VALUE_INOUT: + mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - + TEE_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; + break; + case TEE_PARAM_ATTR_TYPE_MEMREF_INPUT: + case TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_PARAM_ATTR_TYPE_MEMREF_INOUT: + mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr - + TEE_PARAM_ATTR_TYPE_MEMREF_INPUT; + mp->u.rmem.shm_ref = (ulong)p->u.memref.shm; + mp->u.rmem.size = p->u.memref.size; + mp->u.rmem.offs = p->u.memref.shm_offs; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static int from_msg_param(struct tee_param *params, ulong num_params, + const struct optee_msg_param *msg_params) +{ + ulong n; + struct tee_shm *shm; + + 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_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: + p->attr = TEE_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; + 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_PARAM_ATTR_TYPE_MEMREF_INPUT + attr - + OPTEE_MSG_ATTR_TYPE_RMEM_INPUT; + p->u.memref.size = mp->u.rmem.size; + shm = (struct tee_shm *)(ulong)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; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static void handle_rpc(struct udevice *dev, struct rpc_param *param, + void *page_list) +{ + struct tee_shm *shm; + + switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) { + case OPTEE_SMC_RPC_FUNC_ALLOC: + shm = __tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL, + param->a1, + TEE_SHM_ALLOC | TEE_SHM_REGISTER); + if (shm) { + reg_pair_from_64(¶m->a1, ¶m->a2, + virt_to_phys(shm->addr)); + /* "cookie" */ + reg_pair_from_64(¶m->a4, ¶m->a5, (ulong)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: + break; + case OPTEE_SMC_RPC_FUNC_CMD: + shm = reg_pair_to_ptr(param->a1, param->a2); + optee_suppl_cmd(dev, shm, page_list); + break; + default: + break; + } + + param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; +} + +static u32 call_err_to_res(u32 call_err) +{ + switch (call_err) { + case OPTEE_SMC_RETURN_OK: + return TEE_SUCCESS; + default: + return TEE_ERROR_BAD_PARAMETERS; + } +} + +static u32 do_call_with_arg(struct udevice *dev, struct optee_msg_arg *arg) +{ + struct optee_pdata *pdata = dev_get_platdata(dev); + struct rpc_param param = { .a0 = OPTEE_SMC_CALL_WITH_ARG }; + void *page_list = NULL; + + reg_pair_from_64(¶m.a1, ¶m.a2, virt_to_phys(arg)); + while (true) { + struct arm_smccc_res res; + + pdata->invoke_fn(param.a0, param.a1, param.a2, param.a3, + param.a4, param.a5, param.a6, param.a7, &res); + + free(page_list); + page_list = NULL; + + if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { + param.a0 = res.a0; + param.a1 = res.a1; + param.a2 = res.a2; + param.a3 = res.a3; + handle_rpc(dev, ¶m, &page_list); + } else { + return call_err_to_res(res.a0); + } + } +} + +static int optee_close_session(struct udevice *dev, u32 session) +{ + struct tee_shm *shm; + struct optee_msg_arg *msg_arg; + + shm = get_msg_arg(dev, 0, &msg_arg); + if (!shm) + return -ENOMEM; + + msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; + msg_arg->session = session; + do_call_with_arg(dev, msg_arg); + + tee_shm_free(shm); + return 0; +} + +static int optee_open_session(struct udevice *dev, + struct tee_open_session_arg *arg, + ulong num_params, struct tee_param *params) +{ + int rc; + struct tee_shm *shm; + struct optee_msg_arg *msg_arg; + + shm = get_msg_arg(dev, num_params + 2, &msg_arg); + if (!shm) + return -ENOMEM; + + msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION; + /* + * Initialize and add the meta parameters needed when opening a + * session. + */ + msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | + OPTEE_MSG_ATTR_META; + msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | + OPTEE_MSG_ATTR_META; + memcpy(&msg_arg->params[0].u.value, arg->uuid, sizeof(arg->uuid)); + memcpy(&msg_arg->params[1].u.value, arg->uuid, sizeof(arg->clnt_uuid)); + msg_arg->params[1].u.value.c = arg->clnt_login; + + rc = to_msg_param(msg_arg->params + 2, num_params, params); + if (rc) + goto out; + + arg->ret = do_call_with_arg(dev, msg_arg); + if (arg->ret) { + arg->ret_origin = TEE_ORIGIN_COMMS; + goto out; + } + + if (from_msg_param(params, num_params, msg_arg->params + 2)) { + arg->ret = TEE_ERROR_COMMUNICATION; + arg->ret_origin = TEE_ORIGIN_COMMS; + /* Close session again to avoid leakage */ + optee_close_session(dev, msg_arg->session); + goto out; + } + + arg->session = msg_arg->session; + arg->ret = msg_arg->ret; + arg->ret_origin = msg_arg->ret_origin; +out: + tee_shm_free(shm); + + return rc; +} + +static int optee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg, + ulong num_params, struct tee_param *params) +{ + struct tee_shm *shm; + struct optee_msg_arg *msg_arg; + int rc; + + shm = get_msg_arg(dev, num_params, &msg_arg); + if (!shm) + return -ENOMEM; + msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; + msg_arg->func = arg->func; + msg_arg->session = arg->session; + + rc = to_msg_param(msg_arg->params, num_params, params); + if (rc) + goto out; + + arg->ret = do_call_with_arg(dev, msg_arg); + if (arg->ret) { + arg->ret_origin = TEE_ORIGIN_COMMS; + goto out; + } + + if (from_msg_param(params, num_params, msg_arg->params)) { + arg->ret = TEE_ERROR_COMMUNICATION; + arg->ret_origin = TEE_ORIGIN_COMMS; + goto out; + } + + arg->ret = msg_arg->ret; + arg->ret_origin = msg_arg->ret_origin; +out: + tee_shm_free(shm); + return rc; +} + +static int optee_shm_register(struct udevice *dev, struct tee_shm *shm) +{ + struct tee_shm *shm_arg; + struct optee_msg_arg *msg_arg; + void *pl; + u64 ph_ptr; + int rc = 0; + + shm_arg = get_msg_arg(dev, 1, &msg_arg); + if (!shm_arg) + return -ENOMEM; + + pl = optee_alloc_and_init_page_list(shm->addr, shm->size, &ph_ptr); + if (!pl) { + rc = -ENOMEM; + goto out; + } + + 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.buf_ptr = ph_ptr; + msg_arg->params->u.tmem.shm_ref = (ulong)shm; + msg_arg->params->u.tmem.size = shm->size; + + if (do_call_with_arg(dev, msg_arg) || msg_arg->ret) + rc = -EINVAL; + + free(pl); +out: + tee_shm_free(shm_arg); + return rc; +} + +static int optee_shm_unregister(struct udevice *dev, struct tee_shm *shm) +{ + struct tee_shm *shm_arg; + struct optee_msg_arg *msg_arg; + int rc = 0; + + shm_arg = get_msg_arg(dev, 1, &msg_arg); + if (!shm_arg) + return -ENOMEM; + + 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 = (ulong)shm; + + if (do_call_with_arg(dev, msg_arg) || msg_arg->ret) + rc = -EINVAL; + tee_shm_free(shm_arg); + return rc; +} + +static const struct tee_driver_ops optee_ops = { + .get_version = optee_get_version, + .open_session = optee_open_session, + .close_session = optee_close_session, + .invoke_func = optee_invoke_func, + .shm_register = optee_shm_register, + .shm_unregister = optee_shm_unregister, +}; + +static bool 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); + + return res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && + res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3; +} + +static void print_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) + debug("OP-TEE revision %lu.%lu (%08lx)\n", res.result.major, + res.result.minor, res.result.build_id); + else + debug("OP-TEE revision %lu.%lu\n", res.result.major, + res.result.minor); +} + +static bool 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); + + return res.result.major == OPTEE_MSG_REVISION_MAJOR && + (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR; +} + +static bool exchange_capabilities(optee_invoke_fn *invoke_fn, u32 *sec_caps) +{ + union { + struct arm_smccc_res smccc; + struct optee_smc_exchange_capabilities_result result; + } res; + + invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, + OPTEE_SMC_NSEC_CAP_UNIPROCESSOR, 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; +} + +/* 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 udevice *dev) +{ + const char *method; + + debug("optee: looking for conduit method in DT.\n"); + method = ofnode_get_property(dev->node, "method", NULL); + if (!method) { + debug("optee: 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; + + debug("optee: invalid "method" property: %s\n", method); + return ERR_PTR(-EINVAL); +} + +static int optee_ofdata_to_platdata(struct udevice *dev) +{ + struct optee_pdata *pdata = dev_get_platdata(dev); + + pdata->invoke_fn = get_invoke_func(dev); + if (IS_ERR(pdata->invoke_fn)) + return PTR_ERR(pdata->invoke_fn); + + return 0; +} + +static int optee_probe(struct udevice *dev) +{ + struct optee_pdata *pdata = dev_get_platdata(dev); + u32 sec_caps; + + if (!is_optee_api(pdata->invoke_fn)) { + debug("%s: OP-TEE api uid mismatch\n", __func__); + return -ENOENT; + } + + print_os_revision(pdata->invoke_fn); + + if (!api_revision_is_compatible(pdata->invoke_fn)) { + debug("%s: OP-TEE api revision mismatch\n", __func__); + return -ENOENT; + } + + /* + * OP-TEE can use both shared memory via predefined pool or as + * dynamic shared memory provided by normal world. To keep things + * simple we're only using dynamic shared memory in this driver. + */ + if (!exchange_capabilities(pdata->invoke_fn, &sec_caps) || + !(sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)) { + debug("%s: OP-TEE capabilities mismatch\n", __func__); + return -ENOENT; + } + + return 0; +} + +static const struct udevice_id optee_match[] = { + { .compatible = "linaro,optee-tz" }, + {}, +}; + +U_BOOT_DRIVER(optee) = { + .name = "optee", + .id = UCLASS_TEE, + .of_match = optee_match, + .ofdata_to_platdata = optee_ofdata_to_platdata, + .probe = optee_probe, + .ops = &optee_ops, + .platdata_auto_alloc_size = sizeof(struct optee_pdata), +}; diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h new file mode 100644 index 000000000000..ebc16aa8cc31 --- /dev/null +++ b/drivers/tee/optee/optee_msg.h @@ -0,0 +1,423 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2015-2018, Linaro Limited + */ + +#ifndef _OPTEE_MSG_H +#define _OPTEE_MSG_H + +#include <linux/bitops.h> +#include <linux/types.h> + +/* + * This file defines the OP-TEE message protocol used to communicate + * with an instance of OP-TEE running in secure world. + * + * This file is divided into three sections. + * 1. Formatting of messages. + * 2. Requests from normal world + * 3. Requests from secure world, Remote Procedure Call (RPC), handled by + * tee-supplicant. + */ + +/***************************************************************************** + * Part 1 - formatting of messages + *****************************************************************************/ + +#define OPTEE_MSG_ATTR_TYPE_NONE 0x0 +#define OPTEE_MSG_ATTR_TYPE_VALUE_INPUT 0x1 +#define OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT 0x2 +#define OPTEE_MSG_ATTR_TYPE_VALUE_INOUT 0x3 +#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_TMEM_INPUT 0x9 +#define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT 0xa +#define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT 0xb + +#define OPTEE_MSG_ATTR_TYPE_MASK GENMASK(7, 0) + +/* + * Meta parameter to be absorbed by the Secure OS and not passed + * to the Trusted Application. + * + * Currently only used with OPTEE_MSG_CMD_OPEN_SESSION. + */ +#define OPTEE_MSG_ATTR_META BIT(8) + +/* + * Pointer to a list of pages used to register user-defined SHM buffer. + * Used with OPTEE_MSG_ATTR_TYPE_TMEM_*. + * buf_ptr should point to the beginning of the buffer. Buffer will contain + * list of page addresses. OP-TEE core can reconstruct contiguous buffer from + * that page addresses list. Page addresses are stored as 64 bit values. + * Last entry on a page should point to the next page of buffer. + * Every entry in buffer should point to a 4k page beginning (12 least + * significant bits must be equal to zero). + * + * 12 least significant bints of optee_msg_param.u.tmem.buf_ptr should hold page + * offset of the user buffer. + * + * So, entries should be placed like members of this structure: + * + * struct page_data { + * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1]; + * uint64_t next_page_data; + * }; + * + * Structure is designed to exactly fit into the page size + * OPTEE_MSG_NONCONTIG_PAGE_SIZE which is a standard 4KB page. + * + * The size of 4KB is chosen because this is the smallest page size for ARM + * architectures. If REE uses larger pages, it should divide them to 4KB ones. + */ +#define OPTEE_MSG_ATTR_NONCONTIG BIT(9) + +/* + * Memory attributes for caching passed with temp memrefs. The actual value + * used is defined outside the message protocol with the exception of + * OPTEE_MSG_ATTR_CACHE_PREDEFINED which means the attributes already + * defined for the memory range should be used. If optee_smc.h is used as + * bearer of this protocol OPTEE_SMC_SHM_* is used for values. + */ +#define OPTEE_MSG_ATTR_CACHE_SHIFT 16 +#define OPTEE_MSG_ATTR_CACHE_MASK GENMASK(2, 0) +#define OPTEE_MSG_ATTR_CACHE_PREDEFINED 0 + +/* + * Same values as TEE_LOGIN_* from TEE Internal API + */ +#define OPTEE_MSG_LOGIN_PUBLIC 0x00000000 +#define OPTEE_MSG_LOGIN_USER 0x00000001 +#define OPTEE_MSG_LOGIN_GROUP 0x00000002 +#define OPTEE_MSG_LOGIN_APPLICATION 0x00000004 +#define OPTEE_MSG_LOGIN_APPLICATION_USER 0x00000005 +#define OPTEE_MSG_LOGIN_APPLICATION_GROUP 0x00000006 + +/* + * Page size used in non-contiguous buffer entries + */ +#define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096 + +/** + * struct optee_msg_param_tmem - temporary memory reference parameter + * @buf_ptr: Address of the buffer + * @size: Size of the buffer + * @shm_ref: Temporary shared memory reference, pointer to a struct tee_shm + * + * Secure and normal world communicates pointers as physical address + * instead of the virtual address. This is because secure and normal world + * have completely independent memory mapping. Normal world can even have a + * hypervisor which need to translate the guest physical address (AKA IPA + * in ARM documentation) to a real physical address before passing the + * structure to secure world. + */ +struct optee_msg_param_tmem { + u64 buf_ptr; + u64 size; + u64 shm_ref; +}; + +/** + * struct optee_msg_param_rmem - registered memory reference parameter + * @offs: Offset into shared memory reference + * @size: Size of the buffer + * @shm_ref: Shared memory reference, pointer to a struct tee_shm + */ +struct optee_msg_param_rmem { + u64 offs; + u64 size; + u64 shm_ref; +}; + +/** + * struct optee_msg_param_value - opaque value parameter + * + * Value parameters are passed unchecked between normal and secure world. + */ +struct optee_msg_param_value { + u64 a; + u64 b; + u64 c; +}; + +/** + * struct optee_msg_param - parameter used together with struct optee_msg_arg + * @attr: attributes + * @tmem: parameter by temporary memory reference + * @rmem: parameter by registered memory reference + * @value: parameter by opaque value + * + * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in + * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value, + * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and + * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem, + * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used. + */ +struct optee_msg_param { + u64 attr; + union { + struct optee_msg_param_tmem tmem; + struct optee_msg_param_rmem rmem; + struct optee_msg_param_value value; + } u; +}; + +/** + * struct optee_msg_arg - call argument + * @cmd: Command, one of OPTEE_MSG_CMD_* or OPTEE_MSG_RPC_CMD_* + * @func: Trusted Application function, specific to the Trusted Application, + * used if cmd == OPTEE_MSG_CMD_INVOKE_COMMAND + * @session: In parameter for all OPTEE_MSG_CMD_* except + * OPTEE_MSG_CMD_OPEN_SESSION where it's an output parameter instead + * @cancel_id: Cancellation id, a unique value to identify this request + * @ret: return value + * @ret_origin: origin of the return value + * @num_params: number of parameters supplied to the OS Command + * @params: the parameters supplied to the OS Command + * + * All normal calls to Trusted OS uses this struct. If cmd requires further + * information than what these field holds it can be passed as a parameter + * tagged as meta (setting the OPTEE_MSG_ATTR_META bit in corresponding + * attrs field). All parameters tagged as meta has to come first. + * + * Temp memref parameters can be fragmented if supported by the Trusted OS + * (when optee_smc.h is bearer of this protocol this is indicated with + * OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM). If a logical memref parameter is + * fragmented then has all but the last fragment the + * OPTEE_MSG_ATTR_FRAGMENT bit set in attrs. Even if a memref is fragmented + * it will still be presented as a single logical memref to the Trusted + * Application. + */ +struct optee_msg_arg { + u32 cmd; + u32 func; + u32 session; + u32 cancel_id; + u32 pad; + u32 ret; + u32 ret_origin; + u32 num_params; + + /* num_params tells the actual number of element in params */ + struct optee_msg_param params[0]; +}; + +/** + * OPTEE_MSG_GET_ARG_SIZE - return size of struct optee_msg_arg + * + * @num_params: Number of parameters embedded in the struct optee_msg_arg + * + * Returns the size of the struct optee_msg_arg together with the number + * of embedded parameters. + */ +#define OPTEE_MSG_GET_ARG_SIZE(num_params) \ + (sizeof(struct optee_msg_arg) + \ + sizeof(struct optee_msg_param) * (num_params)) + +/***************************************************************************** + * Part 2 - requests from normal world + *****************************************************************************/ + +/* + * Return the following UID if using API specified in this file without + * further extensions: + * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b. + * Represented in 4 32-bit words in OPTEE_MSG_UID_0, OPTEE_MSG_UID_1, + * OPTEE_MSG_UID_2, OPTEE_MSG_UID_3. + */ +#define OPTEE_MSG_UID_0 0x384fb3e0 +#define OPTEE_MSG_UID_1 0xe7f811e3 +#define OPTEE_MSG_UID_2 0xaf630002 +#define OPTEE_MSG_UID_3 0xa5d5c51b +#define OPTEE_MSG_FUNCID_CALLS_UID 0xFF01 + +/* + * Returns 2.0 if using API specified in this file without further + * extensions. Represented in 2 32-bit words in OPTEE_MSG_REVISION_MAJOR + * and OPTEE_MSG_REVISION_MINOR + */ +#define OPTEE_MSG_REVISION_MAJOR 2 +#define OPTEE_MSG_REVISION_MINOR 0 +#define OPTEE_MSG_FUNCID_CALLS_REVISION 0xFF03 + +/* + * Get UUID of Trusted OS. + * + * Used by non-secure world to figure out which Trusted OS is installed. + * Note that returned UUID is the UUID of the Trusted OS, not of the API. + * + * Returns UUID in 4 32-bit words in the same way as + * OPTEE_MSG_FUNCID_CALLS_UID described above. + */ +#define OPTEE_MSG_OS_OPTEE_UUID_0 0x486178e0 +#define OPTEE_MSG_OS_OPTEE_UUID_1 0xe7f811e3 +#define OPTEE_MSG_OS_OPTEE_UUID_2 0xbc5e0002 +#define OPTEE_MSG_OS_OPTEE_UUID_3 0xa5d5c51b +#define OPTEE_MSG_FUNCID_GET_OS_UUID 0x0000 + +/* + * Get revision of Trusted OS. + * + * 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. + * + * Returns revision in 2 32-bit words in the same way as + * OPTEE_MSG_CALLS_REVISION described above. + */ +#define OPTEE_MSG_FUNCID_GET_OS_REVISION 0x0001 + +/* + * Do a secure call with struct optee_msg_arg as argument + * The OPTEE_MSG_CMD_* below defines what goes in struct optee_msg_arg::cmd + * + * OPTEE_MSG_CMD_OPEN_SESSION opens a session to a Trusted Application. + * The first two parameters are tagged as meta, holding two value + * parameters to pass the following information: + * param[0].u.value.a-b uuid of Trusted Application + * param[1].u.value.a-b uuid of Client + * param[1].u.value.c Login class of client OPTEE_MSG_LOGIN_* + * + * OPTEE_MSG_CMD_INVOKE_COMMAND invokes a command a previously opened + * session to a Trusted Application. struct optee_msg_arg::func is Trusted + * Application function, specific to the Trusted Application. + * + * OPTEE_MSG_CMD_CLOSE_SESSION closes a previously opened session to + * Trusted Application. + * + * OPTEE_MSG_CMD_CANCEL cancels a currently invoked command. + * + * OPTEE_MSG_CMD_REGISTER_SHM registers a shared memory reference. The + * information is passed as: + * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + * [| OPTEE_MSG_ATTR_FRAGMENT] + * [in] param[0].u.tmem.buf_ptr physical address (of first fragment) + * [in] param[0].u.tmem.size size (of first fragment) + * [in] param[0].u.tmem.shm_ref holds shared memory reference + * ... + * The shared memory can optionally be fragmented, temp memrefs can follow + * each other with all but the last with the OPTEE_MSG_ATTR_FRAGMENT bit set. + * + * OPTEE_MSG_CMD_UNREGISTER_SHM unregisteres a previously registered shared + * memory reference. The information is passed as: + * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + * [in] param[0].u.rmem.shm_ref holds shared memory reference + * [in] param[0].u.rmem.offs 0 + * [in] param[0].u.rmem.size 0 + */ +#define OPTEE_MSG_CMD_OPEN_SESSION 0 +#define OPTEE_MSG_CMD_INVOKE_COMMAND 1 +#define OPTEE_MSG_CMD_CLOSE_SESSION 2 +#define OPTEE_MSG_CMD_CANCEL 3 +#define OPTEE_MSG_CMD_REGISTER_SHM 4 +#define OPTEE_MSG_CMD_UNREGISTER_SHM 5 +#define OPTEE_MSG_FUNCID_CALL_WITH_ARG 0x0004 + +/***************************************************************************** + * Part 3 - Requests from secure world, RPC + *****************************************************************************/ + +/* + * All RPC is done with a struct optee_msg_arg as bearer of information, + * struct optee_msg_arg::arg holds values defined by OPTEE_MSG_RPC_CMD_* below + * + * RPC communication with tee-supplicant is reversed compared to normal + * client communication desribed above. The supplicant receives requests + * and sends responses. + */ + +/* + * Load a TA into memory, defined in tee-supplicant + */ +#define OPTEE_MSG_RPC_CMD_LOAD_TA 0 + +/* + * Reserved + */ +#define OPTEE_MSG_RPC_CMD_RPMB 1 + +/* + * File system access, defined in tee-supplicant + */ +#define OPTEE_MSG_RPC_CMD_FS 2 + +/* + * Get time + * + * Returns number of seconds and nano seconds since the Epoch, + * 1970-01-01 00:00:00 +0000 (UTC). + * + * [out] param[0].u.value.a Number of seconds + * [out] param[0].u.value.b Number of nano seconds. + */ +#define OPTEE_MSG_RPC_CMD_GET_TIME 3 + +/* + * Wait queue primitive, helper for secure world to implement a wait queue. + * + * If secure world need to wait for a secure world mutex it issues a sleep + * request instead of spinning in secure world. Conversely is a wakeup + * request issued when a secure world mutex with a thread waiting thread is + * unlocked. + * + * Waiting on a key + * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP + * [in] param[0].u.value.b wait key + * + * Waking up a key + * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP + * [in] param[0].u.value.b wakeup key + */ +#define OPTEE_MSG_RPC_CMD_WAIT_QUEUE 4 +#define OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP 0 +#define OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP 1 + +/* + * Suspend execution + * + * [in] param[0].value .a number of milliseconds to suspend + */ +#define OPTEE_MSG_RPC_CMD_SUSPEND 5 + +/* + * Allocate a piece of shared memory + * + * Shared memory can optionally be fragmented, to support that additional + * spare param entries are allocated to make room for eventual fragments. + * The spare param entries has .attr = OPTEE_MSG_ATTR_TYPE_NONE when + * unused. All returned temp memrefs except the last should have the + * OPTEE_MSG_ATTR_FRAGMENT bit set in the attr field. + * + * [in] param[0].u.value.a type of memory one of + * OPTEE_MSG_RPC_SHM_TYPE_* below + * [in] param[0].u.value.b requested size + * [in] param[0].u.value.c required alignment + * + * [out] param[0].u.tmem.buf_ptr physical address (of first fragment) + * [out] param[0].u.tmem.size size (of first fragment) + * [out] param[0].u.tmem.shm_ref shared memory reference + * ... + * [out] param[n].u.tmem.buf_ptr physical address + * [out] param[n].u.tmem.size size + * [out] param[n].u.tmem.shm_ref shared memory reference (same value + * as in param[n-1].u.tmem.shm_ref) + */ +#define OPTEE_MSG_RPC_CMD_SHM_ALLOC 6 +/* Memory that can be shared with a non-secure user space application */ +#define OPTEE_MSG_RPC_SHM_TYPE_APPL 0 +/* Memory only shared with non-secure kernel */ +#define OPTEE_MSG_RPC_SHM_TYPE_KERNEL 1 + +/* + * Free shared memory previously allocated with OPTEE_MSG_RPC_CMD_SHM_ALLOC + * + * [in] param[0].u.value.a type of memory one of + * OPTEE_MSG_RPC_SHM_TYPE_* above + * [in] param[0].u.value.b value of shared memory reference + * returned in param[0].u.tmem.shm_ref + * above + */ +#define OPTEE_MSG_RPC_CMD_SHM_FREE 7 + +#endif /* _OPTEE_MSG_H */ diff --git a/drivers/tee/optee/optee_msg_supplicant.h b/drivers/tee/optee/optee_msg_supplicant.h new file mode 100644 index 000000000000..65ca6c0574b9 --- /dev/null +++ b/drivers/tee/optee/optee_msg_supplicant.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2016-2018, Linaro Limited + */ + +#ifndef __OPTEE_MSG_SUPPLICANT_H +#define __OPTEE_MSG_SUPPLICANT_H + +/* + * Load a TA into memory + */ +#define OPTEE_MSG_RPC_CMD_LOAD_TA 0 + +/* + * Replay Protected Memory Block access + */ +#define OPTEE_MSG_RPC_CMD_RPMB 1 + +/* + * File system access + */ +#define OPTEE_MSG_RPC_CMD_FS 2 + +/* + * Define protocol for messages with .cmd == OPTEE_MSG_RPC_CMD_FS and first + * parameter has the attribute OPTEE_MSG_ATTR_TYPE_VALUE_INPUT. + */ + +/* + * Open a file + * + * [in] param[0].u.value.a OPTEE_MRF_OPEN + * [in] param[1].u.tmem a string holding the file name + * [out] param[2].u.value.a file descriptor of open file + */ +#define OPTEE_MRF_OPEN 0 + +/* + * Create a file + * + * [in] param[0].u.value.a OPTEE_MRF_CREATE + * [in] param[1].u.tmem a string holding the file name + * [out] param[2].u.value.a file descriptor of open file + */ +#define OPTEE_MRF_CREATE 1 + +/* + * Close a file + * + * [in] param[0].u.value.a OPTEE_MRF_CLOSE + * [in] param[0].u.value.b file descriptor of open file. + */ +#define OPTEE_MRF_CLOSE 2 + +/* + * Read from a file + * + * [in] param[0].u.value.a OPTEE_MRF_READ + * [in] param[0].u.value.b file descriptor of open file + * [in] param[0].u.value.c offset into file + * [out] param[1].u.tmem buffer to hold returned data + */ +#define OPTEE_MRF_READ 3 + +/* + * Write to a file + * + * [in] param[0].u.value.a OPTEE_MRF_WRITE + * [in] param[0].u.value.b file descriptor of open file + * [in] param[0].u.value.c offset into file + * [in] param[1].u.tmem buffer holding data to be written + */ +#define OPTEE_MRF_WRITE 4 + +/* + * Truncate a file + * + * [in] param[0].u.value.a OPTEE_MRF_TRUNCATE + * [in] param[0].u.value.b file descriptor of open file + * [in] param[0].u.value.c length of file. + */ +#define OPTEE_MRF_TRUNCATE 5 + +/* + * Remove a file + * + * [in] param[0].u.value.a OPTEE_MRF_REMOVE + * [in] param[1].u.tmem a string holding the file name + */ +#define OPTEE_MRF_REMOVE 6 + +/* + * Rename a file + * + * [in] param[0].u.value.a OPTEE_MRF_RENAME + * [in] param[0].u.value.b true if existing target should be removed + * [in] param[1].u.tmem a string holding the old file name + * [in] param[2].u.tmem a string holding the new file name + */ +#define OPTEE_MRF_RENAME 7 + +/* + * Opens a directory for file listing + * + * [in] param[0].u.value.a OPTEE_MRF_OPENDIR + * [in] param[1].u.tmem a string holding the name of the directory + * [out] param[2].u.value.a handle to open directory + */ +#define OPTEE_MRF_OPENDIR 8 + +/* + * Closes a directory handle + * + * [in] param[0].u.value.a OPTEE_MRF_CLOSEDIR + * [in] param[0].u.value.b handle to open directory + */ +#define OPTEE_MRF_CLOSEDIR 9 + +/* + * Read next file name of directory + * + * + * [in] param[0].u.value.a OPTEE_MRF_READDIR + * [in] param[0].u.value.b handle to open directory + * [out] param[1].u.tmem a string holding the file name + */ +#define OPTEE_MRF_READDIR 10 + +/* + * End of definitions for messages with .cmd == OPTEE_MSG_RPC_CMD_FS + */ + +/* + * Command Ids 3, 4 and 5 of OPTEE_MSG_RPC_CMD_xxx macros are reserved for use + * by the kernel driver. + */ + +/* + * Shared memory allocation + */ +#define OPTEE_MSG_RPC_CMD_SHM_ALLOC 6 +#define OPTEE_MSG_RPC_CMD_SHM_FREE 7 + +/* + * Was OPTEE_MSG_RPC_CMD_SQL_FS, which isn't supported any longer + */ +#define OPTEE_MSG_RPC_CMD_SQL_FS_RESERVED 8 + +/* + * GPROF support management commands + */ +#define OPTEE_MSG_RPC_CMD_GPROF 9 + +/* + * Socket commands + */ +#define OPTEE_MSG_RPC_CMD_SOCKET 10 + +/* + * Define protocol for messages with .cmd == OPTEE_MSG_RPC_CMD_SOCKET + */ + +#define OPTEE_MRC_SOCKET_TIMEOUT_NONBLOCKING 0 +#define OPTEE_MRC_SOCKET_TIMEOUT_BLOCKING 0xffffffff + +/* + * Open socket + * + * [in] param[0].u.value.a OPTEE_MRC_SOCKET_OPEN + * [in] param[0].u.value.b TA instance id + * [in] param[1].u.value.a server port number + * [in] param[1].u.value.b protocol, TEE_ISOCKET_PROTOCOLID_* + * [in] param[1].u.value.c ip version TEE_IP_VERSION_* from tee_ipsocket.h + * [in] param[2].u.tmem server address + * [out] param[3].u.value.a socket handle (32-bit) + */ +#define OPTEE_MRC_SOCKET_OPEN 0 + +/* + * Close socket + * + * [in] param[0].u.value.a OPTEE_MRC_SOCKET_CLOSE + * [in] param[0].u.value.b TA instance id + * [in] param[0].u.value.c socket handle + */ +#define OPTEE_MRC_SOCKET_CLOSE 1 + +/* + * Close all sockets + * + * [in] param[0].u.value.a OPTEE_MRC_SOCKET_CLOSE_ALL + * [in] param[0].u.value.b TA instance id + */ +#define OPTEE_MRC_SOCKET_CLOSE_ALL 2 + +/* + * Send data on socket + * + * [in] param[0].u.value.a OPTEE_MRC_SOCKET_SEND + * [in] param[0].u.value.b TA instance id + * [in] param[0].u.value.c socket handle + * [in] param[1].u.tmem buffer to transmit + * [in] param[2].u.value.a timeout ms or OPTEE_MRC_SOCKET_TIMEOUT_* + * [out] param[2].u.value.b number of transmitted bytes + */ +#define OPTEE_MRC_SOCKET_SEND 3 + +/* + * Receive data on socket + * + * [in] param[0].u.value.a OPTEE_MRC_SOCKET_RECV + * [in] param[0].u.value.b TA instance id + * [in] param[0].u.value.c socket handle + * [out] param[1].u.tmem buffer to receive + * [in] param[2].u.value.a timeout ms or OPTEE_MRC_SOCKET_TIMEOUT_* + */ +#define OPTEE_MRC_SOCKET_RECV 4 + +/* + * Perform IOCTL on socket + * + * [in] param[0].u.value.a OPTEE_MRC_SOCKET_IOCTL + * [in] param[0].u.value.b TA instance id + * [in] param[0].u.value.c socket handle + * [in/out] param[1].u.tmem buffer + * [in] param[2].u.value.a ioctl command + */ +#define OPTEE_MRC_SOCKET_IOCTL 5 + +/* + * End of definitions for messages with .cmd == OPTEE_MSG_RPC_CMD_SOCKET + */ + +#endif /*__OPTEE_MSG_SUPPLICANT_H*/ diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h new file mode 100644 index 000000000000..daa470f812a9 --- /dev/null +++ b/drivers/tee/optee/optee_private.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2018 Linaro Limited + */ + +#ifndef __OPTEE_PRIVATE_H +#define __OPTEE_PRIVATE_H + +void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr); +void optee_suppl_cmd(struct udevice *dev, void *shm, void **page_list); + +#endif /*__OPTEE_PRIVATE_H*/ diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h new file mode 100644 index 000000000000..81069ae6f8ac --- /dev/null +++ b/drivers/tee/optee/optee_smc.h @@ -0,0 +1,444 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2015-2018, Linaro Limited + */ + +#ifndef OPTEE_SMC_H +#define OPTEE_SMC_H + +#include <linux/arm-smccc.h> +#include <linux/bitops.h> + +#define OPTEE_SMC_STD_CALL_VAL(func_num) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_TRUSTED_OS, (func_num)) +#define OPTEE_SMC_FAST_CALL_VAL(func_num) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_TRUSTED_OS, (func_num)) + +/* + * Function specified by SMC Calling convention. + */ +#define OPTEE_SMC_FUNCID_CALLS_COUNT 0xFF00 +#define OPTEE_SMC_CALLS_COUNT \ + ARM_SMCCC_CALL_VAL(OPTEE_SMC_FAST_CALL, SMCCC_SMC_32, \ + SMCCC_OWNER_TRUSTED_OS_END, \ + OPTEE_SMC_FUNCID_CALLS_COUNT) + +/* + * Normal cached memory (write-back), shareable for SMP systems and not + * shareable for UP systems. + */ +#define OPTEE_SMC_SHM_CACHED 1 + +/* + * a0..a7 is used as register names in the descriptions below, on arm32 + * that translates to r0..r7 and on arm64 to w0..w7. In both cases it's + * 32-bit registers. + */ + +/* + * Function specified by SMC Calling convention + * + * Return one of the following UIDs if using API specified in this file + * without further extentions: + * 65cb6b93-af0c-4617-8ed6-644a8d1140f8 + * see also OPTEE_SMC_UID_* in optee_msg.h + */ +#define OPTEE_SMC_FUNCID_CALLS_UID OPTEE_MSG_FUNCID_CALLS_UID +#define OPTEE_SMC_CALLS_UID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_TRUSTED_OS_END, \ + OPTEE_SMC_FUNCID_CALLS_UID) + +/* + * Function specified by SMC Calling convention + * + * Returns 2.0 if using API specified in this file without further extentions. + * see also OPTEE_MSG_REVISION_* in optee_msg.h + */ +#define OPTEE_SMC_FUNCID_CALLS_REVISION OPTEE_MSG_FUNCID_CALLS_REVISION +#define OPTEE_SMC_CALLS_REVISION \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_TRUSTED_OS_END, \ + OPTEE_SMC_FUNCID_CALLS_REVISION) + +struct optee_smc_calls_revision_result { + unsigned long major; + unsigned long minor; + unsigned long reserved0; + unsigned long reserved1; +}; + +/* + * Get UUID of Trusted OS. + * + * Used by non-secure world to figure out which Trusted OS is installed. + * Note that returned UUID is the UUID of the Trusted OS, not of the API. + * + * Returns UUID in a0-4 in the same way as OPTEE_SMC_CALLS_UID + * described above. + */ +#define OPTEE_SMC_FUNCID_GET_OS_UUID OPTEE_MSG_FUNCID_GET_OS_UUID +#define OPTEE_SMC_CALL_GET_OS_UUID \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_UUID) + +/* + * Get revision of Trusted OS. + * + * 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. + * + * Returns revision in a0-1 in the same way as OPTEE_SMC_CALLS_REVISION + * described above. May optionally return a 32-bit build identifier in a2, + * with zero meaning unspecified. + */ +#define OPTEE_SMC_FUNCID_GET_OS_REVISION OPTEE_MSG_FUNCID_GET_OS_REVISION +#define OPTEE_SMC_CALL_GET_OS_REVISION \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_REVISION) + +struct optee_smc_call_get_os_revision_result { + unsigned long major; + unsigned long minor; + unsigned long build_id; + unsigned long reserved1; +}; + +/* + * Call with struct optee_msg_arg as argument + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG + * a1 Upper 32bit of a 64bit physical pointer to a struct optee_msg_arg + * a2 Lower 32bit of a 64bit physical pointer to a struct optee_msg_arg + * a3 Cache settings, not used if physical pointer is in a predefined shared + * memory area else per OPTEE_SMC_SHM_* + * a4-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 Return value, OPTEE_SMC_RETURN_* + * a1-3 Not used + * a4-7 Preserved + * + * OPTEE_SMC_RETURN_ETHREAD_LIMIT return register usage: + * a0 Return value, OPTEE_SMC_RETURN_ETHREAD_LIMIT + * a1-3 Preserved + * a4-7 Preserved + * + * RPC return register usage: + * a0 Return value, OPTEE_SMC_RETURN_IS_RPC(val) + * a1-2 RPC parameters + * a3-7 Resume information, must be preserved + * + * Possible return values: + * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this + * function. + * OPTEE_SMC_RETURN_OK Call completed, result updated in + * the previously supplied struct + * optee_msg_arg. + * OPTEE_SMC_RETURN_ETHREAD_LIMIT Number of Trusted OS threads exceeded, + * try again later. + * OPTEE_SMC_RETURN_EBADADDR Bad physcial pointer to struct + * optee_msg_arg. + * OPTEE_SMC_RETURN_EBADCMD Bad/unknown cmd in struct optee_msg_arg + * OPTEE_SMC_RETURN_IS_RPC() Call suspended by RPC call to normal + * world. + */ +#define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEE_MSG_FUNCID_CALL_WITH_ARG +#define OPTEE_SMC_CALL_WITH_ARG \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG) + +/* + * Get Shared Memory Config + * + * Returns the Secure/Non-secure shared memory config. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_GET_SHM_CONFIG + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Have config return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 Physical address of start of SHM + * a2 Size of of SHM + * a3 Cache settings of memory, as defined by the + * OPTEE_SMC_SHM_* values above + * a4-7 Preserved + * + * Not available register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-3 Not used + * a4-7 Preserved + */ +#define OPTEE_SMC_FUNCID_GET_SHM_CONFIG 7 +#define OPTEE_SMC_GET_SHM_CONFIG \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_SHM_CONFIG) + +struct optee_smc_get_shm_config_result { + unsigned long status; + unsigned long start; + unsigned long size; + unsigned long settings; +}; + +/* + * Exchanges capabilities between normal world and secure world + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_EXCHANGE_CAPABILITIES + * a1 bitfield of normal world capabilities OPTEE_SMC_NSEC_CAP_* + * a2-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_* + * a2-7 Preserved + * + * Error return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL, can't use the capabilities from normal world + * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_* + * a2-7 Preserved + */ +/* Normal world works as a uniprocessor system */ +#define OPTEE_SMC_NSEC_CAP_UNIPROCESSOR BIT(0) +/* Secure world has reserved shared memory for normal world to use */ +#define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM BIT(0) +/* Secure world can communicate via previously unregistered shared memory */ +#define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1) + +/* + * Secure world supports commands "register/unregister shared memory", + * secure world accepts command buffers located in any parts of non-secure RAM + */ +#define OPTEE_SMC_SEC_CAP_DYNAMIC_SHM BIT(2) + +#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 +#define OPTEE_SMC_EXCHANGE_CAPABILITIES \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES) + +struct optee_smc_exchange_capabilities_result { + unsigned long status; + unsigned long capabilities; + unsigned long reserved0; + unsigned long reserved1; +}; + +/* + * Disable and empties cache of shared memory objects + * + * Secure world can cache frequently used shared memory objects, for + * example objects used as RPC arguments. When secure world is idle this + * function returns one shared memory reference to free. To disable the + * cache and free all cached objects this function has to be called until + * it returns OPTEE_SMC_RETURN_ENOTAVAIL. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_DISABLE_SHM_CACHE + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 Upper 32bit of a 64bit Shared memory cookie + * a2 Lower 32bit of a 64bit Shared memory cookie + * a3-7 Preserved + * + * Cache empty return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-7 Preserved + * + * Not idle return register usage: + * a0 OPTEE_SMC_RETURN_EBUSY + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE 10 +#define OPTEE_SMC_DISABLE_SHM_CACHE \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE) + +struct optee_smc_disable_shm_cache_result { + unsigned long status; + unsigned long shm_upper32; + unsigned long shm_lower32; + unsigned long reserved0; +}; + +/* + * Enable cache of shared memory objects + * + * Secure world can cache frequently used shared memory objects, for + * example objects used as RPC arguments. When secure world is idle this + * function returns OPTEE_SMC_RETURN_OK and the cache is enabled. If + * secure world isn't idle OPTEE_SMC_RETURN_EBUSY is returned. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_ENABLE_SHM_CACHE + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1-7 Preserved + * + * Not idle return register usage: + * a0 OPTEE_SMC_RETURN_EBUSY + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE 11 +#define OPTEE_SMC_ENABLE_SHM_CACHE \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE) + +/* + * Resume from RPC (for example after processing a foreign interrupt) + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC + * a1-3 Value of a1-3 when OPTEE_SMC_CALL_WITH_ARG returned + * OPTEE_SMC_RETURN_RPC in a0 + * + * Return register usage is the same as for OPTEE_SMC_*CALL_WITH_ARG above. + * + * Possible return values + * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this + * function. + * OPTEE_SMC_RETURN_OK Original call completed, result + * updated in the previously supplied. + * struct optee_msg_arg + * OPTEE_SMC_RETURN_RPC Call suspended by RPC call to normal + * world. + * OPTEE_SMC_RETURN_ERESUME Resume failed, the opaque resume + * information was corrupt. + */ +#define OPTEE_SMC_FUNCID_RETURN_FROM_RPC 3 +#define OPTEE_SMC_CALL_RETURN_FROM_RPC \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_RETURN_FROM_RPC) + +#define OPTEE_SMC_RETURN_RPC_PREFIX_MASK 0xFFFF0000 +#define OPTEE_SMC_RETURN_RPC_PREFIX 0xFFFF0000 +#define OPTEE_SMC_RETURN_RPC_FUNC_MASK 0x0000FFFF + +#define OPTEE_SMC_RETURN_GET_RPC_FUNC(ret) \ + ((ret) & OPTEE_SMC_RETURN_RPC_FUNC_MASK) + +#define OPTEE_SMC_RPC_VAL(func) ((func) | OPTEE_SMC_RETURN_RPC_PREFIX) + +/* + * Allocate memory for RPC parameter passing. The memory is used to hold a + * struct optee_msg_arg. + * + * "Call" register usage: + * a0 This value, OPTEE_SMC_RETURN_RPC_ALLOC + * a1 Size in bytes of required argument memory + * a2 Not used + * a3 Resume information, must be preserved + * a4-5 Not used + * a6-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1 Upper 32bits of 64bit physical pointer to allocated + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't + * be allocated. + * a2 Lower 32bits of 64bit physical pointer to allocated + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't + * be allocated + * a3 Preserved + * a4 Upper 32bits of 64bit Shared memory cookie used when freeing + * the memory or doing an RPC + * a5 Lower 32bits of 64bit Shared memory cookie used when freeing + * the memory or doing an RPC + * a6-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_ALLOC 0 +#define OPTEE_SMC_RETURN_RPC_ALLOC \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_ALLOC) + +/* + * Free memory previously allocated by OPTEE_SMC_RETURN_RPC_ALLOC + * + * "Call" register usage: + * a0 This value, OPTEE_SMC_RETURN_RPC_FREE + * a1 Upper 32bits of 64bit shared memory cookie belonging to this + * argument memory + * a2 Lower 32bits of 64bit shared memory cookie belonging to this + * argument memory + * a3-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1-2 Not used + * a3-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_FREE 2 +#define OPTEE_SMC_RETURN_RPC_FREE \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE) + +/* + * Deliver foreign interrupt to normal world. + * + * "Call" register usage: + * a0 OPTEE_SMC_RETURN_RPC_FOREIGN_INTR + * a1-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_FOREIGN_INTR 4 +#define OPTEE_SMC_RETURN_RPC_FOREIGN_INTR \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FOREIGN_INTR) + +/* + * Do an RPC request. The supplied struct optee_msg_arg tells which + * request to do and the parameters for the request. The following fields + * are used (the rest are unused): + * - cmd the Request ID + * - ret return value of the request, filled in by normal world + * - num_params number of parameters for the request + * - params the parameters + * - param_attrs attributes of the parameters + * + * "Call" register usage: + * a0 OPTEE_SMC_RETURN_RPC_CMD + * a1 Upper 32bit of a 64bit Shared memory cookie holding a + * struct optee_msg_arg, must be preserved, only the data should + * be updated + * a2 Lower 32bit of a 64bit Shared memory cookie holding a + * struct optee_msg_arg, must be preserved, only the data should + * be updated + * a3-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1-2 Not used + * a3-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_CMD 5 +#define OPTEE_SMC_RETURN_RPC_CMD \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_CMD) + +/* Returned in a0 */ +#define OPTEE_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF + +/* Returned in a0 only from Trusted OS functions */ +#define OPTEE_SMC_RETURN_OK 0x0 +#define OPTEE_SMC_RETURN_ETHREAD_LIMIT 0x1 +#define OPTEE_SMC_RETURN_EBUSY 0x2 +#define OPTEE_SMC_RETURN_ERESUME 0x3 +#define OPTEE_SMC_RETURN_EBADADDR 0x4 +#define OPTEE_SMC_RETURN_EBADCMD 0x5 +#define OPTEE_SMC_RETURN_ENOMEM 0x6 +#define OPTEE_SMC_RETURN_ENOTAVAIL 0x7 +#define OPTEE_SMC_RETURN_IS_RPC(ret) __optee_smc_return_is_rpc((ret)) + +static inline bool __optee_smc_return_is_rpc(u32 ret) +{ + return ret != OPTEE_SMC_RETURN_UNKNOWN_FUNCTION && + (ret & OPTEE_SMC_RETURN_RPC_PREFIX_MASK) == + OPTEE_SMC_RETURN_RPC_PREFIX; +} + +#endif /* OPTEE_SMC_H */ diff --git a/drivers/tee/optee/supplicant.c b/drivers/tee/optee/supplicant.c new file mode 100644 index 000000000000..6965055bd1b5 --- /dev/null +++ b/drivers/tee/optee/supplicant.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2018, Linaro Limited + */ + +#include <common.h> +#include <linux/types.h> +#include <log.h> +#include <tee.h> + +#include "optee_msg.h" +#include "optee_msg_supplicant.h" +#include "optee_private.h" +#include "optee_smc.h" + +static void cmd_shm_alloc(struct udevice *dev, struct optee_msg_arg *arg, + void **page_list) +{ + struct tee_shm *shm; + void *pl; + u64 ph_ptr; + + arg->ret_origin = TEE_ORIGIN_COMMS; + + if (arg->num_params != 1 || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { + arg->ret = TEE_ERROR_BAD_PARAMETERS; + return; + } + + shm = __tee_shm_add(dev, 0, NULL, arg->params[0].u.value.b, + TEE_SHM_REGISTER | TEE_SHM_ALLOC); + if (!shm) { + arg->ret = TEE_ERROR_OUT_OF_MEMORY; + return; + } + + pl = optee_alloc_and_init_page_list(shm->addr, shm->size, &ph_ptr); + if (!pl) { + arg->ret = TEE_ERROR_OUT_OF_MEMORY; + tee_shm_free(shm); + return; + } + + *page_list = pl; + arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | + OPTEE_MSG_ATTR_NONCONTIG; + arg->params[0].u.tmem.buf_ptr = ph_ptr; + arg->params[0].u.tmem.size = shm->size; + arg->params[0].u.tmem.shm_ref = (ulong)shm; + arg->ret = TEE_SUCCESS; +} + +static void cmd_shm_free(struct optee_msg_arg *arg) +{ + arg->ret_origin = TEE_ORIGIN_COMMS; + + if (arg->num_params != 1 || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { + arg->ret = TEE_ERROR_BAD_PARAMETERS; + return; + } + + tee_shm_free((struct tee_shm *)(ulong)arg->params[0].u.value.b); + arg->ret = TEE_SUCCESS; +} + +void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg, + void **page_list) +{ + struct optee_msg_arg *arg = shm_arg->addr; + + switch (arg->cmd) { + case OPTEE_MSG_RPC_CMD_SHM_ALLOC: + cmd_shm_alloc(dev, arg, page_list); + break; + case OPTEE_MSG_RPC_CMD_SHM_FREE: + cmd_shm_free(arg); + break; + case OPTEE_MSG_RPC_CMD_FS: + debug("OPTEE_MSG_RPC_CMD_FS not implemented\n"); + arg->ret = TEE_ERROR_NOT_IMPLEMENTED; + break; + default: + arg->ret = TEE_ERROR_NOT_IMPLEMENTED; + } + + arg->ret_origin = TEE_ORIGIN_COMMS; +}

Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds a OP-TEE driver.
- Targets ARM and ARM64
- Supports using any u-boot memory as shared memory
U-Boot
Please use this consistent.
- Probes OP-TEE version using SMCs
- Uses OPTEE message protocol version 2 to communicate with secure world
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
drivers/tee/Kconfig | 10 + drivers/tee/Makefile | 1 + drivers/tee/optee/Kconfig | 7 + drivers/tee/optee/Makefile | 4 + drivers/tee/optee/core.c | 614 +++++++++++++++++++++++ drivers/tee/optee/optee_msg.h | 423 ++++++++++++++++ drivers/tee/optee/optee_msg_supplicant.h | 234 +++++++++ drivers/tee/optee/optee_private.h | 12 + drivers/tee/optee/optee_smc.h | 444 ++++++++++++++++ drivers/tee/optee/supplicant.c | 89 ++++ 10 files changed, 1838 insertions(+) create mode 100644 drivers/tee/optee/Kconfig create mode 100644 drivers/tee/optee/Makefile create mode 100644 drivers/tee/optee/core.c create mode 100644 drivers/tee/optee/optee_msg.h create mode 100644 drivers/tee/optee/optee_msg_supplicant.h create mode 100644 drivers/tee/optee/optee_private.h create mode 100644 drivers/tee/optee/optee_smc.h create mode 100644 drivers/tee/optee/supplicant.c
Reviewed-by: Simon Glass sjg@chromium.org
diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig index 817ab331b0f8..3e7fe6ddcc5d 100644 --- a/drivers/tee/Kconfig +++ b/drivers/tee/Kconfig @@ -6,3 +6,13 @@ config TEE help This implements a generic interface towards a Trusted Execution Environment (TEE).
+if TEE
+menu "TEE drivers"
+source "drivers/tee/optee/Kconfig"
+endmenu
+endif diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile index b6d8e16e6211..19633b60f235 100644 --- a/drivers/tee/Makefile +++ b/drivers/tee/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+
obj-y += tee-uclass.o +obj-$(CONFIG_OPTEE) += optee/ diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig new file mode 100644 index 000000000000..8f7ebe161111 --- /dev/null +++ b/drivers/tee/optee/Kconfig @@ -0,0 +1,7 @@ +# OP-TEE Trusted Execution Environment Configuration +config OPTEE
bool "OP-TEE"
depends on ARM_SMCCC
help
This implements the OP-TEE Trusted Execution Environment (TEE)
driver.
for ARM? I think you should expand this help. What does the driver support / do?
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile new file mode 100644 index 000000000000..6148feb474a5 --- /dev/null +++ b/drivers/tee/optee/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+
+obj-y += core.o +obj-y += supplicant.o diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c new file mode 100644 index 000000000000..f2d92d96551b --- /dev/null +++ b/drivers/tee/optee/core.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <dm.h> +#include <linux/arm-smccc.h> +#include <linux/io.h> +#include <log.h> +#include <tee.h>
Put linux/ ones after tee.h
+#include "optee_smc.h" +#include "optee_msg.h" +#include "optee_private.h"
+#define PAGELIST_ENTRIES_PER_PAGE \
((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)
+typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long,
struct arm_smccc_res *);
+struct optee_pdata {
optee_invoke_fn *invoke_fn;
+};
+struct rpc_param {
u32 a0;
u32 a1;
u32 a2;
u32 a3;
u32 a4;
u32 a5;
u32 a6;
u32 a7;
+};
+static void *reg_pair_to_ptr(u32 reg0, u32 reg1) +{
return (void *)(ulong)(((u64)reg0 << 32) | reg1);
Can you not remove the u64 here?
E.g.
(void *)((ulong)reg0 << 32) | reg1)
+}
+static void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
Function comment
+{
*reg0 = val >> 32;
*reg1 = val;
+}
+void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr)
Function comment
+{
const unsigned int page_size = OPTEE_MSG_NONCONTIG_PAGE_SIZE;
const phys_addr_t page_mask = page_size - 1;
u8 *buf_base;
unsigned int page_offset;
unsigned int num_pages;
unsigned int list_size;
unsigned int n;
void *page_list;
struct {
u64 pages_list[PAGELIST_ENTRIES_PER_PAGE];
u64 next_page_data;
} *pages_data;
page_offset = (ulong)buf & page_mask;
num_pages = roundup(page_offset + len, page_size) / page_size;
list_size = DIV_ROUND_UP(num_pages, PAGELIST_ENTRIES_PER_PAGE) *
page_size;
page_list = memalign(page_size, list_size);
if (!page_list)
return NULL;
pages_data = page_list;
buf_base = (u8 *)(rounddown((ulong)buf, page_size));
Drop extra pair of ()
n = 0;
while (num_pages) {
pages_data->pages_list[n] = virt_to_phys(buf_base);
n++;
buf_base += page_size;
num_pages--;
if (n == PAGELIST_ENTRIES_PER_PAGE) {
pages_data->next_page_data =
virt_to_phys(pages_data + 1);
pages_data++;
n = 0;
}
}
*phys_buf_ptr = virt_to_phys(page_list) | page_offset;
return page_list;
+}
+static void optee_get_version(struct udevice *dev,
struct tee_version_data *vers)
+{
struct tee_version_data v = {
.gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM,
};
*vers = v;
+}
+static struct tee_shm *get_msg_arg(struct udevice *dev, ulong num_params,
struct optee_msg_arg **msg_arg)
+{
struct tee_shm *shm;
struct optee_msg_arg *ma;
shm = __tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL,
OPTEE_MSG_GET_ARG_SIZE(num_params), TEE_SHM_ALLOC);
if (!shm)
return NULL;
ma = shm->addr;
memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params));
ma->num_params = num_params;
*msg_arg = ma;
return shm;
+}
+static int to_msg_param(struct optee_msg_param *msg_params, ulong num_params,
const struct tee_param *params)
+{
ulong n;
uint? int?
Save below
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_PARAM_ATTR_TYPE_NONE:
mp->attr = OPTEE_MSG_ATTR_TYPE_NONE;
memset(&mp->u, 0, sizeof(mp->u));
break;
case TEE_PARAM_ATTR_TYPE_VALUE_INPUT:
case TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT:
case TEE_PARAM_ATTR_TYPE_VALUE_INOUT:
mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
TEE_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;
break;
case TEE_PARAM_ATTR_TYPE_MEMREF_INPUT:
case TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
case TEE_PARAM_ATTR_TYPE_MEMREF_INOUT:
mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
TEE_PARAM_ATTR_TYPE_MEMREF_INPUT;
mp->u.rmem.shm_ref = (ulong)p->u.memref.shm;
mp->u.rmem.size = p->u.memref.size;
mp->u.rmem.offs = p->u.memref.shm_offs;
break;
default:
return -EINVAL;
}
}
return 0;
+}
+static int from_msg_param(struct tee_param *params, ulong num_params,
const struct optee_msg_param *msg_params)
+{
ulong n;
struct tee_shm *shm;
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_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:
p->attr = TEE_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;
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_PARAM_ATTR_TYPE_MEMREF_INPUT + attr -
OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
p->u.memref.size = mp->u.rmem.size;
shm = (struct tee_shm *)(ulong)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;
break;
default:
return -EINVAL;
}
}
return 0;
+}
+static void handle_rpc(struct udevice *dev, struct rpc_param *param,
void *page_list)
+{
struct tee_shm *shm;
switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) {
case OPTEE_SMC_RPC_FUNC_ALLOC:
shm = __tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL,
param->a1,
TEE_SHM_ALLOC | TEE_SHM_REGISTER);
if (shm) {
reg_pair_from_64(¶m->a1, ¶m->a2,
virt_to_phys(shm->addr));
/* "cookie" */
reg_pair_from_64(¶m->a4, ¶m->a5, (ulong)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:
break;
case OPTEE_SMC_RPC_FUNC_CMD:
shm = reg_pair_to_ptr(param->a1, param->a2);
optee_suppl_cmd(dev, shm, page_list);
break;
default:
break;
}
param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC;
+}
+static u32 call_err_to_res(u32 call_err) +{
switch (call_err) {
case OPTEE_SMC_RETURN_OK:
return TEE_SUCCESS;
default:
return TEE_ERROR_BAD_PARAMETERS;
}
+}
+static u32 do_call_with_arg(struct udevice *dev, struct optee_msg_arg *arg) +{
struct optee_pdata *pdata = dev_get_platdata(dev);
struct rpc_param param = { .a0 = OPTEE_SMC_CALL_WITH_ARG };
void *page_list = NULL;
reg_pair_from_64(¶m.a1, ¶m.a2, virt_to_phys(arg));
while (true) {
struct arm_smccc_res res;
pdata->invoke_fn(param.a0, param.a1, param.a2, param.a3,
param.a4, param.a5, param.a6, param.a7, &res);
free(page_list);
page_list = NULL;
if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) {
param.a0 = res.a0;
param.a1 = res.a1;
param.a2 = res.a2;
param.a3 = res.a3;
handle_rpc(dev, ¶m, &page_list);
} else {
return call_err_to_res(res.a0);
}
}
+}
+static int optee_close_session(struct udevice *dev, u32 session) +{
struct tee_shm *shm;
struct optee_msg_arg *msg_arg;
shm = get_msg_arg(dev, 0, &msg_arg);
if (!shm)
return -ENOMEM;
msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
msg_arg->session = session;
do_call_with_arg(dev, msg_arg);
tee_shm_free(shm);
blank line before return
return 0;
+}
[..]
diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h new file mode 100644 index 000000000000..ebc16aa8cc31 --- /dev/null +++ b/drivers/tee/optee/optee_msg.h @@ -0,0 +1,423 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/*
- Copyright (c) 2015-2018, Linaro Limited
- */
+#ifndef _OPTEE_MSG_H +#define _OPTEE_MSG_H
+#include <linux/bitops.h> +#include <linux/types.h>
+/*
- This file defines the OP-TEE message protocol used to communicate
- with an instance of OP-TEE running in secure world.
Does this file have an upstream, or is this specific to U-Boot? If the former, please add a reference.
- This file is divided into three sections.
- Formatting of messages.
- Requests from normal world
- Requests from secure world, Remote Procedure Call (RPC), handled by
- tee-supplicant.
- */
[...]
Regards, Simon

On Wed, Aug 29, 2018 at 06:28:51PM -0600, Simon Glass wrote:
Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds a OP-TEE driver.
- Targets ARM and ARM64
- Supports using any u-boot memory as shared memory
U-Boot
Please use this consistent.
Sorry, it seems I've found lots of ways of doing that wrong.
- Probes OP-TEE version using SMCs
- Uses OPTEE message protocol version 2 to communicate with secure world
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
drivers/tee/Kconfig | 10 + drivers/tee/Makefile | 1 + drivers/tee/optee/Kconfig | 7 + drivers/tee/optee/Makefile | 4 + drivers/tee/optee/core.c | 614 +++++++++++++++++++++++ drivers/tee/optee/optee_msg.h | 423 ++++++++++++++++ drivers/tee/optee/optee_msg_supplicant.h | 234 +++++++++ drivers/tee/optee/optee_private.h | 12 + drivers/tee/optee/optee_smc.h | 444 ++++++++++++++++ drivers/tee/optee/supplicant.c | 89 ++++ 10 files changed, 1838 insertions(+) create mode 100644 drivers/tee/optee/Kconfig create mode 100644 drivers/tee/optee/Makefile create mode 100644 drivers/tee/optee/core.c create mode 100644 drivers/tee/optee/optee_msg.h create mode 100644 drivers/tee/optee/optee_msg_supplicant.h create mode 100644 drivers/tee/optee/optee_private.h create mode 100644 drivers/tee/optee/optee_smc.h create mode 100644 drivers/tee/optee/supplicant.c
Reviewed-by: Simon Glass sjg@chromium.org
diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig index 817ab331b0f8..3e7fe6ddcc5d 100644 --- a/drivers/tee/Kconfig +++ b/drivers/tee/Kconfig @@ -6,3 +6,13 @@ config TEE help This implements a generic interface towards a Trusted Execution Environment (TEE).
+if TEE
+menu "TEE drivers"
+source "drivers/tee/optee/Kconfig"
+endmenu
+endif diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile index b6d8e16e6211..19633b60f235 100644 --- a/drivers/tee/Makefile +++ b/drivers/tee/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+
obj-y += tee-uclass.o +obj-$(CONFIG_OPTEE) += optee/ diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig new file mode 100644 index 000000000000..8f7ebe161111 --- /dev/null +++ b/drivers/tee/optee/Kconfig @@ -0,0 +1,7 @@ +# OP-TEE Trusted Execution Environment Configuration +config OPTEE
bool "OP-TEE"
depends on ARM_SMCCC
help
This implements the OP-TEE Trusted Execution Environment (TEE)
driver.
for ARM? I think you should expand this help. What does the driver support / do?
I'll expand this.
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile new file mode 100644 index 000000000000..6148feb474a5 --- /dev/null +++ b/drivers/tee/optee/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+
+obj-y += core.o +obj-y += supplicant.o diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c new file mode 100644 index 000000000000..f2d92d96551b --- /dev/null +++ b/drivers/tee/optee/core.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <dm.h> +#include <linux/arm-smccc.h> +#include <linux/io.h> +#include <log.h> +#include <tee.h>
Put linux/ ones after tee.h
OK
+#include "optee_smc.h" +#include "optee_msg.h" +#include "optee_private.h"
+#define PAGELIST_ENTRIES_PER_PAGE \
((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)
+typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long,
struct arm_smccc_res *);
+struct optee_pdata {
optee_invoke_fn *invoke_fn;
+};
+struct rpc_param {
u32 a0;
u32 a1;
u32 a2;
u32 a3;
u32 a4;
u32 a5;
u32 a6;
u32 a7;
+};
+static void *reg_pair_to_ptr(u32 reg0, u32 reg1) +{
return (void *)(ulong)(((u64)reg0 << 32) | reg1);
Can you not remove the u64 here?
E.g.
(void *)((ulong)reg0 << 32) | reg1)
No, that will cause: drivers/tee/optee/core.c:42:38: warning: left shift count >= width of type [-Wshift-count-overflow] return (void *)(ulong)(((ulong)reg0 << 32) | reg1); ^~ on ILP32
+}
+static void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
Function comment
+{
*reg0 = val >> 32;
*reg1 = val;
+}
+void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr)
Function comment
+{
const unsigned int page_size = OPTEE_MSG_NONCONTIG_PAGE_SIZE;
const phys_addr_t page_mask = page_size - 1;
u8 *buf_base;
unsigned int page_offset;
unsigned int num_pages;
unsigned int list_size;
unsigned int n;
void *page_list;
struct {
u64 pages_list[PAGELIST_ENTRIES_PER_PAGE];
u64 next_page_data;
} *pages_data;
page_offset = (ulong)buf & page_mask;
num_pages = roundup(page_offset + len, page_size) / page_size;
list_size = DIV_ROUND_UP(num_pages, PAGELIST_ENTRIES_PER_PAGE) *
page_size;
page_list = memalign(page_size, list_size);
if (!page_list)
return NULL;
pages_data = page_list;
buf_base = (u8 *)(rounddown((ulong)buf, page_size));
Drop extra pair of ()
n = 0;
while (num_pages) {
pages_data->pages_list[n] = virt_to_phys(buf_base);
n++;
buf_base += page_size;
num_pages--;
if (n == PAGELIST_ENTRIES_PER_PAGE) {
pages_data->next_page_data =
virt_to_phys(pages_data + 1);
pages_data++;
n = 0;
}
}
*phys_buf_ptr = virt_to_phys(page_list) | page_offset;
return page_list;
+}
+static void optee_get_version(struct udevice *dev,
struct tee_version_data *vers)
+{
struct tee_version_data v = {
.gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM,
};
*vers = v;
+}
+static struct tee_shm *get_msg_arg(struct udevice *dev, ulong num_params,
struct optee_msg_arg **msg_arg)
+{
struct tee_shm *shm;
struct optee_msg_arg *ma;
shm = __tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL,
OPTEE_MSG_GET_ARG_SIZE(num_params), TEE_SHM_ALLOC);
if (!shm)
return NULL;
ma = shm->addr;
memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params));
ma->num_params = num_params;
*msg_arg = ma;
return shm;
+}
+static int to_msg_param(struct optee_msg_param *msg_params, ulong num_params,
const struct tee_param *params)
+{
ulong n;
uint? int?
Save below
OK, I'll change the type for holding number of parameters to uint here and everywhere else.
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_PARAM_ATTR_TYPE_NONE:
mp->attr = OPTEE_MSG_ATTR_TYPE_NONE;
memset(&mp->u, 0, sizeof(mp->u));
break;
case TEE_PARAM_ATTR_TYPE_VALUE_INPUT:
case TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT:
case TEE_PARAM_ATTR_TYPE_VALUE_INOUT:
mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
TEE_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;
break;
case TEE_PARAM_ATTR_TYPE_MEMREF_INPUT:
case TEE_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
case TEE_PARAM_ATTR_TYPE_MEMREF_INOUT:
mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
TEE_PARAM_ATTR_TYPE_MEMREF_INPUT;
mp->u.rmem.shm_ref = (ulong)p->u.memref.shm;
mp->u.rmem.size = p->u.memref.size;
mp->u.rmem.offs = p->u.memref.shm_offs;
break;
default:
return -EINVAL;
}
}
return 0;
+}
+static int from_msg_param(struct tee_param *params, ulong num_params,
const struct optee_msg_param *msg_params)
+{
ulong n;
struct tee_shm *shm;
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_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:
p->attr = TEE_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;
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_PARAM_ATTR_TYPE_MEMREF_INPUT + attr -
OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
p->u.memref.size = mp->u.rmem.size;
shm = (struct tee_shm *)(ulong)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;
break;
default:
return -EINVAL;
}
}
return 0;
+}
+static void handle_rpc(struct udevice *dev, struct rpc_param *param,
void *page_list)
+{
struct tee_shm *shm;
switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) {
case OPTEE_SMC_RPC_FUNC_ALLOC:
shm = __tee_shm_add(dev, OPTEE_MSG_NONCONTIG_PAGE_SIZE, NULL,
param->a1,
TEE_SHM_ALLOC | TEE_SHM_REGISTER);
if (shm) {
reg_pair_from_64(¶m->a1, ¶m->a2,
virt_to_phys(shm->addr));
/* "cookie" */
reg_pair_from_64(¶m->a4, ¶m->a5, (ulong)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:
break;
case OPTEE_SMC_RPC_FUNC_CMD:
shm = reg_pair_to_ptr(param->a1, param->a2);
optee_suppl_cmd(dev, shm, page_list);
break;
default:
break;
}
param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC;
+}
+static u32 call_err_to_res(u32 call_err) +{
switch (call_err) {
case OPTEE_SMC_RETURN_OK:
return TEE_SUCCESS;
default:
return TEE_ERROR_BAD_PARAMETERS;
}
+}
+static u32 do_call_with_arg(struct udevice *dev, struct optee_msg_arg *arg) +{
struct optee_pdata *pdata = dev_get_platdata(dev);
struct rpc_param param = { .a0 = OPTEE_SMC_CALL_WITH_ARG };
void *page_list = NULL;
reg_pair_from_64(¶m.a1, ¶m.a2, virt_to_phys(arg));
while (true) {
struct arm_smccc_res res;
pdata->invoke_fn(param.a0, param.a1, param.a2, param.a3,
param.a4, param.a5, param.a6, param.a7, &res);
free(page_list);
page_list = NULL;
if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) {
param.a0 = res.a0;
param.a1 = res.a1;
param.a2 = res.a2;
param.a3 = res.a3;
handle_rpc(dev, ¶m, &page_list);
} else {
return call_err_to_res(res.a0);
}
}
+}
+static int optee_close_session(struct udevice *dev, u32 session) +{
struct tee_shm *shm;
struct optee_msg_arg *msg_arg;
shm = get_msg_arg(dev, 0, &msg_arg);
if (!shm)
return -ENOMEM;
msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
msg_arg->session = session;
do_call_with_arg(dev, msg_arg);
tee_shm_free(shm);
blank line before return
return 0;
+}
[..]
diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h new file mode 100644 index 000000000000..ebc16aa8cc31 --- /dev/null +++ b/drivers/tee/optee/optee_msg.h @@ -0,0 +1,423 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/*
- Copyright (c) 2015-2018, Linaro Limited
- */
+#ifndef _OPTEE_MSG_H +#define _OPTEE_MSG_H
+#include <linux/bitops.h> +#include <linux/types.h>
+/*
- This file defines the OP-TEE message protocol used to communicate
- with an instance of OP-TEE running in secure world.
Does this file have an upstream, or is this specific to U-Boot? If the former, please add a reference.
Yes, there's an upstream. I'll update.
- This file is divided into three sections.
- Formatting of messages.
- Requests from normal world
- Requests from secure world, Remote Procedure Call (RPC), handled by
- tee-supplicant.
- */
[...]
Thanks for the review, Jens

Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- doc/README.tee | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 doc/README.tee
diff --git a/doc/README.tee b/doc/README.tee new file mode 100644 index 000000000000..e9c9ef67877a --- /dev/null +++ b/doc/README.tee @@ -0,0 +1,112 @@ +============= +TEE uclass +============= + +This document describes the TEE uclass in U-boot + +A TEE (Trusted Execution Environment) is a trusted OS running in some +secure environment, for example, TrustZone on ARM CPUs, or a separate +secure co-processor etc. A TEE driver handles the details needed to +communicate with the TEE. + +This uclass deals with: + +- Registration of TEE drivers + +- Managing shared memory between U-boot and the TEE + +- Providing a generic API to the TEE + +The TEE interface +================= + +include/tee.h defines the generic interface to a TEE. + +A client finds the TEE device via tee_find_device(). Other important functions +when interfacing with a TEE are: + +- tee_shm_alloc(), tee_shm_register() and tee_shm_free() to manage shared + memory objects often needed when communicating with the TEE. + +- tee_get_version() lets the client know which the capabilities of the TEE + device. + +- tee_open_session() opens a session to a Trusted Application + +- tee_invoke_func() invokes a function in a Trusted Application + +- tee_close_session() closes a session to a Trusted Application + +Much of the communication between clients and the TEE is opaque to the +driver. The main job for the driver is to receive requests from the +clients, forward them to the TEE and send back the results. + +OP-TEE driver +============= + +The OP-TEE driver handles OP-TEE [1] based TEEs. Currently it is only the ARM +TrustZone based OP-TEE solution that is supported. + +Lowest level of communication with OP-TEE builds on ARM SMC Calling +Convention (SMCCC) [2], which is the foundation for OP-TEE's SMC interface +[3] used internally by the driver. Stacked on top of that is OP-TEE Message +Protocol [4]. + +OP-TEE SMC interface provides the basic functions required by SMCCC and some +additional functions specific for OP-TEE. The most interesting functions are: + +- OPTEE_SMC_FUNCID_CALLS_UID (part of SMCCC) returns the version information + which is then returned by TEE_IOC_VERSION + +- OPTEE_SMC_CALL_GET_OS_UUID returns the particular OP-TEE implementation, used + to tell, for instance, a TrustZone OP-TEE apart from an OP-TEE running on a + separate secure co-processor. + +- OPTEE_SMC_CALL_WITH_ARG drives the OP-TEE message protocol + +- OPTEE_SMC_GET_SHM_CONFIG lets the driver and OP-TEE agree on which memory + range to used for shared memory between Linux and OP-TEE. + +The GlobalPlatform TEE Client API [5] is implemented on top of the generic +TEE API. + +Picture of the relationship between the different components in the +OP-TEE architecture: + + U-boot Secure world + ~~~~~~ ~~~~~~~~~~~~ + +------------+ +-------------+ + | Client | | Trusted | + | | | Application | + +------------+ +-------------+ + /\ /\ + || || + / / + +------------+ +-------------+ + | TEE | | TEE Internal| + | uclass | | API | + +------------+ +-------------+ + | OP-TEE | | OP-TEE | + | driver | | Trusted OS | + +------------+-----------+-------------+ + | OP-TEE MSG | + | SMCCC (OPTEE_SMC_CALL_*) | + +--------------------------------------+ + +RPC (Remote Procedure Call) are requests from secure world to the driver. +An RPC is identified by a special range of SMCCC return values from +OPTEE_SMC_CALL_WITH_ARG. + +References +========== + +[1] https://github.com/OP-TEE/optee_os + +[2] http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html + +[3] drivers/tee/optee/optee_smc.h + +[4] drivers/tee/optee/optee_msg.h + +[5] http://www.globalplatform.org/specificationsdevice.asp look for + "TEE Client API Specification v1.0" and click download.

Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
doc/README.tee | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 doc/README.tee
Reviewed-by: Simon Glass sjg@chromium.org
Looks good, nits below.
diff --git a/doc/README.tee b/doc/README.tee new file mode 100644 index 000000000000..e9c9ef67877a --- /dev/null +++ b/doc/README.tee @@ -0,0 +1,112 @@ +============= +TEE uclass +=============
+This document describes the TEE uclass in U-boot
U-Boot
(please can you check all your patches for that? There are more below)
+A TEE (Trusted Execution Environment) is a trusted OS running in some +secure environment, for example, TrustZone on ARM CPUs, or a separate +secure co-processor etc. A TEE driver handles the details needed to +communicate with the TEE.
+This uclass deals with:
+- Registration of TEE drivers
+- Managing shared memory between U-boot and the TEE
+- Providing a generic API to the TEE
+The TEE interface +=================
+include/tee.h defines the generic interface to a TEE.
+A client finds the TEE device via tee_find_device(). Other important functions +when interfacing with a TEE are:
+- tee_shm_alloc(), tee_shm_register() and tee_shm_free() to manage shared
- memory objects often needed when communicating with the TEE.
+- tee_get_version() lets the client know which the capabilities of the TEE
- device.
+- tee_open_session() opens a session to a Trusted Application
+- tee_invoke_func() invokes a function in a Trusted Application
+- tee_close_session() closes a session to a Trusted Application
+Much of the communication between clients and the TEE is opaque to the +driver. The main job for the driver is to receive requests from the +clients, forward them to the TEE and send back the results.
+OP-TEE driver +=============
+The OP-TEE driver handles OP-TEE [1] based TEEs. Currently it is only the ARM +TrustZone based OP-TEE solution that is supported.
In fact, wouldn't other things be supported by different drivers?
Perhaps you should name your driver to indicate it is only for ARM?
+Lowest level of communication with OP-TEE builds on ARM SMC Calling +Convention (SMCCC) [2], which is the foundation for OP-TEE's SMC interface +[3] used internally by the driver. Stacked on top of that is OP-TEE Message +Protocol [4].
+OP-TEE SMC interface provides the basic functions required by SMCCC and some +additional functions specific for OP-TEE. The most interesting functions are:
+- OPTEE_SMC_FUNCID_CALLS_UID (part of SMCCC) returns the version information
- which is then returned by TEE_IOC_VERSION
+- OPTEE_SMC_CALL_GET_OS_UUID returns the particular OP-TEE implementation, used
- to tell, for instance, a TrustZone OP-TEE apart from an OP-TEE running on a
- separate secure co-processor.
+- OPTEE_SMC_CALL_WITH_ARG drives the OP-TEE message protocol
+- OPTEE_SMC_GET_SHM_CONFIG lets the driver and OP-TEE agree on which memory
- range to used for shared memory between Linux and OP-TEE.
+The GlobalPlatform TEE Client API [5] is implemented on top of the generic +TEE API.
+Picture of the relationship between the different components in the +OP-TEE architecture:
U-boot Secure world
~~~~~~ ~~~~~~~~~~~~
+------------+ +-------------+
| Client | | Trusted |
| | | Application |
+------------+ +-------------+
/\ /\
|| ||
\/ \/
+------------+ +-------------+
| TEE | | TEE Internal|
| uclass | | API |
+------------+ +-------------+
| OP-TEE | | OP-TEE |
| driver | | Trusted OS |
+------------+-----------+-------------+
| OP-TEE MSG |
| SMCCC (OPTEE_SMC_CALL_*) |
+--------------------------------------+
+RPC (Remote Procedure Call) are requests from secure world to the driver. +An RPC is identified by a special range of SMCCC return values from +OPTEE_SMC_CALL_WITH_ARG.
+References +==========
+[1] https://github.com/OP-TEE/optee_os
+[2] http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
+[3] drivers/tee/optee/optee_smc.h
+[4] drivers/tee/optee/optee_msg.h
+[5] http://www.globalplatform.org/specificationsdevice.asp look for
- "TEE Client API Specification v1.0" and click download.
-- 2.17.1
Regards, Simon

Hi Simon,
On Wed, Aug 29, 2018 at 06:28:54PM -0600, Simon Glass wrote:
Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
doc/README.tee | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 doc/README.tee
Reviewed-by: Simon Glass sjg@chromium.org
Looks good, nits below.
diff --git a/doc/README.tee b/doc/README.tee new file mode 100644 index 000000000000..e9c9ef67877a --- /dev/null +++ b/doc/README.tee @@ -0,0 +1,112 @@ +============= +TEE uclass +=============
+This document describes the TEE uclass in U-boot
U-Boot
(please can you check all your patches for that? There are more below)
Yes, I'll check them all.
+A TEE (Trusted Execution Environment) is a trusted OS running in some +secure environment, for example, TrustZone on ARM CPUs, or a separate +secure co-processor etc. A TEE driver handles the details needed to +communicate with the TEE.
+This uclass deals with:
+- Registration of TEE drivers
+- Managing shared memory between U-boot and the TEE
+- Providing a generic API to the TEE
+The TEE interface +=================
+include/tee.h defines the generic interface to a TEE.
+A client finds the TEE device via tee_find_device(). Other important functions +when interfacing with a TEE are:
+- tee_shm_alloc(), tee_shm_register() and tee_shm_free() to manage shared
- memory objects often needed when communicating with the TEE.
+- tee_get_version() lets the client know which the capabilities of the TEE
- device.
+- tee_open_session() opens a session to a Trusted Application
+- tee_invoke_func() invokes a function in a Trusted Application
+- tee_close_session() closes a session to a Trusted Application
+Much of the communication between clients and the TEE is opaque to the +driver. The main job for the driver is to receive requests from the +clients, forward them to the TEE and send back the results.
+OP-TEE driver +=============
+The OP-TEE driver handles OP-TEE [1] based TEEs. Currently it is only the ARM +TrustZone based OP-TEE solution that is supported.
In fact, wouldn't other things be supported by different drivers?
Perhaps you should name your driver to indicate it is only for ARM?
The OP-TEE Message prototol isn't tied to ARM only and OP-TEE has been or is used on at least one other architecture (not open sourced though). I think that only small changes will be needed in the the OP-TEE driver to support a different architecture as long as shared memory still can be used. I'd rather keep the name as just "optee" until we know what we need to adapt to.
+Lowest level of communication with OP-TEE builds on ARM SMC Calling +Convention (SMCCC) [2], which is the foundation for OP-TEE's SMC interface +[3] used internally by the driver. Stacked on top of that is OP-TEE Message +Protocol [4].
+OP-TEE SMC interface provides the basic functions required by SMCCC and some +additional functions specific for OP-TEE. The most interesting functions are:
+- OPTEE_SMC_FUNCID_CALLS_UID (part of SMCCC) returns the version information
- which is then returned by TEE_IOC_VERSION
+- OPTEE_SMC_CALL_GET_OS_UUID returns the particular OP-TEE implementation, used
- to tell, for instance, a TrustZone OP-TEE apart from an OP-TEE running on a
- separate secure co-processor.
+- OPTEE_SMC_CALL_WITH_ARG drives the OP-TEE message protocol
+- OPTEE_SMC_GET_SHM_CONFIG lets the driver and OP-TEE agree on which memory
- range to used for shared memory between Linux and OP-TEE.
+The GlobalPlatform TEE Client API [5] is implemented on top of the generic +TEE API.
+Picture of the relationship between the different components in the +OP-TEE architecture:
U-boot Secure world
~~~~~~ ~~~~~~~~~~~~
+------------+ +-------------+
| Client | | Trusted |
| | | Application |
+------------+ +-------------+
/\ /\
|| ||
\/ \/
+------------+ +-------------+
| TEE | | TEE Internal|
| uclass | | API |
+------------+ +-------------+
| OP-TEE | | OP-TEE |
| driver | | Trusted OS |
+------------+-----------+-------------+
| OP-TEE MSG |
| SMCCC (OPTEE_SMC_CALL_*) |
+--------------------------------------+
+RPC (Remote Procedure Call) are requests from secure world to the driver. +An RPC is identified by a special range of SMCCC return values from +OPTEE_SMC_CALL_WITH_ARG.
+References +==========
+[1] https://github.com/OP-TEE/optee_os
+[2] http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
+[3] drivers/tee/optee/optee_smc.h
+[4] drivers/tee/optee/optee_msg.h
+[5] http://www.globalplatform.org/specificationsdevice.asp look for
- "TEE Client API Specification v1.0" and click download.
-- 2.17.1
Thanks for the review, Jens

Tests the TEE uclass with a sandbox tee driver.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/tee/Kconfig | 5 +- test/dm/Makefile | 1 + test/dm/tee.c | 182 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 test/dm/tee.c
diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig index 3e7fe6ddcc5d..032aff4777c6 100644 --- a/drivers/tee/Kconfig +++ b/drivers/tee/Kconfig @@ -1,8 +1,8 @@ # Generic Trusted Execution Environment Configuration config TEE bool "Trusted Execution Environment support" - depends on ARM && (ARM64 || CPU_V7A) - select ARM_SMCCC + depends on (ARM && (ARM64 || CPU_V7A)) || SANDBOX + select ARM_SMCCC if ARM help This implements a generic interface towards a Trusted Execution Environment (TEE). @@ -14,5 +14,4 @@ menu "TEE drivers" source "drivers/tee/optee/Kconfig"
endmenu - endif diff --git a/test/dm/Makefile b/test/dm/Makefile index d2ed96c61533..272374b92fb0 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -44,4 +44,5 @@ obj-$(CONFIG_DM_VIDEO) += video.o obj-$(CONFIG_ADC) += adc.o obj-$(CONFIG_SPMI) += spmi.o obj-$(CONFIG_WDT) += wdt.o +obj-$(CONFIG_TEE) += tee.o endif diff --git a/test/dm/tee.c b/test/dm/tee.c new file mode 100644 index 000000000000..bf9db0c130ac --- /dev/null +++ b/test/dm/tee.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Linaro Limited + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <tee.h> +#include <test/ut.h> + +struct sandbox_tee_state { + u32 session; + int num_shms; +}; + +static void sandbox_tee_get_version(struct udevice *dev, + struct tee_version_data *vers) +{ + memset(vers, 0, sizeof(*vers)); +} + +static int sandbox_tee_close_session(struct udevice *dev, u32 session) +{ + struct sandbox_tee_state *state = dev_get_priv(dev); + + if (!state->session || state->session != session) + return -EINVAL; + + state->session = 0; + + return 0; +} + +static int sandbox_tee_open_session(struct udevice *dev, + struct tee_open_session_arg *arg, + ulong num_params, struct tee_param *params) +{ + struct sandbox_tee_state *state = dev_get_priv(dev); + + if (state->session) + return -EBUSY; + + state->session = 1; + arg->session = state->session; + + return 0; +} + +static int sandbox_tee_invoke_func(struct udevice *dev, + struct tee_invoke_arg *arg, + ulong num_params, struct tee_param *params) +{ + struct sandbox_tee_state *state = dev_get_priv(dev); + + if (!arg->session) + return -EINVAL; + + if (arg->session != state->session) + return -EINVAL; + + if (arg->func != 1) + return -ENOENT; + + return 0; +} + +static int sandbox_tee_shm_register(struct udevice *dev, struct tee_shm *shm) +{ + struct sandbox_tee_state *state = dev_get_priv(dev); + + state->num_shms++; + + return 0; +} + +static int sandbox_tee_shm_unregister(struct udevice *dev, struct tee_shm *shm) +{ + struct sandbox_tee_state *state = dev_get_priv(dev); + + state->num_shms--; + + return 0; +} + +static const struct tee_driver_ops sandbox_tee_ops = { + .get_version = sandbox_tee_get_version, + .open_session = sandbox_tee_open_session, + .close_session = sandbox_tee_close_session, + .invoke_func = sandbox_tee_invoke_func, + .shm_register = sandbox_tee_shm_register, + .shm_unregister = sandbox_tee_shm_unregister, +}; + +static const struct udevice_id sandbox_tee_match[] = { + { .compatible = "sandbox,tee" }, + {}, +}; + +U_BOOT_DRIVER(sandbox_tee) = { + .name = "sandbox_tee", + .id = UCLASS_TEE, + .of_match = sandbox_tee_match, + .ops = &sandbox_tee_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_tee_state), +}; + +static int open_session(struct udevice *dev, u32 *session) +{ + struct tee_open_session_arg arg; + int rc; + + memset(&arg, 0, sizeof(arg)); + rc = tee_open_session(dev, &arg, 0, NULL); + if (rc) + return rc; + if (arg.ret) + return -EIO; + *session = arg.session; + + return 0; +} + +static int invoke_func(struct udevice *dev, u32 session, u32 func) +{ + struct tee_invoke_arg arg = { .session = session, .func = func }; + + return tee_invoke_func(dev, &arg, 0, NULL); +} + +static int match(struct tee_version_data *vers, const void *data) +{ + return !vers->gen_caps; +} + +static int dm_test_tee(struct unit_test_state *uts) +{ + struct tee_version_data vers; + struct udevice *dev; + struct sandbox_tee_state *state; + u32 session; + int rc; + u8 data[128]; + struct tee_shm *reg_shm; + struct tee_shm *alloc_shm; + + dev = tee_find_device(NULL, match, NULL, &vers); + ut_assert(dev); + state = dev_get_priv(dev); + ut_assert(!state->session); + + rc = open_session(dev, &session); + ut_assert(!rc); + ut_assert(session == state->session); + + rc = invoke_func(dev, session, 1); + ut_assert(!rc); + + rc = tee_close_session(dev, session); + ut_assert(!rc); + ut_assert(!state->session); + + ut_assert(!state->num_shms); + reg_shm = tee_shm_register(dev, data, sizeof(data), 0); + ut_assert(reg_shm); + ut_assert(state->num_shms == 1); + + alloc_shm = tee_shm_alloc(dev, 256, 0); + ut_assert(alloc_shm); + ut_assert(state->num_shms == 2); + + ut_assert(tee_shm_is_registered(reg_shm, dev)); + ut_assert(tee_shm_is_registered(alloc_shm, dev)); + + tee_shm_free(reg_shm); + tee_shm_free(alloc_shm); + ut_assert(!state->num_shms); + + return 0; +} + +DM_TEST(dm_test_tee, DM_TESTF_SCAN_FDT);

On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Tests the TEE uclass with a sandbox tee driver.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
drivers/tee/Kconfig | 5 +- test/dm/Makefile | 1 + test/dm/tee.c | 182 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 test/dm/tee.c
Reviewed-by: Simon Glass sjg@chromium.org

Adds a sandbox_tee node to enable the sandbox tee driver in all the sandbox dts files.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- arch/sandbox/dts/sandbox.dts | 4 ++++ arch/sandbox/dts/sandbox64.dts | 4 ++++ arch/sandbox/dts/test.dts | 4 ++++ 3 files changed, 12 insertions(+)
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 9f444c96a9ec..f5c02e5396ff 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -311,6 +311,10 @@ }; }; }; + + sandbox_tee { + compatible = "sandbox,tee"; + }; };
#include "cros-ec-keyboard.dtsi" diff --git a/arch/sandbox/dts/sandbox64.dts b/arch/sandbox/dts/sandbox64.dts index 9e65d2fda3d3..0e32fdad9d81 100644 --- a/arch/sandbox/dts/sandbox64.dts +++ b/arch/sandbox/dts/sandbox64.dts @@ -311,6 +311,10 @@ }; }; }; + + sandbox_tee { + compatible = "sandbox,tee"; + }; };
#include "cros-ec-keyboard.dtsi" diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 70356461959d..eee7518be823 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -604,6 +604,10 @@ }; }; }; + + sandbox_tee { + compatible = "sandbox,tee"; + }; };
#include "sandbox_pmic.dtsi"

On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds a sandbox_tee node to enable the sandbox tee driver in all the sandbox dts files.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
arch/sandbox/dts/sandbox.dts | 4 ++++ arch/sandbox/dts/sandbox64.dts | 4 ++++ arch/sandbox/dts/test.dts | 4 ++++ 3 files changed, 12 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org
BTW it's only the test.dts one that matters for tests. But it's fine to add this in the others.

On Wed, Aug 29, 2018 at 06:28:59PM -0600, Simon Glass wrote:
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds a sandbox_tee node to enable the sandbox tee driver in all the sandbox dts files.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
arch/sandbox/dts/sandbox.dts | 4 ++++ arch/sandbox/dts/sandbox64.dts | 4 ++++ arch/sandbox/dts/test.dts | 4 ++++ 3 files changed, 12 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org
BTW it's only the test.dts one that matters for tests. But it's fine to add this in the others.
OK.
Thanks, Jens

Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + configs/sandbox_noblk_defconfig | 1 + configs/sandbox_spl_defconfig | 1 + 5 files changed, 5 insertions(+)
diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 5bd5927b410b..213755589382 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -199,3 +199,4 @@ CONFIG_UT_TIME=y CONFIG_UT_DM=y CONFIG_UT_ENV=y CONFIG_UT_OVERLAY=y +CONFIG_TEE=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index afc34298ed9f..e9cba7895c33 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -200,3 +200,4 @@ CONFIG_UT_TIME=y CONFIG_UT_DM=y CONFIG_UT_ENV=y CONFIG_UT_OVERLAY=y +CONFIG_TEE=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index 84b0756a4dc7..80f3055fd079 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -174,3 +174,4 @@ CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y CONFIG_UT_ENV=y +CONFIG_TEE=y diff --git a/configs/sandbox_noblk_defconfig b/configs/sandbox_noblk_defconfig index d1ca61bff2e8..54ba5bc7dd36 100644 --- a/configs/sandbox_noblk_defconfig +++ b/configs/sandbox_noblk_defconfig @@ -175,3 +175,4 @@ CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y CONFIG_UT_ENV=y +CONFIG_TEE=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index c00672f6cc0d..87cbd67c6d21 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -194,3 +194,4 @@ CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y CONFIG_UT_ENV=y +CONFIG_TEE=y

On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + configs/sandbox_noblk_defconfig | 1 + configs/sandbox_spl_defconfig | 1 + 5 files changed, 5 insertions(+)
Can you move this to an 'imply' in arch/Kconfig?
Regards, Simon

On Wed, Aug 29, 2018 at 06:29:02PM -0600, Simon Glass wrote:
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + configs/sandbox_noblk_defconfig | 1 + configs/sandbox_spl_defconfig | 1 + 5 files changed, 5 insertions(+)
Can you move this to an 'imply' in arch/Kconfig?
Will do.
Thanks, Jens

Sync with 14e21cb8f811 ("arm64: dt: hikey: Add optee node" from Linux kernel.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- arch/arm/dts/hi6220-hikey.dts | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/arch/arm/dts/hi6220-hikey.dts b/arch/arm/dts/hi6220-hikey.dts index 818525197508..24f09257af00 100644 --- a/arch/arm/dts/hi6220-hikey.dts +++ b/arch/arm/dts/hi6220-hikey.dts @@ -31,6 +31,13 @@ device_type = "memory"; reg = <0x0 0x0 0x0 0x40000000>; }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; };
&uart2 {

On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Sync with 14e21cb8f811 ("arm64: dt: hikey: Add optee node" from Linux kernel.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
arch/arm/dts/hi6220-hikey.dts | 7 +++++++ 1 file changed, 7 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

Adds support in optee supplicant to route signed (MACed) RPMB frames from OP-TEE Secure OS to MMC and vice versa to manipulate the RPMB partition.
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- drivers/tee/optee/Makefile | 1 + drivers/tee/optee/core.c | 8 ++ drivers/tee/optee/optee_private.h | 31 ++++- drivers/tee/optee/rpmb.c | 184 ++++++++++++++++++++++++++++++ drivers/tee/optee/supplicant.c | 3 + 5 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 drivers/tee/optee/rpmb.c
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 6148feb474a5..928d3f80027f 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -2,3 +2,4 @@
obj-y += core.o obj-y += supplicant.o +obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index f2d92d96551b..e297d206af3a 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -280,6 +280,13 @@ static u32 do_call_with_arg(struct udevice *dev, struct optee_msg_arg *arg) param.a3 = res.a3; handle_rpc(dev, ¶m, &page_list); } else { + /* + * In case we've accessed RPMB to serve an RPC + * request we need to restore the previously + * selected partition as the caller may expect it + * to remain unchanged. + */ + optee_suppl_rpmb_release(dev); return call_err_to_res(res.a0); } } @@ -611,4 +618,5 @@ U_BOOT_DRIVER(optee) = { .probe = optee_probe, .ops = &optee_ops, .platdata_auto_alloc_size = sizeof(struct optee_pdata), + .priv_auto_alloc_size = sizeof(struct optee_private), }; diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index daa470f812a9..b76979d21011 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -6,7 +6,36 @@ #ifndef __OPTEE_PRIVATE_H #define __OPTEE_PRIVATE_H
+#include <tee.h> +#include <log.h> + +struct optee_private { + struct mmc *rpmb_mmc; + int rpmb_dev_id; + char rpmb_original_part; +}; + +struct optee_msg_arg; + +void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg, + void **page_list); + +#ifdef CONFIG_SUPPORT_EMMC_RPMB +void optee_suppl_cmd_rpmb(struct udevice *dev, struct optee_msg_arg *arg); +void optee_suppl_rpmb_release(struct udevice *dev); +#else +static inline void optee_suppl_cmd_rpmb(struct udevice *dev, + struct optee_msg_arg *arg) +{ + debug("OPTEE_MSG_RPC_CMD_RPMB not implemented\n"); + arg->ret = TEE_ERROR_NOT_IMPLEMENTED; +} + +static inline void optee_suppl_rpmb_release(struct udevice *dev) +{ +} +#endif + void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr); -void optee_suppl_cmd(struct udevice *dev, void *shm, void **page_list);
#endif /*__OPTEE_PRIVATE_H*/ diff --git a/drivers/tee/optee/rpmb.c b/drivers/tee/optee/rpmb.c new file mode 100644 index 000000000000..c1447a5561c2 --- /dev/null +++ b/drivers/tee/optee/rpmb.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2018 Linaro Limited + */ + +#include <common.h> +#include <log.h> +#include <tee.h> +#include <mmc.h> + +#include "optee_msg.h" +#include "optee_private.h" + +/* + * Request and response definitions must be in sync with the secure side of + * OP-TEE. + */ + +/* Request */ +struct rpmb_req { + u16 cmd; +#define RPMB_CMD_DATA_REQ 0x00 +#define RPMB_CMD_GET_DEV_INFO 0x01 + u16 dev_id; + u16 block_count; + /* Optional data frames (rpmb_data_frame) follow */ +}; + +#define RPMB_REQ_DATA(req) ((void *)((struct rpmb_req *)(req) + 1)) + +/* Response to device info request */ +struct rpmb_dev_info { + u8 cid[16]; + u8 rpmb_size_mult; /* EXT CSD-slice 168: RPMB Size */ + u8 rel_wr_sec_c; /* EXT CSD-slice 222: Reliable Write Sector */ + /* Count */ + u8 ret_code; +#define RPMB_CMD_GET_DEV_INFO_RET_OK 0x00 +#define RPMB_CMD_GET_DEV_INFO_RET_ERROR 0x01 +}; + +static void release_mmc(struct optee_private *priv) +{ + int rc; + + if (!priv->rpmb_mmc) + return; + + rc = blk_select_hwpart_devnum(IF_TYPE_MMC, priv->rpmb_dev_id, + priv->rpmb_original_part); + if (rc) + debug("%s: blk_select_hwpart_devnum() failed: %d\n", + __func__, rc); + + priv->rpmb_mmc = NULL; +} + +static struct mmc *get_mmc(struct optee_private *priv, int dev_id) +{ + struct mmc *mmc; + int rc; + + if (priv->rpmb_mmc && priv->rpmb_dev_id == dev_id) + return priv->rpmb_mmc; + + release_mmc(priv); + + mmc = find_mmc_device(dev_id); + if (!mmc) { + debug("Cannot find RPMB device\n"); + return NULL; + } + if (!(mmc->version & MMC_VERSION_MMC)) { + debug("Device id %d is not an eMMC device\n", dev_id); + return NULL; + } + if (mmc->version < MMC_VERSION_4_41) { + debug("Device id %d: RPMB not supported before version 4.41\n", + dev_id); + return NULL; + } + +#ifdef CONFIG_BLK + priv->rpmb_original_part = mmc_get_blk_desc(mmc)->hwpart; +#else + priv->rpmb_original_part = mmc->block_dev.hwpart; +#endif + + rc = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_id, MMC_PART_RPMB); + if (rc) { + debug("Device id %d: cannot select RPMB partition: %d\n", + dev_id, rc); + return NULL; + } + + priv->rpmb_mmc = mmc; + priv->rpmb_dev_id = dev_id; + return mmc; +} + +static u32 rpmb_get_dev_info(u16 dev_id, struct rpmb_dev_info *info) +{ + struct mmc *mmc = find_mmc_device(dev_id); + + if (!mmc) + return TEE_ERROR_ITEM_NOT_FOUND; + + if (!mmc->ext_csd) + return TEE_ERROR_GENERIC; + + memcpy(info->cid, mmc->cid, sizeof(info->cid)); + info->rel_wr_sec_c = mmc->ext_csd[222]; + info->rpmb_size_mult = mmc->ext_csd[168]; + info->ret_code = RPMB_CMD_GET_DEV_INFO_RET_OK; + + return TEE_SUCCESS; +} + +static u32 rpmb_process_request(struct optee_private *priv, void *req, + ulong req_size, void *rsp, ulong rsp_size) +{ + struct rpmb_req *sreq = req; + struct mmc *mmc; + + if (req_size < sizeof(*sreq)) + return TEE_ERROR_BAD_PARAMETERS; + + switch (sreq->cmd) { + case RPMB_CMD_DATA_REQ: + mmc = get_mmc(priv, sreq->dev_id); + if (!mmc) + return TEE_ERROR_ITEM_NOT_FOUND; + if (mmc_rpmb_route_frames(mmc, RPMB_REQ_DATA(req), + req_size - sizeof(struct rpmb_req), + rsp, rsp_size)) + return TEE_ERROR_BAD_PARAMETERS; + return TEE_SUCCESS; + + case RPMB_CMD_GET_DEV_INFO: + if (req_size != sizeof(struct rpmb_req) || + rsp_size != sizeof(struct rpmb_dev_info)) { + debug("Invalid req/rsp size\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + return rpmb_get_dev_info(sreq->dev_id, rsp); + + default: + debug("Unsupported RPMB command: %d\n", sreq->cmd); + return TEE_ERROR_BAD_PARAMETERS; + } +} + +void optee_suppl_cmd_rpmb(struct udevice *dev, struct optee_msg_arg *arg) +{ + struct tee_shm *req_shm; + struct tee_shm *rsp_shm; + void *req_buf; + void *rsp_buf; + ulong req_size; + ulong rsp_size; + + if (arg->num_params != 2 || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_RMEM_INPUT || + arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT) { + arg->ret = TEE_ERROR_BAD_PARAMETERS; + return; + } + + req_shm = (struct tee_shm *)(ulong)arg->params[0].u.rmem.shm_ref; + req_buf = (u8 *)req_shm->addr + arg->params[0].u.rmem.offs; + req_size = arg->params[0].u.rmem.size; + + rsp_shm = (struct tee_shm *)(ulong)arg->params[1].u.rmem.shm_ref; + rsp_buf = (u8 *)rsp_shm->addr + arg->params[1].u.rmem.offs; + rsp_size = arg->params[1].u.rmem.size; + + arg->ret = rpmb_process_request(dev_get_priv(dev), req_buf, req_size, + rsp_buf, rsp_size); +} + +void optee_suppl_rpmb_release(struct udevice *dev) +{ + release_mmc(dev_get_priv(dev)); +} diff --git a/drivers/tee/optee/supplicant.c b/drivers/tee/optee/supplicant.c index 6965055bd1b5..14cb8717522c 100644 --- a/drivers/tee/optee/supplicant.c +++ b/drivers/tee/optee/supplicant.c @@ -81,6 +81,9 @@ void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg, debug("OPTEE_MSG_RPC_CMD_FS not implemented\n"); arg->ret = TEE_ERROR_NOT_IMPLEMENTED; break; + case OPTEE_MSG_RPC_CMD_RPMB: + optee_suppl_cmd_rpmb(dev, arg); + break; default: arg->ret = TEE_ERROR_NOT_IMPLEMENTED; }

Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds support in optee supplicant to route signed (MACed) RPMB frames from OP-TEE Secure OS to MMC and vice versa to manipulate the RPMB partition.
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
drivers/tee/optee/Makefile | 1 + drivers/tee/optee/core.c | 8 ++ drivers/tee/optee/optee_private.h | 31 ++++- drivers/tee/optee/rpmb.c | 184 ++++++++++++++++++++++++++++++ drivers/tee/optee/supplicant.c | 3 + 5 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 drivers/tee/optee/rpmb.c
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 6148feb474a5..928d3f80027f 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -2,3 +2,4 @@
obj-y += core.o obj-y += supplicant.o +obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index f2d92d96551b..e297d206af3a 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -280,6 +280,13 @@ static u32 do_call_with_arg(struct udevice *dev, struct optee_msg_arg *arg) param.a3 = res.a3; handle_rpc(dev, ¶m, &page_list); } else {
/*
* In case we've accessed RPMB to serve an RPC
* request we need to restore the previously
* selected partition as the caller may expect it
* to remain unchanged.
*/
optee_suppl_rpmb_release(dev); return call_err_to_res(res.a0); } }
@@ -611,4 +618,5 @@ U_BOOT_DRIVER(optee) = { .probe = optee_probe, .ops = &optee_ops, .platdata_auto_alloc_size = sizeof(struct optee_pdata),
.priv_auto_alloc_size = sizeof(struct optee_private),
}; diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index daa470f812a9..b76979d21011 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -6,7 +6,36 @@ #ifndef __OPTEE_PRIVATE_H #define __OPTEE_PRIVATE_H
+#include <tee.h> +#include <log.h>
+struct optee_private {
doc comment
struct mmc *rpmb_mmc;
int rpmb_dev_id;
char rpmb_original_part;
Why is this char? Are you trying to save memory? I doubt it will work :-)
+};
+struct optee_msg_arg;
+void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg,
void **page_list);
Function comments.
+#ifdef CONFIG_SUPPORT_EMMC_RPMB +void optee_suppl_cmd_rpmb(struct udevice *dev, struct optee_msg_arg *arg); +void optee_suppl_rpmb_release(struct udevice *dev); +#else +static inline void optee_suppl_cmd_rpmb(struct udevice *dev,
struct optee_msg_arg *arg)
+{
debug("OPTEE_MSG_RPC_CMD_RPMB not implemented\n");
arg->ret = TEE_ERROR_NOT_IMPLEMENTED;
+}
+static inline void optee_suppl_rpmb_release(struct udevice *dev) +{ +} +#endif
void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr); -void optee_suppl_cmd(struct udevice *dev, void *shm, void **page_list);
#endif /*__OPTEE_PRIVATE_H*/ diff --git a/drivers/tee/optee/rpmb.c b/drivers/tee/optee/rpmb.c new file mode 100644 index 000000000000..c1447a5561c2 --- /dev/null +++ b/drivers/tee/optee/rpmb.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: BSD-2-Clause +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <log.h> +#include <tee.h> +#include <mmc.h>
+#include "optee_msg.h" +#include "optee_private.h"
+/*
- Request and response definitions must be in sync with the secure side of
- OP-TEE.
- */
+/* Request */ +struct rpmb_req {
u16 cmd;
+#define RPMB_CMD_DATA_REQ 0x00 +#define RPMB_CMD_GET_DEV_INFO 0x01
u16 dev_id;
u16 block_count;
/* Optional data frames (rpmb_data_frame) follow */
+};
+#define RPMB_REQ_DATA(req) ((void *)((struct rpmb_req *)(req) + 1))
+/* Response to device info request */ +struct rpmb_dev_info {
u8 cid[16];
u8 rpmb_size_mult; /* EXT CSD-slice 168: RPMB Size */
u8 rel_wr_sec_c; /* EXT CSD-slice 222: Reliable Write Sector */
/* Count */
u8 ret_code;
+#define RPMB_CMD_GET_DEV_INFO_RET_OK 0x00 +#define RPMB_CMD_GET_DEV_INFO_RET_ERROR 0x01 +};
+static void release_mmc(struct optee_private *priv) +{
int rc;
if (!priv->rpmb_mmc)
return;
rc = blk_select_hwpart_devnum(IF_TYPE_MMC, priv->rpmb_dev_id,
priv->rpmb_original_part);
if (rc)
debug("%s: blk_select_hwpart_devnum() failed: %d\n",
__func__, rc);
priv->rpmb_mmc = NULL;
+}
+static struct mmc *get_mmc(struct optee_private *priv, int dev_id) +{
struct mmc *mmc;
int rc;
if (priv->rpmb_mmc && priv->rpmb_dev_id == dev_id)
return priv->rpmb_mmc;
release_mmc(priv);
mmc = find_mmc_device(dev_id);
if (!mmc) {
debug("Cannot find RPMB device\n");
return NULL;
}
if (!(mmc->version & MMC_VERSION_MMC)) {
debug("Device id %d is not an eMMC device\n", dev_id);
return NULL;
}
if (mmc->version < MMC_VERSION_4_41) {
debug("Device id %d: RPMB not supported before version 4.41\n",
dev_id);
return NULL;
}
+#ifdef CONFIG_BLK
You shouldn't support !CONFIG_BLK. It is going away in the next release.
priv->rpmb_original_part = mmc_get_blk_desc(mmc)->hwpart;
+#else
priv->rpmb_original_part = mmc->block_dev.hwpart;
+#endif
rc = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_id, MMC_PART_RPMB);
if (rc) {
debug("Device id %d: cannot select RPMB partition: %d\n",
dev_id, rc);
return NULL;
}
priv->rpmb_mmc = mmc;
priv->rpmb_dev_id = dev_id;
return mmc;
+}
+static u32 rpmb_get_dev_info(u16 dev_id, struct rpmb_dev_info *info) +{
struct mmc *mmc = find_mmc_device(dev_id);
if (!mmc)
return TEE_ERROR_ITEM_NOT_FOUND;
if (!mmc->ext_csd)
return TEE_ERROR_GENERIC;
memcpy(info->cid, mmc->cid, sizeof(info->cid));
info->rel_wr_sec_c = mmc->ext_csd[222];
info->rpmb_size_mult = mmc->ext_csd[168];
info->ret_code = RPMB_CMD_GET_DEV_INFO_RET_OK;
return TEE_SUCCESS;
+}
+static u32 rpmb_process_request(struct optee_private *priv, void *req,
ulong req_size, void *rsp, ulong rsp_size)
+{
struct rpmb_req *sreq = req;
struct mmc *mmc;
if (req_size < sizeof(*sreq))
return TEE_ERROR_BAD_PARAMETERS;
switch (sreq->cmd) {
case RPMB_CMD_DATA_REQ:
mmc = get_mmc(priv, sreq->dev_id);
if (!mmc)
return TEE_ERROR_ITEM_NOT_FOUND;
if (mmc_rpmb_route_frames(mmc, RPMB_REQ_DATA(req),
req_size - sizeof(struct rpmb_req),
rsp, rsp_size))
return TEE_ERROR_BAD_PARAMETERS;
return TEE_SUCCESS;
case RPMB_CMD_GET_DEV_INFO:
if (req_size != sizeof(struct rpmb_req) ||
rsp_size != sizeof(struct rpmb_dev_info)) {
debug("Invalid req/rsp size\n");
return TEE_ERROR_BAD_PARAMETERS;
}
return rpmb_get_dev_info(sreq->dev_id, rsp);
default:
debug("Unsupported RPMB command: %d\n", sreq->cmd);
return TEE_ERROR_BAD_PARAMETERS;
}
+}
+void optee_suppl_cmd_rpmb(struct udevice *dev, struct optee_msg_arg *arg) +{
struct tee_shm *req_shm;
struct tee_shm *rsp_shm;
void *req_buf;
void *rsp_buf;
ulong req_size;
ulong rsp_size;
if (arg->num_params != 2 ||
arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_RMEM_INPUT ||
arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT) {
arg->ret = TEE_ERROR_BAD_PARAMETERS;
return;
}
req_shm = (struct tee_shm *)(ulong)arg->params[0].u.rmem.shm_ref;
req_buf = (u8 *)req_shm->addr + arg->params[0].u.rmem.offs;
req_size = arg->params[0].u.rmem.size;
rsp_shm = (struct tee_shm *)(ulong)arg->params[1].u.rmem.shm_ref;
rsp_buf = (u8 *)rsp_shm->addr + arg->params[1].u.rmem.offs;
rsp_size = arg->params[1].u.rmem.size;
arg->ret = rpmb_process_request(dev_get_priv(dev), req_buf, req_size,
rsp_buf, rsp_size);
+}
+void optee_suppl_rpmb_release(struct udevice *dev) +{
release_mmc(dev_get_priv(dev));
+} diff --git a/drivers/tee/optee/supplicant.c b/drivers/tee/optee/supplicant.c index 6965055bd1b5..14cb8717522c 100644 --- a/drivers/tee/optee/supplicant.c +++ b/drivers/tee/optee/supplicant.c @@ -81,6 +81,9 @@ void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg, debug("OPTEE_MSG_RPC_CMD_FS not implemented\n"); arg->ret = TEE_ERROR_NOT_IMPLEMENTED; break;
case OPTEE_MSG_RPC_CMD_RPMB:
optee_suppl_cmd_rpmb(dev, arg);
break; default: arg->ret = TEE_ERROR_NOT_IMPLEMENTED; }
-- 2.17.1
Regards, Simon

Hi Simon,
On Wed, Aug 29, 2018 at 06:29:12PM -0600, Simon Glass wrote:
Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds support in optee supplicant to route signed (MACed) RPMB frames from OP-TEE Secure OS to MMC and vice versa to manipulate the RPMB partition.
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
drivers/tee/optee/Makefile | 1 + drivers/tee/optee/core.c | 8 ++ drivers/tee/optee/optee_private.h | 31 ++++- drivers/tee/optee/rpmb.c | 184 ++++++++++++++++++++++++++++++ drivers/tee/optee/supplicant.c | 3 + 5 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 drivers/tee/optee/rpmb.c
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 6148feb474a5..928d3f80027f 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile @@ -2,3 +2,4 @@
obj-y += core.o obj-y += supplicant.o +obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index f2d92d96551b..e297d206af3a 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -280,6 +280,13 @@ static u32 do_call_with_arg(struct udevice *dev, struct optee_msg_arg *arg) param.a3 = res.a3; handle_rpc(dev, ¶m, &page_list); } else {
/*
* In case we've accessed RPMB to serve an RPC
* request we need to restore the previously
* selected partition as the caller may expect it
* to remain unchanged.
*/
optee_suppl_rpmb_release(dev); return call_err_to_res(res.a0); } }
@@ -611,4 +618,5 @@ U_BOOT_DRIVER(optee) = { .probe = optee_probe, .ops = &optee_ops, .platdata_auto_alloc_size = sizeof(struct optee_pdata),
.priv_auto_alloc_size = sizeof(struct optee_private),
}; diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index daa470f812a9..b76979d21011 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -6,7 +6,36 @@ #ifndef __OPTEE_PRIVATE_H #define __OPTEE_PRIVATE_H
+#include <tee.h> +#include <log.h>
+struct optee_private {
doc comment
OK, I'll fix.
struct mmc *rpmb_mmc;
int rpmb_dev_id;
char rpmb_original_part;
Why is this char? Are you trying to save memory? I doubt it will work :-)
Good question. I think I got it from struct blk_desc::hwpart, but still got it slightly wrong. I'll change it into an int instead.
+};
+struct optee_msg_arg;
+void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg,
void **page_list);
Function comments.
+#ifdef CONFIG_SUPPORT_EMMC_RPMB +void optee_suppl_cmd_rpmb(struct udevice *dev, struct optee_msg_arg *arg); +void optee_suppl_rpmb_release(struct udevice *dev); +#else +static inline void optee_suppl_cmd_rpmb(struct udevice *dev,
struct optee_msg_arg *arg)
+{
debug("OPTEE_MSG_RPC_CMD_RPMB not implemented\n");
arg->ret = TEE_ERROR_NOT_IMPLEMENTED;
+}
+static inline void optee_suppl_rpmb_release(struct udevice *dev) +{ +} +#endif
void *optee_alloc_and_init_page_list(void *buf, ulong len, u64 *phys_buf_ptr); -void optee_suppl_cmd(struct udevice *dev, void *shm, void **page_list);
#endif /*__OPTEE_PRIVATE_H*/ diff --git a/drivers/tee/optee/rpmb.c b/drivers/tee/optee/rpmb.c new file mode 100644 index 000000000000..c1447a5561c2 --- /dev/null +++ b/drivers/tee/optee/rpmb.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: BSD-2-Clause +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <log.h> +#include <tee.h> +#include <mmc.h>
+#include "optee_msg.h" +#include "optee_private.h"
+/*
- Request and response definitions must be in sync with the secure side of
- OP-TEE.
- */
+/* Request */ +struct rpmb_req {
u16 cmd;
+#define RPMB_CMD_DATA_REQ 0x00 +#define RPMB_CMD_GET_DEV_INFO 0x01
u16 dev_id;
u16 block_count;
/* Optional data frames (rpmb_data_frame) follow */
+};
+#define RPMB_REQ_DATA(req) ((void *)((struct rpmb_req *)(req) + 1))
+/* Response to device info request */ +struct rpmb_dev_info {
u8 cid[16];
u8 rpmb_size_mult; /* EXT CSD-slice 168: RPMB Size */
u8 rel_wr_sec_c; /* EXT CSD-slice 222: Reliable Write Sector */
/* Count */
u8 ret_code;
+#define RPMB_CMD_GET_DEV_INFO_RET_OK 0x00 +#define RPMB_CMD_GET_DEV_INFO_RET_ERROR 0x01 +};
+static void release_mmc(struct optee_private *priv) +{
int rc;
if (!priv->rpmb_mmc)
return;
rc = blk_select_hwpart_devnum(IF_TYPE_MMC, priv->rpmb_dev_id,
priv->rpmb_original_part);
if (rc)
debug("%s: blk_select_hwpart_devnum() failed: %d\n",
__func__, rc);
priv->rpmb_mmc = NULL;
+}
+static struct mmc *get_mmc(struct optee_private *priv, int dev_id) +{
struct mmc *mmc;
int rc;
if (priv->rpmb_mmc && priv->rpmb_dev_id == dev_id)
return priv->rpmb_mmc;
release_mmc(priv);
mmc = find_mmc_device(dev_id);
if (!mmc) {
debug("Cannot find RPMB device\n");
return NULL;
}
if (!(mmc->version & MMC_VERSION_MMC)) {
debug("Device id %d is not an eMMC device\n", dev_id);
return NULL;
}
if (mmc->version < MMC_VERSION_4_41) {
debug("Device id %d: RPMB not supported before version 4.41\n",
dev_id);
return NULL;
}
+#ifdef CONFIG_BLK
You shouldn't support !CONFIG_BLK. It is going away in the next release.
OK, I'll remove the ifdef and just keep the mmc_get_blk_desc(mmc)->hwpart line then.
priv->rpmb_original_part = mmc_get_blk_desc(mmc)->hwpart;
+#else
priv->rpmb_original_part = mmc->block_dev.hwpart;
+#endif
rc = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_id, MMC_PART_RPMB);
if (rc) {
debug("Device id %d: cannot select RPMB partition: %d\n",
dev_id, rc);
return NULL;
}
priv->rpmb_mmc = mmc;
priv->rpmb_dev_id = dev_id;
return mmc;
+}
+static u32 rpmb_get_dev_info(u16 dev_id, struct rpmb_dev_info *info) +{
struct mmc *mmc = find_mmc_device(dev_id);
if (!mmc)
return TEE_ERROR_ITEM_NOT_FOUND;
if (!mmc->ext_csd)
return TEE_ERROR_GENERIC;
memcpy(info->cid, mmc->cid, sizeof(info->cid));
info->rel_wr_sec_c = mmc->ext_csd[222];
info->rpmb_size_mult = mmc->ext_csd[168];
info->ret_code = RPMB_CMD_GET_DEV_INFO_RET_OK;
return TEE_SUCCESS;
+}
+static u32 rpmb_process_request(struct optee_private *priv, void *req,
ulong req_size, void *rsp, ulong rsp_size)
+{
struct rpmb_req *sreq = req;
struct mmc *mmc;
if (req_size < sizeof(*sreq))
return TEE_ERROR_BAD_PARAMETERS;
switch (sreq->cmd) {
case RPMB_CMD_DATA_REQ:
mmc = get_mmc(priv, sreq->dev_id);
if (!mmc)
return TEE_ERROR_ITEM_NOT_FOUND;
if (mmc_rpmb_route_frames(mmc, RPMB_REQ_DATA(req),
req_size - sizeof(struct rpmb_req),
rsp, rsp_size))
return TEE_ERROR_BAD_PARAMETERS;
return TEE_SUCCESS;
case RPMB_CMD_GET_DEV_INFO:
if (req_size != sizeof(struct rpmb_req) ||
rsp_size != sizeof(struct rpmb_dev_info)) {
debug("Invalid req/rsp size\n");
return TEE_ERROR_BAD_PARAMETERS;
}
return rpmb_get_dev_info(sreq->dev_id, rsp);
default:
debug("Unsupported RPMB command: %d\n", sreq->cmd);
return TEE_ERROR_BAD_PARAMETERS;
}
+}
+void optee_suppl_cmd_rpmb(struct udevice *dev, struct optee_msg_arg *arg) +{
struct tee_shm *req_shm;
struct tee_shm *rsp_shm;
void *req_buf;
void *rsp_buf;
ulong req_size;
ulong rsp_size;
if (arg->num_params != 2 ||
arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_RMEM_INPUT ||
arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT) {
arg->ret = TEE_ERROR_BAD_PARAMETERS;
return;
}
req_shm = (struct tee_shm *)(ulong)arg->params[0].u.rmem.shm_ref;
req_buf = (u8 *)req_shm->addr + arg->params[0].u.rmem.offs;
req_size = arg->params[0].u.rmem.size;
rsp_shm = (struct tee_shm *)(ulong)arg->params[1].u.rmem.shm_ref;
rsp_buf = (u8 *)rsp_shm->addr + arg->params[1].u.rmem.offs;
rsp_size = arg->params[1].u.rmem.size;
arg->ret = rpmb_process_request(dev_get_priv(dev), req_buf, req_size,
rsp_buf, rsp_size);
+}
+void optee_suppl_rpmb_release(struct udevice *dev) +{
release_mmc(dev_get_priv(dev));
+} diff --git a/drivers/tee/optee/supplicant.c b/drivers/tee/optee/supplicant.c index 6965055bd1b5..14cb8717522c 100644 --- a/drivers/tee/optee/supplicant.c +++ b/drivers/tee/optee/supplicant.c @@ -81,6 +81,9 @@ void optee_suppl_cmd(struct udevice *dev, struct tee_shm *shm_arg, debug("OPTEE_MSG_RPC_CMD_FS not implemented\n"); arg->ret = TEE_ERROR_NOT_IMPLEMENTED; break;
case OPTEE_MSG_RPC_CMD_RPMB:
optee_suppl_cmd_rpmb(dev, arg);
break; default: arg->ret = TEE_ERROR_NOT_IMPLEMENTED; }
-- 2.17.1
Thanks for the review, Jens

Adds configuration option OPTEE_TA_AVB and a header file describing the interface to the AVB trusted application provided by OP-TEE.
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Reviewed-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- MAINTAINERS | 1 + drivers/tee/optee/Kconfig | 16 +++++++++++++ include/tee.h | 7 ++++++ include/tee/optee_ta_avb.h | 48 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 include/tee/optee_ta_avb.h
diff --git a/MAINTAINERS b/MAINTAINERS index 7458c606ee92..cb36c45d74ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -576,6 +576,7 @@ M: Jens Wiklander jens.wiklander@linaro.org S: Maintained F: drivers/tee/ F: include/tee.h +F: include/tee/
UBI M: Kyungmin Park kmpark@infradead.org diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig index 8f7ebe161111..a5dc08439629 100644 --- a/drivers/tee/optee/Kconfig +++ b/drivers/tee/optee/Kconfig @@ -5,3 +5,19 @@ config OPTEE help This implements the OP-TEE Trusted Execution Environment (TEE) driver. + +if OPTEE + +menu "OP-TEE options" + +config OPTEE_TA_AVB + bool "Support AVB TA" + default y + help + Enables support for the AVB Trusted Application (TA) in OP-TEE. + The TA can support the "avb" subcommands "read_rb", "write"rb" + and "is_unlocked". + +endmenu + +endif diff --git a/include/tee.h b/include/tee.h index 3e6771123ef0..b851d718d32f 100644 --- a/include/tee.h +++ b/include/tee.h @@ -48,6 +48,13 @@
#define TEE_ORIGIN_COMMS 0x00000002
+struct tee_optee_ta_uuid { + u32 time_low; + u16 time_mid; + u16 time_hi_and_version; + u8 clock_seq_and_node[8]; +}; + /** * struct tee_shm - memory shared with the TEE * @dev: The TEE device diff --git a/include/tee/optee_ta_avb.h b/include/tee/optee_ta_avb.h new file mode 100644 index 000000000000..0e1da084e09d --- /dev/null +++ b/include/tee/optee_ta_avb.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* Copyright (c) 2018, Linaro Limited */ + +#ifndef __TA_AVB_H +#define __TA_AVB_H + +#define TA_AVB_UUID { 0x023f8f1a, 0x292a, 0x432b, \ + { 0x8f, 0xc4, 0xde, 0x84, 0x71, 0x35, 0x80, 0x67 } } + +#define TA_AVB_MAX_ROLLBACK_LOCATIONS 256 + +/* + * Gets the rollback index corresponding to the given rollback index slot. + * + * in params[0].value.a: rollback index slot + * out params[1].value.a: upper 32 bits of rollback index + * out params[1].value.b: lower 32 bits of rollback index + */ +#define TA_AVB_CMD_READ_ROLLBACK_INDEX 0 + +/* + * Updates the rollback index corresponding to the given rollback index slot. + * + * Will refuse to update a slot with a lower value. + * + * in params[0].value.a: rollback index slot + * in params[1].value.a: upper 32 bits of rollback index + * in params[1].value.b: lower 32 bits of rollback index + */ +#define TA_AVB_CMD_WRITE_ROLLBACK_INDEX 1 + +/* + * Gets the lock state of the device. + * + * out params[0].value.a: lock state + */ +#define TA_AVB_CMD_READ_LOCK_STATE 2 + +/* + * Sets the lock state of the device. + * + * If the lock state is changed all rollback slots will be reset to 0 + * + * in params[0].value.a: lock state + */ +#define TA_AVB_CMD_WRITE_LOCK_STATE 3 + +#endif /*__TA_AVB_H*/

Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds configuration option OPTEE_TA_AVB and a header file describing the interface to the AVB trusted application provided by OP-TEE.
What is AVB? Can you please write it out in full?
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Reviewed-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 1 + drivers/tee/optee/Kconfig | 16 +++++++++++++ include/tee.h | 7 ++++++ include/tee/optee_ta_avb.h | 48 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 include/tee/optee_ta_avb.h
diff --git a/MAINTAINERS b/MAINTAINERS index 7458c606ee92..cb36c45d74ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -576,6 +576,7 @@ M: Jens Wiklander jens.wiklander@linaro.org S: Maintained F: drivers/tee/ F: include/tee.h +F: include/tee/
UBI M: Kyungmin Park kmpark@infradead.org diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig index 8f7ebe161111..a5dc08439629 100644 --- a/drivers/tee/optee/Kconfig +++ b/drivers/tee/optee/Kconfig @@ -5,3 +5,19 @@ config OPTEE help This implements the OP-TEE Trusted Execution Environment (TEE) driver.
+if OPTEE
+menu "OP-TEE options"
+config OPTEE_TA_AVB
bool "Support AVB TA"
default y
help
Enables support for the AVB Trusted Application (TA) in OP-TEE.
The TA can support the "avb" subcommands "read_rb", "write"rb"
and "is_unlocked".
+endmenu
+endif diff --git a/include/tee.h b/include/tee.h index 3e6771123ef0..b851d718d32f 100644 --- a/include/tee.h +++ b/include/tee.h @@ -48,6 +48,13 @@
#define TEE_ORIGIN_COMMS 0x00000002
+struct tee_optee_ta_uuid {
Comment on this struct. What is it for?
u32 time_low;
u16 time_mid;
u16 time_hi_and_version;
u8 clock_seq_and_node[8];
+};
/**
- struct tee_shm - memory shared with the TEE
- @dev: The TEE device
diff --git a/include/tee/optee_ta_avb.h b/include/tee/optee_ta_avb.h new file mode 100644 index 000000000000..0e1da084e09d --- /dev/null +++ b/include/tee/optee_ta_avb.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* Copyright (c) 2018, Linaro Limited */
+#ifndef __TA_AVB_H +#define __TA_AVB_H
+#define TA_AVB_UUID { 0x023f8f1a, 0x292a, 0x432b, \
{ 0x8f, 0xc4, 0xde, 0x84, 0x71, 0x35, 0x80, 0x67 } }
+#define TA_AVB_MAX_ROLLBACK_LOCATIONS 256
+/*
- Gets the rollback index corresponding to the given rollback index slot.
- in params[0].value.a: rollback index slot
- out params[1].value.a: upper 32 bits of rollback index
- out params[1].value.b: lower 32 bits of rollback index
- */
+#define TA_AVB_CMD_READ_ROLLBACK_INDEX 0
+/*
- Updates the rollback index corresponding to the given rollback index slot.
- Will refuse to update a slot with a lower value.
- in params[0].value.a: rollback index slot
- in params[1].value.a: upper 32 bits of rollback index
- in params[1].value.b: lower 32 bits of rollback index
- */
+#define TA_AVB_CMD_WRITE_ROLLBACK_INDEX 1
+/*
- Gets the lock state of the device.
- out params[0].value.a: lock state
- */
+#define TA_AVB_CMD_READ_LOCK_STATE 2
+/*
- Sets the lock state of the device.
- If the lock state is changed all rollback slots will be reset to 0
- in params[0].value.a: lock state
- */
+#define TA_AVB_CMD_WRITE_LOCK_STATE 3
+#endif /*__TA_AVB_H*/
Space before */
-- 2.17.1
Regards, Simon

Hi Simon,
On Wed, Aug 29, 2018 at 06:29:09PM -0600, Simon Glass wrote:
Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds configuration option OPTEE_TA_AVB and a header file describing the interface to the AVB trusted application provided by OP-TEE.
What is AVB? Can you please write it out in full?
AVB stands for Android Verified Boot 2.0. However, Google is a bit picky about how the name Android is used so I'm trying to avoid using it as much as possible to stay out of trouble. I'll write it out in the commit message.
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org Reviewed-by: Igor Opaniuk igor.opaniuk@linaro.org Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 1 + drivers/tee/optee/Kconfig | 16 +++++++++++++ include/tee.h | 7 ++++++ include/tee/optee_ta_avb.h | 48 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 include/tee/optee_ta_avb.h
diff --git a/MAINTAINERS b/MAINTAINERS index 7458c606ee92..cb36c45d74ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -576,6 +576,7 @@ M: Jens Wiklander jens.wiklander@linaro.org S: Maintained F: drivers/tee/ F: include/tee.h +F: include/tee/
UBI M: Kyungmin Park kmpark@infradead.org diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig index 8f7ebe161111..a5dc08439629 100644 --- a/drivers/tee/optee/Kconfig +++ b/drivers/tee/optee/Kconfig @@ -5,3 +5,19 @@ config OPTEE help This implements the OP-TEE Trusted Execution Environment (TEE) driver.
+if OPTEE
+menu "OP-TEE options"
+config OPTEE_TA_AVB
bool "Support AVB TA"
default y
help
Enables support for the AVB Trusted Application (TA) in OP-TEE.
The TA can support the "avb" subcommands "read_rb", "write"rb"
and "is_unlocked".
+endmenu
+endif diff --git a/include/tee.h b/include/tee.h index 3e6771123ef0..b851d718d32f 100644 --- a/include/tee.h +++ b/include/tee.h @@ -48,6 +48,13 @@
#define TEE_ORIGIN_COMMS 0x00000002
+struct tee_optee_ta_uuid {
Comment on this struct. What is it for?
I'll fix.
u32 time_low;
u16 time_mid;
u16 time_hi_and_version;
u8 clock_seq_and_node[8];
+};
/**
- struct tee_shm - memory shared with the TEE
- @dev: The TEE device
diff --git a/include/tee/optee_ta_avb.h b/include/tee/optee_ta_avb.h new file mode 100644 index 000000000000..0e1da084e09d --- /dev/null +++ b/include/tee/optee_ta_avb.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* Copyright (c) 2018, Linaro Limited */
+#ifndef __TA_AVB_H +#define __TA_AVB_H
+#define TA_AVB_UUID { 0x023f8f1a, 0x292a, 0x432b, \
{ 0x8f, 0xc4, 0xde, 0x84, 0x71, 0x35, 0x80, 0x67 } }
+#define TA_AVB_MAX_ROLLBACK_LOCATIONS 256
+/*
- Gets the rollback index corresponding to the given rollback index slot.
- in params[0].value.a: rollback index slot
- out params[1].value.a: upper 32 bits of rollback index
- out params[1].value.b: lower 32 bits of rollback index
- */
+#define TA_AVB_CMD_READ_ROLLBACK_INDEX 0
+/*
- Updates the rollback index corresponding to the given rollback index slot.
- Will refuse to update a slot with a lower value.
- in params[0].value.a: rollback index slot
- in params[1].value.a: upper 32 bits of rollback index
- in params[1].value.b: lower 32 bits of rollback index
- */
+#define TA_AVB_CMD_WRITE_ROLLBACK_INDEX 1
+/*
- Gets the lock state of the device.
- out params[0].value.a: lock state
- */
+#define TA_AVB_CMD_READ_LOCK_STATE 2
+/*
- Sets the lock state of the device.
- If the lock state is changed all rollback slots will be reset to 0
- in params[0].value.a: lock state
- */
+#define TA_AVB_CMD_WRITE_LOCK_STATE 3
+#endif /*__TA_AVB_H*/
Space before */
-- 2.17.1
Thanks for the review, Jens

Hi Jens,
On 31 August 2018 at 06:10, Jens Wiklander jens.wiklander@linaro.org wrote:
Hi Simon,
On Wed, Aug 29, 2018 at 06:29:09PM -0600, Simon Glass wrote:
Hi Jens,
On 23 August 2018 at 04:43, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds configuration option OPTEE_TA_AVB and a header file describing the interface to the AVB trusted application provided by OP-TEE.
What is AVB? Can you please write it out in full?
AVB stands for Android Verified Boot 2.0. However, Google is a bit picky about how the name Android is used so I'm trying to avoid using it as much as possible to stay out of trouble. I'll write it out in the commit message.
I think it should be somewhere in the docs at least. It looks like this is what you need to follow:
https://developer.android.com/distribute/marketing-tools/brand-guidelines
Regards, Simon

With CONFIG_OPTEE_TA_AVB use the trusted application AVB provided by OP-TEE to manage rollback indexes and device lock status.
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- common/avb_verify.c | 132 ++++++++++++++++++++++++++++++++++++++++++- doc/README.avb2 | 13 +++++ include/avb_verify.h | 4 ++ 3 files changed, 148 insertions(+), 1 deletion(-)
diff --git a/common/avb_verify.c b/common/avb_verify.c index 20e35ade3029..a50c05bf7847 100644 --- a/common/avb_verify.c +++ b/common/avb_verify.c @@ -10,6 +10,8 @@ #include <image.h> #include <malloc.h> #include <part.h> +#include <tee.h> +#include <tee/optee_ta_avb.h>
const unsigned char avb_root_pub[1032] = { 0x0, 0x0, 0x10, 0x0, 0x55, 0xd9, 0x4, 0xad, 0xd8, 0x4, @@ -594,6 +596,79 @@ static AvbIOResult validate_vbmeta_public_key(AvbOps *ops, return AVB_IO_RESULT_OK; }
+#ifdef CONFIG_OPTEE_TA_AVB +static void uuid_to_octets(u8 d[TEE_UUID_LEN], + const struct tee_optee_ta_uuid *s) +{ + d[0] = s->time_low >> 24; + d[1] = s->time_low >> 16; + d[2] = s->time_low >> 8; + d[3] = s->time_low; + d[4] = s->time_mid >> 8; + d[5] = s->time_mid; + d[6] = s->time_hi_and_version >> 8; + d[7] = s->time_hi_and_version; + memcpy(d + 8, s->clock_seq_and_node, sizeof(s->clock_seq_and_node)); +} + +static int get_open_session(struct AvbOpsData *ops_data) +{ + struct udevice *tee = NULL; + + while (!ops_data->tee) { + const struct tee_optee_ta_uuid uuid = TA_AVB_UUID; + struct tee_open_session_arg arg; + int rc; + + tee = tee_find_device(tee, NULL, NULL, NULL); + if (!tee) + return -ENODEV; + + memset(&arg, 0, sizeof(arg)); + uuid_to_octets(arg.uuid, &uuid); + rc = tee_open_session(tee, &arg, 0, NULL); + if (!rc) { + ops_data->tee = tee; + ops_data->session = arg.session; + } + } + + return 0; +} + +static AvbIOResult invoke_func(struct AvbOpsData *ops_data, u32 func, + ulong num_param, struct tee_param *param) +{ + struct tee_invoke_arg arg; + + if (get_open_session(ops_data)) + return AVB_IO_RESULT_ERROR_IO; + + memset(&arg, 0, sizeof(arg)); + arg.func = func; + arg.session = ops_data->session; + + if (tee_invoke_func(ops_data->tee, &arg, num_param, param)) + return AVB_IO_RESULT_ERROR_IO; + switch (arg.ret) { + case TEE_SUCCESS: + return AVB_IO_RESULT_OK; + case TEE_ERROR_OUT_OF_MEMORY: + return AVB_IO_RESULT_ERROR_OOM; + case TEE_ERROR_TARGET_DEAD: + /* + * The TA has paniced, close the session to reload the TA + * for the next request. + */ + tee_close_session(ops_data->tee, ops_data->session); + ops_data->tee = NULL; + return AVB_IO_RESULT_ERROR_IO; + default: + return AVB_IO_RESULT_ERROR_IO; + } +} +#endif + /** * read_rollback_index() - gets the rollback index corresponding to the * location of given by @out_rollback_index. @@ -609,6 +684,7 @@ static AvbIOResult read_rollback_index(AvbOps *ops, size_t rollback_index_slot, u64 *out_rollback_index) { +#ifndef CONFIG_OPTEE_TA_AVB /* For now we always return 0 as the stored rollback index. */ printf("%s not supported yet\n", __func__);
@@ -616,6 +692,27 @@ static AvbIOResult read_rollback_index(AvbOps *ops, *out_rollback_index = 0;
return AVB_IO_RESULT_OK; +#else + AvbIOResult rc; + struct tee_param param[2]; + + if (rollback_index_slot >= TA_AVB_MAX_ROLLBACK_LOCATIONS) + return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE; + + memset(param, 0, sizeof(param)); + param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = rollback_index_slot; + param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT; + + rc = invoke_func(ops->user_data, TA_AVB_CMD_READ_ROLLBACK_INDEX, + ARRAY_SIZE(param), param); + if (rc) + return rc; + + *out_rollback_index = (u64)param[1].u.value.a << 32 | + (u32)param[1].u.value.b; + return AVB_IO_RESULT_OK; +#endif }
/** @@ -633,10 +730,27 @@ static AvbIOResult write_rollback_index(AvbOps *ops, size_t rollback_index_slot, u64 rollback_index) { +#ifndef CONFIG_OPTEE_TA_AVB /* For now this is a no-op. */ printf("%s not supported yet\n", __func__);
return AVB_IO_RESULT_OK; +#else + struct tee_param param[2]; + + if (rollback_index_slot >= TA_AVB_MAX_ROLLBACK_LOCATIONS) + return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE; + + memset(param, 0, sizeof(param)); + param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = rollback_index_slot; + param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT; + param[1].u.value.a = (u32)(rollback_index >> 32); + param[1].u.value.b = (u32)rollback_index; + + return invoke_func(ops->user_data, TA_AVB_CMD_WRITE_ROLLBACK_INDEX, + ARRAY_SIZE(param), param); +#endif }
/** @@ -652,6 +766,7 @@ static AvbIOResult write_rollback_index(AvbOps *ops, */ static AvbIOResult read_is_device_unlocked(AvbOps *ops, bool *out_is_unlocked) { +#ifndef CONFIG_OPTEE_TA_AVB /* For now we always return that the device is unlocked. */
printf("%s not supported yet\n", __func__); @@ -659,6 +774,16 @@ static AvbIOResult read_is_device_unlocked(AvbOps *ops, bool *out_is_unlocked) *out_is_unlocked = true;
return AVB_IO_RESULT_OK; +#else + AvbIOResult rc; + struct tee_param param = { .attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT }; + + rc = invoke_func(ops->user_data, TA_AVB_CMD_READ_LOCK_STATE, 1, ¶m); + if (rc) + return rc; + *out_is_unlocked = !param.u.value.a; + return AVB_IO_RESULT_OK; +#endif }
/** @@ -737,6 +862,11 @@ void avb_ops_free(AvbOps *ops)
ops_data = ops->user_data;
- if (ops_data) + if (ops_data) { +#ifdef CONFIG_OPTEE_TA_AVB + if (ops_data->tee) + tee_close_session(ops_data->tee, ops_data->session); +#endif avb_free(ops_data); + } } diff --git a/doc/README.avb2 b/doc/README.avb2 index 120279fedbe2..a29cee1b6f50 100644 --- a/doc/README.avb2 +++ b/doc/README.avb2 @@ -18,6 +18,13 @@ Integrity of the bootloader (U-boot BLOB and environment) is out of scope. For additional details check: https://android.googlesource.com/platform/external/avb/+/master/README.md
+1.1. AVB using OP-TEE (optional) +--------------------------------- +If AVB is configured to use OP-TEE (see 4. below) rollback indexes and +device lock state are stored in RPMB. The RPMB partition is managed by +OP-TEE (https://www.op-tee.org/) which is a secure OS leveraging ARM +TrustZone. +
2. AVB 2.0 U-BOOT SHELL COMMANDS ----------------------------------- @@ -61,6 +68,12 @@ CONFIG_LIBAVB=y CONFIG_AVB_VERIFY=y CONFIG_CMD_AVB=y
+In addtion optionally if storing rollback indexes in RPMB with help of +OP-TEE: +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_OPTEE_TA_AVB=y +CONFIG_SUPPORT_EMMC_RPMB=y
Then add `avb verify` invocation to your android boot sequence of commands, e.g.: diff --git a/include/avb_verify.h b/include/avb_verify.h index eaa60f5393ef..a532a2331aea 100644 --- a/include/avb_verify.h +++ b/include/avb_verify.h @@ -27,6 +27,10 @@ struct AvbOpsData { struct AvbOps ops; int mmc_dev; enum avb_boot_state boot_state; +#ifdef CONFIG_OPTEE_TA_AVB + struct udevice *tee; + u32 session; +#endif };
struct mmc_part {
participants (3)
-
Jens Wiklander
-
Michal Simek
-
Simon Glass