| /* |
| * Copyright (c) 2017, Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include <misc/printk.h> |
| #include <string.h> |
| #include <xtensa-asm2.h> |
| #include <kernel.h> |
| #include <ksched.h> |
| #include <kernel_structs.h> |
| #include <kernel_internal.h> |
| #include <kswap.h> |
| #include <_soc_inthandlers.h> |
| |
| void *xtensa_init_stack(int *stack_top, |
| void (*entry)(void *, void *, void *), |
| void *arg1, void *arg2, void *arg3) |
| { |
| /* We cheat and shave 16 bytes off, the top four words are the |
| * A0-A3 spill area for the caller of the entry function, |
| * which doesn't exist. It will never be touched, so we |
| * arrange to enter the function with a CALLINC of 1 and a |
| * stack pointer 16 bytes above the top, so its ENTRY at the |
| * start will decrement the stack pointer by 16. |
| */ |
| const int bsasz = BASE_SAVE_AREA_SIZE - 16; |
| void **bsa = (void **) (((char *) stack_top) - bsasz); |
| |
| (void)memset(bsa, 0, bsasz); |
| |
| bsa[BSA_PC_OFF/4] = z_thread_entry; |
| bsa[BSA_PS_OFF/4] = (void *)(PS_WOE | PS_UM | PS_CALLINC(1)); |
| |
| /* Arguments to z_thread_entry(). Remember these start at A6, |
| * which will be rotated into A2 by the ENTRY instruction that |
| * begins the C function. And A4-A7 and A8-A11 are optional |
| * quads that live below the BSA! |
| */ |
| bsa[-1] = arg1; /* a7 */ |
| bsa[-2] = entry; /* a6 */ |
| bsa[-3] = 0; /* a5 */ |
| bsa[-4] = 0; /* a4 */ |
| |
| bsa[-5] = 0; /* a11 */ |
| bsa[-6] = 0; /* a10 */ |
| bsa[-7] = arg3; /* a9 */ |
| bsa[-8] = arg2; /* a8 */ |
| |
| /* Finally push the BSA pointer and return the stack pointer |
| * as the handle |
| */ |
| bsa[-9] = bsa; |
| return &bsa[-9]; |
| } |
| |
| /* This is a kernel hook, just a wrapper around other APIs. Build |
| * only if we're using asm2 as the core OS interface and not just as |
| * utilities/testables. |
| */ |
| #ifdef CONFIG_XTENSA_ASM2 |
| void z_new_thread(struct k_thread *thread, k_thread_stack_t *stack, size_t sz, |
| k_thread_entry_t entry, void *p1, void *p2, void *p3, |
| int prio, unsigned int opts) |
| { |
| char *base = Z_THREAD_STACK_BUFFER(stack); |
| char *top = base + sz; |
| |
| /* Align downward. The API as specified requires a runtime check. */ |
| top = (char *)(((unsigned int)top) & ~3); |
| |
| z_new_thread_init(thread, base, sz, prio, opts); |
| |
| thread->switch_handle = xtensa_init_stack((void *)top, entry, |
| p1, p2, p3); |
| } |
| #endif |
| |
| #ifdef CONFIG_XTENSA_ASM2 |
| void z_irq_spurious(void *arg) |
| { |
| int irqs, ie; |
| |
| ARG_UNUSED(arg); |
| |
| __asm__ volatile("rsr.interrupt %0" : "=r"(irqs)); |
| __asm__ volatile("rsr.intenable %0" : "=r"(ie)); |
| printk(" ** Spurious INTERRUPT(s) %p, INTENABLE = %p\n", |
| (void *)irqs, (void *)ie); |
| z_NanoFatalErrorHandler(_NANO_ERR_RESERVED_IRQ, &_default_esf); |
| } |
| #endif |
| |
| static void dump_stack(int *stack) |
| { |
| int *bsa = *(int **)stack; |
| |
| printk(" ** A0 %p SP %p A2 %p A3 %p\n", |
| (void *)bsa[BSA_A0_OFF/4], ((char *)bsa) + BASE_SAVE_AREA_SIZE, |
| (void *)bsa[BSA_A2_OFF/4], (void *)bsa[BSA_A3_OFF/4]); |
| |
| if (bsa - stack > 4) { |
| printk(" ** A4 %p A5 %p A6 %p A7 %p\n", |
| (void *)bsa[-4], (void *)bsa[-3], |
| (void *)bsa[-2], (void *)bsa[-1]); |
| } |
| |
| if (bsa - stack > 8) { |
| printk(" ** A8 %p A9 %p A10 %p A11 %p\n", |
| (void *)bsa[-8], (void *)bsa[-7], |
| (void *)bsa[-6], (void *)bsa[-5]); |
| } |
| |
| if (bsa - stack > 12) { |
| printk(" ** A12 %p A13 %p A14 %p A15 %p\n", |
| (void *)bsa[-12], (void *)bsa[-11], |
| (void *)bsa[-10], (void *)bsa[-9]); |
| } |
| |
| #if XCHAL_HAVE_LOOPS |
| printk(" ** LBEG %p LEND %p LCOUNT %p\n", |
| (void *)bsa[BSA_LBEG_OFF/4], |
| (void *)bsa[BSA_LEND_OFF/4], |
| (void *)bsa[BSA_LCOUNT_OFF/4]); |
| |
| #endif |
| |
| printk(" ** SAR %p\n", (void *)bsa[BSA_SAR_OFF/4]); |
| } |
| |
| /* The wrapper code lives here instead of in the python script that |
| * generates _xtensa_handle_one_int*(). Seems cleaner, still kind of |
| * ugly. |
| */ |
| #define DEF_INT_C_HANDLER(l) \ |
| void *xtensa_int##l##_c(void *interrupted_stack) \ |
| { \ |
| u32_t irqs, intenable, m; \ |
| __asm__ volatile("rsr.interrupt %0" : "=r"(irqs)); \ |
| __asm__ volatile("rsr.intenable %0" : "=r"(intenable)); \ |
| irqs &= intenable; \ |
| while ((m = _xtensa_handle_one_int##l(irqs))) { \ |
| irqs ^= m; \ |
| __asm__ volatile("wsr.intclear %0" : : "r"(m)); \ |
| } \ |
| return z_get_next_switch_handle(interrupted_stack); \ |
| } |
| |
| DEF_INT_C_HANDLER(2) |
| DEF_INT_C_HANDLER(3) |
| DEF_INT_C_HANDLER(4) |
| DEF_INT_C_HANDLER(5) |
| DEF_INT_C_HANDLER(6) |
| DEF_INT_C_HANDLER(7) |
| |
| static inline DEF_INT_C_HANDLER(1) |
| |
| /* C handler for level 1 exceptions/interrupts. Hooked from the |
| * DEF_EXCINT 1 vector declaration in assembly code. This one looks |
| * different because exceptions and interrupts land at the same |
| * vector; other interrupt levels have their own vectors. |
| */ |
| void *xtensa_excint1_c(int *interrupted_stack) |
| { |
| int cause, vaddr, *bsa = *(int **)interrupted_stack; |
| |
| __asm__ volatile("rsr.exccause %0" : "=r"(cause)); |
| |
| if (cause == EXCCAUSE_LEVEL1_INTERRUPT) { |
| |
| return xtensa_int1_c(interrupted_stack); |
| |
| } else if (cause == EXCCAUSE_SYSCALL) { |
| |
| /* Just report it to the console for now */ |
| printk(" ** SYSCALL PS %p PC %p\n", |
| (void *)bsa[BSA_PS_OFF/4], (void *)bsa[BSA_PC_OFF/4]); |
| dump_stack(interrupted_stack); |
| |
| /* Xtensa exceptions don't automatically advance PC, |
| * have to skip the SYSCALL instruction manually or |
| * else it will just loop forever |
| */ |
| bsa[BSA_PC_OFF/4] += 3; |
| |
| } else { |
| __asm__ volatile("rsr.excvaddr %0" : "=r"(vaddr)); |
| |
| /* Wouldn't hurt to translate EXCCAUSE to a string for |
| * the user... |
| */ |
| printk(" ** FATAL EXCEPTION\n"); |
| printk(" ** CPU %d EXCCAUSE %d PS %p PC %p VADDR %p\n", |
| z_arch_curr_cpu()->id, cause, (void *)bsa[BSA_PS_OFF/4], |
| (void *)bsa[BSA_PC_OFF/4], (void *)vaddr); |
| |
| dump_stack(interrupted_stack); |
| |
| /* FIXME: legacy xtensa port reported "HW" exception |
| * for all unhandled exceptions, which seems incorrect |
| * as these are software errors. Should clean this |
| * up. |
| */ |
| z_NanoFatalErrorHandler(_NANO_ERR_HW_EXCEPTION, &_default_esf); |
| } |
| |
| return z_get_next_switch_handle(interrupted_stack); |
| } |
| |