apic_ia32: Compiler delint
[AROS.git] / arch / all-pc / kernel / apic_ia32.c
blob57dda53d641f35baf323f81a8476726eaa5aca74
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Intel IA-32 APIC driver.
6 */
8 #include <asm/cpu.h>
9 #include <asm/io.h>
10 #include <exec/types.h>
12 #include <inttypes.h>
14 #include "apic.h"
15 #include "apic_ia32.h"
16 #include "kernel_base.h"
17 #include "kernel_debug.h"
18 #include "kernel_syscall.h"
19 #include "kernel_timer.h"
21 #define D(x)
22 #define DWAKE(x) /* Badly interferes with AP startup */
23 /* #define DEBUG_WAIT */
26 * On i386 platform we need to support various quirks of old APICs.
27 * x86-64 is free of that crap.
29 #ifdef __i386__
30 #define CONFIG_LEGACY
31 #endif
33 static ULONG DoIPI(IPTR __APICBase, ULONG target, ULONG cmd)
35 ULONG ipisend_timeout, status_ipisend;
37 D(bug("[IPI] Command 0x%08X to target %u\n", cmd, target));
40 * Send the IPI.
41 * First we write target APIC ID into high command register.
42 * Writing to the low register triggers the IPI itself.
44 APIC_REG(__APICBase, APIC_ICRH) = target << 24;
45 APIC_REG(__APICBase, APIC_ICRL) = cmd;
47 D(bug("[IPI] Waiting for IPI to complete "));
49 for (ipisend_timeout = 1000; ipisend_timeout > 0; ipisend_timeout--)
51 pit_udelay(100);
52 #ifdef DEBUG_WAIT
53 if ((ipisend_timeout % 100) == 0)
55 bug(".");
57 #endif
58 status_ipisend = APIC_REG(__APICBase, APIC_ICRL) & ICR_DS;
59 /* Delivery status resets to 0 when delivery is done */
60 if (status_ipisend == 0)
61 break;
63 D(bug("\n"));
64 D(bug("[IPI] ... left wait loop (status = 0x%08X)\n", status_ipisend));
66 return status_ipisend;
69 /**********************************************************
70 Driver functions
71 **********************************************************/
73 void core_APIC_Init(struct APICData *apic, ULONG cpuNum)
75 IPTR __APICBase = apic->lapicBase;
76 ULONG apic_ver = APIC_REG(__APICBase, APIC_VERSION);
77 ULONG maxlvt = APIC_LVT(apic_ver);
78 ULONG lapic_initial, lapic_final;
79 WORD pit_final;
81 #ifdef CONFIG_LEGACY
82 /* 82489DX doesnt report no. of LVT entries. */
83 if (!APIC_INTEGRATED(apic_ver))
84 maxlvt = 2;
85 #endif
87 /* Use flat interrupt model with logical destination ID = 1 */
88 APIC_REG(__APICBase, APIC_DFR) = DFR_FLAT;
89 APIC_REG(__APICBase, APIC_LDR) = 1 << LDR_ID_SHIFT;
91 /* Set Task Priority to 'accept all interrupts' */
92 APIC_REG(__APICBase, APIC_TPR) = 0;
94 /* Set spurious IRQ vector to 0xFF. APIC enabled, focus check disabled. */
95 APIC_REG(__APICBase, APIC_SVR) = SVR_ASE|SVR_FCC|0xFF;
98 * Set LINT0 to external and LINT1 to NMI.
99 * These are common defaults and they are going to be overriden by ACPI tables.
101 APIC_REG(__APICBase, APIC_LINT0_VEC) = LVT_MT_EXT;
102 APIC_REG(__APICBase, APIC_LINT1_VEC) = LVT_MT_NMI;
104 #ifdef CONFIG_LEGACY
105 /* Due to the Pentium erratum 3AP. */
106 if (maxlvt > 3)
107 APIC_REG(__APICBase, APIC_ESR) = 0;
108 #endif
110 D(bug("[APIC.%u] _APIC_IA32_init: APIC ESR before enabling vector: %08x\n", cpuNum, APIC_REG(__APICBase, APIC_ESR)));
112 /* Set APIC error interrupt to fixed vector 0xFE interrupt on APIC error */
113 APIC_REG(__APICBase, APIC_ERROR_VEC) = 0xfe;
115 /* spec says clear errors after enabling vector. */
116 if (maxlvt > 3)
117 APIC_REG(__APICBase, APIC_ESR) = 0;
119 D(bug("[APIC.%u] _APIC_IA32_init: APIC ESR after enabling vector: %08x\n", cpuNum, APIC_REG(__APICBase, APIC_ESR)));
122 * Now the tricky thing - calibrate LAPIC timer frequency.
123 * In fact we could simply query CPU's clock frequency, but... x86 sucks. There's no
124 * unified way to get it on whatever CPU. Intel has own way, AMD has own way... Etc... Which, additionally,
125 * varies between CPU generations.
127 * The idea behing the calibration is to run the timer once, and see how much ticks passes in some defined
128 * period of time. Then calculate a proportion.
129 * We use 8253 PIT as our reference.
130 * This calibrarion algorighm is based on NetBSD one.
133 /* Set the timer to one-shot mode, no interrupt, 1:1 divisor */
134 APIC_REG(__APICBase, APIC_TIMER_VEC) = LVT_MASK;
135 APIC_REG(__APICBase, APIC_TIMER_DIV) = TIMER_DIV_1;
136 APIC_REG(__APICBase, APIC_TIMER_ICR) = 0x80000000; /* Just some very large value */
139 * Now wait for 11931 PIT ticks, which is equal to 10 milliseconds.
140 * We don't use pit_udelay() here, because for improved accuracy we need to sample LAPIC timer counter twice,
141 * before and after our actual delay (PIT setup also takes up some time, so LAPIC will count away from its
142 * initial value).
144 pit_start(11931);
145 lapic_initial = APIC_REG(__APICBase, APIC_TIMER_CCR);
147 pit_final = pit_wait(11931);
148 lapic_final = APIC_REG(__APICBase, APIC_TIMER_CCR);
151 * TODO: Upon exit from pit_wait() pit_final contains negated number of excessive ticks after 10ms has passed.
152 * This can be used to improve calibration quality (currently we report 265 mHz instead of 266).
154 D(bug("[APIC.%u] LAPIC counted from %u to %u in 10ms (%u ticks)\n", cpuNum, lapic_initial, lapic_final, 11931 - pit_final));
155 (void)pit_final; // Unused if not debugging - suppress compiler warning
157 apic->cores[cpuNum].timerFreq = (lapic_initial - lapic_final) * 100;
158 D(bug("[APIC.%u] LAPIC frequency should be %u Hz (%u mHz)\n", cpuNum, apic->cores[cpuNum].timerFreq, apic->cores[cpuNum].timerFreq / 1000000));
161 IPTR core_APIC_GetBase(void)
163 IPTR _apic_base = 0;
165 if (IN_USER_MODE)
167 D(bug("[APIC] _APIC_IA32_GetMSRAPICBase: Called in UserMode\n"));
169 __asm__ __volatile__ ("int $0x80":"=a"(_apic_base):"a"(SC_RDMSR),"c"(MSR_LAPIC_BASE));
171 else
173 _apic_base = rdmsri(MSR_LAPIC_BASE);
176 _apic_base &= APIC_BASE_MASK;
178 D(bug("[APIC] _APIC_IA32_GetMSRAPICBase: MSR APIC Base @ %p\n", _apic_base));
179 return _apic_base;
182 UBYTE core_APIC_GetID(IPTR _APICBase)
184 UBYTE _apic_id;
186 /* The actual ID is in 8 most significant bits */
187 _apic_id = APIC_REG(_APICBase, APIC_ID) >> APIC_ID_SHIFT;
188 D(bug("[APIC] _APIC_IA32_GetID: APIC ID %d\n", _apic_id));
190 return _apic_id;
193 void core_APIC_AckIntr(void)
195 /* Write zero to EOI of current APIC */
196 IPTR apic_base = rdmsri(MSR_LAPIC_BASE) & APIC_BASE_MASK;
198 APIC_REG(apic_base, APIC_EOI) = 0;
201 ULONG core_APIC_Wake(APTR wake_apicstartrip, UBYTE wake_apicid, IPTR __APICBase)
203 ULONG status_ipisend, status_ipirecv;
204 ULONG start_count;
205 #ifdef CONFIG_LEGACY
206 ULONG apic_ver = APIC_REG(__APICBase, APIC_VERSION);
207 #endif
209 D(bug("[APIC] _APIC_IA32_wake(0x%02X @ %p)\n", wake_apicid, wake_apicstartrip));
210 D(bug("[APIC] _APIC_IA32_wake: APIC ID 0x%02X Base @ %p\n", core_APIC_GetID(__APICBase), __APICBase));
212 #ifdef CONFIG_LEGACY
214 * Check if we have old 82489DX discrete APIC (version & 0xF0 == 0).
215 * This APIC needs different startup procedure. It doesn't support STARTUP IPI
216 * because old CPUs didn't have INIT signal. They jump to BIOS ROM boot code
217 * immediately after INIT IPI. In order to run the bootstrap, a BIOS warm reset
218 * magic has to be used there.
220 if (!APIC_INTEGRATED(apic_ver))
223 * BIOS warm reset magic, part one.
224 * Write real-mode bootstrap routine address to 40:67 (real-mode address) location.
225 * This is standard feature of IBM PC AT BIOS. If a warm reset condition is detected,
226 * the BIOS jumps to the given address.
228 D(bug("[APIC] Setting BIOS vector for trampoline @ %p ..\n", wake_apicstartrip));
229 *((volatile unsigned short *)0x469) = (IPTR)wake_apicstartrip >> 4;
230 *((volatile unsigned short *)0x467) = 0; /* Actually wake_apicstartrip & 0x0F, it's page-aligned. */
233 * BIOS warm reset magic, part two.
234 * This writes 0x0A into CMOS RAM, location 0x0F. This signals a warm reset condition to BIOS,
235 * making part one work.
237 D(bug("[APIC] Setting warm reset code ..\n"));
238 outb(0xf, 0x70);
239 outb(0xa, 0x71);
241 #endif
243 /* Flush TLB (we are supervisor here) */
244 wrcr(cr3, rdcr(cr3));
246 /* First we send the INIT command (reset the core). Vector must be zero for this. */
247 status_ipisend = DoIPI(__APICBase, wake_apicid, ICR_INT_LEVELTRIG | ICR_INT_ASSERT | ICR_DM_INIT);
248 if (status_ipisend)
250 D(bug("[APIC] Error asserting INIT\n"));
251 return status_ipisend;
254 /* Deassert INIT after a small delay */
255 pit_udelay(10 * 1000);
257 /* Deassert INIT */
258 status_ipisend = DoIPI(__APICBase, wake_apicid, ICR_INT_LEVELTRIG | ICR_DM_INIT);
259 if (status_ipisend)
261 D(bug("[APIC] Error deasserting INIT\n"));
262 return status_ipisend;
265 /* memory barrier */
266 asm volatile("mfence":::"memory");
268 #ifdef CONFIG_LEGACY
269 /* If it's 82489DX, we are done. */
270 if (!APIC_INTEGRATED(apic_ver))
272 DWAKE(bug("[APIC] _APIC_IA32_wake: 82489DX detected, wakeup done\n"));
273 return 0;
275 #endif
278 * Perform IPI STARTUP loop.
279 * According to official Intel specification, this must be done twice.
280 * It's not explained why. ;-)
282 for (start_count = 1; start_count <= 2; start_count++)
284 D(bug("[APIC] _APIC_IA32_wake: Attempting STARTUP .. %d\n", start_count));
286 /* Clear any pending error condition */
287 APIC_REG(__APICBase, APIC_ESR) = 0;
290 * Send STARTUP IPI.
291 * The processor starts up at CS = (vector << 16) and IP = 0.
293 status_ipisend = DoIPI(__APICBase, wake_apicid, ICR_DM_STARTUP | ((IPTR)wake_apicstartrip >> 12));
295 /* Allow the target APIC to accept the IPI */
296 pit_udelay(200);
298 #ifdef CONFIG_LEGACY
299 /* Pentium erratum 3AP quirk */
300 if (APIC_LVT(apic_ver) > 3)
301 APIC_REG(__APICBase, APIC_ESR) = 0;
302 #endif
304 status_ipirecv = APIC_REG(__APICBase, APIC_ESR) & 0xEF;
307 * EXPERIMENTAL:
308 * On my machine (macmini 3,1, as OS X system profiler says), the core starts up from first
309 * attempt. The second attempt ends up in error (according to the documentation, the STARTUP
310 * can be accepted only once, while the core in RESET or INIT state, and first STARTUP, if
311 * succesful, brings the core out of this state).
312 * Here we try to detect this condition. If the core accepted STARTUP, we suggest that it has
313 * started up, and break the loop.
314 * A topic at osdev.org forum (http://forum.osdev.org/viewtopic.php?f=1&t=23018)
315 * also tells about some problems with double STARTUP. According to it, the second STARTUP can
316 * manage to re-run the core from the given address, leaving it in 64-bit mode, causing it to crash.
318 * If startup problems pops up (the core doesn't respond and AROS halts at "Launching APIC no X" stage),
319 * the following two variations of this algorithm can be tried:
320 * a) Always send STARTUP twice, but signal error condition only if both attempts failed.
321 * b) Send first STARTUP, abort on error. Allow second attempt to fail and ignore its result.
323 * Sonic <pavel_fedin@mail.ru>
325 if (!status_ipisend && !status_ipirecv)
326 break;
329 DWAKE(bug("[APIC] _APIC_IA32_wake: STARTUP run status 0x%08X, error 0x%08X\n", status_ipisend, status_ipirecv));
332 * We return nonzero on error.
333 * Actually least significant byte of this value holds ESR value, and 12th bit
334 * holds delivery status flag from DoIPI() routine. It will be '1' if we got
335 * stuck at sending phase.
337 return (status_ipisend | status_ipirecv);