1 | /*␊ |
2 | * <Insert copyright here : it must be BSD-like so anyone can use it>␊ |
3 | *␊ |
4 | * Author: Erich Boleyn <erich@uruk.org> http://www.uruk.org/~erich/␊ |
5 | *␊ |
6 | * Source file implementing Intel MultiProcessor Specification (MPS)␊ |
7 | * version 1.1 and 1.4 SMP hardware control for Intel Architecture CPUs,␊ |
8 | * with hooks for running correctly on a standard PC without the hardware.␊ |
9 | *␊ |
10 | * This file was created from information in the Intel MPS version 1.4␊ |
11 | * document, order number 242016-004, which can be ordered from the␊ |
12 | * Intel literature center.␊ |
13 | *␊ |
14 | * General limitations of this code:␊ |
15 | *␊ |
16 | * (1) : This code has never been tested on an MPS-compatible system with␊ |
17 | * 486 CPUs, but is expected to work.␊ |
18 | * (2) : Presumes "int", "long", and "unsigned" are 32 bits in size, and␊ |
19 | *␉ that 32-bit pointers and memory addressing is used uniformly.␊ |
20 | */␊ |
21 | #include "libsaio.h"␊ |
22 | ␊ |
23 | #define _SMP_IMPS_C␊ |
24 | ␊ |
25 | ␊ |
26 | /*␊ |
27 | * XXXXX The following absolutely must be defined!!!␊ |
28 | *␊ |
29 | * The "KERNEL_PRINT" could be made a null macro with no danger, of␊ |
30 | * course, but pretty much nothing would work without the other␊ |
31 | * ones defined.␊ |
32 | */␊ |
33 | ␊ |
34 | ␊ |
35 | #if 0␊ |
36 | #define CMOS_WRITE_BYTE(x,y)␉/* write unsigned char "y" at CMOS loc "x" */␊ |
37 | #define CMOS_READ_BYTE(x)␉/* read unsigned char at CMOS loc "x" */␊ |
38 | //#define TEST_BOOTED(x)␉␉/* test bootaddr x to see if CPU started */␊ |
39 | #define READ_MSR_LO(x)␉␉/* Read MSR low function */␊ |
40 | #endif␊ |
41 | ␊ |
42 | #ifndef DEBUG_SMP␊ |
43 | #define DEBUG_SMP 0␊ |
44 | #endif␊ |
45 | ␊ |
46 | #if DEBUG_SMP␉== 2␊ |
47 | #define DBG(x...)␉␉printf(x)␊ |
48 | #elif DEBUG_SMP == 1␊ |
49 | #define DBG(x...)␉␉msglog(x)␊ |
50 | #else␊ |
51 | #define DBG(x...)␊ |
52 | #endif␊ |
53 | ␊ |
54 | #define PHYS_TO_VIRTUAL(x)␉ptov(x) /* convert physical address "x" to virtual */␊ |
55 | #define VIRTUAL_TO_PHYS(x)␉vtop(x) /* convert virtual address "x" to physical */␊ |
56 | ␊ |
57 | #define CMOS_WRITE_BYTE(x, y)␉cmos_write_byte(x, y)␊ |
58 | #define CMOS_READ_BYTE(x)␉cmos_read_byte(x)␊ |
59 | /*␊ |
60 | * Includes here␊ |
61 | */␊ |
62 | ␊ |
63 | ␊ |
64 | #include "apic.h"␊ |
65 | #include "smp-imps.h"␊ |
66 | ␊ |
67 | /*␊ |
68 | * Defines that are here so as not to be in the global header file.␊ |
69 | */␊ |
70 | #define EBDA_SEG_ADDR␉␉␉0x40E␊ |
71 | #define BIOS_RESET_VECTOR␉␉0x467␊ |
72 | #define LAPIC_ADDR_DEFAULT␉␉0xFEE00000uL␊ |
73 | #define IOAPIC_ADDR_DEFAULT␉␉0xFEC00000uL␊ |
74 | #define CMOS_RESET_CODE␉␉␉0xF␊ |
75 | #define␉CMOS_RESET_JUMP␉␉␉0xa␊ |
76 | #define CMOS_BASE_MEMORY␉␉0x15␊ |
77 | ␊ |
78 | /*␊ |
79 | * Static defines here for SMP use.␊ |
80 | */␊ |
81 | ␊ |
82 | #define DEF_ENTRIES␉23␊ |
83 | ␊ |
84 | static int lapic_dummy = 0;␊ |
85 | static struct {␊ |
86 | ␉imps_processor proc[2];␊ |
87 | ␉imps_bus bus[2];␊ |
88 | ␉imps_ioapic ioapic;␊ |
89 | ␉imps_interrupt intin[16];␊ |
90 | ␉imps_interrupt lintin[2];␊ |
91 | } defconfig = {␊ |
92 | ␉{ { IMPS_BCT_PROCESSOR, 0, 0, 0, 0, 0},␊ |
93 | ␉ { IMPS_BCT_PROCESSOR, 1, 0, 0, 0, 0} },␊ |
94 | ␉{ { IMPS_BCT_BUS, 0, {'E', 'I', 'S', 'A', ' ', ' '}},␊ |
95 | ␉ { 255, 1, {'P', 'C', 'I', ' ', ' ', ' '}} },␊ |
96 | ␉{ IMPS_BCT_IOAPIC, 0, 0, IMPS_FLAG_ENABLED, IOAPIC_ADDR_DEFAULT },␊ |
97 | ␉{ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 0, 0xFF, 0},␊ |
98 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 1, 0xFF, 1},␊ |
99 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 0, 0xFF, 2},␊ |
100 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 3, 0xFF, 3},␊ |
101 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 4, 0xFF, 4},␊ |
102 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 5, 0xFF, 5},␊ |
103 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 6, 0xFF, 6},␊ |
104 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 7, 0xFF, 7},␊ |
105 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 8, 0xFF, 8},␊ |
106 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 9, 0xFF, 9},␊ |
107 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 10, 0xFF, 10},␊ |
108 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 11, 0xFF, 11},␊ |
109 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 12, 0xFF, 12},␊ |
110 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 13, 0xFF, 13},␊ |
111 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 14, 0xFF, 14},␊ |
112 | ␉ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 15, 0xFF, 15} },␊ |
113 | ␉{ { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 15, 0xFF, 0},␊ |
114 | ␉ { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_NMI, 0, 0, 15, 0xFF, 1} }␊ |
115 | };␊ |
116 | ␊ |
117 | /*␊ |
118 | * Exported globals here.␊ |
119 | */␊ |
120 | ␊ |
121 | volatile int imps_release_cpus = 0;␊ |
122 | int imps_enabled = 0;␊ |
123 | int imps_num_cpus = 1;␊ |
124 | unsigned imps_lapic_addr = ((unsigned)(&lapic_dummy)) - LAPIC_ID;␊ |
125 | unsigned char imps_cpu_apic_map[IMPS_MAX_CPUS];␊ |
126 | unsigned char imps_apic_cpu_map[IMPS_MAX_CPUS];␊ |
127 | ␊ |
128 | typedef struct msr_struct␊ |
129 | {␊ |
130 | ␉unsigned lo;␊ |
131 | ␉unsigned hi;␊ |
132 | } msr_t;␊ |
133 | ␊ |
134 | static int imps_scan(unsigned start, unsigned length);␊ |
135 | static void imps_read_bios(imps_fps *fps_ptr);␊ |
136 | static int imps_bad_bios(imps_fps *fps_ptr);␊ |
137 | static void imps_read_config_table(unsigned start, int count);␊ |
138 | static void add_ioapic(imps_ioapic *ioapic);␊ |
139 | static void add_bus(imps_bus *bus);␊ |
140 | static void add_processor(imps_processor *proc);␊ |
141 | static int get_checksum(unsigned start, int length);␊ |
142 | ␊ |
143 | #if BOOT_CPU␊ |
144 | static int boot_cpu(imps_processor *proc);␊ |
145 | static int send_ipi(unsigned int dst, unsigned int v);␊ |
146 | #endif␊ |
147 | ␊ |
148 | static inline __attribute__((always_inline)) msr_t rdmsr(unsigned val)␊ |
149 | {␊ |
150 | ␉msr_t ret;␊ |
151 | ␉__asm__ volatile(␊ |
152 | ␉␉␉␉␉ "rdmsr"␊ |
153 | ␉␉␉␉␉ : "=a" (ret.lo), "=d" (ret.hi)␊ |
154 | ␉␉␉␉␉ : "c" (val)␊ |
155 | ␉␉␉␉␉ );␊ |
156 | ␉return ret;␊ |
157 | }␊ |
158 | ␊ |
159 | static inline __attribute__((always_inline)) void wrmsr(unsigned val, msr_t msr)␊ |
160 | {␊ |
161 | ␉__asm__ __volatile__ (␊ |
162 | ␉␉␉␉␉␉ "wrmsr"␊ |
163 | ␉␉␉␉␉␉ : /* No outputs */␊ |
164 | ␉␉␉␉␉␉ : "c" (val), "a" (msr.lo), "d" (msr.hi)␊ |
165 | ␉␉␉␉␉␉ );␊ |
166 | }␊ |
167 | ␊ |
168 | /*␊ |
169 | * MPS checksum function␊ |
170 | *␊ |
171 | * Function finished.␊ |
172 | */␊ |
173 | ␊ |
174 | static int␊ |
175 | get_checksum(unsigned start, int length)␊ |
176 | {␊ |
177 | ␉unsigned sum = 0;␊ |
178 | ␊ |
179 | ␉while (length-- > 0) {␊ |
180 | ␉␉sum += *((unsigned char *) (start++));␊ |
181 | ␉}␊ |
182 | ␊ |
183 | ␉return (sum&0xFF);␊ |
184 | }␊ |
185 | ␊ |
186 | #if BOOT_CPU␊ |
187 | #if 0␊ |
188 | static inline void␊ |
189 | cmos_write_byte (int loc, int val)␊ |
190 | {␊ |
191 | ␉outb (0x70, loc);␊ |
192 | ␉outb (0x71, val);␊ |
193 | }␊ |
194 | ␊ |
195 | static inline unsigned␊ |
196 | cmos_read_byte (int loc)␊ |
197 | {␊ |
198 | ␉outb (0x70, loc);␊ |
199 | ␉return inb (0x71);␊ |
200 | }␊ |
201 | #endif␊ |
202 | /*␊ |
203 | * APIC ICR write and status check function.␊ |
204 | */␊ |
205 | ␊ |
206 | static int␊ |
207 | send_ipi(unsigned int dst, unsigned int v)␊ |
208 | {␊ |
209 | ␉int to, send_status;␊ |
210 | ␊ |
211 | ␉IMPS_LAPIC_WRITE(LAPIC_ICR+0x10, (dst << 24));␊ |
212 | ␉IMPS_LAPIC_WRITE(LAPIC_ICR, v);␊ |
213 | ␊ |
214 | ␉/* Wait for send to finish */␊ |
215 | ␉to = 0;␊ |
216 | ␉do {␊ |
217 | ␉␉delay(100);␊ |
218 | ␉␉send_status = IMPS_LAPIC_READ(LAPIC_ICR) & LAPIC_ICR_STATUS_PEND;␊ |
219 | ␉} while (send_status && (to++ < 1000));␊ |
220 | ␊ |
221 | ␉return (to < 1000);␊ |
222 | }␊ |
223 | ␊ |
224 | ␊ |
225 | /*␊ |
226 | * Primary function for booting individual CPUs.␊ |
227 | *␊ |
228 | * This must be modified to perform whatever OS-specific initialization␊ |
229 | * that is required.␊ |
230 | */␊ |
231 | ␊ |
232 | static int␊ |
233 | boot_cpu(imps_processor *proc)␊ |
234 | {␊ |
235 | ␉int apicid = proc->apic_id, success = 1/*, to*/;␊ |
236 | ␉unsigned bootaddr, accept_status;␊ |
237 | ␉unsigned bios_reset_vector = PHYS_TO_VIRTUAL(BIOS_RESET_VECTOR);␊ |
238 | ␊ |
239 | ␉/*␊ |
240 | ␉ * Copy boot code for secondary CPUs here. Find it in between␊ |
241 | ␉ * "patch_code_start" and "patch_code_end" symbols. The other CPUs␊ |
242 | ␉ * will start there in 16-bit real mode under the 1MB boundary.␊ |
243 | ␉ * "patch_code_start" should be placed at a 4K-aligned address␊ |
244 | ␉ * under the 1MB boundary.␊ |
245 | ␉ */␊ |
246 | ␊ |
247 | ␉//extern char ???[];␊ |
248 | ␉//bootaddr = (512-64)*1024; ???␊ |
249 | ␉//memcpy((char *)bootaddr, ,??? );␊ |
250 | ␊ |
251 | ␉/*␊ |
252 | ␉ * Generic CPU startup sequence starts here.␊ |
253 | ␉ */␊ |
254 | ␊ |
255 | ␉/* set BIOS reset vector */␊ |
256 | ␉CMOS_WRITE_BYTE(CMOS_RESET_CODE, CMOS_RESET_JUMP);␊ |
257 | ␉*((volatile unsigned *) bios_reset_vector) = ((bootaddr & 0xFF000) << 12);␊ |
258 | ␊ |
259 | ␉/* clear the APIC error register */␊ |
260 | ␉IMPS_LAPIC_WRITE(LAPIC_ESR, 0);␊ |
261 | ␉accept_status = IMPS_LAPIC_READ(LAPIC_ESR);␊ |
262 | ␊ |
263 | ␉/* assert INIT IPI */␊ |
264 | ␉send_ipi(apicid, LAPIC_ICR_TM_LEVEL | LAPIC_ICR_LEVELASSERT | LAPIC_ICR_DM_INIT);␊ |
265 | ␊ |
266 | ␉delay(10000);␊ |
267 | ␊ |
268 | ␉/* de-assert INIT IPI */␊ |
269 | ␉send_ipi(apicid, LAPIC_ICR_TM_LEVEL | LAPIC_ICR_DM_INIT);␊ |
270 | ␊ |
271 | ␉delay(10000);␊ |
272 | ␊ |
273 | ␉/*␊ |
274 | ␉ * Send Startup IPIs if not an old pre-integrated APIC.␊ |
275 | ␉ */␊ |
276 | ␊ |
277 | ␉if (proc->apic_ver >= APIC_VER_NEW) {␊ |
278 | ␉␉int i;␊ |
279 | ␉␉for (i = 1; i <= 2; i++) {␊ |
280 | ␉␉␉send_ipi(apicid, LAPIC_ICR_DM_SIPI | ((bootaddr >> 12) & 0xFF));␊ |
281 | ␉␉␉delay(1000);␊ |
282 | ␉␉}␊ |
283 | ␉}␊ |
284 | ␊ |
285 | ␉/*␊ |
286 | ␉ * Generic CPU startup sequence ends here, the rest is cleanup.␊ |
287 | ␉ */␊ |
288 | ␊ |
289 | ␉/* clear the APIC error register */␊ |
290 | ␉IMPS_LAPIC_WRITE(LAPIC_ESR, 0);␊ |
291 | ␉accept_status = IMPS_LAPIC_READ(LAPIC_ESR);␊ |
292 | ␊ |
293 | ␉/* clean up BIOS reset vector */␊ |
294 | ␉CMOS_WRITE_BYTE(CMOS_RESET_CODE, 0);␊ |
295 | ␉*((volatile unsigned *) bios_reset_vector) = 0;␊ |
296 | ␊ |
297 | ␉DBG("\n");␊ |
298 | ␊ |
299 | ␉return success;␊ |
300 | }␊ |
301 | #endif␊ |
302 | ␊ |
303 | /*␊ |
304 | * read bios stuff and fill tables␊ |
305 | */␊ |
306 | ␊ |
307 | static void␊ |
308 | add_processor(imps_processor *proc)␊ |
309 | {␊ |
310 | ␉int apicid = proc->apic_id;␊ |
311 | ␊ |
312 | ␉msglog(" Processor [APIC id %d ver %d]: ",␊ |
313 | ␉␉ apicid, proc->apic_ver);␊ |
314 | ␉if (!(proc->flags & IMPS_FLAG_ENABLED)) {␊ |
315 | ␉␉msglog("DISABLED\n");␊ |
316 | ␉␉return;␊ |
317 | ␉}␊ |
318 | ␉if (proc->flags & (IMPS_CPUFLAG_BOOT)) {␊ |
319 | ␉␉msglog("#0 BootStrap Processor (BSP)\n");␊ |
320 | ␉␉return;␊ |
321 | ␉}␊ |
322 | #if BOOT_CPU␊ |
323 | ␉if (boot_cpu(proc)) {␊ |
324 | ␊ |
325 | ␉␉/* XXXXX add OS-specific setup for secondary CPUs here */␊ |
326 | ␊ |
327 | ␉␉imps_cpu_apic_map[imps_num_cpus] = apicid;␊ |
328 | ␉␉imps_apic_cpu_map[apicid] = imps_num_cpus;␊ |
329 | ␉␉imps_num_cpus++;␊ |
330 | ␉}␊ |
331 | #else␊ |
332 | ␉imps_cpu_apic_map[imps_num_cpus] = apicid;␊ |
333 | ␉imps_apic_cpu_map[apicid] = imps_num_cpus;␊ |
334 | ␉imps_num_cpus++;␊ |
335 | #endif␊ |
336 | ␉␊ |
337 | }␊ |
338 | ␊ |
339 | ␊ |
340 | static void␊ |
341 | add_bus(imps_bus *bus)␊ |
342 | {␊ |
343 | ␉char str[8];␊ |
344 | ␊ |
345 | ␉memcpy(str, bus->bus_type, 6);␊ |
346 | ␉str[6] = 0;␊ |
347 | ␉DBG(" Bus id %d is %s\n", bus->id, str);␊ |
348 | ␊ |
349 | ␉/* XXXXX add OS-specific code here */␊ |
350 | }␊ |
351 | ␊ |
352 | static void␊ |
353 | add_ioapic(imps_ioapic *ioapic)␊ |
354 | {␊ |
355 | ␉msglog("\n I/O APIC id %d ver %d, address: 0x%x ",␊ |
356 | ␉␉ ioapic->id, ioapic->ver, ioapic->addr);␊ |
357 | ␉if (!(ioapic->flags & IMPS_FLAG_ENABLED)) {␊ |
358 | ␉␉msglog("DISABLED\n");␊ |
359 | ␉␉return;␊ |
360 | ␉}␊ |
361 | ␉msglog("\n");␊ |
362 | ␊ |
363 | ␉/* XXXXX add OS-specific code here */␊ |
364 | }␊ |
365 | ␊ |
366 | ␊ |
367 | static void␊ |
368 | imps_read_config_table(unsigned start, int count)␊ |
369 | {␊ |
370 | ␉while (count-- > 0) {␊ |
371 | ␉␉switch (*((unsigned char *)start)) {␊ |
372 | ␉␉case IMPS_BCT_PROCESSOR:␊ |
373 | ␉␉␉add_processor((imps_processor *)start);␊ |
374 | ␉␉␉start += 12;␉/* 20 total */␊ |
375 | ␉␉␉break;␊ |
376 | ␉␉case IMPS_BCT_BUS:␊ |
377 | ␉␉␉add_bus((imps_bus *)start);␊ |
378 | ␉␉␉break;␊ |
379 | ␉␉case IMPS_BCT_IOAPIC:␊ |
380 | ␉␉␉add_ioapic((imps_ioapic *)start);␊ |
381 | ␉␉␉break;␊ |
382 | #if 0␉/* XXXXX uncomment this if "add_io_interrupt" is implemented */␊ |
383 | ␉␉case IMPS_BCT_IO_INTERRUPT:␊ |
384 | ␉␉␉add_io_interrupt((imps_interrupt *)start);␊ |
385 | ␉␉␉break;␊ |
386 | #endif␊ |
387 | #if 0␉/* XXXXX uncomment this if "add_local_interrupt" is implemented */␊ |
388 | ␉␉case IMPS_BCT_LOCAL_INTERRUPT:␊ |
389 | ␉␉␉add_local_interupt((imps_interrupt *)start);␊ |
390 | ␉␉␉break;␊ |
391 | #endif␊ |
392 | ␉␉default:␊ |
393 | ␉␉␉break;␊ |
394 | ␉␉}␊ |
395 | ␉␉start += 8;␊ |
396 | ␉}␊ |
397 | }␊ |
398 | ␊ |
399 | ␊ |
400 | static int␊ |
401 | imps_bad_bios(imps_fps *fps_ptr)␊ |
402 | {␊ |
403 | ␉int sum;␊ |
404 | ␉imps_cth *local_cth_ptr␊ |
405 | ␉␉= (imps_cth *) PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);␊ |
406 | ␊ |
407 | ␉if (fps_ptr->feature_info[0] > IMPS_FPS_DEFAULT_MAX) {␊ |
408 | ␉␉DBG(" Invalid MP System Configuration type %d\n",␊ |
409 | ␉␉␉ fps_ptr->feature_info[0]);␊ |
410 | ␉␉return 1;␊ |
411 | ␉}␊ |
412 | ␊ |
413 | ␉if (fps_ptr->cth_ptr) {␊ |
414 | ␉␉sum = get_checksum((unsigned)local_cth_ptr,␊ |
415 | local_cth_ptr->base_length);␊ |
416 | ␉␉if (local_cth_ptr->sig != IMPS_CTH_SIGNATURE || sum) {␊ |
417 | ␉␉␉DBG(" Bad MP Config Table sig 0x%x and/or checksum 0x%x\n", (unsigned)(fps_ptr->cth_ptr), sum);␊ |
418 | ␉␉␉return 1;␊ |
419 | ␉␉}␊ |
420 | ␉␉if (local_cth_ptr->spec_rev != fps_ptr->spec_rev) {␊ |
421 | ␉␉␉DBG(" Bad MP Config Table sub-revision # %d\n", local_cth_ptr->spec_rev);␊ |
422 | ␉␉␉return 1;␊ |
423 | ␉␉}␊ |
424 | ␉␉if (local_cth_ptr->extended_length) {␊ |
425 | ␉␉␉sum = (get_checksum(((unsigned)local_cth_ptr)␊ |
426 | ␉␉␉␉␉ + local_cth_ptr->base_length,␊ |
427 | ␉␉␉␉␉ local_cth_ptr->extended_length)␊ |
428 | ␉␉␉ + local_cth_ptr->extended_checksum) & 0xFF;␊ |
429 | ␉␉␉if (sum) {␊ |
430 | ␉␉␉␉DBG(" Bad Extended MP Config Table checksum 0x%x\n", sum);␊ |
431 | ␉␉␉␉return 1;␊ |
432 | ␉␉␉}␊ |
433 | ␉␉}␊ |
434 | ␉} else if (!fps_ptr->feature_info[0]) {␊ |
435 | ␉␉DBG(" Missing configuration information\n");␊ |
436 | ␉␉return 1;␊ |
437 | ␉}␊ |
438 | ␊ |
439 | ␉return 0;␊ |
440 | }␊ |
441 | ␊ |
442 | ␊ |
443 | static void␊ |
444 | imps_read_bios(imps_fps *fps_ptr)␊ |
445 | {␊ |
446 | ␉int apicid;␊ |
447 | ␉unsigned cth_start, cth_count;␊ |
448 | ␉imps_cth *local_cth_ptr␊ |
449 | ␉␉= (imps_cth *)PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);␊ |
450 | ␉char *str_ptr;␉␊ |
451 | ␊ |
452 | ␉/*␊ |
453 | ␉ * Do all checking of errors which would definitely␊ |
454 | ␉ * lead to failure of the SMP boot here.␊ |
455 | ␉ */␊ |
456 | ␊ |
457 | ␉if (imps_bad_bios(fps_ptr)) {␊ |
458 | ␉␉DBG(" Disabling MPS support\n");␊ |
459 | ␉␉return;␊ |
460 | ␉}␊ |
461 | ␉␊ |
462 | ␉verbose("Intel MultiProcessor Spec 1.%d BIOS support detected\n",␊ |
463 | ␉␉ fps_ptr->spec_rev);␊ |
464 | ␊ |
465 | ␉if (fps_ptr->feature_info[1] & IMPS_FPS_IMCRP_BIT) {␊ |
466 | ␉␉str_ptr = "IMCR and PIC";␊ |
467 | ␉} else {␊ |
468 | ␉␉str_ptr = "Virtual Wire";␊ |
469 | ␉}␊ |
470 | ␉if (fps_ptr->cth_ptr) {␊ |
471 | ␉␉imps_lapic_addr = local_cth_ptr->lapic_addr;␊ |
472 | ␉} else {␊ |
473 | ␉␉imps_lapic_addr = LAPIC_ADDR_DEFAULT;␊ |
474 | ␉}␊ |
475 | ␉msglog(" APIC config: \"%s mode\" Local APIC address: 0x%x\n",␊ |
476 | ␉␉ str_ptr, imps_lapic_addr);␊ |
477 | ␉//msr_t msr32 = rdmsr(0x1b); ␊ |
478 | ␉if (imps_lapic_addr != (rdmsr(0x1b).lo & 0xFFFFF000)) {␊ |
479 | ␉␉DBG("Inconsistent Local APIC address, Disabling SMP support\n");␊ |
480 | ␉␉return;␊ |
481 | ␉}␊ |
482 | ␉imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);␊ |
483 | ␊ |
484 | ␉/*␊ |
485 | ␉ * Setup primary CPU.␊ |
486 | ␉ */␊ |
487 | ␉apicid = IMPS_LAPIC_READ(LAPIC_SPIV);␊ |
488 | ␉IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);␊ |
489 | ␉apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));␊ |
490 | ␉imps_cpu_apic_map[0] = apicid;␊ |
491 | ␉imps_apic_cpu_map[apicid] = 0;␊ |
492 | ␊ |
493 | ␉if (fps_ptr->cth_ptr) {␊ |
494 | ␉␉char str1[16], str2[16];␊ |
495 | ␉␉memcpy(str1, local_cth_ptr->oem_id, 8);␊ |
496 | ␉␉str1[8] = 0;␊ |
497 | ␉␉memcpy(str2, local_cth_ptr->prod_id, 12);␊ |
498 | ␉␉str2[12] = 0;␊ |
499 | ␉␉msglog(" OEM id: %s Product id: %s\n", str1, str2);␊ |
500 | ␉␉cth_start = ((unsigned) local_cth_ptr) + sizeof(imps_cth);␊ |
501 | ␉␉cth_count = local_cth_ptr->entry_count;␊ |
502 | ␉} else {␊ |
503 | ␉␉*((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_ID;␊ |
504 | ␉␉defconfig.ioapic.id␊ |
505 | ␉␉␉= APIC_ID(*((volatile unsigned *)␊ |
506 | ␉␉␉␉ (IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));␊ |
507 | ␉␉*((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_VER;␊ |
508 | ␉␉defconfig.ioapic.ver␊ |
509 | ␉␉␉= APIC_VERSION(*((volatile unsigned *)␊ |
510 | ␉␉␉␉␉ (IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));␊ |
511 | ␉␉defconfig.proc[apicid].flags␊ |
512 | ␉␉ = IMPS_FLAG_ENABLED|IMPS_CPUFLAG_BOOT;␊ |
513 | ␉␉defconfig.proc[!apicid].flags = IMPS_FLAG_ENABLED;␊ |
514 | ␉␉imps_num_cpus = 2;␊ |
515 | ␉␉if (fps_ptr->feature_info[0] == 1␊ |
516 | ␉␉ || fps_ptr->feature_info[0] == 5) {␊ |
517 | ␉␉␉memcpy(defconfig.bus[0].bus_type, "ISA ", 6);␊ |
518 | ␉␉}␊ |
519 | ␉␉if (fps_ptr->feature_info[0] == 4␊ |
520 | ␉␉ || fps_ptr->feature_info[0] == 7) {␊ |
521 | ␉␉␉memcpy(defconfig.bus[0].bus_type, "MCA ", 6);␊ |
522 | ␉␉}␊ |
523 | ␉␉if (fps_ptr->feature_info[0] > 4) {␊ |
524 | ␉␉␉defconfig.proc[0].apic_ver = 0x10;␊ |
525 | ␉␉␉defconfig.proc[1].apic_ver = 0x10;␊ |
526 | ␉␉␉defconfig.bus[1].type = IMPS_BCT_BUS;␊ |
527 | ␉␉}␊ |
528 | ␉␉if (fps_ptr->feature_info[0] == 2) {␊ |
529 | ␉␉␉defconfig.intin[2].type = 255;␊ |
530 | ␉␉␉defconfig.intin[13].type = 255;␊ |
531 | ␉␉}␊ |
532 | ␉␉if (fps_ptr->feature_info[0] == 7) {␊ |
533 | ␉␉␉defconfig.intin[0].type = 255;␊ |
534 | ␉␉}␊ |
535 | ␉␉cth_start = (unsigned) &defconfig;␊ |
536 | ␉␉cth_count = DEF_ENTRIES;␊ |
537 | ␉}␊ |
538 | ␉imps_read_config_table(cth_start, cth_count);␊ |
539 | ␊ |
540 | ␉/* %%%%% ESB read extended entries here */␊ |
541 | ␊ |
542 | ␉imps_enabled = 1;␊ |
543 | }␊ |
544 | ␊ |
545 | ␊ |
546 | /*␊ |
547 | * Given a region to check, this actually looks for the "MP Floating␊ |
548 | * Pointer Structure". The return value indicates if the correct␊ |
549 | * signature and checksum for a floating pointer structure of the␊ |
550 | * appropriate spec revision was found. If so, then do not search␊ |
551 | * further.␊ |
552 | *␊ |
553 | * NOTE: The memory scan will always be in the bottom 1 MB.␊ |
554 | *␊ |
555 | * This function presumes that "start" will always be aligned to a 16-bit␊ |
556 | * boundary.␊ |
557 | *␊ |
558 | * Function finished.␊ |
559 | */␊ |
560 | imps_fps *gfps_ptr;␊ |
561 | ␊ |
562 | static int␊ |
563 | imps_scan(unsigned start, unsigned length)␊ |
564 | {␊ |
565 | ␉DBG("Scanning from 0x%x for %d bytes\n",␊ |
566 | ␉␉␉ start, length);␊ |
567 | ␊ |
568 | ␉while (length > 0) {␊ |
569 | ␉␉gfps_ptr = (imps_fps *) PHYS_TO_VIRTUAL(start);␊ |
570 | ␊ |
571 | ␉␉if (gfps_ptr->sig == IMPS_FPS_SIGNATURE␊ |
572 | ␉␉ && gfps_ptr->length == 1␊ |
573 | ␉␉ && (gfps_ptr->spec_rev == 1 || gfps_ptr->spec_rev == 4)␊ |
574 | ␉␉ && !get_checksum(start, 16)) {␊ |
575 | ␉␉␉DBG("Found MP Floating Structure Pointer at %x\n", start);␊ |
576 | ␉␉␉imps_read_bios(gfps_ptr);␊ |
577 | ␉␉␉␊ |
578 | ␉␉␉return 1;␊ |
579 | ␉␉}␊ |
580 | ␊ |
581 | ␉␉length -= 16;␊ |
582 | ␉␉start += 16;␊ |
583 | ␉}␊ |
584 | ␊ |
585 | ␉return 0;␊ |
586 | }␊ |
587 | ␊ |
588 | #if BOOT_CPU␊ |
589 | /*␊ |
590 | * This is the primary function to "force" SMP support, with␊ |
591 | * the assumption that you have consecutively numbered APIC ids.␊ |
592 | */␊ |
593 | int␊ |
594 | imps_force(int ncpus)␊ |
595 | {␊ |
596 | ␉int apicid, i;␊ |
597 | ␉imps_processor p;␊ |
598 | ␊ |
599 | ␉DBG("Intel MultiProcessor \"Force\" Support\n");␊ |
600 | ␊ |
601 | ␉imps_lapic_addr = (rdmsr(0x1b).lo & 0xFFFFF000);␊ |
602 | ␉imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);␊ |
603 | ␊ |
604 | ␉/*␊ |
605 | ␉ * Setup primary CPU.␊ |
606 | ␉ */␊ |
607 | ␉apicid = IMPS_LAPIC_READ(LAPIC_SPIV);␊ |
608 | ␉IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);␊ |
609 | ␉apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));␊ |
610 | ␉imps_cpu_apic_map[0] = apicid;␊ |
611 | ␉imps_apic_cpu_map[apicid] = 0;␊ |
612 | ␊ |
613 | ␉p.type = 0;␊ |
614 | ␉p.apic_ver = 0x10;␊ |
615 | ␉p.signature = p.features = 0;␊ |
616 | ␊ |
617 | ␉for (i = 0; i < ncpus; i++) {␊ |
618 | ␉␉if (apicid == i) {␊ |
619 | ␉␉␉p.flags = IMPS_FLAG_ENABLED | IMPS_CPUFLAG_BOOT;␊ |
620 | ␉␉} else {␊ |
621 | ␉␉␉p.flags = IMPS_FLAG_ENABLED;␊ |
622 | ␉␉}␊ |
623 | ␉␉p.apic_id = i;␊ |
624 | ␉␉add_processor(&p);␊ |
625 | ␉}␊ |
626 | ␊ |
627 | ␉return imps_num_cpus;␊ |
628 | }␊ |
629 | #endif␊ |
630 | ␊ |
631 | /*␊ |
632 | * This is the primary function for probing for MPS compatible hardware␊ |
633 | * and BIOS information. Call this during the early stages of OS startup,␊ |
634 | * before memory can be messed up.␊ |
635 | *␊ |
636 | * The probe looks for the "MP Floating Pointer Structure" at locations␊ |
637 | * listed at the top of page 4-2 of the spec.␊ |
638 | *␊ |
639 | * Environment requirements from the OS to run:␊ |
640 | *␊ |
641 | * (1) : A non-linear virtual to physical memory mapping is probably OK,␊ |
642 | *␉ as (I think) the structures all fall within page boundaries,␊ |
643 | *␉ but a linear mapping is recommended. Currently assumes that␊ |
644 | *␉ the mapping will remain identical over time (which should be␊ |
645 | *␉ OK since it only accesses memory which shouldn't be munged␊ |
646 | *␉ by the OS anyway).␊ |
647 | * (2) : The OS only consumes memory which the BIOS says is OK to use,␊ |
648 | *␉ and not any of the BIOS standard areas (the areas 0x400 to␊ |
649 | *␉ 0x600, the EBDA, 0xE0000 to 0xFFFFF, and unreported physical␊ |
650 | *␉ RAM). Sometimes a small amount of physical RAM is not␊ |
651 | *␉ reported by the BIOS, to be used to store MPS and other␊ |
652 | *␉ information.␊ |
653 | * (3) : It must be possible to read the CMOS.␊ |
654 | * (4) : There must be between 512K and 640K of lower memory (this is a␊ |
655 | *␉ sanity check).␊ |
656 | *␊ |
657 | * Function finished.␊ |
658 | */␊ |
659 | ␊ |
660 | void *␊ |
661 | imps_probe(int * num_cpus)␊ |
662 | {␊ |
663 | ␉/*␊ |
664 | ␉ * Determine possible address of the EBDA␊ |
665 | ␉ */␊ |
666 | ␉unsigned ebda_addr = *((unsigned short *)␊ |
667 | ␉␉␉ PHYS_TO_VIRTUAL(EBDA_SEG_ADDR)) << 4;␊ |
668 | ␊ |
669 | ␉/*␊ |
670 | ␉ * Determine amount of installed lower memory (not *available*␊ |
671 | ␉ * lower memory).␊ |
672 | ␉ *␊ |
673 | ␉ * NOTE: This should work reliably as long as we verify the␊ |
674 | ␉ * machine is at least a system that could possibly have␊ |
675 | ␉ * MPS compatibility to begin with.␊ |
676 | ␉ */␊ |
677 | ␉unsigned mem_lower = ((CMOS_READ_BYTE(CMOS_BASE_MEMORY+1) << 8)␊ |
678 | ␉␉␉ | CMOS_READ_BYTE(CMOS_BASE_MEMORY)) << 10;␊ |
679 | ␊ |
680 | #ifdef DEBUG_SMP␊ |
681 | #if BOOT_CPU␊ |
682 | ␉imps_enabled = 0;␊ |
683 | ␉imps_num_cpus = 1;␊ |
684 | #endif␊ |
685 | #endif␊ |
686 | ␊ |
687 | ␉/*␊ |
688 | ␉ * Sanity check : if this isn't reasonable, it is almost impossibly␊ |
689 | ␉ * unlikely to be an MPS compatible machine, so return failure.␊ |
690 | ␉ */␊ |
691 | ␉if (mem_lower < 512*1024 || mem_lower > 640*1024) {␊ |
692 | ␉␉return 0;␊ |
693 | ␉}␊ |
694 | ␊ |
695 | ␉if (ebda_addr > mem_lower - 1024␊ |
696 | ␉ || ebda_addr + *((unsigned char *) PHYS_TO_VIRTUAL(ebda_addr))␊ |
697 | * 1024 > mem_lower) {␊ |
698 | ␉␉ebda_addr = 0;␊ |
699 | ␉}␊ |
700 | ␊ |
701 | ␉if (((ebda_addr && imps_scan(ebda_addr, 1024))␊ |
702 | ␉ || (!ebda_addr && imps_scan(mem_lower - 1024, 1024))␊ |
703 | ␉ || imps_scan(0xF0000, 0x10000)) && imps_enabled) {␊ |
704 | ␉␉*num_cpus = imps_num_cpus;␊ |
705 | ␉␉msglog("\n");␊ |
706 | ␉␉return gfps_ptr;␊ |
707 | ␉}␊ |
708 | ␊ |
709 | ␉/*␊ |
710 | ␉ * If no BIOS info on MPS hardware is found, then return failure.␊ |
711 | ␉ */␊ |
712 | ␉*num_cpus = 0;␊ |
713 | ␉msglog("\n");␊ |
714 | ␉return NULL;␊ |
715 | }␊ |
716 | |