1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
|
/* This file contains essentially the MP handling code of the Minix kernel.
*
* Changes:
* Apr 1, 2008 Added SMP support.
*/
#define _SMP
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <machine/cmos.h>
#include <machine/bios.h>
#include "kernel/spinlock.h"
#include "kernel/smp.h"
#include "apic.h"
#include "acpi.h"
#include "kernel/clock.h"
#include "glo.h"
void trampoline(void);
/*
* arguments for trampoline. We need to pass the logical cpu id, gdt and idt.
* They have to be in location which is reachable using absolute addressing in
* 16-bit mode
*/
extern volatile u32_t __ap_id, __ap_pt;
extern volatile struct desctableptr_s __ap_gdt, __ap_idt;
extern u32_t __ap_gdt_tab, __ap_idt_tab;
extern void * __trampoline_end;
extern u32_t busclock[CONFIG_MAX_CPUS];
extern int panicking;
static int volatile ap_cpu_ready;
static int volatile cpu_down;
/* there can be at most 255 local APIC ids, each fits in 8 bits */
static unsigned char apicid2cpuid[255];
unsigned char cpuid2apicid[CONFIG_MAX_CPUS];
SPINLOCK_DEFINE(smp_cpu_lock)
SPINLOCK_DEFINE(dispq_lock)
static void smp_reinit_vars(void);
/* These are initialized in protect.c */
extern struct segdesc_s gdt[GDT_SIZE];
extern struct gatedesc_s idt[IDT_SIZE];
extern struct tss_s tss[CONFIG_MAX_CPUS];
extern int prot_init_done; /* Indicates they are ready */
static phys_bytes trampoline_base;
static u32_t ap_lin_addr(void *vaddr)
{
assert(trampoline_base);
return (u32_t) vaddr - (u32_t) &trampoline + trampoline_base;
}
/*
* copies the 16-bit AP trampoline code to the first 1M of memory
*/
void copy_trampoline(void)
{
unsigned tramp_size, tramp_start = (unsigned)&trampoline;;
/* The trampoline code/data is made to be page-aligned. */
assert(!(tramp_start % I386_PAGE_SIZE));
tramp_size = (unsigned) &__trampoline_end - tramp_start;
trampoline_base = alloc_lowest(&kinfo, tramp_size);
/* The memory allocator finds the lowest available memory..
* Verify it's low enough
*/
assert(trampoline_base + tramp_size < (1 << 20));
/* prepare gdt and idt for the new cpus; make copies
* of both the tables and the descriptors of them
* in their boot addressing environment.
*/
assert(prot_init_done);
memcpy(&__ap_gdt_tab, gdt, sizeof(gdt));
memcpy(&__ap_idt_tab, gdt, sizeof(idt));
__ap_gdt.base = ap_lin_addr(&__ap_gdt_tab);
__ap_gdt.limit = sizeof(gdt)-1;
__ap_idt.base = ap_lin_addr(&__ap_idt_tab);
__ap_idt.limit = sizeof(idt)-1;
phys_copy((phys_bytes) trampoline, trampoline_base, tramp_size);
}
extern int booting_cpu; /* tell protect.c what to do */
static void smp_start_aps(void)
{
unsigned cpu;
u32_t biosresetvector;
phys_bytes __ap_id_phys;
struct proc *bootstrap_pt = get_cpulocal_var(ptproc);
/* TODO hack around the alignment problem */
phys_copy(0x467, (phys_bytes) &biosresetvector, sizeof(u32_t));
/* set the bios shutdown code to 0xA */
outb(RTC_INDEX, 0xF);
outb(RTC_IO, 0xA);
assert(bootstrap_pt);
assert(bootstrap_pt->p_seg.p_cr3);
__ap_pt = bootstrap_pt->p_seg.p_cr3;
assert(__ap_pt);
copy_trampoline();
/* New locations for cpu id, pagetable root */
__ap_id_phys = trampoline_base +
(phys_bytes) &__ap_id - (phys_bytes)&trampoline;
/* setup the warm reset vector */
phys_copy((phys_bytes) &trampoline_base, 0x467, sizeof(u32_t));
/* okay, we're ready to go. boot all of the ap's now. we loop through
* using the processor's apic id values.
*/
for (cpu = 0; cpu < ncpus; cpu++) {
ap_cpu_ready = -1;
/* Don't send INIT/SIPI to boot cpu. */
if((apicid() == cpuid2apicid[cpu]) &&
(apicid() == bsp_lapic_id)) {
continue;
}
__ap_id = booting_cpu = cpu;
phys_copy((phys_bytes) &__ap_id, __ap_id_phys, sizeof(__ap_id));
mfence();
if (apic_send_init_ipi(cpu, trampoline_base) ||
apic_send_startup_ipi(cpu, trampoline_base)) {
printf("WARNING cannot boot cpu %d\n", cpu);
continue;
}
/* wait for 5 secs for the processors to boot */
lapic_set_timer_one_shot(5000000);
while (lapic_read(LAPIC_TIMER_CCR)) {
if (ap_cpu_ready == cpu) {
cpu_set_flag(cpu, CPU_IS_READY);
break;
}
}
if (ap_cpu_ready == -1) {
printf("WARNING : CPU %d didn't boot\n", cpu);
}
}
phys_copy((phys_bytes) &biosresetvector, 0x467, sizeof(u32_t));
outb(RTC_INDEX, 0xF);
outb(RTC_IO, 0);
bsp_finish_booting();
NOT_REACHABLE;
}
void smp_halt_cpu (void)
{
NOT_IMPLEMENTED;
}
void smp_shutdown_aps(void)
{
unsigned cpu;
if (ncpus == 1)
goto exit_shutdown_aps;
/* we must let the other cpus enter the kernel mode */
BKL_UNLOCK();
for (cpu = 0; cpu < ncpus; cpu++) {
if (cpu == cpuid)
continue;
if (!cpu_test_flag(cpu, CPU_IS_READY)) {
printf("CPU %d didn't boot\n", cpu);
continue;
}
cpu_down = -1;
barrier();
apic_send_ipi(APIC_SMP_CPU_HALT_VECTOR, cpu, APIC_IPI_DEST);
/* wait for the cpu to be down */
while (cpu_down != cpu);
printf("CPU %d is down\n", cpu);
cpu_clear_flag(cpu, CPU_IS_READY);
}
exit_shutdown_aps:
ioapic_disable_all();
lapic_disable();
ncpus = 1; /* hopefully !!! */
lapic_addr = lapic_eoi_addr = 0;
return;
}
static void ap_finish_booting(void)
{
unsigned cpu = cpuid;
/* inform the world of our presence. */
ap_cpu_ready = cpu;
/*
* Finish processor initialisation. CPUs must be excluded from running.
* lapic timer calibration locks and unlocks the BKL because of the
* nested interrupts used for calibration. Therefore BKL is not good
* enough, the boot_lock must be held.
*/
spinlock_lock(&boot_lock);
BKL_LOCK();
printf("CPU %d is up\n", cpu);
cpu_identify();
lapic_enable(cpu);
fpu_init();
if (app_cpu_init_timer(system_hz)) {
panic("FATAL : failed to initialize timer interrupts CPU %d, "
"cannot continue without any clock source!", cpu);
}
/* FIXME assign CPU local idle structure */
get_cpulocal_var(proc_ptr) = get_cpulocal_var_ptr(idle_proc);
get_cpulocal_var(bill_ptr) = get_cpulocal_var_ptr(idle_proc);
ap_boot_finished(cpu);
spinlock_unlock(&boot_lock);
switch_to_user();
NOT_REACHABLE;
}
void smp_ap_boot(void)
{
switch_k_stack((char *)get_k_stack_top(__ap_id) -
X86_STACK_TOP_RESERVED, ap_finish_booting);
}
static void smp_reinit_vars(void)
{
lapic_addr = lapic_eoi_addr = 0;
ioapic_enabled = 0;
ncpus = 1;
}
static void tss_init_all(void)
{
unsigned cpu;
for(cpu = 0; cpu < ncpus ; cpu++)
tss_init(cpu, get_k_stack_top(cpu));
}
static int discover_cpus(void)
{
struct acpi_madt_lapic * cpu;
while (ncpus < CONFIG_MAX_CPUS && (cpu = acpi_get_lapic_next())) {
apicid2cpuid[cpu->apic_id] = ncpus;
cpuid2apicid[ncpus] = cpu->apic_id;
printf("CPU %3d local APIC id %3d\n", ncpus, cpu->apic_id);
ncpus++;
}
return ncpus;
}
void smp_init (void)
{
/* read the MP configuration */
if (!discover_cpus()) {
ncpus = 1;
goto uniproc_fallback;
}
lapic_addr = LOCAL_APIC_DEF_ADDR;
ioapic_enabled = 0;
tss_init_all();
/*
* we still run on the boot stack and we cannot use cpuid as its value
* wasn't set yet. apicid2cpuid initialized in mps_init()
*/
bsp_cpu_id = apicid2cpuid[apicid()];
if (!lapic_enable(bsp_cpu_id)) {
printf("ERROR : failed to initialize BSP Local APIC\n");
goto uniproc_fallback;
}
bsp_lapic_id = apicid();
acpi_init();
if (!detect_ioapics()) {
lapic_disable();
lapic_addr = 0x0;
goto uniproc_fallback;
}
ioapic_enable_all();
if (ioapic_enabled)
machine.apic_enabled = 1;
/* set smp idt entries. */
apic_idt_init(0); /* Not a reset ! */
idt_reload();
BOOT_VERBOSE(printf("SMP initialized\n"));
switch_k_stack((char *)get_k_stack_top(bsp_cpu_id) -
X86_STACK_TOP_RESERVED, smp_start_aps);
return;
uniproc_fallback:
apic_idt_init(1); /* Reset to PIC idt ! */
idt_reload();
smp_reinit_vars (); /* revert to a single proc system. */
intr_init(0); /* no auto eoi */
printf("WARNING : SMP initialization failed\n");
}
void arch_smp_halt_cpu(void)
{
/* say that we are down */
cpu_down = cpuid;
barrier();
/* unlock the BKL and don't continue */
BKL_UNLOCK();
for(;;);
}
void arch_send_smp_schedule_ipi(unsigned cpu)
{
apic_send_ipi(APIC_SMP_SCHED_PROC_VECTOR, cpu, APIC_IPI_DEST);
}
|