Serenity Operating System
at hosted 249 lines 7.5 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 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 <AK/Assertions.h> 28#include <AK/Types.h> 29#include <Kernel/Arch/i386/CPU.h> 30#include <Kernel/Interrupts/GenericInterruptHandler.h> 31#include <Kernel/Interrupts/PIC.h> 32#include <LibBareMetal/IO.h> 33 34namespace Kernel { 35 36// The slave 8259 is connected to the master's IRQ2 line. 37// This is really only to enhance clarity. 38#define SLAVE_INDEX 2 39 40#define PIC0_CTL 0x20 41#define PIC0_CMD 0x21 42#define PIC1_CTL 0xA0 43#define PIC1_CMD 0xA1 44 45#define ICW1_ICW4 0x01 /* ICW4 (not) needed */ 46#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ 47#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ 48#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ 49#define ICW1_INIT 0x10 /* Initialization - required! */ 50 51#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ 52#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ 53#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ 54#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ 55#define ICW4_SFNM 0x10 /* Special fully nested (not) */ 56 57bool inline static is_all_masked(u16 reg) 58{ 59 return reg == 0xFFFF; 60} 61 62bool PIC::is_enabled() const 63{ 64 return !is_all_masked(m_cached_irq_mask) && !is_hard_disabled(); 65} 66 67void PIC::disable(const GenericInterruptHandler& handler) 68{ 69 InterruptDisabler disabler; 70 ASSERT(!is_hard_disabled()); 71 ASSERT(handler.interrupt_number() >= gsi_base() && handler.interrupt_number() < interrupt_vectors_count()); 72 u8 irq = handler.interrupt_number(); 73 if (m_cached_irq_mask & (1 << irq)) 74 return; 75 u8 imr; 76 if (irq & 8) { 77 imr = IO::in8(PIC1_CMD); 78 imr |= 1 << (irq & 7); 79 IO::out8(PIC1_CMD, imr); 80 } else { 81 imr = IO::in8(PIC0_CMD); 82 imr |= 1 << irq; 83 IO::out8(PIC0_CMD, imr); 84 } 85 m_cached_irq_mask |= 1 << irq; 86} 87 88PIC::PIC() 89{ 90 initialize(); 91} 92 93void PIC::spurious_eoi(const GenericInterruptHandler& handler) const 94{ 95 ASSERT(handler.type() == HandlerType::SpuriousInterruptHandler); 96 if (handler.interrupt_number() == 7) 97 return; 98 if (handler.interrupt_number() == 15) { 99 IO::in8(PIC1_CMD); /* dummy read */ 100 IO::out8(PIC0_CTL, 0x60 | (2)); 101 } 102} 103 104bool PIC::is_vector_enabled(u8 irq) const 105{ 106 return m_cached_irq_mask & (1 << irq); 107} 108 109void PIC::enable(const GenericInterruptHandler& handler) 110{ 111 InterruptDisabler disabler; 112 ASSERT(!is_hard_disabled()); 113 ASSERT(handler.interrupt_number() >= gsi_base() && handler.interrupt_number() < interrupt_vectors_count()); 114 enable_vector(handler.interrupt_number()); 115} 116 117void PIC::enable_vector(u8 irq) 118{ 119 InterruptDisabler disabler; 120 ASSERT(!is_hard_disabled()); 121 if (!(m_cached_irq_mask & (1 << irq))) 122 return; 123 u8 imr; 124 if (irq & 8) { 125 imr = IO::in8(PIC1_CMD); 126 imr &= ~(1 << (irq & 7)); 127 IO::out8(PIC1_CMD, imr); 128 } else { 129 imr = IO::in8(PIC0_CMD); 130 imr &= ~(1 << irq); 131 IO::out8(PIC0_CMD, imr); 132 } 133 m_cached_irq_mask &= ~(1 << irq); 134} 135 136void PIC::eoi(const GenericInterruptHandler& handler) const 137{ 138 InterruptDisabler disabler; 139 ASSERT(!is_hard_disabled()); 140 u8 irq = handler.interrupt_number(); 141 ASSERT(irq >= gsi_base() && irq < interrupt_vectors_count()); 142 if ((1 << irq) & m_cached_irq_mask) { 143 spurious_eoi(handler); 144 return; 145 } 146 eoi_interrupt(irq); 147} 148 149void PIC::eoi_interrupt(u8 irq) const 150{ 151 if (irq & 8) { 152 IO::in8(PIC1_CMD); /* dummy read */ 153 IO::out8(PIC1_CTL, 0x60 | (irq & 7)); 154 IO::out8(PIC0_CTL, 0x60 | (2)); 155 return; 156 } 157 IO::in8(PIC0_CMD); /* dummy read */ 158 IO::out8(PIC0_CTL, 0x60 | irq); 159} 160 161void PIC::complete_eoi() const 162{ 163 IO::out8(PIC1_CTL, 0x20); 164 IO::out8(PIC0_CTL, 0x20); 165} 166 167void PIC::hard_disable() 168{ 169 InterruptDisabler disabler; 170 remap(0x20); 171 IO::out8(PIC0_CMD, 0xff); 172 IO::out8(PIC1_CMD, 0xff); 173 m_cached_irq_mask = 0xffff; 174 IRQController::hard_disable(); 175} 176 177void PIC::remap(u8 offset) 178{ 179 /* ICW1 (edge triggered mode, cascading controllers, expect ICW4) */ 180 IO::out8(PIC0_CTL, ICW1_INIT | ICW1_ICW4); 181 IO::out8(PIC1_CTL, ICW1_INIT | ICW1_ICW4); 182 183 /* ICW2 (upper 5 bits specify ISR indices, lower 3 idunno) */ 184 IO::out8(PIC0_CMD, offset); 185 IO::out8(PIC1_CMD, offset + 0x08); 186 187 /* ICW3 (configure master/slave relationship) */ 188 IO::out8(PIC0_CMD, 1 << SLAVE_INDEX); 189 IO::out8(PIC1_CMD, SLAVE_INDEX); 190 191 /* ICW4 (set x86 mode) */ 192 IO::out8(PIC0_CMD, ICW4_8086); 193 IO::out8(PIC1_CMD, ICW4_8086); 194 195 // Mask -- start out with all IRQs disabled. 196 IO::out8(PIC0_CMD, 0xff); 197 IO::out8(PIC1_CMD, 0xff); 198 m_cached_irq_mask = 0xffff; 199 200 // ...except IRQ2, since that's needed for the master to let through slave interrupts. 201 enable_vector(2); 202} 203 204void PIC::initialize() 205{ 206 /* ICW1 (edge triggered mode, cascading controllers, expect ICW4) */ 207 IO::out8(PIC0_CTL, ICW1_INIT | ICW1_ICW4); 208 IO::out8(PIC1_CTL, ICW1_INIT | ICW1_ICW4); 209 210 /* ICW2 (upper 5 bits specify ISR indices, lower 3 idunno) */ 211 IO::out8(PIC0_CMD, IRQ_VECTOR_BASE); 212 IO::out8(PIC1_CMD, IRQ_VECTOR_BASE + 0x08); 213 214 /* ICW3 (configure master/slave relationship) */ 215 IO::out8(PIC0_CMD, 1 << SLAVE_INDEX); 216 IO::out8(PIC1_CMD, SLAVE_INDEX); 217 218 /* ICW4 (set x86 mode) */ 219 IO::out8(PIC0_CMD, ICW4_8086); 220 IO::out8(PIC1_CMD, ICW4_8086); 221 222 // Mask -- start out with all IRQs disabled. 223 IO::out8(PIC0_CMD, 0xff); 224 IO::out8(PIC1_CMD, 0xff); 225 226 // ...except IRQ2, since that's needed for the master to let through slave interrupts. 227 enable_vector(2); 228 229 klog() << "PIC(i8259): cascading mode, vectors 0x" << String::format("%x", IRQ_VECTOR_BASE) << "-0x" << String::format("%x", IRQ_VECTOR_BASE + 0xf); 230} 231 232u16 PIC::get_isr() const 233{ 234 IO::out8(PIC0_CTL, 0x0b); 235 IO::out8(PIC1_CTL, 0x0b); 236 u8 isr0 = IO::in8(PIC0_CTL); 237 u8 isr1 = IO::in8(PIC1_CTL); 238 return (isr1 << 8) | isr0; 239} 240 241u16 PIC::get_irr() const 242{ 243 IO::out8(PIC0_CTL, 0x0a); 244 IO::out8(PIC1_CTL, 0x0a); 245 u8 irr0 = IO::in8(PIC0_CTL); 246 u8 irr1 = IO::in8(PIC1_CTL); 247 return (irr1 << 8) | irr0; 248} 249}