diff options
Diffstat (limited to 'src/arch/arm64')
-rw-r--r-- | src/arch/arm64/armv8/secmon/Makefile.inc | 3 | ||||
-rw-r--r-- | src/arch/arm64/armv8/secmon/secmon_init.c | 17 | ||||
-rw-r--r-- | src/arch/arm64/armv8/secmon/smc.c | 159 | ||||
-rw-r--r-- | src/arch/arm64/include/arch/smc.h | 122 |
4 files changed, 285 insertions, 16 deletions
diff --git a/src/arch/arm64/armv8/secmon/Makefile.inc b/src/arch/arm64/armv8/secmon/Makefile.inc index 5dadfa9a63..040f56829a 100644 --- a/src/arch/arm64/armv8/secmon/Makefile.inc +++ b/src/arch/arm64/armv8/secmon/Makefile.inc @@ -32,6 +32,7 @@ secmon-c-ccopts += -I$(src)/arch/arm64/include/armv8/ -include $(src)/include/kc secmon-S-ccopts += -I$(src)/arch/arm64/include/armv8/ -include $(src)/include/kconfig.h -D__SECMON__ secmon-y += secmon_init.c +secmon-y += smc.c secmon-y += ../exception.c secmon-y += ../../cpu.c secmon-y += ../../transition_asm.S ../../transition.c @@ -48,4 +49,4 @@ $(SECMON_SRC): $(SECMON_RMOD) $(SECMON_RAMSTAGE): $(SECMON_SRC) @printf " OBJCOPY $(subst $(obj)/,,$(@))\n" - cd $(dir $@); $(OBJCOPY_secmon) -I binary $(notdir $<) -O elf64-littleaarch64 -B aarch64 $(notdir $@)
\ No newline at end of file + cd $(dir $@); $(OBJCOPY_secmon) -I binary $(notdir $<) -O elf64-littleaarch64 -B aarch64 $(notdir $@) diff --git a/src/arch/arm64/armv8/secmon/secmon_init.c b/src/arch/arm64/armv8/secmon/secmon_init.c index 660d6d432b..de433bb9ef 100644 --- a/src/arch/arm64/armv8/secmon/secmon_init.c +++ b/src/arch/arm64/armv8/secmon/secmon_init.c @@ -24,6 +24,7 @@ #include <arch/exception.h> #include <arch/lib_helpers.h> #include <arch/secmon.h> +#include <arch/smc.h> #include <arch/transition.h> #include <console/console.h> #include <rmodule.h> @@ -40,20 +41,6 @@ static void cpu_init(int bsp) cpu_set_bsp(); } -static void secmon_el3_init(void) -{ - uint32_t scr; - - scr = raw_read_scr_el3(); - - /* Enable SMC */ - scr &= ~(SCR_SMC_MASK); - scr |= SCR_SMC_ENABLE; - - raw_write_scr_el3(scr); - isb(); -} - static void secmon_init(struct secmon_params *params, int bsp) { struct exc_state exc_state; @@ -61,7 +48,7 @@ static void secmon_init(struct secmon_params *params, int bsp) exception_hwinit(); cpu_init(bsp); - secmon_el3_init(); + smc_init(); /* * Check if the arg is non-NULL diff --git a/src/arch/arm64/armv8/secmon/smc.c b/src/arch/arm64/armv8/secmon/smc.c new file mode 100644 index 0000000000..3bb52f0311 --- /dev/null +++ b/src/arch/arm64/armv8/secmon/smc.c @@ -0,0 +1,159 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include <string.h> +#include <stdlib.h> +#include <arch/cpu.h> +#include <arch/smc.h> +#include <arch/exception.h> +#include <arch/lib_helpers.h> +#include <console/console.h> + +enum { + EC_SMC32 = 0x13, + EC_SMC64 = 0x17, + + SMC_NUM_RANGES = 8, +}; + +struct smc_range { + uint32_t func_begin; + uint32_t func_end; + int (*handler)(struct smc_call *); +}; + +struct smc_ranges { + size_t used; + struct smc_range handlers[SMC_NUM_RANGES]; +}; + +static struct smc_ranges smc_functions; + +static struct smc_range *smc_handler_by_function(uint32_t fid) +{ + int i; + + for (i = 0; i < smc_functions.used; i++) { + struct smc_range *r = &smc_functions.handlers[i]; + + if (fid >= r->func_begin && fid <= r->func_end) + return r; + } + + return NULL; +} + +int smc_register_range(uint32_t min, uint32_t max, int (*h)(struct smc_call *)) +{ + struct smc_range *r; + + if (smc_functions.used == SMC_NUM_RANGES) + return -1; + + if (min > max) + return -1; + + /* This check isn't exhaustive but it's fairly quick. */ + if (smc_handler_by_function(min) || smc_handler_by_function(max)) + return -1; + + r = &smc_functions.handlers[smc_functions.used]; + r->func_begin = min; + r->func_end = max; + r->handler = h; + smc_functions.used++; + + return 0; +} + +static int smc_cleanup(struct exc_state *state, struct smc_call *smc, int ret) +{ + memcpy(&state->regs.x, &smc->results, ARRAY_SIZE(smc->results)); + + return ret; +} + +static int smc_return_with_error(struct exc_state *state, struct smc_call *smc) +{ + smc32_return(smc, SMC_UNKNOWN_FUNC); + return smc_cleanup(state, smc, EXC_RET_HANDLED); +} + +static int smc_handler(struct exc_state *state, uint64_t vector_id) +{ + struct smc_call smc_storage; + struct smc_call *smc = &smc_storage; + uint32_t exception_class; + uint32_t esr; + struct smc_range *r; + + memcpy(&smc->args, &state->regs.x, ARRAY_SIZE(smc->args)); + memcpy(&smc->results, &state->regs.x, ARRAY_SIZE(smc->results)); + + esr = raw_read_esr_el3(); + exception_class = (esr >> 26) & 0x3f; + + /* No support for 32-bit SMC calls. */ + if (exception_class == EC_SMC32) + smc_return_with_error(state, smc); + + /* Check to ensure this is an SMC from aarch64. */ + if (exception_class != EC_SMC64) + return EXC_RET_IGNORED; + + /* Ensure immediate value is 0. */ + if ((esr & 0xffff) != 0) + smc_return_with_error(state, smc); + + r = smc_handler_by_function(smc_function_id(smc)); + + if (r != NULL) { + if (!r->handler(smc)) + return smc_cleanup(state, smc, EXC_RET_HANDLED); + } + + return smc_return_with_error(state, smc); +} + +/* SMC calls can be generated by 32-bit or 64-bit code. */ +static struct exception_handler smc_handler64 = { + .handler = &smc_handler, +}; + +static struct exception_handler smc_handler32 = { + .handler = &smc_handler, +}; + +void smc_init(void) +{ + uint32_t scr; + + /* Enable SMC */ + scr = raw_read_scr_el3(); + scr &= ~(SCR_SMC_MASK); + scr |= SCR_SMC_ENABLE; + raw_write_scr_el3(scr); + + if (!cpu_is_bsp()) + return; + + /* Register SMC handlers. */ + exception_handler_register(EXC_VID_LOW64_SYNC, &smc_handler64); + exception_handler_register(EXC_VID_LOW32_SYNC, &smc_handler32); +} diff --git a/src/arch/arm64/include/arch/smc.h b/src/arch/arm64/include/arch/smc.h new file mode 100644 index 0000000000..288459e6ba --- /dev/null +++ b/src/arch/arm64/include/arch/smc.h @@ -0,0 +1,122 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#ifndef __ARCH_SMC_H__ +#define __ARCH_SMC_H__ + +#include <stdint.h> + +enum { + FUNC_ID_CALL_TYPE_SHIFT = 31, + FUNC_ID_CALL_TYPE_MASK = (1 << FUNC_ID_CALL_TYPE_SHIFT), + FUNC_ID_FASTCALL = (1 << FUNC_ID_CALL_TYPE_SHIFT), + FUNC_ID_STDCALL = (0 << FUNC_ID_CALL_TYPE_SHIFT), + + FUNC_ID_CALL_CONVENTION_SHIFT = 30, + FUNC_ID_CALL_CONVENTION_MASK = (1 << FUNC_ID_CALL_CONVENTION_SHIFT), + FUNC_ID_SMC32 = (0 << FUNC_ID_CALL_CONVENTION_SHIFT), + FUNC_ID_SMC64 = (1 << FUNC_ID_CALL_CONVENTION_SHIFT), + + FUNC_ID_ENTITY_SHIFT = 24, + FUNC_ID_ENTITY_MASK = (0x3f << FUNC_ID_ENTITY_SHIFT), + + FUNC_ID_FUNC_NUMBER_SHIFT = 0, + FUNC_ID_FUNC_NUMBER_MASK = (0xffff << FUNC_ID_FUNC_NUMBER_SHIFT), + + FUNC_ID_MASK = FUNC_ID_CALL_TYPE_MASK | FUNC_ID_CALL_CONVENTION_MASK | + FUNC_ID_ENTITY_MASK | FUNC_ID_FUNC_NUMBER_MASK, + + SMC_NUM_ARGS = 8, /* The last is optional hypervisor id. */ + SMC_NUM_RESULTS = 4, + + SMC_UNKNOWN_FUNC = 0xffffffff, +}; + +#define SMC_FUNC(entity, number, call_convention, call_type) \ + ((call_type) | (call_convention) | \ + ((entity) << FUNC_ID_ENTITY_SHIFT) | (number)) + +#define SMC_FUNC_FAST(entity, number, call_convention) \ + SMC_FUNC((entity), (number), (call_convention), FUNC_ID_FASTCALL) + +#define SMC_FUNC_FAST32(entity, number) \ + SMC_FUNC_FAST((entity), (number), FUNC_ID_SMC32) + +#define SMC_FUNC_FAST64(entity, number) \ + SMC_FUNC_FAST((entity), (number), FUNC_ID_SMC64) + +struct smc_call { + uint64_t args[SMC_NUM_ARGS]; + uint64_t results[SMC_NUM_RESULTS]; +}; + +/* SMC immediate value needs to be 0. */ +/* Check mod AARCHx mode against calling convention. */ + +static inline uint64_t smc64_arg(const struct smc_call *smc, unsigned i) +{ + return smc->args[i]; +} + +static inline uint32_t smc32_arg(const struct smc_call *smc, unsigned i) +{ + return smc64_arg(smc, i); +} + +static inline void smc64_result(struct smc_call *smc, unsigned i, uint64_t v) +{ + smc->results[i] = v; +} + +static inline void smc32_result(struct smc_call *smc, unsigned i, uint32_t v) +{ + uint64_t v64 = v; + smc64_result(smc, i, v64); +} + +static inline void smc32_return(struct smc_call *smc, int32_t v) +{ + smc32_result(smc, 0, v); +} + +static inline uint32_t smc_hypervisor_id(const struct smc_call *smc) +{ + /* Set in W7 */ + return smc32_arg(smc, 7); +} + +static inline uint32_t smc_session_id(const struct smc_call *smc) +{ + /* Set in W6 */ + return smc32_arg(smc, 6); +} + +static inline uint32_t smc_function_id(const struct smc_call *smc) +{ + /* Function ID in W0. */ + return smc32_arg(smc, 0) & FUNC_ID_MASK; +} + +/* Initialize the SMC layer. */ +void smc_init(void); + +/* Register a handler for a given function range, inclusive. */ +int smc_register_range(uint32_t min, uint32_t max, int (*)(struct smc_call *)); + +#endif /* __ARCH_SMC_H__ */ |