Kernel context (IRQ, etc) tracking
[akaros.git] / kern / arch / riscv / smp.c
1 #include <smp.h>
2 #include <arch/arch.h>
3 #include <arch/smp.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <error.h>
7 #include <assert.h>
8 #include <atomic.h>
9 #include <pmap.h>
10
11 static volatile uint32_t num_cpus_booted = 1;
12
13 void
14 smp_boot(void)
15 {
16         smp_percpu_init();
17
18         printd("Cores, report in!\n");
19
20         for(uint32_t i = 1; i < num_cpus; i++)
21                 send_ipi(i, 0); /* meaningless IRQ vector */
22         
23         while(num_cpus_booted < num_cpus);
24
25         printd("%d cores reporting!\n", num_cpus);
26 }
27
28 void
29 smp_init(void)
30 {
31         smp_percpu_init();
32
33         __sync_fetch_and_add(&num_cpus_booted, 1);
34         printd("Good morning, Vietnam! (core id = %d)\n",core_id());
35
36         smp_idle();
37 }
38
39 handler_wrapper_t*
40 smp_make_wrapper()
41 {
42         static handler_wrapper_t
43         wrapper_pool[MAX_NUM_CPUS*8] = {{{0},SPINLOCK_INITIALIZER}};
44
45         size_t i;
46         for(i = 0; i < sizeof(wrapper_pool)/sizeof(wrapper_pool[0]); i++)
47                 if(spin_trylock(&wrapper_pool[i].lock) == 0)
48                         return &wrapper_pool[i];
49         return NULL;
50 }
51
52 void
53 smp_call_wrapper(trapframe_t* tf, uint32_t src, isr_t handler,
54                  handler_wrapper_t* wrapper, void* data)
55 {
56         if(wrapper)
57                 wrapper->wait_list[core_id()] = 0;
58         handler(tf, data);
59 }
60
61 int smp_call_function_self(isr_t handler, void* data,
62                            handler_wrapper_t** wait_wrapper)
63 {
64         return smp_call_function_single(core_id(), handler, data, wait_wrapper);
65 }
66
67 int smp_call_function_all(isr_t handler, void* data,
68                           handler_wrapper_t** wait_wrapper)
69 {
70         int8_t state = 0;
71         int i, me;
72         handler_wrapper_t* wrapper = 0;
73         if(wait_wrapper)
74         {
75                 wrapper = *wait_wrapper = smp_make_wrapper();
76                 if(!wrapper)
77                         return -ENOMEM;
78
79                 for(i = 0; i < num_cpus; i++)
80                         wrapper->wait_list[i] = 1;
81         }
82
83         enable_irqsave(&state);
84
85         // send to others
86         for(i = 0, me = core_id(); i < num_cpus; i++)
87         {
88                 if(i == me)
89                         continue;
90
91                 send_kernel_message(i, (amr_t)smp_call_wrapper, (long)handler,
92                                     (long)wrapper, (long)data, KMSG_IMMEDIATE);
93         }
94
95         // send to me
96         send_kernel_message(me, (amr_t)smp_call_wrapper, (long)handler,
97                             (long)wrapper, (long)data, KMSG_IMMEDIATE);
98
99         cpu_relax(); // wait to get the interrupt
100
101         disable_irqsave(&state);
102
103         return 0;
104 }
105
106 int smp_call_function_single(uint32_t dest, isr_t handler, void* data,
107                              handler_wrapper_t** wait_wrapper)
108 {
109         int8_t state = 0;
110         handler_wrapper_t* wrapper = 0;
111         if(wait_wrapper)
112         {
113                 wrapper = *wait_wrapper = smp_make_wrapper();
114                 if(!wrapper)
115                         return -ENOMEM;
116                 wrapper->wait_list[dest] = 1;
117         }
118
119         enable_irqsave(&state);
120
121         send_kernel_message(dest, (amr_t)smp_call_wrapper, (long)handler,
122                             (long)wrapper, (long)data, KMSG_IMMEDIATE);
123
124         cpu_relax(); // wait to get the interrupt, if it's to this core
125
126         disable_irqsave(&state);
127
128         return 0;
129 }
130
131 int smp_call_wait(handler_wrapper_t* wrapper)
132 {
133         int i;
134         for(i = 0; i < num_cpus; i++)
135                 while(wrapper->wait_list[i]);
136
137         spin_unlock(&wrapper->lock);
138         return 0;
139 }
140
141 /* Perform any initialization needed by per_cpu_info.  Right now, this just
142  * inits the amsg list (which sparc will probably also want).  Make sure every
143  * core calls this at some point in the smp_boot process. */
144 void __arch_pcpu_init(uint32_t coreid)
145 {
146         // Switch to the real L1 page table, rather than the boot page table which
147         // has the [0,KERNSIZE-1] identity mapping.
148         extern pte_t l1pt[NPTENTRIES];
149         lcr3(PADDR(l1pt));
150
151         register uintptr_t sp asm ("sp");
152         set_stack_top(ROUNDUP(sp, PGSIZE));
153 }