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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
//! 82093AA I/O Advanced Programmable Interrupt Controller (IOAPIC) driver
//!
//! The IO-APIC is used to dispatch external device and inter-process
//! interruptions to the correct CPU.
//!
//! The documentation for the IO-APIC can be found [here](http://web.archive.org/web/20161130153145/http://download.intel.com/design/chipsets/datashts/29056601.pdf).

use sunrise_libutils::io::Mmio;
use sunrise_libutils::io::Io;
use bit_field::BitField;

use bitfield::bitfield;
use crate::frame_allocator::PhysicalMemRegion;
use crate::mem::PhysicalAddress;
use crate::paging::MappingAccessRights;
use crate::paging::kernel_memory::get_kernel_memory;
use core::cell::UnsafeCell;
use core::fmt;

/// Internal IO-APIC registers.
///
/// IO-APIC uses a pair of addr/data registers. The address point to DWORDs
/// instead of bytes, meaning address 1 points to byte address 4.
#[repr(C)]
#[derive(Debug)]
struct IoApicInternal {
    /// Address register.
    addr_reg: Mmio<u32>,
    /// 12 bytes of padding.
    padding: [u8; 0x10-4],
    /// Data register.
    data_reg: Mmio<u32>,
}

/// See [module level documentation](crate::devices::ioapic)
pub struct IoApic {
    /// Pointer to the IO-APIC device registers.
    internal: &'static UnsafeCell<IoApicInternal>,
    /// Start of the IRQ range handled by this IO-APIC device. Systems may have
    /// more than one IO-APIC if they need to handle more than 24 IRQs.
    interrupt_base: u32,
    /// Number of entries this IO-APIC device can handled. Cached.
    redirection_entry_count: u32,
}

/// Specifies how the APICs listed in the destination field should act upon
/// reception of this signal. Note that certain Delivery Modes only operate as
/// intended when used in conjunction with a specific trigger Mode.
#[derive(Debug, Clone, Copy)]
pub enum DeliveryMode {
    /// Deliver the signal on the INTR signal of all processor cores listed in
    /// the destination. Trigger Mode for "fixed" Delivery Mode can be edge or
    /// level.
    Fixed,
    /// Deliver the signal on the INTR signal of the processor core that is
    /// executing at the lowest priority among all the processors listed in the
    /// specified destination. Trigger Mode for "lowest priority". Delivery Mode
    /// can be edge or level.
    LowestPriority,
    /// System Management Interrupt. A delivery mode equal to SMI requires an
    /// edge trigger mode. The vector information is ignored but must be
    /// programmed to all zeroes for future compatibility.
    SMI,
    /// Deliver the signal on the NMI signal of all processor cores listed in the
    /// destination. Vector information is ignored. NMI is treated as an edge
    /// triggered interrupt, even if it is programmed as a level triggered
    /// interrupt. For proper operation, this redirection table entry must be
    /// programmed to "edge" triggered interrupt.
    NMI,
    /// Deliver the signal to all processor cores listed in the destination by
    /// asserting the INIT signal. All addressed local APICs will assume their
    /// INIT state. INIT is always treated as an edge triggered interrupt, even
    /// if programmed otherwise. For proper operation, this redirection table
    /// entry must be programmed to "edge" triggered interrupt.
    INIT,
    /// Deliver the signal to the INTR signal of all processor cores listed in
    /// the destination as an interrupt that originated in an externally
    /// connected (8259A-compatible) interrupt controller. The INTA cycle that
    /// corresponds to this ExtINT delivery is routed to the external controller
    /// that is expected to supply the vector. A Delivery Mode of "ExtINT"
    /// requires an edge trigger mode.
    ExtINT,
    /// Unknown delivery mode encountered.
    Unknown(u64)
}

impl From<DeliveryMode> for u64 {
    fn from(mode: DeliveryMode) -> u64 {
        match mode {
            DeliveryMode::Fixed          => 0b000,
            DeliveryMode::LowestPriority => 0b001,
            DeliveryMode::SMI            => 0b010,
            // RESERVED                  => 0b011,
            DeliveryMode::NMI            => 0b100,
            DeliveryMode::INIT           => 0b101,
            // RESERVED                  => 0b110,
            DeliveryMode::ExtINT         => 0b111,
            DeliveryMode::Unknown(val)   => val,
        }
    }
}

impl From<u64> for DeliveryMode {
    fn from(mode: u64) -> DeliveryMode {
        match mode {
            0b000 => DeliveryMode::Fixed,
            0b001 => DeliveryMode::LowestPriority,
            0b010 => DeliveryMode::SMI,
            // 0b011 RESERVED
            0b100 => DeliveryMode::NMI,
            0b101 => DeliveryMode::INIT,
            // 0b110 RESERVED
            0b111 => DeliveryMode::ExtINT,
            val => DeliveryMode::Unknown(val),
        }
    }
}

// TODO: IoApic should not be Sync!
// BODY: IoApic manually implements Sync to allow it to be stored in a static.
// BODY: This is, however, wildly unsafe. It "works" today because we only have
// BODY: a single CPU and no preemption. We probably should store it in a Mutex.
unsafe impl Send for IoApic {}
unsafe impl Sync for IoApic {}

bitfield! {
    /// Description of a Redirection Entry in the IO-APIC. Unlike IRQ pins of the
    /// 8259A, the notion of interrupt priority is completely unrelated to the
    /// position of the physical interrupt input signal on the APIC. Instead,
    /// software determines the vector (and therefore the priority) for each
    /// corresponding interrupt input signal. For each interrupt signal, the
    /// operating system can also specify the signal polarity (low active or high
    /// active), whether the interrupt is signaled as edges or levels, as well as
    /// the destination and delivery mode of the interrupt. The information in
    /// the redirection table is used to translate the corresponding interrupt
    /// pin information into an inter-APIC message.
    ///
    /// The IOAPIC responds to an edge triggered interrupt as long as the
    /// interrupt is wider than one CLK cycle. The interrupt input is
    /// asynchronous; thus, setup and hold times need to be guaranteed for at
    /// lease one rising edge of the CLK input. Once the interrupt is detected, a
    /// delivery status bit internal to the IOAPIC is set. A new edge on that
    /// Interrupt input pin will not be recongnized until the IOAPIC Unit
    /// broadcasts the corresponding message over the APIC bus and the message
    /// has been accepted by the destination(s) specified in the destination
    /// field. That new edge only results in a new invocation of the handler if
    /// its acceptance by the destination APIC causes the Interrupt Request
    /// Register bit to go from 0 to 1. (In other words, if the interrupt wasn't
    /// already pending at the destination.)
    pub struct RedirectionEntry(u64);
    impl Debug;
    /// The vector field is an 8 bit field containing the interrupt vector for
    /// this interrupt. Vector values range from 0x10 to 0xFE.
    pub interrupt_vector, set_interrupt_vector: 7, 0;
    /// The Delivery Mode is a 3 bit field that specifies how the APICs listed in
    /// the destination field should act upon reception of this signal. Note that
    /// certain Delivery Modes only operate as intended when used in conjunction
    /// with a specific trigger Mode. These restrictions are indicated in the
    /// documentation of [Deliverymode].
    pub from into DeliveryMode, delivery_mode, set_delivery_mode: 10, 8;
    /// This field determines the interpretation of the Destination field. When
    /// it is `false` (physical mode), a destination APIC is identified by its
    /// ID. Bits 56 through 59 of the Destination field specify the 4 bit APIC
    /// ID.
    ///
    /// When this field is `true` (logicalmode), destinations are identified by
    /// matching on the logical destination under the control of the Destination
    /// Format Register and Logical Destination Register in each Local APIC.
    pub destination_mode, set_destination_mode: 11;
    /// The Delivery Status bit contains the current status of the delivery of
    /// this interrupt. Delivery Status is read-only and writes to this bit (as
    /// part of a 32 bitword) do not effect this bit.
    ///
    /// `false` means IDLE (there is currently no activity for this interrupt).
    ///
    /// `true` means SendPending (the interrupt has been injected but its
    /// delivery is temporarily held up due to the APIC bus being busy or the
    /// inability of the receiving APIC unit to accept that interrupt at that
    /// time).
    pub delivery_status, _: 12;
    /// This bit specifies the polarity of the interrupt signal. `false` means
    /// High active, `true` means Low active.
    pub interrupt_input_pin_polarity, set_interrupt_input_pin_polarity: 13;
    /// This bit is used for level triggered interrupts. Its meaning is undefined
    /// for edge triggered interrupts. For level triggered interrupts, this bit
    /// is set to `true` when local APIC(s) accept the level interrupt sent by
    /// the IOAPIC. The Remote IRR bit is set to `false` when an EOI message with
    /// a matching interrupt vector is received from a local APIC.
    pub remote_irr, _: 14;
    /// The trigger mode field indicates the type of signal on the interrupt pin
    /// that triggers an interrupt. `true` means Level sensitive, `false` means
    /// Edge sensitive.
    pub trigger_mode, set_trigger_mode: 15;
    /// When this bit is 1, the interrupt signal is masked. Edge-sensitive
    /// interrupts signaled on a masked interrupt pin are ignored (i.e., not
    /// delivered or held pending). Level-asserts or negates occurring on a
    /// masked level-sensitive pin are also ignored and have no side effects.
    /// Changing the mask bit from unmasked to masked after the interrupt is
    /// accepted by a local APIC has no effect on that interrupt. This behavior
    /// is identical to the case where the device withdraws the interrupt before
    /// that interrupt is posted to the processor. It is software's
    /// responsibility to handle the case where the mask bit is set after the
    /// interrupt message has been accepted by a local APIC unit but before the
    /// interrupt is dispensed to the processor. When this bit is 0, the
    /// interrupt is not masked. An edge or level on an interrupt pin that is not
    /// masked results in the delivery of the interrupt to the destination.
    pub interrupt_mask, set_interrupt_mask: 16;
    /// If the Destination Mode of this entry is Physical Mode, the first 4 bits
    /// contain an APIC ID. If Logical Mode is selected, the Destination Field
    /// potentially defines a set of processors. The Destination Field specify
    /// the logical destination address.
    pub destination_field, set_destination_field: 63, 53;
}

impl IoApic {
    /// Creates a new IO-APIC device at the given Physical Address, mapping
    /// interrupts (typically 24) starting from `interrupt_base`.
    /// 
    /// The IOAPIC will be initialized with sane values: every redirection entry
    /// will be masked, their interrupt vector set to 0x20 + interrupt_idx, and
    /// their destination CPU to `root_cpu_id`.
    ///
    /// # Safety
    ///
    /// Address should point to an IO-APIC, and must not be shared.
    pub unsafe fn new(address: PhysicalAddress, interrupt_base: u32, root_cpu_id: u32) -> IoApic {
        // TODO: Avoid mapping the same MMIO pages multiple times.
        // BODY: Currently, if we need to map distinct MMIO regions sharing the
        // BODY: same page, we do multiple mapping. This is wasteful of address
        // BODY: space, which is a relatively scarce resource.
        // BODY:
        // BODY: It might be a good idea to make an MMIO manager that hands out
        // BODY: references to the same mapping (with different offsets) when
        // BODY: a single page is shared.
        if address.floor() != (address + 8).floor() {
            panic!("Weird MMIO.")
        }

        let mmio = PhysicalMemRegion::on_fixed_mmio(address.floor(), 0x1000).unwrap();

        let vaddr = get_kernel_memory().map_phys_region(mmio, MappingAccessRights::k_rw());

        let vaddr_start = vaddr + (address - address.floor());

        let ioapic_internal = (vaddr_start.addr() as *const UnsafeCell<IoApicInternal>).as_ref().unwrap();

        let mut ret = IoApic {
            internal: ioapic_internal,
            interrupt_base,
            redirection_entry_count: 0
        };

        // Cache redirection entry count since we'll use it quite often.
        ret.redirection_entry_count = ret.read(1).get_bits(16..24);

        for i in 0..ret.redirection_entry_count() {
            let mut entry = ret.redirection_entry(i as u8);
            entry.set_interrupt_mask(true);
            entry.set_interrupt_vector((0x20 + i + interrupt_base).into());
            entry.set_destination_field(root_cpu_id.into());
            ret.set_redirection_entry(i as u8, entry);
        }

        info!("IOAPIC at {}({}) handles irq {}-{}", address, vaddr, interrupt_base, interrupt_base + ret.redirection_entry_count);
        ret
    }

    /// Reads an u32 at the specified DWORD offset.
    fn read(&self, offset: u32) -> u32 {
        unsafe {
            (*self.internal.get()).addr_reg.write(offset);
            (*self.internal.get()).data_reg.read()
        }
    }

    /// Writes an u32 at the specified DWORD offset.
    fn write(&self, offset: u32, data: u32) {
        unsafe {
            (*self.internal.get()).addr_reg.write(offset);
            (*self.internal.get()).data_reg.write(data);
        }
    }

    /// This register contains the 4-bit APIC ID. The ID serves as a physical
    /// name of the IOAPIC. All APIC devices using the APIC bus should have a
    /// unique APIC ID.
    pub fn ioapic_id(&self) -> u32 {
        self.read(0).get_bits(24..28)
    }

    /// Gets the version number of this IO-APIC device. This is expected to be
    /// 0x11 or 0x20.
    pub fn version(&self) -> u8 {
        self.read(1).get_bits(0..8) as u8
    }

    /// Start of the IRQ range handled by this IO-APIC device. Systems may have
    /// more than one IO-APIC if they need to handle more than 24 IRQs.
    pub fn interrupt_base(&self) -> u32 {
        self.interrupt_base
    }

    /// Gets the number of redirection entries in the I/O Redirection Table. This
    /// is expected to be 24, although more recent I/O-APIC devices may have
    /// more.
    pub fn redirection_entry_count(&self) -> u32 {
        self.redirection_entry_count + 1
    }

    /// Gets the bus arbitration priority for the IOAPIC. This register is loaded
    /// when the IOAPIC ID Register is written.
    ///
    /// The APIC uses a one wire arbitration to win bus ownership. A rotating
    /// priority scheme is used for arbitration. The winner of the arbitration
    /// becomes the lowest priority agent and assumes an arbitration ID of 0.
    ///
    /// All other agents, except the agent whose arbitration ID is 15, increment
    /// their arbitration IDs by one. The agent whose ID was 15 takes the
    /// winner's arbitration ID and increments it by one. Arbitration IDs are
    /// changed (incremented or asssumed) only for messages that are transmitted
    /// successfully (except, in the case of low priority messages where
    /// Arbitration ID is changed even if message was not successfully
    /// transmitted). A message is transmitted successfully if no checksum error
    /// or acceptance error is reported for that message. The register is always
    /// loaded with IOAPIC ID during a "level triggered INIT with de-assert"
    /// message.
    pub fn arbitration_id(&self) -> u32 {
        self.read(2).get_bits(24..28)
    }

    /// Gets the [RedirectionEntry] configuration of the specified pin.
    pub fn redirection_entry(&self, entry: u8) -> RedirectionEntry {
        assert!(u32::from(entry) < self.redirection_entry_count(), "Invalid entry {:#04x}", entry);
        let low = self.read(0x10 + u32::from(entry * 2));
        let hi = self.read(0x10 + u32::from(entry * 2) + 1);

        RedirectionEntry(*0u64
            .set_bits(0..32, low.into())
            .set_bits(32..64, hi.into()))
    }

    /// Configure the given pin with a [RedirectionEntry].
    pub fn set_redirection_entry(&self, entry: u8, data: RedirectionEntry) {
        assert!(u32::from(entry) < self.redirection_entry_count(), "Invalid entry {:#04x}", entry);
        self.write(0x10 + u32::from(entry * 2), data.0.get_bits(0..32) as u32);
        self.write(0x10 + u32::from(entry * 2) + 1, data.0.get_bits(32..64) as u32);
    }
}

impl fmt::Debug for IoApic {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

        #[allow(clippy::missing_docs_in_private_items)]
        struct RedirectionEntries<'a>(&'a IoApic);
        impl<'a> fmt::Debug for RedirectionEntries<'a> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                f.debug_list()
                    .entries((0..self.0.redirection_entry_count())
                             .map(|v| self.0.redirection_entry(v as u8)))
                    .finish()
            }
        }

        f.debug_struct("IoApic")
            .field("ioapic_id", &self.ioapic_id())
            .field("version", &self.version())
            .field("interrupt_base", &self.interrupt_base())
            .field("arbitration_id", &self.arbitration_id())
            .field("redirection_entries", &RedirectionEntries(self))
            .finish()
    }
}