Hello community,
This is the next version of OP-TEE support series.
In case of any issues with mail (Julien Grall had some troubles with =20 sequences in the patches, thanks to our corporate Exchange, I assume), this series can be pulled from [4].
Note: I deliberately removed Jan Beulich from CC list, because he explicitly indicated that he is not interested in this series ([5]).
List of changes is below. More specific changes are described along with the corresponding patches.
=== v5: - Series rebased to staging branch instead of master one. - OP-TEE protocol headers was taken from OP-TEE tree instead of Linux one - Added acked-by tags - Fixed (and tested) issue when XEN would not boot if it is build with CONFIG_TEE=n
==== 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
==== 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.
==== 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 [4] https://github.com/lorc/xen/tree/optee_v6 [5] https://lists.xenproject.org/archives/html/xen-devel/2019-05/msg01805.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 | 21 + 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 | 1540 +++++++++++++++++++++++ xen/arch/arm/tee/tee.c | 98 ++ 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 | 310 +++++ xen/include/asm-arm/tee/optee_rpc_cmd.h | 318 +++++ xen/include/asm-arm/tee/optee_smc.h | 564 +++++++++ xen/include/asm-arm/tee/tee.h | 112 ++ xen/include/public/arch-arm.h | 6 + 22 files changed, 3090 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_rpc_cmd.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 Reviewed-by: Julien Grall julien.grall@arm.com
--- Changes from v5: - Fixed bug, when XEN won't boot with CONFIG_TEE=n - Fixed coding style - Added __read_mostly attribute to *cur_mediator variable
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 | 98 +++++++++++++++++++++++++++++ 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 | 112 ++++++++++++++++++++++++++++++++++ xen/include/public/arch-arm.h | 5 ++ 12 files changed, 263 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 6fbdc2bdcb..ab32e7f409 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -381,6 +381,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 wl@xen.org diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index 585b57f023..caaf377a33 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -106,6 +106,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 ad1b106bd7..d27a137f7a 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> @@ -647,6 +648,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; }
@@ -704,6 +711,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);
/* @@ -948,6 +958,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 5af49c7a08..1643f91f1c 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>
@@ -894,6 +895,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..3964a8a5cd --- /dev/null +++ b/xen/arch/arm/tee/tee.c @@ -0,0 +1,98 @@ +/* + * 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 __read_mostly *cur_mediator; + +/* + * TODO: Add function to alter Dom0 DTB, so we can properly describe + * present TEE. + */ + +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..f483986385 --- /dev/null +++ b/xen/include/asm-arm/tee/tee.h @@ -0,0 +1,112 @@ +/* + * 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) +{ + if ( likely(tee_type == XEN_DOMCTL_CONFIG_TEE_NONE) ) + return 0; + + 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..bb69c380ec 100644 --- a/xen/include/public/arch-arm.h +++ b/xen/include/public/arch-arm.h @@ -304,10 +304,15 @@ 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 Tue, 11 Jun 2019, Volodymyr Babchuk wrote:
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 Reviewed-by: Julien Grall julien.grall@arm.com
Acked-by: Stefano Stabellini sstabellini@kernel.org
Changes from v5:
- Fixed bug, when XEN won't boot with CONFIG_TEE=n
- Fixed coding style
- Added __read_mostly attribute to *cur_mediator variable
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 | 98 +++++++++++++++++++++++++++++ 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 | 112 ++++++++++++++++++++++++++++++++++ xen/include/public/arch-arm.h | 5 ++ 12 files changed, 263 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 6fbdc2bdcb..ab32e7f409 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -381,6 +381,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 wl@xen.org diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index 585b57f023..caaf377a33 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -106,6 +106,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 ad1b106bd7..d27a137f7a 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> @@ -647,6 +648,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;
} @@ -704,6 +711,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);
/* @@ -948,6 +958,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 5af49c7a08..1643f91f1c 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> @@ -894,6 +895,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..3964a8a5cd --- /dev/null +++ b/xen/arch/arm/tee/tee.c @@ -0,0 +1,98 @@ +/*
- 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 __read_mostly *cur_mediator;
+/*
- TODO: Add function to alter Dom0 DTB, so we can properly describe
- present TEE.
- */
+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..f483986385 --- /dev/null +++ b/xen/include/asm-arm/tee/tee.h @@ -0,0 +1,112 @@ +/*
- 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) +{
- if ( likely(tee_type == XEN_DOMCTL_CONFIG_TEE_NONE) )
return 0;
- 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..bb69c380ec 100644 --- a/xen/include/public/arch-arm.h +++ b/xen/include/public/arch-arm.h @@ -304,10 +304,15 @@ 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
-- 2.21.0
This header files describes protocol between OP-TEE OS and OP-TEE clients, which are running in Normal World. This headers 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 OP-TEE OS 3.5.0 tree and mangled a bit to compile with XEN.
Location of the files in the original tree:
core/include/optee_msg.h core/include/optee_rpc_cmd.h core/arch/arm/include/sm/optee_smc.h
[OP-TEE commit id 5df2a985b2ffd0b6f1107f12ca2a88203bf31328]
Signed-off-by: Volodymyr Babchuk volodymyr_babchuk@epam.com
--- Changes from v5: - Used optee_os headers instead of linux ones
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 | 310 +++++++++++++ xen/include/asm-arm/tee/optee_rpc_cmd.h | 318 +++++++++++++ xen/include/asm-arm/tee/optee_smc.h | 564 ++++++++++++++++++++++++ 3 files changed, 1192 insertions(+) create mode 100644 xen/include/asm-arm/tee/optee_msg.h create mode 100644 xen/include/asm-arm/tee/optee_rpc_cmd.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..fe743dbde3 --- /dev/null +++ b/xen/include/asm-arm/tee/optee_msg.h @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2015-2017, Linaro Limited + */ +#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. + */ + +/***************************************************************************** + * 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, UL) + +/* + * 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 of optee_msg_param.u.tmem.buf_ptr should hold page + * offset of 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, UL) + +/* + * 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 + +#ifndef ASM +/** + * 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 { + uint64_t buf_ptr; + uint64_t size; + uint64_t 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 { + uint64_t offs; + uint64_t size; + uint64_t shm_ref; +}; + +/** + * struct optee_msg_param_value - values + * @a: first value + * @b: second value + * @c: third value + */ +struct optee_msg_param_value { + uint64_t a; + uint64_t b; + uint64_t c; +}; + +/** + * struct optee_msg_param - parameter + * @attr: attributes + * @memref: a memory reference + * @value: a 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 { + uint64_t 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 fields hold 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 have to come first. + */ +struct optee_msg_arg { + uint32_t cmd; + uint32_t func; + uint32_t session; + uint32_t cancel_id; + uint32_t pad; + uint32_t ret; + uint32_t ret_origin; + uint32_t num_params; + + /* num_params tells the actual number of element in params */ + struct optee_msg_param params[]; +}; + +/** + * 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)) + +/* + * Defines the maximum value of @num_params that can be passed to + * OPTEE_MSG_GET_ARG_SIZE without a risk of crossing page boundary. + */ +#define OPTEE_MSG_MAX_NUM_PARAMS \ + ((OPTEE_MSG_NONCONTIG_PAGE_SIZE - sizeof(struct optee_msg_arg)) / \ + sizeof(struct optee_msg_param)) + +#endif /*ASM*/ + +/***************************************************************************** + * 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_NONCONTIG] + * [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 + * + * 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 + +#endif /* _OPTEE_MSG_H */ diff --git a/xen/include/asm-arm/tee/optee_rpc_cmd.h b/xen/include/asm-arm/tee/optee_rpc_cmd.h new file mode 100644 index 0000000000..d6b9dfe30c --- /dev/null +++ b/xen/include/asm-arm/tee/optee_rpc_cmd.h @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2016-2017, Linaro Limited + */ + +#ifndef __OPTEE_RPC_CMD_H +#define __OPTEE_RPC_CMD_H + +/* + * All RPC is done with a struct optee_msg_arg as bearer of information, + * struct optee_msg_arg::arg holds values defined by OPTEE_RPC_CMD_* below. + * Only the commands handled by the kernel driver are defined here. + * + * RPC communication with tee-supplicant is reversed compared to normal + * client communication described above. The supplicant receives requests + * and sends responses. + */ + +/* + * Load a TA into memory + * + * Since the size of the TA isn't known in advance the size of the TA is + * can be queried with a NULL buffer. + * + * [in] value[0].a-b UUID + * [out] memref[1] Buffer with TA + */ +#define OPTEE_RPC_CMD_LOAD_TA 0 + +/* + * Replay Protected Memory Block access + * + * [in] memref[0] Frames to device + * [out] memref[1] Frames from device + */ +#define OPTEE_RPC_CMD_RPMB 1 + +/* + * File system access, see definition of protocol below + */ +#define OPTEE_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] value[0].a Number of seconds + * [out] value[0].b Number of nano seconds. + */ +#define OPTEE_RPC_CMD_GET_TIME 3 + +/* + * Wait queue primitive, helper for secure world to implement a wait queue. + * + * If secure world needs 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] value[0].a OPTEE_RPC_WAIT_QUEUE_SLEEP + * [in] value[0].b Wait key + * + * Waking up a key + * [in] value[0].a OPTEE_RPC_WAIT_QUEUE_WAKEUP + * [in] value[0].b Wakeup key + */ +#define OPTEE_RPC_CMD_WAIT_QUEUE 4 +#define OPTEE_RPC_WAIT_QUEUE_SLEEP 0 +#define OPTEE_RPC_WAIT_QUEUE_WAKEUP 1 + +/* + * Suspend execution + * + * [in] value[0].a Number of milliseconds to suspend + */ +#define OPTEE_RPC_CMD_SUSPEND 5 + +/* + * Allocate a piece of shared memory + * + * [in] value[0].a Type of memory one of + * OPTEE_RPC_SHM_TYPE_* below + * [in] value[0].b Requested size + * [in] value[0].c Required alignment + * [out] memref[0] Buffer + */ +#define OPTEE_RPC_CMD_SHM_ALLOC 6 +/* Memory that can be shared with a non-secure user space application */ +#define OPTEE_RPC_SHM_TYPE_APPL 0 +/* Memory only shared with non-secure kernel */ +#define OPTEE_RPC_SHM_TYPE_KERNEL 1 +/* + * Memory shared with non-secure kernel and exported to a non-secure user + * space application + */ +#define OPTEE_RPC_SHM_TYPE_GLOBAL 2 + +/* + * Free shared memory previously allocated with OPTEE_RPC_CMD_SHM_ALLOC + * + * [in] value[0].a Type of memory one of + * OPTEE_RPC_SHM_TYPE_* above + * [in] value[0].b Value of shared memory reference or cookie + */ +#define OPTEE_RPC_CMD_SHM_FREE 7 + +/* Was OPTEE_RPC_CMD_SQL_FS, which isn't supported any longer */ +#define OPTEE_RPC_CMD_SQL_FS_RESERVED 8 + +/* + * Send TA profiling information to normal world + * + * [in/out] value[0].a File identifier. Must be set to 0 on + * first call. A value >= 1 will be + * returned on success. Re-use this value + * to append data to the same file. + * [in] memref[1] TA UUID + * [in] memref[2] Profile data + */ +#define OPTEE_RPC_CMD_GPROF 9 + +/* + * Socket command, see definition of protocol below + */ +#define OPTEE_RPC_CMD_SOCKET 10 + +/* + * Register timestamp buffer in the linux kernel optee driver + * + * [in] value[0].a Subcommand (register buffer, unregister buffer) + * [in] value[0].b Physical address of timestamp buffer + * [in] value[0].c Size of buffer + */ +#define OPTEE_RPC_CMD_BENCH_REG 20 + +/* + * Definition of protocol for command OPTEE_RPC_CMD_FS + */ + +/* + * Open a file + * + * [in] value[0].a OPTEE_RPC_FS_OPEN + * [in] memref[1] A string holding the file name + * [out] value[2].a File descriptor of open file + */ +#define OPTEE_RPC_FS_OPEN 0 + +/* + * Create a file + * + * [in] value[0].a OPTEE_RPC_FS_CREATE + * [in] memref[1] A string holding the file name + * [out] value[2].a File descriptor of open file + */ +#define OPTEE_RPC_FS_CREATE 1 + +/* + * Close a file + * + * [in] value[0].a OPTEE_RPC_FS_CLOSE + * [in] value[0].b File descriptor of open file. + */ +#define OPTEE_RPC_FS_CLOSE 2 + +/* + * Read from a file + * + * [in] value[0].a OPTEE_RPC_FS_READ + * [in] value[0].b File descriptor of open file + * [in] value[0].c Offset into file + * [out] memref[1] Buffer to hold returned data + */ +#define OPTEE_RPC_FS_READ 3 + +/* + * Write to a file + * + * [in] value[0].a OPTEE_RPC_FS_WRITE + * [in] value[0].b File descriptor of open file + * [in] value[0].c Offset into file + * [in] memref[1] Buffer holding data to be written + */ +#define OPTEE_RPC_FS_WRITE 4 + +/* + * Truncate a file + * + * [in] value[0].a OPTEE_RPC_FS_TRUNCATE + * [in] value[0].b File descriptor of open file + * [in] value[0].c Length of file. + */ +#define OPTEE_RPC_FS_TRUNCATE 5 + +/* + * Remove a file + * + * [in] value[0].a OPTEE_RPC_FS_REMOVE + * [in] memref[1] A string holding the file name + */ +#define OPTEE_RPC_FS_REMOVE 6 + +/* + * Rename a file + * + * [in] value[0].a OPTEE_RPC_FS_RENAME + * [in] value[0].b True if existing target should be removed + * [in] memref[1] A string holding the old file name + * [in] memref[2] A string holding the new file name + */ +#define OPTEE_RPC_FS_RENAME 7 + +/* + * Opens a directory for file listing + * + * [in] value[0].a OPTEE_RPC_FS_OPENDIR + * [in] memref[1] A string holding the name of the directory + * [out] value[2].a Handle to open directory + */ +#define OPTEE_RPC_FS_OPENDIR 8 + +/* + * Closes a directory handle + * + * [in] value[0].a OPTEE_RPC_FS_CLOSEDIR + * [in] value[0].b Handle to open directory + */ +#define OPTEE_RPC_FS_CLOSEDIR 9 + +/* + * Read next file name of directory + * + * + * [in] value[0].a OPTEE_RPC_FS_READDIR + * [in] value[0].b Handle to open directory + * [out] memref[1] A string holding the file name + */ +#define OPTEE_RPC_FS_READDIR 10 + +/* End of definition of protocol for command OPTEE_RPC_CMD_FS */ + +/* + * Definition of protocol for command OPTEE_RPC_CMD_SOCKET + */ + +#define OPTEE_RPC_SOCKET_TIMEOUT_NONBLOCKING 0 +#define OPTEE_RPC_SOCKET_TIMEOUT_BLOCKING 0xffffffff + +/* + * Open socket + * + * [in] value[0].a OPTEE_RPC_SOCKET_OPEN + * [in] value[0].b TA instance id + * [in] value[1].a Server port number + * [in] value[1].b Protocol, TEE_ISOCKET_PROTOCOLID_* + * [in] value[1].c Ip version TEE_IP_VERSION_* from tee_ipsocket.h + * [in] memref[2] Server address + * [out] value[3].a Socket handle (32-bit) + */ +#define OPTEE_RPC_SOCKET_OPEN 0 + +/* + * Close socket + * + * [in] value[0].a OPTEE_RPC_SOCKET_CLOSE + * [in] value[0].b TA instance id + * [in] value[0].c Socket handle + */ +#define OPTEE_RPC_SOCKET_CLOSE 1 + +/* + * Close all sockets + * + * [in] value[0].a OPTEE_RPC_SOCKET_CLOSE_ALL + * [in] value[0].b TA instance id + */ +#define OPTEE_RPC_SOCKET_CLOSE_ALL 2 + +/* + * Send data on socket + * + * [in] value[0].a OPTEE_RPC_SOCKET_SEND + * [in] value[0].b TA instance id + * [in] value[0].c Socket handle + * [in] memref[1] Buffer to transmit + * [in] value[2].a Timeout ms or OPTEE_RPC_SOCKET_TIMEOUT_* + * [out] value[2].b Number of transmitted bytes + */ +#define OPTEE_RPC_SOCKET_SEND 3 + +/* + * Receive data on socket + * + * [in] value[0].a OPTEE_RPC_SOCKET_RECV + * [in] value[0].b TA instance id + * [in] value[0].c Socket handle + * [out] memref[1] Buffer to receive + * [in] value[2].a Timeout ms or OPTEE_RPC_SOCKET_TIMEOUT_* + */ +#define OPTEE_RPC_SOCKET_RECV 4 + +/* + * Perform IOCTL on socket + * + * [in] value[0].a OPTEE_RPC_SOCKET_IOCTL + * [in] value[0].b TA instance id + * [in] value[0].c Socket handle + * [in/out] memref[1] Buffer + * [in] value[2].a Ioctl command + */ +#define OPTEE_RPC_SOCKET_IOCTL 5 + +/* End of definition of protocol for command OPTEE_RPC_CMD_SOCKET */ + +#endif /*__OPTEE_RPC_CMD_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..d568bb2fe1 --- /dev/null +++ b/xen/include/asm-arm/tee/optee_smc.h @@ -0,0 +1,564 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2015, Linaro Limited + */ +#ifndef OPTEE_SMC_H +#define OPTEE_SMC_H + +/* + * This file is exported by OP-TEE and is in kept in sync between secure + * world and normal world kernel driver. We're following ARM SMC Calling + * Convention as specified in + * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html + * + * This file depends on optee_msg.h being included to expand the SMC id + * macros below. + */ + + +#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 */
(+ Lars)
Hi,
On 6/11/19 7:46 PM, Volodymyr Babchuk wrote:
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..fe743dbde3 --- /dev/null +++ b/xen/include/asm-arm/tee/optee_msg.h @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: BSD-2-Clause */
Hmmm, sorry I haven't noticed it until now (SDPX is more explicit that the full-blown license). I suspect this is fine to have BSD-2 Clause license in Xen but I want to confirmation from someone knowing more than me about license compatibility. Lars?
If this is fine, then I would like to add a word in the commit message (I am happy to do that on commit).
Cheers,
hmm, I forgot to CC lars. Sorry for that.
Cheers,
On 15/06/2019 19:39, Julien Grall wrote:
(+ Lars)
Hi,
On 6/11/19 7:46 PM, Volodymyr Babchuk wrote:
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..fe743dbde3 --- /dev/null +++ b/xen/include/asm-arm/tee/optee_msg.h @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: BSD-2-Clause */
Hmmm, sorry I haven't noticed it until now (SDPX is more explicit that the full-blown license). I suspect this is fine to have BSD-2 Clause license in Xen but I want to confirmation from someone knowing more than me about license compatibility. Lars?
If this is fine, then I would like to add a word in the commit message (I am happy to do that on commit).
Cheers,
Looking at https://www.gnu.org/licenses/license-list.en.html and also looking at the usage in the Linux kernel, I am pretty sure it is compatible. However, given that the Xen hypervisor as a whole is GPLv2, I think it would be more precise to say:
SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
On Mon, 17 Jun 2019, Julien Grall wrote:
hmm, I forgot to CC lars. Sorry for that.
Cheers,
On 15/06/2019 19:39, Julien Grall wrote:
(+ Lars)
Hi,
On 6/11/19 7:46 PM, Volodymyr Babchuk wrote:
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..fe743dbde3 --- /dev/null +++ b/xen/include/asm-arm/tee/optee_msg.h @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: BSD-2-Clause */
Hmmm, sorry I haven't noticed it until now (SDPX is more explicit that the full-blown license). I suspect this is fine to have BSD-2 Clause license in Xen but I want to confirmation from someone knowing more than me about license compatibility. Lars?
If this is fine, then I would like to add a word in the commit message (I am happy to do that on commit).
Cheers,
-- Julien Grall
On 17/06/2019 17:28, Stefano Stabellini wrote:
Looking at https://www.gnu.org/licenses/license-list.en.html and also looking at the usage in the Linux kernel, I am pretty sure it is compatible. However, given that the Xen hypervisor as a whole is GPLv2, I think it would be more precise to say:
SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
Well, this is imported from OP-TEE. So I don't think we have the freedom to change this copyright header here...
What I was asking is whether this is OK to import BSD-2-Clause code in Xen. You seem to agree that it should be possible.
I will give a chance to Lars to answer. I will commit #1-8 tomorrow evening.
Cheers,
On Mon, 17 Jun 2019, Julien Grall wrote:
hmm, I forgot to CC lars. Sorry for that.
Cheers,
On 15/06/2019 19:39, Julien Grall wrote:
(+ Lars)
Hi,
On 6/11/19 7:46 PM, Volodymyr Babchuk wrote:
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..fe743dbde3 --- /dev/null +++ b/xen/include/asm-arm/tee/optee_msg.h @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: BSD-2-Clause */
Hmmm, sorry I haven't noticed it until now (SDPX is more explicit that the full-blown license). I suspect this is fine to have BSD-2 Clause license in Xen but I want to confirmation from someone knowing more than me about license compatibility. Lars?
If this is fine, then I would like to add a word in the commit message (I am happy to do that on commit).
Cheers,
-- Julien Grall
On Mon, 17 Jun 2019, Julien Grall wrote:
On 17/06/2019 17:28, Stefano Stabellini wrote:
Looking at https://www.gnu.org/licenses/license-list.en.html and also looking at the usage in the Linux kernel, I am pretty sure it is compatible. However, given that the Xen hypervisor as a whole is GPLv2, I think it would be more precise to say:
SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
Well, this is imported from OP-TEE. So I don't think we have the freedom to change this copyright header here...
Interesting point: I would have thought that given that this is a GPLv2 project, if we went with SPDX, all files would need to have a GPL-2.0-only tag on them, plus, optionally, an OR XXX clause.
Something for Lars to investigate.
What I was asking is whether this is OK to import BSD-2-Clause code in Xen. You seem to agree that it should be possible.
Yep. The problematic BSD license is the BSD-4-Clause.
I will give a chance to Lars to answer. I will commit #1-8 tomorrow evening.
+1
On Mon, 17 Jun 2019, Julien Grall wrote:
hmm, I forgot to CC lars. Sorry for that.
Cheers,
On 15/06/2019 19:39, Julien Grall wrote:
(+ Lars)
Hi,
On 6/11/19 7:46 PM, Volodymyr Babchuk wrote:
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..fe743dbde3 --- /dev/null +++ b/xen/include/asm-arm/tee/optee_msg.h @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: BSD-2-Clause */
Hmmm, sorry I haven't noticed it until now (SDPX is more explicit that the full-blown license). I suspect this is fine to have BSD-2 Clause license in Xen but I want to confirmation from someone knowing more than me about license compatibility. Lars?
If this is fine, then I would like to add a word in the commit message (I am happy to do that on commit).
Cheers,
-- Julien Grall
-- Julien Grall
On 17/06/2019, 18:28, "Stefano Stabellini" sstabellini@kernel.org wrote:
On Mon, 17 Jun 2019, Julien Grall wrote: > On 17/06/2019 17:28, Stefano Stabellini wrote: > > Looking at https://www.gnu.org/licenses/license-list.en.html and also > > looking at the usage in the Linux kernel, I am pretty sure it is > > compatible. However, given that the Xen hypervisor as a whole is GPLv2, > > I think it would be more precise to say: > > > > SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > > Well, this is imported from OP-TEE. So I don't think we have the freedom to > change this copyright header here...
Interesting point: I would have thought that given that this is a GPLv2 project, if we went with SPDX, all files would need to have a GPL-2.0-only tag on them, plus, optionally, an OR XXX clause.
Something for Lars to investigate.
That is not really how this works. The resulting Xen binary would be GPL 2.0, while individual parts of the source tree can be of different licenses.
> What I was asking is whether this is OK to import BSD-2-Clause code in Xen. > You seem to agree that it should be possible.
Yep. The problematic BSD license is the BSD-4-Clause.
It is definitely OK: see http://xenbits.xen.org/gitweb/?p=xen.git%3Ba=blob%3Bf=CONTRIBUTING
Lars
Hi Lars,
On 19/06/2019 09:20, Lars Kurth wrote:
On 17/06/2019, 18:28, "Stefano Stabellini" sstabellini@kernel.org wrote:
On Mon, 17 Jun 2019, Julien Grall wrote: > On 17/06/2019 17:28, Stefano Stabellini wrote: > > Looking at https://www.gnu.org/licenses/license-list.en.html and also > > looking at the usage in the Linux kernel, I am pretty sure it is > > compatible. However, given that the Xen hypervisor as a whole is GPLv2, > > I think it would be more precise to say: > > > > SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > > Well, this is imported from OP-TEE. So I don't think we have the freedom to > change this copyright header here... Interesting point: I would have thought that given that this is a GPLv2 project, if we went with SPDX, all files would need to have a GPL-2.0-only tag on them, plus, optionally, an OR XXX clause. Something for Lars to investigate.
That is not really how this works. The resulting Xen binary would be GPL 2.0, while individual parts of the source tree can be of different licenses. > What I was asking is whether this is OK to import BSD-2-Clause code in Xen. > You seem to agree that it should be possible. Yep. The problematic BSD license is the BSD-4-Clause. It is definitely OK: see http://xenbits.xen.org/gitweb/?p=xen.git%3Ba=blob%3Bf=CONTRIBUTING
Thank you for the input! I will add a word in the commit message:
"Note the imported header is licensed BSD-2-clause. This is fine as it is compatible with GPLv2-only".
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 Acked-by: Julien Grall julien.grall@arm.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 caaf377a33..04d399ffbf 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -238,3 +238,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 d27a137f7a..e8657447d7 100644 --- a/xen/arch/arm/domain.c +++ b/xen/arch/arm/domain.c @@ -648,7 +648,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 bb69c380ec..3e8cdc151d 100644 --- a/xen/include/public/arch-arm.h +++ b/xen/include/public/arch-arm.h @@ -306,6 +306,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); #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 */
Hi Volodymyr,
On 11/06/2019 19:46, Volodymyr Babchuk wrote:
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
I have played a bit with the menuconfig that "Enable OP-TEE mediator" will appear at the top-level while "Enable TEE mediators support" is under "Architecture features".
Arguably, both should be under "Device Drivers". Can you send a follow-up patch to fix that up?
While you are doing that, can you add a description in "Enable OP-TEE mediator" explaining this require a virtualization-aware OP-TEE in order to work.
Cheers,
On 19/06/2019 12:01, Julien Grall wrote:
Hi Volodymyr,
On 11/06/2019 19:46, Volodymyr Babchuk wrote:
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
I have played a bit with the menuconfig that "Enable OP-TEE mediator" will appear at the top-level while "Enable TEE mediators support" is under "Architecture features".
Arguably, both should be under "Device Drivers". Can you send a follow-up patch to fix that up?
Another alternative is moving the two in "Architecture features" with "Enable OP-TEE mediator" under "Enable TEE mediator supports".
While you are doing that, can you add a description in "Enable OP-TEE mediator" explaining this require a virtualization-aware OP-TEE in order to work.
Cheers,
Hi Julien,
Julien Grall writes:
On 19/06/2019 12:01, Julien Grall wrote:
Hi Volodymyr,
On 11/06/2019 19:46, Volodymyr Babchuk wrote:
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
I have played a bit with the menuconfig that "Enable OP-TEE mediator" will appear at the top-level while "Enable TEE mediators support" is under "Architecture features".
Arguably, both should be under "Device Drivers". Can you send a follow-up patch to fix that up?
Another alternative is moving the two in "Architecture features" with "Enable OP-TEE mediator" under "Enable TEE mediator supports".
I'll do in this way, if there is no objections. As TEE is not a device, strictly speaking, I don't think that "Device Drivers" is a good place for it.
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
--- 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);
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 Acked-by: Julien Grall julien.grall@arm.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:
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
--- 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); }
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 Acked-by: Julien Grall julien.grall@arm.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 v5: - Added BUILD_BUG_ON(PAGE_SIZE != 4096) - Updated "TODO" comment about 16384 calls to lookup_and_pin_guest_ram_addr()
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 | 416 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+)
diff --git a/xen/arch/arm/tee/optee.c b/xen/arch/arm/tee/optee.c index 175789fb00..d4888acd8d 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,189 @@ 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 + * (assuming that PAGE_SIZE equals to 4096). + * This should be addressed before declaring OP-TEE security + * supported. + */ + BUILD_BUG_ON(PAGE_SIZE != 4096); + 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 +909,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 +955,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 +980,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 +1062,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);
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
--- 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 v5: - There was change to RPC command names, because of different header file was used (see comments to patch 2 "xen/arm: optee: add OP-TEE header files"). This is non-functional change.
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 | 230 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 223 insertions(+), 7 deletions(-)
diff --git a/xen/arch/arm/tee/optee.c b/xen/arch/arm/tee/optee.c index d4888acd8d..28d34360fc 100644 --- a/xen/arch/arm/tee/optee.c +++ b/xen/arch/arm/tee/optee.c @@ -36,6 +36,7 @@ #include <asm/tee/tee.h> #include <asm/tee/optee_msg.h> #include <asm/tee/optee_smc.h> +#include <asm/tee/optee_rpc_cmd.h>
/* Number of SMCs known to the mediator */ #define OPTEE_MEDIATOR_SMC_COUNT 11 @@ -47,6 +48,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 +93,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 +102,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 +358,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 +392,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 +422,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, @@ -931,10 +969,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; @@ -944,6 +985,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; }
/* @@ -956,6 +1042,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, @@ -971,7 +1060,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; @@ -1097,6 +1195,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_RPC_CMD_GET_TIME: + case OPTEE_RPC_CMD_WAIT_QUEUE: + case OPTEE_RPC_CMD_SUSPEND: + break; + case OPTEE_RPC_CMD_SHM_ALLOC: + handle_rpc_cmd_alloc(ctx, regs, call, shm_rpc); + break; + case OPTEE_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) @@ -1128,7 +1344,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); @@ -1174,8 +1390,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,
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.
'optee' enables access to the OP-TEE running on a host machine. This requires special OP-TEE build with virtualization support enabled.
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 v5: - Replaced "native" with "optee" in the commit description. - Updated and extended documentation based on Julien Grall's and Ian Jackson's suggestions.
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 | 21 +++++++++++++++++++++ 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, 54 insertions(+)
diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in index c99d40307e..e65ab6111f 100644 --- a/docs/man/xl.cfg.5.pod.in +++ b/docs/man/xl.cfg.5.pod.in @@ -1544,6 +1544,27 @@ Set maximum height for pointer device.
=back
+=item B<tee="STRING"> + +B<Arm only.> Set TEE type for the guest. TEE is a Trusted Execution +Environment -- separate secure OS found on some platforms. B<STRING> can be one of the: + +=over 4 + +=item B<none> + +Disable TEE support at all. This is the default value. + +=item B<optee> + +Allow a guest to use OP-TEE. Note that a virtualization-aware OP-TEE +is required for this. If this option is selected, guest will be able +to access to the real OP-TEE OS running on the host. Guest creation +will fail if OP-TEE have no resources for a new guest. Number of supported +guests depends on OP-TEE configuration. + +=back + =back
=head2 Paravirtualised (PV) Guest Specific Options diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h index 9bacfb97f0..1fe6ea2bd8 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 b61399ce36..fa5ee65463 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 e105bda2bb..0604374ef3 100644 --- a/tools/xl/xl_parse.c +++ b/tools/xl/xl_parse.c @@ -2691,6 +2691,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,
On 6/11/19 7:46 PM, Volodymyr Babchuk wrote:
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.
'optee' enables access to the OP-TEE running on a host machine. This requires special OP-TEE build with virtualization support enabled.
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 v5:
- Replaced "native" with "optee" in the commit description.
- Updated and extended documentation based on Julien Grall's and Ian Jackson's suggestions.
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 | 21 +++++++++++++++++++++ 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, 54 insertions(+)
diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in index c99d40307e..e65ab6111f 100644 --- a/docs/man/xl.cfg.5.pod.in +++ b/docs/man/xl.cfg.5.pod.in @@ -1544,6 +1544,27 @@ Set maximum height for pointer device. =back +=item B<tee="STRING">
+B<Arm only.> Set TEE type for the guest. TEE is a Trusted Execution +Environment -- separate secure OS found on some platforms. B<STRING> can be one of the:
+=over 4
+=item B<none>
+Disable TEE support at all. This is the default value.
How about "Don't allow the guest to use TEE if present on the platform. This is the default value.".
+=item B<optee>
+Allow a guest to use OP-TEE. Note that a virtualization-aware OP-TEE +is required for this. If this option is selected, guest will be able
OOI, what happen if OP-TEE does not support virtualization. Will Xen forbid to use it?
+to access to the real OP-TEE OS running on the host. Guest creation
s/real// it is redundant with the rest of the sentence. However, it does not really answer to the question regarding isolation.
+will fail if OP-TEE have no resources for a new guest. Number of supported +guests depends on OP-TEE configuration.
How about the following description (correct me if my understanding is wrong):
"Allow a guest to access the host OP-TEE OS. Xen will mediate the access to OP-TEE and the resource isolation will be provided directly by OP-TEE. OP-TEE itself may limit the number of guests that can concurrently use it. This requires a virtualization-aware OP-TEE for this to work.
This feature is a B<technology preview>."
How can a user know whether OP-TEE supports virtualization? Is it configurable at build?
Cheers,
Hi Julien,
Julien Grall writes:
Hi Volodymyr,
On 6/11/19 7:46 PM, Volodymyr Babchuk wrote:
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.
'optee' enables access to the OP-TEE running on a host machine. This requires special OP-TEE build with virtualization support enabled.
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 v5:
- Replaced "native" with "optee" in the commit description.
- Updated and extended documentation based on Julien Grall's and Ian Jackson's suggestions.
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 | 21 +++++++++++++++++++++ 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, 54 insertions(+)
diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in index c99d40307e..e65ab6111f 100644 --- a/docs/man/xl.cfg.5.pod.in +++ b/docs/man/xl.cfg.5.pod.in @@ -1544,6 +1544,27 @@ Set maximum height for pointer device. =back +=item B<tee="STRING">
+B<Arm only.> Set TEE type for the guest. TEE is a Trusted Execution +Environment -- separate secure OS found on some platforms. B<STRING> can be one of the:
+=over 4
+=item B<none>
+Disable TEE support at all. This is the default value.
How about "Don't allow the guest to use TEE if present on the platform. This is the default value.".
I'm perfectly fine with this.
+=item B<optee>
+Allow a guest to use OP-TEE. Note that a virtualization-aware OP-TEE +is required for this. If this option is selected, guest will be able
OOI, what happen if OP-TEE does not support virtualization. Will Xen forbid to use it?
Yes, Xen will get an error from OP-TEE during domain construction. This will lead to domain creation failure.
+to access to the real OP-TEE OS running on the host. Guest creation
s/real// it is redundant with the rest of the sentence. However, it does not really answer to the question regarding isolation.
Your assumption is correct - OP-TEE provides isolation on its side.
+will fail if OP-TEE have no resources for a new guest. Number of supported +guests depends on OP-TEE configuration.
How about the following description (correct me if my understanding is wrong):
"Allow a guest to access the host OP-TEE OS. Xen will mediate the access to OP-TEE and the resource isolation will be provided directly by OP-TEE. OP-TEE itself may limit the number of guests that can concurrently use it. This requires a virtualization-aware OP-TEE for this to work.
This feature is a B<technology preview>."
That's much better than my version. Thank you.
How can a user know whether OP-TEE supports virtualization? Is it configurable at build?
Yes, there is a special configuration option CFG_VIRTUALIZATION. This is covered in OP-TEE documentation at [1]
[1] https://optee.readthedocs.io/architecture/virtualization.html
On 18/06/2019 12:19, Volodymyr Babchuk wrote:
Hi Julien,
Hi,
Julien Grall writes:
+=item B<optee>
+Allow a guest to use OP-TEE. Note that a virtualization-aware OP-TEE +is required for this. If this option is selected, guest will be able
OOI, what happen if OP-TEE does not support virtualization. Will Xen forbid to use it?
Yes, Xen will get an error from OP-TEE during domain construction. This will lead to domain creation failure.
This is a bit odd. It means we have no way to know in advance whether OP-TEE will be able to create a client. In other word, when the mediator is built in Xen, all existing setup with OP-TEE (and no-virtualization) will fail.
My expectation is Xen should be able to know whether the mediator can be used.
+to access to the real OP-TEE OS running on the host. Guest creation
s/real// it is redundant with the rest of the sentence. However, it does not really answer to the question regarding isolation.
Your assumption is correct - OP-TEE provides isolation on its side.
+will fail if OP-TEE have no resources for a new guest. Number of supported +guests depends on OP-TEE configuration.
How about the following description (correct me if my understanding is wrong):
"Allow a guest to access the host OP-TEE OS. Xen will mediate the access to OP-TEE and the resource isolation will be provided directly by OP-TEE. OP-TEE itself may limit the number of guests that can concurrently use it. This requires a virtualization-aware OP-TEE for this to work.
This feature is a B<technology preview>."
That's much better than my version. Thank you.
How can a user know whether OP-TEE supports virtualization? Is it configurable at build?
Yes, there is a special configuration option CFG_VIRTUALIZATION. This is covered in OP-TEE documentation at [1]
[1] https://optee.readthedocs.io/architecture/virtualization.html
Do we expect the link to be stable? If so, then I think a link in the documentation would be useful.
Cheers,
Julien Grall writes:
On 18/06/2019 12:19, Volodymyr Babchuk wrote:
Hi Julien,
Hi,
Julien Grall writes:
+=item B<optee>
+Allow a guest to use OP-TEE. Note that a virtualization-aware OP-TEE +is required for this. If this option is selected, guest will be able
OOI, what happen if OP-TEE does not support virtualization. Will Xen forbid to use it?
Yes, Xen will get an error from OP-TEE during domain construction. This will lead to domain creation failure.
This is a bit odd. It means we have no way to know in advance whether OP-TEE will be able to create a client.
Yes. There can be at least two reasons for this: 1. OP-TEE is built without virtualization support at all 2. OP-TEE have no resources for a new guest
In other word, when the mediator is built in Xen, all existing setup with OP-TEE (and no-virtualization) will fail.
Right. If user provides DTB with 'optee' node, but OP-TEE is build without virtualization support, Dom0 will not be created. This can be fixed by adding new capability flag into OP-TEE, that tells Xen about virtualization support. For some reason I missed this when I implemented VM support in OP-TEE :(
My expectation is Xen should be able to know whether the mediator can be used.
I need to implement additional capability flag in the OP-TEE. This is not so hard, but it will be available only in the next release. For now, we can document this limitation somewhere.
+to access to the real OP-TEE OS running on the host. Guest creation
s/real// it is redundant with the rest of the sentence. However, it does not really answer to the question regarding isolation.
Your assumption is correct - OP-TEE provides isolation on its side.
+will fail if OP-TEE have no resources for a new guest. Number of supported +guests depends on OP-TEE configuration.
How about the following description (correct me if my understanding is wrong):
"Allow a guest to access the host OP-TEE OS. Xen will mediate the access to OP-TEE and the resource isolation will be provided directly by OP-TEE. OP-TEE itself may limit the number of guests that can concurrently use it. This requires a virtualization-aware OP-TEE for this to work.
This feature is a B<technology preview>."
That's much better than my version. Thank you.
How can a user know whether OP-TEE supports virtualization? Is it configurable at build?
Yes, there is a special configuration option CFG_VIRTUALIZATION. This is covered in OP-TEE documentation at [1]
[1] https://optee.readthedocs.io/architecture/virtualization.html
Do we expect the link to be stable? If so, then I think a link in the documentation would be useful.
This is the official OP-TEE documentation. So, yes, it should be stable. I can put this link into the code somewhere.
On 6/18/19 3:30 PM, Volodymyr Babchuk wrote:
Julien Grall writes:
On 18/06/2019 12:19, Volodymyr Babchuk wrote:
Hi Julien,
Hi,
Julien Grall writes:
+=item B<optee>
+Allow a guest to use OP-TEE. Note that a virtualization-aware OP-TEE +is required for this. If this option is selected, guest will be able
OOI, what happen if OP-TEE does not support virtualization. Will Xen forbid to use it?
Yes, Xen will get an error from OP-TEE during domain construction. This will lead to domain creation failure.
This is a bit odd. It means we have no way to know in advance whether OP-TEE will be able to create a client.
Yes. There can be at least two reasons for this:
- OP-TEE is built without virtualization support at all
- OP-TEE have no resources for a new guest
In other word, when the mediator is built in Xen, all existing setup with OP-TEE (and no-virtualization) will fail.
Right. If user provides DTB with 'optee' node, but OP-TEE is build without virtualization support, Dom0 will not be created. This can be fixed by adding new capability flag into OP-TEE, that tells Xen about virtualization support. For some reason I missed this when I implemented VM support in OP-TEE :(
My expectation is Xen should be able to know whether the mediator can be used.
I need to implement additional capability flag in the OP-TEE. This is not so hard, but it will be available only in the next release. For now, we can document this limitation somewhere.
Is OP-TEE already released with virtualization? If not, when will it be?
+to access to the real OP-TEE OS running on the host. Guest creation
s/real// it is redundant with the rest of the sentence. However, it does not really answer to the question regarding isolation.
Your assumption is correct - OP-TEE provides isolation on its side.
+will fail if OP-TEE have no resources for a new guest. Number of supported +guests depends on OP-TEE configuration.
How about the following description (correct me if my understanding is wrong):
"Allow a guest to access the host OP-TEE OS. Xen will mediate the access to OP-TEE and the resource isolation will be provided directly by OP-TEE. OP-TEE itself may limit the number of guests that can concurrently use it. This requires a virtualization-aware OP-TEE for this to work.
This feature is a B<technology preview>."
That's much better than my version. Thank you.
How can a user know whether OP-TEE supports virtualization? Is it configurable at build?
Yes, there is a special configuration option CFG_VIRTUALIZATION. This is covered in OP-TEE documentation at [1]
[1] https://optee.readthedocs.io/architecture/virtualization.html
Do we expect the link to be stable? If so, then I think a link in the documentation would be useful.
This is the official OP-TEE documentation. So, yes, it should be stable. I can put this link into the code somewhere.
I would add the link in the xl documentation and also in the commit message of patch #2. I can do the later on commit.
Cheers,
Julien Grall writes:
On 6/18/19 3:30 PM, Volodymyr Babchuk wrote:
Julien Grall writes:
On 18/06/2019 12:19, Volodymyr Babchuk wrote:
Hi Julien,
Hi,
Julien Grall writes:
+=item B<optee>
+Allow a guest to use OP-TEE. Note that a virtualization-aware OP-TEE +is required for this. If this option is selected, guest will be able
OOI, what happen if OP-TEE does not support virtualization. Will Xen forbid to use it?
Yes, Xen will get an error from OP-TEE during domain construction. This will lead to domain creation failure.
This is a bit odd. It means we have no way to know in advance whether OP-TEE will be able to create a client.
Yes. There can be at least two reasons for this:
- OP-TEE is built without virtualization support at all
- OP-TEE have no resources for a new guest
In other word, when the mediator is built in Xen, all existing setup with OP-TEE (and no-virtualization) will fail.
Right. If user provides DTB with 'optee' node, but OP-TEE is build without virtualization support, Dom0 will not be created. This can be fixed by adding new capability flag into OP-TEE, that tells Xen about virtualization support. For some reason I missed this when I implemented VM support in OP-TEE :(
My expectation is Xen should be able to know whether the mediator can be used.
I need to implement additional capability flag in the OP-TEE. This is not so hard, but it will be available only in the next release. For now, we can document this limitation somewhere.
Is OP-TEE already released with virtualization? If not, when will it be?
Yes, OP-TEE 3.5.0 was released on 26 April 2019 and it includes virtualization support.
+to access to the real OP-TEE OS running on the host. Guest creation
s/real// it is redundant with the rest of the sentence. However, it does not really answer to the question regarding isolation.
Your assumption is correct - OP-TEE provides isolation on its side.
+will fail if OP-TEE have no resources for a new guest. Number of supported +guests depends on OP-TEE configuration.
How about the following description (correct me if my understanding is wrong):
"Allow a guest to access the host OP-TEE OS. Xen will mediate the access to OP-TEE and the resource isolation will be provided directly by OP-TEE. OP-TEE itself may limit the number of guests that can concurrently use it. This requires a virtualization-aware OP-TEE for this to work.
This feature is a B<technology preview>."
That's much better than my version. Thank you.
How can a user know whether OP-TEE supports virtualization? Is it configurable at build?
Yes, there is a special configuration option CFG_VIRTUALIZATION. This is covered in OP-TEE documentation at [1]
[1] https://optee.readthedocs.io/architecture/virtualization.html
Do we expect the link to be stable? If so, then I think a link in the documentation would be useful.
This is the official OP-TEE documentation. So, yes, it should be stable. I can put this link into the code somewhere.
I would add the link in the xl documentation and also in the commit message of patch #2. I can do the later on commit.
It would be great. Thank you.
Hi,
On 18/06/2019 16:23, Volodymyr Babchuk wrote:
Julien Grall writes:
On 6/18/19 3:30 PM, Volodymyr Babchuk wrote:
Julien Grall writes:
On 18/06/2019 12:19, Volodymyr Babchuk wrote:
Hi Julien,
Hi,
Julien Grall writes:
> + > +=item B<optee> > + > +Allow a guest to use OP-TEE. Note that a virtualization-aware OP-TEE > +is required for this. If this option is selected, guest will be able
OOI, what happen if OP-TEE does not support virtualization. Will Xen forbid to use it?
Yes, Xen will get an error from OP-TEE during domain construction. This will lead to domain creation failure.
This is a bit odd. It means we have no way to know in advance whether OP-TEE will be able to create a client.
Yes. There can be at least two reasons for this:
- OP-TEE is built without virtualization support at all
- OP-TEE have no resources for a new guest
In other word, when the mediator is built in Xen, all existing setup with OP-TEE (and no-virtualization) will fail.
Right. If user provides DTB with 'optee' node, but OP-TEE is build without virtualization support, Dom0 will not be created. This can be fixed by adding new capability flag into OP-TEE, that tells Xen about virtualization support. For some reason I missed this when I implemented VM support in OP-TEE :(
My expectation is Xen should be able to know whether the mediator can be used.
I need to implement additional capability flag in the OP-TEE. This is not so hard, but it will be available only in the next release. For now, we can document this limitation somewhere.
Is OP-TEE already released with virtualization? If not, when will it be?
Yes, OP-TEE 3.5.0 was released on 26 April 2019 and it includes virtualization support.
Ok. Please try to solve this problem for the next release.
For now, I think there are a way to workaround the lack of a feature flag. In the detection of OP-TEE, you can try to create a client. If it fails, then it means OP-TEE does not support virtualization.
This is assuming that OP-TEE will fail gracefully.
Cheers,
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
--- 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) );
Hello Volodymyr,
On 11/06/2019 19:46, Volodymyr Babchuk wrote:
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
I have committed the first 8 patches.
tools/arm: tee: add "tee" option for xl.cfg tools/arm: optee: create optee firmware node in DT if tee=optee
Please resend the last two patches with the comments addressed and follow-up on the others couple of improvements (docs and code) I suggested.
Cheers,
MAINTAINERS | 6 + docs/man/xl.cfg.5.pod.in | 21 + 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 | 1540 +++++++++++++++++++++++ xen/arch/arm/tee/tee.c | 98 ++ 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 | 310 +++++ xen/include/asm-arm/tee/optee_rpc_cmd.h | 318 +++++ xen/include/asm-arm/tee/optee_smc.h | 564 +++++++++ xen/include/asm-arm/tee/tee.h | 112 ++ xen/include/public/arch-arm.h | 6 + 22 files changed, 3090 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_rpc_cmd.h create mode 100644 xen/include/asm-arm/tee/optee_smc.h create mode 100644 xen/include/asm-arm/tee/tee.h