small comment about change needing done.
[AROS.git] / arch / all-pc / kernel / apic_ia32.c
blobddaedb52c937ed838a7aa5709b25f17710bccb12
1 /*
2 Copyright © 1995-2017, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Intel IA-32 APIC driver.
6 */
8 #include <aros/macros.h>
9 #include <asm/cpu.h>
10 #include <asm/io.h>
11 #include <exec/types.h>
13 #define __KERNEL_NOLIBBASE__
14 #include <proto/kernel.h>
15 #include <proto/exec.h>
16 #include <proto/acpica.h>
18 #include <acpica/acnames.h>
19 #include <acpica/accommon.h>
21 #include <inttypes.h>
23 #include "kernel_base.h"
24 #include "kernel_intern.h"
25 #include "kernel_objects.h"
26 #include "kernel_debug.h"
27 #include "kernel_syscall.h"
28 #include "kernel_timer.h"
30 #include "kernel_interrupts.h"
32 #include "acpi.h"
33 #include "apic_ia32.h"
35 #define D(x)
36 #define DINT(x)
37 #define DWAKE(x) /* Badly interferes with AP startup */
38 #define DID(x) /* Badly interferes with everything */
39 /* #define DEBUG_WAIT */
42 * On i386 platform we need to support various quirks of old APICs.
43 * x86-64 is free of that crap.
45 #ifdef __i386__
46 #define CONFIG_LEGACY
47 #endif
49 extern void core_APICErrorHandle(struct ExceptionContext *, void *, void *);
50 AROS_INTP(APICHeartbeatServer);
52 /* APIC Interrupt Controller Functions ... ***************************/
54 struct APICInt_Private
59 icid_t APICInt_Register(struct KernelBase *KernelBase)
61 DINT(bug("[Kernel:APIC-IA32] %s()\n", __func__));
63 return (icid_t)APICInt_IntrController.ic_Node.ln_Type;
66 BOOL APICInt_Init(struct KernelBase *KernelBase, icid_t instanceCount)
68 struct PlatformData *kernPlatD = (struct PlatformData *)KernelBase->kb_PlatformData;
69 struct ACPIData *acpiData = kernPlatD->kb_ACPI;
70 struct APICData *apicPrivate = kernPlatD->kb_APIC;
71 APTR ssp;
72 int irq, count = 0;
74 DINT(bug("[Kernel:APIC-IA32] %s(%d)\n", __func__, instanceCount));
76 /* its not fatal to fail on these irqs... */
77 if ((ssp = SuperState()) != NULL)
79 /* Setup the APIC IRQs for CPU #0*/
80 for (irq = (APIC_IRQ_BASE - X86_CPU_EXCEPT_COUNT); irq < ((APIC_IRQ_BASE - X86_CPU_EXCEPT_COUNT) + APIC_IRQ_COUNT); irq++)
82 if (((APIC_IRQ_HEARTBEAT - HW_IRQ_BASE) == irq) && (apicPrivate->flags & APF_TIMER))
84 /* if we have the heartbeat timer, its time to enable it for the boot processor... */
85 ictl_enable_irq((APIC_IRQ_HEARTBEAT - HW_IRQ_BASE), KernelBase);
87 else
89 if (!krnInitInterrupt(KernelBase, irq, APICInt_IntrController.ic_Node.ln_Type, 0))
91 D(bug("[Kernel:APIC-IA32] %s: failed to obtain IRQ %d\n", __func__, irq);)
93 else
95 /* dont enable the vector yet...*/
96 if (!core_SetIDTGate((apicidt_t *)apicPrivate->cores[0].cpu_IDT, HW_IRQ_BASE + irq, (uintptr_t)IntrDefaultGates[HW_IRQ_BASE + irq], FALSE))
98 bug("[Kernel:APIC-IA32] %s: failed to set IRQ %d's Vector gate\n", __func__, irq);
100 else
101 count++;
105 UserState(ssp);
109 * If we have atleast 32 APIC interrupts available (the
110 * most a single MSI device will request) then report that
111 * we can use MSI
113 if ((count > 32) && (acpiData->acpi_fadt))
115 ACPI_TABLE_FADT *fadt = (ACPI_TABLE_FADT *)acpiData->acpi_fadt;
117 if ((!(fadt->BootFlags & ACPI_FADT_NO_MSI)) &&
118 (!(kernPlatD->kb_PDFlags & PLATFORMF_HAVEMSI)))
120 // TODO: Register the MSI interrupt controller..
121 kernPlatD->kb_PDFlags |= PLATFORMF_HAVEMSI;
122 bug("[Kernel:APIC-IA32] MSI Interrupts enabled\n");
125 return TRUE;
128 BOOL APICInt_DisableIRQ(APTR icPrivate, icid_t icInstance, icid_t intNum)
130 struct PlatformData *kernPlatD = (struct PlatformData *)KernelBase->kb_PlatformData;
131 struct APICData *apicPrivate = kernPlatD->kb_APIC;
132 apicid_t cpuNum = KrnGetCPUNumber();
133 apicidt_t *IGATES;
134 APTR ssp = NULL;
135 BOOL retVal = FALSE;
137 DINT(bug("[Kernel:APIC-IA32.%03u] %s(#$%02X)\n", cpuNum, __func__, intNum));
139 IGATES = (apicidt_t *)apicPrivate->cores[cpuNum].cpu_IDT;
141 if ((KrnIsSuper()) || ((ssp = SuperState()) != NULL))
143 IGATES[HW_IRQ_BASE + intNum].p = 0;
144 retVal = TRUE;
146 if (ssp)
147 UserState(ssp);
150 return retVal;
153 BOOL APICInt_EnableIRQ(APTR icPrivate, icid_t icInstance, icid_t intNum)
155 struct PlatformData *kernPlatD = (struct PlatformData *)KernelBase->kb_PlatformData;
156 struct APICData *apicPrivate = kernPlatD->kb_APIC;
157 apicid_t cpuNum = KrnGetCPUNumber();
158 apicidt_t *IGATES;
159 APTR ssp = NULL;
160 BOOL retVal = FALSE;
162 DINT(bug("[Kernel:APIC-IA32.%03u] %s(#$%02X)\n", cpuNum, __func__, intNum));
164 IGATES = (apicidt_t *)apicPrivate->cores[cpuNum].cpu_IDT;
166 if ((KrnIsSuper()) || ((ssp = SuperState()) != NULL))
168 IGATES[HW_IRQ_BASE + intNum].p = 1;
169 retVal = TRUE;
171 if (ssp)
172 UserState(ssp);
175 return retVal;
178 BOOL APICInt_AckIntr(APTR icPrivate, icid_t icInstance, icid_t intNum)
180 IPTR apic_base;
182 DINT(bug("[Kernel:APIC-IA32] %s(%03u #$%02X)\n", __func__, icInstance, intNum));
184 /* Write zero to EOI of APIC */
185 apic_base = core_APIC_GetBase();
187 APIC_REG(apic_base, APIC_EOI) = 0;
189 return TRUE;
192 struct IntrController APICInt_IntrController =
195 .ln_Name = "x86 Local APIC",
196 .ln_Pri = -50
199 AROS_MAKE_ID('A','P','I','C'),
201 NULL,
202 APICInt_Register,
203 APICInt_Init,
204 APICInt_EnableIRQ,
205 APICInt_DisableIRQ,
206 APICInt_AckIntr
209 /* APIC IPI Related Functions ... ***************************/
211 static ULONG DoIPI(IPTR __APICBase, ULONG target, ULONG cmd)
213 ULONG ipisend_timeout, status_ipisend;
216 apicid_t cpuNum = KrnGetCPUNumber();
217 bug("[Kernel:APIC-IA32.%03u] %s: Command 0x%08X to target %03u\n", cpuNum, __func__, cmd, target);
221 * Send the IPI.
222 * First we write target APIC ID into high command register.
223 * Writing to the low register triggers the IPI itself.
225 APIC_REG(__APICBase, APIC_ICRH) = target << 24;
226 APIC_REG(__APICBase, APIC_ICRL) = cmd;
228 D(bug("[Kernel:APIC-IA32.%03u] %s: Waiting for IPI to complete ", cpuNum, __func__));
230 for (ipisend_timeout = 1000; ipisend_timeout > 0; ipisend_timeout--)
232 pit_udelay(100);
233 #ifdef DEBUG_WAIT
234 if ((ipisend_timeout % 100) == 0)
236 bug(".");
238 #endif
239 status_ipisend = APIC_REG(__APICBase, APIC_ICRL) & ICR_DS;
240 /* Delivery status resets to 0 when delivery is done */
241 if (status_ipisend == 0)
242 break;
244 D(bug("\n"));
245 D(bug("[Kernel:APIC-IA32.%03u] %s: ... left wait loop (status = 0x%08X)\n", cpuNum, __func__, status_ipisend));
247 return status_ipisend;
250 /**********************************************************
251 Driver functions
252 **********************************************************/
254 void core_APIC_Init(struct APICData *apic, apicid_t cpuNum)
256 IPTR __APICBase = apic->lapicBase;
257 ULONG apic_ver = APIC_REG(__APICBase, APIC_VERSION);
258 ULONG maxlvt = APIC_LVT(apic_ver), calibrated = 0;
259 LONG lapic_initial, lapic_final;
260 UQUAD tsc_initial, tsc_final;
261 UQUAD calibrated_tsc = 0;
262 WORD pit_final;
263 icintrid_t coreICInstID;
265 #ifdef CONFIG_LEGACY
266 /* 82489DX doesnt report no. of LVT entries. */
267 if (!APIC_INTEGRATED(apic_ver))
268 maxlvt = 2;
269 #endif
271 if ((coreICInstID = krnAddInterruptController(KernelBase, &APICInt_IntrController)) != (icintrid_t)-1)
273 APTR ssp = NULL;
274 int i;
276 D(bug("[Kernel:APIC-IA32.%03u] %s: APIC IC ID #%d:%d\n", cpuNum, __func__, ICINTR_ICID(coreICInstID), ICINTR_INST(coreICInstID)));
279 * NB: - BSP calls us in user mode, but AP's call us from supervisor
281 if ((KrnIsSuper()) || ((ssp = SuperState()) != NULL))
283 /* Obtain/set the critical IRQs and Vectors */
284 for (i = 0; i < X86_CPU_EXCEPT_COUNT; i++)
286 if ((HW_IRQ_BASE < i) && (cpuNum == 0))
288 if (!krnInitInterrupt(KernelBase, (i - HW_IRQ_BASE), APICInt_IntrController.ic_Node.ln_Type, 0))
290 krnPanic(NULL, "Failed to obtain APIC Exception IRQ\n"
291 "IRQ #$%02X\n", (i - HW_IRQ_BASE));
293 if (!core_SetIRQGate(apic->cores[cpuNum].cpu_IDT, (i - HW_IRQ_BASE), (uintptr_t)IntrDefaultGates[i]))
295 krnPanic(NULL, "Failed to set APIC Exception IRQ Vector\n"
296 "IRQ #$%02X, Vector #$%02X\n", (i - HW_IRQ_BASE), i);
299 else if (!core_SetIDTGate((apicidt_t *)apic->cores[cpuNum].cpu_IDT, i, (uintptr_t)IntrDefaultGates[i], TRUE))
301 krnPanic(NULL, "Failed to set APIC Exception Vector\n"
302 "Vector #$%02X\n", i);
305 D(bug("[Kernel:APIC-IA32.%03u] %s: APIC Exception Vectors configured\n", cpuNum, __func__));
307 if ((APIC_IRQ_ERROR < HW_IRQ_COUNT) && (cpuNum == 0))
309 if (!krnInitInterrupt(KernelBase, (APIC_IRQ_ERROR - HW_IRQ_BASE), APICInt_IntrController.ic_Node.ln_Type, 0))
311 krnPanic(NULL, "Failed to obtain APIC Error IRQ\n"
312 "IRQ #$%02X\n", (APIC_IRQ_ERROR - HW_IRQ_BASE));
314 if (!core_SetIRQGate(apic->cores[cpuNum].cpu_IDT, (APIC_IRQ_ERROR - HW_IRQ_BASE), (uintptr_t)IntrDefaultGates[APIC_IRQ_ERROR]))
316 krnPanic(NULL, "Failed to set APIC Error IRQ Vector\n"
317 "IRQ #$%02X, Vector #$%02X\n", (APIC_IRQ_ERROR - HW_IRQ_BASE), APIC_IRQ_ERROR);
320 else if (!core_SetIDTGate((apicidt_t *)apic->cores[cpuNum].cpu_IDT, APIC_IRQ_ERROR, (uintptr_t)IntrDefaultGates[APIC_IRQ_ERROR], TRUE))
322 krnPanic(NULL, "Failed to set APIC Error Vector\n"
323 "Vector #$%02X\n", APIC_IRQ_ERROR);
325 else if (cpuNum == 0)
326 KrnAddExceptionHandler((APIC_IRQ_ERROR - APIC_IRQ_COUNT), core_APICErrorHandle, NULL, NULL);
328 D(bug("[Kernel:APIC-IA32.%03u] %s: APIC Error Vector #$%02X configured\n", cpuNum, __func__, APIC_IRQ_ERROR));
330 for (i = APIC_IRQ_IPI_START; i <= APIC_IRQ_IPI_END; i++)
332 if (!core_SetIDTGate((apicidt_t *)apic->cores[cpuNum].cpu_IDT, i, (uintptr_t)IntrDefaultGates[i], TRUE))
334 krnPanic(NULL, "Failed to set APIC IPI Vector\n"
335 "Vector #$%02X\n", i);
338 D(bug("[Kernel::APIC-IA32.%03u] %s: APIC IPI Vectors configured\n", cpuNum, __func__));
340 if (ssp)
341 UserState(ssp);
343 else
345 krnPanic(NULL, "Failed to configure APIC\n"
346 "APIC #%03e ID %03u\n", cpuNum, apic->cores[cpuNum].cpu_LocalID);
349 /* Use flat interrupt model with logical destination ID = 1 */
350 APIC_REG(__APICBase, APIC_DFR) = DFR_FLAT;
351 APIC_REG(__APICBase, APIC_LDR) = 1 << LDR_ID_SHIFT;
353 /* Set Task Priority to 'accept all interrupts' */
354 APIC_REG(__APICBase, APIC_TPR) = 0;
356 /* Set spurious IRQ vector to 0xFF. APIC enabled, focus check disabled. */
357 APIC_REG(__APICBase, APIC_SVR) = SVR_ASE|SVR_FCC|0xFF;
360 * Set LINT0 to external and LINT1 to NMI.
361 * These are common defaults and they are going to be overriden by ACPI tables.
363 if (cpuNum == 0)
364 APIC_REG(__APICBase, APIC_LINT0_VEC) = LVT_MT_EXT;
365 else
366 APIC_REG(__APICBase, APIC_LINT0_VEC) = LVT_MASK;
368 APIC_REG(__APICBase, APIC_LINT1_VEC) = LVT_MT_NMI;
370 #ifdef CONFIG_LEGACY
371 /* Due to the Pentium erratum 3AP. */
372 if (maxlvt > 3)
373 APIC_REG(__APICBase, APIC_ESR) = 0;
374 #endif
376 D(bug("[Kernel:APIC-IA32.%03u] %s: APIC ESR before enabling vector: %08x\n", cpuNum, __func__, APIC_REG(__APICBase, APIC_ESR)));
378 /* Set APIC error interrupt to fixed vector interrupt "APIC_IRQ_ERROR", on APIC error */
379 APIC_REG(__APICBase, APIC_ERROR_VEC) = APIC_IRQ_ERROR;
381 /* spec says clear errors after enabling vector. */
382 if (maxlvt > 3)
383 APIC_REG(__APICBase, APIC_ESR) = 0;
385 D(bug("[Kernel:APIC-IA32.%03u] %s: APIC ESR after enabling vector: %08x\n", cpuNum, __func__, APIC_REG(__APICBase, APIC_ESR)));
388 * Now the tricky thing - calibrate LAPIC timer frequency.
389 * In fact we could simply query CPU's clock frequency, but... x86 sucks. There's no
390 * unified way to get it on whatever CPU. Intel has own way, AMD has own way... Etc... Which, additionally,
391 * varies between CPU generations.
393 * The idea behing the calibration is to run the timer once, and see how much ticks passes in some defined
394 * period of time. Then calculate a proportion.
395 * We use 8253 PIT as our reference.
396 * This calibrarion algorighm is based on NetBSD one.
399 /* Set the timer to one-shot mode, no interrupt, 1:1 divisor */
400 APIC_REG(__APICBase, APIC_TIMER_VEC) = LVT_MASK;
401 APIC_REG(__APICBase, APIC_TIMER_DIV) = TIMER_DIV_1;
402 APIC_REG(__APICBase, APIC_TIMER_ICR) = 0x80000000; /* Just some very large value */
405 * Now wait for 11931 PIT ticks, which is equal to 10 milliseconds.
406 * We don't use pit_udelay() here, because for improved accuracy we need to sample LAPIC timer counter twice,
407 * before and after our actual delay (PIT setup also takes up some time, so LAPIC will count away from its
408 * initial value). We run it 10 times to make up for cache setup discrepancies.
410 for (i = 0; i < 10; i ++)
412 pit_start(11931);
413 lapic_initial = (LONG)APIC_REG(__APICBase, APIC_TIMER_CCR);
414 tsc_initial = RDTSC();
416 pit_final = pit_wait(11931);
418 tsc_final = RDTSC();
419 lapic_final = (LONG)APIC_REG(__APICBase, APIC_TIMER_CCR);
421 calibrated += (((QUAD)(lapic_initial - lapic_final) * 11931LL)/(11931LL - (QUAD)pit_final)) ;
422 calibrated_tsc += ((tsc_final - tsc_initial) * 11931LL) / (11931LL - (QUAD)pit_final);
424 apic->cores[cpuNum].cpu_TimerFreq = 10 * calibrated;
425 apic->cores[cpuNum].cpu_TSCFreq = 10 * calibrated_tsc;
426 D(bug("[Kernel:APIC-IA32.%03u] %s: LAPIC frequency should be %u Hz (%u MHz)\n", cpuNum, __func__, apic->cores[cpuNum].cpu_TimerFreq, (apic->cores[cpuNum].cpu_TimerFreq + 500000) / 1000000));
427 D(bug("[Kernel:APIC-IA32.%03u] %s: TSC frequency should be %u kHz (%u MHz)\n", cpuNum, __func__, (ULONG)((apic->cores[cpuNum].cpu_TSCFreq + 500)/1000), (ULONG)((apic->cores[cpuNum].cpu_TSCFreq + 500000) / 1000000)));
429 * Once APIC timer has been calibrated -:
430 * # Set it to run at it's full frequency.
431 * # Enable the heartbeat vector and use a suitable rate,
432 * otherwise set to reload every second and disable it.
434 if (cpuNum == 0)
436 if (krnInitInterrupt(KernelBase, (APIC_IRQ_HEARTBEAT - HW_IRQ_BASE), APICInt_IntrController.ic_Node.ln_Type, 0))
438 struct IntrNode *hbHandle;
440 hbHandle = krnAllocIntrNode();
441 D(bug("[Kernel:APIC-IA32.%03u] %s: heartbeat IRQ #$%02X (%d) handler @ 0x%p\n", cpuNum, __func__, (APIC_IRQ_HEARTBEAT - HW_IRQ_BASE), (APIC_IRQ_HEARTBEAT - HW_IRQ_BASE), hbHandle);)
443 if (hbHandle)
445 hbHandle->in_Handler = APICHeartbeatServer;
446 hbHandle->in_HandlerData = SysBase;
447 hbHandle->in_HandlerData2 = NULL;
448 hbHandle->in_type = it_interrupt;
449 hbHandle->in_nr = APIC_IRQ_HEARTBEAT - HW_IRQ_BASE;
451 Disable();
452 ADDHEAD(&KERNELIRQ_LIST(hbHandle->in_nr), &hbHandle->in_Node);
453 Enable();
455 apic->flags |= APF_TIMER;
457 else
459 D(bug("[Kernel:APIC-IA32.%03u] %s: failed to allocate HeartBeat handler\n", cpuNum, __func__);)
462 else
464 D(bug("[Kernel:APIC-IA32.%03u] %s: failed to obtain HeartBeat IRQ %d\n", cpuNum, __func__, (APIC_IRQ_HEARTBEAT - HW_IRQ_BASE));)
468 APIC_REG(__APICBase, APIC_TIMER_DIV) = TIMER_DIV_1;
470 if ((apic->flags & APF_TIMER) &&
471 ((KrnIsSuper()) || ((ssp = SuperState()) != NULL)))
473 #if defined(__AROSEXEC_SMP__)
474 tls_t *apicTLS = apic->cores[cpuNum].cpu_TLS;
475 struct X86SchedulerPrivate *schedData = apicTLS->ScheduleData;
476 D(bug("[Kernel:APIC-IA32.%03u] %s: tls @ 0x%p, scheduling data @ 0x%p\n", cpuNum, __func__, apicTLS, schedData);)
477 #endif
479 if (!core_SetIDTGate(apic->cores[cpuNum].cpu_IDT, APIC_IRQ_HEARTBEAT, (uintptr_t)IntrDefaultGates[APIC_IRQ_HEARTBEAT], TRUE))
481 krnPanic(NULL, "Failed to set APIC HeartBeat IRQ Vector\n"
482 "IRQ #$%02X, Vector #$%02X\n", (APIC_IRQ_HEARTBEAT - HW_IRQ_BASE), APIC_IRQ_HEARTBEAT);
485 apic->cores[cpuNum].cpu_LAPICTick = 0;
486 D(bug("[Kernel:APIC-IA32.%03u] %s: heartbeat IRQ Vector #$%02X (%d) set\n", cpuNum, __func__, APIC_IRQ_HEARTBEAT, APIC_IRQ_HEARTBEAT);)
488 if (ssp)
489 UserState(ssp);
491 #if defined(__AROSEXEC_SMP__)
492 // TODO: Adjust based on the amount of work the APIC can do at its given frequency.
493 schedData->Granularity = 1;
494 schedData->Quantum = 5;
495 APIC_REG(__APICBase, APIC_TIMER_ICR) = (apic->cores[cpuNum].cpu_TimerFreq);
496 #else
497 APIC_REG(__APICBase, APIC_TIMER_ICR) = (apic->cores[cpuNum].cpu_TimerFreq + 25) / 50;
498 #endif
499 APIC_REG(__APICBase, APIC_TIMER_VEC) = APIC_IRQ_HEARTBEAT; // | LVT_TMM_PERIOD;
502 else
504 APIC_REG(__APICBase, APIC_TIMER_ICR) = apic->cores[cpuNum].cpu_TimerFreq;
505 APIC_REG(__APICBase, APIC_TIMER_VEC) = LVT_MASK | LVT_TMM_PERIOD;
510 apicid_t core_APIC_GetID(IPTR _APICBase)
512 apicid_t _apic_id;
514 /* The actual ID is in 8 most significant bits */
515 _apic_id = APIC_REG(_APICBase, APIC_ID) >> APIC_ID_SHIFT;
516 DID(bug("[Kernel:APIC-IA32] %s: %03u\n", __func__, _apic_id));
518 return _apic_id;
521 ULONG core_APIC_Wake(APTR wake_apicstartrip, apicid_t wake_apicid, IPTR __APICBase)
523 ULONG status_ipisend, status_ipirecv;
524 ULONG start_count;
525 #ifdef CONFIG_LEGACY
526 ULONG apic_ver = APIC_REG(__APICBase, APIC_VERSION);
527 #endif
529 apicid_t cpuNo = KrnGetCPUNumber();
531 bug("[Kernel:APIC-IA32.%03u] %s(%03u @ %p)\n", cpuNo, __func__, wake_apicid, wake_apicstartrip);
532 bug("[Kernel:APIC-IA32.%03u] %s: Base @ %p\n", cpuNo, __func__, __APICBase);
534 #ifdef CONFIG_LEGACY
536 * Check if we have old 82489DX discrete APIC (version & 0xF0 == 0).
537 * This APIC needs different startup procedure. It doesn't support STARTUP IPI
538 * because old CPUs didn't have INIT signal. They jump to BIOS ROM boot code
539 * immediately after INIT IPI. In order to run the bootstrap, a BIOS warm reset
540 * magic has to be used there.
542 if (!APIC_INTEGRATED(apic_ver))
545 * BIOS warm reset magic, part one.
546 * Write real-mode bootstrap routine address to 40:67 (real-mode address) location.
547 * This is standard feature of IBM PC AT BIOS. If a warm reset condition is detected,
548 * the BIOS jumps to the given address.
550 D(bug("[Kernel:APIC-IA32.%03u] %s: Setting BIOS vector for trampoline @ %p ..\n", cpuNo, __func__, wake_apicstartrip));
551 *((volatile unsigned short *)0x469) = (IPTR)wake_apicstartrip >> 4;
552 *((volatile unsigned short *)0x467) = 0; /* Actually wake_apicstartrip & 0x0F, it's page-aligned. */
555 * BIOS warm reset magic, part two.
556 * This writes 0x0A into CMOS RAM, location 0x0F. This signals a warm reset condition to BIOS,
557 * making part one work.
559 D(bug("[Kernel:APIC-IA32.%03u] %s: Setting warm reset code ..\n", cpuNo, __func__));
560 outb(0xf, 0x70);
561 outb(0xa, 0x71);
563 #endif
565 /* Flush TLB (we are supervisor here) */
566 wrcr(cr3, rdcr(cr3));
568 /* First we send the INIT command (reset the core). Vector must be zero for this. */
569 status_ipisend = DoIPI(__APICBase, wake_apicid, ICR_INT_LEVELTRIG | ICR_INT_ASSERT | ICR_DM_INIT);
570 if (status_ipisend)
572 D(bug("[Kernel:APIC-IA32.%03u] %s: Error asserting INIT\n", cpuNo, __func__));
573 return status_ipisend;
576 /* Deassert INIT after a small delay */
577 pit_udelay(10 * 1000);
579 /* Deassert INIT */
580 status_ipisend = DoIPI(__APICBase, wake_apicid, ICR_INT_LEVELTRIG | ICR_DM_INIT);
581 if (status_ipisend)
583 D(bug("[Kernel:APIC-IA32.%03u] %s: Error deasserting INIT\n", cpuNo, __func__));
584 return status_ipisend;
587 /* memory barrier */
588 asm volatile("mfence":::"memory");
590 #ifdef CONFIG_LEGACY
591 /* If it's 82489DX, we are done. */
592 if (!APIC_INTEGRATED(apic_ver))
594 DWAKE(bug("[Kernel:APIC-IA32.%03u] %s: 82489DX detected, wakeup done\n", cpuNo, __func__));
595 return 0;
597 #endif
600 * Perform IPI STARTUP loop.
601 * According to official Intel specification, this must be done twice.
602 * It's not explained why. ;-)
604 for (start_count = 1; start_count <= 2; start_count++)
606 D(bug("[Kernel:APIC-IA32.%03u] %s: Attempting STARTUP .. %u\n", cpuNo, __func__, start_count));
608 /* Clear any pending error condition */
609 APIC_REG(__APICBase, APIC_ESR) = 0;
612 * Send STARTUP IPI.
613 * The processor starts up at CS = (vector << 16) and IP = 0.
615 status_ipisend = DoIPI(__APICBase, wake_apicid, ICR_DM_STARTUP | ((IPTR)wake_apicstartrip >> 12));
617 /* Allow the target APIC to accept the IPI */
618 pit_udelay(200);
620 #ifdef CONFIG_LEGACY
621 /* Pentium erratum 3AP quirk */
622 if (APIC_LVT(apic_ver) > 3)
623 APIC_REG(__APICBase, APIC_ESR) = 0;
624 #endif
626 status_ipirecv = APIC_REG(__APICBase, APIC_ESR) & 0xEF;
629 * EXPERIMENTAL:
630 * On my machine (macmini 3,1, as OS X system profiler says), the core starts up from first
631 * attempt. The second attempt ends up in error (according to the documentation, the STARTUP
632 * can be accepted only once, while the core in RESET or INIT state, and first STARTUP, if
633 * successful, brings the core out of this state).
634 * Here we try to detect this condition. If the core accepted STARTUP, we suggest that it has
635 * started up, and break the loop.
636 * A topic at osdev.org forum (http://forum.osdev.org/viewtopic.php?f=1&t=23018)
637 * also tells about some problems with double STARTUP. According to it, the second STARTUP can
638 * manage to re-run the core from the given address, leaving it in 64-bit mode, causing it to crash.
640 * If startup problems pops up (the core doesn't respond and AROS halts at "Launching APIC no X" stage),
641 * the following two variations of this algorithm can be tried:
642 * a) Always send STARTUP twice, but signal error condition only if both attempts failed.
643 * b) Send first STARTUP, abort on error. Allow second attempt to fail and ignore its result.
645 * Sonic <pavel_fedin@mail.ru>
647 if (!status_ipisend && !status_ipirecv)
648 break;
651 DWAKE(bug("[Kernel:APIC-IA32.%03u] %s: STARTUP run status 0x%08X, error 0x%08X\n", cpuNo, __func__, status_ipisend, status_ipirecv));
654 * We return nonzero on error.
655 * Actually least significant byte of this value holds ESR value, and 12th bit
656 * holds delivery status flag from DoIPI() routine. It will be '1' if we got
657 * stuck at sending phase.
659 return (status_ipisend | status_ipirecv);