1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
#![no_std]
use log::debug;
use spin::Mutex;
use volatile::{Volatile, WriteOnly};
use zerocopy::FromBytes;
use memory::{PageTable, PhysicalAddress, PteFlags, allocate_pages, allocate_frames_at, BorrowedMappedPages, Mutable};
use atomic_linked_list::atomic_map::{AtomicMap, AtomicMapIter};
use apic::ApicId;
/// The system-wide list of all `IoApic`s, of which there is usually one,
/// but larger systems can have multiple IoApic chips.
static IOAPICS: AtomicMap<u8, Mutex<IoApic>> = AtomicMap::new();
/// Returns an iterator over the list of `IoApic`s.
pub fn get_ioapics() -> AtomicMapIter<'static, u8, Mutex<IoApic>> {
IOAPICS.iter()
}
/// If an `IoApic` with the given `id` exists, then lock it (acquire its Mutex)
/// and return the locked `IoApic`.
pub fn get_ioapic(ioapic_id: u8) -> Option<&'static Mutex<IoApic>> {
IOAPICS.get(&ioapic_id)
}
#[derive(FromBytes)]
#[repr(C)]
struct IoApicRegisters {
/// Chooses which IoApic register the following access will write to or read from.
register_index: WriteOnly<u32>,
_padding0: [u32; 3],
/// The register containing the actual data that we want to read or write.
register_data: Volatile<u32>,
_padding1: [u32; 3],
}
/// Each IoApic handles a maximum of 24 interrupt redirection entries.
const INTERRUPT_ENTRIES_PER_IOAPIC: u32 = 24;
/// A representation of an IoApic (x86-specific interrupt chip for I/O devices).
pub struct IoApic {
regs: BorrowedMappedPages<IoApicRegisters, Mutable>,
/// The ID of this IoApic.
pub id: u8,
/// not yet used.
_phys_addr: PhysicalAddress,
/// The first global interrupt number handled by this IoApic.
/// Each IoApic only handles 24 interrupts,
/// so the last interrupt number supported by thie IoApic is `gsi_base + 23`.
gsi_base: u32,
}
impl IoApic {
/// Creates a new IoApic struct from the given `id`, `PhysicalAddress`, and `gsi_base`,
/// and then adds it to the system-wide list of all IOAPICs.
pub fn create(page_table: &mut PageTable, id: u8, phys_addr: PhysicalAddress, gsi_base: u32) -> Result<(), &'static str> {
let new_page = allocate_pages(1).ok_or("IoApic::new(): couldn't allocate_pages!")?;
let frame = allocate_frames_at(phys_addr, 1).map_err(|_e| "Couldn't allocate physical frame for IOAPIC")?;
let ioapic_mapped_page = page_table.map_allocated_pages_to(
new_page,
frame,
PteFlags::new().valid(true).writable(true).device_memory(true),
)?;
let ioapic_regs = ioapic_mapped_page.into_borrowed_mut(0).map_err(|(_mp, err)| err)?;
let ioapic = IoApic {
regs: ioapic_regs,
id,
_phys_addr: phys_addr,
gsi_base,
};
debug!("Created new IoApic, id: {}, gsi_base: {}, phys_addr: {:#X}", id, gsi_base, phys_addr);
IOAPICS.insert(id, Mutex::new(ioapic));
Ok(())
}
/// Returns whether this IoApic handles the given `irq_num`, i.e.,
/// whether it's within the range of IRQs handled by this `IoApic`.
pub fn handles_irq(&self, irq_num: u32) -> bool {
(irq_num >= self.gsi_base) &&
(irq_num < (self.gsi_base + INTERRUPT_ENTRIES_PER_IOAPIC))
}
fn read_reg(&mut self, register_index: u32) -> u32 {
// to read from an IoApic reg, we first write which register we want to read from,
// then we read the value from it in the next register
self.regs.register_index.write(register_index);
self.regs.register_data.read()
}
fn write_reg(&mut self, register_index: u32, value: u32) {
// to write to an IoApic reg, we first write which register we want to write to,
// then we write the value to it in the next register
self.regs.register_index.write(register_index);
self.regs.register_data.write(value);
}
/// gets this IoApic's id.
pub fn id(&mut self) -> u32 {
self.read_reg(0x0)
}
/// gets this IoApic's version.
pub fn version(&mut self) -> u32 {
self.read_reg(0x1)
}
/// gets this IoApic's arbitration id.
pub fn arbitration_id(&mut self) -> u32 {
self.read_reg(0x2)
}
/// Masks (disables) the given IRQ line.
/// NOTE: this function is UNTESTED!
pub fn mask_irq(&mut self, irq: u8) {
let irq_reg: u32 = 0x10 + (2 * irq as u32);
let direction = self.read_reg(irq_reg);
self.write_reg(irq_reg, direction | (1 << 16));
}
/// Set IRQ to an interrupt vector.
///
/// # Arguments
/// * `ioapic_irq`: the IRQ number that this interrupt will trigger on this IoApic.
/// * `apic_id`: the ID of the Local APIC, i.e., the CPU, that should handle this interrupt.
/// * `irq_vector`: the system-wide IRQ vector number,
/// which after remapping is from 0x20 to 0x2F
/// (see [`interrupts::IRQ_BASE_OFFSET`](../interrupts/constant.IRQ_BASE_OFFSET.html)).
/// For example, 0x20 is the PIT timer, 0x21 is the PS2 keyboard, etc.
///
/// # Return
/// * Returns `Ok` upon success
/// * Returns `Err` if the given `ApicId` value exceeds the bounds of `u8`, i.e.,
/// if it is larger than 255.
/// This is because the IOAPIC only supports redirecting interrupts to APICs
/// with IDs that fit within 8-bit values.
pub fn set_irq(
&mut self,
ioapic_irq: u8,
apic_id: ApicId,
irq_vector: u8,
) -> Result<(), &'static str> {
if apic_id.value() > u8::MAX as u32 {
log::error!("Cannot set IOAPIC redirection table {} -> {} for APIC ID {} larger than 255",
ioapic_irq, irq_vector, apic_id.value(),
);
return Err("Cannot set IOAPIC redirection table entry for APIC ID larger than 255")
}
let low_index: u32 = 0x10 + ((ioapic_irq as u32) * 2);
let high_index: u32 = low_index + 1;
let mut high = self.read_reg(high_index);
high &= !0xff000000;
high |= apic_id.value() << 24;
self.write_reg(high_index, high);
let mut low = self.read_reg(low_index);
// Clear mask, enabling this interrupt
low &= !(1<<16);
// Use physical destination mode, not logical destination mode
low &= !(1<<11);
// Set the delivery mode to Fixed
low &= !0x700;
// Set the lowest 8 bits, which correspond to the IRQ vector.
low &= !0xff;
low |= irq_vector as u32;
self.write_reg(low_index, low);
Ok(())
}
}