Serenity Operating System
at portability 310 lines 11 kB view raw
1/* 2 * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <Kernel/ACPI/MultiProcessorParser.h> 28#include <Kernel/Arch/i386/CPU.h> 29#include <Kernel/Interrupts/APIC.h> 30#include <Kernel/Interrupts/IOAPIC.h> 31#include <Kernel/Interrupts/InterruptManagement.h> 32#include <Kernel/VM/MemoryManager.h> 33 34#define IOAPIC_REDIRECTION_ENTRY_OFFSET 0x10 35namespace Kernel { 36enum DeliveryMode { 37 Normal = 0, 38 LowPriority = 1, 39 SMI = 2, 40 NMI = 3, 41 INIT = 4, 42 External = 7 43}; 44 45IOAPIC::IOAPIC(ioapic_mmio_regs& regs, u32 gsi_base, Vector<RefPtr<ISAInterruptOverrideMetadata>>& isa_overrides, Vector<RefPtr<PCIInterruptOverrideMetadata>>& pci_overrides) 46 : m_physical_access_registers(regs) 47 , m_gsi_base(gsi_base) 48 , m_id((read_register(0x0) >> 24) & 0xFF) 49 , m_version(read_register(0x1) & 0xFF) 50 , m_redirection_entries((read_register(0x1) >> 16) + 1) 51 , m_isa_interrupt_overrides(isa_overrides) 52 , m_pci_interrupt_overrides(pci_overrides) 53{ 54 kprintf("IOAPIC ID: 0x%x\n", m_id); 55 kprintf("IOAPIC Version: 0x%x, Redirection Entries count - %u\n", m_version, m_redirection_entries); 56 kprintf("IOAPIC Arbitration ID 0x%x\n", read_register(0x2)); 57 mask_all_redirection_entries(); 58} 59 60void IOAPIC::initialize() 61{ 62} 63 64void IOAPIC::map_interrupt_redirection(u8 interrupt_vector) 65{ 66 if (interrupt_vector == 11) { 67 configure_redirection_entry(11, 11 + IRQ_VECTOR_BASE, DeliveryMode::LowPriority, false, true, true, true, 0); 68 return; 69 } 70 for (auto redirection_override : m_isa_interrupt_overrides) { 71 ASSERT(!redirection_override.is_null()); 72 if (redirection_override->source() != interrupt_vector) 73 continue; 74 bool active_low; 75 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. 76 switch ((redirection_override->flags() & 0b11)) { 77 case 0: 78 active_low = false; 79 break; 80 case 1: 81 active_low = false; 82 break; 83 case 2: 84 ASSERT_NOT_REACHED(); // Reserved value 85 case 3: 86 active_low = true; 87 break; 88 } 89 90 bool trigger_level_mode; 91 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. 92 switch (((redirection_override->flags() >> 2) & 0b11)) { 93 case 0: 94 trigger_level_mode = false; 95 break; 96 case 1: 97 trigger_level_mode = false; 98 break; 99 case 2: 100 ASSERT_NOT_REACHED(); // Reserved value 101 case 3: 102 trigger_level_mode = true; 103 break; 104 } 105 configure_redirection_entry(redirection_override->gsi(), redirection_override->source() + IRQ_VECTOR_BASE, DeliveryMode::LowPriority, false, active_low, trigger_level_mode, true, 0); 106 return; 107 } 108 isa_identity_map(interrupt_vector); 109} 110 111void IOAPIC::isa_identity_map(int index) 112{ 113 configure_redirection_entry(index, index + IRQ_VECTOR_BASE, DeliveryMode::Normal, true, false, false, true, 1); 114} 115 116 117void IOAPIC::map_pci_interrupts() 118{ 119 configure_redirection_entry(11, 11 + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, false, true, true, 0); 120} 121 122void IOAPIC::map_isa_interrupts() 123{ 124 InterruptDisabler disabler; 125 for (auto redirection_override : m_isa_interrupt_overrides) { 126 ASSERT(!redirection_override.is_null()); 127 bool active_low; 128 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. 129 switch ((redirection_override->flags() & 0b11)) { 130 case 0: 131 active_low = false; 132 break; 133 case 1: 134 active_low = false; 135 break; 136 case 2: 137 ASSERT_NOT_REACHED(); 138 case 3: 139 active_low = true; 140 break; 141 } 142 143 bool trigger_level_mode; 144 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. 145 switch (((redirection_override->flags() >> 2) & 0b11)) { 146 case 0: 147 trigger_level_mode = false; 148 break; 149 case 1: 150 trigger_level_mode = false; 151 break; 152 case 2: 153 ASSERT_NOT_REACHED(); 154 case 3: 155 trigger_level_mode = true; 156 break; 157 } 158 configure_redirection_entry(redirection_override->gsi(), redirection_override->source() + IRQ_VECTOR_BASE, 0, false, active_low, trigger_level_mode, true, 0); 159 } 160} 161 162void IOAPIC::reset_all_redirection_entries() const 163{ 164 InterruptDisabler disabler; 165 for (size_t index = 0; index < m_redirection_entries; index++) 166 reset_redirection_entry(index); 167} 168 169void IOAPIC::hard_disable() 170{ 171 InterruptDisabler disabler; 172 reset_all_redirection_entries(); 173 IRQController::hard_disable(); 174} 175 176void IOAPIC::reset_redirection_entry(int index) const 177{ 178 InterruptDisabler disabler; 179 configure_redirection_entry(index, 0, 0, false, false, false, true, 0); 180} 181 182void IOAPIC::configure_redirection_entry(int index, u8 interrupt_vector, u8 delivery_mode, bool logical_destination, bool active_low, bool trigger_level_mode, bool masked, u8 destination) const 183{ 184 ASSERT((u32)index < m_redirection_entries); 185 u32 redirection_entry1 = interrupt_vector | (delivery_mode & 0b111) << 8 | logical_destination << 11 | active_low << 13 | trigger_level_mode << 15 | masked << 16; 186 u32 redirection_entry2 = destination << 24; 187 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry1); 188#ifdef IOAPIC_DEBUG 189 dbgprintf("IOAPIC Value: 0x%x\n", read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET)); 190#endif 191 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET + 1, redirection_entry2); 192#ifdef IOAPIC_DEBUG 193 dbgprintf("IOAPIC Value: 0x%x\n", read_register((index << 1) + 0x11)); 194#endif 195} 196 197void IOAPIC::mask_all_redirection_entries() const 198{ 199 InterruptDisabler disabler; 200 for (size_t index = 0; index < m_redirection_entries; index++) 201 mask_redirection_entry(index); 202} 203 204void IOAPIC::mask_redirection_entry(u8 index) const 205{ 206 ASSERT((u32)index < m_redirection_entries); 207 u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) | (1 << 16); 208 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry); 209} 210 211bool IOAPIC::is_redirection_entry_masked(u8 index) const 212{ 213 ASSERT((u32)index < m_redirection_entries); 214 return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & (1 << 16)) != 0; 215} 216 217void IOAPIC::unmask_redirection_entry(u8 index) const 218{ 219 ASSERT((u32)index < m_redirection_entries); 220 u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET); 221 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry & ~(1 << 16)); 222} 223 224bool IOAPIC::is_vector_enabled(u8 interrupt_vector) const 225{ 226 InterruptDisabler disabler; 227 return is_redirection_entry_masked(interrupt_vector); 228} 229 230u8 IOAPIC::read_redirection_entry_vector(u8 index) const 231{ 232 ASSERT((u32)index < m_redirection_entries); 233 return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & 0xFF); 234} 235 236int IOAPIC::find_redirection_entry_by_vector(u8 vector) const 237{ 238 InterruptDisabler disabler; 239 for (size_t index = 0; index < m_redirection_entries; index++) { 240 if (read_redirection_entry_vector(index) == (vector + IRQ_VECTOR_BASE)) 241 return index; 242 } 243 return -1; 244} 245 246void IOAPIC::disable(u8 interrupt_vector) 247{ 248 InterruptDisabler disabler; 249 int index = find_redirection_entry_by_vector(interrupt_vector); 250 if (index == (-1)) { 251 map_interrupt_redirection(interrupt_vector); 252 index = find_redirection_entry_by_vector(interrupt_vector); 253 } 254 ASSERT(index != (-1)); 255 mask_redirection_entry(index); 256} 257 258void IOAPIC::enable(u8 interrupt_vector) 259{ 260 InterruptDisabler disabler; 261 int index = find_redirection_entry_by_vector(interrupt_vector); 262 if (index == (-1)) { 263 map_interrupt_redirection(interrupt_vector); 264 index = find_redirection_entry_by_vector(interrupt_vector); 265 } 266 ASSERT(index != (-1)); 267 unmask_redirection_entry(index); 268} 269 270void IOAPIC::eoi(u8) const 271{ 272 InterruptDisabler disabler; 273 APIC::eoi(); 274} 275 276u16 IOAPIC::get_isr() const 277{ 278 InterruptDisabler disabler; 279 ASSERT_NOT_REACHED(); 280} 281 282u16 IOAPIC::get_irr() const 283{ 284 InterruptDisabler disabler; 285 ASSERT_NOT_REACHED(); 286} 287 288void IOAPIC::write_register(u32 index, u32 value) const 289{ 290 InterruptDisabler disabler; 291 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of(&m_physical_access_registers)), (PAGE_SIZE * 2), "IOAPIC Write", Region::Access::Read | Region::Access::Write); 292 auto& regs = *(volatile ioapic_mmio_regs*)region->vaddr().offset(offset_in_page(&m_physical_access_registers)).as_ptr(); 293 regs.select = index; 294 regs.window = value; 295#ifdef IOAPIC_DEBUG 296 dbgprintf("IOAPIC Writing, Value 0x%x @ offset 0x%x\n", regs.window, regs.select); 297#endif 298} 299u32 IOAPIC::read_register(u32 index) const 300{ 301 InterruptDisabler disabler; 302 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of(&m_physical_access_registers)), (PAGE_SIZE * 2), "IOAPIC Read", Region::Access::Read | Region::Access::Write); 303 auto& regs = *(volatile ioapic_mmio_regs*)region->vaddr().offset(offset_in_page(&m_physical_access_registers)).as_ptr(); 304 regs.select = index; 305#ifdef IOAPIC_DEBUG 306 dbgprintf("IOAPIC Reading, Value 0x%x @ offset 0x%x\n", regs.window, regs.select); 307#endif 308 return regs.window; 309} 310}