| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include <mos/mm/paging/paging.hpp> |
| 4 | #include <mos/mm/physical/pmm.hpp> |
| 5 | #include <mos/platform/platform.hpp> |
| 6 | #include <mos/syslog/printk.hpp> |
| 7 | #include <mos/x86/acpi/madt.hpp> |
| 8 | #include <mos/x86/interrupt/apic.hpp> |
| 9 | #include <mos/x86/x86_interrupt.hpp> |
| 10 | #include <mos/x86/x86_platform.hpp> |
| 11 | |
| 12 | // +-------+ +-------+ +-------+ |
| 13 | // | | | | | | |
| 14 | // | CPU 0 | | CPU 1 | | CPU 2 | ... |
| 15 | // | | | | | | |
| 16 | // +-------+ +-------+ +-------+ |
| 17 | // | LAPIC | | LAPIC | | LAPIC | ... |
| 18 | // +---+---+ +---+---+ +---+---+ |
| 19 | // | | | |
| 20 | // | +------+ | |
| 21 | // +---+ | +-----------------+ |
| 22 | // | | | |
| 23 | // | | | |
| 24 | // +--v--v--v---+ |
| 25 | // | I/O APIC | <- Interrupts are sent to this |
| 26 | // +------------+ |
| 27 | |
| 28 | #define IOAPIC_REG_ID 0x00 |
| 29 | #define IOAPIC_REG_VERSION 0x01 |
| 30 | #define IOAPIC_REG_ARB_ID 0x02 |
| 31 | #define IOAPIC_REG_REDIR_TABLE(n) (0x10 + 2 * (n)) |
| 32 | |
| 33 | typedef struct |
| 34 | { |
| 35 | u8 interrupt_vec : 8; |
| 36 | u8 delivery_mode : 3; // 0 = Normal, 1 = Low Priority, 2 = SMI, 4 = NMI, 5 = INIT, 7 = External |
| 37 | bool destination_mode : 1; // 0 = Physical, 1 = Logical |
| 38 | bool pending : 1; // 1 = if this interrupt is going to be sent, but the APIC is busy |
| 39 | ioapic_polarity_t polarity : 1; // 0 = Active High, 1 = Active Low |
| 40 | bool something : 1; // 0 = a local APIC has sent an EOI, 1 = a local APIC has received the interrupt |
| 41 | ioapic_trigger_mode_t trigger_mode : 1; // 0 = Edge, 1 = Level |
| 42 | bool mask : 1; // 0 = Enabled, 1 = Disabled |
| 43 | u64 reserved : 39; |
| 44 | struct |
| 45 | { |
| 46 | u8 target_apic_id : 4; |
| 47 | u8 something_else : 4; |
| 48 | } __packed destination; |
| 49 | } __packed ioapic_redirection_entry_t; |
| 50 | |
| 51 | MOS_STATIC_ASSERT(sizeof(ioapic_redirection_entry_t) == sizeof(u64), "ioapic_register_1 is not 64 bits" ); |
| 52 | |
| 53 | static u32 volatile *ioapic = NULL; |
| 54 | |
| 55 | should_inline u32 ioapic_read(u32 reg) |
| 56 | { |
| 57 | ioapic[0] = reg & 0xff; |
| 58 | return ioapic[4]; |
| 59 | } |
| 60 | |
| 61 | should_inline void ioapic_write(u32 reg, u32 value) |
| 62 | { |
| 63 | ioapic[0] = reg & 0xff; |
| 64 | ioapic[4] = value; |
| 65 | } |
| 66 | |
| 67 | should_inline void ioapic_write_redirection_entry(u32 irq, ioapic_redirection_entry_t entry) |
| 68 | { |
| 69 | union |
| 70 | { |
| 71 | u64 value; |
| 72 | ioapic_redirection_entry_t entry; |
| 73 | } __packed u = { .entry = entry }; |
| 74 | |
| 75 | ioapic_write(IOAPIC_REG_REDIR_TABLE(irq), value: u.value & 0xffffffff); |
| 76 | ioapic_write(IOAPIC_REG_REDIR_TABLE(irq) + 1, value: u.value >> 32); |
| 77 | } |
| 78 | |
| 79 | should_inline ioapic_redirection_entry_t ioapic_read_redirection_entry(u32 irq) |
| 80 | { |
| 81 | union |
| 82 | { |
| 83 | u64 value; |
| 84 | ioapic_redirection_entry_t entry; |
| 85 | } __packed u = { .value: 0 }; |
| 86 | |
| 87 | u.value = ioapic_read(IOAPIC_REG_REDIR_TABLE(irq)); |
| 88 | u.value |= (u64) ioapic_read(IOAPIC_REG_REDIR_TABLE(irq) + 1) << 32; |
| 89 | |
| 90 | return u.entry; |
| 91 | } |
| 92 | |
| 93 | void ioapic_init(void) |
| 94 | { |
| 95 | MOS_ASSERT_X(x86_ioapic_phyaddr != 0, "ioapic: no ioapic found in madt" ); |
| 96 | if (!pmm_find_reserved_region(needle: x86_ioapic_phyaddr)) |
| 97 | { |
| 98 | pr_info("reserving ioapic address" ); |
| 99 | pmm_reserve_address(x86_ioapic_phyaddr); |
| 100 | } |
| 101 | |
| 102 | ioapic = (u32 volatile *) pa_va(x86_ioapic_phyaddr); |
| 103 | const u32 ioapic_id = ioapic_read(IOAPIC_REG_ID) >> 24 & 0xf; // get the 24-27 bits |
| 104 | |
| 105 | const union |
| 106 | { |
| 107 | u32 value; |
| 108 | struct |
| 109 | { |
| 110 | u32 version : 8; // 0 - 7 |
| 111 | u32 reserved : 8; |
| 112 | u32 max_entries : 8; // 16 - 23 |
| 113 | u32 reserved2 : 8; |
| 114 | } __packed; |
| 115 | } version = { .value = ioapic_read(IOAPIC_REG_VERSION) }; |
| 116 | |
| 117 | const u32 arb_id = ioapic_read(IOAPIC_REG_ARB_ID) >> 24 & 0xf; // get the 24-27 bits |
| 118 | |
| 119 | pr_dinfo2(x86_ioapic, "max IRQs: %d, id: %d, version: %d, arb: %d" , version.max_entries + 1, ioapic_id, version.version, arb_id); |
| 120 | |
| 121 | for (int i = 0; i < version.max_entries + 1; i++) |
| 122 | ioapic_disable(irq: i); |
| 123 | } |
| 124 | |
| 125 | void ioapic_enable_with_mode(u32 irq, u32 lapic_id, ioapic_trigger_mode_t trigger_mode, ioapic_polarity_t polarity) |
| 126 | { |
| 127 | pr_dinfo2(x86_ioapic, "enable irq %d, cpu lapic-id: %d, trigger_mode %d, polarity %d" , irq, lapic_id, trigger_mode, polarity); |
| 128 | |
| 129 | ioapic_redirection_entry_t entry = { .interrupt_vec: 0 }; |
| 130 | entry.interrupt_vec = irq + ISR_MAX_COUNT; // the vector number received by the CPU |
| 131 | entry.polarity = polarity; |
| 132 | entry.trigger_mode = trigger_mode; |
| 133 | entry.destination.target_apic_id = lapic_id; |
| 134 | |
| 135 | const u32 irq_overridden = x86_ioapic_get_irq_override(irq); // the irq number received by the ioapic "pin" |
| 136 | ioapic_write_redirection_entry(irq: irq_overridden, entry); |
| 137 | } |
| 138 | |
| 139 | void ioapic_disable(u32 irq) |
| 140 | { |
| 141 | ioapic_redirection_entry_t entry = { .interrupt_vec: 0 }; |
| 142 | entry.interrupt_vec = irq + ISR_MAX_COUNT; |
| 143 | entry.mask = true; |
| 144 | ioapic_write_redirection_entry(irq, entry); |
| 145 | } |
| 146 | |