Max OS 0.3
Loading...
Searching...
No Matches
apic.cpp
Go to the documentation of this file.
1
10#include <common/logger.h>
12
13using namespace MaxOS;
14using namespace MaxOS::hardwarecommunication;
15using namespace MaxOS::system;
16using namespace MaxOS::memory;
17
22
23 // Get the APIC base address
24 uint64_t msr_info = CPU::read_msr(0x1B);
25 m_apic_base = msr_info & 0xFFFFF000;
26
27 // Reserve the base address once
28 static bool bsp_setup = false;
29 if(!bsp_setup)
31
32 // Check if the APIC supports x2APIC
33 uint32_t ignored, xleaf, x2leaf;
34 CPU::cpuid(0x01, &ignored, &ignored, &x2leaf, &xleaf);
35
36 if (x2leaf & (1 << 21)) {
37
38 // Enable x2APIC
39 m_x2apic = true;
40 msr_info |= (1 << 10);
41 CPU::write_msr(0x1B, msr_info);
42 Logger::DEBUG() << "CPU supports x2APIC\n";
43
44 } else if (xleaf & (1 << 9)) {
45
46 m_x2apic = false;
47 Logger::DEBUG() << "CPU supports xAPIC\n";
48
49 // Map the APIC base address to the higher half
50 m_apic_base_high = (uint64_t) PhysicalMemoryManager::to_io_region(m_apic_base);
52 Logger::DEBUG() << "APIC Base: phy=0x" << m_apic_base << ", virt=0x" << m_apic_base_high << "\n";
53 } else {
54 ASSERT(false, "CPU does not support xAPIC");
55 }
56
57 if (!(msr_info & (1 << 11))) {
58 Logger::WARNING() << "APIC is not enabled\n";
59 return;
60 }
61
62 // Enable the APIC
63 write(0xF0, (1 << 8) | 0x100);
64 bsp_setup = true;
65 Logger::DEBUG() << "APIC Enabled\n";
66}
67
68LocalAPIC::~LocalAPIC() = default;
69
76uint32_t LocalAPIC::read(uint32_t reg) const {
77
78 // If x2APIC is enabled I/O is done through the MSR
79 if (m_x2apic)
80 return (uint32_t) CPU::read_msr((reg >> 4) + 0x800);
81
82 return *(volatile uint32_t*) ((uintptr_t) m_apic_base_high + reg);
83}
84
91void LocalAPIC::write(uint32_t reg, uint32_t value) const {
92
93 // If x2APIC is enabled I/O is done through the MSR
94 if (m_x2apic){
95 CPU::write_msr((reg >> 4) + 0x800, value);
96 return;
97 }
98
99 // Default to memory I/O
100 *(volatile uint32_t*) ((uintptr_t) m_apic_base_high + reg) = value;
101}
102
108uint32_t LocalAPIC::id() const {
109
110 // Read the id
111 uint32_t id = read(0x20);
112
113 // Return the id
114 return m_x2apic ? id : (id >> 24);
115}
116
121
122 write(0xB0, 0);
123}
124
132void LocalAPIC::send_icr(uint32_t icr_low, uint32_t icr_high, bool use_shorthand) const {
133
134 if (!m_x2apic) {
135
136 // Select target core
137 if (!use_shorthand)
138 write(0x310, icr_high << 24);
139
140 // Send the interrupt
141 write(0x300, icr_low);
142
143 // Wait for delivery
144 while (read(0x300) & (1 << 12))
145 asm volatile("pause");
146
147 } else {
148
149 // x2APIC: pack and write the full 64-bit ICR
150 uint64_t icr = icr_low;
151 if (!use_shorthand)
152 icr |= (uint64_t)icr_high << 32;
153
154 CPU::write_msr(0x830, icr);
155 }
156
157}
158
165void LocalAPIC::send_init(uint8_t apic_id, bool assert) const {
166
167 uint32_t icr_low = 0;
168 uint32_t icr_high = apic_id;
169
170 // Set delivery mode to INIT
171 icr_low |= (0b101 << 8);
172
173 // Set assert if needed
174 if (assert)
175 icr_low |= (1 << 14);
176
177 // Level triggered
178 icr_low |= (1 << 15);
179
180 // Send the init command
181 send_icr(icr_low, icr_high, false);
182}
183
190void LocalAPIC::send_startup(uint8_t apic_id, uint8_t vector) const {
191
192 uint32_t icr_low = 0;
193 uint32_t icr_high = apic_id;
194
195 // Vector being sent
196 icr_low |= vector;
197
198 // Set delivery mode to STARTUP
199 icr_low |= (0b110 << 8);
200
201 // Send the startup command
202 send_icr(icr_low, icr_high, false);
203}
204
205
213void LocalAPIC::send_interrupt(uint8_t apic_id, uint8_t vector, size_t flags) const {
214
215 uint32_t icr_low = 0;
216 uint32_t icr_high = 0;
217
218 // Vector (bits 0–7)
219 icr_low |= vector;
220
221 // Destination
222 bool use_shorthand = true;
223 if (flags & SEND_ALL) {
224 // All including self (11b)
225 icr_low |= (0b11 << 18);
226 } else if (flags & EXCLUDE_SELF) {
227 // All excluding self (10b)
228 icr_low |= (0b10 << 18);
229 } else {
230 // Specific core
231 icr_high = ((uint32_t)apic_id) << 24;
232 use_shorthand = false;
233 }
234
235 // Send the vector
236 send_icr(icr_low, icr_high, use_shorthand);
237
238}
239
246: m_acpi(acpi)
247{
248
249 // Get the information about the IO APIC
250 m_madt = (MADT*) m_acpi->find("APIC");
251 MADTEntry* io_apic_item = get_madt_item(MADT_TYPE::IO_APIC, 0);
252
253 // Get the IO APIC
254 auto* io_apic = (MADT_IO_APIC*) PhysicalMemoryManager::to_io_region((uint64_t) io_apic_item + sizeof(MADTEntry));
256
257 // Map the IO APIC address to the higher half
258 m_address = io_apic->io_apic_address;
259 m_address_high = (uint64_t) PhysicalMemoryManager::to_io_region(m_address);
261 Logger::DEBUG() << "IO APIC Address: phy=0x" << m_address << ", virt=0x" << m_address_high << "\n";
262
263 // Get the IO APIC version and max redirection entry
264 m_version = read(0x1);
265 m_max_redirect_entry = (uint8_t) (m_version >> 16);
266
267 // Log the IO APIC information
268 Logger::DEBUG() << "IO APIC Version: 0x" << (uint64_t) (m_version & 0xFF) << "\n";
269 Logger::DEBUG() << "IO APIC Max Redirection Entry: 0x" << (uint64_t) m_max_redirect_entry << "\n";
270
271 // Get the source override item
272 MADTEntry* source_override_item = get_madt_item(MADT_TYPE::IO_APIC, m_override_array_size);
273 uint32_t total_length = sizeof(MADT);
274 while (total_length < m_madt->header.length && m_override_array_size < 0x10) { // 0x10 is the max items
275
276 total_length += source_override_item->length;
277
278 // If there is an override, populate the array
279 if (source_override_item != nullptr && source_override_item->type == 2) {
280
281 // Get the override and populate the array
282 auto* override = (Override*) (source_override_item + 1);
283 m_override_array[m_override_array_size].bus = override->bus;
284 m_override_array[m_override_array_size].source = override->source;
285 m_override_array[m_override_array_size].global_system_interrupt = override->global_system_interrupt;
286 m_override_array[m_override_array_size].flags = override->flags;
287 m_override_array_size++;
288 }
289
290 // Get the next item
291 source_override_item = get_madt_item(MADT_TYPE::IO_APIC, m_override_array_size);
292 if (source_override_item == nullptr)
293 break;
294 }
295
296 Logger::DEBUG() << "IO APIC Source Overrides: 0x" << m_override_array_size << "\n";
297}
298
299IOAPIC::~IOAPIC() = default;
300
309
310 // The item starts at the start of the MADT
311 auto* item = (MADTEntry*) ((uint64_t) m_madt + sizeof(MADT));
312 uint64_t total_length = 0;
313 uint8_t current_index = 0;
314
315 // Loop through the items
316 while (total_length + sizeof(MADT) < m_madt->header.length && current_index <= index) {
317
318 // Correct type
319 if (item->type == (uint8_t)type) {
320
321 // Correct index means found
322 if (current_index == index)
323 return item;
324
325 // Right type wrong index so move on
326 current_index++;
327 }
328
329 // Increment the total length
330 total_length += item->length;
331 item = (MADTEntry*) ((uint64_t) item + item->length);
332 }
333
334 // No item found
335 return nullptr;
336}
337
344uint32_t IOAPIC::read(uint32_t reg) const {
345
346 // Tell the APIC what register to read from
347 *(volatile uint32_t*) (m_address_high + 0x00) = reg;
348
349 // Read the value
350 return *(volatile uint32_t*) (m_address_high + 0x10);
351}
352
359void IOAPIC::write(uint32_t reg, uint32_t value) const {
360
361 // Write the register
362 *(volatile uint32_t*) (m_address_high + 0x00) = reg;
363
364 // Write the value
365 *(volatile uint32_t*) (m_address_high + 0x10) = value;
366}
367
374void IOAPIC::read_redirect(uint8_t index, RedirectionEntry* entry) const {
375
376 // Check bounds
377 if (index < 0x10 || index > 0x3F)
378 return;
379
380 // Read the entry chunks into the buffer (struct is auto extracted from the raw information)
381 uint32_t low = read(index);
382 uint32_t high = read(index + 1);
383 entry->raw = ((uint64_t) high << 32) | ((uint64_t) low);
384}
385
392void IOAPIC::write_redirect(uint8_t index, RedirectionEntry* entry) const {
393
394 // Check bounds
395 if (index < 0x10 || index > 0x3F)
396 return;
397
398 // Break the entry down into 32bit chunks
399 auto low = (uint32_t) entry->raw;
400 auto high = (uint32_t) (entry->raw >> 32);
401
402 // Set the entry
403 write(index, low);
404 write(index + 1, high);
405}
406
413
414 // Create the redirection entry
415 RedirectionEntry entry = {};
416 entry.raw = redirect->flags | (redirect->interrupt & 0xFF);
417 entry.destination = redirect->destination;
418 entry.mask = redirect->mask;
419
420 // Check if a global system interrupt is used
421 for (uint8_t i = 0; i < m_override_array_size; i++) {
422
423 if (m_override_array[i].source != redirect->type)
424 continue;
425
426 // Set the lower 4 bits of the pin
427 entry.pin_polarity = ((m_override_array[i].flags & 0b11) == 2) ? 0b1 : 0b0;
428 entry.pin_polarity = (((m_override_array[i].flags >> 2) & 0b11) == 2) ? 0b1 : 0b0;
429
430 // Set the trigger mode
431 entry.trigger_mode = (((m_override_array[i].flags >> 2) & 0b11) == 2);
432 break;
433
434 }
435
436 // Write the redirect
437 write_redirect(redirect->index, &entry);
438}
439
446void IOAPIC::set_redirect_mask(uint8_t index, bool mask) {
447
448 // Read the current entry
449 RedirectionEntry entry = {};
450 read_redirect(index, &entry);
451
452 // Set the mask
453 entry.mask = mask;
454 write_redirect(index, &entry);
455}
456
463: m_pic_master_command_port(0x20),
464 m_pic_master_data_port(0x21),
465 m_pic_slave_command_port(0xA0),
466 m_pic_slave_data_port(0xA1)
467{
468
469 // Register this APIC
470 Logger::INFO() << "Setting up APIC\n";
472
473 // Init the Local APIC
474 Logger::DEBUG() << "Initialising Local APIC\n";
475 m_local_apic = new LocalAPIC();
476
477 // Disable the legacy mode PIC
478 Logger::DEBUG() << "Disabling PIC\n";
479 disable_pic();
480
481 // Init the IO APIC
482 Logger::DEBUG() << "Initialising IO APIC\n";
483 m_io_apic = new IOAPIC(acpi);
484}
485
490
491 // Free the memory
492 delete m_local_apic;
493 delete m_io_apic;
494}
495
499void AdvancedProgrammableInterruptController::disable_pic() {
500
501 // Initialise the PIC
502 m_pic_master_command_port.write(0x11);
503 m_pic_slave_command_port.write(0x11);
504
505 // Set the offsets
506 m_pic_master_data_port.write(0x20);
507 m_pic_slave_data_port.write(0x28);
508
509 // Set the slave/master relationships
510 m_pic_master_data_port.write(0x04);
511 m_pic_slave_data_port.write(0x02);
512
513 // Set the modes (8086/8086)
514 m_pic_master_data_port.write(0x01);
515 m_pic_slave_data_port.write(0x01);
516
517 // Mask the interrupts
518 m_pic_master_data_port.write(0xFF);
519 m_pic_slave_data_port.write(0xFF);
520}
521
528
529 return m_local_apic;
530}
531
538
539 return m_io_apic;
540}
Defines classes and structures for handling the Advanced Programmable Interrupt Controller (APIC) in ...
MADT_TYPE
The types of MADT entries.
Definition apic.h:89
static LogRecord DEBUG()
Gets the active logger for debug logs.
Definition logger.cpp:204
static LogRecord INFO()
Gets the active logger for info logs.
Definition logger.cpp:195
static LogRecord WARNING()
Gets the active logger for warnings.
Definition logger.cpp:213
Handles ACPI table parsing and retrieval.
Definition acpi.h:102
acpi_sdt_header_t * find(const char *signature)
Finds a table with the given signature.
Definition acpi.cpp:163
~AdvancedProgrammableInterruptController()
Destroy the Advanced Programmable Interrupt Controller object and frees the Local APIC and IO APIC.
Definition apic.cpp:489
AdvancedProgrammableInterruptController(AdvancedConfigurationAndPowerInterface *acpi)
Construct a new Advanced Programmable Interrupt Controller object and initialises the Local APIC and ...
Definition apic.cpp:462
Handles the IO APIC in the system (one per system)
Definition apic.h:205
void write(uint32_t reg, uint32_t value) const
write a value to an IO Apic register
Definition apic.cpp:359
void set_redirect(interrupt_redirect_t *redirect)
Redirect a system interrupt to a different IRQ.
Definition apic.cpp:412
MADTEntry * get_madt_item(MADT_TYPE type, uint8_t index)
Get an item in the MADT.
Definition apic.cpp:308
void set_redirect_mask(uint8_t index, bool mask)
Enables/Disables an interrupt redirect by masking it.
Definition apic.cpp:446
uint32_t read(uint32_t reg) const
read a value from a IO Apic register
Definition apic.cpp:344
IOAPIC(AdvancedConfigurationAndPowerInterface *acpi)
Construct a new IO APIC object. Maps the IO APIC registers and reads the MADT to get information abou...
Definition apic.cpp:245
static InterruptManager * active_interrupt_manager()
Gets the active interrupt manager.
void set_apic(AdvancedProgrammableInterruptController *apic)
Sets the APIC.
Handles the local APIC for the current core.
Definition apic.h:34
void send_eoi() const
Acknowledge that the interrupt has been handled, allowing the PIC to process the next interrupt.
Definition apic.cpp:120
void write(uint32_t reg, uint32_t value) const
write a value to the apic register using the MSR or memory I/O depending on the local apic version
Definition apic.cpp:91
void send_icr(uint32_t icr_low, uint32_t icr_high, bool use_shorthand) const
Send an Interrupt Command Register (ICR) to the local apic.
Definition apic.cpp:132
uint32_t id() const
Get the id of the local apic.
Definition apic.cpp:108
LocalAPIC()
Construct a new Local APIC object and map the APIC base address to the higher half if needed as well ...
Definition apic.cpp:21
void send_startup(uint8_t apic_id, uint8_t vector) const
Send the start up IPI to another apic.
Definition apic.cpp:190
void send_interrupt(uint8_t apic_id, uint8_t vector, size_t flags) const
Send an interrupt to a specific core.
Definition apic.cpp:213
uint32_t read(uint32_t reg) const
read a value from the apic register using the MSR or memory I/O depending on the local apic version
Definition apic.cpp:76
void send_init(uint8_t apic_id, bool assert) const
send the init IPI to another apic
Definition apic.cpp:165
static void * to_io_region(uintptr_t physical_address)
Converts a physical address to an IO region address if it is in the lower region using the higher hal...
Definition physical.cpp:914
void reserve(uint64_t address)
Reserves a physical address.
Definition physical.cpp:774
virtual_address_t * map(virtual_address_t *virtual_address, size_t flags)
Allocates a physical address to a virtual address.
Definition physical.cpp:535
static PhysicalMemoryManager * s_current_manager
The current physical memory manager in use.
Definition physical.h:183
static void cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
Executes the CPUID instruction with the specified leaf and returns the results in the provided pointe...
Definition cpu.cpp:402
static uint64_t read_msr(uint32_t msr)
Reads the value of the specified MSR.
Definition cpu.cpp:371
static void write_msr(uint32_t msr, uint64_t value)
Writes the specified value to the specified MSR.
Definition cpu.cpp:387
Defines a InterruptManager and InterruptHandler for managing hardware and software interrupts.
Defines a Logger class for logging messages with different severity levels to multiple output streams...
void virtual_address_t
A type representing a virtual address (used for readability)
Definition physical.h:30
void physical_address_t
A type representing a physical address (used for readability)
Definition physical.h:31
@ PRESENT
The page is present in memory.
Definition physical.h:44
@ WRITE
Memory in this page is writable.
Definition physical.h:45
uint32_t length
The size of the entire table, including the header, in bytes.
Definition acpi.h:64
Defines how an interrupt input is redirected to a specific processor via the I/O APIC.
Definition apic.h:174
uint8_t index
Where in the IO APIC redirection table this redirect should be placed.
Definition apic.h:177
uint8_t interrupt
The interrupt number to trigger when this redirect is used.
Definition apic.h:178
uint32_t flags
Configuration flags for the interrupt (see MADT override flags)
Definition apic.h:180
bool mask
Should the interrupt be disabled initially.
Definition apic.h:181
uint8_t type
The type of redirect (should be 0 for normal interrupts)
Definition apic.h:176
uint8_t destination
The ID of the core the interrupt should be sent to.
Definition apic.h:179
An item in the MADT table.
Definition apic.h:80
uint8_t type
The type of the MADT entry (see MADT_TYPE)
Definition apic.h:81
uint8_t length
How many bytes this entry takes up (including the type and length fields)
Definition apic.h:82
An entry in the MADT table describing an IO APIC.
Definition apic.h:108
Multiple APIC Description Table (MADT) (ACPI 2 or higher). Provides information about the APICs in th...
Definition apic.h:67
ACPISDTHeader header
Common ACPI SDT header.
Definition apic.h:68
Specifies how a specific interrupt source is mapped to a global system interrupt.
Definition apic.h:192
uint8_t source
The interrupt source number on the specified bus.
Definition apic.h:195
uint32_t global_system_interrupt
The global system interrupt that the source is mapped to.
Definition apic.h:196
uint8_t bus
Identifies the hardware bus the interrupt comes from (0 = ISA, 1 = PCI)
Definition apic.h:194
uint16_t flags
Flags describing the polarity and trigger mode of the interrupt source (see MADT override flags)
Definition apic.h:197
An IO APIC redirection entry. Describes how an interrupt is routed.
Definition apic.h:135
uint64_t raw
The raw 64-bit value of the redirection entry.
Definition apic.h:149