1#include <acpi/ioapic.h>
2#include <acpi/lapic.h>
3#include <acpi/madt.h>
4#include <console/panic.h>
5#include <console/printf.h>
6#include <drivers/mmio.h>
7#include <irq/irq.h>
8#include <log.h>
9#include <mem/page.h>
10#include <mem/vmm.h>
11#include <stdint.h>
12
13static struct ioapic_info ioapic;
14static LOG_HANDLE_DECLARE_PRINT(ioapic);
15
16void ioapic_write(uint8_t reg, uint32_t val) {
17 mmio_write_32(address: &ioapic.mmio_base[IOAPIC_REGSEL_INDEX], value: reg);
18 mmio_write_32(address: &ioapic.mmio_base[IOAPIC_IOWIN_INDEX], value: val);
19}
20
21uint32_t ioapic_read(uint8_t reg) {
22 mmio_write_32(address: &ioapic.mmio_base[IOAPIC_REGSEL_INDEX], value: reg);
23 return mmio_read_32(address: &ioapic.mmio_base[IOAPIC_IOWIN_INDEX]);
24}
25
26uint64_t ioapic_get_redirection_entry(int irq) {
27 uint32_t low = ioapic_read(IOAPIC_REDTBL_REG_LOW(irq));
28 uint32_t high = ioapic_read(IOAPIC_REDTBL_REG_HIGH(irq));
29 return ((uint64_t) high << 32) | low;
30}
31
32void ioapic_set_redirection_entry(int irq, uint64_t entry) {
33 ioapic_write(IOAPIC_REDTBL_REG_LOW(irq), val: (uint32_t) (entry & 0xFFFFFFFF));
34 ioapic_write(IOAPIC_REDTBL_REG_HIGH(irq), val: (uint32_t) (entry >> 32));
35}
36
37void ioapic_mask_irq(irq_t irq) {
38 uint32_t low = ioapic_read(IOAPIC_REDTBL_REG_LOW(irq));
39 low |= IOAPIC_REDTBL_MASK;
40 ioapic_write(IOAPIC_REDTBL_REG_LOW(irq), val: low);
41}
42
43void ioapic_unmask_irq(irq_t irq) {
44 uint32_t low = ioapic_read(IOAPIC_REDTBL_REG_LOW(irq));
45 low &= ~IOAPIC_REDTBL_MASK;
46 ioapic_write(IOAPIC_REDTBL_REG_LOW(irq), val: low);
47}
48
49static void ioapic_chip_mask(struct irq_desc *desc) {
50 uint32_t gsi = (uint32_t) (uintptr_t) desc->chip_data;
51 ioapic_mask_irq(irq: gsi);
52}
53
54static void ioapic_chip_unmask(struct irq_desc *desc) {
55 uint32_t gsi = (uint32_t) (uintptr_t) desc->chip_data;
56 ioapic_unmask_irq(irq: gsi);
57}
58
59static void ioapic_chip_eoi(struct irq_desc *desc) {
60 (void) desc;
61 lapic_write(LAPIC_REG_EOI, val: 0);
62}
63
64static struct irq_chip ioapic_chip = {
65 .name = "ioapic",
66 .mask = ioapic_chip_mask,
67 .unmask = ioapic_chip_unmask,
68 .eoi = ioapic_chip_eoi,
69 .set_affinity = NULL,
70 .set_rate_limit = NULL,
71};
72
73struct irq_chip *ioapic_get_chip(void) {
74 return &ioapic_chip;
75}
76
77uint32_t ioapic_isa_to_gsi(uint8_t isa_irq) {
78 return madt_isa_to_gsi(isa_irq);
79}
80
81void ioapic_route_irq(irq_t irq, uint8_t vector, uint8_t dest_apic_id,
82 bool masked) {
83 union ioapic_redirection_entry entry = {0};
84 entry.vector = vector;
85 entry.delivery_mode = IOAPIC_DELIVERY_FIXED;
86 entry.dest_mode = IOAPIC_DEST_PHYSICAL;
87 entry.polarity = IOAPIC_POLARITY_ACTIVE_HIGH;
88 entry.trigger_mode = IOAPIC_TRIGGER_EDGE;
89 entry.mask = masked ? IOAPIC_MASK_MASKED : IOAPIC_MASK_UNMASKED;
90 entry.dest_apic_id = dest_apic_id;
91 ioapic_set_redirection_entry(irq, entry: entry.raw);
92}
93
94void ioapic_route_isa_irq(uint8_t isa_irq, uint8_t vector, uint8_t dest_apic_id,
95 bool masked) {
96 uint32_t gsi = madt_isa_to_gsi(isa_irq);
97 uint16_t flags = madt_isa_flags(isa_irq);
98
99 union ioapic_redirection_entry entry = {0};
100 entry.vector = vector;
101 entry.delivery_mode = IOAPIC_DELIVERY_FIXED;
102 entry.dest_mode = IOAPIC_DEST_PHYSICAL;
103 entry.dest_apic_id = dest_apic_id;
104 entry.mask = masked ? IOAPIC_MASK_MASKED : IOAPIC_MASK_UNMASKED;
105
106 entry.polarity =
107 ((flags & IOAPIC_ISO_POLARITY_MASK) == IOAPIC_ISO_POLARITY_ACTIVE_LOW)
108 ? IOAPIC_POLARITY_ACTIVE_LOW
109 : IOAPIC_POLARITY_ACTIVE_HIGH;
110
111 entry.trigger_mode =
112 ((flags & IOAPIC_ISO_TRIGGER_MASK) == IOAPIC_ISO_TRIGGER_LEVEL)
113 ? IOAPIC_TRIGGER_LEVEL
114 : IOAPIC_TRIGGER_EDGE;
115
116 ioapic_set_redirection_entry(irq: gsi, entry: entry.raw);
117}
118
119void ioapic_route_isa_nmi(uint8_t isa_irq, uint8_t dest_apic_id) {
120 uint32_t gsi = madt_isa_to_gsi(isa_irq);
121
122 union ioapic_redirection_entry entry = {0};
123 entry.vector = IRQ_NMI;
124 entry.delivery_mode = IOAPIC_DELIVERY_NMI;
125 entry.dest_mode = IOAPIC_DEST_PHYSICAL;
126 entry.polarity = IOAPIC_POLARITY_ACTIVE_HIGH;
127 entry.trigger_mode = IOAPIC_TRIGGER_EDGE;
128 entry.mask = IOAPIC_MASK_UNMASKED;
129 entry.dest_apic_id = dest_apic_id;
130
131 ioapic_set_redirection_entry(irq: gsi, entry: entry.raw);
132}
133
134void ioapic_init(void) {
135 madt_init();
136
137 struct madt_ioapic_info *info = madt_get_ioapic();
138 if (!info->present) {
139 panic("no I/O APIC entry found in MADT");
140 }
141
142 ioapic.id = info->id;
143 ioapic.gsi_base = info->gsi_base;
144 ioapic.mmio_base = mmio_map(phys: info->address, IOAPIC_MMIO_SIZE);
145
146 log_info_global(LOG_HANDLE(ioapic), "ID: %u, GSI Base: %u, MMIO: %p",
147 ioapic.id, ioapic.gsi_base, ioapic.mmio_base);
148}
149