armv8: Implement wait_for_interrupt
authorLukas Humbel <lukas.humbel@inf.ethz.ch>
Fri, 8 Nov 2019 10:27:59 +0000 (11:27 +0100)
committerLukas Humbel <lukas.humbel@inf.ethz.ch>
Fri, 8 Nov 2019 10:46:35 +0000 (11:46 +0100)
Enable interrupts and correct handling of irq from EL1

Signed-off-by: Lukas Humbel <lukas.humbel@inf.ethz.ch>

kernel/arch/armv8/exceptions.S
kernel/arch/armv8/exec.c
kernel/arch/armv8/exn.c
kernel/include/arch/armv8/exceptions.h

index 19a39b4..c167bff 100644 (file)
@@ -75,7 +75,7 @@ el1_sp_el0_sync:
     invalid_exception AARCH64_EVECTOR_EL0_SYNC
 .align 7 /* 0x080 */
 el1_sp_el0_irq:
-    invalid_exception AARCH64_EVECTOR_EL0_IRQ /* TODO: Waait, wenn we do wait for interrupt in kernel, that shouldnt be invalid */
+    invalid_exception AARCH64_EVECTOR_EL0_IRQ 
 .align 7 /* 0x100 */
 el1_sp_el0_fiq:
     invalid_exception AARCH64_EVECTOR_EL0_FIQ
@@ -90,6 +90,14 @@ el1_sync:
     invalid_exception AARCH64_EVECTOR_EL1_SYNC
 .align 7 /* 0x280 */
 el1_irq:
+    /* This happens only in case the kernel runs out of work and
+     * calls wait_for_interrupt. To make sure we come from this function
+     * we store a magic value in a register */
+    mov w1, #WAIT_FOR_INTERRUPT_MAGIC
+    cmp w0,w1
+    b.ne el1_irq_failure
+    b nosave_handle_irq
+el1_irq_failure:
     invalid_exception AARCH64_EVECTOR_EL1_IRQ
 .align 7 /* 0x300 */
 el1_fiq:
@@ -251,7 +259,7 @@ el0_aarch64_irq:
     /* x0 = save area, x1 = EPC,
      * x2 = user x0, x3 = user x1,
      * x4 = user x2, x5 = user x3 */
-    b handle_irq
+    b save_handle_irq
 
     /* 30 instructions. */
 
index 59babe1..10e38ca 100644 (file)
@@ -101,14 +101,12 @@ void __attribute__ ((noreturn)) resume(arch_registers_state_t *state)
 
 void wait_for_interrupt(void)
 {
-    // REVIEW: Timer interrupt could be masked here.
-
-    // Switch to system mode with interrupts enabled. -- OLD
-    // Switch to priviledged mode with interrupts enabled.
+    // Load magic and enable interrupts.
     __asm volatile(
-        "mov    x0, #" XTR(AARCH64_MODE_PRIV) "              \n\t"
+        "mov    w0, #" XTR(WAIT_FOR_INTERRUPT_MAGIC) "              \n\t"
         "0:                                             \n\t"
 #if defined(__ARM_ARCH_8A__)
+        "msr daifclr, #2                  \n\t"
         "wfi                  \n\t"
 #else
           // If no WFI functionality exists on system, just
index 9d7b337..7b041c7 100644 (file)
@@ -201,10 +201,40 @@ void handle_user_fault(lvaddr_t fault_address, uintptr_t cause,
     resume(&resume_area);
 }
 
-void handle_irq(arch_registers_state_t* save_area, uintptr_t fault_pc,
-                uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3)
+void nosave_handle_irq(void) 
 {
     uint32_t irq = 0;
+    irq = platform_get_active_irq();
+
+    debug(SUBSYS_DISPATCH, "IRQ %"PRIu32" while %s\n", irq,
+      dcb_current ? (dcb_current->disabled ? "disabled": "enabled") :
+                    "in kernel");
+
+    static int first_timer_interrupt_fired = 0;
+    // Offer it to the timer
+    if (platform_is_timer_interrupt(irq)) {
+        if(!first_timer_interrupt_fired) {
+            printk(LOG_NOTE, "ARMv8-A: Timer interrupt received!\n");
+            first_timer_interrupt_fired = 1;
+        }
+        platform_acknowledge_irq(irq);
+#ifndef CONFIG_ONESHOT_TIMER
+        // Set next trigger
+        systime_set_timer(kernel_timeslice);
+#endif
+        wakeup_check(systime_now());
+        dispatch(schedule());
+    } else {
+        printf("%s: %d\n", __func__, irq);
+        platform_acknowledge_irq(irq);
+        send_user_interrupt(irq);
+        panic("Unhandled IRQ %"PRIu32"\n", irq);
+    }
+}
+
+void save_handle_irq(arch_registers_state_t* save_area, uintptr_t fault_pc,
+                uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3)
+{
 
     /* Save the FPU registers */
     __asm volatile(
@@ -237,49 +267,19 @@ void handle_irq(arch_registers_state_t* save_area, uintptr_t fault_pc,
     save_area->named.spsr  = armv8_SPSR_EL1_rd(NULL);
     save_area->named.pc    = fault_pc;
 
-    irq = platform_get_active_irq();
-
-    debug(SUBSYS_DISPATCH, "IRQ %"PRIu32" while %s\n", irq,
-          dcb_current ? (dcb_current->disabled ? "disabled": "enabled") :
-                        "in kernel");
-
     if (dcb_current != NULL) {
         dispatcher_handle_t handle = dcb_current->disp;
         if (save_area == dispatcher_get_disabled_save_area(handle)) {
             assert(dispatcher_is_disabled_ip(handle, fault_pc));
             dcb_current->disabled = true;
         } else {
-/*            debug(SUBSYS_DISPATCH,
-                  "save_area=%p, dispatcher_get_enabled_save_are(handle)=%p\n",
-                   save_area, dispatcher_get_enabled_save_area(handle));
-*/
-
             assert(save_area == dispatcher_get_enabled_save_area(handle));
             assert(!dispatcher_is_disabled_ip(handle, fault_pc));
             dcb_current->disabled = false;
         }
     }
-    static int first_timer_interrupt_fired = 0;
-    // Offer it to the timer
-    if (platform_is_timer_interrupt(irq)) {
-        if(!first_timer_interrupt_fired){
-            printk(LOG_NOTE, "ARMv8-A: Timer interrupt received\n");
-            first_timer_interrupt_fired = 1;
-        }
-        platform_acknowledge_irq(irq);
-        wakeup_check(systime_now());
-#ifndef CONFIG_ONESHOT_TIMER
-        // Set next trigger
-        systime_set_timer(kernel_timeslice);
-#endif
-        dispatch(schedule());
-    } else {
-        printf("%s: %d\n", __func__, irq);
-        platform_acknowledge_irq(irq);
-        send_user_interrupt(irq);
-        panic("Unhandled IRQ %"PRIu32"\n", irq);
-    }
 
+    nosave_handle_irq();
 }
 
 #define STACK_DUMP_LIMIT 32
index cfb58f4..dfb6bbc 100644 (file)
@@ -29,6 +29,8 @@
 #define AARCH32_EVECTOR_EL0_FIQ     0x12
 #define AARCH32_EVECTOR_EL0_SERROR  0x13
 
+#define WAIT_FOR_INTERRUPT_MAGIC    0x1234
+
 #if !defined(__ASSEMBLER__)
 
 enum aarch64_exception_class {
@@ -144,11 +146,21 @@ void fatal_kernel_fault(lvaddr_t epc, uint64_t spsr, uint64_t esr,
     __attribute__((noreturn));
 
 /**
- * Handle IRQs in occuring in USR or SYS mode.
+ * Handle IRQs without saving any state, hence it should be called when
+ * the state is properly saved and an interrupt occurs. Can happen
+ * from EL0 interrupt after we saved the state and EL1 (kernel) interrupt.
+ *
+ * This function should be called with interrupts disabled.
+ */
+void nosave_handle_irq(void) __attribute__((noreturn));
+
+/**
+ * Handle IRQs occuring in EL0. Expects a partially saved register
+ * state. Stores the arguments and FPU state in the save_area. 
  *
  * This function should be called with interrupts disabled.
  */
-void handle_irq(arch_registers_state_t* save_area, uintptr_t fault_pc,
+void save_handle_irq(arch_registers_state_t* save_area, uintptr_t fault_pc,
                 uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3)
     __attribute__((noreturn));