diff options
Diffstat (limited to 'src/arch/arm64/transition.c')
-rw-r--r-- | src/arch/arm64/transition.c | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/src/arch/arm64/transition.c b/src/arch/arm64/transition.c new file mode 100644 index 0000000000..523960e741 --- /dev/null +++ b/src/arch/arm64/transition.c @@ -0,0 +1,134 @@ +/* + * 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 <arch/lib_helpers.h> +#include <arch/transition.h> +#include <console/console.h> + +/* Mask out debug exceptions, serror, irq and fiq */ +#define SPSR_MASK (SPSR_FIQ_MASK | SPSR_IRQ_MASK | SPSR_SERROR_MASK | \ + SPSR_DEBUG_MASK) +/* Litte-endian, No XN-forced, Instr cache disabled, + * Stack alignment disabled, Data and unified cache + * disabled, Alignment check disabled, MMU disabled + */ +#define SCTLR_MASK (SCTLR_MMU_DISABLE | SCTLR_ACE_DISABLE | \ + SCTLR_CACHE_DISABLE | SCTLR_SAE_DISABLE | SCTLR_RES1 | \ + SCTLR_ICE_DISABLE | SCTLR_WXN_DISABLE | SCTLR_LITTLE_END) + +void __attribute__((weak)) exc_dispatch(struct exc_state *exc_state, uint64_t id) +{ + /* Default weak implementation does nothing. */ +} + +void exc_entry(struct exc_state *exc_state, uint64_t id) +{ + struct elx_state *elx = &exc_state->elx; + struct regs *regs = &exc_state->regs; + uint8_t elx_mode, elx_el; + + elx->spsr = raw_read_spsr_current(); + elx_mode = get_mode_from_spsr(elx->spsr); + elx_el = get_el_from_spsr(elx->spsr); + + if (elx_mode == SPSR_USE_H) { + if (elx_el == get_current_el()) + regs->sp = (uint64_t)&exc_state[1]; + else + regs->sp = raw_read_sp_elx(elx_el); + } else { + regs->sp = raw_read_sp_el0(); + } + + elx->elr = raw_read_elr_current(); + + exc_dispatch(exc_state, id); +} + +void transition_with_entry(void *entry, void *arg, struct exc_state *exc_state) +{ + /* Argument to entry point goes into X0 */ + exc_state->regs.x[X0_INDEX] = (uint64_t)arg; + /* Entry point goes into ELR */ + exc_state->elx.elr = (uint64_t)entry; + + transition(exc_state); +} + +void transition(struct exc_state *exc_state) +{ + uint32_t scr_mask; + uint64_t hcr_mask; + uint64_t sctlr; + uint32_t current_el = get_current_el(); + + struct elx_state *elx = &exc_state->elx; + struct regs *regs = &exc_state->regs; + + uint8_t elx_el = get_el_from_spsr(elx->spsr); + + /* + * Policies enforced: + * 1. We support only elx --> (elx - 1) transitions + * 2. We support transitions to Aarch64 mode only + * + * If any of the above conditions holds false, then we need a proper way + * to update SCR/HCR before removing the checks below + */ + if ((current_el - elx_el) != 1) + die("ARM64 Error: Do not support transition\n"); + + if (elx->spsr & SPSR_ERET_32) + die("ARM64 Error: Do not support eret to Aarch32\n"); + else { + scr_mask = SCR_LOWER_AARCH64; + hcr_mask = HCR_LOWER_AARCH64; + } + + /* SPSR: Mask out debug exceptions, serror, irq, fiq */ + elx->spsr |= SPSR_MASK; + raw_write_spsr_current(elx->spsr); + + /* SCR: Write to SCR if current EL is EL3 */ + if (current_el == EL3) { + uint32_t scr = raw_read_scr_el3(); + scr |= scr_mask; + raw_write_scr_el3(scr); + } + /* HCR: Write to HCR if current EL is EL2 */ + else if (current_el == EL2) { + uint64_t hcr = raw_read_hcr_el2(); + hcr |= hcr_mask; + raw_write_hcr_el2(hcr); + } + + /* ELR: Write entry point of program */ + raw_write_elr_current(elx->elr); + + /* SCTLR: Initialize EL with selected properties */ + sctlr = raw_read_sctlr(elx_el); + sctlr &= SCTLR_MASK; + raw_write_sctlr(sctlr, elx_el); + + /* SP_ELx: Initialize stack pointer */ + raw_write_sp_elx(elx->sp_elx, elx_el); + + /* Eret to the entry point */ + trans_switch(regs); +} |