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