From 2d0d7dd3fb8f982c3033538b801aebabb53fa67d Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 23 May 2019 00:24:15 +0000 Subject: [PATCH 1/4] Kernel: Use IOAPIC and LAPIC instead of the PIC PIC is so 1980s, IOAPIC is so 200s. Moving to the APIC provides us with two immediate and one long term benefit: 1. It allows us to use MSI for PCI interruptions, which should cleanly resolve the problem that level triggered interrupts don't work well in the context of a microkernel. 2. It provides us with a nice speed boost compared to the PIC, what with the IO-APIC being a lot cleaner to use. 3. It will eventually allow SMP. The IO-APIC is mostly wired in the same way the PIC is. However, under QEmu, there's a bug that prevents using IRQ line 0, so we have to move the timer to another IRQ line (we use 2 since that's the recommended line for timers). --- Cargo.lock | 14 +- kernel/Cargo.toml | 2 +- kernel/src/devices/apic.rs | 723 +++++++++++++++++++++++++++++++++++ kernel/src/devices/hpet.rs | 49 ++- kernel/src/devices/ioapic.rs | 370 ++++++++++++++++++ kernel/src/devices/mod.rs | 6 + kernel/src/devices/pit.rs | 7 + kernel/src/event.rs | 11 +- kernel/src/i386/interrupt.rs | 146 +++++++ kernel/src/i386/mod.rs | 1 + kernel/src/interrupts/irq.rs | 8 +- kernel/src/interrupts/mod.rs | 3 +- kernel/src/main.rs | 4 +- 13 files changed, 1306 insertions(+), 38 deletions(-) create mode 100644 kernel/src/devices/apic.rs create mode 100644 kernel/src/devices/ioapic.rs create mode 100644 kernel/src/i386/interrupt.rs diff --git a/Cargo.lock b/Cargo.lock index 230b60c52..91dde385a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,7 +3,7 @@ [[package]] name = "acpi" version = "0.1.0" -source = "git+https://github.com/sunriseos/acpi.git#e7c80c0c43e7141100f384c590c09637ed3db4a7" +source = "git+https://github.com/sunriseos/acpi#38526dd0774003ca71c822b882b646dbfb7ee548" dependencies = [ "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -45,6 +45,11 @@ name = "bit_field" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bitfield" +version = "0.13.1" +source = "git+https://github.com/sunriseos/rust-bitfield#0fe12f3bf50d9afbbf2c73f627aad73558a679cf" + [[package]] name = "bitfield" version = "0.13.1" @@ -510,9 +515,9 @@ dependencies = [ name = "sunrise-kernel" version = "0.1.0" dependencies = [ - "acpi 0.1.0 (git+https://github.com/sunriseos/acpi.git)", + "acpi 0.1.0 (git+https://github.com/sunriseos/acpi)", "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitfield 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitfield 0.13.1 (git+https://github.com/sunriseos/rust-bitfield)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -725,12 +730,13 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum acpi 0.1.0 (git+https://github.com/sunriseos/acpi.git)" = "" +"checksum acpi 0.1.0 (git+https://github.com/sunriseos/acpi)" = "" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" +"checksum bitfield 0.13.1 (git+https://github.com/sunriseos/rust-bitfield)" = "" "checksum bitfield 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a260ed6b9f3ca16a4389390b1b1cd15a3bc0a9d3e63b1ef39f4978cec58a4e83" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index a2e014a80..0bb2864ab 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -29,7 +29,7 @@ log = "0.4.6" xmas-elf = "0.6.2" rustc-demangle = "0.1" failure = { version = "0.1", default-features = false, features = ["derive"] } -bitfield = "0.13" +bitfield = { git = "https://github.com/sunriseos/rust-bitfield" } mashup = "0.1.9" acpi = { git = "https://github.com/sunriseos/acpi.git" } diff --git a/kernel/src/devices/apic.rs b/kernel/src/devices/apic.rs new file mode 100644 index 000000000..f5cfecb00 --- /dev/null +++ b/kernel/src/devices/apic.rs @@ -0,0 +1,723 @@ +//! Local APIC driver. +//! +//! Specification can be found in Intel's System Programmer's Guide. The chapters +//! used as reference here will be from [this version] of the guide. +//! +//! [this version]: https://web.archive.org/web/20190212230443/https://software.intel.com/sites/default/files/managed/a4/60/325384-sdm-vol-3abcd.pdf + +use crate::frame_allocator::PhysicalMemRegion; +use crate::mem::PhysicalAddress; +use crate::paging::MappingAccessRights; +use sunrise_libutils::io::Io; +use crate::paging::PAGE_SIZE; +use crate::paging::kernel_memory::get_kernel_memory; +use core::cell::UnsafeCell; +use core::marker::PhantomData; +use core::fmt; +use bit_field::BitField; + +/// 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 { + /// Delivers the interrupt specified in the vector field. + Fixed, + /// Delivers an SMI interrupt to the processor core through the processor’s + /// local SMI signal path. When using this delivery mode, the vector field + /// should be set to 0x00 for future compatibility. + SMI, + /// Delivers an NMI interrupt to the processor. The vector information is + /// ignored. + NMI, + + /// Delivers an INIT request to the processor core, which causes the + /// processor to perform an INIT. When using this delivery mode, the vector + /// field should be set to 0x00 for future compatibility. Not supported for + /// the LVT CMCI register, the LVT thermal monitor register, or the LVT + /// performance counter register. + INIT, + /// Causes the processor to respond to the interrupt as if the interrupt + /// originated in an externally connected (8259A-compatible) interrupt + /// controller. A special INTA bus cycle corresponding to ExtINT, is routed + /// to the external controller. The external controller is expected to supply + /// the vector information. The APIC architecture supports only one ExtINT + /// source in a system, usually contained in the compatibility bridge. Only + /// one processor in the system should have an LVT entry configured to use + /// the ExtINT delivery mode. Not supported for the LVT CMCI register, the + /// LVT thermal monitor register, or the LVT performance counter register. + ExtINT, + /// Unknown delivery mode encountered. + Unknown(u32) +} + +impl From for u32 { + fn from(mode: DeliveryMode) -> u32 { + match mode { + DeliveryMode::Fixed => 0b000, + DeliveryMode::SMI => 0b010, + // RESERVED => 0b011, + DeliveryMode::NMI => 0b100, + DeliveryMode::INIT => 0b101, + // RESERVED => 0b110, + DeliveryMode::ExtINT => 0b111, + DeliveryMode::Unknown(val) => val, + } + } +} + +impl From for DeliveryMode { + fn from(mode: u32) -> DeliveryMode { + match mode { + 0b000 => DeliveryMode::Fixed, + 0b010 => DeliveryMode::SMI, + // 0b011 RESERVED + 0b100 => DeliveryMode::NMI, + 0b101 => DeliveryMode::INIT, + // 0b110 RESERVED + 0b111 => DeliveryMode::ExtINT, + val => DeliveryMode::Unknown(val), + } + } +} + +/// Selects the Timer Mode of the LVT Timer. +#[derive(Debug, Clone, Copy)] +enum TimerMode { + /// One-shot mode using a count-down value. + OneShot, + /// Periodic mode reloading a count-down value. + Periodic, + /// TSC-Deadline mode using absolute target value in IA32_TSC_DEADLINE MSR. + TscDeadline, + /// Reserved value, might be used in later revision. + Reserved +} + +impl From for u32 { + fn from(mode: TimerMode) -> u32 { + match mode { + TimerMode::OneShot => 0b00, + TimerMode::Periodic => 0b01, + TimerMode::TscDeadline => 0b10, + TimerMode::Reserved => 0b11, + } + } +} + +impl From for TimerMode { + fn from(mode: u32) -> TimerMode { + match mode { + 0b00 => TimerMode::OneShot, + 0b01 => TimerMode::Periodic, + 0b10 => TimerMode::TscDeadline, + 0b11 => TimerMode::Reserved, + _ => unreachable!(), + } + } +} + +/// Local APIC Registers are 128-bit wide, with the 32 lower bits containing the +/// actual register, and the top bits being reserved for future use. +#[repr(transparent)] +#[derive(Debug)] +struct LocalApicRegister(u128, PhantomData); + +impl Io for LocalApicRegister { + type Value = T; + + fn read(&self) -> Self::Value { + unsafe { (&self.0 as *const u128 as *const T).read_volatile() } + } + + fn write(&mut self, value: Self::Value) { + unsafe { (&mut self.0 as *mut u128 as *mut T).write_volatile(value) } + } +} + +bitfield! { + /// The version and associated metadata of a Local APIC are described by this + /// struct. + /// + /// See chapter 10.4.8: Local APIC Version Register + #[repr(transparent)] + #[derive(Clone, Copy)] + pub struct LocalApicVersion(u32); + impl Debug; + /// The version numbers of the local APIC: + /// + /// - 00H - 0FH: 82489DX discrete APIC. + /// - 10H - 15H: Integrated APIC. + version, _: 7, 0; + /// Shows the number of LVT entries minus 1. + max_lvt_entry, _: 23, 16; + /// Indicates whether software can inhibit the broadcast of EOI message by + /// setting bit 12 of the Spurious Interrupt Vector Register. + can_suppress_eoi_broadcast, _: 24; +} + +bitfield! { + /// Allows software to specify the manner in which the local interrupts are + /// delivered to the processor core. + /// + /// See chapter 10.5.1: Local Vector Table + #[repr(transparent)] + #[derive(Clone, Copy)] + pub struct LocalVector(u32); + impl Debug; + /// Interrupt vector number. + vector, set_vector: 7, 0; + /// Specifies the type of interrupt to be sent to the processor. Some + /// delivery modes will only operate as intended when used in conjunction + /// with a specific trigger mode. See [DeliveryMode] for documentation about + /// available modes. + from into DeliveryMode, delivery_mode, set_delivery_mode: 10, 8; + /// Indicates the interrupt delivery status, as follows: + /// + /// - `false` (Idle): There is currently no activity for this interrupt + /// source, or the previous in-terrupt from this source was delivered to + /// the processor core and accepted. + /// - `true` (Send Pending): Indicates that an interrupt from this source has + /// been delivered to the pro-cessor core but has not yet been accepted. + delivery_status, _: 12; + /// Specifies the polarity of the corresponding interrupt pin: (`false`) + /// active high or (`true`) active low. + polarity, set_polarity: 13; + /// For fixed mode, level-triggered interrupts; this flag is set when the + /// local APIC accepts the interrupt for servicing and is reset when an EOI + /// command is received from the processor. The meaning of this flag is + /// undefined for edge-triggered interrupts and other delivery modes. + remote_irr, _: 14; + /// Selects the trigger mode for the local LINT0 and LINT1 pins: (`false`) + /// edge sensitive and (`true`) level sensitive. This flag is only used when + /// the delivery mode is Fixed. When the delivery mode is NMI, SMI, or INIT, + /// the trigger mode is always edge sensitive. When the delivery mode is + /// ExtINT, the trigger mode is always level sensitive. The timer and error + /// interrupts are always treated as edge sensitive. + /// + /// If the local APIC is not used in conjunction with an I/O APIC and fixed + /// delivery mode is selected; the Pentium 4, Intel Xeon, and P6 family + /// processors will always use level-sensitive triggering, regardless if + /// edge-sensitive triggering is selected. + /// + /// Software should always set the trigger mode in the LVT LINT1 register to + /// 0 (edge sensitive). Level-sensitive interrupts are not supported for + /// LINT1. + trigger_mode, set_trigger_mode: 15; + /// Interrupt mask: (`false`) enables reception of the interrupt and (`true`) + /// inhibits reception of the interrupt. When the local APIC handles a + /// performance-monitoring counters interrupt, it automatically sets the mask + /// flag in the LVT performance counter register. This flag is set to true on + /// reset. It can be cleared only by software. + masked, set_masked: 16; + /// Selects timer mode. See [TimerMode] for possible values. + from into TimerMode, timer_mode, set_timer_mode: 18, 17; +} + +bitfield! { + /// See chapter 10.9: Spurious Interrupt + #[repr(transparent)] + #[derive(Clone, Copy)] + pub struct SpuriousInterrupt(u32); + impl Debug; + /// Determines the vector number to be delivered to the processor when the + /// local APIC generates a spurious vector. + spurious_vector, set_spurious_vector: 7, 0; + /// Allows software to temporarily enable (1) or disable (0) the local APIC. + /// + /// See Section 10.4.3, Enabling or Disabling the Local APIC. + apic_software_enable, set_apic_software_enable: 8; + /// Determines if focus processor checking is enabled when using the + /// lowest-priority delivery mode. In Pentium 4 and Intel Xeon processors, + /// this bit is reserved and should be cleared to 0. + focus_processor_checking, _: 9; + /// Determines whether an EOI for a level-triggered interrupt causes EOI + /// messages to be broadcast to the I/O APICs or not. The default value for + /// this bit is false, indicating that EOI broadcasts are performed. This bit + /// is reserved to false if the processor does not support EOI-broadcast + /// suppression. + /// + /// See chapter 10.8.5 Signaling Interrupt Servicing Completion + suppress_eoi_broadcast, set_suppress_eoi_broadcast: 12; +} + + +bitflags! { + /// Contains the set of errors the LAPIC has encountered while running. + /// + /// See 10.5.3: Error Handling + struct Error: u32 { + /// Set when the local APIC detects a checksum error for a message that + /// it sent on the APIC bus. Used only on P6 family and Pentium + /// processors. + const SEND_CHECKSUM_ERROR = 1 << 0; + /// Set when the local APIC detects a checksum error for a message that + /// it received on the APIC bus. Used only on P6 family and Pentium + /// processors. + const RECEIVE_CHECKSUM_ERROR = 1 << 1; + /// Set when the local APIC detects that a message it sent was not + /// accepted by any APIC on the APIC bus. Used only on P6 family and + /// Pentium processors. + const SEND_ACCEPT_ERROR = 1 << 2; + /// Set when the local APIC detects that the message it received was not + /// accepted by any APIC on the APIC bus, including itself. Used only on + /// P6 family and Pentium processors + const RECEIVE_ACCEPT_ERROR = 1 << 3; + /// Set when the local APIC detects an attempt to send an IPI with the + /// lowest-priority delivery mode and the local APIC does not support the + /// sending of such IPIs. This bit is used on some Intel Core and Intel + /// Xeon processors. As noted in chapter 10.6.2: Determining IPI + /// Destination, the ability of a processor to send a lowest-priority IPI + /// is model-specific and should be avoided. + const REDIRECTABLE_IPI = 1 << 4; + /// Set when the local APIC detects an illegal vector (one in the range 0 + /// to 15) in the message that it is sending. This occurs as the result + /// of a write to the ICR (in both xAPIC and x2APIC modes) or to SELF IPI + /// register (x2APIC mode only) with an illegal vector. + /// + /// If the local APIC does not support the sending of lowest-priority + /// IPIs and software writes the ICR to send a lowest-priority IPI with + /// an illegal vector, the local APIC sets only the "redirectable IPI" + /// error bit. The interrupt is not processed and hence the "Send Illegal + /// Vector" bit is not set in the ESR. + const SEND_ILLEGAL_VECTOR = 1 << 5; + /// Set when the local APIC detects an illegal vector (one in the range 0 + /// to 15) in an interrupt message it receives or in an interrupt + /// generated locally from the local vector table or via a self IPI. Such + /// interrupts are not delivered to the processor; the local APIC will + /// never set an IRR bit in the range 0 to 15. + const RECEIVE_ILLEGAL_VECTOR = 1 << 6; + /// Set when the local APIC is in xAPIC mode and software attempts to + /// access a register that is reserved in the processor's local-APIC + /// register-address space; The local-APIC register-address space + /// comprises the 4 KBytes at the physical address specified in the + /// IA32_APIC_BASE MSR. Used only on Intel Core, Intel Atom™, Pentium 4, + /// Intel Xeon, and P6 family processors. + /// + /// In x2APIC mode, software accesses the APIC registers using the RDMSR + /// and WRMSR instructions. Use of one of these instructions to access a + /// reserved register cause a general-protection exception. They do not + /// set the “Illegal Register Access” bit in the ESR. + const ILLEGAL_REGISTER_ADDRESS = 1 << 7; + } +} + +/// Local APIC Register structure. +#[repr(C)] +#[allow(clippy::missing_docs_in_private_items)] +#[allow(missing_debug_implementations)] // Implementation is on LocalApic +struct LocalApicInternal { + reserved_000: LocalApicRegister, + reserved_010: LocalApicRegister, + + /// Unique ID of this Local APIC. May also be used as a way to uniquely + /// identify a CPU. + /// + /// On power up, system hardware assigns a unique APIC ID to each local APIC. + /// The hardware assigned APIC ID is based on system topology and includes + /// encoding for socket position and cluster information. + /// + /// See chapter 10.4.6: Local APIC ID. + local_apic_id: LocalApicRegister, + /// Can be used to identify the APIC version. In addition, the register + /// specifies the number of entries in the local vector table (LVT) for a + /// specific implementation. + /// + /// See chapter 10.4.8: Local APIC Version. + local_apic_version: LocalApicRegister, + + reserved_040: LocalApicRegister, + reserved_050: LocalApicRegister, + reserved_060: LocalApicRegister, + reserved_070: LocalApicRegister, + + /// The task priority allows software to set a priority threshold for + /// interrupting the processor. This mechanism enables the operating system + /// to temporarily block low priority interrupts from disturbing + /// high-priority work that the processor is doing. The ability to block such + /// interrupts using task priority results from the way that the TPR controls + /// the value of the processor-priority register. + /// + /// See chapter 10.8.3.1: Task and Processor Priorities + task_priority: LocalApicRegister, + /// Priority used for lowest-priority arbitration. + /// + /// Only available on Nahalem CPUs. + /// + /// See chapter 10.6.2.4: Lowest Priority Delivery Mode. + arbitration_priority: LocalApicRegister, + /// The processor-priority class determines the priority threshold for + /// interrupting the processor. The processor will deliver only those + /// interrupts that have an interrupt-priority class higher than the + /// processor-priority class in the PPR. If the processor-priority class is + /// 0, the PPR does not inhibit the delivery any interrupt; if it is 15, the + /// processor inhibits the delivery of all interrupts. (The + /// processor-priority mechanism does not affect the delivery of interrupts + /// with the NMI, SMI, INIT, ExtINT, INIT-deassert, and start-up delivery + /// modes.) + /// + /// See chapter 10.8.3.1: Task and Processor Priorities + processor_priority: LocalApicRegister, + /// For all interrupts except those delivered with the NMI, SMI, INIT, + /// ExtINT, the start-up, or INIT-Deassert delivery mode, the interrupt + /// handler must include a write to the end-of-interrupt (EOI) register. This + /// write must occur at the end of the handler routine, sometime before the + /// IRET instruction. This action indicates that the servicing of the current + /// interrupt is complete and the local APIC can issue the next interrupt + /// from the ISR. + /// + /// See chapter 10.8.5: Signaling Interrupt Servicing Completion + end_of_interrupt: LocalApicRegister, + /// Only available on Nahalem CPUs. Undocumented... + remote_read: LocalApicRegister, + /// Upon receiving an interrupt that was sent using logical destination mode, + /// a local APIC compares the Message Destination Address with the values in + /// its Logical Destination Register and Destination Format Register to + /// determine if it should accept and handle the interrupt request. + /// + /// See chapter 10.6.2.2: Logical Destination Mode + logical_destination: LocalApicRegister, + /// See chapter 10.6.2.2: Logical Destination Mode + destination_format: LocalApicRegister, + /// A special situation may occur when a processor raises its task priority + /// to be greater than or equal to the level of the interrupt for which the + /// processor INTR signal is currently being asserted. If at the time the + /// INTA cycle is issued, the interrupt that was to be dispensed has become + /// masked (programmed by software), the local APIC will deliver a + /// spurious-interrupt vector. Dispensing the spurious-interrupt vector does + /// not affect the ISR, so the handler for this vector should return without + /// an EOI. + /// + /// See chapter 10.9: Spurious Interrupt + spurious_interrupt_vector: LocalApicRegister, + /// See [Self::in_service] documentation. + in_service0: LocalApicRegister, + /// See [Self::in_service] documentation. + in_service1: LocalApicRegister, + /// See [Self::in_service] documentation. + in_service2: LocalApicRegister, + /// See [Self::in_service] documentation. + in_service3: LocalApicRegister, + /// See [Self::in_service] documentation. + in_service4: LocalApicRegister, + /// See [Self::in_service] documentation. + in_service5: LocalApicRegister, + /// See [Self::in_service] documentation. + in_service6: LocalApicRegister, + /// See [Self::in_service] documentation. + in_service7: LocalApicRegister, + /// See [Self::trigger_mode] documentation. + trigger_mode0: LocalApicRegister, + /// See [Self::trigger_mode] documentation. + trigger_mode1: LocalApicRegister, + /// See [Self::trigger_mode] documentation. + trigger_mode2: LocalApicRegister, + /// See [Self::trigger_mode] documentation. + trigger_mode3: LocalApicRegister, + /// See [Self::trigger_mode] documentation. + trigger_mode4: LocalApicRegister, + /// See [Self::trigger_mode] documentation. + trigger_mode5: LocalApicRegister, + /// See [Self::trigger_mode] documentation. + trigger_mode6: LocalApicRegister, + /// See [Self::trigger_mode] documentation. + trigger_mode7: LocalApicRegister, + /// See [Self::interrupt_request] documentation. + interrupt_request0: LocalApicRegister, + /// See [Self::interrupt_request] documentation. + interrupt_request1: LocalApicRegister, + /// See [Self::interrupt_request] documentation. + interrupt_request2: LocalApicRegister, + /// See [Self::interrupt_request] documentation. + interrupt_request3: LocalApicRegister, + /// See [Self::interrupt_request] documentation. + interrupt_request4: LocalApicRegister, + /// See [Self::interrupt_request] documentation. + interrupt_request5: LocalApicRegister, + /// See [Self::interrupt_request] documentation. + interrupt_request6: LocalApicRegister, + /// See [Self::interrupt_request] documentation. + interrupt_request7: LocalApicRegister, + /// The local APIC records errors detected during interrupt handling in the + /// error status register (ESR). + /// + /// See chapter 10.5.3: Error Handling. + error_status: LocalApicRegister, + + reserved_290: LocalApicRegister, + reserved_2a0: LocalApicRegister, + reserved_2b0: LocalApicRegister, + reserved_2c0: LocalApicRegister, + reserved_2d0: LocalApicRegister, + reserved_2e0: LocalApicRegister, + + /// Specifies interrupt delivery when an overflow condition of corrected + /// machine check error count reaching a threshold value occurred in a + /// machine check bank supporting CMCI. + /// + /// See Section 10.5.1, "Local Vector Table". + lvt_corrected_machine_interrupt: LocalApicRegister, + /// See [Self::send_interrupt_command] documentation. + interrupt_command_register0: LocalApicRegister, + /// See [Self::send_interrupt_command] documentation. + interrupt_command_register1: LocalApicRegister, + /// Specifies interrupt delivery when the APIC timer signals an interrupt. + /// + /// See Section 10.5.1, "Local Vector Table". + lvt_timer: LocalApicRegister, + /// Specifies interrupt delivery when the thermal sensor generates an + /// interrupt. + /// + /// See Section 10.5.1, "Local Vector Table". + lvt_thermal_sensor: LocalApicRegister, + /// Specifies interrupt delivery when a performance counter generates an + /// interrupt on overflow + /// + /// See Section 10.5.1, "Local Vector Table". + lvt_performance_monitoring_counter: LocalApicRegister, + /// Specifies interrupt delivery when an interrupt is signaled at the LINT0. + /// + /// See Section 10.5.1, "Local Vector Table". + lvt_lint0: LocalApicRegister, + /// Specifies interrupt delivery when an interrupt is signaled at the LINT1. + /// + /// See Section 10.5.1, "Local Vector Table". + lvt_lint1: LocalApicRegister, + /// Specifies interrupt delivery when the APIC detects an internal error. + /// + /// See Section 10.5.1, "Local Vector Table". + lvt_error: LocalApicRegister, + /// Initial count used by the APIC Timer. + /// + /// See Section 10.5.4: APIC Timer. + initial_count: LocalApicRegister, + /// Current count used by the APIC Timer. + /// + /// See Section 10.5.4: APIC Timer. + current_count: LocalApicRegister, + + reserved_3a0: LocalApicRegister, + reserved_3b0: LocalApicRegister, + reserved_3c0: LocalApicRegister, + reserved_3d0: LocalApicRegister, + + /// Divide configuration used by the APIC timer. + /// + /// See Section 10.5.4: APIC Timer. + divide_configuration: LocalApicRegister, + + reserved_3f0: LocalApicRegister, +} +assert_eq_size!(LocalApicInternal, [u8; 0x400]); + +// LocalApic should be cpu_local. +/// LocalApic driver. +pub struct LocalApic { + /// Pointer to the LocalApic registers. + internal: &'static UnsafeCell, +} + +impl fmt::Debug for LocalApic { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unsafe { + f.debug_struct("LocalApic") + .field("local_apic_id", &(*self.internal.get()).local_apic_id.read()) + .field("local_apic_version", &(*self.internal.get()).local_apic_version.read()) + .field("task_priority", &(*self.internal.get()).task_priority.read()) + .field("arbitration_priority", &(*self.internal.get()).arbitration_priority.read()) + .field("processor_priority", &(*self.internal.get()).processor_priority.read()) + .field("end_of_interrupt", &(*self.internal.get()).end_of_interrupt.read()) + .field("remote_read", &(*self.internal.get()).remote_read.read()) + .field("logical_destination", &(*self.internal.get()).logical_destination.read()) + .field("destination_format", &(*self.internal.get()).destination_format.read()) + .field("spurious_interrupt_vector", &(*self.internal.get()).spurious_interrupt_vector.read()) + .field("in_service", &self.in_service()) + .field("trigger_mode", &self.trigger_mode()) + .field("interrupt_request_register", &self.interrupt_request_register()) + .field("error_status", &(*self.internal.get()).error_status.read()) + .field("lvt_corrected_machine_interrupt", &(*self.internal.get()).lvt_corrected_machine_interrupt.read()) + //.field("interrupt_command_register", &self.interrupt_command_register()) + .field("lvt_timer", &(*self.internal.get()).lvt_timer.read()) + .field("lvt_thermal_sensor", &(*self.internal.get()).lvt_thermal_sensor.read()) + .field("lvt_performance_monitoring_counter", &(*self.internal.get()).lvt_performance_monitoring_counter.read()) + .field("lvt_lint0", &(*self.internal.get()).lvt_lint0.read()) + .field("lvt_lint1", &(*self.internal.get()).lvt_lint1.read()) + .field("lvt_error", &(*self.internal.get()).lvt_error.read()) + .field("initial_count", &(*self.internal.get()).initial_count.read()) + .field("current_count", &(*self.internal.get()).current_count.read()) + .field("divide_configuration", &(*self.internal.get()).divide_configuration.read()) + .finish() + } + } +} + +// TODO: LocalAPIC should not be Send/Sync. +// BODY: LocalApic should be stored in a cpu_local, removing the need for Send/ +// BODY: Sync bounds. Problem is, we don't really have a way to create CPU Locals +// BODY: yet. +unsafe impl Send for LocalApic {} +unsafe impl Sync for LocalApic {} + +impl LocalApic { + /// Create a new LocalApic at the specified address. + /// + /// # Panics + /// + /// Panics if address is not page-aligned. + /// + /// # Safety + /// + /// `address` should be the physical address of a LocalApic device, and + /// should not be shared. + pub unsafe fn new(address: PhysicalAddress) -> Self { + assert!(address.addr() % PAGE_SIZE == 0, "Unaligned local APIC address"); + + let lapic = get_kernel_memory().map_phys_region(PhysicalMemRegion::on_fixed_mmio(address, 0x1000).unwrap(), MappingAccessRights::WRITABLE); + + let lapic = LocalApic { + internal: (lapic.addr() as *const UnsafeCell).as_ref().unwrap(), + }; + + // Mask all the interrupt vectors. + let mut masked_vector = LocalVector(0); + masked_vector.set_masked(true); + (*lapic.internal.get()).lvt_corrected_machine_interrupt.write(masked_vector); + (*lapic.internal.get()).lvt_thermal_sensor.write(masked_vector); + (*lapic.internal.get()).lvt_performance_monitoring_counter.write(masked_vector); + (*lapic.internal.get()).lvt_lint0.write(masked_vector); + (*lapic.internal.get()).lvt_lint1.write(masked_vector); + (*lapic.internal.get()).lvt_error.write(masked_vector); + + lapic + } + + /// 10.4.3 Enabling or Disabling the Local APIC + /// + /// The local APIC can be enabled or disabled in either of two ways: + /// + /// - Using the APIC global enable/disable flag in the IA32_APIC_BASE MSR (MSR address 1BH; see Figure 10-5) + /// - Using the APIC software enable/disable flag in the spurious-interrupt vector register (see Figure 10-23) + pub fn enable(&self) { + // Enable the LAPIC. The MSR should be set by the BIOS, so we're only + // going to set the spurious-interrupt vector register. + unsafe { + let mut val = (*self.internal.get()).spurious_interrupt_vector.read(); + val.set_apic_software_enable(true); + (*self.internal.get()).spurious_interrupt_vector.write(val); + } + } + + /// Acknowledge the last interrupt, signaling an end of interrupt. + /// + /// See chapter 10.8: Handling Interrupts + pub fn acknowledge(&self) { + unsafe { (*self.internal.get()).end_of_interrupt.write(0); } + } + + /// Unique ID of this Local APIC. May also be used as a way to uniquely + /// identify a CPU. + pub fn local_apic_id(&self) -> u32 { + unsafe { (*self.internal.get()).local_apic_id.read() } + } + + // Sucks that we don't have an u256 :'). + /// The ISR contains interrupt requests that have been dispatched to the + /// processor for servicing, but not yet acknowledged by said processor. + /// + /// See chapter 10.8.4: Interrupt Acceptance for Fixed Interrupts. + pub fn in_service(&self) -> [u32; 8] { + unsafe { + let in_service0 = (*self.internal.get()).in_service0.read(); + let in_service1 = (*self.internal.get()).in_service1.read(); + let in_service2 = (*self.internal.get()).in_service2.read(); + let in_service3 = (*self.internal.get()).in_service3.read(); + let in_service4 = (*self.internal.get()).in_service4.read(); + let in_service5 = (*self.internal.get()).in_service5.read(); + let in_service6 = (*self.internal.get()).in_service6.read(); + let in_service7 = (*self.internal.get()).in_service7.read(); + + [ + in_service0, + in_service1, + in_service2, + in_service3, + in_service4, + in_service5, + in_service6, + in_service7 + ] + } + } + + /// The trigger mode register (TMR) indicates the trigger mode of the + /// interrupt. Upon acceptance of an interrupt into the IRR, the + /// corresponding TMR bit is cleared for edge-triggered interrupts and set + /// for level-triggered interrupts. If a TMR bit is set when an EOI cycle for + /// its corresponding interrupt vector is generated, an EOI message is sent + /// to all I/O APICs. + /// + /// See chapter 10.8.4: Interrupt Acceptance for Fixed Interrupts. + pub fn trigger_mode(&self) -> [u32; 8] { + unsafe { + let trigger_mode0 = (*self.internal.get()).trigger_mode0.read(); + let trigger_mode1 = (*self.internal.get()).trigger_mode1.read(); + let trigger_mode2 = (*self.internal.get()).trigger_mode2.read(); + let trigger_mode3 = (*self.internal.get()).trigger_mode3.read(); + let trigger_mode4 = (*self.internal.get()).trigger_mode4.read(); + let trigger_mode5 = (*self.internal.get()).trigger_mode5.read(); + let trigger_mode6 = (*self.internal.get()).trigger_mode6.read(); + let trigger_mode7 = (*self.internal.get()).trigger_mode7.read(); + + [ + trigger_mode0, + trigger_mode1, + trigger_mode2, + trigger_mode3, + trigger_mode4, + trigger_mode5, + trigger_mode6, + trigger_mode7 + ] + } + } + + /// The IRR contains the active interrupt requests that have been accepted, + /// but not yet dispatched to the processor for servicing. + /// + /// See chapter 10.8.4: Interrupt Acceptance for Fixed Interrupts. + pub fn interrupt_request_register(&self) -> [u32; 8] { + unsafe { + let interrupt_request0 = (*self.internal.get()).interrupt_request0.read(); + let interrupt_request1 = (*self.internal.get()).interrupt_request1.read(); + let interrupt_request2 = (*self.internal.get()).interrupt_request2.read(); + let interrupt_request3 = (*self.internal.get()).interrupt_request3.read(); + let interrupt_request4 = (*self.internal.get()).interrupt_request4.read(); + let interrupt_request5 = (*self.internal.get()).interrupt_request5.read(); + let interrupt_request6 = (*self.internal.get()).interrupt_request6.read(); + let interrupt_request7 = (*self.internal.get()).interrupt_request7.read(); + + [ + interrupt_request0, + interrupt_request1, + interrupt_request2, + interrupt_request3, + interrupt_request4, + interrupt_request5, + interrupt_request6, + interrupt_request7 + ] + } + } + + /// Sends an IPI. + /// + /// See 10.6 Issuing Interprocessor Interrupts + pub fn send_interrupt_command(&mut self, val: u64) { + // First write the top bits, since writing to the low bits triggers the + // IPI. + unsafe { + (*self.internal.get()).interrupt_command_register1.write(val.get_bits(32..64) as u32); + (*self.internal.get()).interrupt_command_register0.write(val.get_bits(0..32) as u32); + } + } +} diff --git a/kernel/src/devices/hpet.rs b/kernel/src/devices/hpet.rs index 53fd0a2e1..a0f319c5e 100644 --- a/kernel/src/devices/hpet.rs +++ b/kernel/src/devices/hpet.rs @@ -1,4 +1,6 @@ //! HPET driver implementation. +//! +//! HPET documentation: https://web.archive.org/web/20190411220000/https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf use crate::frame_allocator::PhysicalMemRegion; use crate::mem::PhysicalAddress; use crate::paging; @@ -221,6 +223,21 @@ impl HpetTimer { (self.interrupt_route_capability & irq_mask) == irq_mask } + /// Set the routing for the interrupt to the I/O APIC. + /// + /// # Panics + /// + /// Panics if the given interrupt route is not supported by this hpet timer. + pub fn set_interrupt_route(&self, index: u32) { + assert!(self.support_interrupt_routing(index), "Illegal interrupt route."); + let mut config = unsafe { (*self.inner).config.read() }; + config.set_interrupt_route(index); + unsafe { (*self.inner).config.write(config); } + + let config = unsafe { (*self.inner).config.read() }; + assert!(config.interrupt_route() == index, "Illegal interrupt route."); + } + /// Set the timer comparactor value pub fn set_comparator_value(&self, value: u64) { unsafe { @@ -320,6 +337,7 @@ impl HpetTimer { impl Hpet { /// Create a new HPET device instance from MMIO registers. fn new(inner: *mut HpetRegister) -> Self { + debug!("Creating new HPET with registers at {:p}", inner); let mut res = Hpet { inner, timer_count: 1, @@ -459,7 +477,6 @@ pub unsafe fn init(hpet: &acpi::Hpet) -> bool { return false; } - // TODO: enable main timer let main_timer_opt = hpet_instance.get_timer(0); if main_timer_opt.is_none() { @@ -488,34 +505,26 @@ pub unsafe fn init(hpet: &acpi::Hpet) -> bool { let irq_period_tick = irq_period_fs / u64::from(hpet_instance.get_period()); + // IO-APIC expects edge triggering by default. main_timer.set_edge_trigger(); main_timer.set_periodic_mode(); main_timer.enable_interrupt(); main_timer.set_accumulator_value(irq_period_tick); main_timer.set_comparator_value(irq_period_tick); - - // Fake the RTC as in legacy mode it's taking it's IRQ. - // TODO: remove this when IO-APIC will be implemented - // Cannot fail, by spec there is at least 3 comparators defined. - let rtc_timer = hpet_instance.get_timer(1).unwrap(); - if !rtc_timer.support_periodic_interrupt() { - warn!("RTC irq cannot be faked, clock will be broken!"); - } else { - let rtc_period_in_ticks = hpet_instance.get_frequency(); - rtc_timer.set_edge_trigger(); - rtc_timer.set_periodic_mode(); - rtc_timer.enable_interrupt(); - rtc_timer.set_accumulator_value(rtc_period_in_ticks); - rtc_timer.set_comparator_value(rtc_period_in_ticks); - } - - // TODO: switch HPET to normal mode when IO-APIC will be implemented. - hpet_instance.enable_legacy_mapping(); + // Route the timer to the IRQ 2. + // TODO: Report that IOAPIC IRQ0 is broken under qemu. + // BODY: Ideally, we'd use IRQ0 for the timer, in order to match what we have + // BODY: with the PIC. Unfortunately, qemu [unconditionally redirects irqs on + // BODY: pin0 to pin2](https://github.com/qemu/qemu/blob/37560c259d7a0d6aceb96e9d6903ee002f4e5e0c/hw/intc/ioapic.c#L152). + // BODY: + // BODY: We should report this upstream bug, and move back to IRQ0 once it is + // BODY: fixed. + main_timer.set_interrupt_route(2); // Clear the interrupt state hpet_instance.enable(); - timer::set_kernel_timer_info(0, hpet_instance.get_frequency(), irq_period_ns); + timer::set_kernel_timer_info(2, hpet_instance.get_frequency(), irq_period_ns); HPET_INSTANCE = Some(hpet_instance); true diff --git a/kernel/src/devices/ioapic.rs b/kernel/src/devices/ioapic.rs new file mode 100644 index 000000000..37dd32d78 --- /dev/null +++ b/kernel/src/devices/ioapic.rs @@ -0,0 +1,370 @@ +//! 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, + /// 12 bytes of padding. + padding: [u8; 0x10-4], + /// Data register. + data_reg: Mmio, +} + +/// See [module level documentation](crate::device::ioapic) +pub struct IoApic { + /// Pointer to the IO-APIC device registers. + internal: &'static UnsafeCell, + /// 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 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 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 where the region. + /// + /// # 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).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); + if i + interrupt_base < 16 { + // Legacy interrupt, set the interrupt vector. + 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() + } +} diff --git a/kernel/src/devices/mod.rs b/kernel/src/devices/mod.rs index 705858037..789c814a7 100644 --- a/kernel/src/devices/mod.rs +++ b/kernel/src/devices/mod.rs @@ -5,6 +5,9 @@ pub mod pic; pub mod pit; pub mod rs232; +pub mod apic; +pub mod ioapic; + use crate::i386::acpi; /// Initialize a timer to be used by the OS. @@ -27,5 +30,8 @@ pub fn init_timer() { if use_pit { unsafe { pit::init_channel_0() }; info!("Initialized PIT"); + } else { + unsafe { pit::disable() }; + info!("Disabled PIT"); } } diff --git a/kernel/src/devices/pit.rs b/kernel/src/devices/pit.rs index 5bf8619d4..1ee01b36a 100644 --- a/kernel/src/devices/pit.rs +++ b/kernel/src/devices/pit.rs @@ -208,3 +208,10 @@ pub unsafe fn init_channel_0() { timer::set_kernel_timer_info(0, OSCILLATOR_FREQ as u64, 1_000_000_000 / (CHAN_0_FREQUENCY as u64)); } + +/// Prevent the PIT from generating interrupts. +pub unsafe fn disable() { + let mut ports = PIT_PORTS.lock(); + ports.port_cmd.write(0b00110010); // channel 0, lobyte/hibyte, one-shot + ports.write_reload_value(ChannelSelector::Channel0, 1); +} diff --git a/kernel/src/event.rs b/kernel/src/event.rs index 1a3a50a47..ce91c72a5 100644 --- a/kernel/src/event.rs +++ b/kernel/src/event.rs @@ -180,7 +180,8 @@ pub fn dispatch_event(irq: usize) { /// Creates an IRQEvent waiting for the given IRQ number. pub fn wait_event(irq: u8) -> IRQEvent { - crate::devices::pic::get().unmask(irq); + debug!("Waiting for {}", irq); + crate::i386::interrupt::unmask(irq); IRQEvent { state: &IRQ_STATES[irq as usize], ack: AtomicUsize::new(IRQ_STATES[irq as usize].counter.load(Ordering::SeqCst)) } @@ -214,8 +215,8 @@ impl IRQState { /// Global state for all the IRQ handled by the PIC. static IRQ_STATES: [IRQState; 16] = [ - IRQState::new(20), IRQState::new(21), IRQState::new(22), IRQState::new(23), - IRQState::new(24), IRQState::new(25), IRQState::new(26), IRQState::new(27), - IRQState::new(28), IRQState::new(29), IRQState::new(30), IRQState::new(31), - IRQState::new(32), IRQState::new(33), IRQState::new(34), IRQState::new(35), + IRQState::new(0x20), IRQState::new(0x21), IRQState::new(0x22), IRQState::new(0x23), + IRQState::new(0x24), IRQState::new(0x25), IRQState::new(0x26), IRQState::new(0x27), + IRQState::new(0x28), IRQState::new(0x29), IRQState::new(0x2A), IRQState::new(0x2B), + IRQState::new(0x2C), IRQState::new(0x2D), IRQState::new(0x2E), IRQState::new(0x2F), ]; diff --git a/kernel/src/i386/interrupt.rs b/kernel/src/i386/interrupt.rs new file mode 100644 index 000000000..806e1c951 --- /dev/null +++ b/kernel/src/i386/interrupt.rs @@ -0,0 +1,146 @@ +//! Arch-generic interrupt handling. +//! +//! This file contains the arch-generic implementation details of interrupt +//! handling. It contains the interrupt initialization routine, and routines to +//! unmask and acknowledge interrupts. + +use crate::devices::pic; +use crate::devices::apic::LocalApic; +use crate::devices::ioapic::IoApic; +use acpi::interrupt::{InterruptModel, InterruptSourceOverride}; +use crate::sync::Once; +use alloc::vec::Vec; +use crate::mem::PhysicalAddress; + +/// Global state for the interrupt handler. +struct InterruptHandler { + /// Root CPU's Local APIC. + root_lapic: LocalApic, + /// Vector of all the IO-APICs. + ioapics: Vec, + /// List of interrupt mappings. + isa_mappings: Vec +} + +/// Global state for the interrupt handler. +static INTERRUPT_HANDLER: Once = Once::new(); + +/// Initialize the interrupt handler. +pub fn init() { + // Always initialize the pic to redirect entries (otherwise, we might + // get spurious interrupts on the CPU exception handler) and mask everything. + info!("Init pic"); + pic::init(); + + info!("Acquire INTERRUPT_HANDLER"); + let handler = INTERRUPT_HANDLER.call_once(|| { + match crate::i386::acpi::try_get_acpi_information().and_then(|v| v.interrupt_model().as_ref()) { + Some(InterruptModel::Apic { local_apic_address, io_apics, interrupt_source_overrides, .. }) => { + unsafe { + let lapic = LocalApic::new(PhysicalAddress(*local_apic_address as usize)); + let ioapics = io_apics.iter().map(|v| + IoApic::new(PhysicalAddress(v.address as usize), v.global_system_interrupt_base, lapic.local_apic_id()) + ).collect(); + + InterruptHandler { + root_lapic: lapic, + ioapics, + isa_mappings: interrupt_source_overrides.clone() + } + } + } + _ => panic!("ACPI did not find a Local APIC"), + /* + // According to https://web.archive.org/web/20121002210153/http://download.intel.com/design/archives/processors/pro/docs/24201606.pdf, + // Local APIC is at 0xFEE0_0000 + // I/O - APIC is at 0xFEC0_0000 + let lapic = LocalApic::new(PhysicalAddress(0xFEE0_0000)); + let ioapic = IoApic::new(PhysicalAddress(0xFEC0_0000), 0, lapic.local_apic_id()) + */ + } + }); + + info!("Mask all interrupts in PIC"); + let pic = pic::get(); + for i in 0..16 { + pic.mask(i); + } + + info!("Enable the APIC"); + handler.root_lapic.enable(); + + for mapping in &handler.isa_mappings { + if mapping.isa_source == 0 { + // Ignore the PIT, we're using the HPET. + continue; + } + let irq = mapping.global_system_interrupt; + let ioapic = handler.ioapics.iter().find(|ioapic| + ioapic.interrupt_base() <= irq && + irq < ioapic.interrupt_base() + ioapic.redirection_entry_count()).unwrap(); + + let mut redirection_entry = ioapic.redirection_entry((irq - ioapic.interrupt_base()) as u8); + info!("Mapping ISA interrupt {} (at IOAPIC {})", mapping.isa_source, irq); + redirection_entry.set_interrupt_vector(u64::from(0x20 + mapping.isa_source)); + let trigger_mode = match mapping.trigger_mode { + acpi::interrupt::TriggerMode::Level => true, + _ => false + }; + redirection_entry.set_trigger_mode(trigger_mode); + let polarity = match mapping.polarity { + acpi::interrupt::Polarity::ActiveHigh => false, + _ => true + }; + redirection_entry.set_interrupt_input_pin_polarity(polarity); + ioapic.set_redirection_entry((irq - ioapic.interrupt_base()) as u8, redirection_entry); + } +} + +/// Acknowledge the given IRQ. +/// +/// # Panic +/// +/// Panics if called before calling `init`. +pub fn acknowledge(_irq: u8) { + INTERRUPT_HANDLER.r#try().unwrap().root_lapic.acknowledge(); +} + +/// Unmasks the given IRQ. +/// +/// # Panic +/// +/// Panics if called before calling `init`. +pub fn unmask(irq: u8) { + let ioapics = &INTERRUPT_HANDLER.r#try().unwrap().ioapics; + + // First, find the "real" IRQ number: + let irqisa = irq; + let irq = match irqisa { + // We use the HPET to replace the PIT. + 0 => 0, + _ => isa_to_ioapic_irq(irq) + }; + + debug!("Unmasking IRQ {} (ISA {})", irq, irqisa); + + // Then, unmask it. + let ioapic = ioapics.iter().find(|ioapic| + ioapic.interrupt_base() <= irq && + irq < ioapic.interrupt_base() + ioapic.redirection_entry_count()).unwrap(); + + let mut redirection_entry = ioapic.redirection_entry((irq - ioapic.interrupt_base()) as u8); + redirection_entry.set_interrupt_mask(false); + ioapic.set_redirection_entry((irq - ioapic.interrupt_base()) as u8, redirection_entry); +} + +/// Gets the IOAPIC pin associated with an ISA (i8259) IRQ. +/// +/// # Panic +/// +/// Panics if called before calling `init`. +fn isa_to_ioapic_irq(irq: u8) -> u32 { + let isa_mappings = &INTERRUPT_HANDLER.r#try().unwrap().isa_mappings; + isa_mappings.iter() + .find(|v| v.isa_source == irq).map(|v| v.global_system_interrupt) + .unwrap_or_else(|| irq.into()) +} diff --git a/kernel/src/i386/mod.rs b/kernel/src/i386/mod.rs index 26c958e5a..becbd166c 100644 --- a/kernel/src/i386/mod.rs +++ b/kernel/src/i386/mod.rs @@ -16,6 +16,7 @@ pub mod multiboot; pub mod structures; pub mod process_switch; pub mod gdt; +pub mod interrupt; pub mod pio { //! Port IO diff --git a/kernel/src/interrupts/irq.rs b/kernel/src/interrupts/irq.rs index e3092ac7d..a14866e70 100644 --- a/kernel/src/interrupts/irq.rs +++ b/kernel/src/interrupts/irq.rs @@ -8,13 +8,13 @@ //! inserted in an architecture-specific interrupt table (such as i386's IDT). use crate::i386::structures::idt::ExceptionStackFrame; -use crate::devices::pic; macro_rules! irq_handler { ($irq:expr, $name:ident) => {{ #[allow(clippy::missing_docs_in_private_items)] extern "x86-interrupt" fn $name(_stack_frame: &mut ExceptionStackFrame) { - pic::get().acknowledge($irq); + // pic::get().acknowledge($irq); + crate::i386::interrupt::acknowledge($irq); crate::event::dispatch_event($irq); } $name @@ -24,9 +24,9 @@ macro_rules! irq_handler { /// Array of interrupt handlers. The position in the array defines the IRQ this /// handler is targeting. See the module documentation for more information. pub static IRQ_HANDLERS : [extern "x86-interrupt" fn(stack_frame: &mut ExceptionStackFrame); 16] = [ - irq_handler!(0, timer_handler), + irq_handler!(0, pin0_handler), irq_handler!(1, keyboard_handler), - irq_handler!(2, cascade_handler), + irq_handler!(2, timer_handler), irq_handler!(3, serial2_handler), irq_handler!(4, serial1_handler), irq_handler!(5, sound_handler), diff --git a/kernel/src/interrupts/mod.rs b/kernel/src/interrupts/mod.rs index 670d509b1..c0e4d1311 100644 --- a/kernel/src/interrupts/mod.rs +++ b/kernel/src/interrupts/mod.rs @@ -18,7 +18,6 @@ use core::sync::atomic::Ordering; use core::fmt::Arguments; use crate::sync::SpinLock; -use crate::devices::pic; use crate::scheduler; mod irq; @@ -425,7 +424,7 @@ lazy_static! { #[allow(clippy::cast_ptr_alignment)] // this function is x86_32 only #[allow(clippy::fn_to_numeric_cast)] // this function is x86_32 only pub unsafe fn init() { - pic::init(); + crate::i386::interrupt::init(); { let page = get_kernel_memory().get_page(); diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 43db38f8e..5cd8829ec 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -225,11 +225,11 @@ pub extern "C" fn common_start(multiboot_info_addr: usize) -> ! { info!("Start ACPI detection"); unsafe { i386::acpi::init(); } - devices::init_timer(); - info!("Enabling interrupts"); unsafe { interrupts::init(); } + devices::init_timer(); + //info!("Disable timer interrupt"); //devices::pic::get().mask(0); From 331b1785ef71b1a489cb869e5dcd1309bddf527a Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 23 May 2019 14:42:15 +0000 Subject: [PATCH 2/4] Fix documentation --- kernel/src/devices/apic.rs | 54 ++++++++++++++++++------------------ kernel/src/devices/ioapic.rs | 2 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/kernel/src/devices/apic.rs b/kernel/src/devices/apic.rs index f5cfecb00..dac32187a 100644 --- a/kernel/src/devices/apic.rs +++ b/kernel/src/devices/apic.rs @@ -28,7 +28,7 @@ pub enum DeliveryMode { /// should be set to 0x00 for future compatibility. SMI, /// Delivers an NMI interrupt to the processor. The vector information is - /// ignored. + /// ignored. NMI, /// Delivers an INIT request to the processor core, which causes the @@ -390,53 +390,53 @@ struct LocalApicInternal { /// /// See chapter 10.9: Spurious Interrupt spurious_interrupt_vector: LocalApicRegister, - /// See [Self::in_service] documentation. + /// See [LocalApic::in_service()] documentation. in_service0: LocalApicRegister, - /// See [Self::in_service] documentation. + /// See [LocalApic::in_service()] documentation. in_service1: LocalApicRegister, - /// See [Self::in_service] documentation. + /// See [LocalApic::in_service()] documentation. in_service2: LocalApicRegister, - /// See [Self::in_service] documentation. + /// See [LocalApic::in_service()] documentation. in_service3: LocalApicRegister, - /// See [Self::in_service] documentation. + /// See [LocalApic::in_service()] documentation. in_service4: LocalApicRegister, - /// See [Self::in_service] documentation. + /// See [LocalApic::in_service()] documentation. in_service5: LocalApicRegister, - /// See [Self::in_service] documentation. + /// See [LocalApic::in_service()] documentation. in_service6: LocalApicRegister, - /// See [Self::in_service] documentation. + /// See [LocalApic::in_service()] documentation. in_service7: LocalApicRegister, - /// See [Self::trigger_mode] documentation. + /// See [LocalApic::trigger_mode()] documentation. trigger_mode0: LocalApicRegister, - /// See [Self::trigger_mode] documentation. + /// See [LocalApic::trigger_mode()] documentation. trigger_mode1: LocalApicRegister, - /// See [Self::trigger_mode] documentation. + /// See [LocalApic::trigger_mode()] documentation. trigger_mode2: LocalApicRegister, - /// See [Self::trigger_mode] documentation. + /// See [LocalApic::trigger_mode()] documentation. trigger_mode3: LocalApicRegister, - /// See [Self::trigger_mode] documentation. + /// See [LocalApic::trigger_mode()] documentation. trigger_mode4: LocalApicRegister, - /// See [Self::trigger_mode] documentation. + /// See [LocalApic::trigger_mode()] documentation. trigger_mode5: LocalApicRegister, - /// See [Self::trigger_mode] documentation. + /// See [LocalApic::trigger_mode()] documentation. trigger_mode6: LocalApicRegister, - /// See [Self::trigger_mode] documentation. + /// See [LocalApic::trigger_mode()] documentation. trigger_mode7: LocalApicRegister, - /// See [Self::interrupt_request] documentation. + /// See [LocalApic::interrupt_request_register()] documentation. interrupt_request0: LocalApicRegister, - /// See [Self::interrupt_request] documentation. + /// See [LocalApic::interrupt_request_register()] documentation. interrupt_request1: LocalApicRegister, - /// See [Self::interrupt_request] documentation. + /// See [LocalApic::interrupt_request_register()] documentation. interrupt_request2: LocalApicRegister, - /// See [Self::interrupt_request] documentation. + /// See [LocalApic::interrupt_request_register()] documentation. interrupt_request3: LocalApicRegister, - /// See [Self::interrupt_request] documentation. + /// See [LocalApic::interrupt_request_register()] documentation. interrupt_request4: LocalApicRegister, - /// See [Self::interrupt_request] documentation. + /// See [LocalApic::interrupt_request_register()] documentation. interrupt_request5: LocalApicRegister, - /// See [Self::interrupt_request] documentation. + /// See [LocalApic::interrupt_request_register()] documentation. interrupt_request6: LocalApicRegister, - /// See [Self::interrupt_request] documentation. + /// See [LocalApic::interrupt_request_register()] documentation. interrupt_request7: LocalApicRegister, /// The local APIC records errors detected during interrupt handling in the /// error status register (ESR). @@ -457,9 +457,9 @@ struct LocalApicInternal { /// /// See Section 10.5.1, "Local Vector Table". lvt_corrected_machine_interrupt: LocalApicRegister, - /// See [Self::send_interrupt_command] documentation. + /// See [LocalApic::send_interrupt_command()] documentation. interrupt_command_register0: LocalApicRegister, - /// See [Self::send_interrupt_command] documentation. + /// See [LocalApic::send_interrupt_command()] documentation. interrupt_command_register1: LocalApicRegister, /// Specifies interrupt delivery when the APIC timer signals an interrupt. /// diff --git a/kernel/src/devices/ioapic.rs b/kernel/src/devices/ioapic.rs index 37dd32d78..467791b91 100644 --- a/kernel/src/devices/ioapic.rs +++ b/kernel/src/devices/ioapic.rs @@ -32,7 +32,7 @@ struct IoApicInternal { data_reg: Mmio, } -/// See [module level documentation](crate::device::ioapic) +/// See [module level documentation](crate::devices::ioapic) pub struct IoApic { /// Pointer to the IO-APIC device registers. internal: &'static UnsafeCell, From f62b07913d8449c2996df815c8b9593c39d39f0c Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 12 Jun 2019 09:38:26 +0000 Subject: [PATCH 3/4] Rename apic.rs to lapic.rs --- kernel/src/devices/{apic.rs => lapic.rs} | 0 kernel/src/devices/mod.rs | 2 +- kernel/src/i386/interrupt.rs | 9 +-------- 3 files changed, 2 insertions(+), 9 deletions(-) rename kernel/src/devices/{apic.rs => lapic.rs} (100%) diff --git a/kernel/src/devices/apic.rs b/kernel/src/devices/lapic.rs similarity index 100% rename from kernel/src/devices/apic.rs rename to kernel/src/devices/lapic.rs diff --git a/kernel/src/devices/mod.rs b/kernel/src/devices/mod.rs index 789c814a7..4894441de 100644 --- a/kernel/src/devices/mod.rs +++ b/kernel/src/devices/mod.rs @@ -5,7 +5,7 @@ pub mod pic; pub mod pit; pub mod rs232; -pub mod apic; +pub mod lapic; pub mod ioapic; use crate::i386::acpi; diff --git a/kernel/src/i386/interrupt.rs b/kernel/src/i386/interrupt.rs index 806e1c951..d4344e44f 100644 --- a/kernel/src/i386/interrupt.rs +++ b/kernel/src/i386/interrupt.rs @@ -5,7 +5,7 @@ //! unmask and acknowledge interrupts. use crate::devices::pic; -use crate::devices::apic::LocalApic; +use crate::devices::lapic::LocalApic; use crate::devices::ioapic::IoApic; use acpi::interrupt::{InterruptModel, InterruptSourceOverride}; use crate::sync::Once; @@ -50,13 +50,6 @@ pub fn init() { } } _ => panic!("ACPI did not find a Local APIC"), - /* - // According to https://web.archive.org/web/20121002210153/http://download.intel.com/design/archives/processors/pro/docs/24201606.pdf, - // Local APIC is at 0xFEE0_0000 - // I/O - APIC is at 0xFEC0_0000 - let lapic = LocalApic::new(PhysicalAddress(0xFEE0_0000)); - let ioapic = IoApic::new(PhysicalAddress(0xFEC0_0000), 0, lapic.local_apic_id()) - */ } }); From 80126374958aad84e1c279de967dbce206777546 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 12 Jun 2019 09:42:20 +0000 Subject: [PATCH 4/4] LAPIC: Fix WOM mapping, improve IOAPIC doc --- kernel/src/devices/ioapic.rs | 7 ++++++- kernel/src/devices/lapic.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/kernel/src/devices/ioapic.rs b/kernel/src/devices/ioapic.rs index 467791b91..e4f094503 100644 --- a/kernel/src/devices/ioapic.rs +++ b/kernel/src/devices/ioapic.rs @@ -213,7 +213,12 @@ bitfield! { } impl IoApic { - /// Creates a new IO-APIC device where the region. + /// 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 /// diff --git a/kernel/src/devices/lapic.rs b/kernel/src/devices/lapic.rs index dac32187a..ae960e86b 100644 --- a/kernel/src/devices/lapic.rs +++ b/kernel/src/devices/lapic.rs @@ -572,7 +572,7 @@ impl LocalApic { pub unsafe fn new(address: PhysicalAddress) -> Self { assert!(address.addr() % PAGE_SIZE == 0, "Unaligned local APIC address"); - let lapic = get_kernel_memory().map_phys_region(PhysicalMemRegion::on_fixed_mmio(address, 0x1000).unwrap(), MappingAccessRights::WRITABLE); + let lapic = get_kernel_memory().map_phys_region(PhysicalMemRegion::on_fixed_mmio(address, 0x1000).unwrap(), MappingAccessRights::k_rw()); let lapic = LocalApic { internal: (lapic.addr() as *const UnsafeCell).as_ref().unwrap(),