From c9444c0eeeac60be446bc1a04452a6e536fb778f Mon Sep 17 00:00:00 2001 From: Sergiu Moga Date: Thu, 23 Nov 2023 16:00:34 +0200 Subject: [PATCH] arch/arm64: Implement stack switching on IRQ/trap Define a per-cpu buffer whose size is twice that of the configured stacks and whose definition may be represented through the following diagram: ``` STACK_SIZE STACK_SIZE <---------------------><---------------------> |============================================| | | | | trap stack | IRQ stack | | | | |============================================= ^ SP_EL0 ``` The middle address of this buffer shall be assigned to `SP_EL0`, a great candidate for a register free of use as a `EL1` only Unikernel. Now, depending on whether an exception is an IRQ or a trap, the early assembly entry will switch to either the IRQ stack or the trap stack, by simply making use of the `SP_EL0` system register. Signed-off-by: Sergiu Moga --- plat/common/arm/lcpu.c | 22 ++++++++++ plat/kvm/arm/exceptions.S | 89 +++++++++++++++++++++++++++------------ 2 files changed, 84 insertions(+), 27 deletions(-) diff --git a/plat/common/arm/lcpu.c b/plat/common/arm/lcpu.c index 342a44a8b..654a2ea47 100644 --- a/plat/common/arm/lcpu.c +++ b/plat/common/arm/lcpu.c @@ -30,6 +30,9 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#include +#include +#include #include #include #include @@ -43,6 +46,20 @@ #define CPU_ID_MASK 0xff00ffffffUL +/* + * STACK_SIZE STACK_SIZE + * <---------------------><---------------------> + * |============================================| + * | | | + * | trap stack | IRQ stack | + * | | | + * |============================================= + * ^ + * SP_EL0 + */ +static __align(UKARCH_SP_ALIGN) +UKPLAT_PER_LCPU_ARRAY_DEFINE(__u8, lcpu_irqntrap_sp, STACK_SIZE * 2); + __lcpuid lcpu_arch_id(void) { __u64 mpidr_reg; @@ -80,6 +97,7 @@ extern struct _gic_dev *gic; int lcpu_arch_init(struct lcpu *this_lcpu) { + __uptr irqntrap_sp; int ret = 0; /* Initialize the interrupt controller for non-bsp cores */ @@ -91,6 +109,10 @@ int lcpu_arch_init(struct lcpu *this_lcpu) SYSREG_WRITE64(tpidr_el1, (__uptr)this_lcpu); + irqntrap_sp = (__uptr)&ukplat_per_lcpu_array_current(lcpu_irqntrap_sp, + STACK_SIZE / 2); + SYSREG_WRITE64(sp_el0, irqntrap_sp); + return ret; } diff --git a/plat/kvm/arm/exceptions.S b/plat/kvm/arm/exceptions.S index 98501c13b..865859849 100644 --- a/plat/kvm/arm/exceptions.S +++ b/plat/kvm/arm/exceptions.S @@ -28,6 +28,8 @@ */ #include #include +#include +#include .macro EXCHANGE_SP_WITH_X0 add sp, sp, x0 // new_sp = sp + x0 @@ -66,34 +68,69 @@ EXCHANGE_SP_WITH_X0 .endm -.macro SAVE_REGS - sub sp, sp, #__TRAP_STACK_SIZE +.macro SAVE_REGS, is_irq + /* Use TPIDRRO_EL0 as scratch register. It is fine to do so because + * it will always hold a value the application can't modify and we will + * always be able to restore it to its desired known value anytime we + * want. Thus, temporarily store x0. + */ + msr tpidrro_el0, x0 + + /* Fetch middle of `lcpu_irqntrap_sp`: + * STACK_SIZE STACK_SIZE + * <---------------------><---------------------> + * |============================================| + * | | | + * | trap stack | IRQ stack | + * | | | + * |============================================= + * ^ + * SP_EL0 + */ + mrs x0, sp_el0 + + /* Make it so that SP contains SP_EL0 and x0 contains old SP */ + EXCHANGE_SP_WITH_X0 + +.if \is_irq != 0 + /* If we are an IRQ, use the IRQ stack instead. */ + add sp, sp, #STACK_SIZE +.endif + + /* Store old SP previously saved into x0 */ + str x0, [sp, #-16] + + /* Restore x0 */ + mrs x0, tpidrro_el0 + + /* Begin saving registers */ + sub sp, sp, #__TRAP_STACK_SIZE /* Save general purpose registers */ - stp x0, x1, [sp, #16 * 0] - stp x2, x3, [sp, #16 * 1] - stp x4, x5, [sp, #16 * 2] - stp x6, x7, [sp, #16 * 3] - stp x8, x9, [sp, #16 * 4] - stp x10, x11, [sp, #16 * 5] - stp x12, x13, [sp, #16 * 6] - stp x14, x15, [sp, #16 * 7] - stp x16, x17, [sp, #16 * 8] - stp x18, x19, [sp, #16 * 9] - stp x20, x21, [sp, #16 * 10] - stp x22, x23, [sp, #16 * 11] - stp x24, x25, [sp, #16 * 12] - stp x26, x27, [sp, #16 * 13] - stp x28, x29, [sp, #16 * 14] + stp x0, x1, [sp, #16 * 0] + stp x2, x3, [sp, #16 * 1] + stp x4, x5, [sp, #16 * 2] + stp x6, x7, [sp, #16 * 3] + stp x8, x9, [sp, #16 * 4] + stp x10, x11, [sp, #16 * 5] + stp x12, x13, [sp, #16 * 6] + stp x14, x15, [sp, #16 * 7] + stp x16, x17, [sp, #16 * 8] + stp x18, x19, [sp, #16 * 9] + stp x20, x21, [sp, #16 * 10] + stp x22, x23, [sp, #16 * 11] + stp x24, x25, [sp, #16 * 12] + stp x26, x27, [sp, #16 * 13] + stp x28, x29, [sp, #16 * 14] /* Save LR and exception PC */ - mrs x21, elr_el1 - stp x30, x21, [sp, #16 * 15] + mrs x21, elr_el1 + stp x30, x21, [sp, #16 * 15] /* Save pstate and exception status register */ - mrs x22, spsr_el1 - mrs x23, esr_el1 - stp x22, x23, [sp, #16 * 16] + mrs x22, spsr_el1 + mrs x23, esr_el1 + stp x22, x23, [sp, #16 * 16] .endm .macro RESTORE_REGS @@ -134,8 +171,6 @@ /* Restore x18, x19 */ ldp x18, x19, [x19, #16 * 9] - add sp, sp, #__TRAP_STACK_SIZE - eret .endm @@ -146,7 +181,7 @@ */ .align 6 el1_sync: - SAVE_REGS + SAVE_REGS 0 mov x0, sp mrs x1, far_el1 bl trap_el1_sync @@ -154,7 +189,7 @@ el1_sync: .align 6 el1_irq: - SAVE_REGS + SAVE_REGS 1 msr daifclr, #(8 | 4 | 1) mov x0, sp bl trap_el1_irq @@ -169,7 +204,7 @@ el1_irq: #define el_invalid(name, reason, el) \ .align 6; \ name##_invalid: \ - SAVE_REGS; \ + SAVE_REGS 0; \ mov x0, sp; \ mov x1, el; \ mov x2, #(reason); \ -- 2.39.5