SYSCALL naked assembly working
[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. Normally, this would require
38  * assembly or inline assembly code. I use a so-called
39  * 'naked function' __attribute__ ((interrupt)) which
40  * gives me a little bit more control over the caller
41  *
42  * The following register are pushed to the stack
43  * in reverse order:
44  *
45  * */
46 struct interrupt_frame {
47
48         uint32_t r0; // N-32
49         uint32_t r1;
50         uint32_t r2;
51         uint32_t r3;
52         uint32_t r12;
53         uint32_t lr;
54         uint32_t pc;
55         uint32_t psr; // N-4
56 };
57
58 /* 
59  * Vector table, each entry contains an interrupt
60  * service routine: 
61  * interrupt vector 1-15: processor exceptions
62  * interrupt vector 16-92: irq0 - irq ..
63  * Vector table needs to be aligned in memory.
64  * */
65 uint32_t __attribute__((aligned(0x100))) ivt[92];
66
67 /* Each message corresponds to each and every exception. */
68 char * exception_message(uint8_t intnr) {
69
70 char * messages[] = {
71     "--",
72     "RESET",
73     "NMI",
74     "HARD FAULT",
75     "MEMMANAGE FAULT",
76     "BUS FAULT",
77     "USAGE FAULT",
78     "RESERVED",
79     "RESERVED",
80     "RESERVED",
81     "RESERVED",
82     "SVC",
83     "DEBUG MONITOR",
84     "RESERVED",
85     "RESERVED",
86     "RESERVED",
87     "RESERVED",
88     "PENDSV",
89     "SYSTICK",
90     "IRQ1",
91     "IRQ2",
92     "IRQ3",
93     "IRQ4",
94     // add more if needed
95 };
96
97 if (intnr < 20) // TODO: strlen
98         return messages[intnr];
99
100 return "UNKNOWN";
101 }
102
103 void ivt_set_gate(unsigned char num, void * isr(), short pri) {
104
105         ivt[num] = (uint32_t) isr;
106 //      if (num <= 32)
107 //              *NVIC_ISER0 = (1 << ((uint32_t)(num) & 0x1F));
108         /* TODO: Priorities */
109 }
110
111
112 /* Dummy interrupt: comment out the comment to use a naked
113  * function */
114
115 struct interrupt_frame * frame;
116
117 //__attribute__ ((interrupt)) 
118 void * dummy_isr( struct interrupt_frame * f ) {
119
120         //uint32_t link_register = 0;
121         //asm ("mov %0, #33" : "=r" (lp));
122         
123         /* Check Link Register:
124          * If you ever need to debug these: here are the most common
125          * values
126          * 0xFFFFFFF1: return to handler mode (nested interrupts
127          * 0xFFFFFFF9: return to thread mode using main stack
128          * 0xFFFFFFFD: return to thread mode using process stack */
129         // asm ("mov %0, lr" : "=r" (link_register));
130         // printf("%x\n", link_register);
131         // for(;;);
132
133 //      asm volatile ("push {r0-r12}"); 
134 //      int * stack;
135 //      asm volatile ("tst lr, #4" "\n\t" 
136 //      "ite eq" "\n\t"
137 //      "mrseq %0, msp" "\n\t" 
138 //      "mrsne r0, psp" : "=r" (stack));
139 //      
140 //      printf("STACK:  %x, %x,  %x, %x, %x", stack[32], stack[28], stack[24], stack[20], stack[16]);
141
142 //      asm volatile ("CPSID f");
143
144 //      uint32_t tmp = args[0];
145 //      uint32_t tmp2 = args[1];
146 //      printf("%x, %x\n", tmp, tmp2); 
147 //      uint32_t tmp3 = args[2];
148 //      uint32_t tmp4 = args[3];
149 //      printf("%x, %x\n", tmp3, tmp4); 
150
151         struct interrupt_frame * frame = (struct interrupt_frame * )kalloc(get_kheap());
152         memcpy(frame, f, sizeof(struct interrupt_frame));
153
154         uint8_t nr = *SCB_VTOR_ST & 0xFF;
155         printf("EXCEPTION: %s\n", exception_message(nr));
156         printf("STACK TRACE:\n");
157         printf("R0:%p\n",frame->r0);
158         printf("R1:%p\n",frame->r1);
159         printf("R2:%p\n",frame->r2);
160         printf("R3:%p\n",frame->r3);
161         printf("R12:%p\n",frame->r12);
162         printf("LR:%p\n",frame->lr);
163         printf("PC:%p\n",frame->pc);
164         printf("PSR:%p\n",frame->psr);
165         
166         for(;;); 
167 }
168
169 /* Initialize interrupt vector  */
170 void ivt_init() {
171
172         /* clear entire IVT location in memory  */
173         memset(&ivt, 0, (sizeof(uint32_t) * 92));
174
175         /* The reset, NMI and hardfault handlers are originally
176          * defined in the assembly start up and can be 
177          * reused or overwritten. 
178         *  */
179         extern void * reset,  * nmi, * hardfault;
180
181         // set dummy handlers 
182         for (int i = 1; i <= 64 ; i++) {
183                 ivt_set_gate(i, dummy_isr, 0);
184         }
185
186         /* The vector table is intially at 0x0. The vector table can be
187          * relocated to other memory locations. We can do this by setting 
188          * a register in the NVIC called the vector table offset register */
189
190         rwrite(SCB_VTOR, (uint32_t) &ivt);
191
192 }