[U-Boot] [PATCH 00/10] AVB using OP-TEE

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.
Thanks, Jens
Jens Wiklander (10): dm: fdt: scan for devices under /firmware too cmd: avb read_rb: print rb_idx in hexadecimal 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 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 + cmd/avb.c | 2 +- common/avb_verify.c | 132 +++- .../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 | 18 + 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 | 180 +++++ include/avb_verify.h | 4 + include/dm/uclass-id.h | 1 + include/mmc.h | 2 + include/tee.h | 141 ++++ include/tee/optee_ta_avb.h | 48 ++ 26 files changed, 2816 insertions(+), 7 deletions(-) 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

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 Mon, Aug 13, 2018 at 05:53:38PM +0200, Jens Wiklander 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(-)
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);
So, the /firmware node seems special. Have you talked with the devicetree folks to get it listed in the spec? That would seem rather valuable for something used by many parties. Thanks!

On 15.8.2018 16:17, Tom Rini wrote:
On Mon, Aug 13, 2018 at 05:53:38PM +0200, Jens Wiklander 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(-)
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);
So, the /firmware node seems special. Have you talked with the devicetree folks to get it listed in the spec? That would seem rather valuable for something used by many parties. Thanks!
some days ago we have sent a patch for this too. https://lists.denx.de/pipermail/u-boot/2018-August/338049.html
It is using different way but it should do the same thing.
Thanks, Michal

On Wed, Aug 15, 2018 at 04:30:15PM +0200, Michal Simek wrote:
On 15.8.2018 16:17, Tom Rini wrote:
On Mon, Aug 13, 2018 at 05:53:38PM +0200, Jens Wiklander 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(-)
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);
So, the /firmware node seems special. Have you talked with the devicetree folks to get it listed in the spec? That would seem rather valuable for something used by many parties. Thanks!
some days ago we have sent a patch for this too. https://lists.denx.de/pipermail/u-boot/2018-August/338049.html
It is using different way but it should do the same thing.
OK, so it sounds like in terms of code clean-ups, we need something like what you reference and then some further clean-ups on top of that perhaps for other places to call dm_scan_fdt_ofnode_path() for special cases. And in terms of formalized specification bits, I do think /firmware should perhaps get kicked up to the spec itself so that it's very clear to all consumers.

Hi Rob,
On 15.8.2018 16:34, Tom Rini wrote:
On Wed, Aug 15, 2018 at 04:30:15PM +0200, Michal Simek wrote:
On 15.8.2018 16:17, Tom Rini wrote:
On Mon, Aug 13, 2018 at 05:53:38PM +0200, Jens Wiklander 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(-)
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);
So, the /firmware node seems special. Have you talked with the devicetree folks to get it listed in the spec? That would seem rather valuable for something used by many parties. Thanks!
some days ago we have sent a patch for this too. https://lists.denx.de/pipermail/u-boot/2018-August/338049.html
It is using different way but it should do the same thing.
OK, so it sounds like in terms of code clean-ups, we need something like what you reference and then some further clean-ups on top of that perhaps for other places to call dm_scan_fdt_ofnode_path() for special cases. And in terms of formalized specification bits, I do think /firmware should perhaps get kicked up to the spec itself so that it's very clear to all consumers.
I was also checking latest devicetree spec and there is no record about /firmware node and how it is supposed to be used.
Thanks, Michal

On Wed, Aug 15, 2018 at 8:51 AM Michal Simek michal.simek@xilinx.com wrote:
Hi Rob,
On 15.8.2018 16:34, Tom Rini wrote:
On Wed, Aug 15, 2018 at 04:30:15PM +0200, Michal Simek wrote:
On 15.8.2018 16:17, Tom Rini wrote:
On Mon, Aug 13, 2018 at 05:53:38PM +0200, Jens Wiklander 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(-)
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);
So, the /firmware node seems special. Have you talked with the devicetree folks to get it listed in the spec? That would seem rather valuable for something used by many parties. Thanks!
some days ago we have sent a patch for this too. https://lists.denx.de/pipermail/u-boot/2018-August/338049.html
It is using different way but it should do the same thing.
OK, so it sounds like in terms of code clean-ups, we need something like what you reference and then some further clean-ups on top of that perhaps for other places to call dm_scan_fdt_ofnode_path() for special cases. And in terms of formalized specification bits, I do think /firmware should perhaps get kicked up to the spec itself so that it's very clear to all consumers.
I was also checking latest devicetree spec and there is no record about /firmware node and how it is supposed to be used.
Patches welcome. :)
It's really only a container to define non-discoverable firmware interfaces. Typically accessed thru a secure call (for ARM) or a mailbox. It is primarily just convention rather than a strict requirement. We have to support firmware nodes at the top-level too anyways.
Rob

On Wed, Aug 15, 2018 at 09:31:30AM -0600, Rob Herring wrote:
On Wed, Aug 15, 2018 at 8:51 AM Michal Simek michal.simek@xilinx.com wrote:
Hi Rob,
On 15.8.2018 16:34, Tom Rini wrote:
On Wed, Aug 15, 2018 at 04:30:15PM +0200, Michal Simek wrote:
On 15.8.2018 16:17, Tom Rini wrote:
On Mon, Aug 13, 2018 at 05:53:38PM +0200, Jens Wiklander 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(-)
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);
So, the /firmware node seems special. Have you talked with the devicetree folks to get it listed in the spec? That would seem rather valuable for something used by many parties. Thanks!
some days ago we have sent a patch for this too. https://lists.denx.de/pipermail/u-boot/2018-August/338049.html
It is using different way but it should do the same thing.
OK, so it sounds like in terms of code clean-ups, we need something like what you reference and then some further clean-ups on top of that perhaps for other places to call dm_scan_fdt_ofnode_path() for special cases. And in terms of formalized specification bits, I do think /firmware should perhaps get kicked up to the spec itself so that it's very clear to all consumers.
I was also checking latest devicetree spec and there is no record about /firmware node and how it is supposed to be used.
Patches welcome. :)
It's really only a container to define non-discoverable firmware interfaces. Typically accessed thru a secure call (for ARM) or a mailbox. It is primarily just convention rather than a strict requirement. We have to support firmware nodes at the top-level too anyways.
Right. To be clear, I'm suggesting that someone (I was thinking Jens) pop over to the devicetree-spec list and ask about adding _something_ there as "firmware" isn't even on the list of generic names and may even warrant something in section 3 under base device node types.

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 hexadeciaml number too.
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;

Typo in the commit message "hexadeciaml number", otherwise:
Reviewed-by: Igor Opaniuk igor.opaniuk@linaro.org
On 13 August 2018 at 18:53, 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 hexadeciaml number too.
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;
-- 2.17.1

Adds mmc_rpmb_route_frames() to route RPMB data frames from/to an external entity.
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

As I didn't have any available Hikey board, tested this on Poplar:
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org
BTW, we've had it up for discussion already, but just to clarify and summarize: As ID of eMMC is hardcoded in the OP-TEE OS core (CFG_RPMB_FS_DEV_ID), we will probably have issues on some platforms, where there is a difference in the probe order of MMC controllers (for example, on Poplar eMMC is 0 in U-boot, but in Linux it's 1, as SD is enumerated as 0). I guess it's unlikely that people will introduce changes to U-boot/Linux to make this order conform to each other, so instead, we should let the Normal World-part to decide what eMMC id to use from these RPMB frames.
Added Joakim and Jerome so they can follow this thread.
Thanks
On 13 August 2018 at 18:53, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds mmc_rpmb_route_frames() to route RPMB data frames from/to an external entity.
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
2.17.1

Hi Igor,
On Thu, Aug 16, 2018 at 2:13 PM, Igor Opaniuk igor.opaniuk@linaro.org wrote:
As I didn't have any available Hikey board, tested this on Poplar:
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org
BTW, we've had it up for discussion already, but just to clarify and summarize: As ID of eMMC is hardcoded in the OP-TEE OS core (CFG_RPMB_FS_DEV_ID), we will probably have issues on some platforms, where there is a difference in the probe order of MMC controllers (for example, on Poplar eMMC is 0 in U-boot, but in Linux it's 1, as SD is enumerated as 0). I guess it's unlikely that people will introduce changes to U-boot/Linux to make this order conform to each other, so instead, we should let the Normal World-part to decide what eMMC id to use from these RPMB frames.
Both U-boot and Linux are part of Normal World. I guess you're suggesting to move the logic of selecting RPMB device based on the ID from Secure World to tee-supplicant. For Linux that's a user space daemon and in U-boot that's part of the OP-TEE driver. I think it would be unfortunate to have this logic in user space, upgrades can make a mess of this.
This is in one aspect a board specific problem which can be addressed by defining the sequence number (what's indicated by CFG_RPMB_FS_DEV_ID above) of relevant RPMB partitions on a specific board. This is what we have been relying on indirectly so far.
Another way to deal with this problem is to let Secure World probe all available RPMB partitions and use the one using the expected shared secret for MACing. A drawback is that it's more complicated in Secure World and will take a while before it's implemented.
Thanks, Jens

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.
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 | 180 +++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/tee.h | 134 +++++++++++++++++++++++++++++ 8 files changed, 335 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..f0243a0f2e4e --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018 Linaro Limited + */ + +#include <common.h> +#include <dm.h> +#include <tee.h> + +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; + + 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..c2ac13e34128 --- /dev/null +++ b/include/tee.h @@ -0,0 +1,134 @@ +/* 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) +#define TEE_SHM_SEC_REGISTER BIT(1) +#define TEE_SHM_ALLOC BIT(2) + +#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. + */ +#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_driver_ops; + +struct tee_shm { + struct udevice *dev; + struct list_head link; + void *addr; + ulong size; + u32 flags; +}; + +struct tee_param_memref { + ulong shm_offs; + ulong size; + struct tee_shm *shm; +}; + +struct tee_param_value { + u64 a; + u64 b; + u64 c; +}; + +struct tee_param { + u64 attr; + union { + struct tee_param_memref memref; + struct tee_param_value value; + } u; +}; + +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 { + u32 func; + u32 session; + u32 ret; + u32 ret_origin; +}; + +struct tee_version_data { + u32 gen_caps; +}; + +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); + int (*shm_register)(struct udevice *dev, struct tee_shm *shm); + int (*shm_unregister)(struct udevice *dev, struct tee_shm *shm); +}; + +struct tee_shm *__tee_shm_add(struct udevice *dev, ulong align, void *addr, + ulong size, u32 flags); +struct tee_shm *tee_shm_alloc(struct udevice *dev, ulong size, u32 flags); +struct tee_shm *tee_shm_register(struct udevice *dev, void *addr, + ulong length, u32 flags); +void tee_shm_free(struct tee_shm *shm); +bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev); + +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); + +void tee_get_version(struct udevice *dev, struct tee_version_data *vers); +int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg, + ulong num_param, struct tee_param *param); + +int tee_close_session(struct udevice *dev, u32 session); + +int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg, + ulong num_param, struct tee_param *param); + +#endif /*__TEE_H*/

Tested this on Poplar:
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org
On 13 August 2018 at 18:53, 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.
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 | 180 +++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/tee.h | 134 +++++++++++++++++++++++++++++ 8 files changed, 335 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..f0243a0f2e4e --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <dm.h> +#include <tee.h>
+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;
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..c2ac13e34128 --- /dev/null +++ b/include/tee.h @@ -0,0 +1,134 @@ +/* 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) +#define TEE_SHM_SEC_REGISTER BIT(1) +#define TEE_SHM_ALLOC BIT(2)
+#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.
- */
+#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_driver_ops;
+struct tee_shm {
struct udevice *dev;
struct list_head link;
void *addr;
ulong size;
u32 flags;
+};
+struct tee_param_memref {
ulong shm_offs;
ulong size;
struct tee_shm *shm;
+};
+struct tee_param_value {
u64 a;
u64 b;
u64 c;
+};
+struct tee_param {
u64 attr;
union {
struct tee_param_memref memref;
struct tee_param_value value;
} u;
+};
+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 {
u32 func;
u32 session;
u32 ret;
u32 ret_origin;
+};
+struct tee_version_data {
u32 gen_caps;
+};
+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);
int (*shm_register)(struct udevice *dev, struct tee_shm *shm);
int (*shm_unregister)(struct udevice *dev, struct tee_shm *shm);
+};
+struct tee_shm *__tee_shm_add(struct udevice *dev, ulong align, void *addr,
ulong size, u32 flags);
+struct tee_shm *tee_shm_alloc(struct udevice *dev, ulong size, u32 flags); +struct tee_shm *tee_shm_register(struct udevice *dev, void *addr,
ulong length, u32 flags);
+void tee_shm_free(struct tee_shm *shm); +bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev);
+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);
+void tee_get_version(struct udevice *dev, struct tee_version_data *vers); +int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg,
ulong num_param, struct tee_param *param);
+int tee_close_session(struct udevice *dev, u32 session);
+int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg,
ulong num_param, struct tee_param *param);
+#endif /*__TEE_H*/
2.17.1

Hi Jens,
On 13 August 2018 at 09:53, 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.
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 | 180 +++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/tee.h | 134 +++++++++++++++++++++++++++++ 8 files changed, 335 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
Please add a sandbox driver and a test in test/dm for any new uclass.
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..f0243a0f2e4e --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <dm.h> +#include <tee.h>
+struct tee_uclass_priv {
struct list_head list_shm;
Struct comment please
+};
+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));
Rather than allocating memory here, can you use driver-model's auto-alloc features?
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)
'find' should find the device, not probe anything. Is that what this does?
+{
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);
blank line before return
return 0;
+}
+static int tee_pre_remove(struct udevice *dev) +{
struct tee_uclass_priv *priv = dev_get_uclass_priv(dev);
struct tee_shm *shm;
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..c2ac13e34128 --- /dev/null +++ b/include/tee.h @@ -0,0 +1,134 @@ +/* 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) +#define TEE_SHM_SEC_REGISTER BIT(1) +#define TEE_SHM_ALLOC BIT(2)
+#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.
- */
+#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_driver_ops;
+struct tee_shm {
comments for these structs please.
Also the whole uclass needs a README or more docs. Perhaps I have missed this?
struct udevice *dev;
struct list_head link;
void *addr;
ulong size;
u32 flags;
+};
+struct tee_param_memref {
ulong shm_offs;
ulong size;
struct tee_shm *shm;
+};
+struct tee_param_value {
u64 a;
u64 b;
u64 c;
+};
+struct tee_param {
u64 attr;
union {
struct tee_param_memref memref;
struct tee_param_value value;
} u;
+};
+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 {
u32 func;
u32 session;
u32 ret;
u32 ret_origin;
+};
+struct tee_version_data {
u32 gen_caps;
+};
+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);
int (*shm_register)(struct udevice *dev, struct tee_shm *shm);
int (*shm_unregister)(struct udevice *dev, struct tee_shm *shm);
Each of these needs a full function comment. Without that I cannot really comment on the API. Same for functions below.
+};
+struct tee_shm *__tee_shm_add(struct udevice *dev, ulong align, void *addr,
ulong size, u32 flags);
+struct tee_shm *tee_shm_alloc(struct udevice *dev, ulong size, u32 flags); +struct tee_shm *tee_shm_register(struct udevice *dev, void *addr,
ulong length, u32 flags);
+void tee_shm_free(struct tee_shm *shm); +bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev);
+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);
+void tee_get_version(struct udevice *dev, struct tee_version_data *vers); +int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg,
ulong num_param, struct tee_param *param);
+int tee_close_session(struct udevice *dev, u32 session);
+int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg,
ulong num_param, struct tee_param *param);
+#endif /*__TEE_H*/
2.17.1
Regards, Simon

Hi Simon,
On Fri, Aug 17, 2018 at 06:48:47AM -0600, Simon Glass wrote:
Hi Jens,
On 13 August 2018 at 09:53, 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.
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 | 180 +++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/tee.h | 134 +++++++++++++++++++++++++++++ 8 files changed, 335 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
Please add a sandbox driver and a test in test/dm for any new uclass.
OK
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..f0243a0f2e4e --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <dm.h> +#include <tee.h>
+struct tee_uclass_priv {
struct list_head list_shm;
Struct comment please
+};
+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));
Rather than allocating memory here, can you use driver-model's auto-alloc features?
I don't see how that can be done, these shared memory objects are dynamically allocated by the client. The number of needed objects isn't easily predicted.
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)
'find' should find the device, not probe anything. Is that what this does?
It probes the device too, I'm adding that in the description of this function. The probe is needed in order to read out the capabilities of the TEE.
+{
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);
blank line before return
return 0;
+}
+static int tee_pre_remove(struct udevice *dev) +{
struct tee_uclass_priv *priv = dev_get_uclass_priv(dev);
struct tee_shm *shm;
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..c2ac13e34128 --- /dev/null +++ b/include/tee.h @@ -0,0 +1,134 @@ +/* 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) +#define TEE_SHM_SEC_REGISTER BIT(1) +#define TEE_SHM_ALLOC BIT(2)
+#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.
- */
+#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_driver_ops;
+struct tee_shm {
comments for these structs please.
Also the whole uclass needs a README or more docs. Perhaps I have missed this?
I'm sorry, didn't supply that. I'll add it in the v2.
struct udevice *dev;
struct list_head link;
void *addr;
ulong size;
u32 flags;
+};
+struct tee_param_memref {
ulong shm_offs;
ulong size;
struct tee_shm *shm;
+};
+struct tee_param_value {
u64 a;
u64 b;
u64 c;
+};
+struct tee_param {
u64 attr;
union {
struct tee_param_memref memref;
struct tee_param_value value;
} u;
+};
+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 {
u32 func;
u32 session;
u32 ret;
u32 ret_origin;
+};
+struct tee_version_data {
u32 gen_caps;
+};
+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);
int (*shm_register)(struct udevice *dev, struct tee_shm *shm);
int (*shm_unregister)(struct udevice *dev, struct tee_shm *shm);
Each of these needs a full function comment. Without that I cannot really comment on the API. Same for functions below.
OK
+};
+struct tee_shm *__tee_shm_add(struct udevice *dev, ulong align, void *addr,
ulong size, u32 flags);
+struct tee_shm *tee_shm_alloc(struct udevice *dev, ulong size, u32 flags); +struct tee_shm *tee_shm_register(struct udevice *dev, void *addr,
ulong length, u32 flags);
+void tee_shm_free(struct tee_shm *shm); +bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev);
+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);
+void tee_get_version(struct udevice *dev, struct tee_version_data *vers); +int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg,
ulong num_param, struct tee_param *param);
+int tee_close_session(struct udevice *dev, u32 session);
+int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg,
ulong num_param, struct tee_param *param);
+#endif /*__TEE_H*/
2.17.1
Thanks for the review. I'll address all the comments in the comming v2 patch set.
-- Jens

Hi Jens,
On 13 August 2018 at 09:53, 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.
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 | 180 +++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/tee.h | 134 +++++++++++++++++++++++++++++ 8 files changed, 335 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..f0243a0f2e4e --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <dm.h> +#include <tee.h>
+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;
It seems like this can return errors other than -ENOMEM. How about changing this function to return an int?
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 before return
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;
This will probe each device as it looks through them. Is that what you want?
}
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;
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 */
We need a bit more information about what this is for. It could go in a README or in the uclass header file.
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..c2ac13e34128 --- /dev/null +++ b/include/tee.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#ifndef __TEE_H +#define __TEE_H
+#include <common.h> +#include <dm.h>
These should be included by .c files.
+#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) +#define TEE_SHM_SEC_REGISTER BIT(1) +#define TEE_SHM_ALLOC BIT(2)
+#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.
- */
+#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_driver_ops;
+struct tee_shm {
Comment structure, and below
struct udevice *dev;
struct list_head link;
void *addr;
ulong size;
u32 flags;
+};
+struct tee_param_memref {
ulong shm_offs;
ulong size;
struct tee_shm *shm;
+};
+struct tee_param_value {
u64 a;
u64 b;
u64 c;
+};
+struct tee_param {
u64 attr;
union {
struct tee_param_memref memref;
struct tee_param_value value;
} u;
+};
+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 {
u32 func;
u32 session;
u32 ret;
u32 ret_origin;
+};
+struct tee_version_data {
u32 gen_caps;
+};
+struct tee_driver_ops {
These all need full comments.
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);
int (*shm_register)(struct udevice *dev, struct tee_shm *shm);
int (*shm_unregister)(struct udevice *dev, struct tee_shm *shm);
+};
And these
+struct tee_shm *__tee_shm_add(struct udevice *dev, ulong align, void *addr,
ulong size, u32 flags);
+struct tee_shm *tee_shm_alloc(struct udevice *dev, ulong size, u32 flags);
What are flags? All of this needs documentation please.
+struct tee_shm *tee_shm_register(struct udevice *dev, void *addr,
ulong length, u32 flags);
+void tee_shm_free(struct tee_shm *shm); +bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev);
+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);
+void tee_get_version(struct udevice *dev, struct tee_version_data *vers); +int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg,
ulong num_param, struct tee_param *param);
+int tee_close_session(struct udevice *dev, u32 session);
+int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg,
ulong num_param, struct tee_param *param);
+#endif /*__TEE_H*/
2.17.1
Regards, Simon

Hi Simon,
On Thu, Aug 23, 2018 at 12:45 PM, Simon Glass sjg@chromium.org wrote:
Hi Jens,
On 13 August 2018 at 09:53, 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.
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 | 180 +++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/tee.h | 134 +++++++++++++++++++++++++++++ 8 files changed, 335 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..f0243a0f2e4e --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <dm.h> +#include <tee.h>
+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;
It seems like this can return errors other than -ENOMEM. How about changing this function to return an int?
OK, I'll fix.
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 before return
OK
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;
This will probe each device as it looks through them. Is that what you want?
Yes, in practice there will be only one or perhaps in some special cases two. The capabilities reported by secure world are needed tee.
}
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;
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 */
We need a bit more information about what this is for. It could go in a README or in the uclass header file.
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..c2ac13e34128 --- /dev/null +++ b/include/tee.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#ifndef __TEE_H +#define __TEE_H
+#include <common.h> +#include <dm.h>
These should be included by .c files.
Yes, but what those provides is also needed by this .h file. Aren't all .h files supposed to include what they depend on?
+#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) +#define TEE_SHM_SEC_REGISTER BIT(1) +#define TEE_SHM_ALLOC BIT(2)
+#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.
- */
+#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_driver_ops;
+struct tee_shm {
Comment structure, and below
I've added that in the V2 that I posted as you replied here.
struct udevice *dev;
struct list_head link;
void *addr;
ulong size;
u32 flags;
+};
+struct tee_param_memref {
ulong shm_offs;
ulong size;
struct tee_shm *shm;
+};
+struct tee_param_value {
u64 a;
u64 b;
u64 c;
+};
+struct tee_param {
u64 attr;
union {
struct tee_param_memref memref;
struct tee_param_value value;
} u;
+};
+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 {
u32 func;
u32 session;
u32 ret;
u32 ret_origin;
+};
+struct tee_version_data {
u32 gen_caps;
+};
+struct tee_driver_ops {
These all need full comments.
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);
int (*shm_register)(struct udevice *dev, struct tee_shm *shm);
int (*shm_unregister)(struct udevice *dev, struct tee_shm *shm);
+};
And these
+struct tee_shm *__tee_shm_add(struct udevice *dev, ulong align, void *addr,
ulong size, u32 flags);
+struct tee_shm *tee_shm_alloc(struct udevice *dev, ulong size, u32 flags);
What are flags? All of this needs documentation please.
+struct tee_shm *tee_shm_register(struct udevice *dev, void *addr,
ulong length, u32 flags);
+void tee_shm_free(struct tee_shm *shm); +bool tee_shm_is_registered(struct tee_shm *shm, struct udevice *dev);
+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);
+void tee_get_version(struct udevice *dev, struct tee_version_data *vers); +int tee_open_session(struct udevice *dev, struct tee_open_session_arg *arg,
ulong num_param, struct tee_param *param);
+int tee_close_session(struct udevice *dev, u32 session);
+int tee_invoke_func(struct udevice *dev, struct tee_invoke_arg *arg,
ulong num_param, struct tee_param *param);
+#endif /*__TEE_H*/
2.17.1
Thanks for the review. I posted V2 of this patch set at the same time as you replied, I'll address what's not already done in V2 in the coming V3.
-- Jens

Hi Jens,
On 23 August 2018 at 05:11, Jens Wiklander jens.wiklander@linaro.org wrote:
Hi Simon,
On Thu, Aug 23, 2018 at 12:45 PM, Simon Glass sjg@chromium.org wrote:
Hi Jens,
On 13 August 2018 at 09:53, 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.
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 | 180 +++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/tee.h | 134 +++++++++++++++++++++++++++++ 8 files changed, 335 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..f0243a0f2e4e --- /dev/null +++ b/drivers/tee/tee-uclass.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#include <common.h> +#include <dm.h> +#include <tee.h>
+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;
It seems like this can return errors other than -ENOMEM. How about changing this function to return an int?
OK, I'll fix.
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 before return
OK
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;
This will probe each device as it looks through them. Is that what you want?
Yes, in practice there will be only one or perhaps in some special cases two. The capabilities reported by secure world are needed tee.
}
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;
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 */
We need a bit more information about what this is for. It could go in a README or in the uclass header file.
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..c2ac13e34128 --- /dev/null +++ b/include/tee.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright (c) 2018 Linaro Limited
- */
+#ifndef __TEE_H +#define __TEE_H
+#include <common.h> +#include <dm.h>
These should be included by .c files.
Yes, but what those provides is also needed by this .h file. Aren't all .h files supposed to include what they depend on?
common.h should be included first by any .c file. I suppose it is not essential if it does not use CONFIG options (e.g. just a library file for a 3rd-party library), but that is the general rule. So it should not be in header files.
For dm.h, I have adopted a similar convention. If you like you can add things like 'struct udevice;' I have done that in cases where dm.h is needed. I believe reducing unnecessary includes helps to reduce the amount of code that goes through the preprocessor, but perhaps I am superstitious. Chrome C++ does the same thing.
Thanks for the review. I posted V2 of this patch set at the same time as you replied, I'll address what's not already done in V2 in the coming V3.
Yes I think I lost an email as I thought I had already replied on some of these points, and apparently I had.
Regards, Simon

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
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..a810f3b965de --- /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; +} + +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); + } + } +} + +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; +} + +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; +} + +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; +} + +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; +}

Tested-by: Igor Opaniuk igor.opaniuk@linaro.org
On 13 August 2018 at 18:53, 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
- Probes OP-TEE version using SMCs
- Uses OPTEE message protocol version 2 to communicate with secure world
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..a810f3b965de --- /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;
+}
+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);
}
}
+}
+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;
+}
+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;
+}
+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;
+}
+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.
- Formatting of messages.
- Requests from normal world
- 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;
+}
2.17.1

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 {

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.
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 a810f3b965de..5f308a0c6a96 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; }

Tested-by: Igor Opaniuk igor.opaniuk@linaro.org
On 13 August 2018 at 18:53, 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.
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 a810f3b965de..5f308a0c6a96 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; }
-- 2.17.1

Adds configuration option OPTEE_TA_AVB and a header file describing the interface to the AVB trusted application provided by OP-TEE.
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 c2ac13e34128..3d95d4b3f740 100644 --- a/include/tee.h +++ b/include/tee.h @@ -48,6 +48,13 @@
struct tee_driver_ops;
+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 { struct udevice *dev; struct list_head link; 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*/

Tested-by: Igor Opaniuk igor.opaniuk@linaro.org
On 13 August 2018 at 18:53, 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.
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 c2ac13e34128..3d95d4b3f740 100644 --- a/include/tee.h +++ b/include/tee.h @@ -48,6 +48,13 @@
struct tee_driver_ops;
+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 { struct udevice *dev; struct list_head link; 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*/
2.17.1

Reviewed-by: Igor Opaniuk igor.opaniuk@linaro.org
On 16 August 2018 at 15:22, Igor Opaniuk igor.opaniuk@linaro.org wrote:
Tested-by: Igor Opaniuk igor.opaniuk@linaro.org
On 13 August 2018 at 18:53, 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.
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 c2ac13e34128..3d95d4b3f740 100644 --- a/include/tee.h +++ b/include/tee.h @@ -48,6 +48,13 @@
struct tee_driver_ops;
+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 { struct udevice *dev; struct list_head link; 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*/
2.17.1
-- Regards, Igor Opaniuk

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 ++++++++++++++++++++++++++++++++++++++++++- include/avb_verify.h | 4 ++ 2 files changed, 135 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/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 {

Hi Jens,
Could you please also add info about CONFIG_OPTEE_TA_AVB to the existing doc/README.avb2 and more description of how/where rollback indexes/device lock state are stored (this what you explained in the cover letter for this patchset).
Thanks
On 13 August 2018 at 18:53, Jens Wiklander jens.wiklander@linaro.org wrote:
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 ++++++++++++++++++++++++++++++++++++++++++- include/avb_verify.h | 4 ++ 2 files changed, 135 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/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 {
2.17.1

Hi Jens,
Minor issue: There should be some error message when we try to write ф rollback index, which value is less than existing. For example: poplar# avb write_rb 1 4 poplar# avb read_rb 1 Rollback index: 4 poplar# avb write_rb 1 3 // Should print error message here poplar# avb read_rb 1 Rollback index: 4
Otherwise: Tested-by: Igor Opaniuk igor.opaniuk@linaro.org
On 14 August 2018 at 14:20, Igor Opaniuk igor.opaniuk@linaro.org wrote:
Hi Jens,
Could you please also add info about CONFIG_OPTEE_TA_AVB to the existing doc/README.avb2 and more description of how/where rollback indexes/device lock state are stored (this what you explained in the cover letter for this patchset).
Thanks
On 13 August 2018 at 18:53, Jens Wiklander jens.wiklander@linaro.org wrote:
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 ++++++++++++++++++++++++++++++++++++++++++- include/avb_verify.h | 4 ++ 2 files changed, 135 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/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 {
2.17.1
-- Regards, Igor Opaniuk

Hi Jens,
On 13 August 2018 at 09:53, Jens Wiklander jens.wiklander@linaro.org wrote:
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.
Thanks, Jens
Jens Wiklander (10): dm: fdt: scan for devices under /firmware too cmd: avb read_rb: print rb_idx in hexadecimal 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 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 + cmd/avb.c | 2 +- common/avb_verify.c | 132 +++- .../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 | 18 + 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 | 180 +++++ include/avb_verify.h | 4 + include/dm/uclass-id.h | 1 + include/mmc.h | 2 + include/tee.h | 141 ++++ include/tee/optee_ta_avb.h | 48 ++ 26 files changed, 2816 insertions(+), 7 deletions(-) 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
-- 2.17.1
I missed the Android Verified Boot stuff going in. I did see the v1 patch but not the v2.
Was there discussion of coding style for lib/libavb?
Also, how is this stuff tested in U-Boot? I don't see any tests.
For the uclass, please add a sandbox driver and test even if there are no other tests.
Regards, Simon

Hi Simon,
On Thu, Aug 23, 2018 at 12:45 PM, Simon Glass sjg@chromium.org wrote:
Hi Jens,
On 13 August 2018 at 09:53, Jens Wiklander jens.wiklander@linaro.org wrote:
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.
Thanks, Jens
Jens Wiklander (10): dm: fdt: scan for devices under /firmware too cmd: avb read_rb: print rb_idx in hexadecimal 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 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 + cmd/avb.c | 2 +- common/avb_verify.c | 132 +++- .../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 | 18 + 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 | 180 +++++ include/avb_verify.h | 4 + include/dm/uclass-id.h | 1 + include/mmc.h | 2 + include/tee.h | 141 ++++ include/tee/optee_ta_avb.h | 48 ++ 26 files changed, 2816 insertions(+), 7 deletions(-) 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
-- 2.17.1
I missed the Android Verified Boot stuff going in. I did see the v1 patch but not the v2.
Was there discussion of coding style for lib/libavb?
I don't know. It was Igor who posted that.
Also, how is this stuff tested in U-Boot? I don't see any tests.
This depends on OP-TEE running in secure world. The tests are a bit destructive since we're writing in RPMB and also need to have a specific key programmed.
Before posting the V2 patch set I did some final manual testing:
=> avb init 1 => avb read_rb 0 I/TC: Dynamic shared memory is enabled Rollback index: 0 => avb read_rb 1 Rollback index: 0 => avb read_rb 3 Rollback index: 0 => avb read_rb 4 Rollback index: 0 => avb read_rb 5 Rollback index: 34 => avb write_rb 5 3 Failed to write rollback index => avb write_rb 5 35 => avb write_rb 5 3 Failed to write rollback index => avb read_rb 5 Rollback index: 35
For the uclass, please add a sandbox driver and test even if there are no other tests.
I've added that in the V2 patch set.
Thanks, Jens

Hi Jens,
Also, how is this stuff tested in U-Boot? I don't see any tests.
I might be wrong, but I guess Simon means U-boot pytests in this context.
When initial AVB2.0 patches were introduced, I've also added some avb tests for testing functionality rollback indexes/device unlock state (there were added some simple stubs, so I guess they need to be extended, to test such cases as updating rb index to the lesser value etc.)
Please check test/py/tests/test_avb.py for additional details.
BR, Igor
On 23 August 2018 at 14:23, Jens Wiklander jens.wiklander@linaro.org wrote:
Hi Simon,
On Thu, Aug 23, 2018 at 12:45 PM, Simon Glass sjg@chromium.org wrote:
Hi Jens,
On 13 August 2018 at 09:53, Jens Wiklander jens.wiklander@linaro.org wrote:
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.
Thanks, Jens
Jens Wiklander (10): dm: fdt: scan for devices under /firmware too cmd: avb read_rb: print rb_idx in hexadecimal 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 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 + cmd/avb.c | 2 +- common/avb_verify.c | 132 +++- .../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 | 18 + 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 | 180 +++++ include/avb_verify.h | 4 + include/dm/uclass-id.h | 1 + include/mmc.h | 2 + include/tee.h | 141 ++++ include/tee/optee_ta_avb.h | 48 ++ 26 files changed, 2816 insertions(+), 7 deletions(-) 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
-- 2.17.1
I missed the Android Verified Boot stuff going in. I did see the v1 patch but not the v2.
Was there discussion of coding style for lib/libavb?
I don't know. It was Igor who posted that.
Also, how is this stuff tested in U-Boot? I don't see any tests.
This depends on OP-TEE running in secure world. The tests are a bit destructive since we're writing in RPMB and also need to have a specific key programmed.
Before posting the V2 patch set I did some final manual testing:
=> avb init 1 => avb read_rb 0 I/TC: Dynamic shared memory is enabled Rollback index: 0 => avb read_rb 1 Rollback index: 0 => avb read_rb 3 Rollback index: 0 => avb read_rb 4 Rollback index: 0 => avb read_rb 5 Rollback index: 34 => avb write_rb 5 3 Failed to write rollback index => avb write_rb 5 35 => avb write_rb 5 3 Failed to write rollback index => avb read_rb 5 Rollback index: 35
For the uclass, please add a sandbox driver and test even if there are no other tests.
I've added that in the V2 patch set.
Thanks, Jens

Hi Jens,
On 23 August 2018 at 05:23, Jens Wiklander jens.wiklander@linaro.org wrote:
Hi Simon,
On Thu, Aug 23, 2018 at 12:45 PM, Simon Glass sjg@chromium.org wrote:
Hi Jens,
On 13 August 2018 at 09:53, Jens Wiklander jens.wiklander@linaro.org wrote:
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.
Thanks, Jens
Jens Wiklander (10): dm: fdt: scan for devices under /firmware too cmd: avb read_rb: print rb_idx in hexadecimal 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 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 + cmd/avb.c | 2 +- common/avb_verify.c | 132 +++- .../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 | 18 + 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 | 180 +++++ include/avb_verify.h | 4 + include/dm/uclass-id.h | 1 + include/mmc.h | 2 + include/tee.h | 141 ++++ include/tee/optee_ta_avb.h | 48 ++ 26 files changed, 2816 insertions(+), 7 deletions(-) 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
-- 2.17.1
I missed the Android Verified Boot stuff going in. I did see the v1 patch but not the v2.
Was there discussion of coding style for lib/libavb?
I don't know. It was Igor who posted that.
Also, how is this stuff tested in U-Boot? I don't see any tests.
This depends on OP-TEE running in secure world. The tests are a bit destructive since we're writing in RPMB and also need to have a specific key programmed.
Before posting the V2 patch set I did some final manual testing:
=> avb init 1 => avb read_rb 0 I/TC: Dynamic shared memory is enabled Rollback index: 0 => avb read_rb 1 Rollback index: 0 => avb read_rb 3 Rollback index: 0 => avb read_rb 4 Rollback index: 0 => avb read_rb 5 Rollback index: 34 => avb write_rb 5 3 Failed to write rollback index => avb write_rb 5 35 => avb write_rb 5 3 Failed to write rollback index => avb read_rb 5 Rollback index: 35
OK, I wonder if that can be converted into a simple test in test/py?
Better would be to have a fake sandbox OP-TEE implementation that we can use to write a test of all the code. Does that sound feasible?
For the uclass, please add a sandbox driver and test even if there are no other tests.
I've added that in the V2 patch set.
OK great.
Regards, Simon

On Thu, Aug 23, 2018 at 6:31 PM, Simon Glass sjg@chromium.org wrote:
Hi Jens,
On 23 August 2018 at 05:23, Jens Wiklander jens.wiklander@linaro.org wrote:
Hi Simon,
On Thu, Aug 23, 2018 at 12:45 PM, Simon Glass sjg@chromium.org wrote:
Hi Jens,
On 13 August 2018 at 09:53, Jens Wiklander jens.wiklander@linaro.org wrote:
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.
Thanks, Jens
Jens Wiklander (10): dm: fdt: scan for devices under /firmware too cmd: avb read_rb: print rb_idx in hexadecimal 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 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 + cmd/avb.c | 2 +- common/avb_verify.c | 132 +++- .../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 | 18 + 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 | 180 +++++ include/avb_verify.h | 4 + include/dm/uclass-id.h | 1 + include/mmc.h | 2 + include/tee.h | 141 ++++ include/tee/optee_ta_avb.h | 48 ++ 26 files changed, 2816 insertions(+), 7 deletions(-) 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
-- 2.17.1
I missed the Android Verified Boot stuff going in. I did see the v1 patch but not the v2.
Was there discussion of coding style for lib/libavb?
I don't know. It was Igor who posted that.
Also, how is this stuff tested in U-Boot? I don't see any tests.
This depends on OP-TEE running in secure world. The tests are a bit destructive since we're writing in RPMB and also need to have a specific key programmed.
Before posting the V2 patch set I did some final manual testing:
=> avb init 1 => avb read_rb 0 I/TC: Dynamic shared memory is enabled Rollback index: 0 => avb read_rb 1 Rollback index: 0 => avb read_rb 3 Rollback index: 0 => avb read_rb 4 Rollback index: 0 => avb read_rb 5 Rollback index: 34 => avb write_rb 5 3 Failed to write rollback index => avb write_rb 5 35 => avb write_rb 5 3 Failed to write rollback index => avb read_rb 5 Rollback index: 35
OK, I wonder if that can be converted into a simple test in test/py?
Better would be to have a fake sandbox OP-TEE implementation that we can use to write a test of all the code. Does that sound feasible?
Good idea, shouldn't be too hard.
Thanks, Jens
participants (6)
-
Igor Opaniuk
-
Jens Wiklander
-
Michal Simek
-
Rob Herring
-
Simon Glass
-
Tom Rini