The Pedigree Project 0.1
x86_common/Multiprocessor.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 <config.h>
21
22#if MULTIPROCESSOR
23
24#include "pedigree/kernel/Log.h"
25#include "pedigree/kernel/Spinlock.h"
26#include "pedigree/kernel/panic.h"
27#include "pedigree/kernel/processor/Processor.h"
28#include "pedigree/kernel/processor/ProcessorInformation.h"
29#include "pedigree/kernel/processor/VirtualAddressSpace.h"
30#include "pedigree/kernel/processor/types.h"
31#include "pedigree/kernel/utilities/Vector.h"
32#include "pedigree/kernel/utilities/utility.h"
33
34#include "../x64/VirtualAddressSpace.h"
35#include "Multiprocessor.h"
36#include <machine/mach_pc/Acpi.h>
37#include <machine/mach_pc/LocalApic.h>
38#include <machine/mach_pc/Pc.h>
39#include <machine/mach_pc/Rtc.h>
40
41Atomic<bool> Multiprocessor::m_ProcessorStarted(false);
42// Don't track this lock - it is for startup synchronisation, not for protecting
43// a specific resource.
44Spinlock Multiprocessor::m_ProcessorLock2(true, true);
45
46namespace {
47constexpr uint64_t InitToStartupDelayMicroseconds = 10000;
48constexpr uint64_t StartupIpiDelayMicroseconds = 200;
49constexpr uint64_t NanosecondsPerMicrosecond = 1000;
50constexpr uint64_t ApplicationProcessorTimeoutNanoseconds = 1000000000;
51constexpr size_t EarlyBootPollLimit = 10000000;
52constexpr size_t ApplicationProcessorPollLimit = 100000000;
53
54bool earlyBootDelay(uint64_t microseconds) {
55 // Time::delay needs the scheduler. Pc::initialise has already calibrated
56 // this TSC-backed clock before Processor::initialise2 reaches us.
57 if (microseconds > (~static_cast<uint64_t>(0) / NanosecondsPerMicrosecond)) {
58 return false;
59 }
60 const uint64_t duration = microseconds * NanosecondsPerMicrosecond;
61 const uint64_t start = Rtc::instance().getTickCountNano();
62 for (size_t poll = 0; poll < EarlyBootPollLimit; ++poll) {
63 if ((Rtc::instance().getTickCountNano() - start) >= duration)
64 return true;
66 }
67 return (Rtc::instance().getTickCountNano() - start) >= duration;
68}
69
70} // namespace
71
72extern "C" void mp_trampoline16(void);
73extern "C" void mp_trampoline32(void);
74extern "C" void* trampolinegdt;
75extern "C" void* trampolinegdtr;
76extern "C" void* trampolinegdt64;
77extern "C" void* trampolinegdtr64;
78
80 // Keep the BSP's early slot stable even if firmware lists an AP first.
82
83 if (!Pc::instance().localApicAvailable()) {
84 NOTICE(
85 "Multiprocessor: local APIC unavailable; keeping the bootstrap "
86 "processor only");
87 return 1;
88 }
89
90 const Vector<ProcessorInformation*>* Processors = nullptr;
91#if ACPI
92 Acpi& acpi = Acpi::instance();
93 if (acpi.validProcessorInfo()) {
94 Processors = &acpi.getProcessorList();
95 }
96#endif
97
98 if (!Processors) {
99 NOTICE(
100 "Multiprocessor: couldn't find any information about multiple "
101 "processors");
102 return 1;
103 }
104
105 NOTICE("Multiprocessor: Found " << Dec << Processors->count() << Hex << " processors");
106
107 // Copy the trampoline code to 0x7000
112 MemoryCopy(reinterpret_cast<void*>(0x7000), reinterpret_cast<void*>(&mp_trampoline16), 0x100);
113 MemoryCopy(reinterpret_cast<void*>(0x7100), reinterpret_cast<void*>(&mp_trampoline32), 0x100);
114 // The first far jump enters 32-bit protected mode; the trampoline loads
115 // the 64-bit GDT itself only after enabling long mode.
116 MemoryCopy(reinterpret_cast<void*>(0x7200), &trampolinegdtr, 0x10);
117 MemoryCopy(reinterpret_cast<void*>(0x7210), &trampolinegdt, 0xF0);
118
119 volatile uintptr_t* trampolineStack;
120 volatile uintptr_t* trampolineKernelEntry;
121 volatile uintptr_t* trampolineKernelGsAnchor = reinterpret_cast<volatile uintptr_t*>(0x7FE0);
122
123 // Shared page-table cache flags must have the same meaning on every CPU.
124 *reinterpret_cast<volatile uint64_t*>(0x7FD8) = Processor::readMachineSpecificRegister(0x277);
125
126 // Parameters for the trampoline code
127 trampolineStack = reinterpret_cast<volatile uintptr_t*>(0x7FF0);
128 trampolineKernelEntry = reinterpret_cast<volatile uintptr_t*>(0x7FE8);
129
130 // The AP trampoline ABI reserves 0x7FF8 for the boot PML4 address.
131 // NOLINTNEXTLINE(clang-analyzer-core.FixedAddressDereference)
132 *reinterpret_cast<volatile uintptr_t*>(0x7FF8) =
134 .m_PhysicalPML4;
135
136 // Set the entry point
137 *trampolineKernelEntry = reinterpret_cast<uintptr_t>(&applicationProcessorStartup);
138
139 LocalApic& localApic = Pc::instance().getLocalApic();
141 Processor::m_SafeBspProcessorInformation.setIds(0, localApic.getId());
142 for (size_t i = 0; i < Processors->count(); ++i) {
143 if ((*Processors)[i]->apicId == localApic.getId()) {
144 Processor::m_SafeBspProcessorInformation.setIds((*Processors)[i]->processorId,
145 (*Processors)[i]->apicId);
146 break;
147 }
148 }
149
150 // Startup the application processors through startup interprocessor
151 // interrupt
152 for (size_t i = 0; i < Processors->count(); i++) {
153 // Add a ProcessorInformation object
154 ::ProcessorInformation* pProcessorInfo = 0;
155
156 // Startup the processor
157 if (localApic.getId() != (*Processors)[i]->apicId) {
158 // AP: set up a proper information structure
159 pProcessorInfo =
160 new ::ProcessorInformation((*Processors)[i]->processorId, (*Processors)[i]->apicId);
161 pProcessorInfo->kernelGsAnchor()->processorIndex = Processor::m_ProcessorInformation.count();
163
164 // Allocate kernel stack
165 VirtualAddressSpace::Stack* pStack = kernelSpace.allocateStack();
166
167 // Set trampoline stack
168 *trampolineStack = reinterpret_cast<uintptr_t>(pStack->getTop());
169 *trampolineKernelGsAnchor = reinterpret_cast<uintptr_t>(pProcessorInfo->kernelGsAnchor());
170
171 NOTICE(" Booting processor #" << Dec << (*Processors)[i]->processorId << ", stack at 0x"
172 << Hex << reinterpret_cast<uintptr_t>(pStack->getTop()));
173
174 m_ProcessorStarted = false;
175
176 // INIT ignores the vector field and requires an asserted and a
177 // deasserted level write. The trampoline is at 0x7000, so the
178 // STARTUP vector is page 7.
179 if (!localApic.interProcessorInterrupt((*Processors)[i]->apicId, 0,
180 LocalApic::deliveryModeInit, true, true)) {
181 ERROR("Multiprocessor: INIT assert failed for processor #"
182 << Dec << (*Processors)[i]->processorId << " (APIC " << (*Processors)[i]->apicId
183 << ")");
184 panic("Multiprocessor: INIT assert did not complete");
185 }
186 if (!localApic.interProcessorInterrupt((*Processors)[i]->apicId, 0,
187 LocalApic::deliveryModeInit, false, true)) {
188 ERROR("Multiprocessor: INIT deassert failed for processor #"
189 << Dec << (*Processors)[i]->processorId << " (APIC " << (*Processors)[i]->apicId
190 << ")");
191 panic("Multiprocessor: INIT deassert did not complete");
192 }
193 if (!earlyBootDelay(InitToStartupDelayMicroseconds))
194 panic("Multiprocessor: early-boot INIT delay timed out");
195
196 if (!localApic.interProcessorInterrupt((*Processors)[i]->apicId, 0x07,
197 LocalApic::deliveryModeStartup, true, false)) {
198 ERROR(
199 "Multiprocessor: first STARTUP IPI failed for "
200 "processor #"
201 << Dec << (*Processors)[i]->processorId << " (APIC " << (*Processors)[i]->apicId
202 << ")");
203 panic("Multiprocessor: first STARTUP IPI did not complete");
204 }
205 if (!earlyBootDelay(StartupIpiDelayMicroseconds))
206 panic("Multiprocessor: early-boot STARTUP delay timed out");
207
208 if (!m_ProcessorStarted) {
209 if (!localApic.interProcessorInterrupt((*Processors)[i]->apicId, 0x07,
210 LocalApic::deliveryModeStartup, true, false)) {
211 ERROR(
212 "Multiprocessor: second STARTUP IPI failed for "
213 "processor #"
214 << Dec << (*Processors)[i]->processorId << " (APIC " << (*Processors)[i]->apicId
215 << ")");
216 panic("Multiprocessor: second STARTUP IPI did not complete");
217 }
218 if (!earlyBootDelay(StartupIpiDelayMicroseconds))
219 panic("Multiprocessor: second STARTUP delay timed out");
220 }
221
222 const uint64_t startupWaitStart = Rtc::instance().getTickCountNano();
223 bool processorStarted = false;
224 for (size_t poll = 0; poll < ApplicationProcessorPollLimit; ++poll) {
225 if (m_ProcessorStarted) {
226 processorStarted = true;
227 break;
228 }
229 if ((Rtc::instance().getTickCountNano() - startupWaitStart) >=
230 ApplicationProcessorTimeoutNanoseconds)
231 break;
233 }
234 if (!processorStarted && !m_ProcessorStarted) {
235 ERROR("Multiprocessor: processor #" << Dec << (*Processors)[i]->processorId << " (APIC "
236 << (*Processors)[i]->apicId
237 << ") did not acknowledge startup");
238 panic("Multiprocessor: application processor startup timed out");
239 }
240 } else {
241 NOTICE("Currently running on CPU #" << Dec << localApic.getId() << Hex
242 << ", skipping boot (not necessary)");
243 }
244 }
245
247}
248
250 m_ProcessorLock2.release();
251}
252
253#endif
static size_t initialise1() INITIALISATION_ONLY
static void initialise2() INITIALISATION_ONLY
static void pause()
static Vector< ProcessorInformation * > m_ProcessorInformation
Definition Processor.h:512
static ProcessorInformation m_SafeBspProcessorInformation
Definition Processor.h:516
virtual uint64_t getTickCountNano()
Definition Rtc.cc:416
A vector / dynamic array.
Definition Vector.h:33
virtual Stack * allocateStack()=0
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
static uint64_t readMachineSpecificRegister(uint32_t index)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
void pushBack(const T &value)
Definition Vector.h:275
size_t count() const
Definition Vector.h:270