6 #include <inc/string.h>
7 #include <inc/assert.h>
11 #include <kern/console.h>
12 #include <kern/pmap.h>
14 #include <kern/apic.h>
15 #include <kern/atomic.h>
17 volatile uint32_t waiting = 1;
18 volatile uint8_t num_cpus = 0xee;
19 uintptr_t smp_stack_top;
20 barrier_t generic_barrier;
22 /* Breaks us out of the waiting loop in smp_boot */
23 static void smp_boot_handler(trapframe_t *tf)
25 {HANDLER_ATOMIC atomic_dec(&waiting); }
28 static void smp_mtrr_handler(trapframe_t *tf)
30 setup_default_mtrrs(&generic_barrier);
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);
43 // This mapping allows access to the trampoline with paging on and off
45 page_insert(boot_pgdir, pa2page(0x00001000), (void*)0x00001000, PTE_W);
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);
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);
58 // Start the IPI process (INIT, wait, SIPI, wait, SIPI, wait)
60 enable_irq(); // LAPIC timer will fire, extINTs are blocked at LINT0 now
61 while (waiting) // gets released in smp_boot_handler
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
69 /* //BOCHS does not like this second SIPI.
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
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));
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
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);
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);
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);
113 // Should probably flush everyone's TLB at this point, to get rid of
114 // temp mappings that were removed. TODO
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.
122 uint32_t smp_main(void)
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");
132 cprintf("I am an Application Processor\n");
133 cprintf("Num_Cpus: %d\n\n", num_cpus);
136 // Get a per-core kernel stack
138 if (page_alloc(&my_stack))
139 panic("Unable to alloc a per-core stack!");
140 memset(page2kva(my_stack), 0, PGSIZE);
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 -
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;
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));
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;
173 // Loads the same IDT used by the other cores
174 asm volatile("lidt idt_pd");
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
185 // set a default logical id for now
186 lapic_set_logid(lapic_get_id());
188 return my_stack_top; // will be loaded in smp_entry.S
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)
196 extern isr_t interrupt_handlers[];
197 uint32_t i, amount = SMP_CALL_FUNCTION_TIMEOUT; // should calibrate this!! just remove it!
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.
208 send_self_ipi(vector);
211 send_broadcast_ipi(vector);
214 send_all_others_ipi(vector);
216 case 4: // physical mode
217 send_ipi(dest, 0, vector);
219 case 5: // logical mode
220 send_ipi(dest, 1, vector);
223 panic("Invalid type for cross-core function call!");
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);
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
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)
243 smp_call_function(1, 0, handler, vector);
246 void smp_call_function_all(isr_t handler, uint8_t vector)
248 smp_call_function(2, 0, handler, vector);
251 void smp_call_function_single(uint8_t dest, isr_t handler, uint8_t vector)
253 smp_call_function(4, dest, handler, vector);