SYSCALL cleanup and ivt rewrite
[cortex-from-scratch] / ivt.c
1 /* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
2  * 
3  * $LOG$
4  * 2019/7/20 - ROBIN KRENS      
5  * Initial version 
6  * 
7  * $DESCRIPTION$
8  * Set up of basic exceptions and interrupts. These interrupts
9  * don't do much, except for halting the system. 
10  * ivt_set_gate(interrupt nr, function, priority) can be used
11  * later to define more appropriate handling. See timer (timer.c) 
12  * or serial or (uart.c) handling for non-trivial examples.
13  *
14  * The actual code is not much, but there are a lot of details
15  * to consider. Besides that, in case more control is desired over
16  * entering and exiting interrupts (what is pushed on the stack) 
17  * A so-called naked function can be used. See below for more
18  * details.
19  * 
20  *
21  * */
22
23 #include <stdbool.h>
24 #include <stddef.h>
25 #include <stdint.h>
26
27 #include <sys/robsys.h>
28 #include <sys/mmap.h>
29
30 #include <lib/stdio.h>
31 #include <lib/string.h>
32 #include <lib/regfunc.h>
33 #include <lib/tinyprintf.h>
34
35 /* 
36  * These values are pushed on the stack just before
37  * entering the ISR. I use a so-called
38  * 'naked function' __attribute__ ((naked)) which
39  * gives me a little bit more control over the caller
40  *
41  * The following register are pushed to the stack
42  * in reverse order:
43  *
44  * */
45 struct interrupt_frame {
46
47         uint32_t r0; // N-32
48         uint32_t r1;
49         uint32_t r2;
50         uint32_t r3;
51         uint32_t r12;
52         uint32_t lr;
53         uint32_t pc;
54         uint32_t psr; // N-4
55 } frame;
56
57 /* 
58  * Vector table, each entry contains an interrupt
59  * service routine: 
60  * interrupt vector 1-15: processor exceptions
61  * interrupt vector 16-92: irq0 - irq ..
62  * Vector table needs to be aligned in memory.
63  * */
64 uint32_t __attribute__((aligned(0x100))) ivt[92];
65
66 /* Each message corresponds to an exception in the vector table. */
67 char * exception_message(uint8_t intnr) {
68
69 char * messages[] = {
70     "--",
71     "RESET",
72     "NMI",
73     "HARD FAULT",
74     "MEMMANAGE FAULT",
75     "BUS FAULT",
76     "USAGE FAULT",
77     "RESERVED",
78     "RESERVED",
79     "RESERVED",
80     "RESERVED",
81     "SVC",
82     "DEBUG MONITOR",
83     "RESERVED",
84     "RESERVED",
85     "RESERVED",
86     "RESERVED",
87     "PENDSV",
88     "SYSTICK",
89     "IRQ1",
90     "IRQ2",
91     "IRQ3",
92     "IRQ4",
93     // add more if needed
94 };
95
96 if (intnr < 20) // TODO: strlen
97         return messages[intnr];
98
99 return "UNKNOWN";
100
101 }
102
103 /* Function to set entries of the interrupt vector table 
104  * */
105 void ivt_set_gate(unsigned char num, void * isr(), short pri) {
106
107         ivt[num] = (uint32_t) isr;
108         //if (num <= 32)
109         //*NVIC_ISER0 = (1 << ((uint32_t)(num) & 0x1F));
110         /* TODO: Priorities */
111 }
112
113
114 /* Dummy interrupt: shows the saved stack registers and then
115  * halts */
116 __attribute__ ((naked)) void * dummy_isr(void) {
117
118         uint32_t * current_sp;
119
120         /* Test whether system call was invoked from supervisor (use MSP) or
121          * user (use PSP) mode */
122         asm volatile (
123         "tst lr, #4" "\n\t"
124         "ite eq" "\n\t"
125         "mrseq %0, msp" "\n\t"
126         "mrsne %0, psp" : "=r" (current_sp));
127
128         memcpy(&frame, current_sp, sizeof(struct interrupt_frame));
129
130         uint8_t nr = *SCB_VTOR_ST & 0xFF;
131         printf("EXCEPTION: %s\n", exception_message(nr));
132         printf("STACKFRAME:\n");
133         printf("R0:%p\n",frame.r0);
134         printf("R1:%p\n",frame.r1);
135         printf("R2:%p\n",frame.r2);
136         printf("R3:%p\n",frame.r3);
137         printf("R12:%p\n",frame.r12);
138         printf("LR:%p\n",frame.lr);
139         printf("PC:%p\n",frame.pc);
140         printf("PSR:%p\n",frame.psr);
141         
142         for(;;); 
143 }
144
145 /* Initialize interrupt vector  */
146 void ivt_init() {
147
148         /* clear entire IVT location in memory  */
149         memset(&ivt, 0, (sizeof(uint32_t) * 92));
150
151         /* The reset, NMI and hardfault handlers are originally
152          * defined in the assembly start up and can be 
153          * reused or overwritten. 
154         *  */
155         extern void * reset,  * nmi, * hardfault;
156
157         // set dummy handlers 
158         for (int i = 1; i <= 64 ; i++) {
159                 ivt_set_gate(i, dummy_isr, 0);
160         }
161
162         /* The vector table is intially at 0x0. The vector table can be
163          * relocated to other memory locations. We can do this by setting 
164          * a register in the NVIC called the vector table offset register */
165
166         rwrite(SCB_VTOR, (uint32_t) &ivt);
167
168 }