The Pedigree Project 0.1
Pc.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 "Pc.h"
21#if ACPI
22#include "Acpi.h"
23#endif
24#if MULTIPROCESSOR
25#include "Apic.h"
26#endif
27#include "pedigree/kernel/Log.h"
28#include "pedigree/kernel/machine/Bus.h"
29#include "pedigree/kernel/machine/Controller.h"
30#include "pedigree/kernel/machine/Device.h"
31#include "pedigree/kernel/machine/Pci.h"
32#include "pedigree/kernel/panic.h"
33#include "pedigree/kernel/processor/Processor.h"
34#include "pedigree/kernel/utilities/String.h"
35#include "pedigree/kernel/utilities/Vector.h"
36#include "pedigree/kernel/utilities/new"
37
38#include "Pic.h"
39#include "Pit.h"
40#include "Rtc.h"
41
42class IrqManager;
43class Keyboard;
44class SchedulerTimer;
45class Serial;
46class Timer;
47class Vga;
48
49Pc Pc::m_Instance;
50
51#if X64
52namespace {
53constexpr uint32_t Ia32TscAux = 0xC0000103;
54constexpr uint32_t RdtscpFeature = 1u << 27;
55
56bool supportsRdtscp() {
57 uint32_t maxLeaf, unusedEax, unusedEbx, unusedEcx, unusedEdx;
58 Processor::cpuid(0x80000000, 0, maxLeaf, unusedEbx, unusedEcx, unusedEdx);
59 if (maxLeaf < 0x80000001) {
60 return false;
61 }
62
63 Processor::cpuid(0x80000001, 0, unusedEax, unusedEbx, unusedEcx, unusedEdx);
64 return (unusedEdx & RdtscpFeature) != 0;
65}
66
67void initialiseVdsoCpuId() {
68 if (!supportsRdtscp()) {
69 return;
70 }
71
72 const size_t processor = Processor::index();
73 if (processor >= Processor::getCount()) {
74 return;
75 }
76 Processor::writeMachineSpecificRegister(Ia32TscAux, static_cast<uint32_t>(processor));
77}
78} // namespace
79#endif
80
81#if MULTIPROCESSOR
82namespace {
83bool reportProcessorControlResult(LocalApic::ProcessorControlResult result, const char* operation) {
84 if (result != LocalApic::ProcessorControlResult::Success &&
86 // The terminal TLB coordinator may hold an outer allocator lock. Its
87 // retry loop must not format or publish diagnostics before peers stop.
88 return false;
89 }
90
91 switch (result) {
92 case LocalApic::ProcessorControlResult::Success:
93 return true;
94 case LocalApic::ProcessorControlResult::InvalidState:
95 ERROR_NOLOCK("Pc: " << operation << " failed: invalid processor state");
96 break;
97 case LocalApic::ProcessorControlResult::SubmissionFailed:
98 ERROR_NOLOCK("Pc: " << operation << " IPI transaction failed");
99 break;
100 case LocalApic::ProcessorControlResult::AcknowledgementTimedOut:
101 ERROR_NOLOCK("Pc: " << operation << " acknowledgement timed out");
102 break;
103 case LocalApic::ProcessorControlResult::DrainTimedOut:
104 ERROR_NOLOCK("Pc: " << operation << " processor-control drain timed out");
105 break;
106 }
107 return false;
108}
109} // namespace
110#endif
111
112bool Pc::supportsPowerOff() const {
113#if ACPI
114 return Acpi::instance().supportsPowerOff();
115#else
116 return false;
117#endif
118}
119
120bool Pc::prepareShutdown(ShutdownType type) {
121#if ACPI
122 return Acpi::instance().prepareShutdown(type == ShutdownType::PowerOff);
123#else
124 return true;
125#endif
126}
127
128const char* Pc::finalShutdown(ShutdownType type) {
129 if (type == ShutdownType::PowerOff) {
130 NOTICE_NOLOCK("Powering off...");
131#if ACPI
132 return Acpi::instance().powerOff();
133#else
134 return "ACPI power-off support is disabled.";
135#endif
136 } else if (type == ShutdownType::Restart) {
137 NOTICE_NOLOCK("Rebooting...");
138#if ACPI
139 Acpi::instance().reset();
140#endif
141 // The controller is already quiesced, so terminal reset can access it
142 // without acquiring the keyboard driver's retired locks.
143 for (size_t i = 0; i < 100000; ++i) {
144 uint8_t status;
145 asm volatile("inb %1, %0" : "=a"(status) : "Nd"(uint16_t(0x64)));
146 if (!(status & 2)) {
147 asm volatile("outb %0, %1" : : "a"(uint8_t(0xfe)), "Nd"(uint16_t(0x64)));
148 break;
149 }
150 }
151 for (size_t i = 0; i < 100000; ++i)
152 asm volatile("pause");
154 return "The firmware reset request returned without restarting the computer.";
155 } else {
156 NOTICE_NOLOCK("System halted");
157 }
158
159 return nullptr;
160}
161
163 // Initialise Vga
164 if (m_Vga->initialise() == false)
165 panic("Pc: Vga initialisation failed");
166
167 uint8_t rtcCenturyIndex = 0;
168#if ACPI
169 Acpi& acpi = Acpi::instance();
170 acpi.initialise();
171 rtcCenturyIndex = acpi.getRtcCenturyIndex();
172#endif
173
174 Rtc& rtc = Rtc::instance();
175 if (rtc.initialise1(rtcCenturyIndex) == false)
176 panic("Pc: Rtc initialisation phase 1 failed");
177 if (rtc.initialise2() == false)
178 panic("Pc: Rtc initialisation phase 2 failed");
179
180 bool localApicInitialised = false;
181#if MULTIPROCESSOR && ACPI
182 const uint64_t localApicAddress = acpi.getLocalApicAddress();
183 if (acpi.validApicInfo() && localApicAddress && m_LocalApic->initialise(localApicAddress)) {
184 localApicInitialised = true;
185 NOTICE("Local APIC initialised");
186 }
187#endif
188
189 m_SchedulerTimerSelection.recordLocalApicInitialisation(localApicInitialised);
190
191 NOTICE("Falling back to dual 8259 PIC Mode");
192 Pic& pic = Pic::instance();
193 if (pic.initialise() == false) {
194 panic("Pc: Pic initialisation failed");
195 }
196
197 // Initialise serial ports.
198 m_pSerial[0]->setBase(0x3F8);
199 m_pSerial[1]->setBase(0x2F8);
200 m_pSerial[2]->setBase(0x3E8);
201 m_pSerial[3]->setBase(0x2E8);
202
203 if (m_SchedulerTimerSelection.usesPit()) {
204 NOTICE("Using PIT scheduler timer fallback");
205 Pit& pit = Pit::instance();
206 if (pit.initialise() == false)
207 panic("Pc: Pit initialisation failed");
208 }
209
210 // Set up PS/2
211 m_Ps2Controller->initialise();
212 m_Keyboard->initialise();
213
214#if X64
215 initialiseVdsoCpuId();
216#endif
217 m_bInitialised = true;
218}
219
221 if (!m_bInitialised) {
222 return;
223 }
224
225 setShutdownPhase(ShutdownPhase::Devices, "RTC: stopping callbacks");
226 Rtc::instance().uninitialise();
227 setShutdownPhase(ShutdownPhase::Devices, "PS/2: stopping controller");
228 m_Ps2Controller->uninitialise();
229 setShutdownPhase(ShutdownPhase::Devices, "PS/2: joining keyboard reader");
230 m_Keyboard->stopReaderThread();
231 if (m_SchedulerTimerSelection.usesPit()) {
232 setShutdownPhase(ShutdownPhase::Devices, "PIT: stopping callbacks");
234 }
235 setShutdownPhase(ShutdownPhase::Devices, "IRQ: joining interrupt workers");
236 if (!Pic::instance().shutdownThreaded()) {
237 panic("Shutdown aborted: threaded IRQ workers did not stop");
238 }
239 m_bInitialised = false;
240}
241
242#if MULTIPROCESSOR
243void Pc::initialiseProcessor() {
244 // TODO: we might need to initialise per-processor ACPI shit, no idea atm
245
246 // Initialise the local APIC
247 if (m_LocalApic->initialiseProcessor() == false)
248 panic("Pc::initialiseProcessor(): Failed to initialise the local APIC");
249
250 // AP startup calls this before creating its scheduler and enabling
251 // interrupts, so no migratable work can observe an unanchored local TSC.
252 Rtc::instance().initialiseProcessorClock();
253 initialiseVdsoCpuId();
254}
255#endif
256
258 if (!Pic::instance().initialiseThreaded()) {
259 panic("Pc: threaded IRQ worker initialisation failed");
260 }
261 if (!Rtc::instance().initialise3()) {
262 panic("Pc: RTC bottom-half initialisation failed");
263 }
264 if (!m_Ps2Controller->initialise3()) {
265 panic("Pc: PS/2 bottom-half initialisation failed");
266 }
267 m_Keyboard->startReaderThread();
268}
269
271 for (size_t i = 0; i < 4; ++i) {
272 m_pSerial[i] = new X86Serial();
273 }
274
275 m_Vga = new X86Vga();
276
277 m_IsaBus = new Bus("ISA");
278 m_AtaMaster = new Controller();
279 m_AtaSlave = new Controller();
280 m_Watchdog = new Device();
281
282 m_Ps2Controller = new Ps2Controller();
283 m_pKeyboard = m_Keyboard = new X86Keyboard(m_Ps2Controller);
284
285#if MULTIPROCESSOR
286 m_LocalApic = new LocalApic();
287#endif
288
289 // Firstly add the ISA bus.
290 m_IsaBus->setSpecificType(String("isa"));
291
292 // ATA controllers.
293 m_AtaMaster->setSpecificType(String("ata"));
294 m_AtaMaster->addresses().pushBack(new Device::Address(String("command"), 0x1F0, 8, true));
295 m_AtaMaster->addresses().pushBack(new Device::Address(String("control"), 0x3F0, 8, true));
296 m_AtaMaster->setInterruptNumber(14);
297 m_IsaBus->addChild(m_AtaMaster);
298 m_AtaMaster->setParent(m_IsaBus);
299
300 m_AtaSlave->setSpecificType(String("ata"));
301 m_AtaSlave->addresses().pushBack(new Device::Address(String("command"), 0x170, 8, true));
302 m_AtaSlave->addresses().pushBack(new Device::Address(String("control"), 0x370, 8, true));
303 m_AtaSlave->setInterruptNumber(15);
304 m_IsaBus->addChild(m_AtaSlave);
305 m_AtaSlave->setParent(m_IsaBus);
306
307 // PS/2
308 m_Ps2Controller->setSpecificType(String("ps2"));
309 m_Ps2Controller->addresses().pushBack(new Device::Address(String("ps2-base"), 0x60, 5, true));
310 m_Ps2Controller->setInterruptNumber(1); // 12 for mouse, handled by the driver
311 m_IsaBus->addChild(m_Ps2Controller);
312 m_Ps2Controller->setParent(m_IsaBus);
313
314 // IB700 Watchdog Timer
315 m_Watchdog->addresses().pushBack(new Device::Address(String("ib700-base"), 0x441, 4, true));
316 m_IsaBus->addChild(m_Watchdog);
317 m_Watchdog->setParent(m_IsaBus);
318
319 Device::addToRoot(m_IsaBus);
320
321 // Initialise the PCI interface
322 PciBus::instance().initialise();
323}
324
326 return m_pSerial[n];
327}
328
330 return 4;
331}
332
333Vga* Pc::getVga(size_t n) {
334 return m_Vga;
335}
336
338 return 1;
339}
340
341IrqManager* Pc::getIrqManager() {
342 return &Pic::instance();
343}
344
346#if MULTIPROCESSOR
347 if (m_SchedulerTimerSelection.usesLocalApic()) {
348 return m_LocalApic;
349 }
350#endif
351 return &Pit::instance();
352}
353
355 return &Rtc::instance();
356}
357
359 return m_pKeyboard;
360}
361
363 if (m_pKeyboard) {
364 delete m_pKeyboard;
365 }
366
367 m_pKeyboard = kb;
368}
369
370#if MULTIPROCESSOR
372 const size_t processorCount = Processor::getCount();
373 return reportProcessorControlResult(
374 m_LocalApic->quiesceAllOtherProcessors(processorCount > 1 ? processorCount - 1 : 0),
375 "processor quiesce");
376}
377
379 if (Processor::getCount() <= 1)
380 return true;
381 return reportProcessorControlResult(m_LocalApic->resumeAllOtherProcessors(), "processor resume");
382}
383
385 const size_t processorCount = Processor::getCount();
386 return reportProcessorControlResult(
387 m_LocalApic->haltAllOtherProcessors(processorCount > 1 ? processorCount - 1 : 0),
388 "processor halt");
389}
390#endif
391
393 : m_pSerial(),
394 m_Vga(nullptr),
395 m_pKeyboard(nullptr),
396#if MULTIPROCESSOR
397 m_LocalApic(nullptr),
398#endif
399 m_SchedulerTimerSelection(),
400 m_Keyboard(nullptr),
401 m_IsaBus(nullptr),
402 m_AtaMaster(nullptr),
403 m_AtaSlave(nullptr),
404 m_Ps2Controller(nullptr),
405 m_Watchdog(nullptr) {
406 for (size_t i = 0; i < 4; ++i) {
407 m_pSerial[i] = nullptr;
408 }
409}
410
Definition Bus.h:31
static void addToRoot(Device *device)
Definition Device.cc:114
virtual Vector< Address * > & addresses()
Definition Device.h:266
virtual void setInterruptNumber(uintptr_t n)
Definition Device.h:276
void addChild(Device *pDevice)
Definition Device.cc:135
virtual void setSpecificType(String str)
Definition Device.h:187
void setParent(Device *p)
Definition Device.h:170
virtual MUST_USE_RESULT bool quiesceAllOtherProcessors()
Definition Machine.cc:149
static void setShutdownPhase(ShutdownPhase phase, const char *detail=nullptr)
Definition Machine.cc:33
virtual MUST_USE_RESULT bool resumeAllOtherProcessors()
Definition Machine.cc:156
virtual MUST_USE_RESULT bool stopAllOtherProcessors()
Definition Machine.cc:163
Definition Pc.h:48
virtual void initialise3()
Called after processor startup - for thread creation etc.
Definition Pc.cc:257
virtual void initialise() INITIALISATION_ONLY
Definition Pc.cc:162
Pc() INITIALISATION_ONLY
Definition Pc.cc:392
virtual Serial * getSerial(size_t n)
Definition Pc.cc:325
virtual Timer * getTimer()
Definition Pc.cc:354
virtual Vga * getVga(size_t n)
Definition Pc.cc:333
virtual SchedulerTimer * getSchedulerTimer()
Definition Pc.cc:345
virtual void initialiseDeviceTree()
Definition Pc.cc:270
bool prepareShutdown(ShutdownType type) override
Definition Pc.cc:120
virtual void deinitialise()
Definition Pc.cc:220
virtual size_t getNumSerial()
Definition Pc.cc:329
virtual void setKeyboard(Keyboard *kb)
Definition Pc.cc:362
virtual ~Pc()
Definition Pc.cc:411
virtual Keyboard * getKeyboard()
Definition Pc.cc:358
const char * finalShutdown(ShutdownType type) override
Definition Pc.cc:128
virtual size_t getNumVga()
Definition Pc.cc:337
void initialise()
Definition Pic.h:52
static Pic & instance()
Definition Pic.h:56
bool initialise() INITIALISATION_ONLY
Definition Pic.cc:648
Definition Pit.h:38
bool initialise() INITIALISATION_ONLY
Definition Pit.cc:51
void uninitialise()
Definition Pit.cc:81
static Pit & instance()
Definition Pit.h:41
static void reset()
static bool tlbInvalidationTerminal()
static size_t getCount()
static size_t index()
Definition Rtc.h:50
void uninitialise()
Definition Rtc.cc:694
bool initialise1(uint8_t centuryIndex) INITIALISATION_ONLY
Definition Rtc.cc:455
bool initialise2() INITIALISATION_ONLY
Definition Rtc.cc:512
void initialiseProcessorClock() INITIALISATION_ONLY
Definition Rtc.cc:583
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)
virtual void initialise()
Initialises the device.
virtual void setBase(uintptr_t nBaseAddr)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118