Sfoglia il codice sorgente

RISC-V: Fix vectors.S assembly file indentation and macro usage

The file is now more consistent as the macros have been fixed, more comments
have been added and the indentation is now using spaces only.
Omar Chebib 4 anni fa
parent
commit
220e52fca1
1 ha cambiato i file con 260 aggiunte e 278 eliminazioni
  1. 260 278
      components/riscv/vectors.S

+ 260 - 278
components/riscv/vectors.S

@@ -1,16 +1,9 @@
-// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
 #include "soc/soc.h"
 #include "soc/interrupt_reg.h"
 #include "riscv/rvruntime-frames.h"
@@ -18,285 +11,274 @@
 #include "sdkconfig.h"
 
 
-	.equ SAVE_REGS, 32
-	.equ CONTEXT_SIZE, (SAVE_REGS * 4)
-	.equ panic_from_exception, xt_unhandled_exception
-	.equ panic_from_isr, panicHandler
-
-.macro save_regs
-	addi sp, sp, -CONTEXT_SIZE
-	sw   ra,  RV_STK_RA(sp)
-	sw   tp,  RV_STK_TP(sp)
-	sw   t0,  RV_STK_T0(sp)
-	sw   t1,  RV_STK_T1(sp)
-	sw   t2,  RV_STK_T2(sp)
-	sw   s0,  RV_STK_S0(sp)
-	sw   s1,  RV_STK_S1(sp)
-	sw   a0,  RV_STK_A0(sp)
-	sw   a1,  RV_STK_A1(sp)
-	sw   a2,  RV_STK_A2(sp)
-	sw   a3,  RV_STK_A3(sp)
-	sw   a4,  RV_STK_A4(sp)
-	sw   a5,  RV_STK_A5(sp)
-	sw   a6,  RV_STK_A6(sp)
-	sw   a7,  RV_STK_A7(sp)
-	sw   s2,  RV_STK_S2(sp)
-	sw   s3,  RV_STK_S3(sp)
-	sw   s4,  RV_STK_S4(sp)
-	sw   s5,  RV_STK_S5(sp)
-	sw   s6,  RV_STK_S6(sp)
-	sw   s7,  RV_STK_S7(sp)
-	sw   s8,  RV_STK_S8(sp)
-	sw   s9,  RV_STK_S9(sp)
-	sw   s10, RV_STK_S10(sp)
-	sw   s11, RV_STK_S11(sp)
-	sw   t3,  RV_STK_T3(sp)
-	sw   t4,  RV_STK_T4(sp)
-	sw   t5,  RV_STK_T5(sp)
-	sw   t6,  RV_STK_T6(sp)
+    .equ SAVE_REGS, 32
+    .equ CONTEXT_SIZE, (SAVE_REGS * 4)
+    .equ panic_from_exception, xt_unhandled_exception
+    .equ panic_from_isr, panicHandler
+
+/* Macro which first allocates space on the stack to save general
+ * purpose registers, and then save them. GP register is excluded.
+ * The default size allocated on the stack is CONTEXT_SIZE, but it
+ * can be overridden. */
+.macro save_general_regs cxt_size=CONTEXT_SIZE
+    addi sp, sp, -\cxt_size
+    sw   ra, RV_STK_RA(sp)
+    sw   tp, RV_STK_TP(sp)
+    sw   t0, RV_STK_T0(sp)
+    sw   t1, RV_STK_T1(sp)
+    sw   t2, RV_STK_T2(sp)
+    sw   s0, RV_STK_S0(sp)
+    sw   s1, RV_STK_S1(sp)
+    sw   a0, RV_STK_A0(sp)
+    sw   a1, RV_STK_A1(sp)
+    sw   a2, RV_STK_A2(sp)
+    sw   a3, RV_STK_A3(sp)
+    sw   a4, RV_STK_A4(sp)
+    sw   a5, RV_STK_A5(sp)
+    sw   a6, RV_STK_A6(sp)
+    sw   a7, RV_STK_A7(sp)
+    sw   s2, RV_STK_S2(sp)
+    sw   s3, RV_STK_S3(sp)
+    sw   s4, RV_STK_S4(sp)
+    sw   s5, RV_STK_S5(sp)
+    sw   s6, RV_STK_S6(sp)
+    sw   s7, RV_STK_S7(sp)
+    sw   s8, RV_STK_S8(sp)
+    sw   s9, RV_STK_S9(sp)
+    sw   s10, RV_STK_S10(sp)
+    sw   s11, RV_STK_S11(sp)
+    sw   t3, RV_STK_T3(sp)
+    sw   t4, RV_STK_T4(sp)
+    sw   t5, RV_STK_T5(sp)
+    sw   t6, RV_STK_T6(sp)
 .endm
 
 .macro save_mepc
-	csrr t0, mepc
-	sw   t0, RV_STK_MEPC(sp)
+    csrr t0, mepc
+    sw   t0, RV_STK_MEPC(sp)
 .endm
 
-.macro restore_regs
-	lw   ra,  RV_STK_RA(sp)
-	lw   tp,  RV_STK_TP(sp)
-	lw   t0,  RV_STK_T0(sp)
-	lw   t1,  RV_STK_T1(sp)
-	lw   t2,  RV_STK_T2(sp)
-	lw   s0,  RV_STK_S0(sp)
-	lw   s1,  RV_STK_S1(sp)
-	lw   a0,  RV_STK_A0(sp)
-	lw   a1,  RV_STK_A1(sp)
-	lw   a2,  RV_STK_A2(sp)
-	lw   a3,  RV_STK_A3(sp)
-	lw   a4,  RV_STK_A4(sp)
-	lw   a5,  RV_STK_A5(sp)
-	lw   a6,  RV_STK_A6(sp)
-	lw   a7,  RV_STK_A7(sp)
-	lw   s2,  RV_STK_S2(sp)
-	lw   s3,  RV_STK_S3(sp)
-	lw   s4,  RV_STK_S4(sp)
-	lw   s5,  RV_STK_S5(sp)
-	lw   s6,  RV_STK_S6(sp)
-	lw   s7,  RV_STK_S7(sp)
-	lw   s8,  RV_STK_S8(sp)
-	lw   s9,  RV_STK_S9(sp)
-	lw   s10, RV_STK_S10(sp)
-	lw   s11, RV_STK_S11(sp)
-	lw   t3,  RV_STK_T3(sp)
-	lw   t4,  RV_STK_T4(sp)
-	lw   t5,  RV_STK_T5(sp)
-	lw   t6,  RV_STK_T6(sp)
-	addi sp, sp, CONTEXT_SIZE
+/* Restore the general purpose registers (excluding gp) from the context on
+ * the stack. The context is then deallocated. The default size is CONTEXT_SIZE
+ * but it can be overriden. */
+.macro restore_general_regs cxt_size=CONTEXT_SIZE
+    lw   ra, RV_STK_RA(sp)
+    lw   tp, RV_STK_TP(sp)
+    lw   t0, RV_STK_T0(sp)
+    lw   t1, RV_STK_T1(sp)
+    lw   t2, RV_STK_T2(sp)
+    lw   s0, RV_STK_S0(sp)
+    lw   s1, RV_STK_S1(sp)
+    lw   a0, RV_STK_A0(sp)
+    lw   a1, RV_STK_A1(sp)
+    lw   a2, RV_STK_A2(sp)
+    lw   a3, RV_STK_A3(sp)
+    lw   a4, RV_STK_A4(sp)
+    lw   a5, RV_STK_A5(sp)
+    lw   a6, RV_STK_A6(sp)
+    lw   a7, RV_STK_A7(sp)
+    lw   s2, RV_STK_S2(sp)
+    lw   s3, RV_STK_S3(sp)
+    lw   s4, RV_STK_S4(sp)
+    lw   s5, RV_STK_S5(sp)
+    lw   s6, RV_STK_S6(sp)
+    lw   s7, RV_STK_S7(sp)
+    lw   s8, RV_STK_S8(sp)
+    lw   s9, RV_STK_S9(sp)
+    lw   s10, RV_STK_S10(sp)
+    lw   s11, RV_STK_S11(sp)
+    lw   t3, RV_STK_T3(sp)
+    lw   t4, RV_STK_T4(sp)
+    lw   t5, RV_STK_T5(sp)
+    lw   t6, RV_STK_T6(sp)
+    addi sp,sp, \cxt_size
 .endm
 
 .macro restore_mepc
-	lw	t0, RV_STK_MEPC(sp)
-	csrw	mepc, t0
+    lw      t0, RV_STK_MEPC(sp)
+    csrw    mepc, t0
 .endm
 
-	.global rtos_int_enter
-	.global rtos_int_exit
-	.global _global_interrupt_handler
-
-	.section .exception_vectors.text
-	/* This is the vector table. MTVEC points here.
-	 *
-	 * Use 4-byte intructions here. 1 instruction = 1 entry of the table.
-	 * The CPU jumps to MTVEC (i.e. the first entry) in case of an exception,
-	 * and (MTVEC & 0xfffffffc) + (mcause & 0x7fffffff) * 4, in case of an interrupt.
-	 *
-	 * Note: for our CPU, we need to place this on a 256-byte boundary, as CPU
-	 * only uses the 24 MSBs of the MTVEC, i.e. (MTVEC & 0xffffff00).
-	 */
-
-	.balign 0x100
-	.global _vector_table
-	.type _vector_table, @function
+    .global rtos_int_enter
+    .global rtos_int_exit
+    .global _global_interrupt_handler
+
+    .section .exception_vectors.text
+    /* This is the vector table. MTVEC points here.
+     *
+     * Use 4-byte intructions here. 1 instruction = 1 entry of the table.
+     * The CPU jumps to MTVEC (i.e. the first entry) in case of an exception,
+     * and (MTVEC & 0xfffffffc) + (mcause & 0x7fffffff) * 4, in case of an interrupt.
+     *
+     * Note: for our CPU, we need to place this on a 256-byte boundary, as CPU
+     * only uses the 24 MSBs of the MTVEC, i.e. (MTVEC & 0xffffff00).
+     */
+
+    .balign 0x100
+    .global _vector_table
+    .type _vector_table, @function
 _vector_table:
-	.option push
-	.option norvc
-	j _panic_handler			/* exception handler, entry 0 */
-	.rept (ETS_T1_WDT_INUM - 1)
-	j _interrupt_handler		/* 24 identical entries, all pointing to the interrupt handler */
-	.endr
-	j _panic_handler			/* Call panic handler for ETS_T1_WDT_INUM interrupt (soc-level panic)*/
+    .option push
+    .option norvc
+    j _panic_handler			/* exception handler, entry 0 */
+    .rept (ETS_T1_WDT_INUM - 1)
+    j _interrupt_handler		/* 24 identical entries, all pointing to the interrupt handler */
+    .endr
+    j _panic_handler			/* Call panic handler for ETS_T1_WDT_INUM interrupt (soc-level panic)*/
     j _panic_handler			/* Call panic handler for ETS_CACHEERR_INUM interrupt (soc-level panic)*/
     #ifdef CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
     j _panic_handler			/* Call panic handler for ETS_MEMPROT_ERR_INUM interrupt (soc-level panic)*/
-	.rept (ETS_MAX_INUM - ETS_MEMPROT_ERR_INUM)
-	#else
-	.rept (ETS_MAX_INUM - ETS_CACHEERR_INUM)
-	#endif
-	j _interrupt_handler		/* 6 identical entries, all pointing to the interrupt handler */
-	.endr
-
-	.option pop
-	.size _vector_table, .-_vector_table
-
-	/* Exception handler.*/
-	.type _panic_handler, @function
+    .rept (ETS_MAX_INUM - ETS_MEMPROT_ERR_INUM)
+    #else
+    .rept (ETS_MAX_INUM - ETS_CACHEERR_INUM)
+    #endif //CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
+    j _interrupt_handler		/* 6 identical entries, all pointing to the interrupt handler */
+    .endr
+
+    .option pop
+    .size _vector_table, .-_vector_table
+
+    /* Exception handler.*/
+    .type _panic_handler, @function
 _panic_handler:
-	addi sp, sp, -RV_STK_FRMSZ /* allocate space on stack to store necessary registers */
-	/* save general registers */
-	sw ra,  RV_STK_RA(sp)
-	sw gp,  RV_STK_GP(sp)
-	sw tp,  RV_STK_TP(sp)
-	sw t0,  RV_STK_T0(sp)
-	sw t1,  RV_STK_T1(sp)
-	sw t2,  RV_STK_T2(sp)
-	sw s0,  RV_STK_S0(sp)
-	sw s1,  RV_STK_S1(sp)
-	sw a0,  RV_STK_A0(sp)
-	sw a1,  RV_STK_A1(sp)
-	sw a2,  RV_STK_A2(sp)
-	sw a3,  RV_STK_A3(sp)
-	sw a4,  RV_STK_A4(sp)
-	sw a5,  RV_STK_A5(sp)
-	sw a6,  RV_STK_A6(sp)
-	sw a7,  RV_STK_A7(sp)
-	sw s2,  RV_STK_S2(sp)
-	sw s3,  RV_STK_S3(sp)
-	sw s4,  RV_STK_S4(sp)
-	sw s5,  RV_STK_S5(sp)
-	sw s6,  RV_STK_S6(sp)
-	sw s7,  RV_STK_S7(sp)
-	sw s8,  RV_STK_S8(sp)
-	sw s9,  RV_STK_S9(sp)
-	sw s10, RV_STK_S10(sp)
-	sw s11, RV_STK_S11(sp)
-	sw t3,  RV_STK_T3(sp)
-	sw t4,  RV_STK_T4(sp)
-	sw t5,  RV_STK_T5(sp)
-	sw t6,  RV_STK_T6(sp)
-	addi t0, sp, RV_STK_FRMSZ /* restore sp with the value when trap happened */
-	sw t0,  RV_STK_SP(sp)
-	csrr t0, mepc
-	sw t0,  RV_STK_MEPC(sp)
-	csrr t0, mstatus
-	sw t0,  RV_STK_MSTATUS(sp)
-	csrr t0, mtvec
-	sw t0,  RV_STK_MTVEC(sp)
-	csrr t0, mtval
-	sw t0,  RV_STK_MTVAL(sp)
-	csrr t0, mhartid
-	sw t0,  RV_STK_MHARTID(sp)
-
-	/* Call panic_from_exception(sp) or panic_from_isr(sp)
-	 * depending on whether we have a pseudo excause or not.
-	 * If mcause's highest bit is 1, then an interrupt called this routine,
-	 * so we have a pseudo excause. Else, it is due to a exception, we don't
-         * have an pseudo excause */
-	mv a0, sp
-	csrr a1, mcause
-	/* Branches instructions don't accept immediates values, so use t1 to
-         * store our comparator */
-	li t0, 0x80000000
-	bgeu a1, t0, _call_panic_handler
-	sw a1,  RV_STK_MCAUSE(sp)
-	/* exception_from_panic never returns */
-	j panic_from_exception
+    /* Allocate space on the stack and store general purpose registers */
+    save_general_regs RV_STK_FRMSZ
+
+    /* As gp register is not saved by the macro, save it here */
+    sw    gp, RV_STK_GP(sp)
+
+    /* Same goes for the SP value before trapping */
+    addi  t0, sp, RV_STK_FRMSZ /* restore sp with the value when trap happened */
+
+    /* Save CSRs */
+    sw    t0, RV_STK_SP(sp)
+    csrr  t0, mepc
+    sw    t0, RV_STK_MEPC(sp)
+    csrr  t0, mstatus
+    sw    t0, RV_STK_MSTATUS(sp)
+    csrr  t0, mtvec
+    sw    t0, RV_STK_MTVEC(sp)
+    csrr  t0, mtval
+    sw    t0, RV_STK_MTVAL(sp)
+    csrr  t0, mhartid
+    sw    t0, RV_STK_MHARTID(sp)
+
+    /* Call panic_from_exception(sp) or panic_from_isr(sp)
+     * depending on whether we have a pseudo excause or not.
+     * If mcause's highest bit is 1, then an interrupt called this routine,
+     * so we have a pseudo excause. Else, it is due to a exception, we don't
+     * have an pseudo excause */
+    mv    a0, sp
+    csrr  a1, mcause
+    /* Branches instructions don't accept immediates values, so use t1 to
+     * store our comparator */
+    li    t0, 0x80000000
+    bgeu  a1, t0, _call_panic_handler
+    sw    a1, RV_STK_MCAUSE(sp)
+    /* exception_from_panic never returns */
+    j panic_from_exception
 _call_panic_handler:
-	/* Remove highest bit from mcause (a1) register and save it in the
-	 * structure */
-	not t0, t0
-	and a1, a1, t0
-	sw a1, RV_STK_MCAUSE(sp)
-	/* exception_from_isr never returns */
-	j panic_from_isr
-	.size  panic_from_isr, .-panic_from_isr
-
-	/* This is the interrupt handler.
-	 * It saves the registers on the stack,
-	 * prepares for interrupt nesting,
-	 * re-enables the interrupts,
-	 * then jumps to the C dispatcher in interrupt.c.
-	 */
-	.global _interrupt_handler
-	.type _interrupt_handler, @function
+    /* Remove highest bit from mcause (a1) register and save it in the
+     * structure */
+    not   t0, t0
+    and   a1, a1, t0
+    sw    a1, RV_STK_MCAUSE(sp)
+    /* exception_from_isr never returns */
+    j panic_from_isr
+    .size  panic_from_isr, .-panic_from_isr
+
+    /* This is the interrupt handler.
+     * It saves the registers on the stack,
+     * prepares for interrupt nesting,
+     * re-enables the interrupts,
+     * then jumps to the C dispatcher in interrupt.c.
+     */
+    .global _interrupt_handler
+    .type _interrupt_handler, @function
 _interrupt_handler:
-	/* entry */
-	save_regs
-	save_mepc
-
-	/* Before doing anythig preserve the stack pointer */
-	/* It will be saved in current TCB, if needed */
-	mv a0, sp
-	call rtos_int_enter
-
-	/* Before dispatch c handler, restore interrupt to enable nested intr */
-	csrr s1, mcause
-	csrr s2, mstatus
-
-	/* Save the interrupt threshold level */
-	la t0, INTERRUPT_CORE0_CPU_INT_THRESH_REG
-	lw s3, 0(t0)
-
-	/* Increase interrupt threshold level */
-	li t2, 0x7fffffff
-	and t1, s1, t2		/* t1 = mcause & mask */
-	slli t1, t1, 2 		/* t1 = mcause * 4 */
-	la t2, INTC_INT_PRIO_REG(0)
-	add t1, t2, t1		/* t1 = INTC_INT_PRIO_REG + 4 * mcause */
-	lw t2, 0(t1)		/* t2 = INTC_INT_PRIO_REG[mcause] */
-	addi t2, t2, 1		/* t2 = t2 +1 */
-	sw t2, 0(t0)		/* INTERRUPT_CORE0_CPU_INT_THRESH_REG = t2 */
-	fence
-
-	li t0, 0x8
-	csrrs t0, mstatus, t0
-
-	#ifdef CONFIG_PM_TRACE
-	li      a0, 0       /* = ESP_PM_TRACE_IDLE */
-	#if SOC_CPU_CORES_NUM == 1
-	li      a1, 0       /* No need to check core ID on single core hardware */
-	#else
-	csrr    a1, mhartid
-	#endif
-	la      t0, esp_pm_trace_exit
-	jalr    t0          /* absolute jump, avoid the 1 MiB range constraint */
-	#endif
-
-	#ifdef CONFIG_PM_ENABLE
-	la      t0, esp_pm_impl_isr_hook
-	jalr    t0          /* absolute jump, avoid the 1 MiB range constraint */
-	#endif
-
-	/* call the C dispatcher */
-	mv      a0, sp      /* argument 1, stack pointer */
-	mv      a1, s1      /* argument 2, interrupt number (mcause) */
-	/* mask off the interrupt flag of mcause */
-	li	    t0, 0x7fffffff
-	and     a1, a1, t0
-	jal     _global_interrupt_handler
-
-	/* After dispatch c handler, disable interrupt to make freertos make context switch */
-
-	li t0, 0x8
-	csrrc t0, mstatus, t0
-
-	/* restore the interrupt threshold level */
-	la t0, INTERRUPT_CORE0_CPU_INT_THRESH_REG
-	sw s3, 0(t0)
-	fence
-
-	/* Yield to the next task is needed: */
-	mv a0, sp
-	call rtos_int_exit
-
-	/* The next (or current) stack pointer is returned in a0 */
-	mv sp, a0
-
-	/* restore the rest of the registers */
-	csrw mcause, s1
-	csrw mstatus, s2
-	restore_mepc
-	restore_regs
-
-	/* exit, this will also re-enable the interrupts */
-	mret
-	.size  _interrupt_handler, .-_interrupt_handler
+    /* Start by saving the general purpose registers and the PC value before
+     * the interrupt happened. */
+    save_general_regs
+    save_mepc
+
+    /* Before doing anythig preserve the stack pointer */
+    /* It will be saved in current TCB, if needed */
+    mv      a0, sp
+    call    rtos_int_enter
+    /* If this is a non-nested interrupt, SP now points to the interrupt stack */
+
+    /* Before dispatch c handler, restore interrupt to enable nested intr */
+    csrr    s1, mcause
+    csrr    s2, mstatus
+
+    /* Save the interrupt threshold level */
+    la      t0, INTERRUPT_CORE0_CPU_INT_THRESH_REG
+    lw      s3, 0(t0)
+
+    /* Increase interrupt threshold level */
+    li      t2, 0x7fffffff
+    and     t1, s1, t2       /* t1 = mcause & mask */
+    slli    t1, t1, 2        /* t1 = mcause * 4 */
+    la      t2, INTC_INT_PRIO_REG(0)
+    add     t1, t2, t1       /* t1 = INTC_INT_PRIO_REG + 4 * mcause */
+    lw      t2, 0(t1)        /* t2 = INTC_INT_PRIO_REG[mcause] */
+    addi    t2, t2, 1        /* t2 = t2 +1 */
+    sw      t2, 0(t0)        /* INTERRUPT_CORE0_CPU_INT_THRESH_REG = t2 */
+    fence
+
+    li      t0, 0x8
+    csrrs   t0, mstatus, t0
+    /* MIE set. Nested interrupts can now occur */
+
+    #ifdef CONFIG_PM_TRACE
+    li      a0, 0       /* = ESP_PM_TRACE_IDLE */
+    #if SOC_CPU_CORES_NUM == 1
+    li      a1, 0       /* No need to check core ID on single core hardware */
+    #else
+    csrr    a1, mhartid
+    #endif
+    la      t0, esp_pm_trace_exit
+    jalr    t0          /* absolute jump, avoid the 1 MiB range constraint */
+    #endif
+
+    #ifdef CONFIG_PM_ENABLE
+    la      t0, esp_pm_impl_isr_hook
+    jalr    t0          /* absolute jump, avoid the 1 MiB range constraint */
+    #endif
+
+    /* call the C dispatcher */
+    mv      a0, sp      /* argument 1, stack pointer */
+    mv      a1, s1      /* argument 2, interrupt number (mcause) */
+    /* mask off the interrupt flag of mcause */
+    li	    t0, 0x7fffffff
+    and     a1, a1, t0
+    jal     _global_interrupt_handler
+
+    /* After dispatch c handler, disable interrupt to make freertos make context switch */
+
+    li      t0, 0x8
+    csrrc   t0, mstatus, t0
+    /* MIE cleared. Nested interrupts are disabled */
+
+    /* restore the interrupt threshold level */
+    la      t0, INTERRUPT_CORE0_CPU_INT_THRESH_REG
+    sw      s3, 0(t0)
+    fence
+
+    /* Yield to the next task is needed: */
+    mv      a0, sp
+    call    rtos_int_exit
+    /* If this is a non-nested interrupt, context switch called, SP now points to back to task stack. */
+
+    /* The next (or current) stack pointer is returned in a0 */
+    mv      sp, a0
+
+    /* restore the rest of the registers */
+    csrw    mcause, s1
+    csrw    mstatus, s2
+    restore_mepc
+    restore_general_regs
+
+    /* exit, this will also re-enable the interrupts */
+    mret
+    .size  _interrupt_handler, .-_interrupt_handler