AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
smp.c
Go to the documentation of this file.
1
24
25#include <core/system.h>
26#include <core/kargs.h>
27#include <core/string.h>
28#include <core/arch.h>
29#include <cpu/cpu.h>
30#include <cpu/msr.h>
31#include <cpu/smp.h>
32#include <mm/mm.h>
33#include <mm/pmap.h>
34#include <mm/vm.h>
35#include <platform/misc.h>
36#include <sys/cpu.h>
37
38MALLOC_DEFINE(M_IDLE_STACK, "idle-stack", "a CPU's idle stack");
39
40#define IDLE_STACK_SIZE 8192
41
42/* The IPIs: INIT, and the startup IPI, whose vector is the page where the
43 * AP starts. */
44#define ICR_INIT 0x00000500
45#define ICR_STARTUP 0x00000600
46#define ICR_ASSERT 0x00004000
47
48/* trampoline.S */
50
55 uint64_t entry; /* where it goes: x86_ap_start() */
56 uint64_t stack; /* the top of its idle stack */
57 uint64_t cpu; /* its struct cpu */
58};
59
60/* the trampoline's page tables, in the pages after its code */
61#define TABLES (X86_TRAMPOLINE + 0x1000)
62
63#define PG_PRESENT 0x001
64#define PG_WRITE 0x002
65#define PG_LARGE 0x080
66
67#define MSR_EFER 0xC0000080
68
69/* set by the AP that was started, once it is in the kernel */
70static volatile int ap_started;
71
74static void __attribute__((noreturn)) x86_ap_start(struct cpu *cpu)
75{
76 x86_gdt_setup(cpu, VMA(0x100000ULL));
78
79 /* switch from the trampoline's page tables to the kernel's */
81
83
84 asm volatile("" ::: "memory");
85 ap_started = 1;
86
87 /* idle, with interrupts on, until there's a thread to run */
88 cpu->idle = 1;
89 arch_idle();
90 __builtin_unreachable();
91}
92
93/*
94 * Makes another CPU enter the kernel now, with the wake IPI: to run a
95 * thread that just became ready, or to see that its thread was killed.
96 */
101
106{
107 uintptr_t *top = PHYS2VIRT(TABLES);
109
111
112#if ARCH_BITS==32
113 /* the page directory, and a page table for the first 4 MiB */
115
116 for (int i = 768; i < 1024; ++i)
117 top[i] = kernel[i];
118
119 top[0] = (TABLES + PAGE_SIZE) | PG_PRESENT | PG_WRITE;
121#else
122 /* the PML4, a PDPT, and a page directory that maps the first 2 MiB
123 * as one large page (not a global one, so it goes away with these
124 * tables) */
126 uint64_t *pd = PHYS2VIRT(TABLES + 2 * PAGE_SIZE);
127
128 for (int i = 256; i < 512; ++i)
129 top[i] = kernel[i];
130
131 top[0] = (TABLES + PAGE_SIZE) | PG_PRESENT | PG_WRITE;
132 pdpt[0] = (TABLES + 2 * PAGE_SIZE) | PG_PRESENT | PG_WRITE;
133 pd[0] = 0 | PG_PRESENT | PG_WRITE | PG_LARGE;
134#endif
135
136 return TABLES;
137}
138
141static int ap_boot(struct cpu *cpu, uint32_t id)
142{
144
145 data->entry = (uintptr_t) x86_ap_start;
146 data->stack = cpu->arch.idle_stack;
147 data->cpu = (uintptr_t) cpu;
148
149 ap_started = 0;
150 asm volatile("" ::: "memory");
151
152 /* INIT, wait 10 ms, then the startup IPI, wait 200 us, and send it
153 * again if the AP isn't there */
155 x86_pit_delay(10000);
156
157 for (int sipi = 0; sipi < 2 && !ap_started; ++sipi) {
159 x86_pit_delay(200);
160 }
161
162 /* wait up to 100 ms more */
163 for (int ms = 0; ms < 100 && !ap_started; ++ms)
164 x86_pit_delay(1000);
165
166 if (ap_started)
167 return 0;
168
169 /* Too late: send INIT again, so that the AP waits for a startup IPI
170 * that won't come. If it started now, it would run on a stack and a
171 * struct cpu that are about to be freed and reused. */
173 x86_pit_delay(10000);
174
175 return -1;
176}
177
182void x86_smp_init(void)
183{
184 const char *arg = NULL;
185 uint32_t ids[CPUS_MAX];
186 int n = x86_apic_cpus(ids, CPUS_MAX);
187
188 if (n <= 1)
189 return;
190
191 if (!kargs_get("smp", &arg) && arg && !strcmp(arg, "off")) {
192 printk("smp: off (smp=off), %d CPUs\n", n);
193 return;
194 }
195
196 /* copy the trampoline to its page */
197 size_t size = x86_trampoline_end - x86_trampoline;
198
199 if (size > PAGE_SIZE) {
200 printk("smp: the trampoline is more than a page\n");
201 return;
202 }
203
205
207
208 /* the APs take the boot CPU's control registers (the FPU's bits,
209 * write protection, long mode), and the trampoline's page tables */
210 data->cr0 = read_cr0();
211 data->cr3 = tables_build();
212 data->cr4 = read_cr4();
213#if ARCH_BITS==64
214 data->efer = msr_read(MSR_EFER);
215#else
216 data->efer = 0;
217#endif
218
219 for (int i = 0; i < n && cpus_count < CPUS_MAX; ++i) {
220 if (ids[i] == cpus[0].arch.apic_id)
221 continue;
222
223 struct cpu *cpu = &cpus[cpus_count];
224 void *stack = kmalloc(IDLE_STACK_SIZE, &M_IDLE_STACK, 0);
225
226 if (!stack)
227 break;
228
229 memset(cpu, 0, sizeof(*cpu));
230 cpu->id = cpus_count;
231 cpu->arch.apic_id = ids[i];
233
234 if (ap_boot(cpu, ids[i])) {
235 printk("smp: the CPU with local APIC %d didn't start\n", ids[i]);
236 kfree(stack);
237 continue;
238 }
239
240 cpus_count++;
241 }
242
243 printk("smp: %d CPUs\n", cpus_count);
244}
void arch_cpu_kick(struct cpu *cpu)
Makes another CPU enter the kernel now, with an inter-processor interrupt: to run a thread that just ...
Definition smp.c:14
#define PAGE_SIZE
Definition system.h:7
uint64_t paddr_t
Definition mm.h:39
#define PHYS2VIRT(paddr)
Definition mm.h:26
#define VMA(obj)
Definition mm.h:36
void arch_idle(void)
Makes this CPU idle, on its idle stack, until an interrupt makes a thread ready.
Definition sched.c:62
static uintptr_t read_cr4(void)
Definition cpu.h:212
void x86_gdt_setup(struct cpu *cpu, uintptr_t sp)
Sets up and loads the running CPU's GDT and TSS, and points gs (or the GS base, on x86_64) at its str...
Definition gdt.c:79
#define PG_LARGE
Definition smp.c:65
static volatile int ap_started
Definition smp.c:70
static void x86_ap_start(struct cpu *cpu)
Where an AP enters the kernel, from the trampoline: it sets up its own CPU structures,...
Definition smp.c:74
#define PG_PRESENT
Definition smp.c:63
char x86_trampoline[]
static int ap_boot(struct cpu *cpu, uint32_t id)
Starts the CPU whose local APIC is id, as cpu.
Definition smp.c:141
#define PG_WRITE
Definition smp.c:64
void x86_smp_init(void)
Starts the other CPUs, at boot, unless the kernel's command line says smp=off.
Definition smp.c:182
#define IDLE_STACK_SIZE
Definition smp.c:40
char x86_trampoline_end[]
Definition smp.c:49
#define MSR_EFER
Definition smp.c:67
#define ICR_ASSERT
Definition smp.c:46
static paddr_t tables_build(void)
Builds the trampoline's page tables, which map the trampoline's page where it is and the kernel's hal...
Definition smp.c:105
#define ICR_STARTUP
Definition smp.c:45
#define TABLES
Definition smp.c:61
#define ICR_INIT
Definition smp.c:44
char x86_trampoline_data[]
Definition smp.c:49
void x86_idt_setup()
Loads the IDT on the running CPU.
Definition idt.c:82
struct cpu cpus[CPUS_MAX]
Definition sched.c:28
int cpus_count
Definition sched.c:29
#define CPUS_MAX
Definition cpu.h:21
int kargs_get(const char *key, const char **value)
Looks up an argument of the kernel's command line.
Definition kargs.c:73
#define MALLOC_DEFINE(type, _name, _desc)
Definition kvmem.h:30
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
void * memset(void *dst, int c, size_t n)
Sets n bytes to c.
Definition libc.c:23
void * memcpy(void *dst, const void *src, size_t n)
Copies n bytes; dst and src must not overlap.
Definition libc.c:11
void x86_pit_delay(uint32_t us)
Waits us microseconds, at most 54 ms, busy.
Definition pit.c:123
int x86_apic_cpus(uint32_t *ids, int max)
Lists the CPUs' local APIC IDs, the boot CPU's among them.
Definition apic.c:322
#define X86_WAKE_VECTOR
Definition misc.h:16
void x86_lapic_ipi(uint32_t id, uint32_t icr)
Sends an IPI to a CPU, and waits until it is delivered.
Definition apic.c:342
void x86_lapic_cpu_setup(void)
Sets up the running CPU's local APIC: enabled, with the spurious vector 0xFF, its timer off,...
Definition apic.c:303
struct pmap * pmap_switch(struct pmap *pmap)
Switches this CPU to an address space's page tables.
Definition pmap.c:164
int printk(const char *fmt,...)
Writes a kernel message, to the message buffer and the consoles.
Definition printk.c:249
#define X86_TRAMPOLINE
Definition smp.h:8
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned int uint32_t
Definition stddef.h:22
unsigned long long int uint64_t
Definition stddef.h:23
uintptr_t idle_stack
Definition cpu.h:55
uint32_t apic_id
Definition cpu.h:125
A processor, and what runs on it.
Definition cpu.h:11
int id
Definition cpu.h:13
int idle
Definition cpu.h:15
struct arch_cpu arch
Definition cpu.h:17
paddr_t map
Definition pmap.h:10
What an AP needs to reach the kernel, at x86_trampoline_data (the fields are at trampoline....
Definition smp.c:53
uint64_t stack
Definition smp.c:56
uint64_t cpu
Definition smp.c:57
uint64_t cr3
Definition smp.c:54
uint64_t cr0
Definition smp.c:54
uint64_t efer
Definition smp.c:54
uint64_t cr4
Definition smp.c:54
uint64_t entry
Definition smp.c:55
struct pmap * pmap
its page tables (the architecture's)
Definition vm.h:27
struct vm_space kvm_space
Definition vmm.c:17