|
| 1 | +/* |
| 2 | + * Copyright (c) 2023 Advanced Micro Devices, Inc. (AMD) |
| 3 | + * Copyright (c) 2023 Alp Sayin <alpsayin@gmail.com> |
| 4 | + * |
| 5 | + * SPDX-License-Identifier: Apache-2.0 |
| 6 | + */ |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | +#include <zephyr/init.h> |
| 11 | +#include <zephyr/kernel.h> |
| 12 | +#include <zephyr/logging/log.h> |
| 13 | +#include <zephyr/sys/printk.h> |
| 14 | + |
| 15 | +#include <offsets_short.h> |
| 16 | + |
| 17 | +/* Hardware includes. */ |
| 18 | +#include "microblaze/mb_interface.h" |
| 19 | +#include "microblaze/microblaze_regs.h" |
| 20 | + |
| 21 | +LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL); |
| 22 | + |
| 23 | +/* The number of bytes a MicroBlaze instruction consumes. */ |
| 24 | +#define MICROBLAZE_INSTRUCTION_SIZE 4 |
| 25 | + |
| 26 | +extern void _exception_handler_entry(void *exception_id); |
| 27 | + |
| 28 | +/* Used by assembly routine _exception_handler_entry to store sp */ |
| 29 | +void *stack_pointer_on_exception_entry; |
| 30 | + |
| 31 | +FUNC_NORETURN void z_microblaze_fatal_error(unsigned int reason, |
| 32 | + const microblaze_register_dump_t *dump) |
| 33 | +{ |
| 34 | + if (dump != NULL && IS_ENABLED(CONFIG_MICROBLAZE_DUMP_ON_EXCEPTION)) { |
| 35 | + printk("r1:\t0x%08x\t(sp)\n", dump->esf.r1); |
| 36 | + printk("r2:\t0x%08x\t(small data area)\n", dump->esf.r2); |
| 37 | + printk("r3:\t0x%x\t\t(retval 1)\n", dump->esf.r3); |
| 38 | + printk("r4:\t0x%x\t\t(retval 2)\n", dump->esf.r4); |
| 39 | + printk("r5:\t0x%x\t\t(arg1)\n", dump->esf.r5); |
| 40 | + printk("r6:\t0x%x\t\t(arg2)\n", dump->esf.r6); |
| 41 | + printk("r7:\t0x%x\t\t(arg3)\n", dump->esf.r7); |
| 42 | + printk("r8:\t0x%x\t\t(arg4)\n", dump->esf.r8); |
| 43 | + printk("r9:\t0x%x\t\t(arg5)\n", dump->esf.r9); |
| 44 | + printk("r10:\t0x%x\t\t(arg6)\n", dump->esf.r10); |
| 45 | + printk("r11:\t0x%08x\t(temp1)\n", dump->esf.r11); |
| 46 | + printk("r12:\t0x%08x\t(temp2)\n", dump->esf.r12); |
| 47 | + printk("r13:\t0x%08x\t(rw small data area)\n", dump->esf.r13); |
| 48 | + printk("r14:\t0x%08x\t(return from interrupt)\n", dump->esf.r14); |
| 49 | + printk("r15:\t0x%08x\t(return from subroutine)\n", dump->esf.r15); |
| 50 | + printk("r16:\t0x%08x\t(return from trap)\n", dump->esf.r16); |
| 51 | + printk("r17:\t0x%08x\t(return from exception)\n", dump->esf.r17); |
| 52 | + printk("r18:\t0x%08x\t(compiler/assembler temp)\n", dump->esf.r18); |
| 53 | + printk("r19:\t0x%08x\t(global offset table ptr)\n", dump->esf.r19); |
| 54 | + printk("r20:\t0x%x\n", dump->esf.r20); |
| 55 | + printk("r21:\t0x%x\n", dump->esf.r21); |
| 56 | + printk("r22:\t0x%x\n", dump->esf.r22); |
| 57 | + printk("r23:\t0x%x\n", dump->esf.r23); |
| 58 | + printk("r24:\t0x%x\n", dump->esf.r24); |
| 59 | + printk("r25:\t0x%x\n", dump->esf.r25); |
| 60 | + printk("r26:\t0x%x\n", dump->esf.r26); |
| 61 | + printk("r27:\t0x%x\n", dump->esf.r27); |
| 62 | + printk("r28:\t0x%x\n", dump->esf.r28); |
| 63 | + printk("r29:\t0x%x\n", dump->esf.r29); |
| 64 | + printk("r30:\t0x%x\n", dump->esf.r30); |
| 65 | + printk("r31:\t0x%x\n", dump->esf.r31); |
| 66 | + |
| 67 | + printk("MSR:\t0x%08x\t(exc)\n", dump->esf.msr); |
| 68 | +#if defined(CONFIG_USE_HARDWARE_FLOAT_INSTR) |
| 69 | + printk("FSR:\t%08x\n", dump->esf.fsr); |
| 70 | +#endif |
| 71 | + printk("ESR:\t0x%08x\n", dump->esr); |
| 72 | + printk("EAR:\t0x%x\n", dump->ear); |
| 73 | + printk("EDR:\t0x%x\n", dump->edr); |
| 74 | + printk("PC:\t0x%x\n", dump->pc); |
| 75 | + } |
| 76 | + |
| 77 | + /* This hack allows us to re-enable exceptions properly before continuing. |
| 78 | + * r15 is safe to use becaue this function is noreturn. |
| 79 | + */ |
| 80 | + __asm__ volatile("\tmfs r15, rpc\n" |
| 81 | + "\trted r15, 0x8\n" |
| 82 | + "\tnop\n"); |
| 83 | + |
| 84 | + printk("MSR:\t0x%08x\t(%s)\n", mfmsr(), __func__); |
| 85 | + |
| 86 | + z_fatal_error(reason, &dump->esf); |
| 87 | + CODE_UNREACHABLE; |
| 88 | +} |
| 89 | + |
| 90 | +#if defined(CONFIG_EXTRA_EXCEPTION_INFO) |
| 91 | +static char *cause_str(uint32_t cause) |
| 92 | +{ |
| 93 | + switch (cause) { |
| 94 | + case 0: |
| 95 | + return "stream exception"; |
| 96 | + case 1: |
| 97 | + return "unaligned data access exception"; |
| 98 | + case 2: |
| 99 | + return "illegal op-code exception"; |
| 100 | + case 3: |
| 101 | + return "instruction bus error exception"; |
| 102 | + case 4: |
| 103 | + return "data bus error exception"; |
| 104 | + case 5: |
| 105 | + return "divide exception"; |
| 106 | + case 6: |
| 107 | + return "floating point unit exception"; |
| 108 | + case 7: |
| 109 | + return "privileged instruction exception"; |
| 110 | + case 8: |
| 111 | + return "stack protection violation exception"; |
| 112 | + case 9: |
| 113 | + return "data storage exception"; |
| 114 | + case 10: |
| 115 | + return "instruction storage exception"; |
| 116 | + case 11: |
| 117 | + return "data TLB miss exception"; |
| 118 | + case 12: |
| 119 | + return "instruction TLB miss exception"; |
| 120 | + default: |
| 121 | + return "unknown"; |
| 122 | + } |
| 123 | +} |
| 124 | +#endif /* defined(CONFIG_EXTRA_EXCEPTION_INFO) */ |
| 125 | + |
| 126 | +FUNC_NORETURN void _Fault(uint32_t esr, uint32_t ear, uint32_t edr) |
| 127 | +{ |
| 128 | + static microblaze_register_dump_t microblaze_register_dump = {0}; |
| 129 | + /* Log the simplest possible exception information before anything */ |
| 130 | + uint32_t cause = (mfesr() & CAUSE_EXP_MASK) >> CAUSE_EXP_SHIFT; |
| 131 | + |
| 132 | + LOG_ERR(""); |
| 133 | +#if defined(CONFIG_EXTRA_EXCEPTION_INFO) |
| 134 | + LOG_ERR("Cause: %d, %s", cause, cause_str(cause)); |
| 135 | +#endif /* defined(CONFIG_EXTRA_EXCEPTION_INFO) */ |
| 136 | + |
| 137 | + /* Fill an register dump structure with the MicroBlaze context as it |
| 138 | + * was immediately before the exception occurrence. |
| 139 | + */ |
| 140 | + |
| 141 | + __ASSERT_NO_MSG(stack_pointer_on_exception_entry); |
| 142 | + z_arch_esf_t *sp_ptr = (z_arch_esf_t *)stack_pointer_on_exception_entry; |
| 143 | + |
| 144 | + /* Obtain the values of registers that were stacked prior to this function |
| 145 | + * being called, and may have changed since they were stacked. |
| 146 | + */ |
| 147 | + microblaze_register_dump.esf = *sp_ptr; |
| 148 | + microblaze_register_dump.esf.r1 = ((uint32_t)sp_ptr) + sizeof(z_arch_esf_t); |
| 149 | + microblaze_register_dump.esr = esr; |
| 150 | + microblaze_register_dump.ear = ear; |
| 151 | + microblaze_register_dump.edr = edr; |
| 152 | + |
| 153 | + /* Move the saved program counter back to the instruction that was executed |
| 154 | + * when the exception occurred. This is only valid for certain types of |
| 155 | + * exception. |
| 156 | + */ |
| 157 | + microblaze_register_dump.pc = |
| 158 | + microblaze_register_dump.esf.r17 - MICROBLAZE_INSTRUCTION_SIZE; |
| 159 | + |
| 160 | + /* Also fill in a string that describes what type of exception this is. |
| 161 | + * The string uses the same ID names as defined in the MicroBlaze standard |
| 162 | + * library exception header files. |
| 163 | + */ |
| 164 | +#if defined(CONFIG_EXTRA_EXCEPTION_INFO) |
| 165 | + microblaze_register_dump.exception_cause_str = cause_str(cause); |
| 166 | +#endif /* defined(CONFIG_EXTRA_EXCEPTION_INFO) */ |
| 167 | + |
| 168 | + z_microblaze_fatal_error(K_ERR_CPU_EXCEPTION, µblaze_register_dump); |
| 169 | + CODE_UNREACHABLE; |
| 170 | +} |
0 commit comments