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
131void LocalAPIC::send_init(uint8_t apic_id, bool assert) const {
132
133 uint32_t icr_low = 0;
134
135 // Delivery mode = INIT (101b at bits 8-10)
136 icr_low |= (0b101 << 8);
137
138 // Level (bit 14): 1 = assert, 0 = de-assert
139 if (assert)
140 icr_low |= (1 << 14);
141
142 // Trigger mode: Level = 1
143 icr_low |= (1 << 15);
144
145 if (!m_x2apic) {
146
147 // Select target core
148 write(0x310, apic_id << 24);
149
150 // Send INIT (Delivery mode = INIT, Level = 1)
151 write(0x300, icr_low);
152
153 // Wait for delivery
154 while (read(0x300) & (1 << 12))
155 asm volatile("pause");
156
157 } else {
158
159 // x2APIC
160 CPU::write_msr(0x830, (uint64_t)apic_id << 32 | icr_low);
161 }
162}
163
170void LocalAPIC::send_startup(uint8_t apic_id, uint8_t vector) const {
171
172 if (!m_x2apic) {
173
174 // Select target core
175 write(0x310, apic_id << 24);
176
177 // Send SIPI (Delivery mode = STARTUP)
178 write(0x300, 0x4600 | vector);
179
180 // Wait for delivery
181 while (read(0x300) & (1 << 12))
182 asm volatile("pause");
183
184 } else {
185
186 // x2APIC
187 CPU::write_msr(0x831, (uint64_t)apic_id << 32 | 0x4600 | vector);
188 }
189
190
191}
192
199: m_acpi(acpi)
200{
201
202 // Get the information about the IO APIC
203 m_madt = (MADT*) m_acpi->find("APIC");
204 MADTEntry* io_apic_item = get_madt_item(MADT_TYPE::IO_APIC, 0);
205
206 // Get the IO APIC
207 auto* io_apic = (MADT_IO_APIC*) PhysicalMemoryManager::to_io_region((uint64_t) io_apic_item + sizeof(MADTEntry));
209
210 // Map the IO APIC address to the higher half
211 m_address = io_apic->io_apic_address;
212 m_address_high = (uint64_t) PhysicalMemoryManager::to_io_region(m_address);
214 Logger::DEBUG() << "IO APIC Address: phy=0x" << m_address << ", virt=0x" << m_address_high << "\n";
215
216 // Get the IO APIC version and max redirection entry
217 m_version = read(0x1);
218 m_max_redirect_entry = (uint8_t) (m_version >> 16);
219
220 // Log the IO APIC information
221 Logger::DEBUG() << "IO APIC Version: 0x" << (uint64_t) (m_version & 0xFF) << "\n";
222 Logger::DEBUG() << "IO APIC Max Redirection Entry: 0x" << (uint64_t) m_max_redirect_entry << "\n";
223
224 // Get the source override item
225 MADTEntry* source_override_item = get_madt_item(MADT_TYPE::IO_APIC, m_override_array_size);
226 uint32_t total_length = sizeof(MADT);
227 while (total_length < m_madt->header.length && m_override_array_size < 0x10) { // 0x10 is the max items
228
229 total_length += source_override_item->length;
230
231 // If there is an override, populate the array
232 if (source_override_item != nullptr && source_override_item->type == 2) {
233
234 // Get the override and populate the array
235 auto* override = (Override*) (source_override_item + 1);
236 m_override_array[m_override_array_size].bus = override->bus;
237 m_override_array[m_override_array_size].source = override->source;
238 m_override_array[m_override_array_size].global_system_interrupt = override->global_system_interrupt;
239 m_override_array[m_override_array_size].flags = override->flags;
240 m_override_array_size++;
241 }
242
243 // Get the next item
244 source_override_item = get_madt_item(MADT_TYPE::IO_APIC, m_override_array_size);
245 if (source_override_item == nullptr)
246 break;
247 }
248
249 Logger::DEBUG() << "IO APIC Source Overrides: 0x" << m_override_array_size << "\n";
250}
251
252IOAPIC::~IOAPIC() = default;
253
262
263 // The item starts at the start of the MADT
264 auto* item = (MADTEntry*) ((uint64_t) m_madt + sizeof(MADT));
265 uint64_t total_length = 0;
266 uint8_t current_index = 0;
267
268 // Loop through the items
269 while (total_length + sizeof(MADT) < m_madt->header.length && current_index <= index) {
270
271 // Correct type
272 if (item->type == (uint8_t)type) {
273
274 // Correct index means found
275 if (current_index == index)
276 return item;
277
278 // Right type wrong index so move on
279 current_index++;
280 }
281
282 // Increment the total length
283 total_length += item->length;
284 item = (MADTEntry*) ((uint64_t) item + item->length);
285 }
286
287 // No item found
288 return nullptr;
289}
290
297uint32_t IOAPIC::read(uint32_t reg) const {
298
299 // Tell the APIC what register to read from
300 *(volatile uint32_t*) (m_address_high + 0x00) = reg;
301
302 // Read the value
303 return *(volatile uint32_t*) (m_address_high + 0x10);
304}
305
312void IOAPIC::write(uint32_t reg, uint32_t value) const {
313
314 // Write the register
315 *(volatile uint32_t*) (m_address_high + 0x00) = reg;
316
317 // Write the value
318 *(volatile uint32_t*) (m_address_high + 0x10) = value;
319}
320
327void IOAPIC::read_redirect(uint8_t index, RedirectionEntry* entry) const {
328
329 // Check bounds
330 if (index < 0x10 || index > 0x3F)
331 return;
332
333 // Read the entry chunks into the buffer (struct is auto extracted from the raw information)
334 uint32_t low = read(index);
335 uint32_t high = read(index + 1);
336 entry->raw = ((uint64_t) high << 32) | ((uint64_t) low);
337}
338
345void IOAPIC::write_redirect(uint8_t index, RedirectionEntry* entry) const {
346
347 // Check bounds
348 if (index < 0x10 || index > 0x3F)
349 return;
350
351 // Break the entry down into 32bit chunks
352 auto low = (uint32_t) entry->raw;
353 auto high = (uint32_t) (entry->raw >> 32);
354
355 // Set the entry
356 write(index, low);
357 write(index + 1, high);
358}
359
366
367 // Create the redirection entry
368 RedirectionEntry entry = {};
369 entry.raw = redirect->flags | (redirect->interrupt & 0xFF);
370 entry.destination = redirect->destination;
371 entry.mask = redirect->mask;
372
373 // Check if a global system interrupt is used
374 for (uint8_t i = 0; i < m_override_array_size; i++) {
375
376 if (m_override_array[i].source != redirect->type)
377 continue;
378
379 // Set the lower 4 bits of the pin
380 entry.pin_polarity = ((m_override_array[i].flags & 0b11) == 2) ? 0b1 : 0b0;
381 entry.pin_polarity = (((m_override_array[i].flags >> 2) & 0b11) == 2) ? 0b1 : 0b0;
382
383 // Set the trigger mode
384 entry.trigger_mode = (((m_override_array[i].flags >> 2) & 0b11) == 2);
385 break;
386
387 }
388
389 // Write the redirect
390 write_redirect(redirect->index, &entry);
391}
392
399void IOAPIC::set_redirect_mask(uint8_t index, bool mask) {
400
401 // Read the current entry
402 RedirectionEntry entry = {};
403 read_redirect(index, &entry);
404
405 // Set the mask
406 entry.mask = mask;
407 write_redirect(index, &entry);
408}
409
416: m_pic_master_command_port(0x20),
417 m_pic_master_data_port(0x21),
418 m_pic_slave_command_port(0xA0),
419 m_pic_slave_data_port(0xA1)
420{
421
422 // Register this APIC
423 Logger::INFO() << "Setting up APIC\n";
425
426 // Init the Local APIC
427 Logger::DEBUG() << "Initialising Local APIC\n";
428 m_local_apic = new LocalAPIC();
429
430 // Disable the legacy mode PIC
431 Logger::DEBUG() << "Disabling PIC\n";
432 disable_pic();
433
434 // Init the IO APIC
435 Logger::DEBUG() << "Initialising IO APIC\n";
436 m_io_apic = new IOAPIC(acpi);
437}
438
443
444 // Free the memory
445 delete m_local_apic;
446 delete m_io_apic;
447}
448
452void AdvancedProgrammableInterruptController::disable_pic() {
453
454 // Initialise the PIC
455 m_pic_master_command_port.write(0x11);
456 m_pic_slave_command_port.write(0x11);
457
458 // Set the offsets
459 m_pic_master_data_port.write(0x20);
460 m_pic_slave_data_port.write(0x28);
461
462 // Set the slave/master relationships
463 m_pic_master_data_port.write(0x04);
464 m_pic_slave_data_port.write(0x02);
465
466 // Set the modes (8086/8086)
467 m_pic_master_data_port.write(0x01);
468 m_pic_slave_data_port.write(0x01);
469
470 // Mask the interrupts
471 m_pic_master_data_port.write(0xFF);
472 m_pic_slave_data_port.write(0xFF);
473}
474
481
482 return m_local_apic;
483}
484
491
492 return m_io_apic;
493}
Defines classes and structures for handling the Advanced Programmable Interrupt Controller (APIC) in ...
MADT_TYPE
The types of MADT entries.
Definition apic.h:77
static Logger DEBUG()
Gets active logger set to DEBUG level.
Definition logger.cpp:193
static Logger WARNING()
Gets active logger set to WARNING level.
Definition logger.cpp:205
static Logger INFO()
Gets active logger set to info level.
Definition logger.cpp:171
Stores the left, top, width and height of a rectangle.
Definition rectangle.h:22
Handles ACPI table parsing and retrieval.
Definition acpi.h:99
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:442
AdvancedProgrammableInterruptController(AdvancedConfigurationAndPowerInterface *acpi)
Construct a new Advanced Programmable Interrupt Controller object and initialises the Local APIC and ...
Definition apic.cpp:415
Handles the IO APIC in the system (one per system)
Definition apic.h:193
void write(uint32_t reg, uint32_t value) const
write a value to an IO Apic register
Definition apic.cpp:312
void set_redirect(interrupt_redirect_t *redirect)
Redirect a system interrupt to a different IRQ.
Definition apic.cpp:365
MADTEntry * get_madt_item(MADT_TYPE type, uint8_t index)
Get an item in the MADT.
Definition apic.cpp:261
void set_redirect_mask(uint8_t index, bool mask)
Enables/Disables an interrupt redirect by masking it.
Definition apic.cpp:399
uint32_t read(uint32_t reg) const
read a value from a IO Apic register
Definition apic.cpp:297
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:198
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:24
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
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:170
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:131
void write(uint8_t data) final
write a byte to the port (slow)
Definition port.cpp:74
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:915
void reserve(uint64_t address)
Reserves a physical address.
Definition physical.cpp:777
virtual_address_t * map(virtual_address_t *virtual_address, size_t flags)
Allocates a physical address to a virtual address.
Definition physical.cpp:538
static PhysicalMemoryManager * s_current_manager
The current physical memory manager in use.
Definition physical.h:185
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:403
static uint64_t read_msr(uint32_t msr)
Reads the value of the specified MSR.
Definition cpu.cpp:372
static void write_msr(uint32_t msr, uint64_t value)
Writes the specified value to the specified MSR.
Definition cpu.cpp:388
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...
#define ASSERT(condition, format,...)
If the specified condition is not met then the kernel will crash with the specified message.
Definition logger.h:100
@ PRESENT
The page is present in memory.
Definition physical.h:43
@ WRITE
Memory in this page is writable.
Definition physical.h:44
uint32_t length
The size of the entire table, including the header, in bytes.
Definition acpi.h:61
Defines how an interrupt input is redirected to a specific processor via the I/O APIC.
Definition apic.h:162
uint8_t index
Where in the IO APIC redirection table this redirect should be placed.
Definition apic.h:165
uint8_t interrupt
The interrupt number to trigger when this redirect is used.
Definition apic.h:166
uint32_t flags
Configuration flags for the interrupt (see MADT override flags)
Definition apic.h:168
bool mask
Should the interrupt be disabled initially.
Definition apic.h:169
uint8_t type
The type of redirect (should be 0 for normal interrupts)
Definition apic.h:164
uint8_t destination
The ID of the core the interrupt should be sent to.
Definition apic.h:167
An item in the MADT table.
Definition apic.h:68
uint8_t type
The type of the MADT entry (see MADT_TYPE)
Definition apic.h:69
uint8_t length
How many bytes this entry takes up (including the type and length fields)
Definition apic.h:70
An entry in the MADT table describing an IO APIC.
Definition apic.h:96
Multiple APIC Description Table (MADT) (ACPI 2 or higher). Provides information about the APICs in th...
Definition apic.h:55
ACPISDTHeader header
Common ACPI SDT header.
Definition apic.h:56
Specifies how a specific interrupt source is mapped to a global system interrupt.
Definition apic.h:180
uint8_t source
The interrupt source number on the specified bus.
Definition apic.h:183
uint32_t global_system_interrupt
The global system interrupt that the source is mapped to.
Definition apic.h:184
uint8_t bus
Identifies the hardware bus the interrupt comes from (0 = ISA, 1 = PCI)
Definition apic.h:182
uint16_t flags
Flags describing the polarity and trigger mode of the interrupt source (see MADT override flags)
Definition apic.h:185
An IO APIC redirection entry. Describes how an interrupt is routed.
Definition apic.h:123
uint64_t raw
The raw 64-bit value of the redirection entry.
Definition apic.h:137