1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include <mos/mm/paging/paging.h> |
4 | #include <mos/mm/physical/pmm.h> |
5 | #include <mos/platform/platform.h> |
6 | #include <mos/syslog/printk.h> |
7 | #include <mos/x86/acpi/madt.h> |
8 | #include <mos/x86/interrupt/apic.h> |
9 | #include <mos/x86/x86_interrupt.h> |
10 | #include <mos/x86/x86_platform.h> |
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 = { 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 = { 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 = { 0 }; |
142 | entry.interrupt_vec = irq + ISR_MAX_COUNT; |
143 | entry.mask = true; |
144 | ioapic_write_redirection_entry(irq, entry); |
145 | } |
146 | |