Hello community,
This is the fifth version of OP-TEE mediator in XEN.
OP-TEE 3.5.0 was released when I worked on this version of the pathes. This is the first release where virtualization support is available. This release includes both original virtualization patches and new SMC that retrevies number of threads from OP-TEE.
Many thanks to both OP-TEE and Xen communities for reviewing and helping with my changes to the both projects.
Global changes from v4: - Substantial rework of OP-TEE mediator. Now it tries to return meaningful error codes back to the guest. - OP-TEE mediator does not use struct cpu_user_regs as a storage for parameters and return values when calling OP-TEE. This makes it compatbile with requirement from SMCCC. - tee=native option replaced with tee=optee - Authorship and s-o-b tag reset to my EPAM mail address
Overall changes from v3:
- Patch "arm: add tee_enabled flag to xen_arch_domainconfig" was squashed into "xen/arm: add generic TEE mediator framework" - I implemented more elaborate error repoting to a guest. Now guest will get meaningful error codes instead of generic ARM_SMCCC_ERR_UNKNOWN_FUNCTION.
Per-patch changes are described in corresponding emails.
Changes from v2:
- Use domain flags insted of domctl interface to enable optee for guests - Remove patch "libxc: add xc_dom_tee_enable(...) function" because of previous change - Mediator now stores own context in arch part of struct domain, so I removed patch "optee: add domain contexts"
Per-patch changes are described in corresponding emails.
==== v2:
This is v2 of patch series for OP-TEE mediator support in XEN. Changes from v1:
- Added domctl interface, so now xl decides what domain should work with TEE - Removed XSM support due to change described above - Patch with OP-TEE mediator was splited to 7 separate patches - Removed patch with call_smccc() function. Now this series depend on Julien Grall's series "xen/arm: SMCCC fixup and improvement" [3]
===== v1:
This is follow for patch series [1]. There was lots of discussions for that series and I tried to address all of them in this new patchset.
Currently, I had a working solution for OP-TEE virtualization and it is being upstreamed right now ([2]). So, I think it is a good time to introduce support in XEN as well.
This series include generic TEE mediator framework and full-scale OP-TEE mediator which is working with mentioned chages in OP-TEE. So, multiple domains can work simultaneously with OP-TEE.
I added XSM support, so now it is possible to control which domains can work with TEEs. Also I changed way how TEE discovery is done. Now it is very generic and should support any platform.
[1] https://lists.xenproject.org/archives/html/xen-devel/2017-10/msg01451.html [2] https://github.com/OP-TEE/optee_os/pull/2370 [3] https://lists.xenproject.org/archives/html/xen-devel/2018-08/msg02138.html
Volodymyr Babchuk (10): xen/arm: add generic TEE mediator framework xen/arm: optee: add OP-TEE header files xen/arm: optee: add OP-TEE mediator skeleton xen/arm: optee: add fast calls handling xen/arm: optee: add std call handling xen/arm: optee: add support for RPC SHM buffers xen/arm: optee: add support for arbitrary shared memory xen/arm: optee: add support for RPC commands tools/arm: tee: add "tee" option for xl.cfg tools/arm: optee: create optee firmware node in DT if tee=optee
MAINTAINERS | 6 + docs/man/xl.cfg.5.pod.in | 19 + tools/libxl/libxl.h | 5 + tools/libxl/libxl_arm.c | 42 + tools/libxl/libxl_types.idl | 6 + tools/xl/xl_parse.c | 9 + xen/arch/arm/Kconfig | 9 + xen/arch/arm/Makefile | 1 + xen/arch/arm/domain.c | 19 + xen/arch/arm/setup.c | 2 + xen/arch/arm/tee/Kconfig | 4 + xen/arch/arm/tee/Makefile | 2 + xen/arch/arm/tee/optee.c | 1536 +++++++++++++++++++++++++++ xen/arch/arm/tee/tee.c | 93 ++ xen/arch/arm/vsmc.c | 5 + xen/arch/arm/xen.lds.S | 7 + xen/include/asm-arm/domain.h | 4 + xen/include/asm-arm/tee/optee_msg.h | 444 ++++++++ xen/include/asm-arm/tee/optee_smc.h | 556 ++++++++++ xen/include/asm-arm/tee/tee.h | 109 ++ xen/include/public/arch-arm.h | 4 + 21 files changed, 2882 insertions(+) create mode 100644 xen/arch/arm/tee/Kconfig create mode 100644 xen/arch/arm/tee/Makefile create mode 100644 xen/arch/arm/tee/optee.c create mode 100644 xen/arch/arm/tee/tee.c create mode 100644 xen/include/asm-arm/tee/optee_msg.h create mode 100644 xen/include/asm-arm/tee/optee_smc.h create mode 100644 xen/include/asm-arm/tee/tee.h
This patch adds basic framework for TEE mediators. Guests can't talk to TEE directly, we need some entity that will intercept request and decide what to do with them. "TEE mediator" is a such entity.
This is how it works: user can build XEN with multiple TEE mediators (see the next patches, where OP-TEE mediator is introduced). TEE mediator register self with REGISTER_TEE_MEDIATOR() macro in the same way, as device drivers use DT_DEVICE_START()/DT_DEVICE_END() macros.
At run-time, during initialization, framework calls probe() function for each available mediator driver to find which TEE is installed on the platform. Then generic vSMC handler will call selected mediator when it intercept SMC/HVC that belongs to TEE OS or TEE application.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com
---
Changes from v4: - Added tee_get_type() function, which returns id of currently available TEE - Removed "dom0_tee_enabled" command line option. Dom0 now always uses currently available TEE. - Added TEE type sanity check in arch_sanitise_domain_config() - tee_domain_init() now internally checks if requested TEE type corresponds to available TEE - removed tee_domain_destroy() function because it is not used by anyone
Changes from v3:
- tee_enable() renamed to tee_domain_init() - Added tee_relinquish_resources() function along with changes to domain_relinquish_resources() - Added command-line parameter dom0_tee_enabled, which controls if tee is enabled for Dom0. It is disabled by default - Instead of boolean tee state (enabled/disabled) I introduced enumeration with two values: none or native. It is possible to add other types of tee in the future
Changes from v2: - Removed empty tee/Kconfig file
Changes from v1: - Removed tee_remove() function - CONFIG_TEE depends on EXPERT - tee_domain_created() converted to tee_enable() - tee_init() is called using initcall() mechanism - tee_handle_smc() renamed to tee_handle_call()
Changes from "RFC" version: - renamed CONFIG_ARM_TEE to CONFIG_TEE - changed discovery mechanism: instead of UUID mathing, TEE-specific probing is used --- MAINTAINERS | 6 ++ xen/arch/arm/Kconfig | 7 +++ xen/arch/arm/Makefile | 1 + xen/arch/arm/domain.c | 18 ++++++ xen/arch/arm/setup.c | 2 + xen/arch/arm/tee/Makefile | 1 + xen/arch/arm/tee/tee.c | 93 +++++++++++++++++++++++++++++ xen/arch/arm/vsmc.c | 5 ++ xen/arch/arm/xen.lds.S | 7 +++ xen/include/asm-arm/domain.h | 1 + xen/include/asm-arm/tee/tee.h | 109 ++++++++++++++++++++++++++++++++++ xen/include/public/arch-arm.h | 3 + 12 files changed, 253 insertions(+) create mode 100644 xen/arch/arm/tee/Makefile create mode 100644 xen/arch/arm/tee/tee.c create mode 100644 xen/include/asm-arm/tee/tee.h
diff --git a/MAINTAINERS b/MAINTAINERS index a208bbe304..17906b8321 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -385,6 +385,12 @@ F: config/Stubdom.mk.in F: m4/stubdom.m4 F: stubdom/
+TEE MEDIATORS +M: Volodymyr Babchuk volodymyr_babchuk@epam.com +S: Supported +F: xen/arch/arm/tee/ +F: xen/include/asm-arm/tee + TOOLSTACK M: Ian Jackson ian.jackson@eu.citrix.com M: Wei Liu wei.liu2@citrix.com diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index 581de67b6b..e527b2f885 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -105,6 +105,13 @@ config HARDEN_BRANCH_PREDICTOR
If unsure, say Y.
+config TEE + bool "Enable TEE mediators support" if EXPERT = "y" + default n + help + This option enables generic TEE mediators support. It allows guests + to access real TEE via one of TEE mediators implemented in XEN. + endmenu
menu "ARM errata workaround via the alternative framework" diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index cb902cb6fe..5c2aa34557 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -5,6 +5,7 @@ subdir-$(CONFIG_ACPI) += acpi ifneq ($(CONFIG_NO_PLAT),y) subdir-y += platforms endif +subdir-$(CONFIG_TEE) += tee
obj-$(CONFIG_HAS_ALTERNATIVE) += alternative.o obj-y += bootfdt.init.o diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c index 6dc633ed50..0c8e50f48f 100644 --- a/xen/arch/arm/domain.c +++ b/xen/arch/arm/domain.c @@ -32,6 +32,7 @@ #include <asm/platform.h> #include <asm/procinfo.h> #include <asm/regs.h> +#include <asm/tee/tee.h> #include <asm/vfp.h> #include <asm/vgic.h> #include <asm/vtimer.h> @@ -648,6 +649,12 @@ int arch_sanitise_domain_config(struct xen_domctl_createdomain *config) return -EINVAL; }
+ if ( config->arch.tee_type != XEN_DOMCTL_CONFIG_TEE_NONE ) + { + dprintk(XENLOG_INFO, "Unsupported TEE type\n"); + return -EINVAL; + } + return 0; }
@@ -705,6 +712,9 @@ int arch_domain_create(struct domain *d, if ( (rc = domain_vtimer_init(d, &config->arch)) != 0 ) goto fail;
+ if ( (rc = tee_domain_init(d, config->arch.tee_type)) != 0 ) + goto fail; + update_domain_wallclock_time(d);
/* @@ -949,6 +959,14 @@ int domain_relinquish_resources(struct domain *d) */ domain_vpl011_deinit(d);
+ d->arch.relmem = RELMEM_tee; + /* Fallthrough */ + + case RELMEM_tee: + ret = tee_relinquish_resources(d); + if (ret ) + return ret; + d->arch.relmem = RELMEM_xen; /* Fallthrough */
diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index ccb0f181ea..1a240d208b 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -49,6 +49,7 @@ #include <asm/platform.h> #include <asm/procinfo.h> #include <asm/setup.h> +#include <asm/tee/tee.h> #include <xsm/xsm.h> #include <asm/acpi.h>
@@ -895,6 +896,7 @@ void __init start_xen(unsigned long boot_phys_offset, dom0_cfg.arch.nr_spis = min(gic_number_lines(), (unsigned int) 992) - 32; if ( gic_number_lines() > 992 ) printk(XENLOG_WARNING "Maximum number of vGIC IRQs exceeded.\n"); + dom0_cfg.arch.tee_type = tee_get_type(); dom0_cfg.max_vcpus = dom0_max_vcpus();
dom0 = domain_create(0, &dom0_cfg, true); diff --git a/xen/arch/arm/tee/Makefile b/xen/arch/arm/tee/Makefile new file mode 100644 index 0000000000..c54d4796ff --- /dev/null +++ b/xen/arch/arm/tee/Makefile @@ -0,0 +1 @@ +obj-y += tee.o diff --git a/xen/arch/arm/tee/tee.c b/xen/arch/arm/tee/tee.c new file mode 100644 index 0000000000..6bda846953 --- /dev/null +++ b/xen/arch/arm/tee/tee.c @@ -0,0 +1,93 @@ +/* + * xen/arch/arm/tee/tee.c + * + * Generic part of TEE mediator subsystem + * + * Volodymyr Babchuk volodymyr_babchuk@epam.com + * Copyright (c) 2018-2019 EPAM Systems. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <xen/errno.h> +#include <xen/init.h> +#include <xen/types.h> + +#include <asm/tee/tee.h> + +extern const struct tee_mediator_desc _steemediator[], _eteemediator[]; +static const struct tee_mediator_desc *cur_mediator; + +bool tee_handle_call(struct cpu_user_regs *regs) +{ + if ( unlikely(!cur_mediator) ) + return false; + + return cur_mediator->ops->handle_call(regs); +} + +int tee_domain_init(struct domain *d, uint16_t tee_type) +{ + if ( tee_type == XEN_DOMCTL_CONFIG_TEE_NONE) + return 0; + + if ( !cur_mediator ) + return -ENODEV; + + if ( cur_mediator->tee_type != tee_type ) + return -EINVAL; + + return cur_mediator->ops->domain_init(d); +} + +int tee_relinquish_resources(struct domain *d) +{ + if ( !cur_mediator ) + return 0; + + return cur_mediator->ops->relinquish_resources(d); +} + +uint16_t tee_get_type(void) +{ + if ( !cur_mediator ) + return XEN_DOMCTL_CONFIG_TEE_NONE; + + return cur_mediator->tee_type; +} + + +static int __init tee_init(void) +{ + const struct tee_mediator_desc *desc; + + for ( desc = _steemediator; desc != _eteemediator; desc++ ) + { + if ( desc->ops->probe() ) + { + printk(XENLOG_INFO "Using TEE mediator for %s\n", desc->name); + cur_mediator = desc; + return 0; + } + } + + return 0; +} + +__initcall(tee_init); + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/arm/vsmc.c b/xen/arch/arm/vsmc.c index c72b9a04ff..f8e350311d 100644 --- a/xen/arch/arm/vsmc.c +++ b/xen/arch/arm/vsmc.c @@ -23,6 +23,7 @@ #include <asm/monitor.h> #include <asm/regs.h> #include <asm/smccc.h> +#include <asm/tee/tee.h> #include <asm/traps.h> #include <asm/vpsci.h> #include <asm/platform.h> @@ -276,6 +277,10 @@ static bool vsmccc_handle_call(struct cpu_user_regs *regs) case ARM_SMCCC_OWNER_SIP: handled = platform_smc(regs); break; + case ARM_SMCCC_OWNER_TRUSTED_APP ... ARM_SMCCC_OWNER_TRUSTED_APP_END: + case ARM_SMCCC_OWNER_TRUSTED_OS ... ARM_SMCCC_OWNER_TRUSTED_OS_END: + handled = tee_handle_call(regs); + break; } }
diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S index 1e72906477..e664c4441a 100644 --- a/xen/arch/arm/xen.lds.S +++ b/xen/arch/arm/xen.lds.S @@ -137,6 +137,13 @@ SECTIONS _aedevice = .; } :text
+ . = ALIGN(8); + .teemediator.info : { + _steemediator = .; + *(.teemediator.info) + _eteemediator = .; + } :text + . = ALIGN(PAGE_SIZE); /* Init code and data */ __init_begin = .; .init.text : { diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h index 312fec8932..0f15372098 100644 --- a/xen/include/asm-arm/domain.h +++ b/xen/include/asm-arm/domain.h @@ -58,6 +58,7 @@ struct arch_domain /* Continuable domain_relinquish_resources(). */ enum { RELMEM_not_started, + RELMEM_tee, RELMEM_xen, RELMEM_page, RELMEM_mapping, diff --git a/xen/include/asm-arm/tee/tee.h b/xen/include/asm-arm/tee/tee.h new file mode 100644 index 0000000000..6eeefd4440 --- /dev/null +++ b/xen/include/asm-arm/tee/tee.h @@ -0,0 +1,109 @@ +/* + * xen/include/asm-arm/tee/tee.h + * + * Generic part of TEE mediator subsystem + * + * Volodymyr Babchuk volodymyr_babchuk@epam.com + * Copyright (c) 2018 EPAM Systems. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ARCH_ARM_TEE_TEE_H__ +#define __ARCH_ARM_TEE_TEE_H__ + +#include <xen/lib.h> +#include <xen/types.h> + +#include <asm/regs.h> + +#ifdef CONFIG_TEE + +struct tee_mediator_ops { + /* + * Probe for TEE. Should return true if TEE found and + * mediator is initialized. + */ + bool (*probe)(void); + + /* + * Called during domain construction if toolstack requests to enable + * TEE support so mediator can inform TEE about new + * guest and create own structures for the new domain. + */ + int (*domain_init)(struct domain *d); + + /* + * Called during domain destruction to relinquish resources used + * by mediator itself. This function can return -ERESTART to indicate + * that it does not finished work and should be called again. + */ + int (*relinquish_resources)(struct domain *d); + + /* Handle SMCCC call for current domain. */ + bool (*handle_call)(struct cpu_user_regs *regs); +}; + +struct tee_mediator_desc { + /* Printable name of the TEE. */ + const char *name; + + /* Mediator callbacks as described above. */ + const struct tee_mediator_ops *ops; + + /* + * ID of TEE. Corresponds to xen_arch_domainconfig.tee_type. + * Should be one of XEN_DOMCTL_CONFIG_TEE_xxx + */ + uint16_t tee_type; +}; + +bool tee_handle_call(struct cpu_user_regs *regs); +int tee_domain_init(struct domain *d, uint16_t tee_type); +int tee_relinquish_resources(struct domain *d); +uint16_t tee_get_type(void); + +#define REGISTER_TEE_MEDIATOR(_name, _namestr, _type, _ops) \ +static const struct tee_mediator_desc __tee_desc_##_name __used \ +__section(".teemediator.info") = { \ + .name = _namestr, \ + .ops = _ops, \ + .tee_type = _type \ +} + +#else + +static inline bool tee_handle_call(struct cpu_user_regs *regs) +{ + return false; +} + +static inline int tee_domain_init(struct domain *d, uint16_t tee_type) +{ + return -ENODEV; +} + +static inline int tee_relinquish_resources(struct domain *d) +{ + return 0; +} + +static inline uint16_t tee_get_type(void) +{ + return XEN_DOMCTL_CONFIG_TEE_NONE; +} + +#endif /* CONFIG_TEE */ + +#endif /* __ARCH_ARM_TEE_TEE_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h index eb424e8286..5e938a91cc 100644 --- a/xen/include/public/arch-arm.h +++ b/xen/include/public/arch-arm.h @@ -304,10 +304,13 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); #define XEN_DOMCTL_CONFIG_GIC_NATIVE 0 #define XEN_DOMCTL_CONFIG_GIC_V2 1 #define XEN_DOMCTL_CONFIG_GIC_V3 2 +#define XEN_DOMCTL_CONFIG_TEE_NONE 0 struct xen_arch_domainconfig { /* IN/OUT */ uint8_t gic_version; /* IN */ + uint16_t tee_type; + /* IN */ uint32_t nr_spis; /* * OUT
On 21.05.19 at 23:25, Volodymyr_Babchuk@epam.com wrote:
MAINTAINERS | 6 ++ xen/arch/arm/Kconfig | 7 +++ xen/arch/arm/Makefile | 1 + xen/arch/arm/domain.c | 18 ++++++ xen/arch/arm/setup.c | 2 + xen/arch/arm/tee/Makefile | 1 + xen/arch/arm/tee/tee.c | 93 +++++++++++++++++++++++++++++ xen/arch/arm/vsmc.c | 5 ++ xen/arch/arm/xen.lds.S | 7 +++ xen/include/asm-arm/domain.h | 1 + xen/include/asm-arm/tee/tee.h | 109 ++++++++++++++++++++++++++++++++++ xen/include/public/arch-arm.h | 3 + 12 files changed, 253 insertions(+) create mode 100644 xen/arch/arm/tee/Makefile create mode 100644 xen/arch/arm/tee/tee.c create mode 100644 xen/include/asm-arm/tee/tee.h
I don't think I'm asking this for the first time: Why am I being Cc-ed here? It's all Arm code that gets changed, and the MAINTAINERS addition alone imo doesn't warrant widening the Cc list.
Jan
On 22/05/2019 09:45, Jan Beulich wrote:
On 21.05.19 at 23:25, Volodymyr_Babchuk@epam.com wrote:
MAINTAINERS | 6 ++ xen/arch/arm/Kconfig | 7 +++ xen/arch/arm/Makefile | 1 + xen/arch/arm/domain.c | 18 ++++++ xen/arch/arm/setup.c | 2 + xen/arch/arm/tee/Makefile | 1 + xen/arch/arm/tee/tee.c | 93 +++++++++++++++++++++++++++++ xen/arch/arm/vsmc.c | 5 ++ xen/arch/arm/xen.lds.S | 7 +++ xen/include/asm-arm/domain.h | 1 + xen/include/asm-arm/tee/tee.h | 109 ++++++++++++++++++++++++++++++++++ xen/include/public/arch-arm.h | 3 + 12 files changed, 253 insertions(+) create mode 100644 xen/arch/arm/tee/Makefile create mode 100644 xen/arch/arm/tee/tee.c create mode 100644 xen/include/asm-arm/tee/tee.h
I don't think I'm asking this for the first time: Why am I being Cc-ed here? It's all Arm code that gets changed, and the MAINTAINERS addition alone imo doesn't warrant widening the Cc list.
A lot of users uses the scripts/{add, get}_maintainers.pl to get the list of maintainers to CCed. Both of them output "THE REST" because of the file MAINTAINERS is modified.
I don't think it is sensible to expect users to know when to strip the list...
Cheers,
On 22.05.19 at 11:27, julien.grall@arm.com wrote:
On 22/05/2019 09:45, Jan Beulich wrote:
On 21.05.19 at 23:25, Volodymyr_Babchuk@epam.com wrote:
MAINTAINERS | 6 ++ xen/arch/arm/Kconfig | 7 +++ xen/arch/arm/Makefile | 1 + xen/arch/arm/domain.c | 18 ++++++ xen/arch/arm/setup.c | 2 + xen/arch/arm/tee/Makefile | 1 + xen/arch/arm/tee/tee.c | 93 +++++++++++++++++++++++++++++ xen/arch/arm/vsmc.c | 5 ++ xen/arch/arm/xen.lds.S | 7 +++ xen/include/asm-arm/domain.h | 1 + xen/include/asm-arm/tee/tee.h | 109 ++++++++++++++++++++++++++++++++++ xen/include/public/arch-arm.h | 3 + 12 files changed, 253 insertions(+) create mode 100644 xen/arch/arm/tee/Makefile create mode 100644 xen/arch/arm/tee/tee.c create mode 100644 xen/include/asm-arm/tee/tee.h
I don't think I'm asking this for the first time: Why am I being Cc-ed here? It's all Arm code that gets changed, and the MAINTAINERS addition alone imo doesn't warrant widening the Cc list.
A lot of users uses the scripts/{add, get}_maintainers.pl to get the list of
maintainers to CCed. Both of them output "THE REST" because of the file MAINTAINERS is modified.
I don't think it is sensible to expect users to know when to strip the list...
Hmm, well, I see your point, but I think applying some common sense still can be expected. It's also not sensible for unrelated people to get Cc-ed. I think anyone knowing enough to modify MAINTAINERS can be expected to know whom to Cc.
Jan
Hi Jan,
On 22/05/2019 11:02, Jan Beulich wrote:
On 22.05.19 at 11:27, julien.grall@arm.com wrote:
On 22/05/2019 09:45, Jan Beulich wrote:
On 21.05.19 at 23:25, Volodymyr_Babchuk@epam.com wrote:
MAINTAINERS | 6 ++ xen/arch/arm/Kconfig | 7 +++ xen/arch/arm/Makefile | 1 + xen/arch/arm/domain.c | 18 ++++++ xen/arch/arm/setup.c | 2 + xen/arch/arm/tee/Makefile | 1 + xen/arch/arm/tee/tee.c | 93 +++++++++++++++++++++++++++++ xen/arch/arm/vsmc.c | 5 ++ xen/arch/arm/xen.lds.S | 7 +++ xen/include/asm-arm/domain.h | 1 + xen/include/asm-arm/tee/tee.h | 109 ++++++++++++++++++++++++++++++++++ xen/include/public/arch-arm.h | 3 + 12 files changed, 253 insertions(+) create mode 100644 xen/arch/arm/tee/Makefile create mode 100644 xen/arch/arm/tee/tee.c create mode 100644 xen/include/asm-arm/tee/tee.h
I don't think I'm asking this for the first time: Why am I being Cc-ed here? It's all Arm code that gets changed, and the MAINTAINERS addition alone imo doesn't warrant widening the Cc list.
A lot of users uses the scripts/{add, get}_maintainers.pl to get the list of
maintainers to CCed. Both of them output "THE REST" because of the file MAINTAINERS is modified.
I don't think it is sensible to expect users to know when to strip the list...
Hmm, well, I see your point, but I think applying some common sense still can be expected. It's also not sensible for unrelated people to get Cc-ed. I think anyone knowing enough to modify MAINTAINERS can be expected to know whom to Cc.
I disagree here, someone in "THE REST" may have an opinion in adding Volodymyr as a maintainer (this is only example).
This is a bit similar to when you send a patch to add a 3 lines timer helper in a common header but it is only used by x86. Even if I will not necessary answer on the patch because it does not impact Arm directly, I will still have a quick look to see if it makes sense.
Anyway, you can't expect the contributor to guess your will on MAINTAINERS. You should update the documentation/script if this is the expectation you have.
Cheers,
Hi Volodymyr,
On 21/05/2019 22:25, Volodymyr Babchuk wrote:
diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index ccb0f181ea..1a240d208b 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -49,6 +49,7 @@ #include <asm/platform.h> #include <asm/procinfo.h> #include <asm/setup.h> +#include <asm/tee/tee.h> #include <xsm/xsm.h> #include <asm/acpi.h> @@ -895,6 +896,7 @@ void __init start_xen(unsigned long boot_phys_offset, dom0_cfg.arch.nr_spis = min(gic_number_lines(), (unsigned int) 992) - 32; if ( gic_number_lines() > 992 ) printk(XENLOG_WARNING "Maximum number of vGIC IRQs exceeded.\n");
- dom0_cfg.arch.tee_type = tee_get_type();
I was expecting some code to generate/dropped the OP-TEE node in Dom0 DTB. For instance, we want to promote the use of "hvc" and not "smc".
dom0_cfg.max_vcpus = dom0_max_vcpus();
dom0 = domain_create(0, &dom0_cfg, true); diff --git a/xen/arch/arm/tee/Makefile b/xen/arch/arm/tee/Makefile new file mode 100644 index 0000000000..c54d4796ff --- /dev/null +++ b/xen/arch/arm/tee/Makefile @@ -0,0 +1 @@ +obj-y += tee.o diff --git a/xen/arch/arm/tee/tee.c b/xen/arch/arm/tee/tee.c new file mode 100644 index 0000000000..6bda846953 --- /dev/null +++ b/xen/arch/arm/tee/tee.c @@ -0,0 +1,93 @@ +/*
- xen/arch/arm/tee/tee.c
- Generic part of TEE mediator subsystem
- Volodymyr Babchuk volodymyr_babchuk@epam.com
- Copyright (c) 2018-2019 EPAM Systems.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- */
+#include <xen/errno.h> +#include <xen/init.h> +#include <xen/types.h>
+#include <asm/tee/tee.h>
+extern const struct tee_mediator_desc _steemediator[], _eteemediator[]; +static const struct tee_mediator_desc *cur_mediator;
NIT: You probably want a __read_mostly here.
Both changes can be done in follow-up:
Reviewed-by: Julien Grall julien.grall@arm.com
Cheers,
On 21/05/2019 22:25, Volodymyr Babchuk wrote:
diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h index eb424e8286..5e938a91cc 100644 --- a/xen/include/public/arch-arm.h +++ b/xen/include/public/arch-arm.h @@ -304,10 +304,13 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); #define XEN_DOMCTL_CONFIG_GIC_NATIVE 0 #define XEN_DOMCTL_CONFIG_GIC_V2 1 #define XEN_DOMCTL_CONFIG_GIC_V3 2
I forgot to mention the newline here. This would help to differentiate the two set of define.
I can add this one on commit.
Cheers,
Hi Volodymyr,
On 5/21/19 10:25 PM, Volodymyr Babchuk wrote:
+static inline bool tee_handle_call(struct cpu_user_regs *regs) +{
- return false;
+}
+static inline int tee_domain_init(struct domain *d, uint16_t tee_type) +{
- return -ENODEV;
+}
I had a report that Xen fails to boot with this series and !CONFIG_TEE. This is because you return an error here in all the case some domain creation will always fail.
Instead this should check that tee_type is always NONE or else return an error.
Also, please at least check that your series does not break boot when CONFIG_TEE is not selected. It would also be ideal (but not mandatory) if you can check that it does not break on non-OPTEE platform when !CONFIG_TEE is selected.
Cheers,
On 06/06/2019 17:02, Julien Grall wrote:
Hi Volodymyr,
On 5/21/19 10:25 PM, Volodymyr Babchuk wrote:
+static inline bool tee_handle_call(struct cpu_user_regs *regs) +{ +Â Â Â return false; +}
+static inline int tee_domain_init(struct domain *d, uint16_t tee_type) +{ +Â Â Â return -ENODEV; +}
I had a report that Xen fails to boot with this series and !CONFIG_TEE. This is because you return an error here in all the case some domain creation will always fail.
Instead this should check that tee_type is always NONE or else return an error.
Also, please at least check that your series does not break boot when CONFIG_TEE is not selected. It would also be ideal (but not mandatory) if you can check that it does not break on non-OPTEE platform when !CONFIG_TEE is selected.
I just realized this paragraph may not be clear. What I meant is we need to at least test there are no regression when booting when with CONFIG_TEE=n.
For CONFIG_TEE=y, it would be good to test that it still boots on platform not providing OP-TEE. This is not critical because the config cannot be selected without CONFIG_XEN_EXPERT=y.
Cheers,
Cheers,
Hi Julien,
Julien Grall writes:
On 06/06/2019 17:02, Julien Grall wrote:
Hi Volodymyr,
On 5/21/19 10:25 PM, Volodymyr Babchuk wrote:
+static inline bool tee_handle_call(struct cpu_user_regs *regs) +{
- return false;
+}
+static inline int tee_domain_init(struct domain *d, uint16_t tee_type) +{
- return -ENODEV;
+}
I had a report that Xen fails to boot with this series and !CONFIG_TEE. This is because you return an error here in all the case some domain creation will always fail.
Thanks for reporting. I forgot to test that case :(
Instead this should check that tee_type is always NONE or else return an error.
Also, please at least check that your series does not break boot when CONFIG_TEE is not selected. It would also be ideal (but not mandatory) if you can check that it does not break on non-OPTEE platform when !CONFIG_TEE is selected.
I just realized this paragraph may not be clear. What I meant is we need to at least test there are no regression when booting when with CONFIG_TEE=n.
For CONFIG_TEE=y, it would be good to test that it still boots on platform not providing OP-TEE. This is not critical because the config cannot be selected without CONFIG_XEN_EXPERT=y.
I fixed CONFIG_TEE=n issue in the new version, which I'm going to send later today.
Also I made optee_probe() (with CONFIG_OPTEE=y of course) to return false to emulated platform without OP-TEE. System boots and works as usual.
Also I addressed your other comments for this patch.
-- Best regards,Volodymyr Babchuk
On 11/06/2019 19:18, Volodymyr Babchuk wrote:
Hi Julien,
Hi,
Julien Grall writes:
On 06/06/2019 17:02, Julien Grall wrote:
Hi Volodymyr,
On 5/21/19 10:25 PM, Volodymyr Babchuk wrote:
+static inline bool tee_handle_call(struct cpu_user_regs *regs) +{
- return false;
+}
+static inline int tee_domain_init(struct domain *d, uint16_t tee_type) +{
- return -ENODEV;
+}
I had a report that Xen fails to boot with this series and !CONFIG_TEE. This is because you return an error here in all the case some domain creation will always fail.
Thanks for reporting. I forgot to test that case :(
Instead this should check that tee_type is always NONE or else return an error.
Also, please at least check that your series does not break boot when CONFIG_TEE is not selected. It would also be ideal (but not mandatory) if you can check that it does not break on non-OPTEE platform when !CONFIG_TEE is selected.
I just realized this paragraph may not be clear. What I meant is we need to at least test there are no regression when booting when with CONFIG_TEE=n.
For CONFIG_TEE=y, it would be good to test that it still boots on platform not providing OP-TEE. This is not critical because the config cannot be selected without CONFIG_XEN_EXPERT=y.
I fixed CONFIG_TEE=n issue in the new version, which I'm going to send later today.
Also I made optee_probe() (with CONFIG_OPTEE=y of course) to return false to emulated platform without OP-TEE. System boots and works as usual.
Also I addressed your other comments for this patch.
Thank you! I will have a look at the next version.
Cheers,
This header files describes protocol between OP-TEE and OP-TEE client driver in Linux. They are needed for upcoming OP-TEE mediator, which is added in the next patch. Reason to add those headers in separate patch is to ease up review. Those files were taken from linux tree (drivers/tee/optee/) and mangled a bit to compile with XEN.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com
---
Changes from v4: - Updated to latest OP-TEE version because of adding OPTEE_SMC_GET_THREAD_COUNT call which will be released with OP-TEE 3.5.0
Changes from v3: - Updated to latest OP-TEE version because virtualization support to OP-TEE was merged into mainline. --- xen/include/asm-arm/tee/optee_msg.h | 444 ++++++++++++++++++++++ xen/include/asm-arm/tee/optee_smc.h | 556 ++++++++++++++++++++++++++++ 2 files changed, 1000 insertions(+) create mode 100644 xen/include/asm-arm/tee/optee_msg.h create mode 100644 xen/include/asm-arm/tee/optee_smc.h
diff --git a/xen/include/asm-arm/tee/optee_msg.h b/xen/include/asm-arm/tee/optee_msg.h new file mode 100644 index 0000000000..10747b2aa8 --- /dev/null +++ b/xen/include/asm-arm/tee/optee_msg.h @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2015-2016, Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _OPTEE_MSG_H +#define _OPTEE_MSG_H + +#include <xen/bitops.h> +#include <xen/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/xen/include/asm-arm/tee/optee_smc.h b/xen/include/asm-arm/tee/optee_smc.h new file mode 100644 index 0000000000..6825747a92 --- /dev/null +++ b/xen/include/asm-arm/tee/optee_smc.h @@ -0,0 +1,556 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2015, Linaro Limited + */ +#ifndef OPTEE_SMC_H +#define OPTEE_SMC_H + +#include <asm/smccc.h> +#include <xen/bitops.h> + +#define OPTEE_SMC_STD_CALL_VAL(func_num) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_CONV_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_CONV_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(ARM_SMCCC_FAST_CALL, ARM_SMCCC_CONV_32, \ + ARM_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 the following UID if using API specified in this file + * without further extensions: + * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b. + * see also OPTEE_MSG_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_CONV_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 extensions. + * 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_CONV_32, \ + ARM_SMCCC_OWNER_TRUSTED_OS_END, \ + OPTEE_SMC_FUNCID_CALLS_REVISION) + +/* + * 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) + +/* + * Call with struct optee_msg_arg as argument + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG + * a1 Upper 32 bits of a 64-bit physical pointer to a struct optee_msg_arg + * a2 Lower 32 bits of a 64-bit 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 physical 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) + +/* + * Configures L2CC mutex + * + * Disables, enables usage of L2CC mutex. Returns or sets physical address + * of L2CC mutex. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_L2CC_MUTEX + * a1 OPTEE_SMC_L2CC_MUTEX_GET_ADDR Get physical address of mutex + * OPTEE_SMC_L2CC_MUTEX_SET_ADDR Set physical address of mutex + * OPTEE_SMC_L2CC_MUTEX_ENABLE Enable usage of mutex + * OPTEE_SMC_L2CC_MUTEX_DISABLE Disable usage of mutex + * a2 if a1 == OPTEE_SMC_L2CC_MUTEX_SET_ADDR, upper 32bit of a 64bit + * physical address of mutex + * a3 if a1 == OPTEE_SMC_L2CC_MUTEX_SET_ADDR, lower 32bit of a 64bit + * physical address of mutex + * a3-6 Not used + * a7 Hypervisor Client ID register + * + * Have config return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 Preserved + * a2 if a1 == OPTEE_SMC_L2CC_MUTEX_GET_ADDR, upper 32bit of a 64bit + * physical address of mutex + * a3 if a1 == OPTEE_SMC_L2CC_MUTEX_GET_ADDR, lower 32bit of a 64bit + * physical address of mutex + * a3-7 Preserved + * + * Error return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL Physical address not available + * OPTEE_SMC_RETURN_EBADADDR Bad supplied physical address + * OPTEE_SMC_RETURN_EBADCMD Unsupported value in a1 + * a1-7 Preserved + */ +#define OPTEE_SMC_L2CC_MUTEX_GET_ADDR 0 +#define OPTEE_SMC_L2CC_MUTEX_SET_ADDR 1 +#define OPTEE_SMC_L2CC_MUTEX_ENABLE 2 +#define OPTEE_SMC_L2CC_MUTEX_DISABLE 3 +#define OPTEE_SMC_FUNCID_L2CC_MUTEX 8 +#define OPTEE_SMC_L2CC_MUTEX \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_L2CC_MUTEX) + +/* + * 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 (1 << 0) +/* Secure world has reserved shared memory for normal world to use */ +#define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM (1 << 0) +/* Secure world can communicate via previously unregistered shared memory */ +#define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM (1 << 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 (1 << 2) + +#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 +#define OPTEE_SMC_EXCHANGE_CAPABILITIES \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES) + +/* + * 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 32 bits of a 64-bit Shared memory cookie + * a2 Lower 32 bits of a 64-bit 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) + +/* + * 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) + +/* + * Release of secondary cores + * + * OP-TEE in secure world is in charge of the release process of secondary + * cores. The Rich OS issue the this request to ask OP-TEE to boot up the + * secondary cores, go through the OP-TEE per-core initialization, and then + * switch to the Non-seCure world with the Rich OS provided entry address. + * The secondary cores enter Non-Secure world in SVC mode, with Thumb, FIQ, + * IRQ and Abort bits disabled. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_BOOT_SECONDARY + * a1 Index of secondary core to boot + * a2 Upper 32 bits of a 64-bit Non-Secure world entry physical address + * a3 Lower 32 bits of a 64-bit Non-Secure world entry physical address + * a4-7 Not used + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1-7 Preserved + * + * Error return: + * a0 OPTEE_SMC_RETURN_EBADCMD Core index out of range + * a1-7 Preserved + * + * Not idle return register usage: + * a0 OPTEE_SMC_RETURN_EBUSY + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_BOOT_SECONDARY 12 +#define OPTEE_SMC_BOOT_SECONDARY \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_BOOT_SECONDARY) + +/* + * Inform OP-TEE about a new virtual machine + * + * Hypervisor issues this call during virtual machine (guest) creation. + * OP-TEE records client id of new virtual machine and prepares + * to receive requests from it. This call is available only if OP-TEE + * was built with virtualization support. + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_VM_CREATED + * a1 Hypervisor Client ID of newly created virtual machine + * a2-6 Not used + * a7 Hypervisor Client ID register. Must be 0, because only hypervisor + * can issue this call + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1-7 Preserved + * + * Error return: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL OP-TEE have no resources for + * another VM + * a1-7 Preserved + * + */ +#define OPTEE_SMC_FUNCID_VM_CREATED 13 +#define OPTEE_SMC_VM_CREATED \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_VM_CREATED) + +/* + * Inform OP-TEE about shutdown of a virtual machine + * + * Hypervisor issues this call during virtual machine (guest) destruction. + * OP-TEE will clean up all resources associated with this VM. This call is + * available only if OP-TEE was built with virtualization support. + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_VM_DESTROYED + * a1 Hypervisor Client ID of virtual machine being shut down + * a2-6 Not used + * a7 Hypervisor Client ID register. Must be 0, because only hypervisor + * can issue this call + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1-7 Preserved + * + */ +#define OPTEE_SMC_FUNCID_VM_DESTROYED 14 +#define OPTEE_SMC_VM_DESTROYED \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_VM_DESTROYED) + +/* + * Query OP-TEE about number of supported threads + * + * Normal World OS or Hypervisor issues this call to find out how many + * threads OP-TEE supports. That is how many standard calls can be issued + * in parallel before OP-TEE will return OPTEE_SMC_RETURN_ETHREAD_LIMIT. + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_GET_THREAD_COUNT + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 Number of threads + * a2-7 Preserved + * + * Error return: + * a0 OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Requested call is not implemented + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_GET_THREAD_COUNT 15 +#define OPTEE_SMC_GET_THREAD_COUNT \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_THREAD_COUNT) + +/* + * 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 32 bits of 64-bit physical pointer to allocated + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't + * be allocated. + * a2 Lower 32 bits of 64-bit physical pointer to allocated + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't + * be allocated + * a3 Preserved + * a4 Upper 32 bits of 64-bit Shared memory cookie used when freeing + * the memory or doing an RPC + * a5 Lower 32 bits of 64-bit 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 32 bits of 64-bit shared memory cookie belonging to this + * argument memory + * a2 Lower 32 bits of 64-bit 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 a foreign interrupt in 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 32 bits of a 64-bit Shared memory cookie holding a + * struct optee_msg_arg, must be preserved, only the data should + * be updated + * a2 Lower 32 bits of a 64-bit 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) \ + (((ret) != OPTEE_SMC_RETURN_UNKNOWN_FUNCTION) && \ + ((((ret) & OPTEE_SMC_RETURN_RPC_PREFIX_MASK) == \ + OPTEE_SMC_RETURN_RPC_PREFIX))) + +#endif /* OPTEE_SMC_H */
Hi,
On 21/05/2019 22:25, Volodymyr Babchuk wrote:
This header files describes protocol between OP-TEE and OP-TEE client driver in Linux. They are needed for upcoming OP-TEE mediator, which is added in the next patch. Reason to add those headers in separate patch is to ease up review. Those files were taken from linux tree (drivers/tee/optee/) and mangled a bit to compile with XEN.
Can you mention the version of the Linux tree you use? This would help to track change in the future.
With that:
Acked-by: Julien Grall julien.grall@arm.com
Cheers,
Hi Julien,
Julien Grall writes:
Hi,
On 21/05/2019 22:25, Volodymyr Babchuk wrote:
This header files describes protocol between OP-TEE and OP-TEE client driver in Linux. They are needed for upcoming OP-TEE mediator, which is added in the next patch. Reason to add those headers in separate patch is to ease up review. Those files were taken from linux tree (drivers/tee/optee/) and mangled a bit to compile with XEN.
Can you mention the version of the Linux tree you use? This would help to track change in the future.
Actually this commit description is not valid anymore. After I added calls to inform OP-TEE about guest creation/destruction, those files does not correspond to any Linux version anymore.
So, I'll take this files from optee_os repository instead and I'll update the commit message accordingly. Can I keep your acked-by tag in such case?
-- Best regards,Volodymyr Babchuk
On 6/10/19 7:20 PM, Volodymyr Babchuk wrote:
Hi Julien,
Hi,
Julien Grall writes:
Hi,
On 21/05/2019 22:25, Volodymyr Babchuk wrote:
This header files describes protocol between OP-TEE and OP-TEE client driver in Linux. They are needed for upcoming OP-TEE mediator, which is added in the next patch. Reason to add those headers in separate patch is to ease up review. Those files were taken from linux tree (drivers/tee/optee/) and mangled a bit to compile with XEN.
Can you mention the version of the Linux tree you use? This would help to track change in the future.
Actually this commit description is not valid anymore. After I added calls to inform OP-TEE about guest creation/destruction, those files does not correspond to any Linux version anymore.
So, I'll take this files from optee_os repository instead and I'll update the commit message accordingly. Can I keep your acked-by tag in such case?
I am not entirely sure to understand plan. If you only intend to modify the commit message, then I am happy if keep my acked-by. However, if you plan to re-sync the header, then please drop my acked-by.
Cheers,
Add very basic OP-TEE mediator. It can probe for OP-TEE presence, tell it about domain creation/destruction and then return an error to all calls to the guest.
This code issues two non-preemptible calls to OP-TEE: to create and to destroy client context. They can't block in OP-TEE, as they are considered "fast calls" in terms of ARM SMCCC.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com
---
All the patches to optee.c should be merged together. They were split to ease up review. But they depend heavily on each other.
Changes from v4: - Removed OPTEE_ENABLED macro. Empty (for now) struct optee_domain is used instead. - Removed forward_call() function, mediator now will return OPTEE_SMC_RETURN_ENOTAVAIL for all unimplemented calls - Fixed mistake when OPTEE_SMC_FUNCID_GET_OS_REVISION instead of OPTEE_SMC_CALL_GET_OS_REVISION was used - OP-TEE is informed about domain destruction in optee_relinquish_resources() - removed optee_domain_destroy() function because all job is done in the optee_relinquish_resources() function
Changes from v3: - Introduced optee_relinquish_resources() function to free mediator resources in a more controllable way
Changes from v2: - Fixed coding style - Introduced tee/Kconfig - Fixed error messages --- xen/arch/arm/Kconfig | 2 + xen/arch/arm/domain.c | 3 +- xen/arch/arm/tee/Kconfig | 4 + xen/arch/arm/tee/Makefile | 1 + xen/arch/arm/tee/optee.c | 166 ++++++++++++++++++++++++++++++++++ xen/include/asm-arm/domain.h | 3 + xen/include/public/arch-arm.h | 1 + 7 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 xen/arch/arm/tee/Kconfig create mode 100644 xen/arch/arm/tee/optee.c
diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index e527b2f885..99e6f0ebb2 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -237,3 +237,5 @@ source "arch/arm/platforms/Kconfig" source "common/Kconfig"
source "drivers/Kconfig" + +source "arch/arm/tee/Kconfig" diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c index 0c8e50f48f..94e6f47f75 100644 --- a/xen/arch/arm/domain.c +++ b/xen/arch/arm/domain.c @@ -649,7 +649,8 @@ int arch_sanitise_domain_config(struct xen_domctl_createdomain *config) return -EINVAL; }
- if ( config->arch.tee_type != XEN_DOMCTL_CONFIG_TEE_NONE ) + if ( config->arch.tee_type != XEN_DOMCTL_CONFIG_TEE_NONE && + config->arch.tee_type != tee_get_type() ) { dprintk(XENLOG_INFO, "Unsupported TEE type\n"); return -EINVAL; diff --git a/xen/arch/arm/tee/Kconfig b/xen/arch/arm/tee/Kconfig new file mode 100644 index 0000000000..5b829db2e9 --- /dev/null +++ b/xen/arch/arm/tee/Kconfig @@ -0,0 +1,4 @@ +config OPTEE + bool "Enable OP-TEE mediator" + default n + depends on TEE diff --git a/xen/arch/arm/tee/Makefile b/xen/arch/arm/tee/Makefile index c54d4796ff..982c879684 100644 --- a/xen/arch/arm/tee/Makefile +++ b/xen/arch/arm/tee/Makefile @@ -1 +1,2 @@ obj-y += tee.o +obj-$(CONFIG_OPTEE) += optee.o diff --git a/xen/arch/arm/tee/optee.c b/xen/arch/arm/tee/optee.c new file mode 100644 index 0000000000..e9b69bd2d2 --- /dev/null +++ b/xen/arch/arm/tee/optee.c @@ -0,0 +1,166 @@ +/* + * xen/arch/arm/tee/optee.c + * + * OP-TEE mediator. It sits in between OP-TEE and guests and performs + * actual calls to OP-TEE when some guest tries to interact with + * OP-TEE. As OP-TEE does not know about second stage MMU translation, + * mediator does this translation and performs other housekeeping tasks. + * + * OP-TEE ABI/protocol is described in two header files: + * - optee_smc.h provides information about SMCs: all possible calls, + * register allocation and return codes. + * - optee_msg.h provides format for messages that are passed with + * standard call OPTEE_SMC_CALL_WITH_ARG. + * + * Volodymyr Babchuk volodymyr_babchuk@epam.com + * Copyright (c) 2018-2019 EPAM Systems. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <xen/device_tree.h> +#include <xen/sched.h> + +#include <asm/smccc.h> +#include <asm/tee/tee.h> +#include <asm/tee/optee_msg.h> +#include <asm/tee/optee_smc.h> + +/* Client ID 0 is reserved for the hypervisor itself */ +#define OPTEE_CLIENT_ID(domain) ((domain)->domain_id + 1) + +/* Domain context */ +struct optee_domain { +}; + +static bool optee_probe(void) +{ + struct dt_device_node *node; + struct arm_smccc_res resp; + + /* Check for entry in dtb */ + node = dt_find_compatible_node(NULL, NULL, "linaro,optee-tz"); + if ( !node ) + return false; + + /* Check UID */ + arm_smccc_smc(ARM_SMCCC_CALL_UID_FID(TRUSTED_OS_END), &resp); + + if ( (uint32_t)resp.a0 != OPTEE_MSG_UID_0 || + (uint32_t)resp.a1 != OPTEE_MSG_UID_1 || + (uint32_t)resp.a2 != OPTEE_MSG_UID_2 || + (uint32_t)resp.a3 != OPTEE_MSG_UID_3 ) + return false; + + return true; +} + +static int optee_domain_init(struct domain *d) +{ + struct arm_smccc_res resp; + struct optee_domain *ctx; + + ctx = xzalloc(struct optee_domain); + if ( !ctx ) + return -ENOMEM; + + /* + * Inform OP-TEE about a new guest. This is a "Fast" call in + * terms of OP-TEE. This basically means that it can't be + * preempted, because there is no thread allocated for it in + * OP-TEE. No blocking calls can be issued and interrupts are + * disabled. + * + * a7 should be 0, so we can't skip last 6 parameters of arm_smccc_smc() + */ + arm_smccc_smc(OPTEE_SMC_VM_CREATED, OPTEE_CLIENT_ID(d), 0, 0, 0, 0, 0, 0, + &resp); + if ( resp.a0 != OPTEE_SMC_RETURN_OK ) + { + printk(XENLOG_WARNING "%pd: Unable to create OPTEE client: rc = 0x%X\n", + d, (uint32_t)resp.a0); + + xfree(ctx); + + return -ENODEV; + } + + d->arch.tee = ctx; + + return 0; +} + +static int optee_relinquish_resources(struct domain *d) +{ + struct arm_smccc_res resp; + + if ( !d->arch.tee ) + return 0; + + /* + * Inform OP-TEE that domain is shutting down. This is + * also a fast SMC call, like OPTEE_SMC_VM_CREATED, so + * it is also non-preemptible. + * At this time all domain VCPUs should be stopped. OP-TEE + * relies on this. + * + * a7 should be 0, so we can't skip last 6 parameters of arm_smccc_smc() + */ + arm_smccc_smc(OPTEE_SMC_VM_DESTROYED, OPTEE_CLIENT_ID(d), 0, 0, 0, 0, 0, 0, + &resp); + + XFREE(d->arch.tee); + + return 0; +} + +static bool optee_handle_call(struct cpu_user_regs *regs) +{ + if ( !current->domain->arch.tee ) + return false; + + switch ( get_user_reg(regs, 0) ) + { + case OPTEE_SMC_CALLS_COUNT: + case OPTEE_SMC_CALLS_UID: + case OPTEE_SMC_CALLS_REVISION: + case OPTEE_SMC_CALL_GET_OS_UUID: + case OPTEE_SMC_CALL_GET_OS_REVISION: + case OPTEE_SMC_ENABLE_SHM_CACHE: + case OPTEE_SMC_DISABLE_SHM_CACHE: + case OPTEE_SMC_GET_SHM_CONFIG: + case OPTEE_SMC_EXCHANGE_CAPABILITIES: + case OPTEE_SMC_CALL_WITH_ARG: + case OPTEE_SMC_CALL_RETURN_FROM_RPC: + set_user_reg(regs, 0, OPTEE_SMC_RETURN_ENOTAVAIL); + return true; + + default: + return false; + } +} + +static const struct tee_mediator_ops optee_ops = +{ + .probe = optee_probe, + .domain_init = optee_domain_init, + .relinquish_resources = optee_relinquish_resources, + .handle_call = optee_handle_call, +}; + +REGISTER_TEE_MEDIATOR(optee, "OP-TEE", XEN_DOMCTL_CONFIG_TEE_OPTEE, &optee_ops); + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h index 0f15372098..2960a53e69 100644 --- a/xen/include/asm-arm/domain.h +++ b/xen/include/asm-arm/domain.h @@ -98,6 +98,9 @@ struct arch_domain struct vpl011 vpl011; #endif
+#ifdef CONFIG_TEE + void *tee; +#endif } __cacheline_aligned;
struct arch_vcpu diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h index 5e938a91cc..ced9545596 100644 --- a/xen/include/public/arch-arm.h +++ b/xen/include/public/arch-arm.h @@ -305,6 +305,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); #define XEN_DOMCTL_CONFIG_GIC_V2 1 #define XEN_DOMCTL_CONFIG_GIC_V3 2 #define XEN_DOMCTL_CONFIG_TEE_NONE 0 +#define XEN_DOMCTL_CONFIG_TEE_OPTEE 1 struct xen_arch_domainconfig { /* IN/OUT */ uint8_t gic_version;
Hi Volodymyr,
On 21/05/2019 22:25, Volodymyr Babchuk wrote:
Add very basic OP-TEE mediator. It can probe for OP-TEE presence, tell it about domain creation/destruction and then return an error to all calls to the guest.
This code issues two non-preemptible calls to OP-TEE: to create and to destroy client context. They can't block in OP-TEE, as they are considered "fast calls" in terms of ARM SMCCC.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com
Acked-by: Julien Grall julien.grall@arm.com
Cheers,
This patch adds handling for the fast SMCs. As name suggests, those calls can't be preempted and are used for auxiliary tasks such as information retrieval. Most handlers are quite trivial, with exception for capabilities information.
Capabilities exchange should be filtered out, so only caps known to mediator are used. Also mediator disables static SHM memory capability, because it can't share OP-TEE memory with a domain. Only domain can share memory with OP-TEE, so it ensures that OP-TEE supports dynamic SHM.
Basically, static SHM is a reserved memory region which is always mapped into OP-TEE address space. It belongs to OP-TEE. Normally, NW is allowed to access there, so it can communicate with OP-TEE.
On other hand, dynamic SHM is NW's own memory, which it can share with OP-TEE. OP-TEE maps this memory dynamically, when it wants to access it.
Because mediator can't share one static SHM region with all guests, it just disables it for all of them. It is possible to make exception for Dom0, but it requires separate handling for buffers allocated from that region. Thus, it is not implemented yet.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com ---
All the patches to optee.c should be merged together. They were split to ease up review. But they depend heavily on each other.
Changes from v4: - Handler does not use forward_call(). Instead it calls OP-TEE directly with arm_smccc_smc(). - Handler modifies only those guest registers that are should be touched according to OP-TEE protocol specification. - Added OPTEE_MEDIATOR_SMC_COUNT definition.
Changes from v2: - Defined known capabilities explicitly - Fixed code style --- xen/arch/arm/tee/optee.c | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+)
diff --git a/xen/arch/arm/tee/optee.c b/xen/arch/arm/tee/optee.c index e9b69bd2d2..6c51caa41a 100644 --- a/xen/arch/arm/tee/optee.c +++ b/xen/arch/arm/tee/optee.c @@ -32,9 +32,17 @@ #include <asm/tee/optee_msg.h> #include <asm/tee/optee_smc.h>
+/* Number of SMCs known to the mediator */ +#define OPTEE_MEDIATOR_SMC_COUNT 11 + /* Client ID 0 is reserved for the hypervisor itself */ #define OPTEE_CLIENT_ID(domain) ((domain)->domain_id + 1)
+#define OPTEE_KNOWN_NSEC_CAPS OPTEE_SMC_NSEC_CAP_UNIPROCESSOR +#define OPTEE_KNOWN_SEC_CAPS (OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM | \ + OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM | \ + OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) + /* Domain context */ struct optee_domain { }; @@ -120,22 +128,111 @@ static int optee_relinquish_resources(struct domain *d) return 0; }
+static void handle_exchange_capabilities(struct cpu_user_regs *regs) +{ + struct arm_smccc_res resp; + uint32_t caps; + + /* Filter out unknown guest caps */ + caps = get_user_reg(regs, 1); + caps &= OPTEE_KNOWN_NSEC_CAPS; + + arm_smccc_smc(OPTEE_SMC_EXCHANGE_CAPABILITIES, caps, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + if ( resp.a0 != OPTEE_SMC_RETURN_OK ) { + set_user_reg(regs, 0, resp.a0); + return; + } + + caps = resp.a1; + + /* Filter out unknown OP-TEE caps */ + caps &= OPTEE_KNOWN_SEC_CAPS; + + /* Drop static SHM_RPC cap */ + caps &= ~OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM; + + /* Don't allow guests to work without dynamic SHM */ + if ( !(caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) ) + { + set_user_reg(regs, 0, OPTEE_SMC_RETURN_ENOTAVAIL); + return; + } + + set_user_reg(regs, 0, OPTEE_SMC_RETURN_OK); + set_user_reg(regs, 1, caps); +} + static bool optee_handle_call(struct cpu_user_regs *regs) { + struct arm_smccc_res resp; + if ( !current->domain->arch.tee ) return false;
switch ( get_user_reg(regs, 0) ) { case OPTEE_SMC_CALLS_COUNT: + set_user_reg(regs, 0, OPTEE_MEDIATOR_SMC_COUNT); + return true; + case OPTEE_SMC_CALLS_UID: + arm_smccc_smc(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + set_user_reg(regs, 0, resp.a0); + set_user_reg(regs, 1, resp.a1); + set_user_reg(regs, 2, resp.a2); + set_user_reg(regs, 3, resp.a3); + return true; + case OPTEE_SMC_CALLS_REVISION: + arm_smccc_smc(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + set_user_reg(regs, 0, resp.a0); + set_user_reg(regs, 1, resp.a1); + return true; + case OPTEE_SMC_CALL_GET_OS_UUID: + arm_smccc_smc(OPTEE_SMC_CALL_GET_OS_UUID, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain),&resp); + set_user_reg(regs, 0, resp.a0); + set_user_reg(regs, 1, resp.a1); + set_user_reg(regs, 2, resp.a2); + set_user_reg(regs, 3, resp.a3); + return true; + case OPTEE_SMC_CALL_GET_OS_REVISION: + arm_smccc_smc(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + set_user_reg(regs, 0, resp.a0); + set_user_reg(regs, 1, resp.a1); + return true; + case OPTEE_SMC_ENABLE_SHM_CACHE: + arm_smccc_smc(OPTEE_SMC_ENABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + set_user_reg(regs, 0, resp.a0); + return true; + case OPTEE_SMC_DISABLE_SHM_CACHE: + arm_smccc_smc(OPTEE_SMC_ENABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + set_user_reg(regs, 0, resp.a0); + if ( resp.a0 == OPTEE_SMC_RETURN_OK ) { + set_user_reg(regs, 1, resp.a1); + set_user_reg(regs, 2, resp.a2); + } + return true; + case OPTEE_SMC_GET_SHM_CONFIG: + /* No static SHM available for guests */ + set_user_reg(regs, 0, OPTEE_SMC_RETURN_ENOTAVAIL); + return true; + case OPTEE_SMC_EXCHANGE_CAPABILITIES: + handle_exchange_capabilities(regs); + return true; + case OPTEE_SMC_CALL_WITH_ARG: case OPTEE_SMC_CALL_RETURN_FROM_RPC: set_user_reg(regs, 0, OPTEE_SMC_RETURN_ENOTAVAIL);
Hi Volodymyr,
On 21/05/2019 22:25, Volodymyr Babchuk wrote:
This patch adds handling for the fast SMCs. As name suggests, those calls can't be preempted and are used for auxiliary tasks such as information retrieval. Most handlers are quite trivial, with exception for capabilities information.
Capabilities exchange should be filtered out, so only caps known to mediator are used. Also mediator disables static SHM memory capability, because it can't share OP-TEE memory with a domain. Only domain can share memory with OP-TEE, so it ensures that OP-TEE supports dynamic SHM.
Basically, static SHM is a reserved memory region which is always mapped into OP-TEE address space. It belongs to OP-TEE. Normally, NW is allowed to access there, so it can communicate with OP-TEE.
On other hand, dynamic SHM is NW's own memory, which it can share with OP-TEE. OP-TEE maps this memory dynamically, when it wants to access it.
Because mediator can't share one static SHM region with all guests, it just disables it for all of them. It is possible to make exception for Dom0, but it requires separate handling for buffers allocated from that region. Thus, it is not implemented yet.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com
Acked-by: Julien Gral julien.grall@arm.com
Cheers,
The main way to communicate with OP-TEE is to issue standard SMCCC call. "Standard" is a SMCCC term and it means that call can be interrupted and OP-TEE can return control to NW before completing the call.
In contrast with fast calls, where arguments and return values are passed in registers, standard calls use shared memory. Register pair a1,a2 holds 64-bit PA of command buffer, where all arguments are stored and which is used to return data. OP-TEE internally copies contents of this buffer into own secure memory before accessing and validating any data in command buffer. This is done to make sure that NW will not change contents of the validated parameters.
Mediator needs to do the same for number of reasons:
1. To make sure that guest will not change data after validation. 2. To translate IPAs to PAs in the command buffer (this is not done in this patch). 3. To hide translated address from guest, so it will not be able to do IPA->PA translation by misusing mediator.
During standard call OP-TEE can issue multiple "RPC returns", asking NW to do some work for OP-TEE. NW then issues special call OPTEE_SMC_CALL_RETURN_FROM_RPC to resume handling of the original call. Thus, mediator needs to maintain context for original standard call during multiple SMCCC calls.
Standard call is considered complete, when returned value is not a RPC request.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com ---
All the patches to optee.c should be merged together. They were split to ease up review. But they depend heavily on each other.
Changes from v4: - Code uses arm_smccc_smc() directly, instead of forward_call() - do_call_with_arg() function now accepts register values as parameters, so it can be called by RPC handlers with correct values for the given RPC type - optee_probe() calls OPTEE_SMC_GET_THREAD_COUNT. This call is merged into OP-TEE mainline and will be released with OP-TEE v3.5.0 - Removed DEF_MAX_OPTEE_THREADS because it is expected that OP-TEE would support OPTEE_SMC_GET_THREAD_COUNT - Moved map/unmap_xen_arg() outside the spinlocks - Added get_domain_ram_page() helper function - Check the number of parameters, that are supplied by guest
Changes from v3: - Added ability to read number of threads from OP-TEE, if it supports this feature - Pages are allocated from domheap, instead of xenheap - Added comments for complex code
Changes from v2: - renamed struct domain_ctx to struct optee_domain - fixed coding style - Now I use access_guest_memory_by_ipa() instead of mappings to read command buffer - Added tracking for in flight calls, so guest can't resume the same call from two CPUs simultaniously --- xen/arch/arm/tee/optee.c | 510 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 507 insertions(+), 3 deletions(-)
diff --git a/xen/arch/arm/tee/optee.c b/xen/arch/arm/tee/optee.c index 6c51caa41a..f092492849 100644 --- a/xen/arch/arm/tee/optee.c +++ b/xen/arch/arm/tee/optee.c @@ -25,8 +25,13 @@ */
#include <xen/device_tree.h> +#include <xen/domain_page.h> +#include <xen/err.h> +#include <xen/guest_access.h> +#include <xen/mm.h> #include <xen/sched.h>
+#include <asm/event.h> #include <asm/smccc.h> #include <asm/tee/tee.h> #include <asm/tee/optee_msg.h> @@ -35,6 +40,19 @@ /* Number of SMCs known to the mediator */ #define OPTEE_MEDIATOR_SMC_COUNT 11
+/* + * "The return code is an error that originated within the underlying + * communications stack linking the rich OS with the TEE" as described + * in GP TEE Client API Specification. + */ +#define TEEC_ORIGIN_COMMS 0x00000002 + +/* + * "Input parameters were invalid" as described + * in GP TEE Client API Specification. + */ +#define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006 + /* Client ID 0 is reserved for the hypervisor itself */ #define OPTEE_CLIENT_ID(domain) ((domain)->domain_id + 1)
@@ -43,8 +61,31 @@ OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM | \ OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
+static unsigned int __read_mostly max_optee_threads; + +/* + * Call context. OP-TEE can issue multiple RPC returns during one call. + * We need to preserve context during them. + */ +struct optee_std_call { + struct list_head list; + /* Page where shadowed copy of call arguments is stored */ + struct page_info *xen_arg_pg; + /* Above page mapped into XEN */ + struct optee_msg_arg *xen_arg; + /* Address of original call arguments */ + paddr_t guest_arg_ipa; + int optee_thread_id; + int rpc_op; + bool in_flight; + register_t rpc_params[2]; +}; + /* Domain context */ struct optee_domain { + struct list_head call_list; + atomic_t call_count; + spinlock_t lock; };
static bool optee_probe(void) @@ -66,6 +107,23 @@ static bool optee_probe(void) (uint32_t)resp.a3 != OPTEE_MSG_UID_3 ) return false;
+ /* Read number of threads */ + arm_smccc_smc(OPTEE_SMC_GET_THREAD_COUNT, &resp); + if ( resp.a0 == OPTEE_SMC_RETURN_OK ) + { + max_optee_threads = resp.a1; + printk(XENLOG_INFO + "OP-TEE supports %u simultaneous threads per guest.\n", + max_optee_threads); + } + else + { + printk(XENLOG_ERR + "Can't read number of threads supported by OP-TEE: %x\n", + (uint32_t)resp.a0); + return false; + } + return true; }
@@ -99,18 +157,163 @@ static int optee_domain_init(struct domain *d) return -ENODEV; }
+ INIT_LIST_HEAD(&ctx->call_list); + atomic_set(&ctx->call_count, 0); + spin_lock_init(&ctx->lock); + d->arch.tee = ctx;
return 0; }
+static uint64_t regpair_to_uint64(register_t reg0, register_t reg1) +{ + return ((uint64_t)reg0 << 32) | (uint32_t)reg1; +} + +static void uint64_to_regpair(register_t *reg0, register_t *reg1, uint64_t val) +{ + *reg0 = val >> 32; + *reg1 = (uint32_t)val; +} + +static struct page_info *get_domain_ram_page(gfn_t gfn) +{ + struct page_info *page; + p2m_type_t t; + + page = get_page_from_gfn(current->domain, gfn_x(gfn), &t, P2M_ALLOC); + if ( !page || t != p2m_ram_rw ) + { + if ( page ) + put_page(page); + + return NULL; + } + + return page; +} + +static struct optee_std_call *allocate_std_call(struct optee_domain *ctx) +{ + struct optee_std_call *call; + int count; + + /* Make sure that guest does not execute more than max_optee_threads */ + count = atomic_add_unless(&ctx->call_count, 1, max_optee_threads); + if ( count == max_optee_threads ) + return ERR_PTR(-ENOSPC); + + call = xzalloc(struct optee_std_call); + if ( !call ) + { + atomic_dec(&ctx->call_count); + return ERR_PTR(-ENOMEM); + } + + call->optee_thread_id = -1; + call->in_flight = true; + + spin_lock(&ctx->lock); + list_add_tail(&call->list, &ctx->call_list); + spin_unlock(&ctx->lock); + + return call; +} + +static void free_std_call(struct optee_domain *ctx, + struct optee_std_call *call) +{ + atomic_dec(&ctx->call_count); + + spin_lock(&ctx->lock); + list_del(&call->list); + spin_unlock(&ctx->lock); + + ASSERT(!call->in_flight); + ASSERT(!call->xen_arg); + + if ( call->xen_arg_pg ) + free_domheap_page(call->xen_arg_pg); + + xfree(call); +} + +static void map_xen_arg(struct optee_std_call *call) +{ + ASSERT(!call->xen_arg); + + call->xen_arg = __map_domain_page(call->xen_arg_pg); +} + +static void unmap_xen_arg(struct optee_std_call *call) +{ + if ( !call->xen_arg ) + return; + + unmap_domain_page(call->xen_arg); + call->xen_arg = NULL; +} + +static struct optee_std_call *get_std_call(struct optee_domain *ctx, + int thread_id) +{ + struct optee_std_call *call; + + spin_lock(&ctx->lock); + list_for_each_entry( call, &ctx->call_list, list ) + { + if ( call->optee_thread_id == thread_id ) + { + if ( call->in_flight ) + { + gdprintk(XENLOG_WARNING, + "Guest tries to execute call which is already in flight.\n"); + goto out; + } + call->in_flight = true; + spin_unlock(&ctx->lock); + map_xen_arg(call); + + return call; + } + } + +out: + spin_unlock(&ctx->lock); + + return NULL; +} + +static void put_std_call(struct optee_domain *ctx, struct optee_std_call *call) +{ + ASSERT(call->in_flight); + unmap_xen_arg(call); + spin_lock(&ctx->lock); + call->in_flight = false; + spin_unlock(&ctx->lock); +} + static int optee_relinquish_resources(struct domain *d) { struct arm_smccc_res resp; + struct optee_std_call *call, *call_tmp; + struct optee_domain *ctx = d->arch.tee;
- if ( !d->arch.tee ) + if ( !ctx ) return 0;
+ /* + * We need to free up to max_optee_threads calls. Usually, this is + * no more than 8-16 calls. But it depends on OP-TEE configuration + * (CFG_NUM_THREADS option). + */ + list_for_each_entry_safe( call, call_tmp, &ctx->call_list, list ) + free_std_call(ctx, call); + + if ( hypercall_preempt_check() ) + return -ERESTART; + /* * Inform OP-TEE that domain is shutting down. This is * also a fast SMC call, like OPTEE_SMC_VM_CREATED, so @@ -123,11 +326,308 @@ static int optee_relinquish_resources(struct domain *d) arm_smccc_smc(OPTEE_SMC_VM_DESTROYED, OPTEE_CLIENT_ID(d), 0, 0, 0, 0, 0, 0, &resp);
+ ASSERT(!spin_is_locked(&ctx->lock)); + ASSERT(!atomic_read(&ctx->call_count)); + XFREE(d->arch.tee);
return 0; }
+/* + * Copy command buffer into domheap memory to: + * 1) Hide translated addresses from guest + * 2) Make sure that guest wouldn't change data in command buffer during call + */ +static bool copy_std_request(struct cpu_user_regs *regs, + struct optee_std_call *call) +{ + call->guest_arg_ipa = regpair_to_uint64(get_user_reg(regs, 1), + get_user_reg(regs, 2)); + + /* + * Command buffer should start at page boundary. + * This is OP-TEE ABI requirement. + */ + if ( call->guest_arg_ipa & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1) ) + { + set_user_reg(regs, 0, OPTEE_SMC_RETURN_EBADADDR); + return false; + } + + BUILD_BUG_ON(OPTEE_MSG_NONCONTIG_PAGE_SIZE > PAGE_SIZE); + + call->xen_arg_pg = alloc_domheap_page(current->domain, 0); + if ( !call->xen_arg_pg ) + { + set_user_reg(regs, 0, OPTEE_SMC_RETURN_ENOMEM); + return false; + } + + map_xen_arg(call); + + if ( access_guest_memory_by_ipa(current->domain, call->guest_arg_ipa, + call->xen_arg, + OPTEE_MSG_NONCONTIG_PAGE_SIZE, false) ) + { + set_user_reg(regs, 0, OPTEE_SMC_RETURN_EBADADDR); + return false; + } + + return true; +} + +/* + * Copy result of completed request back to guest's buffer. + * We are copying only values that subjected to change to minimize + * possible information leak. + * + * Because there can be multiple RPCs during standard call, and guest + * is not obligated to return from RPC immediately, there can be + * arbitrary time span between calling copy_std_request() and + * copy_std_request(). So we need to validate guest's command buffer + * again. + */ +static void copy_std_request_back(struct optee_domain *ctx, + struct cpu_user_regs *regs, + struct optee_std_call *call) +{ + struct optee_msg_arg *guest_arg; + struct page_info *page; + unsigned int i; + uint32_t attr; + + page = get_domain_ram_page(gaddr_to_gfn(call->guest_arg_ipa)); + if ( !page ) + { + /* + * Guest did something to own command buffer during the call. + * Now we even can't write error code to the command + * buffer. Let's try to return generic error via + * register. Problem is that OP-TEE does not know that guest + * didn't received valid response. But at least guest will + * know that something bad happened. + */ + set_user_reg(regs, 0, OPTEE_SMC_RETURN_EBADADDR); + + return; + } + + guest_arg = __map_domain_page(page); + + guest_arg->ret = call->xen_arg->ret; + guest_arg->ret_origin = call->xen_arg->ret_origin; + guest_arg->session = call->xen_arg->session; + + for ( i = 0; i < call->xen_arg->num_params; i++ ) + { + attr = call->xen_arg->params[i].attr; + + switch ( attr & OPTEE_MSG_ATTR_TYPE_MASK ) + { + case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: + guest_arg->params[i].u.tmem.size = + call->xen_arg->params[i].u.tmem.size; + continue; + case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: + guest_arg->params[i].u.rmem.size = + call->xen_arg->params[i].u.rmem.size; + continue; + case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: + guest_arg->params[i].u.value.a = + call->xen_arg->params[i].u.value.a; + guest_arg->params[i].u.value.b = + call->xen_arg->params[i].u.value.b; + guest_arg->params[i].u.value.c = + call->xen_arg->params[i].u.value.c; + continue; + case OPTEE_MSG_ATTR_TYPE_NONE: + case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: + continue; + } + } + + unmap_domain_page(guest_arg); + put_page(page); +} + +/* Handle RPC return from OP-TEE */ +static void handle_rpc_return(struct arm_smccc_res *res, + struct cpu_user_regs *regs, + struct optee_std_call *call) +{ + call->rpc_op = OPTEE_SMC_RETURN_GET_RPC_FUNC(res->a0); + call->rpc_params[0] = res->a1; + call->rpc_params[1] = res->a2; + call->optee_thread_id = res->a3; + + set_user_reg(regs, 0, res->a0); + set_user_reg(regs, 1, res->a1); + set_user_reg(regs, 2, res->a2); + set_user_reg(regs, 3, res->a3); +} + +/* + * (Re)start standard call. This function will be called in two cases: + * 1. Guest initiates new standard call + * 2. Guest finished RPC handling and asks OP-TEE to resume the call + * + * In any case OP-TEE can either complete call or issue another RPC. + * If this is RPC - we need to store call context and return back to guest. + * If call is complete - we need to return results with copy_std_request_back() + * and then we will destroy the call context as it is not needed anymore. + */ +static void do_call_with_arg(struct optee_domain *ctx, + struct optee_std_call *call, + struct cpu_user_regs *regs, + register_t a0, register_t a1, register_t a2, + register_t a3, register_t a4, register_t a5) +{ + struct arm_smccc_res res; + + arm_smccc_smc(a0, a1, a2, a3, a4, a5, 0, OPTEE_CLIENT_ID(current->domain), + &res); + + if ( OPTEE_SMC_RETURN_IS_RPC(res.a0) ) + { + handle_rpc_return(&res, regs, call); + put_std_call(ctx, call); + + return; + } + + copy_std_request_back(ctx, regs, call); + set_user_reg(regs, 0, res.a0); + + put_std_call(ctx, call); + free_std_call(ctx, call); +} + +/* + * Standard call handling. This is the main type of the call which + * makes OP-TEE useful. Most of the other calls type are utility + * calls, while standard calls are needed to interact with Trusted + * Applications which are running inside the OP-TEE. + * + * All arguments for this type of call are passed in the command + * buffer in the guest memory. We will copy this buffer into + * own shadow buffer and provide the copy to OP-TEE. + * + * This call is preemptible. OP-TEE will return from the call if there + * is an interrupt request pending. Also, OP-TEE will interrupt the + * call if it needs some service from guest. In both cases it will + * issue RPC, which is processed by handle_rpc_return() function. + */ +static void handle_std_call(struct optee_domain *ctx, + struct cpu_user_regs *regs) +{ + register_t a1, a2; + paddr_t xen_addr; + size_t arg_size; + struct optee_std_call *call = allocate_std_call(ctx); + + if ( IS_ERR(call) ) + { + if ( PTR_ERR(call) == -ENOMEM ) + set_user_reg(regs, 0, OPTEE_SMC_RETURN_ENOMEM); + else + set_user_reg(regs, 0, OPTEE_SMC_RETURN_ETHREAD_LIMIT); + + return; + } + + if ( !copy_std_request(regs, call) ) + goto err; + + arg_size = OPTEE_MSG_GET_ARG_SIZE(call->xen_arg->num_params); + if ( arg_size > OPTEE_MSG_NONCONTIG_PAGE_SIZE ) + { + call->xen_arg->ret = TEEC_ERROR_BAD_PARAMETERS; + call->xen_arg->ret_origin = TEEC_ORIGIN_COMMS; + /* Make sure that copy_std_request_back() will stay within the buffer */ + call->xen_arg->num_params = 0; + + copy_std_request_back(ctx, regs, call); + + goto err; + } + + switch ( call->xen_arg->cmd ) + { + case OPTEE_MSG_CMD_OPEN_SESSION: + case OPTEE_MSG_CMD_CLOSE_SESSION: + case OPTEE_MSG_CMD_INVOKE_COMMAND: + case OPTEE_MSG_CMD_CANCEL: + case OPTEE_MSG_CMD_REGISTER_SHM: + case OPTEE_MSG_CMD_UNREGISTER_SHM: + xen_addr = page_to_maddr(call->xen_arg_pg); + uint64_to_regpair(&a1, &a2, xen_addr); + + do_call_with_arg(ctx, call, regs, OPTEE_SMC_CALL_WITH_ARG, a1, a2, + OPTEE_SMC_SHM_CACHED, 0, 0); + return; + default: + set_user_reg(regs, 0, OPTEE_SMC_RETURN_EBADCMD); + break; + } + +err: + put_std_call(ctx, call); + free_std_call(ctx, call); + + return; +} + +/* + * This function is called when guest is finished processing RPC + * request from OP-TEE and wished to resume the interrupted standard + * call. + */ +static void handle_rpc(struct optee_domain *ctx, struct cpu_user_regs *regs) +{ + struct optee_std_call *call; + int optee_thread_id = get_user_reg(regs, 3); + + call = get_std_call(ctx, optee_thread_id); + + if ( !call ) + { + set_user_reg(regs, 0, OPTEE_SMC_RETURN_ERESUME); + return; + } + + /* + * This is to prevent race between new call with the same thread id. + * OP-TEE can reuse thread id right after it finished handling the call, + * before XEN had chance to free old call context. + */ + call->optee_thread_id = -1; + + switch ( call->rpc_op ) + { + case OPTEE_SMC_RPC_FUNC_ALLOC: + /* TODO: Add handling */ + break; + case OPTEE_SMC_RPC_FUNC_FREE: + /* TODO: Add handling */ + break; + case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: + break; + case OPTEE_SMC_RPC_FUNC_CMD: + /* TODO: Add handling */ + break; + } + + do_call_with_arg(ctx, call, regs, OPTEE_SMC_CALL_RETURN_FROM_RPC, + call->rpc_params[0], call->rpc_params[1], + optee_thread_id, 0, 0); + return; +} + static void handle_exchange_capabilities(struct cpu_user_regs *regs) { struct arm_smccc_res resp; @@ -166,8 +666,9 @@ static void handle_exchange_capabilities(struct cpu_user_regs *regs) static bool optee_handle_call(struct cpu_user_regs *regs) { struct arm_smccc_res resp; + struct optee_domain *ctx = current->domain->arch.tee;
- if ( !current->domain->arch.tee ) + if ( !ctx ) return false;
switch ( get_user_reg(regs, 0) ) @@ -234,8 +735,11 @@ static bool optee_handle_call(struct cpu_user_regs *regs) return true;
case OPTEE_SMC_CALL_WITH_ARG: + handle_std_call(ctx, regs); + return true; + case OPTEE_SMC_CALL_RETURN_FROM_RPC: - set_user_reg(regs, 0, OPTEE_SMC_RETURN_ENOTAVAIL); + handle_rpc(ctx, regs); return true;
default:
Hi Volodymyr,
On 21/05/2019 22:26, Volodymyr Babchuk wrote:
The main way to communicate with OP-TEE is to issue standard SMCCC call. "Standard" is a SMCCC term and it means that call can be interrupted and OP-TEE can return control to NW before completing the call.
In contrast with fast calls, where arguments and return values are passed in registers, standard calls use shared memory. Register pair a1,a2 holds 64-bit PA of command buffer, where all arguments are stored and which is used to return data. OP-TEE internally copies contents of this buffer into own secure memory before accessing and validating any data in command buffer. This is done to make sure that NW will not change contents of the validated parameters.
Mediator needs to do the same for number of reasons:
- To make sure that guest will not change data after validation.
- To translate IPAs to PAs in the command buffer (this is not done in this patch).
- To hide translated address from guest, so it will not be able to do IPA->PA translation by misusing mediator.
During standard call OP-TEE can issue multiple "RPC returns", asking NW to do some work for OP-TEE. NW then issues special call OPTEE_SMC_CALL_RETURN_FROM_RPC to resume handling of the original call. Thus, mediator needs to maintain context for original standard call during multiple SMCCC calls.
Standard call is considered complete, when returned value is not a RPC request.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com
Acked-by: Julien Grall julien.grall@arm.com
Cheers,
OP-TEE usually uses the same idea with command buffers (see previous commit) to issue RPC requests. Problem is that initially it has no buffer, where it can write request. So the first RPC request it makes is special: it requests NW to allocate shared buffer for other RPC requests. Usually this buffer is allocated only once for every OP-TEE thread and it remains allocated all the time until guest shuts down. Guest can ask OP-TEE to disable RPC buffers caching, in this case OP-TEE will ask guest to allocate/free buffer for the each RPC.
Mediator needs to pin this buffer to make sure that page will be not free while it is shared with OP-TEE.
Life cycle of this buffer is controlled by OP-TEE. It asks guest to create buffer and it asks it to free it. So it there is not much sense to limit number of those buffers, because we already limit the number of concurrent standard calls and prevention of RPC buffer allocation will impair OP-TEE functionality.
Those buffers can be freed in two ways: either OP-TEE issues OPTEE_SMC_RPC_FUNC_FREE RPC request or guest tries to disable buffer caching by calling OPTEE_SMC_DISABLE_SHM_CACHE function. In the latter case OP-TEE will return cookie of the SHM buffer it just freed.
OP-TEE expects that this RPC buffer have size of OPTEE_MSG_NONCONTIG_PAGE_SIZE, which equals to 4096 and is aligned with the same size. So, basically it expects one 4k page from the guest. This is the same as Xen's PAGE_SIZE.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com
---
All the patches to optee.c should be merged together. They were split to ease up review. But they depend heavily on each other.
Changes from v4: - handle_rpc_func_alloc() now calls do_call_with_arg() directly
Changes from v3: - Removed MAX_RPC_SHMS constant. Now this value depends on number of OP-TEE threads - Various formatting fixes - Added checks for guest memory type
Changes from v2: - Added check to ensure that guests does not return two SHM buffers with the same cookie - Fixed coding style - Storing RPC parameters during RPC return to make sure, that guest will not change them during call continuation --- xen/arch/arm/tee/optee.c | 149 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 145 insertions(+), 4 deletions(-)
diff --git a/xen/arch/arm/tee/optee.c b/xen/arch/arm/tee/optee.c index f092492849..175789fb00 100644 --- a/xen/arch/arm/tee/optee.c +++ b/xen/arch/arm/tee/optee.c @@ -81,9 +81,17 @@ struct optee_std_call { register_t rpc_params[2]; };
+/* Pre-allocated SHM buffer for RPC commands */ +struct shm_rpc { + struct list_head list; + struct page_info *guest_page; + uint64_t cookie; +}; + /* Domain context */ struct optee_domain { struct list_head call_list; + struct list_head shm_rpc_list; atomic_t call_count; spinlock_t lock; }; @@ -158,6 +166,7 @@ static int optee_domain_init(struct domain *d) }
INIT_LIST_HEAD(&ctx->call_list); + INIT_LIST_HEAD(&ctx->shm_rpc_list); atomic_set(&ctx->call_count, 0); spin_lock_init(&ctx->lock);
@@ -199,7 +208,11 @@ static struct optee_std_call *allocate_std_call(struct optee_domain *ctx) struct optee_std_call *call; int count;
- /* Make sure that guest does not execute more than max_optee_threads */ + /* + * Make sure that guest does not execute more than max_optee_threads. + * This also indirectly limits number of RPC SHM buffers, because OP-TEE + * allocates one such buffer per standard call. + */ count = atomic_add_unless(&ctx->call_count, 1, max_optee_threads); if ( count == max_optee_threads ) return ERR_PTR(-ENOSPC); @@ -294,10 +307,80 @@ static void put_std_call(struct optee_domain *ctx, struct optee_std_call *call) spin_unlock(&ctx->lock); }
+static struct shm_rpc *allocate_and_pin_shm_rpc(struct optee_domain *ctx, + gfn_t gfn, uint64_t cookie) +{ + struct shm_rpc *shm_rpc, *shm_rpc_tmp; + + shm_rpc = xzalloc(struct shm_rpc); + if ( !shm_rpc ) + return ERR_PTR(-ENOMEM); + + /* This page will be shared with OP-TEE, so we need to pin it. */ + shm_rpc->guest_page = get_domain_ram_page(gfn); + if ( !shm_rpc->guest_page ) + goto err; + + shm_rpc->cookie = cookie; + + spin_lock(&ctx->lock); + /* Check if there is existing SHM with the same cookie. */ + list_for_each_entry( shm_rpc_tmp, &ctx->shm_rpc_list, list ) + { + if ( shm_rpc_tmp->cookie == cookie ) + { + spin_unlock(&ctx->lock); + gdprintk(XENLOG_WARNING, "Guest tries to use the same RPC SHM cookie %lx\n", + cookie); + goto err; + } + } + + list_add_tail(&shm_rpc->list, &ctx->shm_rpc_list); + spin_unlock(&ctx->lock); + + return shm_rpc; + +err: + if ( shm_rpc->guest_page ) + put_page(shm_rpc->guest_page); + xfree(shm_rpc); + + return ERR_PTR(-EINVAL); +} + +static void free_shm_rpc(struct optee_domain *ctx, uint64_t cookie) +{ + struct shm_rpc *shm_rpc; + bool found = false; + + spin_lock(&ctx->lock); + + list_for_each_entry( shm_rpc, &ctx->shm_rpc_list, list ) + { + if ( shm_rpc->cookie == cookie ) + { + found = true; + list_del(&shm_rpc->list); + break; + } + } + spin_unlock(&ctx->lock); + + if ( !found ) + return; + + ASSERT(shm_rpc->guest_page); + put_page(shm_rpc->guest_page); + + xfree(shm_rpc); +} + static int optee_relinquish_resources(struct domain *d) { struct arm_smccc_res resp; struct optee_std_call *call, *call_tmp; + struct shm_rpc *shm_rpc, *shm_rpc_tmp; struct optee_domain *ctx = d->arch.tee;
if ( !ctx ) @@ -314,6 +397,16 @@ static int optee_relinquish_resources(struct domain *d) if ( hypercall_preempt_check() ) return -ERESTART;
+ /* + * Number of this buffers also depends on max_optee_threads, so + * check the comment above. + */ + list_for_each_entry_safe( shm_rpc, shm_rpc_tmp, &ctx->shm_rpc_list, list ) + free_shm_rpc(ctx, shm_rpc->cookie); + + if ( hypercall_preempt_check() ) + return -ERESTART; + /* * Inform OP-TEE that domain is shutting down. This is * also a fast SMC call, like OPTEE_SMC_VM_CREATED, so @@ -328,6 +421,7 @@ static int optee_relinquish_resources(struct domain *d)
ASSERT(!spin_is_locked(&ctx->lock)); ASSERT(!atomic_read(&ctx->call_count)); + ASSERT(list_empty(&ctx->shm_rpc_list));
XFREE(d->arch.tee);
@@ -587,6 +681,48 @@ err: * request from OP-TEE and wished to resume the interrupted standard * call. */ +static void handle_rpc_func_alloc(struct optee_domain *ctx, + struct cpu_user_regs *regs, + struct optee_std_call *call) +{ + struct shm_rpc *shm_rpc; + register_t r1, r2; + paddr_t ptr = regpair_to_uint64(get_user_reg(regs, 1), + get_user_reg(regs, 2)); + uint64_t cookie = regpair_to_uint64(get_user_reg(regs, 4), + get_user_reg(regs, 5)); + + if ( ptr & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1) ) + { + gdprintk(XENLOG_WARNING, "Domain returned invalid RPC command buffer\n"); + /* + * OP-TEE is waiting for a response to the RPC. We can't just + * return error to the guest. We need to provide some invalid + * value to OP-TEE, so it can handle error on its side. + */ + ptr = 0; + goto out; + } + + shm_rpc = allocate_and_pin_shm_rpc(ctx, gaddr_to_gfn(ptr), cookie); + if ( IS_ERR(shm_rpc) ) + { + gdprintk(XENLOG_WARNING, "Failed to allocate shm_rpc object: %ld\n", + PTR_ERR(shm_rpc)); + ptr = 0; + } + else + ptr = page_to_maddr(shm_rpc->guest_page); + +out: + uint64_to_regpair(&r1, &r2, ptr); + + do_call_with_arg(ctx, call, regs, OPTEE_SMC_CALL_RETURN_FROM_RPC, r1, r2, + get_user_reg(regs, 3), + get_user_reg(regs, 4), + get_user_reg(regs, 5)); +} + static void handle_rpc(struct optee_domain *ctx, struct cpu_user_regs *regs) { struct optee_std_call *call; @@ -610,11 +746,15 @@ static void handle_rpc(struct optee_domain *ctx, struct cpu_user_regs *regs) switch ( call->rpc_op ) { case OPTEE_SMC_RPC_FUNC_ALLOC: - /* TODO: Add handling */ - break; + handle_rpc_func_alloc(ctx, regs, call); + return; case OPTEE_SMC_RPC_FUNC_FREE: - /* TODO: Add handling */ + { + uint64_t cookie = regpair_to_uint64(call->rpc_params[0], + call->rpc_params[1]); + free_shm_rpc(ctx, cookie); break; + } case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: break; case OPTEE_SMC_RPC_FUNC_CMD: @@ -720,6 +860,7 @@ static bool optee_handle_call(struct cpu_user_regs *regs) OPTEE_CLIENT_ID(current->domain), &resp); set_user_reg(regs, 0, resp.a0); if ( resp.a0 == OPTEE_SMC_RETURN_OK ) { + free_shm_rpc(ctx, regpair_to_uint64(resp.a1, resp.a2)); set_user_reg(regs, 1, resp.a1); set_user_reg(regs, 2, resp.a2); }
Hi Volodymyr,
On 21/05/2019 22:26, Volodymyr Babchuk wrote:
OP-TEE usually uses the same idea with command buffers (see previous commit) to issue RPC requests. Problem is that initially it has no buffer, where it can write request. So the first RPC request it makes is special: it requests NW to allocate shared buffer for other RPC requests. Usually this buffer is allocated only once for every OP-TEE thread and it remains allocated all the time until guest shuts down. Guest can ask OP-TEE to disable RPC buffers caching, in this case OP-TEE will ask guest to allocate/free buffer for the each RPC.
Mediator needs to pin this buffer to make sure that page will be not free while it is shared with OP-TEE.
Life cycle of this buffer is controlled by OP-TEE. It asks guest to create buffer and it asks it to free it. So it there is not much sense to limit number of those buffers, because we already limit the number of concurrent standard calls and prevention of RPC buffer allocation will impair OP-TEE functionality.
Those buffers can be freed in two ways: either OP-TEE issues OPTEE_SMC_RPC_FUNC_FREE RPC request or guest tries to disable buffer caching by calling OPTEE_SMC_DISABLE_SHM_CACHE function. In the latter case OP-TEE will return cookie of the SHM buffer it just freed.
OP-TEE expects that this RPC buffer have size of OPTEE_MSG_NONCONTIG_PAGE_SIZE, which equals to 4096 and is aligned with the same size. So, basically it expects one 4k page from the guest. This is the same as Xen's PAGE_SIZE.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com
Acked-by: Julien Grall julien.grall@arm.com
Cheers,
Shared memory is widely used by NW (Normal World) to communicate with TAs (Trusted Applications) in OP-TEE. NW can share part of own memory with TA or with OP-TEE core, by registering it in OP-TEE, or by providing a temporal reference. Anyways, information about such memory buffers are sent to OP-TEE as a list of pages. This mechanism is described in optee_msg.h.
Mediator should step in when NW tries to share memory with OP-TEE for two reasons:
1. Do address translation from IPA to PA. 2. Pin domain pages while they are mapped into OP-TEE or TA address space, so domain can't transfer this pages to other domain or balloon out them.
Address translation is done by translate_noncontig(...) function. It allocates new buffer from domheap and then walks on guest provided list of pages, translates addresses and stores PAs into newly allocated buffer. This buffer will be provided to OP-TEE instead of original buffer from the guest. This buffer will be freed at the end of standard call.
In the same time this function pins pages and stores them in struct optee_shm_buf object. This object will live all the time, when given SHM buffer is known to OP-TEE. It will be freed after guest unregisters shared buffer. At this time pages will be unpinned.
Guest can share buffer with OP-TEE for duration for one call, or permanently, using OPTEE_MSG_CMD_REGISTER_SHM call. We need to handle both options.
Also we want to limit total size of shared buffers. As it is not possible to get limit from OP-TEE, we need to choose some arbitrary value. Currently limit is 16384 of 4K pages.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com --- All the patches to optee.c should be merged together. They were split to ease up review. But they depend heavily on each other.
Changes from v3: - Reworked pagelists storage - there is no more static storage for 5 buffers, instead structure with all data is allocated dynamically - Now this code uses domheap instead of xenheap - Various style fixes - gdprintk() fixes
Changes from v2: - Made sure that guest does not tries to register shared buffer with the same cookie twice - Fixed coding style - Use access_guest_memory_by_ipa() instead of direct memory mapping --- xen/arch/arm/tee/optee.c | 413 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 413 insertions(+)
diff --git a/xen/arch/arm/tee/optee.c b/xen/arch/arm/tee/optee.c index 175789fb00..4b41bcde9f 100644 --- a/xen/arch/arm/tee/optee.c +++ b/xen/arch/arm/tee/optee.c @@ -53,9 +53,21 @@ */ #define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006
+/* "System ran out of resources" as in GP TEE Client API Specification */ +#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C + /* Client ID 0 is reserved for the hypervisor itself */ #define OPTEE_CLIENT_ID(domain) ((domain)->domain_id + 1)
+/* + * Maximum total number of pages that guest can share with + * OP-TEE. Currently value is selected arbitrary. Actual number of + * pages depends on free heap in OP-TEE. As we can't do any + * assumptions about OP-TEE heap usage, we limit number of pages + * arbitrary. + */ +#define MAX_TOTAL_SMH_BUF_PG 16384 + #define OPTEE_KNOWN_NSEC_CAPS OPTEE_SMC_NSEC_CAP_UNIPROCESSOR #define OPTEE_KNOWN_SEC_CAPS (OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM | \ OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM | \ @@ -88,11 +100,31 @@ struct shm_rpc { uint64_t cookie; };
+/* Shared memory buffer for arbitrary data */ +struct optee_shm_buf { + struct list_head list; + uint64_t cookie; + unsigned int page_cnt; + /* + * Shadowed container for list of pages that guest tries to share + * with OP-TEE. This is not the list of pages that guest shared + * with OP-TEE, but container for list of those pages. Check + * OPTEE_MSG_ATTR_NONCONTIG definition in optee_msg.h for more + * information. + */ + struct page_info *pg_list; + unsigned int pg_list_order; + /* Pinned guest pages that are shared with OP-TEE */ + struct page_info *pages[]; +}; + /* Domain context */ struct optee_domain { struct list_head call_list; struct list_head shm_rpc_list; + struct list_head optee_shm_buf_list; atomic_t call_count; + atomic_t optee_shm_buf_pages; spinlock_t lock; };
@@ -167,7 +199,9 @@ static int optee_domain_init(struct domain *d)
INIT_LIST_HEAD(&ctx->call_list); INIT_LIST_HEAD(&ctx->shm_rpc_list); + INIT_LIST_HEAD(&ctx->optee_shm_buf_list); atomic_set(&ctx->call_count, 0); + atomic_set(&ctx->optee_shm_buf_pages, 0); spin_lock_init(&ctx->lock);
d->arch.tee = ctx; @@ -376,11 +410,142 @@ static void free_shm_rpc(struct optee_domain *ctx, uint64_t cookie) xfree(shm_rpc); }
+static struct optee_shm_buf *allocate_optee_shm_buf(struct optee_domain *ctx, + uint64_t cookie, + unsigned int pages_cnt, + struct page_info *pg_list, + unsigned int pg_list_order) +{ + struct optee_shm_buf *optee_shm_buf, *optee_shm_buf_tmp; + int old, new; + int err_code; + + do + { + old = atomic_read(&ctx->optee_shm_buf_pages); + new = old + pages_cnt; + if ( new >= MAX_TOTAL_SMH_BUF_PG ) + return ERR_PTR(-ENOMEM); + } + while ( unlikely(old != atomic_cmpxchg(&ctx->optee_shm_buf_pages, + old, new)) ); + + /* + * TODO: Guest can try to register many small buffers, thus, forcing + * XEN to allocate context for every buffer. Probably we need to + * limit not only total number of pages pinned but also number + * of buffer objects. + */ + optee_shm_buf = xzalloc_bytes(sizeof(struct optee_shm_buf) + + pages_cnt * sizeof(struct page *)); + if ( !optee_shm_buf ) + { + err_code = -ENOMEM; + goto err; + } + + optee_shm_buf->cookie = cookie; + optee_shm_buf->pg_list = pg_list; + optee_shm_buf->pg_list_order = pg_list_order; + + spin_lock(&ctx->lock); + /* Check if there is already SHM with the same cookie */ + list_for_each_entry( optee_shm_buf_tmp, &ctx->optee_shm_buf_list, list ) + { + if ( optee_shm_buf_tmp->cookie == cookie ) + { + spin_unlock(&ctx->lock); + gdprintk(XENLOG_WARNING, "Guest tries to use the same SHM buffer cookie %lx\n", + cookie); + err_code = -EINVAL; + goto err; + } + } + + list_add_tail(&optee_shm_buf->list, &ctx->optee_shm_buf_list); + spin_unlock(&ctx->lock); + + return optee_shm_buf; + +err: + xfree(optee_shm_buf); + atomic_sub(pages_cnt, &ctx->optee_shm_buf_pages); + + return ERR_PTR(err_code); +} + +static void free_pg_list(struct optee_shm_buf *optee_shm_buf) +{ + if ( optee_shm_buf->pg_list ) + { + free_domheap_pages(optee_shm_buf->pg_list, + optee_shm_buf->pg_list_order); + optee_shm_buf->pg_list = NULL; + } +} + +static void free_optee_shm_buf(struct optee_domain *ctx, uint64_t cookie) +{ + struct optee_shm_buf *optee_shm_buf; + unsigned int i; + bool found = false; + + spin_lock(&ctx->lock); + list_for_each_entry( optee_shm_buf, &ctx->optee_shm_buf_list, list ) + { + if ( optee_shm_buf->cookie == cookie ) + { + found = true; + list_del(&optee_shm_buf->list); + break; + } + } + spin_unlock(&ctx->lock); + + if ( !found ) + return; + + for ( i = 0; i < optee_shm_buf->page_cnt; i++ ) + if ( optee_shm_buf->pages[i] ) + put_page(optee_shm_buf->pages[i]); + + free_pg_list(optee_shm_buf); + + atomic_sub(optee_shm_buf->page_cnt, &ctx->optee_shm_buf_pages); + + xfree(optee_shm_buf); +} + +static void free_optee_shm_buf_pg_list(struct optee_domain *ctx, + uint64_t cookie) +{ + struct optee_shm_buf *optee_shm_buf; + bool found = false; + + spin_lock(&ctx->lock); + list_for_each_entry( optee_shm_buf, &ctx->optee_shm_buf_list, list ) + { + if ( optee_shm_buf->cookie == cookie ) + { + found = true; + break; + } + } + spin_unlock(&ctx->lock); + + if ( found ) + free_pg_list(optee_shm_buf); + else + gdprintk(XENLOG_ERR, "Can't find pagelist for SHM buffer with cookie %lx to free it\n", + cookie); +} + static int optee_relinquish_resources(struct domain *d) { struct arm_smccc_res resp; struct optee_std_call *call, *call_tmp; struct shm_rpc *shm_rpc, *shm_rpc_tmp; + struct optee_shm_buf *optee_shm_buf, *optee_shm_buf_tmp; struct optee_domain *ctx = d->arch.tee;
if ( !ctx ) @@ -407,6 +572,17 @@ static int optee_relinquish_resources(struct domain *d) if ( hypercall_preempt_check() ) return -ERESTART;
+ /* + * TODO: Guest can pin up to MAX_TOTAL_SMH_BUF_PG pages and all of + * them will be put in this loop. It is worth considering to + * check for preemption inside the loop. + */ + list_for_each_entry_safe( optee_shm_buf, optee_shm_buf_tmp, + &ctx->optee_shm_buf_list, list ) + free_optee_shm_buf(ctx, optee_shm_buf->cookie); + + if ( hypercall_preempt_check() ) + return -ERESTART; /* * Inform OP-TEE that domain is shutting down. This is * also a fast SMC call, like OPTEE_SMC_VM_CREATED, so @@ -421,6 +597,7 @@ static int optee_relinquish_resources(struct domain *d)
ASSERT(!spin_is_locked(&ctx->lock)); ASSERT(!atomic_read(&ctx->call_count)); + ASSERT(!atomic_read(&ctx->optee_shm_buf_pages)); ASSERT(list_empty(&ctx->shm_rpc_list));
XFREE(d->arch.tee); @@ -428,6 +605,186 @@ static int optee_relinquish_resources(struct domain *d) return 0; }
+#define PAGELIST_ENTRIES_PER_PAGE \ + ((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1) + +static size_t get_pages_list_size(size_t num_entries) +{ + int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE); + + return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE; +} + +static int translate_noncontig(struct optee_domain *ctx, + struct optee_std_call *call, + struct optee_msg_param *param) +{ + uint64_t size; + unsigned int offset; + unsigned int pg_count; + unsigned int order; + unsigned int idx = 0; + gfn_t gfn; + struct page_info *guest_pg, *xen_pgs; + struct optee_shm_buf *optee_shm_buf; + /* + * This is memory layout for page list. Basically list consists of 4k pages, + * every page store 511 page addresses of user buffer and page address of + * the next page of list. + * + * Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h for details. + */ + struct { + uint64_t pages_list[PAGELIST_ENTRIES_PER_PAGE]; + uint64_t next_page_data; + } *guest_data, *xen_data; + + /* Offset of user buffer withing OPTEE_MSG_NONCONTIG_PAGE_SIZE-sized page */ + offset = param->u.tmem.buf_ptr & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1); + + /* Size of the user buffer in bytes */ + size = ROUNDUP(param->u.tmem.size + offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE); + + pg_count = DIV_ROUND_UP(size, OPTEE_MSG_NONCONTIG_PAGE_SIZE); + order = get_order_from_bytes(get_pages_list_size(pg_count)); + + /* + * In the worst case we will want to allocate 33 pages, which is + * MAX_TOTAL_SMH_BUF_PG/511 rounded up. This gives order 6 or at + * most 64 pages allocated. This buffer will be freed right after + * the end of the call and there can be no more than + * max_optee_threads calls simultaneously. So in the worst case + * guest can trick us to allocate 64 * max_optee_threads pages in + * total. + */ + xen_pgs = alloc_domheap_pages(current->domain, order, 0); + if ( !xen_pgs ) + return -ENOMEM; + + optee_shm_buf = allocate_optee_shm_buf(ctx, param->u.tmem.shm_ref, + pg_count, xen_pgs, order); + if ( IS_ERR(optee_shm_buf) ) + return PTR_ERR(optee_shm_buf); + + gfn = gaddr_to_gfn(param->u.tmem.buf_ptr & + ~(OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1)); + + while ( pg_count ) + { + struct page_info *page; + + if ( idx == 0 ) + { + guest_pg = get_domain_ram_page(gfn); + if ( !guest_pg ) + return -EINVAL; + + guest_data = __map_domain_page(guest_pg); + xen_data = __map_domain_page(xen_pgs); + } + + /* + * TODO: That function can pin up to 64MB of guest memory by + * calling lookup_and_pin_guest_ram_addr() 16384 times. + * This is considered insecure and should be fixed in the future. + */ + page = get_domain_ram_page(gaddr_to_gfn(guest_data->pages_list[idx])); + if ( !page ) + goto err_unmap; + + optee_shm_buf->pages[optee_shm_buf->page_cnt++] = page; + xen_data->pages_list[idx] = page_to_maddr(page); + idx++; + + if ( idx == PAGELIST_ENTRIES_PER_PAGE ) + { + /* Roll over to the next page */ + xen_data->next_page_data = page_to_maddr(xen_pgs + 1); + xen_pgs++; + + gfn = gaddr_to_gfn(guest_data->next_page_data); + + unmap_domain_page(xen_data); + unmap_domain_page(guest_data); + put_page(guest_pg); + + idx = 0; + } + pg_count--; + } + + if ( idx ) + { + unmap_domain_page(guest_data); + unmap_domain_page(xen_data); + put_page(guest_pg); + } + param->u.tmem.buf_ptr = page_to_maddr(optee_shm_buf->pg_list) | offset; + + return 0; + +err_unmap: + unmap_domain_page(guest_data); + unmap_domain_page(xen_data); + put_page(guest_pg); + free_optee_shm_buf(ctx, optee_shm_buf->cookie); + + return -EINVAL; +} + +static int translate_params(struct optee_domain *ctx, + struct optee_std_call *call) +{ + unsigned int i; + uint32_t attr; + int ret = 0; + + for ( i = 0; i < call->xen_arg->num_params; i++ ) + { + attr = call->xen_arg->params[i].attr; + + switch ( attr & OPTEE_MSG_ATTR_TYPE_MASK ) + { + case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: + if ( attr & OPTEE_MSG_ATTR_NONCONTIG ) + { + ret = translate_noncontig(ctx, call, call->xen_arg->params + i); + if ( ret ) + goto out; + } + else + { + gdprintk(XENLOG_WARNING, "Guest tries to use old tmem arg\n"); + ret = -EINVAL; + goto out; + } + break; + case OPTEE_MSG_ATTR_TYPE_NONE: + case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: + case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT: + continue; + } + } + +out: + if ( ret ) + { + call->xen_arg->ret_origin = TEEC_ORIGIN_COMMS; + if ( ret == -ENOMEM ) + call->xen_arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + else + call->xen_arg->ret = TEEC_ERROR_BAD_PARAMETERS; + } + + return ret; +} + /* * Copy command buffer into domheap memory to: * 1) Hide translated addresses from guest @@ -549,6 +906,27 @@ static void copy_std_request_back(struct optee_domain *ctx, put_page(page); }
+ +static void free_shm_buffers(struct optee_domain *ctx, + struct optee_msg_arg *arg) +{ + unsigned int i; + + for ( i = 0; i < arg->num_params; i ++ ) + { + switch ( arg->params[i].attr & OPTEE_MSG_ATTR_TYPE_MASK ) + { + case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: + free_optee_shm_buf(ctx, arg->params[i].u.tmem.shm_ref); + break; + default: + break; + } + } +} + /* Handle RPC return from OP-TEE */ static void handle_rpc_return(struct arm_smccc_res *res, struct cpu_user_regs *regs, @@ -574,6 +952,8 @@ static void handle_rpc_return(struct arm_smccc_res *res, * If this is RPC - we need to store call context and return back to guest. * If call is complete - we need to return results with copy_std_request_back() * and then we will destroy the call context as it is not needed anymore. + * + * Shared buffers should be handled in a special way. */ static void do_call_with_arg(struct optee_domain *ctx, struct optee_std_call *call, @@ -597,6 +977,27 @@ static void do_call_with_arg(struct optee_domain *ctx, copy_std_request_back(ctx, regs, call); set_user_reg(regs, 0, res.a0);
+ switch ( call->xen_arg->cmd ) + { + case OPTEE_MSG_CMD_REGISTER_SHM: + if ( call->xen_arg->ret == 0 ) + /* OP-TEE registered buffer, we don't need pg_list anymore */ + free_optee_shm_buf_pg_list(ctx, + call->xen_arg->params[0].u.tmem.shm_ref); + else + /* OP-TEE failed to register buffer, we need to unpin guest pages */ + free_optee_shm_buf(ctx, call->xen_arg->params[0].u.tmem.shm_ref); + break; + case OPTEE_MSG_CMD_UNREGISTER_SHM: + if ( call->xen_arg->ret == 0 ) + /* Now we can unpin guest pages */ + free_optee_shm_buf(ctx, call->xen_arg->params[0].u.rmem.shm_ref); + break; + default: + /* Free any temporary shared buffers */ + free_shm_buffers(ctx, call->xen_arg); + } + put_std_call(ctx, call); free_std_call(ctx, call); } @@ -658,6 +1059,18 @@ static void handle_std_call(struct optee_domain *ctx, case OPTEE_MSG_CMD_CANCEL: case OPTEE_MSG_CMD_REGISTER_SHM: case OPTEE_MSG_CMD_UNREGISTER_SHM: + if( translate_params(ctx, call) ) + { + /* + * translate_params() sets xen_arg->ret value to non-zero. + * So, technically, SMC was successful, but there was an error + * during handling standard call encapsulated into this SMC. + */ + copy_std_request_back(ctx, regs, call); + set_user_reg(regs, 0, OPTEE_SMC_RETURN_OK); + goto err; + } + xen_addr = page_to_maddr(call->xen_arg_pg); uint64_to_regpair(&a1, &a2, xen_addr);
Hi Volodymyr,
On 21/05/2019 22:26, Volodymyr Babchuk wrote:
- while ( pg_count )
- {
struct page_info *page;
if ( idx == 0 )
{
guest_pg = get_domain_ram_page(gfn);
if ( !guest_pg )
return -EINVAL;
guest_data = __map_domain_page(guest_pg);
xen_data = __map_domain_page(xen_pgs);
}
/*
* TODO: That function can pin up to 64MB of guest memory by
* calling lookup_and_pin_guest_ram_addr() 16384 times.
I can't find the BUILD_BUG_ON(PAGE_SIZE != 4096) as I requested before.
* This is considered insecure and should be fixed in the future.
Well, it is not "considered", it is insecure as the guest would be able to delay processing other guest for some times.
So how about:
"This should be addressed before declaring OP-TEE security supported".
With that:
Acked-by: Julien Grall julien.grall@arm.com
*/
Cheers,
OP-TEE can issue multiple RPC requests. We are interested mostly in request that asks NW to allocate/free shared memory for OP-TEE needs, because mediator needs to do address translation in the same way as it was done for shared buffers registered by NW.
OP-TEE can ask NW to allocate multiple buffers during the call. We know that if OP-TEE asks for another buffer, we can free pglist for the previous one.
As mediator now accesses shared command buffer, we need to shadow it in the same way, as we shadow request buffers for STD calls. Earlier, we just passed address of this buffer to OP-TEE, but now we need to read and write to it, so it should be shadowed.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com ---
All the patches to optee.c should be merged together. They were split to ease up review. But they depend heavily on each other.
Changes from v3: - return value of access_guest_memory_by_ipa() now checked - changed how information about shared buffer is stored in call context - domheap now used instead of xenheap - various coding style fixes
Changes from v2: - Use access_guest_memory_by_ipa() instead of direct mapping --- xen/arch/arm/tee/optee.c | 229 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 222 insertions(+), 7 deletions(-)
diff --git a/xen/arch/arm/tee/optee.c b/xen/arch/arm/tee/optee.c index 4b41bcde9f..0a1684ba15 100644 --- a/xen/arch/arm/tee/optee.c +++ b/xen/arch/arm/tee/optee.c @@ -47,6 +47,9 @@ */ #define TEEC_ORIGIN_COMMS 0x00000002
+/* "Non-specific cause" as in GP TEE Client API Specification */ +#define TEEC_ERROR_GENERIC 0xFFFF0000 + /* * "Input parameters were invalid" as described * in GP TEE Client API Specification. @@ -89,6 +92,7 @@ struct optee_std_call { paddr_t guest_arg_ipa; int optee_thread_id; int rpc_op; + uint64_t rpc_data_cookie; bool in_flight; register_t rpc_params[2]; }; @@ -97,6 +101,9 @@ struct optee_std_call { struct shm_rpc { struct list_head list; struct page_info *guest_page; + struct page_info *xen_arg_pg; + struct optee_msg_arg *xen_arg; + gfn_t gfn; uint64_t cookie; };
@@ -350,10 +357,18 @@ static struct shm_rpc *allocate_and_pin_shm_rpc(struct optee_domain *ctx, if ( !shm_rpc ) return ERR_PTR(-ENOMEM);
+ shm_rpc->xen_arg_pg = alloc_domheap_page(current->domain, 0); + if ( !shm_rpc->xen_arg_pg ) + { + xfree(shm_rpc); + return ERR_PTR(-ENOMEM); + } + /* This page will be shared with OP-TEE, so we need to pin it. */ shm_rpc->guest_page = get_domain_ram_page(gfn); if ( !shm_rpc->guest_page ) goto err; + shm_rpc->gfn = gfn;
shm_rpc->cookie = cookie;
@@ -376,6 +391,8 @@ static struct shm_rpc *allocate_and_pin_shm_rpc(struct optee_domain *ctx, return shm_rpc;
err: + free_domheap_page(shm_rpc->xen_arg_pg); + if ( shm_rpc->guest_page ) put_page(shm_rpc->guest_page); xfree(shm_rpc); @@ -404,12 +421,32 @@ static void free_shm_rpc(struct optee_domain *ctx, uint64_t cookie) if ( !found ) return;
+ free_domheap_page(shm_rpc->xen_arg_pg); + ASSERT(shm_rpc->guest_page); put_page(shm_rpc->guest_page);
xfree(shm_rpc); }
+static struct shm_rpc *find_shm_rpc(struct optee_domain *ctx, uint64_t cookie) +{ + struct shm_rpc *shm_rpc; + + spin_lock(&ctx->lock); + list_for_each_entry( shm_rpc, &ctx->shm_rpc_list, list ) + { + if ( shm_rpc->cookie == cookie ) + { + spin_unlock(&ctx->lock); + return shm_rpc; + } + } + spin_unlock(&ctx->lock); + + return NULL; +} + static struct optee_shm_buf *allocate_optee_shm_buf(struct optee_domain *ctx, uint64_t cookie, unsigned int pages_cnt, @@ -928,10 +965,13 @@ static void free_shm_buffers(struct optee_domain *ctx, }
/* Handle RPC return from OP-TEE */ -static void handle_rpc_return(struct arm_smccc_res *res, - struct cpu_user_regs *regs, - struct optee_std_call *call) +static int handle_rpc_return(struct optee_domain *ctx, + struct arm_smccc_res *res, + struct cpu_user_regs *regs, + struct optee_std_call *call) { + int ret = 0; + call->rpc_op = OPTEE_SMC_RETURN_GET_RPC_FUNC(res->a0); call->rpc_params[0] = res->a1; call->rpc_params[1] = res->a2; @@ -941,6 +981,51 @@ static void handle_rpc_return(struct arm_smccc_res *res, set_user_reg(regs, 1, res->a1); set_user_reg(regs, 2, res->a2); set_user_reg(regs, 3, res->a3); + + if ( call->rpc_op == OPTEE_SMC_RPC_FUNC_CMD ) + { + /* Copy RPC request from shadowed buffer to guest */ + uint64_t cookie = regpair_to_uint64(get_user_reg(regs, 1), + get_user_reg(regs, 2)); + struct shm_rpc *shm_rpc = find_shm_rpc(ctx, cookie); + + if ( !shm_rpc ) + { + /* + * This is a very exceptional situation: OP-TEE used + * cookie for unknown shared buffer. Something is very + * wrong there. We can't even report error back to OP-TEE, + * because there is no buffer where we can write return + * code. Luckily, OP-TEE sets default error code into that + * buffer before the call, expecting that normal world + * will overwrite it with actual result. So we can just + * continue the call. + */ + gprintk(XENLOG_ERR, "Can't find SHM-RPC with cookie %lx\n", cookie); + + return -ERESTART; + } + + shm_rpc->xen_arg = __map_domain_page(shm_rpc->xen_arg_pg); + + if ( access_guest_memory_by_ipa(current->domain, + gfn_to_gaddr(shm_rpc->gfn), + shm_rpc->xen_arg, + OPTEE_MSG_GET_ARG_SIZE(shm_rpc->xen_arg->num_params), + true) ) + { + /* + * We were unable to propagate request to guest, so let's return + * back to OP-TEE. + */ + shm_rpc->xen_arg->ret = TEEC_ERROR_GENERIC; + ret = -ERESTART; + } + + unmap_domain_page(shm_rpc->xen_arg); + } + + return ret; }
/* @@ -953,6 +1038,9 @@ static void handle_rpc_return(struct arm_smccc_res *res, * If call is complete - we need to return results with copy_std_request_back() * and then we will destroy the call context as it is not needed anymore. * + * In some rare cases we can't propagate RPC request back to guest, so we will + * restart the call, telling OP-TEE that request had failed. + * * Shared buffers should be handled in a special way. */ static void do_call_with_arg(struct optee_domain *ctx, @@ -968,7 +1056,16 @@ static void do_call_with_arg(struct optee_domain *ctx,
if ( OPTEE_SMC_RETURN_IS_RPC(res.a0) ) { - handle_rpc_return(&res, regs, call); + while ( handle_rpc_return(ctx, &res, regs, call) == -ERESTART ) + { + arm_smccc_smc(res.a0, res.a1, res.a2, res.a3, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &res); + + if ( !OPTEE_SMC_RETURN_IS_RPC(res.a0) ) + break; + + } + put_std_call(ctx, call);
return; @@ -1094,6 +1191,124 @@ err: * request from OP-TEE and wished to resume the interrupted standard * call. */ +static void handle_rpc_cmd_alloc(struct optee_domain *ctx, + struct cpu_user_regs *regs, + struct optee_std_call *call, + struct shm_rpc *shm_rpc) +{ + if ( shm_rpc->xen_arg->ret || shm_rpc->xen_arg->num_params != 1 ) + return; + + if ( shm_rpc->xen_arg->params[0].attr != (OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT | + OPTEE_MSG_ATTR_NONCONTIG) ) + { + gdprintk(XENLOG_WARNING, "Invalid attrs for shared mem buffer: %lx\n", + shm_rpc->xen_arg->params[0].attr); + return; + } + + /* Free pg list for buffer */ + if ( call->rpc_data_cookie ) + free_optee_shm_buf_pg_list(ctx, call->rpc_data_cookie); + + if ( !translate_noncontig(ctx, call, &shm_rpc->xen_arg->params[0]) ) + { + call->rpc_data_cookie = + shm_rpc->xen_arg->params[0].u.tmem.shm_ref; + } + else + { + call->rpc_data_cookie = 0; + /* + * Okay, so there was problem with guest's buffer and we need + * to tell about this to OP-TEE. + */ + shm_rpc->xen_arg->ret = TEEC_ERROR_GENERIC; + shm_rpc->xen_arg->num_params = 0; + /* + * TODO: With current implementation, OP-TEE will not issue + * RPC to free this buffer. Guest and OP-TEE will be out of + * sync: guest believes that it provided buffer to OP-TEE, + * while OP-TEE thinks of opposite. Ideally, we need to + * emulate RPC with OPTEE_MSG_RPC_CMD_SHM_FREE command. + */ + gprintk(XENLOG_WARNING, + "translate_noncontig() failed, OP-TEE/guest state is out of sync.\n"); + } +} + +static void handle_rpc_cmd(struct optee_domain *ctx, struct cpu_user_regs *regs, + struct optee_std_call *call) +{ + struct shm_rpc *shm_rpc; + uint64_t cookie; + size_t arg_size; + + cookie = regpair_to_uint64(get_user_reg(regs, 1), + get_user_reg(regs, 2)); + + shm_rpc = find_shm_rpc(ctx, cookie); + + if ( !shm_rpc ) + { + gdprintk(XENLOG_ERR, "Can't find SHM-RPC with cookie %lx\n", cookie); + return; + } + + shm_rpc->xen_arg = __map_domain_page(shm_rpc->xen_arg_pg); + + /* First, copy only header to read number of arguments */ + if ( access_guest_memory_by_ipa(current->domain, + gfn_to_gaddr(shm_rpc->gfn), + shm_rpc->xen_arg, + sizeof(struct optee_msg_arg), + false) ) + { + shm_rpc->xen_arg->ret = TEEC_ERROR_GENERIC; + goto out; + } + + arg_size = OPTEE_MSG_GET_ARG_SIZE(shm_rpc->xen_arg->num_params); + if ( arg_size > OPTEE_MSG_NONCONTIG_PAGE_SIZE ) + { + shm_rpc->xen_arg->ret = TEEC_ERROR_GENERIC; + goto out; + } + + /* Read the whole command structure */ + if ( access_guest_memory_by_ipa(current->domain, gfn_to_gaddr(shm_rpc->gfn), + shm_rpc->xen_arg, arg_size, false) ) + { + shm_rpc->xen_arg->ret = TEEC_ERROR_GENERIC; + goto out; + } + + switch (shm_rpc->xen_arg->cmd) + { + case OPTEE_MSG_RPC_CMD_GET_TIME: + case OPTEE_MSG_RPC_CMD_WAIT_QUEUE: + case OPTEE_MSG_RPC_CMD_SUSPEND: + break; + case OPTEE_MSG_RPC_CMD_SHM_ALLOC: + handle_rpc_cmd_alloc(ctx, regs, call, shm_rpc); + break; + case OPTEE_MSG_RPC_CMD_SHM_FREE: + free_optee_shm_buf(ctx, shm_rpc->xen_arg->params[0].u.value.b); + if ( call->rpc_data_cookie == shm_rpc->xen_arg->params[0].u.value.b ) + call->rpc_data_cookie = 0; + break; + default: + break; + } + +out: + unmap_domain_page(shm_rpc->xen_arg); + + do_call_with_arg(ctx, call, regs, OPTEE_SMC_CALL_RETURN_FROM_RPC, 0, 0, + get_user_reg(regs, 3), 0, 0); + +} + static void handle_rpc_func_alloc(struct optee_domain *ctx, struct cpu_user_regs *regs, struct optee_std_call *call) @@ -1125,7 +1340,7 @@ static void handle_rpc_func_alloc(struct optee_domain *ctx, ptr = 0; } else - ptr = page_to_maddr(shm_rpc->guest_page); + ptr = page_to_maddr(shm_rpc->xen_arg_pg);
out: uint64_to_regpair(&r1, &r2, ptr); @@ -1171,8 +1386,8 @@ static void handle_rpc(struct optee_domain *ctx, struct cpu_user_regs *regs) case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: break; case OPTEE_SMC_RPC_FUNC_CMD: - /* TODO: Add handling */ - break; + handle_rpc_cmd(ctx, regs, call); + return; }
do_call_with_arg(ctx, call, regs, OPTEE_SMC_CALL_RETURN_FROM_RPC,
Hi Volodymyr,
On 21/05/2019 22:26, Volodymyr Babchuk wrote:
OP-TEE can issue multiple RPC requests. We are interested mostly in request that asks NW to allocate/free shared memory for OP-TEE needs, because mediator needs to do address translation in the same way as it was done for shared buffers registered by NW.
OP-TEE can ask NW to allocate multiple buffers during the call. We know that if OP-TEE asks for another buffer, we can free pglist for the previous one.
As mediator now accesses shared command buffer, we need to shadow it in the same way, as we shadow request buffers for STD calls. Earlier, we just passed address of this buffer to OP-TEE, but now we need to read and write to it, so it should be shadowed.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com
Acked-by: Julien Grall julien.grall@arm.com
Cheers,
This enumeration controls TEE type for a domain. Currently there is two possible options: either 'none' or 'optee'.
'none' is the default value and it basically disables TEE support at all.
'native' enables access to a "real" OP-TEE installed on a platform.
It is possible to add another types in the future.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com ---
All the patches to optee.c should be merged together. They were split to ease up review. But they depend heavily on each other.
Changes from v4: - "native" option was replaced with "optee" - "tee" property was moved from arch-specific section to the global one. Documentation moved inside "Devices" section.
Changes from v3: - tee_enabled renamed to tee_type. Currently two types are supported as described in the commit message - Add LIBXL_HAVE_BUILDINFO_ARCH_ARM_TEE definition
Changes from v2: - Use arch.tee_enabled instead of separate domctl --- docs/man/xl.cfg.5.pod.in | 19 +++++++++++++++++++ tools/libxl/libxl.h | 5 +++++ tools/libxl/libxl_arm.c | 13 +++++++++++++ tools/libxl/libxl_types.idl | 6 ++++++ tools/xl/xl_parse.c | 9 +++++++++ 5 files changed, 52 insertions(+)
diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in index c7d70e618b..73c64dc896 100644 --- a/docs/man/xl.cfg.5.pod.in +++ b/docs/man/xl.cfg.5.pod.in @@ -1544,6 +1544,25 @@ Set maximum height for pointer device.
=back
+=item B<tee=["none", "optee"]> + +Set TEE type for the guest. TEE is a Trusted Execution Environment -- separate +secuse OS found on some platforms. + +=over 4 + +=item B<"none"> + +Disable TEE support at all. This is the default value. + +=item B<"optee"> + +Allow guest to access to OP-TEE enabled on the platform. Guest will not be created +if platform does not have OP-TEE with virtualization feature or if OP-TEE will +deny access. + +=back + =back
=head2 Paravirtualised (PV) Guest Specific Options diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h index 482499a6c0..294a92f645 100644 --- a/tools/libxl/libxl.h +++ b/tools/libxl/libxl.h @@ -273,6 +273,11 @@ */ #define LIBXL_HAVE_BUILDINFO_ARM_GIC_VERSION 1
+/* + * libxl_domain_build_info has the arch_arm.tee field. + */ +#define LIBXL_HAVE_BUILDINFO_ARCH_ARM_TEE 1 + /* * LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing * 'soft reset' for domains and there is 'soft_reset' shutdown reason diff --git a/tools/libxl/libxl_arm.c b/tools/libxl/libxl_arm.c index 141e159043..6b72c00960 100644 --- a/tools/libxl/libxl_arm.c +++ b/tools/libxl/libxl_arm.c @@ -89,6 +89,19 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc, return ERROR_FAIL; }
+ switch (d_config->b_info.tee) { + case LIBXL_TEE_TYPE_NONE: + config->arch.tee_type = XEN_DOMCTL_CONFIG_TEE_NONE; + break; + case LIBXL_TEE_TYPE_OPTEE: + config->arch.tee_type = XEN_DOMCTL_CONFIG_TEE_OPTEE; + break; + default: + LOG(ERROR, "Unknown TEE type %d", + d_config->b_info.tee); + return ERROR_FAIL; + } + return 0; }
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl index cb4702fd7a..4eaccd2cc7 100644 --- a/tools/libxl/libxl_types.idl +++ b/tools/libxl/libxl_types.idl @@ -460,6 +460,11 @@ libxl_gic_version = Enumeration("gic_version", [ (0x30, "v3") ], init_val = "LIBXL_GIC_VERSION_DEFAULT")
+libxl_tee_type = Enumeration("tee_type", [ + (0, "none"), + (1, "optee") + ], init_val = "LIBXL_TEE_TYPE_NONE") + libxl_rdm_reserve = Struct("rdm_reserve", [ ("strategy", libxl_rdm_reserve_strategy), ("policy", libxl_rdm_reserve_policy), @@ -537,6 +542,7 @@ libxl_domain_build_info = Struct("domain_build_info",[ ("nested_hvm", libxl_defbool), ("apic", libxl_defbool), ("dm_restrict", libxl_defbool), + ("tee", libxl_tee_type), ("u", KeyedUnion(None, libxl_domain_type, "type", [("hvm", Struct(None, [("firmware", string), ("bios", libxl_bios_type), diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c index 352cd214dd..d98ad0cffb 100644 --- a/tools/xl/xl_parse.c +++ b/tools/xl/xl_parse.c @@ -2690,6 +2690,15 @@ skip_usbdev: } }
+ if (!xlu_cfg_get_string (config, "tee", &buf, 1)) { + e = libxl_tee_type_from_string(buf, &b_info->tee); + if (e) { + fprintf(stderr, + "Unknown tee "%s" specified\n", buf); + exit(-ERROR_FAIL); + } + } + parse_vkb_list(config, d_config);
xlu_cfg_destroy(config);
Hi Volodymyr,
Some comment on the documentation, the rest looks good to me.
On 21/05/2019 22:26, Volodymyr Babchuk wrote:
diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in index c7d70e618b..73c64dc896 100644 --- a/docs/man/xl.cfg.5.pod.in +++ b/docs/man/xl.cfg.5.pod.in @@ -1544,6 +1544,25 @@ Set maximum height for pointer device. =back +=item B<tee=["none", "optee"]>
This will become quite difficult to read if we add more TEE. How about:
<tee="STRING">?
+Set TEE type for the guest. TEE is a Trusted Execution Environment -- separate +secuse OS found on some platforms.
s/secuse/secure/
I would also mention this is Arm only so far. Maybe:
B<Arm only> Set TEE...
+=over 4
+=item B<"none">
+Disable TEE support at all. This is the default value.
+=item B<"optee">
+Allow guest to access to OP-TEE enabled on the platform. Guest will not be created +if platform does not have OP-TEE with virtualization feature or if OP-TEE will +deny access.
I have some trouble to read this paragraph. How about:
"Allow a guest to use OP-TEE. Note that a virtualization-aware OP-TEE is required for this.".
+=back
- =back
=head2 Paravirtualised (PV) Guest Specific Options
Cheers,
Volodymyr Babchuk writes ("[Xen-devel] [PATCH v5 09/10] tools/arm: tee: add "tee" option for xl.cfg"):
This enumeration controls TEE type for a domain. Currently there is two possible options: either 'none' or 'optee'.
'none' is the default value and it basically disables TEE support at all.
'native' enables access to a "real" OP-TEE installed on a platform.
It is possible to add another types in the future.
Could improve this bit maybe ?
+=item B<"optee">
+Allow guest to access to OP-TEE enabled on the platform. Guest will not be created +if platform does not have OP-TEE with virtualization feature or if OP-TEE will +deny access.
To me (who doesn't really understand this stuff very well) this doesn't answer a very important question: if I enable this, what (if any) host/machine/&c resources will this grant the guest access to ?
It sounds like the the answer should be "none", because if I search for "op-tee" online I would get the impression that it is an emulator. Normally granting access to an emulator does not grant access to host resources.
But in this series you talk about it being a mediator so I suspect that is not right.
Ian.
If TEE support is enabled with "tee=optee" option in xl.cfg, then we need to inform guest about available TEE, by creating corresponding node in the guest's device tree.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com Reviewed-by: Julien Grall julien.grall@arm.com
--- This patch depends on patches to optee.c.
Changes from v4: - "native" option replaced with "optee"
Changes from v3: - "smc" method replaced with "hvc" - Coding style fixes --- tools/libxl/libxl_arm.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
diff --git a/tools/libxl/libxl_arm.c b/tools/libxl/libxl_arm.c index 6b72c00960..bf31b9b3ca 100644 --- a/tools/libxl/libxl_arm.c +++ b/tools/libxl/libxl_arm.c @@ -420,6 +420,32 @@ static int make_psci_node(libxl__gc *gc, void *fdt) return 0; }
+static int make_optee_node(libxl__gc *gc, void *fdt) +{ + int res; + LOG(DEBUG, "Creating OP-TEE node in dtb"); + + res = fdt_begin_node(fdt, "firmware"); + if (res) return res; + + res = fdt_begin_node(fdt, "optee"); + if (res) return res; + + res = fdt_property_compat(gc, fdt, 1, "linaro,optee-tz"); + if (res) return res; + + res = fdt_property_string(fdt, "method", "hvc"); + if (res) return res; + + res = fdt_end_node(fdt); + if (res) return res; + + res = fdt_end_node(fdt); + if (res) return res; + + return 0; +} + static int make_memory_nodes(libxl__gc *gc, void *fdt, const struct xc_dom_image *dom) { @@ -933,6 +959,9 @@ next_resize: if (info->arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) FDT( make_vpl011_uart_node(gc, fdt, ainfo, dom) );
+ if (info->tee == LIBXL_TEE_TYPE_OPTEE) + FDT( make_optee_node(gc, fdt) ); + if (pfdt) FDT( copy_partial_fdt(gc, fdt, pfdt) );
Volodymyr Babchuk writes ("[Xen-devel] [PATCH v5 10/10] tools/arm: optee: create optee firmware node in DT if tee=optee"):
If TEE support is enabled with "tee=optee" option in xl.cfg, then we need to inform guest about available TEE, by creating corresponding node in the guest's device tree.
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com Reviewed-by: Julien Grall julien.grall@arm.com
Acked-by: Ian Jackson ian.jackson@eu.citrix.com
(relying on Julien's review for the actual DT details).
Looking at this, it is not clear to me why the fdt parameter to all these functions is a void*. Why does libfdt not introduce a struct or something ? (This is by no means a blocker for this patch.)
Thanks, Ian.
Hi Volodymyr,
I tried to apply the patches to staging but fail because the patches contains =20. Do you have a tree with the series applied?
Cheers,
On 5/21/19 10:25 PM, Volodymyr Babchuk wrote:
Hello community,
This is the fifth version of OP-TEE mediator in XEN.
OP-TEE 3.5.0 was released when I worked on this version of the pathes. This is the first release where virtualization support is available. This release includes both original virtualization patches and new SMC that retrevies number of threads from OP-TEE.
Many thanks to both OP-TEE and Xen communities for reviewing and helping with my changes to the both projects.
Global changes from v4:
- Substantial rework of OP-TEE mediator. Now it tries to return meaningful error codes back to the guest.
- OP-TEE mediator does not use struct cpu_user_regs as a storage for parameters and return values when calling OP-TEE. This makes it compatbile with requirement from SMCCC.
- tee=native option replaced with tee=optee
- Authorship and s-o-b tag reset to my EPAM mail address
Overall changes from v3:
- Patch "arm: add tee_enabled flag to xen_arch_domainconfig" was squashed into "xen/arm: add generic TEE mediator framework"
- I implemented more elaborate error repoting to a guest. Now guest will get meaningful error codes instead of generic ARM_SMCCC_ERR_UNKNOWN_FUNCTION.
Per-patch changes are described in corresponding emails.
Changes from v2:
- Use domain flags insted of domctl interface to enable optee for guests
- Remove patch "libxc: add xc_dom_tee_enable(...) function" because of previous change
- Mediator now stores own context in arch part of struct domain, so I removed patch "optee: add domain contexts"
Per-patch changes are described in corresponding emails.
==== v2:
This is v2 of patch series for OP-TEE mediator support in XEN. Changes from v1:
- Added domctl interface, so now xl decides what domain should work with TEE
- Removed XSM support due to change described above
- Patch with OP-TEE mediator was splited to 7 separate patches
- Removed patch with call_smccc() function. Now this series depend on Julien Grall's series "xen/arm: SMCCC fixup and improvement" [3]
===== v1:
This is follow for patch series [1]. There was lots of discussions for that series and I tried to address all of them in this new patchset.
Currently, I had a working solution for OP-TEE virtualization and it is being upstreamed right now ([2]). So, I think it is a good time to introduce support in XEN as well.
This series include generic TEE mediator framework and full-scale OP-TEE mediator which is working with mentioned chages in OP-TEE. So, multiple domains can work simultaneously with OP-TEE.
I added XSM support, so now it is possible to control which domains can work with TEEs. Also I changed way how TEE discovery is done. Now it is very generic and should support any platform.
[1] https://lists.xenproject.org/archives/html/xen-devel/2017-10/msg01451.html [2] https://github.com/OP-TEE/optee_os/pull/2370 [3] https://lists.xenproject.org/archives/html/xen-devel/2018-08/msg02138.html
Volodymyr Babchuk (10): xen/arm: add generic TEE mediator framework xen/arm: optee: add OP-TEE header files xen/arm: optee: add OP-TEE mediator skeleton xen/arm: optee: add fast calls handling xen/arm: optee: add std call handling xen/arm: optee: add support for RPC SHM buffers xen/arm: optee: add support for arbitrary shared memory xen/arm: optee: add support for RPC commands tools/arm: tee: add "tee" option for xl.cfg tools/arm: optee: create optee firmware node in DT if tee=optee
MAINTAINERS | 6 + docs/man/xl.cfg.5.pod.in | 19 + tools/libxl/libxl.h | 5 + tools/libxl/libxl_arm.c | 42 + tools/libxl/libxl_types.idl | 6 + tools/xl/xl_parse.c | 9 + xen/arch/arm/Kconfig | 9 + xen/arch/arm/Makefile | 1 + xen/arch/arm/domain.c | 19 + xen/arch/arm/setup.c | 2 + xen/arch/arm/tee/Kconfig | 4 + xen/arch/arm/tee/Makefile | 2 + xen/arch/arm/tee/optee.c | 1536 +++++++++++++++++++++++++++ xen/arch/arm/tee/tee.c | 93 ++ xen/arch/arm/vsmc.c | 5 + xen/arch/arm/xen.lds.S | 7 + xen/include/asm-arm/domain.h | 4 + xen/include/asm-arm/tee/optee_msg.h | 444 ++++++++ xen/include/asm-arm/tee/optee_smc.h | 556 ++++++++++ xen/include/asm-arm/tee/tee.h | 109 ++ xen/include/public/arch-arm.h | 4 + 21 files changed, 2882 insertions(+) create mode 100644 xen/arch/arm/tee/Kconfig create mode 100644 xen/arch/arm/tee/Makefile create mode 100644 xen/arch/arm/tee/optee.c create mode 100644 xen/arch/arm/tee/tee.c create mode 100644 xen/include/asm-arm/tee/optee_msg.h create mode 100644 xen/include/asm-arm/tee/optee_smc.h create mode 100644 xen/include/asm-arm/tee/tee.h
Hi Julien,
Julien Grall writes:
Hi Volodymyr,
I tried to apply the patches to staging but fail because the patches contains =20. Do you have a tree with the series applied?
Sure, you can find them at [1]
[1] https://github.com/lorc/xen/tree/optee_v5
On 6/1/19 5:07 PM, Volodymyr Babchuk wrote:
Hi Julien,
Hi Volodymyr,
Julien Grall writes:
Hi Volodymyr,
I tried to apply the patches to staging but fail because the patches contains =20. Do you have a tree with the series applied?
Sure, you can find them at [1]
Thank you! The branch is based on master. This is fairly behind staging. Could you rebase this on top of staging?
I will go through the series next week. As this is a tech preview, I am planning to merge this version unless I find something horribly wrong in it.
Cheers,
Hello Julien,
Julien Grall writes:
On 6/1/19 5:07 PM, Volodymyr Babchuk wrote:
Hi Julien,
Hi Volodymyr,
Julien Grall writes:
Hi Volodymyr,
I tried to apply the patches to staging but fail because the patches contains =20. Do you have a tree with the series applied?
Sure, you can find them at [1]
Thank you! The branch is based on master. This is fairly behind staging. Could you rebase this on top of staging?
I will go through the series next week. As this is a tech preview, I am planning to merge this version unless I find something horribly wrong in it.
Thank you for review. I really appreciate this.
I just want to clarify, what I should do next. If I got you right, I should send v6, rebased on staging and with your comments addressed. Is this right?
-- Best regards,Volodymyr Babchuk
Hi Volodymyr,
On 6/4/19 2:31 PM, Volodymyr Babchuk wrote:
Hello Julien,
Julien Grall writes:
On 6/1/19 5:07 PM, Volodymyr Babchuk wrote:
Hi Julien,
Hi Volodymyr,
Julien Grall writes:
Hi Volodymyr,
I tried to apply the patches to staging but fail because the patches contains =20. Do you have a tree with the series applied?
Sure, you can find them at [1]
Thank you! The branch is based on master. This is fairly behind staging. Could you rebase this on top of staging?
I will go through the series next week. As this is a tech preview, I am planning to merge this version unless I find something horribly wrong in it.
Thank you for review. I really appreciate this.
I just want to clarify, what I should do next. If I got you right, I should send v6, rebased on staging and with your comments addressed. Is this right?
Yes please. Can you collect the acks at the same time? Let me know if there are any major changes on the rebase on staging.
Cheers,