System Calls cleanup, multiple Processes and context switch
[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_ICSR & 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 (<-- last function call)\n",frame.pc);
140         printf("PSR:%p\n",frame.psr);
141         
142         printf("CPU STATUS:");
143         printf("%x\n", *SCB_CFSR);
144         for(;;); 
145 }
146
147 /* Initialize interrupt vector  */
148 void ivt_init() {
149
150         /* clear entire IVT location in memory  */
151         memset(&ivt, 0, (sizeof(uint32_t) * 92));
152
153         /* The reset, NMI and hardfault handlers are originally
154          * defined in the assembly start up and can be 
155          * reused or overwritten. 
156         *  */
157         extern void * reset,  * nmi, * hardfault;
158
159         /* set dummy handlers */
160         for (int i = 1; i <= 64 ; i++) {
161                 ivt_set_gate(i, dummy_isr, 0);
162         }
163
164
165         /* Enable memory management, bus and usage fault exceptions handlers
166          * If these are not enabled, the processor treats them as a hard
167          * faults. Unpriviliged access will cause a busfault in case no MPU */
168         rsetbit(SCB_SHCSR, 16); // MPU violation
169         rsetbit(SCB_SHCSR, 17); // Bus faults
170         rsetbit(SCB_SHCSR, 18); // Usage faults
171         
172         /* Enable various other faults */
173         // rsetbit(SCB_CCR, 4); // division by zero, (needed if you write a lot
174         // of assembly, otherwise the compiler probably leave these divisions out
175
176
177         /* The vector table is intially at 0x0. The vector table can be
178          * relocated to other memory locations. We can do this by setting 
179          * a register in the NVIC called the vector table offset register */
180
181         rwrite(SCB_VTOR, (uint32_t) &ivt);
182
183 }