The Pedigree Project 0.1
x86_common/Processor.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/ActivityDiagnostics.h"
21#include "pedigree/kernel/BootstrapInfo.h"
22#include "pedigree/kernel/panic.h"
23#include "pedigree/kernel/processor/Processor.h"
24#include "pedigree/kernel/processor/ProcessorInformation.h"
25#include "pedigree/kernel/processor/VirtualAddressSpace.h"
26#include "pedigree/kernel/processor/state.h"
27#include "pedigree/kernel/processor/x86_common/ProcessorInformation.h"
28#include "pedigree/kernel/utilities/Vector.h"
29
30#include "../x64/VirtualAddressSpace.h"
31#include "PhysicalMemoryManager.h"
32#include <machine/mach_pc/LocalApic.h>
33#include <machine/mach_pc/Pc.h>
34
36 // Don't allow the bootstrap info to be used anymore - we're killing it here
37 g_pBootstrapInfo = nullptr;
38
39 // Unmap the identity mapping of the first MBs
40 X64VirtualAddressSpace& KernelAddressSpace =
42 *reinterpret_cast<uint64_t*>(KernelAddressSpace.m_PhysicalPML4) = 0;
43 invalidate(0);
44
46}
47
49 return 4;
50}
51
52uintptr_t ProcessorBase::getDebugBreakpoint(size_t nBpNumber, DebugFlags::FaultType& nFaultType,
53 size_t& nLength, bool& bEnabled) {
54 uintptr_t nLinearAddress = 0;
55 switch (nBpNumber) {
56 case 0:
57 asm volatile("mov %%db0, %0" : "=r"(nLinearAddress));
58 break;
59 case 1:
60 asm volatile("mov %%db1, %0" : "=r"(nLinearAddress));
61 break;
62 case 2:
63 asm volatile("mov %%db2, %0" : "=r"(nLinearAddress));
64 break;
65 case 3:
66 asm volatile("mov %%db3, %0" : "=r"(nLinearAddress));
67 break;
68 }
69
70 uintptr_t nStatus;
71 asm volatile("mov %%db7, %0" : "=r"(nStatus));
72
73 bEnabled = static_cast<bool>(nStatus & (1 << (nBpNumber * 2 + 1))); // See intel manual 3b.
74 nFaultType = static_cast<DebugFlags::FaultType>((nStatus >> (nBpNumber * 4 + 16)) & 0x3);
75 switch ((nStatus >> (nBpNumber * 4 + 18)) & 0x3) {
76 case 0:
77 nLength = 1;
78 break;
79 case 1:
80 nLength = 2;
81 break;
82 case 2:
83 nLength = 8;
84 break;
85 case 3:
86 nLength = 4;
87 break;
88 }
89
90 return nLinearAddress;
91}
92
93void ProcessorBase::enableDebugBreakpoint(size_t nBpNumber, uintptr_t nLinearAddress,
94 DebugFlags::FaultType nFaultType, size_t nLength) {
95 switch (nBpNumber) {
96 case 0:
97 asm volatile("mov %0, %%db0" ::"r"(nLinearAddress));
98 break;
99 case 1:
100 asm volatile("mov %0, %%db1" ::"r"(nLinearAddress));
101 break;
102 case 2:
103 asm volatile("mov %0, %%db2" ::"r"(nLinearAddress));
104 break;
105 case 3:
106 asm volatile("mov %0, %%db3" ::"r"(nLinearAddress));
107 break;
108 }
109
110 uintptr_t nStatus;
111 asm volatile("mov %%db7, %0" : "=r"(nStatus));
112
113 size_t lengthField = 0;
114 switch (nLength) {
115 case 1:
116 lengthField = 0;
117 break;
118 case 2:
119 lengthField = 1;
120 break;
121 case 8:
122 lengthField = 2;
123 break;
124 case 4:
125 lengthField = 3;
126 break;
127 }
128
129 nStatus |= 1 << (nBpNumber * 2 + 1);
130 nStatus |= (nFaultType & 0x3) << (nBpNumber * 4 + 16);
131 nStatus |= (lengthField & 0x3) << (nBpNumber * 4 + 18);
132 asm volatile("mov %0, %%db7" ::"r"(nStatus));
133}
134
135void ProcessorBase::disableDebugBreakpoint(size_t nBpNumber) {
136 uintptr_t nStatus;
137 asm volatile("mov %%db7, %0" : "=r"(nStatus));
138
139 nStatus &= ~(1 << (nBpNumber * 2 + 1));
140 asm volatile("mov %0, %%db7" ::"r"(nStatus));
141}
142
143namespace {
144// Keep addressable kernel exports for modules built against the interrupt API.
145void (*const setInterruptsEntry)(bool) USED = &ProcessorBase::setInterrupts;
146bool (*const getInterruptsEntry)() USED = &ProcessorBase::getInterrupts;
147} // namespace
148
149void ProcessorBase::setSingleStep(bool bEnable, InterruptState& state) {
150 uintptr_t eflags = state.getFlags();
151 if (bEnable)
152 eflags |= 0x100;
153 else
154 eflags &= ~0x100;
155 state.setFlags(eflags);
156}
157
159 uint32_t eax, edx;
160 asm volatile("rdmsr" : "=a"(eax), "=d"(edx) : "c"(index));
161 return static_cast<uint64_t>(eax) | (static_cast<uint64_t>(edx) << 32);
162}
163
164void X86CommonProcessor::writeMachineSpecificRegister(uint32_t index, uint64_t value) {
165 uint32_t eax = value, edx = value >> 32;
166 asm volatile("wrmsr" ::"a"(eax), "d"(edx), "c"(index));
167}
168
169void ProcessorBase::invalidate(void* pAddress) {
170 asm volatile("invlpg (%0)" ::"a"(pAddress));
171}
172
174 const ExecutionContext context = executionContext();
175 if (guard.m_Active ||
176 (context != ExecutionContext::WaitableThread && context != ExecutionContext::AtomicThread)) {
177 return TlbInvalidationResult::InvalidContext;
178 }
179
180#if MULTIPROCESSOR
181 if (m_Initialised == 2 && getCount() > 1) {
182 Pc& pc = Pc::instance();
183 if (!pc.localApicAvailable()) {
184 return TlbInvalidationResult::UnsupportedTopology;
185 }
186
187 bool global = false;
188 const TlbInvalidationResult result = pc.getLocalApic().beginTlbInvalidation(global);
189 if (result != TlbInvalidationResult::Success) {
190 return result;
191 }
192 guard.m_Global = global;
193 }
194#endif
195
196 guard.m_Active = true;
197 return TlbInvalidationResult::Success;
198}
199
201#if MULTIPROCESSOR
202 if (guard.m_Global) {
203 Pc::instance().getLocalApic().endTlbInvalidation();
204 }
205#endif
206 guard.m_Global = false;
207 guard.m_Active = false;
208}
209
211 TlbInvalidationResult result) {
212 if (!guard.m_Active) {
213 return false;
214 }
215
216#if MULTIPROCESSOR
217 if (guard.m_Global) {
218 return Pc::instance().getLocalApic().closeTlbInvalidationAdmissionForTerminalFailure(result);
219 }
220#endif
221
222 return true;
223}
224
226#if MULTIPROCESSOR
227 if (m_Initialised == 2 && getCount() > 1) {
228 Pc& pc = Pc::instance();
229 return pc.localApicAvailable() && pc.getLocalApic().tlbInvalidationFailureActive();
230 }
231#endif
232 return false;
233}
234
236#if MULTIPROCESSOR
237 if (m_Initialised == 2 && getCount() > 1) {
238 Pc& pc = Pc::instance();
239 return pc.localApicAvailable() && pc.getLocalApic().tlbInvalidationTerminal();
240 }
241#endif
242 return false;
243}
244
247 const TlbInvalidationResult result = beginTlbInvalidation(guard);
248 if (result != TlbInvalidationResult::Success) {
249 if (result == TlbInvalidationResult::SerialisationTimedOut && tlbInvalidationFailureActive()) {
251 while (true) {
253 }
254 }
255 return result;
256 }
257 const TlbInvalidationResult invalidation = invalidateAll(pAddress, guard);
258 if (invalidation == TlbInvalidationResult::Success) {
259 return invalidation;
260 }
261
262 // The Local APIC retains a failed generation for terminal adoption. Even
263 // this non-mutating convenience call must complete that handoff rather than
264 // returning with an orphaned shootdown owner.
265 const bool coordinator = guard.closeAdmissionForTerminalFailure(invalidation);
266 guard.retire();
268 if (!coordinator) {
269 while (true) {
271 }
272 }
273
274 switch (invalidation) {
275 case TlbInvalidationResult::InvalidContext:
276 panic("TLB invalidation failed from an invalid context");
277 case TlbInvalidationResult::UnsupportedTopology:
278 panic("TLB invalidation has no safe all-processor route");
279 case TlbInvalidationResult::SerialisationTimedOut:
280 panic("TLB invalidation serialisation timed out");
281 case TlbInvalidationResult::SubmissionFailed:
282 panic("TLB invalidation IPI submission failed");
283 case TlbInvalidationResult::AcknowledgementTimedOut:
284 panic("TLB invalidation acknowledgement timed out");
285 case TlbInvalidationResult::DrainTimedOut:
286 panic("TLB invalidation service drain timed out");
287 case TlbInvalidationResult::Success:
288 break;
289 }
290 panic("TLB invalidation returned an unknown result");
291}
292
294 return invalidateProcessors(pAddress, guard, ~uint64_t(0));
295}
296
299 uint64_t processors) {
300 if (!guard.m_Active) {
301 return TlbInvalidationResult::InvalidContext;
302 }
303
304#if MULTIPROCESSOR
305 if (guard.m_Global) {
306 return Pc::instance().getLocalApic().invalidateAllProcessors(pAddress, processors);
307 }
308#endif
309
310 invalidate(pAddress);
311 return TlbInvalidationResult::Success;
312}
313
314void X86CommonProcessor::cpuid(uint32_t inEax, uint32_t inEcx, uint32_t& eax, uint32_t& ebx,
315 uint32_t& ecx, uint32_t& edx) {
316 asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(inEax), "c"(inEcx));
317}
318
319#if MULTIPROCESSOR && !X64
320NEVER_INLINE __attribute__((cold)) ProcessorInformation* currentProcessorInformationFromApic(
321 const Vector<ProcessorInformation*>& processors, size_t* processorIndex) {
322 Pc& pc = Pc::instance();
323 if (!pc.localApicAvailable()) {
324 if (processorIndex)
325 *processorIndex = 0;
326 return nullptr;
327 }
328
329 const uint8_t apicId = pc.getLocalApic().getId();
330 for (size_t i = 0; i < processors.count(); ++i) {
331 ProcessorInformation* information = processors.begin()[i];
332 if (information->localApicId() == apicId) {
333 if (processorIndex)
334 *processorIndex = i;
335 return information;
336 }
337 }
338
339 // IRQ publication must reject an unknown identity rather than use the BSP.
340 if (processorIndex)
341 *processorIndex = processors.count();
342 return nullptr;
343}
344#endif
345
347#if X64
348 return information().processorId();
349#else
350 if (m_Initialised < 2)
351 return 0;
352
353#if MULTIPROCESSOR
354 if (m_ProcessorInformation.count() == 1)
355 return (*m_ProcessorInformation.begin())->m_ProcessorId;
356
357 if (auto* information = currentProcessorInformationFromApic(m_ProcessorInformation))
358 return information->m_ProcessorId;
359#endif
360
361 return 0;
362#endif
363}
364
365#if !X64
366size_t ProcessorBase::index() {
367 if (m_Initialised < 2)
368 return 0;
369
370#if MULTIPROCESSOR
371 if (m_ProcessorInformation.count() == 1)
372 return 0;
373
374 size_t index;
375 currentProcessorInformationFromApic(m_ProcessorInformation, &index);
376 return index;
377#else
378 return 0;
379#endif
380}
381#endif
382
384#if MULTIPROCESSOR
385 const size_t count = m_ProcessorInformation.count();
386 return count ? count : 1;
387#else
388 return 1;
389#endif
390}
391
393 asm volatile("int $3");
394}
395
396void ProcessorBase::halt() {
397 asm volatile("hlt");
398}
399
401#if MULTIPROCESSOR
402 // A processor can spin on an IRQ-disabling lock held by a shootdown
403 // initiator. Cooperating here prevents that lock dependency from delaying
404 // the initiator's bounded acknowledgement barrier indefinitely.
405 if (m_Initialised == 2) {
406 Pc& pc = Pc::instance();
407 if (pc.localApicAvailable()) {
408 pc.getLocalApic().servicePendingTlbShootdown();
409 pc.getLocalApic().servicePendingTerminalProcessorControl();
410 }
411 }
412#endif
413 asm volatile("pause");
414}
415
417 // The IDTR operand is ten bytes on x64, not sizeof(size_t).
418 struct {
419 uint16_t limit;
420 uintptr_t base;
421 } PACKED emptyIdt = {0, 0};
422 asm volatile("cli; lidt %0; int $3" : : "m"(emptyIdt) : "memory");
423}
424
426#if PEDIGREE_ACTIVITY_DIAGNOSTICS
427 ActivityDiagnostics::recordIdleHalt();
428#endif
429 bool bWasInterrupts = getInterrupts();
430#if PEDIGREE_LATENCY_ACCOUNTING
431 asm volatile("cli" : : : "memory", "cc");
432 pedigree_irq_time_state(true);
433#endif
434 __asm__ __volatile__("sti; hlt");
435 if (!bWasInterrupts)
436 setInterrupts(false);
437}
438
439void ProcessorBase::invalidateICache(uintptr_t nAddr) {
440 __asm__ __volatile__("clflush (%0)" ::"a"(nAddr));
441}
442
443void ProcessorBase::invalidateDCache(uintptr_t nAddr) {
444 __asm__ __volatile__("clflush (%0)" ::"a"(nAddr));
445}
446
447void ProcessorBase::flushDCache(uintptr_t nAddr) {
448 __asm__ __volatile__("clflush (%0)" ::"a"(nAddr));
449}
450
451void ProcessorBase::flushDCacheAndInvalidateICache(uintptr_t startAddr, uintptr_t endAddr) {
452 const size_t length = endAddr > startAddr ? endAddr - startAddr : 0;
453 for (size_t i = 0; i < length; ++i) {
454 __asm__ __volatile__("clflush (%0)" ::"a"(startAddr + i));
455 }
456}
Definition Pc.h:48
bool localApicAvailable() const
Definition Pc.h:78
static void halt()
static MUST_USE_RESULT TlbInvalidationResult invalidateProcessors(void *pAddress, TlbInvalidationGuard &guard, uint64_t processors)
static void flushDCache(uintptr_t nAddr)
static void invalidateICache(uintptr_t nAddr)
static bool getInterrupts()
static ProcessorId id()
static void reset()
static MUST_USE_RESULT TlbInvalidationResult invalidateAll(void *pAddress)
static void initialisationDone()
static ProcessorInformation & information()
static bool tlbInvalidationTerminal()
static size_t getCount()
static void pause()
static uintptr_t getDebugBreakpoint(size_t nBpNumber, DebugFlags::FaultType &nFaultType, size_t &nLength, bool &bEnabled)
static bool tlbInvalidationFailureActive()
static void breakpoint()
static size_t m_Initialised
Definition Processor.h:483
static void disableDebugBreakpoint(size_t nBpNumber)
static Vector< ProcessorInformation * > m_ProcessorInformation
Definition Processor.h:512
static ExecutionContext executionContext()
Definition Processor.cc:109
static void flushDCacheAndInvalidateICache(uintptr_t startAddr, uintptr_t endAddr)
static void setSingleStep(bool bEnable, InterruptState &state)
static void endTlbInvalidation(TlbInvalidationGuard &guard)
static void invalidate(void *pAddress)
static void invalidateDCache(uintptr_t nAddr)
static void enableDebugBreakpoint(size_t nBpNumber, uintptr_t nLinearAddress, DebugFlags::FaultType nFaultType, size_t nLength)
static void setInterrupts(bool bEnable)
static size_t index()
static MUST_USE_RESULT TlbInvalidationResult beginTlbInvalidation(TlbInvalidationGuard &guard)
static size_t getDebugBreakpointCount()
static MUST_USE_RESULT bool closeTlbInvalidationAdmissionForTerminalFailure(TlbInvalidationGuard &guard, TlbInvalidationResult result)
static void haltUntilInterrupt()
A vector / dynamic array.
Definition Vector.h:33
Iterator begin()
Definition Vector.h:162
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
static void cpuid(uint32_t inEax, uint32_t inEcx, uint32_t &eax, uint32_t &ebx, uint32_t &ecx, uint32_t &edx)
static void writeMachineSpecificRegister(uint32_t index, uint64_t value)
static uint64_t readMachineSpecificRegister(uint32_t index)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
size_t ProcessorId
MUST_USE_RESULT bool closeAdmissionForTerminalFailure(TlbInvalidationResult result)
Definition Processor.h:551
TlbInvalidationResult
Definition Processor.h:56
size_t count() const
Definition Vector.h:270