Message ID | 1507650771-16631-8-git-send-email-volodymyr_babchuk@epam.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Tue, 10 Oct 2017, Volodymyr Babchuk wrote: > SMCCC (SMC Call Convention) describes how to handle both HVCs and SMCs. > SMCCC states that both HVC and SMC are valid conduits to call to different > firmware functions. Thus, for example, PSCI calls can be made both by > SMC or HVC. Also SMCCC defines function number coding for such calls. > Besides functional calls there are query calls, which allows underling > OS determine version, UUID and number of functions provided by service > provider. > > This patch adds new file `vsmc.c`, which handles both generic SMCs > and HVC according to SMCCC. At this moment it implements only one > service: Standard Hypervisor Service. > > At this time Standard Hypervisor Service only supports query calls, > so caller can ask about hypervisor UID and determine that it is XEN running. > > This change allows more generic handling for SMCs and HVCs and it can > be easily extended to support new services and functions. > > But, before SMC is forwarded to standard SMCCC handler, it can be routed > to a domain monitor, if one is installed. > > Signed-off-by: Volodymyr Babchuk <volodymyr_babchuk@epam.com> > Reviewed-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> > Reviewed-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> > Acked-by: Julien Grall <julien.grall@arm.com> > --- > xen/arch/arm/Makefile | 1 + > xen/arch/arm/traps.c | 17 ---- > xen/arch/arm/vsmc.c | 191 ++++++++++++++++++++++++++++++++++++ > xen/include/asm-arm/traps.h | 3 + > xen/include/public/arch-arm/smccc.h | 58 +++++++++++ > 5 files changed, 253 insertions(+), 17 deletions(-) > create mode 100644 xen/arch/arm/vsmc.c > create mode 100644 xen/include/public/arch-arm/smccc.h > > diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile > index 424580b..30a2a65 100644 > --- a/xen/arch/arm/Makefile > +++ b/xen/arch/arm/Makefile > @@ -53,6 +53,7 @@ obj-$(CONFIG_HAS_ITS) += vgic-v3-its.o > obj-y += vm_event.o > obj-y += vtimer.o > obj-$(CONFIG_SBSA_VUART_CONSOLE) += vpl011.o > +obj-y += vsmc.o > obj-y += vpsci.o > obj-y += vuart.o > > diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c > index 5b91e6c..6ea0090 100644 > --- a/xen/arch/arm/traps.c > +++ b/xen/arch/arm/traps.c > @@ -2195,23 +2195,6 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs, > inject_dabt_exception(regs, info.gva, hsr.len); > } > > -static void do_trap_smc(struct cpu_user_regs *regs, const union hsr hsr) > -{ > - int rc = 0; > - > - if ( !check_conditional_instr(regs, hsr) ) > - { > - advance_pc(regs, hsr); > - return; > - } > - > - if ( current->domain->arch.monitor.privileged_call_enabled ) > - rc = monitor_smc(); > - > - if ( rc != 1 ) > - inject_undef_exception(regs, hsr); > -} > - > static void enter_hypervisor_head(struct cpu_user_regs *regs) > { > if ( guest_mode(regs) ) > diff --git a/xen/arch/arm/vsmc.c b/xen/arch/arm/vsmc.c > new file mode 100644 > index 0000000..38df821 > --- /dev/null > +++ b/xen/arch/arm/vsmc.c > @@ -0,0 +1,191 @@ > +/* > + * xen/arch/arm/vsmc.c > + * > + * Generic handler for SMC and HVC calls according to > + * ARM SMC calling convention > + * > + * 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/lib.h> > +#include <xen/types.h> > +#include <public/arch-arm/smccc.h> > +#include <asm/monitor.h> > +#include <asm/regs.h> > +#include <asm/smccc.h> > +#include <asm/traps.h> > + > +/* Number of functions currently supported by Hypervisor Service. */ > +#define XEN_SMCCC_FUNCTION_COUNT 3 > + > +static bool fill_uid(struct cpu_user_regs *regs, xen_uuid_t uuid) > +{ > + int n; > + > + /* > + * UID is returned in registers r0..r3, four bytes per register, > + * first byte is stored in low-order bits of a register. > + * (ARM DEN 0028B page 14) > + */ > + for (n = 0; n < 4; n++) > + { > + const uint8_t *bytes = uuid.a + n * 4; > + uint32_t r; > + > + r = bytes[0]; > + r |= bytes[1] << 8; > + r |= bytes[2] << 16; > + r |= bytes[3] << 24; > + > + set_user_reg(regs, n, r); > + } > + > + return true; > +} > + > +static bool fill_revision(struct cpu_user_regs *regs, uint32_t major, > + uint32_t minor) > +{ > + /* > + * Revision is returned in registers r0 and r1. > + * r0 stores major part of the version > + * r1 stores minor part of the version > + * (ARM DEN 0028B page 15) > + */ > + set_user_reg(regs, 0, major); > + set_user_reg(regs, 1, minor); > + > + return true; > +} > + > +static bool fill_function_call_count(struct cpu_user_regs *regs, uint32_t cnt) > +{ > + /* > + * Function call count is retuned as any other return value in register r0 > + * (ARM DEN 0028B page 17) > + */ > + set_user_reg(regs, 0, cnt); > + > + return true; > +} > + > +/* SMCCC interface for hypervisor. Tell about itself. */ > +static bool handle_hypervisor(struct cpu_user_regs *regs) > +{ > + switch ( smccc_get_fn(get_user_reg(regs, 0)) ) > + { > + case ARM_SMCCC_FUNC_CALL_COUNT: > + return fill_function_call_count(regs, XEN_SMCCC_FUNCTION_COUNT); > + case ARM_SMCCC_FUNC_CALL_UID: > + return fill_uid(regs, XEN_SMCCC_UID); > + case ARM_SMCCC_FUNC_CALL_REVISION: > + return fill_revision(regs, XEN_SMCCC_MAJOR_REVISION, > + XEN_SMCCC_MINOR_REVISION); > + default: > + return false; > + } > +} > + > +/* > + * vsmccc_handle_call() - handle SMC/HVC call according to ARM SMCCC. > + * returns true if that was valid SMCCC call (even if function number > + * was unknown). > + */ > +static bool vsmccc_handle_call(struct cpu_user_regs *regs) > +{ > + bool handled = false; > + const union hsr hsr = { .bits = regs->hsr }; > + register_t funcid = get_user_reg(regs, 0); > + > + /* > + * Check immediate value for HVC32, HVC64 and SMC64. > + * It is not so easy to check immediate value for SMC32, > + * because it is not stored in HSR.ISS field. To get immediate > + * value we need to disassemble instruction at current pc, which > + * is expensive. So we will assume that it is 0x0. > + */ > + switch ( hsr.ec ) > + { > + case HSR_EC_HVC32: > + case HSR_EC_HVC64: > + case HSR_EC_SMC64: HSR_EC_HVC64 and HSR_EC_SMC64 are only defined on ARM64. I added an #ifdef here while committing. > + if ( (hsr.iss & HSR_XXC_IMM_MASK) != 0) > + return false; > + break; > + case HSR_EC_SMC32: > + break; > + default: > + return false; > + } > + > + /* 64 bit calls are allowed only from 64 bit domains. */ > + if ( smccc_is_conv_64(funcid) && is_32bit_domain(current->domain) ) > + { > + set_user_reg(regs, 0, ARM_SMCCC_ERR_UNKNOWN_FUNCTION); > + return true; > + } > + > + switch ( smccc_get_owner(funcid) ) > + { > + case ARM_SMCCC_OWNER_HYPERVISOR: > + handled = handle_hypervisor(regs); > + break; > + } > + > + if ( !handled ) > + { > + gprintk(XENLOG_INFO, "Unhandled SMC/HVC: %08"PRIregister"\n", funcid); > + > + /* Inform caller that function is not supported. */ > + set_user_reg(regs, 0, ARM_SMCCC_ERR_UNKNOWN_FUNCTION); > + } > + > + return true; > +} > + > +void do_trap_smc(struct cpu_user_regs *regs, const union hsr hsr) > +{ > + int rc = 0; > + > + if ( !check_conditional_instr(regs, hsr) ) > + { > + advance_pc(regs, hsr); > + return; > + } > + > + /* If monitor is enabled, let it handle the call. */ > + if ( current->domain->arch.monitor.privileged_call_enabled ) > + rc = monitor_smc(); > + > + if ( rc == 1 ) > + return; > + > + /* > + * Use standard routines to handle the call. > + * vsmccc_handle_call() will return false if this call is not > + * SMCCC compatible (e.g. immediate value != 0). As it is not > + * compatible, we can't be sure that guest will understand > + * ARM_SMCCC_ERR_UNKNOWN_FUNCTION. > + */ > + if ( vsmccc_handle_call(regs) ) > + advance_pc(regs, hsr); > + else > + inject_undef_exception(regs, hsr); > +} > + > +/* > + * Local variables: > + * mode: C > + * c-file-style: "BSD" > + * c-basic-offset: 4 > + * indent-tabs-mode: nil > + * End: > + */ > diff --git a/xen/include/asm-arm/traps.h b/xen/include/asm-arm/traps.h > index 7508af8..325c15f 100644 > --- a/xen/include/asm-arm/traps.h > +++ b/xen/include/asm-arm/traps.h > @@ -35,6 +35,9 @@ void do_cp14_64(struct cpu_user_regs *regs, const union hsr hsr); > void do_cp14_dbg(struct cpu_user_regs *regs, const union hsr hsr); > void do_cp(struct cpu_user_regs *regs, const union hsr hsr); > > +/* SMCCC handling */ > +void do_trap_smc(struct cpu_user_regs *regs, const union hsr hsr); > + > #endif /* __ASM_ARM_TRAPS__ */ > /* > * Local variables: > diff --git a/xen/include/public/arch-arm/smccc.h b/xen/include/public/arch-arm/smccc.h > new file mode 100644 > index 0000000..2bee5b3 > --- /dev/null > +++ b/xen/include/public/arch-arm/smccc.h > @@ -0,0 +1,58 @@ > +/* > + * smccc.h > + * > + * SMC/HVC interface in accordance with SMC Calling Convention. > + * > + * Permission is hereby granted, free of charge, to any person obtaining a copy > + * of this software and associated documentation files (the "Software"), to > + * deal in the Software without restriction, including without limitation the > + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or > + * sell copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE > + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > + * DEALINGS IN THE SOFTWARE. > + * > + * Copyright 2017 (C) EPAM Systems > + */ > + > +#ifndef __XEN_PUBLIC_ARCH_ARM_SMCCC_H__ > +#define __XEN_PUBLIC_ARCH_ARM_SMCCC_H__ > + > +#include "public/xen.h" > + > +/* > + * Hypervisor Service version. > + * > + * We can't use XEN version here, because of SMCCC requirements: > + * Major revision should change every time SMC/HVC function is removed. > + * Minor revision should change every time SMC/HVC function is added. > + * So, it is SMCCC protocol revision code, not XEN version. > + * > + * Those values are subjected to change, when interface will be extended. > + */ > +#define XEN_SMCCC_MAJOR_REVISION 0 > +#define XEN_SMCCC_MINOR_REVISION 1 > + > +/* Hypervisor Service UID. Randomly generated with uuidgen. */ > +#define XEN_SMCCC_UID XEN_DEFINE_UUID(0xa71812dc, 0xc698, 0x4369, 0x9acf, \ > + 0x79, 0xd1, 0x8d, 0xde, 0xe6, 0x67) > + > +#endif /* __XEN_PUBLIC_ARCH_ARM_SMCCC_H__ */ > + > +/* > + * Local variables: > + * mode: C > + * c-file-style: "BSD" > + * c-basic-offset: 4 > + * indent-tabs-mode: nil > + * End:b > + */ > -- > 2.7.4 > > > _______________________________________________ > Xen-devel mailing list > Xen-devel@lists.xen.org > https://lists.xen.org/xen-devel >
diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 424580b..30a2a65 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_HAS_ITS) += vgic-v3-its.o obj-y += vm_event.o obj-y += vtimer.o obj-$(CONFIG_SBSA_VUART_CONSOLE) += vpl011.o +obj-y += vsmc.o obj-y += vpsci.o obj-y += vuart.o diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c index 5b91e6c..6ea0090 100644 --- a/xen/arch/arm/traps.c +++ b/xen/arch/arm/traps.c @@ -2195,23 +2195,6 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs, inject_dabt_exception(regs, info.gva, hsr.len); } -static void do_trap_smc(struct cpu_user_regs *regs, const union hsr hsr) -{ - int rc = 0; - - if ( !check_conditional_instr(regs, hsr) ) - { - advance_pc(regs, hsr); - return; - } - - if ( current->domain->arch.monitor.privileged_call_enabled ) - rc = monitor_smc(); - - if ( rc != 1 ) - inject_undef_exception(regs, hsr); -} - static void enter_hypervisor_head(struct cpu_user_regs *regs) { if ( guest_mode(regs) ) diff --git a/xen/arch/arm/vsmc.c b/xen/arch/arm/vsmc.c new file mode 100644 index 0000000..38df821 --- /dev/null +++ b/xen/arch/arm/vsmc.c @@ -0,0 +1,191 @@ +/* + * xen/arch/arm/vsmc.c + * + * Generic handler for SMC and HVC calls according to + * ARM SMC calling convention + * + * 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/lib.h> +#include <xen/types.h> +#include <public/arch-arm/smccc.h> +#include <asm/monitor.h> +#include <asm/regs.h> +#include <asm/smccc.h> +#include <asm/traps.h> + +/* Number of functions currently supported by Hypervisor Service. */ +#define XEN_SMCCC_FUNCTION_COUNT 3 + +static bool fill_uid(struct cpu_user_regs *regs, xen_uuid_t uuid) +{ + int n; + + /* + * UID is returned in registers r0..r3, four bytes per register, + * first byte is stored in low-order bits of a register. + * (ARM DEN 0028B page 14) + */ + for (n = 0; n < 4; n++) + { + const uint8_t *bytes = uuid.a + n * 4; + uint32_t r; + + r = bytes[0]; + r |= bytes[1] << 8; + r |= bytes[2] << 16; + r |= bytes[3] << 24; + + set_user_reg(regs, n, r); + } + + return true; +} + +static bool fill_revision(struct cpu_user_regs *regs, uint32_t major, + uint32_t minor) +{ + /* + * Revision is returned in registers r0 and r1. + * r0 stores major part of the version + * r1 stores minor part of the version + * (ARM DEN 0028B page 15) + */ + set_user_reg(regs, 0, major); + set_user_reg(regs, 1, minor); + + return true; +} + +static bool fill_function_call_count(struct cpu_user_regs *regs, uint32_t cnt) +{ + /* + * Function call count is retuned as any other return value in register r0 + * (ARM DEN 0028B page 17) + */ + set_user_reg(regs, 0, cnt); + + return true; +} + +/* SMCCC interface for hypervisor. Tell about itself. */ +static bool handle_hypervisor(struct cpu_user_regs *regs) +{ + switch ( smccc_get_fn(get_user_reg(regs, 0)) ) + { + case ARM_SMCCC_FUNC_CALL_COUNT: + return fill_function_call_count(regs, XEN_SMCCC_FUNCTION_COUNT); + case ARM_SMCCC_FUNC_CALL_UID: + return fill_uid(regs, XEN_SMCCC_UID); + case ARM_SMCCC_FUNC_CALL_REVISION: + return fill_revision(regs, XEN_SMCCC_MAJOR_REVISION, + XEN_SMCCC_MINOR_REVISION); + default: + return false; + } +} + +/* + * vsmccc_handle_call() - handle SMC/HVC call according to ARM SMCCC. + * returns true if that was valid SMCCC call (even if function number + * was unknown). + */ +static bool vsmccc_handle_call(struct cpu_user_regs *regs) +{ + bool handled = false; + const union hsr hsr = { .bits = regs->hsr }; + register_t funcid = get_user_reg(regs, 0); + + /* + * Check immediate value for HVC32, HVC64 and SMC64. + * It is not so easy to check immediate value for SMC32, + * because it is not stored in HSR.ISS field. To get immediate + * value we need to disassemble instruction at current pc, which + * is expensive. So we will assume that it is 0x0. + */ + switch ( hsr.ec ) + { + case HSR_EC_HVC32: + case HSR_EC_HVC64: + case HSR_EC_SMC64: + if ( (hsr.iss & HSR_XXC_IMM_MASK) != 0) + return false; + break; + case HSR_EC_SMC32: + break; + default: + return false; + } + + /* 64 bit calls are allowed only from 64 bit domains. */ + if ( smccc_is_conv_64(funcid) && is_32bit_domain(current->domain) ) + { + set_user_reg(regs, 0, ARM_SMCCC_ERR_UNKNOWN_FUNCTION); + return true; + } + + switch ( smccc_get_owner(funcid) ) + { + case ARM_SMCCC_OWNER_HYPERVISOR: + handled = handle_hypervisor(regs); + break; + } + + if ( !handled ) + { + gprintk(XENLOG_INFO, "Unhandled SMC/HVC: %08"PRIregister"\n", funcid); + + /* Inform caller that function is not supported. */ + set_user_reg(regs, 0, ARM_SMCCC_ERR_UNKNOWN_FUNCTION); + } + + return true; +} + +void do_trap_smc(struct cpu_user_regs *regs, const union hsr hsr) +{ + int rc = 0; + + if ( !check_conditional_instr(regs, hsr) ) + { + advance_pc(regs, hsr); + return; + } + + /* If monitor is enabled, let it handle the call. */ + if ( current->domain->arch.monitor.privileged_call_enabled ) + rc = monitor_smc(); + + if ( rc == 1 ) + return; + + /* + * Use standard routines to handle the call. + * vsmccc_handle_call() will return false if this call is not + * SMCCC compatible (e.g. immediate value != 0). As it is not + * compatible, we can't be sure that guest will understand + * ARM_SMCCC_ERR_UNKNOWN_FUNCTION. + */ + if ( vsmccc_handle_call(regs) ) + advance_pc(regs, hsr); + else + inject_undef_exception(regs, hsr); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-arm/traps.h b/xen/include/asm-arm/traps.h index 7508af8..325c15f 100644 --- a/xen/include/asm-arm/traps.h +++ b/xen/include/asm-arm/traps.h @@ -35,6 +35,9 @@ void do_cp14_64(struct cpu_user_regs *regs, const union hsr hsr); void do_cp14_dbg(struct cpu_user_regs *regs, const union hsr hsr); void do_cp(struct cpu_user_regs *regs, const union hsr hsr); +/* SMCCC handling */ +void do_trap_smc(struct cpu_user_regs *regs, const union hsr hsr); + #endif /* __ASM_ARM_TRAPS__ */ /* * Local variables: diff --git a/xen/include/public/arch-arm/smccc.h b/xen/include/public/arch-arm/smccc.h new file mode 100644 index 0000000..2bee5b3 --- /dev/null +++ b/xen/include/public/arch-arm/smccc.h @@ -0,0 +1,58 @@ +/* + * smccc.h + * + * SMC/HVC interface in accordance with SMC Calling Convention. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright 2017 (C) EPAM Systems + */ + +#ifndef __XEN_PUBLIC_ARCH_ARM_SMCCC_H__ +#define __XEN_PUBLIC_ARCH_ARM_SMCCC_H__ + +#include "public/xen.h" + +/* + * Hypervisor Service version. + * + * We can't use XEN version here, because of SMCCC requirements: + * Major revision should change every time SMC/HVC function is removed. + * Minor revision should change every time SMC/HVC function is added. + * So, it is SMCCC protocol revision code, not XEN version. + * + * Those values are subjected to change, when interface will be extended. + */ +#define XEN_SMCCC_MAJOR_REVISION 0 +#define XEN_SMCCC_MINOR_REVISION 1 + +/* Hypervisor Service UID. Randomly generated with uuidgen. */ +#define XEN_SMCCC_UID XEN_DEFINE_UUID(0xa71812dc, 0xc698, 0x4369, 0x9acf, \ + 0x79, 0xd1, 0x8d, 0xde, 0xe6, 0x67) + +#endif /* __XEN_PUBLIC_ARCH_ARM_SMCCC_H__ */ + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End:b + */