]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
arm: restore kernel on setup failure
authorJean-Philippe Brucker <jean-philippe.brucker@arm.com>
Fri, 11 Jul 2014 12:40:54 +0000 (13:40 +0100)
committerJan Kiszka <jan.kiszka@siemens.com>
Fri, 19 Dec 2014 10:04:08 +0000 (11:04 +0100)
This patch implements two cases:
- When an error occurs before setting up EL2, there is nothing much
  to do except restore the linux registers stored in the per_cpu
  datas.
- When it happens after EL2 setup, arch_cpu_restore copies the saved
  registers on the stack, and continues into arch_shutdown_self

When it happens during the MMU setup, chances of recovering a clean
state are pretty thin anyway. The bootstrap vectors could be used to
catch and dump a minimal context (which would require a raw_printk
implementation), but we cowardly ignore this case for the moment.

Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
[Jan: fix memcpy size in cpu_return_el1]
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/arch/arm/entry.S
hypervisor/arch/arm/include/asm/setup.h
hypervisor/arch/arm/irqchip.c
hypervisor/arch/arm/setup.c

index 2dd1a9a857479ecb290f48a8d7db2721b458c0f2..6f9178ca9ad31368e3ea00701a2450ec3440ef84 100644 (file)
@@ -21,25 +21,46 @@ arch_entry:
        push    {r0 - r12}
 
        ldr     r1, =__page_pool
-       mov     r2, #1
-       lsl     r2, #PERCPU_SIZE_SHIFT
+       mov     r4, #1
+       lsl     r4, #PERCPU_SIZE_SHIFT
        /*
         * percpu data = pool + cpuid * shift
         * TODO: handle aff1 and aff2
         */
-       mla     r1, r2, r0, r1
-       add     r2, r1, #PERCPU_LINUX_SP
+       mla     r1, r4, r0, r1
+       add     r4, r1, #PERCPU_LINUX_SP
 
-       /* Save SP, LR, CPSR */
-       str     sp, [r2], #4
-       str     lr, [r2], #4
+       /*
+        * Save SP, LR, CPSR
+        * r4 is used so that they can be easily retrieved on failure.
+        */
+       str     sp, [r4], #4
+       str     lr, [r4], #4
        mrs     r3, cpsr
-       str     r3, [r2]
+       str     r3, [r4]
 
        mov     sp, r1
        add     sp, #PERCPU_STACK_END
+       /*
+        * Keep some space for a struct registers, in case setup fails and needs
+        * to return to the driver through the arch_shutdown_self path.
+        */
+       sub     sp, #((NUM_USR_REGS + 1) * 4)
        /* Call entry(cpuid, struct per_cpu*) */
-       b       entry
+       bl      entry
+
+       /*
+        * entry only returns here when there is an error before setting up EL2
+        */
+       ldr     r3, [r4], #-4
+       msr     spsr, r3
+       ldr     lr, [r4], #-4
+       ldr     sp, [r4]
+
+       /* Keep the return value in r0 */
+       pop     {r1}
+       pop     {r1 - r12}
+       subs    pc, lr, #0
 
        .globl bootstrap_vectors
        .align 5
index ca9acafa7043245d0620200798f4f761fa115b75..013def7d6c58d6c3fa23215306790c6ef20ce31e 100644 (file)
 
 #ifndef __ASSEMBLY__
 
+#include <jailhouse/string.h>
+
 static inline void __attribute__((always_inline))
-cpu_return_el1(struct per_cpu *cpu_data)
+cpu_return_el1(struct per_cpu *cpu_data, bool panic)
 {
-       /* Return value */
-       cpu_data->linux_reg[0] = 0;
-
-       asm volatile(
-       /* Reset the hypervisor stack */
-       "mov    sp, %4\n"
+       /*
+        * Return value
+        * FIXME: there is no way, currently, to communicate the precise error
+        * number from the core. A `EDISASTER' would be appropriate here.
+        */
+       cpu_data->linux_reg[0] = (panic ? -EIO : 0);
 
+       asm volatile (
        "msr    sp_svc, %0\n"
        "msr    elr_hyp, %1\n"
        "msr    spsr_hyp, %2\n"
+       :
+       : "r" (cpu_data->linux_sp + (NUM_ENTRY_REGS * sizeof(unsigned long))),
+         "r" (cpu_data->linux_ret),
+         "r" (cpu_data->linux_flags));
+
+       if (panic) {
+               /* A panicking return needs to shutdown EL2 before the ERET. */
+               struct registers *ctx = guest_regs(cpu_data);
+               memcpy(&ctx->usr, &cpu_data->linux_reg,
+                      NUM_ENTRY_REGS * sizeof(unsigned long));
+               return;
+       }
+
+       asm volatile(
+       /* Reset the hypervisor stack */
+       "mov    sp, %0\n"
        /*
         * We don't care about clobbering the other registers from now on. Must
         * be in sync with arch_entry.
         */
-       "ldm    %3, {r0 - r12}\n"
+       "ldm    %1, {r0 - r12}\n"
        /* After this, the kernel won't be able to access the hypervisor code */
        "eret\n"
        :
-       : "r" (cpu_data->linux_sp + (NUM_ENTRY_REGS * sizeof(unsigned long))),
-         "r" (cpu_data->linux_ret),
-         "r" (cpu_data->linux_flags),
-         "r" (cpu_data->linux_reg),
-         "r" (cpu_data->stack + PERCPU_STACK_END)
-       :);
+       : "r" (cpu_data->stack + PERCPU_STACK_END),
+         "r" (cpu_data->linux_reg));
 }
 
 int switch_exception_level(struct per_cpu *cpu_data);
index 76041ec3e2e4791b822747278f3cf9d0d10f96bd..a421a27a10ea7c2526089ca5383d0aa33113de7b 100644 (file)
@@ -228,6 +228,11 @@ int irqchip_cpu_reset(struct per_cpu *cpu_data)
 
 void irqchip_cpu_shutdown(struct per_cpu *cpu_data)
 {
+       /*
+        * The GIC backend must take care of only resetting the hyp interface if
+        * it has been initialised: this function may be executed during the
+        * setup phase.
+        */
        if (irqchip.cpu_reset)
                irqchip.cpu_reset(cpu_data, true);
 }
index 83fdac827ae06e038a346ad191ee77fc4b9004af..e72b73739105096cff3689f3db044f3755544968 100644 (file)
@@ -119,7 +119,7 @@ int arch_init_late(void)
 void arch_cpu_activate_vmm(struct per_cpu *cpu_data)
 {
        /* Return to the kernel */
-       cpu_return_el1(cpu_data);
+       cpu_return_el1(cpu_data, false);
 
        while (1);
 }
@@ -146,4 +146,20 @@ void arch_shutdown_self(struct per_cpu *cpu_data)
 
 void arch_cpu_restore(struct per_cpu *cpu_data)
 {
+       /*
+        * If we haven't reached switch_exception_level yet, there is nothing to
+        * clean up.
+        */
+       if (!is_el2())
+               return;
+
+       /*
+        * Otherwise, attempt do disable the MMU and return to EL1 using the
+        * arch_shutdown path. cpu_return will fill the banked registers and the
+        * guest regs structure (stored at the begginning of the stack) to
+        * prepare the ERET.
+        */
+       cpu_return_el1(cpu_data, true);
+
+       arch_shutdown_self(cpu_data);
 }