arm64: Add support for transition library

Transition library provides the following functionalities:
1) Setup the environment for switching to any particular EL and jump to the
loaded program at that EL. In short "Execute program X at exception level Y
using the state Z"
2) Provides routines for exception entry and exception exit that can be used by
any program to implement exception handling. The only routine required by the
program would be exc_dispatch which handles the exception in its own required
way and returns by making a call to exc_exit. On exc_exit, the transition
library unwinds the whole stack by popping out the saved state of xregs

BUG=chrome-os-partner:30785
BRANCH=None
TEST=Compiles successfully and exceptions are tested for ramstage on ryu

Change-Id: I8116556109665e61a53e4b3987d649e3cfed64a1
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: 8ab888e8cae0c5f1e79b0e16ca292869f16f1cca
Original-Change-Id: I90f664ac657258724dc0c79bd9f6ceef70064f90
Original-Signed-off-by: Furquan Shaikh <furquan@google.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/216375
Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Original-Tested-by: Furquan Shaikh <furquan@chromium.org>
Original-Commit-Queue: Furquan Shaikh <furquan@chromium.org>
Reviewed-on: http://review.coreboot.org/9070
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Furquan Shaikh 2014-08-30 21:59:11 -07:00 committed by Patrick Georgi
parent 0b606730c9
commit 668316bdcc
4 changed files with 496 additions and 0 deletions

View File

@ -61,6 +61,8 @@ bootblock-y += c_entry.c
bootblock-y += stage_entry.S bootblock-y += stage_entry.S
bootblock-y += stages.c bootblock-y += stages.c
bootblock-y += eabi_compat.c bootblock-y += eabi_compat.c
bootblock-y += transition.c transition_asm.S
bootblock-y += ../../lib/memset.c bootblock-y += ../../lib/memset.c
bootblock-y += ../../lib/memcpy.c bootblock-y += ../../lib/memcpy.c
bootblock-y += ../../lib/memmove.c bootblock-y += ../../lib/memmove.c
@ -88,6 +90,7 @@ romstage-y += ../../lib/memset.c
romstage-y += ../../lib/memcpy.c romstage-y += ../../lib/memcpy.c
romstage-y += ../../lib/memmove.c romstage-y += ../../lib/memmove.c
romstage-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c romstage-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c
romstage-y += transition.c transition_asm.S
rmodules_arm64-y += ../../lib/memset.c rmodules_arm64-y += ../../lib/memset.c
rmodules_arm64-y += ../../lib/memcpy.c rmodules_arm64-y += ../../lib/memcpy.c
@ -120,6 +123,8 @@ ramstage-y += ../../lib/memset.c
ramstage-y += ../../lib/memcpy.c ramstage-y += ../../lib/memcpy.c
ramstage-y += ../../lib/memmove.c ramstage-y += ../../lib/memmove.c
ramstage-y += stage_entry.S ramstage-y += stage_entry.S
ramstage-y += transition.c transition_asm.S
rmodules_arm64-y += ../../lib/memset.c rmodules_arm64-y += ../../lib/memset.c
rmodules_arm64-y += ../../lib/memcpy.c rmodules_arm64-y += ../../lib/memcpy.c
rmodules_arm64-y += ../../lib/memmove.c rmodules_arm64-y += ../../lib/memmove.c

View File

@ -0,0 +1,188 @@
/*
* 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_ARM64_TRANSITION_H__
#define __ARCH_ARM64_TRANSITION_H__
/* ======================== Transition Library =================================
* Transition library provides two main functionalities:
* 1) It allows any program X to be executed at EL Y using the state Z. It
* provides struct exc_state which holds the state of the EL to which we want to
* execute X at. Before performing an eret to the entry point of the program X,
* it initializes required registers using this exc_state structure. Here, X0 =
* args to the program X. IMP!!! : We do not initialize SP_EL0 for the program
* X, the program will have to handle that on its own. This is because while
* performing an eret to X, we could make SP_EL0 point to regs structure which
* then follows common exception exit path.
* 2) It serves as a common mechanism for handling exception entry and exit at
* any given EL. On entry to an exception, SP_ELx is selected by default. The
* exc entry routine stores all xregs and jumps to exc_entry which
* saves ELR, SPSR, EL, Mode and other information about the state from which
* exception was generated. On exit, xregs are restored by unwinding of SP_ELx.
* =============================================================================
*/
/* Macros for EL mode in SPSR */
#define STACK_POP_BYTES 16
#define STACK_PUSH_BYTES -16
#ifndef __ASSEMBLY__
#include <stdint.h>
#include <arch/lib_helpers.h>
#define XI_INDEX(i) X##i##_INDEX = i
enum {
XI_INDEX(0),
XI_INDEX(1),
XI_INDEX(2),
XI_INDEX(3),
XI_INDEX(4),
XI_INDEX(5),
XI_INDEX(6),
XI_INDEX(7),
XI_INDEX(8),
XI_INDEX(9),
XI_INDEX(10),
XI_INDEX(11),
XI_INDEX(12),
XI_INDEX(13),
XI_INDEX(14),
XI_INDEX(15),
XI_INDEX(16),
XI_INDEX(17),
XI_INDEX(18),
XI_INDEX(19),
XI_INDEX(20),
XI_INDEX(21),
XI_INDEX(22),
XI_INDEX(23),
XI_INDEX(24),
XI_INDEX(25),
XI_INDEX(26),
XI_INDEX(27),
XI_INDEX(28),
XI_INDEX(29),
XI_INDEX(30),
XMAX_INDEX,
};
/*
* Important: Any changes made to the two structures below should reflect in the
* exc_prologue and exc_exit routines in transition_asm.S
*/
struct regs {
uint64_t sp;
uint64_t x[31];
};
struct elx_state {
uint64_t spsr;
uint64_t sp_el0;
uint64_t sp_elx;
uint64_t elr;
};
struct exc_state {
struct elx_state elx;
struct regs regs;
};
/*
* get_eret_EL returns the value of the exception state to which we will be
* returning. This value is saved in SPSR before performing an eret.
*
* Exception mode is defined by M[3:0] bits in SPSR:
* ( M[3:2] = EL, M[1] = unused, M[0] = t/h mode for stack
*
* 0b0000 EL0t
* 0b0100 EL1t
* 0b0101 EL1h
* 0b1000 EL2t
* 0b1001 EL2h
* 0b1100 EL3t
* 0b1101 EL3h
*/
static inline uint8_t get_eret_el(uint8_t el, uint8_t l_or_h)
{
uint8_t el_mode = el << CURRENT_EL_SHIFT;
el_mode |= l_or_h;
return el_mode;
}
static inline uint8_t get_el_from_spsr(uint64_t spsr)
{
return ((spsr >> CURRENT_EL_SHIFT) & CURRENT_EL_MASK);
}
static inline uint8_t get_mode_from_spsr(uint64_t spsr)
{
return (spsr & SPSR_L_H_MASK);
}
/* Transitions supported are:
* 1. elx --> elx - 1
* 2. Transitions to aarch64 state
*
* Other than this, if any transition needs to be supported, relevant changes
* need to be done to hcr/scr registers.
*/
/*
* User of transition library can make a call to transition_with_entry and pass
* the entry point and its argument which are put into elr and x0 by this
* function. After that it makes a call to transition.
*/
void transition_with_entry(void *entry, void *arg, struct exc_state *exc_state);
/*
* transition function sets up all the registers as per the struct elx_state
* before jumping to trans_switch.
*/
void transition(struct exc_state *exc_state);
/*
* exc_exit it called while returning from an exception. It expects pointer to
* the regs structure on stack so that it can unwind the used stack.
*/
void exc_exit(struct regs *regs);
/*
* trans_switch is called by the non-exception path i.e. transition C code
* while making a transition to lower EL. It select L mode so that SP_EL0 is
* used during the unwinding in exc_exit.
*/
void trans_switch(struct regs *regs);
/* exc_set_vbar sets up the vbar for exception vectors. */
void exc_set_vbar(void);
/* exc_dispatch is the user-defined exception handler. */
void exc_dispatch(struct exc_state *exc_state, uint64_t id);
/*
* exc_entry is the C based component of the exception entry before we
* jump to user-defined handler. This initializes all the regs in elx_state and
* also sets the sp value in regs structure.
*/
void exc_entry(struct exc_state *exc_state, uint64_t id);
#endif /* __ASSEMBLY__ */
#endif /* __ARCH_ARM64_TRANSITION_H__ */

134
src/arch/arm64/transition.c Normal file
View File

@ -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);
}

View File

@ -0,0 +1,169 @@
/*
* 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
*/
/*
* transition_asm.S: This file handles the entry and exit from an exception
*
* Flow: exception --> exc_vectors --> exc_entry --> exc_dispatch -->
* exc_exit
* Transition Flow: transition --> trans_switch --> exc_exit
*
* |---| Exception Entry |---|
*
* On exception entry, it saves all the xregs on SP_ELx since SP_ELx is
* selected on entry. Some dummy pushes are performed to create space for
* elx_state structure. It then passes pointer to this saved set of regs and
* a unique id(for identifying exception) to exc_entry.
*
* |---| Exception Transition Dispatch |---|
*
* This is the C-component of exception entry. It does the work of initializing
* the exc_state registers. Finally it calls exception dispatch implemented by
* the user. This is point of no-return.
*
* |---| Exception Dispatch |---|
*
* User of this functionality is expected to implement exc_dispatch which
* acts as entry point for it. Once exception handling is complete, the user
* needs to call exc_exit with pointer to struct regs.
*
* |---| Exception Exit |---|
*
* Once exc_dispatch is done with handling the exception based on the id passed
* to it, it needs to call exc_exit with pointer to struct regs. This is done to
* unwind the exception stack by popping off all the xregs.
*
* |---| Exception Transition Exit |---|
*
* This routine makes SP_EL0 point to the regs structure passed and continues
* onto the exception exit routine described above. This is the reason that
* transition library does not handle initialization of SP_EL0 for the program
* to be executed.
*/
#define __ASSEMBLY__
#include <arch/asm.h>
#include <arch/lib_helpers.h>
#include <arch/transition.h>
.macro eentry lbl id
.align 7
\lbl:
stp x29, x30, [sp, #STACK_PUSH_BYTES]!
bl exc_prologue
mov x1, \id
mov x0, sp
b exc_entry
.endm
/*
* exc_vectors: Entry point for an exception
*/
ENTRY_WITH_ALIGN(exc_vectors, 11)
eentry sync_curr_sp0,#0
eentry irq_curr_sp0,#1
eentry fiq_curr_sp0,#2
eentry serror_curr_sp0,#3
eentry sync_curr_spx,#4
eentry irq_curr_spx,#5
eentry fiq_curr_spx,#6
eentry serror_curr_spx,#7
eentry sync_lower_64,#8
eentry irq_lower_64,#9
eentry fiq_lower_64,#10
eentry serror_lower_64,#11
eentry sync_lower_32,#12
eentry irq_lower_32,#13
eentry fiq_lower_32,#14
eentry serror_lower_32,#15
ENDPROC(exc_vectors)
ENTRY(exc_prologue)
stp x27, x28, [sp, #STACK_PUSH_BYTES]!
stp x25, x26, [sp, #STACK_PUSH_BYTES]!
stp x23, x24, [sp, #STACK_PUSH_BYTES]!
stp x21, x22, [sp, #STACK_PUSH_BYTES]!
stp x19, x20, [sp, #STACK_PUSH_BYTES]!
stp x17, x18, [sp, #STACK_PUSH_BYTES]!
stp x15, x16, [sp, #STACK_PUSH_BYTES]!
stp x13, x14, [sp, #STACK_PUSH_BYTES]!
stp x11, x12, [sp, #STACK_PUSH_BYTES]!
stp x9, x10, [sp, #STACK_PUSH_BYTES]!
stp x7, x8, [sp, #STACK_PUSH_BYTES]!
stp x5, x6, [sp, #STACK_PUSH_BYTES]!
stp x3, x4, [sp, #STACK_PUSH_BYTES]!
stp x1, x2, [sp, #STACK_PUSH_BYTES]!
/* xzr pushed as place holder for sp */
stp xzr, x0, [sp, #STACK_PUSH_BYTES]!
/*
* xzr pushed as place holder for:
* 1. sp_elx and elr
*/
stp xzr, xzr, [sp, #STACK_PUSH_BYTES]!
/* 2. spsr and sp_el0 */
stp xzr, xzr, [sp, #STACK_PUSH_BYTES]!
ret
ENDPROC(exc_prologue)
/*
* trans_switch: Set SPSel to use SP_EL0
* x0 = regs structure
*/
ENTRY(trans_switch)
msr SPSel, #SPSR_USE_L
b exc_exit
ENDPROC(trans_switch)
/*
* exc_exit: Return from exception by restoring saved state of xregs
* x0 = regs structure
*/
ENTRY(exc_exit)
/* Unwind the stack by making sp point to regs structure */
mov sp, x0
/* Load registers x0-x30 */
ldp xzr, x0, [sp], #STACK_POP_BYTES
ldp x1, x2, [sp], #STACK_POP_BYTES
ldp x3, x4, [sp], #STACK_POP_BYTES
ldp x5, x6, [sp], #STACK_POP_BYTES
ldp x7, x8, [sp], #STACK_POP_BYTES
ldp x9, x10, [sp], #STACK_POP_BYTES
ldp x11, x12, [sp], #STACK_POP_BYTES
ldp x13, x14, [sp], #STACK_POP_BYTES
ldp x15, x16, [sp], #STACK_POP_BYTES
ldp x17, x18, [sp], #STACK_POP_BYTES
ldp x19, x20, [sp], #STACK_POP_BYTES
ldp x21, x22, [sp], #STACK_POP_BYTES
ldp x23, x24, [sp], #STACK_POP_BYTES
ldp x25, x26, [sp], #STACK_POP_BYTES
ldp x27, x28, [sp], #STACK_POP_BYTES
ldp x29, x30, [sp], #STACK_POP_BYTES
eret
ENDPROC(exc_exit)
/* exc_set_vbar: Initialize the exception entry address in vbar */
ENTRY(exc_set_vbar)
adr x0, exc_vectors
write_current vbar, x0, x1
dsb sy
isb
ret
ENDPROC(exc_set_vbar)