99b714fd7fae60bf03db7e3c8edba1bb65a64394
[akaros.git] / kern / smp.c
1 #ifdef __DEPUTY__
2 #pragma nodeputy
3 #endif
4
5 #include <inc/stdio.h>
6 #include <inc/string.h>
7 #include <inc/assert.h>
8 #include <inc/x86.h>
9
10 #include <kern/smp.h>
11 #include <kern/console.h>
12 #include <kern/pmap.h>
13 #include <kern/env.h>
14 #include <kern/apic.h>
15 #include <kern/atomic.h>
16
17 volatile uint32_t waiting = 1;
18 volatile uint8_t num_cpus = 0xee;
19 uintptr_t smp_stack_top;
20 barrier_t generic_barrier;
21
22 /* Breaks us out of the waiting loop in smp_boot */
23 static void smp_boot_handler(trapframe_t *tf)
24 {
25         {HANDLER_ATOMIC atomic_dec(&waiting); }
26 }
27
28 static void smp_mtrr_handler(trapframe_t *tf)
29 {
30         setup_default_mtrrs(&generic_barrier);
31 }
32
33 void smp_boot(void)
34 {
35         page_t *smp_stack;
36         extern isr_t interrupt_handlers[];
37         // NEED TO GRAB A LOWMEM FREE PAGE FOR AP BOOTUP CODE
38         // page1 (2nd page) is reserved, hardcoded in pmap.c
39         extern smp_entry(), smp_entry_end(), smp_boot_lock(), smp_semaphore();
40         memset(KADDR(0x00001000), 0, PGSIZE);           
41         memcpy(KADDR(0x00001000), &smp_entry, &smp_entry_end - &smp_entry);             
42
43         // This mapping allows access to the trampoline with paging on and off
44         // via 0x00001000
45         page_insert(boot_pgdir, pa2page(0x00001000), (void*)0x00001000, PTE_W);
46
47         // Allocate a stack for the cores starting up.  One for all, must share
48         if (page_alloc(&smp_stack))
49                 panic("No memory for SMP boot stack!");
50         smp_stack_top = (uintptr_t)(page2kva(smp_stack) + PGSIZE);
51
52         // set up the local APIC timer to fire 0xf0 once.  hardcoded to break
53         // out of the spinloop on waiting.  really just want to wait a little
54         lapic_set_timer(0x0000ffff, 0xf0, 0); // TODO - fix timing
55         // set the function handler to respond to this
56         register_interrupt_handler(interrupt_handlers, 0xf0, smp_boot_handler);
57
58         // Start the IPI process (INIT, wait, SIPI, wait, SIPI, wait)
59         send_init_ipi();
60         enable_irq(); // LAPIC timer will fire, extINTs are blocked at LINT0 now
61         while (waiting) // gets released in smp_boot_handler
62                 cpu_relax();
63         // first SIPI
64         waiting = 1;
65         send_startup_ipi(0x01);
66         lapic_set_timer(SMP_BOOT_TIMEOUT, 0xf0, 0); // TODO - fix timing
67         while(waiting) // wait for the first SIPI to take effect
68                 cpu_relax();
69         /* //BOCHS does not like this second SIPI.
70         // second SIPI
71         waiting = 1;
72         send_startup_ipi(0x01);
73         lapic_set_timer(0x000fffff, 0xf0, 0); // TODO - fix timing
74         while(waiting) // wait for the second SIPI to take effect
75                 cpu_relax();
76         */
77         disable_irq();
78
79         // Each core will also increment smp_semaphore, and decrement when it is done, 
80         // all in smp_entry.  It's purpose is to keep Core0 from competing for the 
81         // smp_boot_lock.  So long as one AP increments the sem before the final 
82         // LAPIC timer goes off, all available cores will be initialized.
83         while(*(volatile uint32_t*)(&smp_semaphore - &smp_entry + 0x00001000));
84
85         // From here on, no other cores are coming up.  Grab the lock to ensure it.
86         // Another core could be in it's prelock phase and be trying to grab the lock
87         // forever.... 
88         // The lock exists on the trampoline, so it can be grabbed right away in 
89         // real mode.  If core0 wins the race and blocks other CPUs from coming up
90         // it can crash the machine if the other cores are allowed to proceed with
91         // booting.  Specifically, it's when they turn on paging and have that temp
92         // mapping pulled out from under them.  Now, if a core loses, it will spin
93         // on the trampoline (which we must be careful to not deallocate)
94         spin_lock((uint32_t*)(&smp_boot_lock - &smp_entry + 0x00001000));
95         cprintf("Num_Cpus Detected: %d\n", num_cpus);
96
97         // Deregister smp_boot_handler
98         register_interrupt_handler(interrupt_handlers, 0xf0, 0);
99         // Remove the mapping of the page used by the trampoline
100         page_remove(boot_pgdir, (void*)0x00001000);
101         // It had a refcount of 2 earlier, so we need to dec once more to free it
102         // but only if all cores are in (or we reset / reinit those that failed)
103         // TODO after we parse ACPI tables
104         if (num_cpus == 8) // TODO - ghetto coded for our 8 way SMPs
105                 page_decref(pa2page(0x00001000));
106         // Dealloc the temp shared stack
107         page_decref(smp_stack);
108
109         // Set up all cores to use the proper MTRRs
110         init_barrier(&generic_barrier, num_cpus); // barrier used by smp_mtrr_handler
111         smp_call_function_all(smp_mtrr_handler, 0);
112
113         // Should probably flush everyone's TLB at this point, to get rid of 
114         // temp mappings that were removed.  TODO
115 }
116
117 /* 
118  * This is called from smp_entry by each core to finish the core bootstrapping.
119  * There is a spinlock around this entire function in smp_entry, for a few reasons,
120  * the most important being that all cores use the same stack when entering here.
121  */
122 uint32_t smp_main(void)
123 {
124         /*
125         // Print some diagnostics.  Uncomment if there're issues.
126         cprintf("Good morning Vietnam!\n");
127         cprintf("This core's Default APIC ID: 0x%08x\n", lapic_get_default_id());
128         cprintf("This core's Current APIC ID: 0x%08x\n", lapic_get_id());
129         if (read_msr(IA32_APIC_BASE) & 0x00000100)
130                 cprintf("I am the Boot Strap Processor\n");
131         else
132                 cprintf("I am an Application Processor\n");
133         cprintf("Num_Cpus: %d\n\n", num_cpus);
134         */
135         
136         // Get a per-core kernel stack
137         page_t *my_stack;
138         if (page_alloc(&my_stack))
139                 panic("Unable to alloc a per-core stack!");
140         memset(page2kva(my_stack), 0, PGSIZE);
141
142         // Set up a gdt / gdt_pd for this core, stored at the top of the stack
143         // This is necessary, eagle-eyed readers know why
144         // GDT should be 4-byte aligned.  TS isn't aligned.  Not sure if it matters.
145         pseudodesc_t *my_gdt_pd = page2kva(my_stack) + PGSIZE - 
146                 sizeof(pseudodesc_t) - sizeof(segdesc_t)*SEG_COUNT;
147         segdesc_t *my_gdt = page2kva(my_stack) + PGSIZE - 
148                 sizeof(segdesc_t)*SEG_COUNT;
149         // TS also needs to be permanent
150         taskstate_t *my_ts = page2kva(my_stack) + PGSIZE - 
151                 sizeof(pseudodesc_t) - sizeof(segdesc_t)*SEG_COUNT - 
152                 sizeof(taskstate_t);
153         // Usable portion of the KSTACK grows down from here
154         // Won't actually start using this stack til our first interrupt
155         // (issues with changing the stack pointer and then trying to "return")
156         uintptr_t my_stack_top = (uintptr_t)my_ts;
157
158         // Build and load the gdt / gdt_pd
159         memcpy(my_gdt, gdt, sizeof(segdesc_t)*SEG_COUNT);
160         *my_gdt_pd = (pseudodesc_t) { 
161                 sizeof(segdesc_t)*SEG_COUNT - 1, (uintptr_t) my_gdt };
162         asm volatile("lgdt %0" : : "m"(*my_gdt_pd));
163
164         // Need to set the TSS so we know where to trap on this core
165         my_ts->ts_esp0 = my_stack_top;
166         my_ts->ts_ss0 = GD_KD;
167         // Initialize the TSS field of my_gdt.
168         my_gdt[GD_TSS >> 3] = SEG16(STS_T32A, (uint32_t) (my_ts), sizeof(taskstate_t), 0);
169         my_gdt[GD_TSS >> 3].sd_s = 0;
170         // Load the TSS
171         ltr(GD_TSS);
172
173         // Loads the same IDT used by the other cores
174         asm volatile("lidt idt_pd");
175
176         // APIC setup
177         // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
178         write_mmreg32(LAPIC_LVT_LINT0, 0x700); 
179         // mask it to shut it up for now.  Doesn't seem to matter yet, since both
180         // KVM and Bochs seem to only route the PIC to core0.
181         mask_lapic_lvt(LAPIC_LVT_LINT0);
182         // and then turn it on
183         lapic_enable();
184
185         // set a default logical id for now
186         lapic_set_logid(lapic_get_id());
187
188         return my_stack_top; // will be loaded in smp_entry.S
189 }
190
191 // as far as completion mechs go, we might want a bit mask that every sender 
192 // has to toggle.  or a more general barrier that works on a queue / LL, 
193 // instead of everyone.  TODO!
194 static void smp_call_function(uint8_t type, uint8_t dest, isr_t handler, uint8_t vector)
195 {
196         extern isr_t interrupt_handlers[];
197         uint32_t i, amount = SMP_CALL_FUNCTION_TIMEOUT; // should calibrate this!!  just remove it!
198         int8_t state = 0;
199
200         if (!vector)
201                 vector = 0xf1; //default value
202         register_interrupt_handler(interrupt_handlers, vector, handler);
203         // WRITE MEMORY BARRIER HERE
204         enable_irqsave(&state);
205         // Send the proper type of IPI.  I made up these numbers.
206         switch (type) {
207                 case 1:
208                         send_self_ipi(vector);
209                         break;
210                 case 2:
211                         send_broadcast_ipi(vector);
212                         break;
213                 case 3:
214                         send_all_others_ipi(vector);
215                         break;
216                 case 4: // physical mode
217                         send_ipi(dest, 0, vector);
218                         break;
219                 case 5: // logical mode
220                         send_ipi(dest, 1, vector);
221                         break;
222                 default:
223                         panic("Invalid type for cross-core function call!");
224         }
225         // wait some arbitrary amount til we think all the cores could be done.
226         // very wonky without an idea of how long the function takes.
227         // maybe should think of some sort of completion notification mech
228         for (i = 0; i < amount; i++)
229                 asm volatile("nop;");
230         disable_irqsave(&state);
231         // TODO
232         // consider doing this, but we can't remove it before the receiver is done
233         //register_interrupt_handler(interrupt_handlers, vector, 0);
234         // we also will have issues if we call this function again too quickly
235 }
236
237 // I'd rather have these functions take an arbitrary function and arguments...
238 // Right now, I build a handler that just calls whatever I want, which is
239 // another layer of indirection.  Might like some ability to specify if
240 // we want to wait or not.
241 void smp_call_function_self(isr_t handler, uint8_t vector)
242 {
243         smp_call_function(1, 0, handler, vector);
244 }
245
246 void smp_call_function_all(isr_t handler, uint8_t vector)
247 {
248         smp_call_function(2, 0, handler, vector);
249 }
250
251 void smp_call_function_single(uint8_t dest, isr_t handler, uint8_t vector)
252 {
253         smp_call_function(4, dest, handler, vector);
254 }
255