├── .gitignore ├── Cargo.toml ├── rust-toolchain └── src ├── io_apic ├── mod.rs └── values.rs ├── lib.rs └── registers.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "apic" 3 | version = "0.1.0" 4 | authors = ["Philipp Oppermann "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | bit_field = "0.10.1" 9 | volatile = "0.4.1" 10 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly -------------------------------------------------------------------------------- /src/io_apic/mod.rs: -------------------------------------------------------------------------------- 1 | use core::convert::TryInto; 2 | 3 | use bit_field::BitField; 4 | use volatile::Volatile; 5 | 6 | pub mod values; 7 | 8 | pub struct IoApicBase<'a> { 9 | pub select: Volatile<&'a mut Select>, 10 | pub window: Volatile<&'a mut u32>, 11 | } 12 | 13 | impl IoApicBase<'_> { 14 | /// base address must have 'a lifetime 15 | pub unsafe fn new(base_addr: *mut u8) -> Self { 16 | Self { 17 | select: unsafe { Self::offset(base_addr, Offset::Select) }, 18 | window: unsafe { Self::offset(base_addr, Offset::Window) }, 19 | } 20 | } 21 | 22 | pub fn read_id(&mut self) -> u8 { 23 | self.select.update(|v| v.set_index(Index::Id)); 24 | self.window.read().get_bits(24..28).try_into().unwrap() 25 | } 26 | 27 | pub fn read_version(&mut self) -> values::Version { 28 | self.select.update(|v| v.set_index(Index::Version)); 29 | values::Version::from_raw(self.window.read()) 30 | } 31 | 32 | pub fn read_arbitration(&mut self) -> values::Arbitration { 33 | self.select.update(|v| v.set_index(Index::Arbitration)); 34 | values::Arbitration::from_raw(self.window.read()) 35 | } 36 | 37 | pub fn write_arbitration(&mut self, value: values::Arbitration) { 38 | self.select.update(|v| v.set_index(Index::Arbitration)); 39 | self.window.write(value.into_raw()); 40 | } 41 | 42 | pub fn read_redirection_table_entry(&mut self, irq: u8) -> values::RedirectionTableEntry { 43 | assert!(irq < 24); 44 | 45 | let index_low = Index::RedirectionTableEntryBase as u8 + irq * 2; 46 | let index_high = index_low + 1; 47 | 48 | self.select.update(|v| v.set_index(index_low)); 49 | let low = self.window.read(); 50 | 51 | self.select.update(|v| v.set_index(index_high)); 52 | let high = self.window.read(); 53 | 54 | values::RedirectionTableEntry::from_raw(low, high) 55 | } 56 | 57 | pub fn write_redirection_table_entry(&mut self, irq: u8, value: values::RedirectionTableEntry) { 58 | assert!(irq < 24); 59 | 60 | let index_low = Index::RedirectionTableEntryBase as u8 + irq * 2; 61 | let index_high = index_low + 1; 62 | 63 | let (low, high) = value.into_raw(); 64 | 65 | self.select.update(|v| v.set_index(index_low)); 66 | self.window.write(low); 67 | 68 | self.select.update(|v| v.set_index(index_high)); 69 | self.window.write(high); 70 | } 71 | 72 | pub fn update_redirection_table_entry(&mut self, irq: u8, f: F) 73 | where 74 | F: FnOnce(&mut values::RedirectionTableEntry), 75 | { 76 | assert!(irq < 24); 77 | 78 | let mut value = self.read_redirection_table_entry(irq); 79 | f(&mut value); 80 | self.write_redirection_table_entry(irq, value); 81 | } 82 | 83 | unsafe fn offset<'a, T>(base_addr: *mut u8, offset: Offset) -> Volatile<&'a mut T> { 84 | let ptr = Self::offset_ptr(base_addr, offset).cast(); 85 | Volatile::new(unsafe { &mut *ptr }) 86 | } 87 | 88 | fn offset_ptr(base_addr: *mut u8, offset: Offset) -> *mut u8 { 89 | base_addr.wrapping_add(offset as usize) 90 | } 91 | } 92 | 93 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] 94 | #[repr(usize)] 95 | pub enum Offset { 96 | Select = 0x0, 97 | Window = 0x10, 98 | } 99 | 100 | #[repr(u8)] 101 | pub enum Index { 102 | Id = 0x0, 103 | Version = 0x1, 104 | Arbitration = 0x2, 105 | RedirectionTableEntryBase = 0x10, 106 | } 107 | 108 | impl Into for Index { 109 | fn into(self) -> u8 { 110 | self as u8 111 | } 112 | } 113 | 114 | #[derive(Debug, Copy, Clone)] 115 | #[repr(transparent)] 116 | pub struct Select(u32); 117 | 118 | impl Select { 119 | pub fn new(index: u8) -> Self { 120 | Self(index.into()) 121 | } 122 | 123 | pub fn set_index(&mut self, index: impl Into) { 124 | self.0.set_bits(0..8, index.into().into()); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/io_apic/values.rs: -------------------------------------------------------------------------------- 1 | use bit_field::BitField; 2 | use core::convert::{TryFrom, TryInto}; 3 | 4 | #[derive(Debug, Copy, Clone)] 5 | pub struct Version(u32); 6 | 7 | impl Version { 8 | pub(crate) fn from_raw(value: u32) -> Self { 9 | Self(value) 10 | } 11 | 12 | pub fn max_redirection_entry(&self) -> u8 { 13 | self.0.get_bits(16..24).try_into().unwrap() 14 | } 15 | 16 | pub fn apic_version(&self) -> u8 { 17 | self.0.get_bits(0..8).try_into().unwrap() 18 | } 19 | } 20 | 21 | #[derive(Debug, Copy, Clone)] 22 | pub struct Arbitration(u32); 23 | 24 | impl Arbitration { 25 | pub(crate) fn from_raw(value: u32) -> Self { 26 | Self(value) 27 | } 28 | 29 | pub(crate) fn into_raw(self) -> u32 { 30 | self.0 31 | } 32 | 33 | pub fn new(arbitration_id: u8) -> Self { 34 | let mut value = 0; 35 | value.set_bits(24..28, arbitration_id.into()); 36 | Self(value) 37 | } 38 | 39 | pub fn arbitration_id(&self) -> u8 { 40 | self.0.get_bits(24..28).try_into().unwrap() 41 | } 42 | } 43 | 44 | #[derive(Debug, Copy, Clone)] 45 | pub struct RedirectionTableEntry { 46 | low: u32, 47 | high: u32, 48 | } 49 | 50 | impl RedirectionTableEntry { 51 | pub(crate) fn from_raw(low: u32, high: u32) -> Self { 52 | Self { low, high } 53 | } 54 | 55 | pub(crate) fn into_raw(self) -> (u32, u32) { 56 | (self.low, self.high) 57 | } 58 | 59 | pub fn vector(&self) -> u8 { 60 | self.low.get_bits(0..8).try_into().unwrap() 61 | } 62 | 63 | pub fn set_vector(&mut self, vector: u8) { 64 | self.low.set_bits(0..8, vector.into()); 65 | } 66 | 67 | pub fn delivery_mode(&self) -> DeliveryMode { 68 | self.low.get_bits(8..11).try_into().unwrap() 69 | } 70 | 71 | pub fn set_delivery_mode(&mut self, mode: DeliveryMode) { 72 | let raw: u8 = mode.into(); 73 | self.low.set_bits(8..11, raw.into()); 74 | } 75 | 76 | pub fn destination_mode_logical(&self) -> bool { 77 | self.low.get_bit(11) 78 | } 79 | 80 | pub fn set_destination_mode_logical(&mut self, logical: bool) { 81 | self.low.set_bit(11, logical); 82 | } 83 | 84 | pub fn delivery_status_send_pending(&self) -> bool { 85 | self.low.get_bit(12) 86 | } 87 | 88 | pub fn polarity_low_active(&self) -> bool { 89 | self.low.get_bit(13) 90 | } 91 | 92 | pub fn set_polarity_low_active(&mut self, low_active: bool) { 93 | self.low.set_bit(13, low_active); 94 | } 95 | 96 | pub fn remote_irr(&self) -> bool { 97 | self.low.get_bit(14) 98 | } 99 | 100 | pub fn trigger_mode_level(&self) -> bool { 101 | self.low.get_bit(15) 102 | } 103 | 104 | pub fn set_trigger_mode_level(&mut self, level_sensitive: bool) { 105 | self.low.set_bit(15, level_sensitive); 106 | } 107 | 108 | pub fn masked(&self) -> bool { 109 | self.low.get_bit(16) 110 | } 111 | 112 | pub fn set_masked(&mut self, masked: bool) { 113 | self.low.set_bit(16, masked); 114 | } 115 | 116 | pub fn destination(&self) -> u8 { 117 | self.high.get_bits(24..32).try_into().unwrap() 118 | } 119 | 120 | pub fn set_destination(&mut self, destination: u8) { 121 | self.high.set_bits(24..32, destination.into()); 122 | } 123 | } 124 | 125 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 126 | pub enum DeliveryMode { 127 | Fixed, 128 | LowestPriority, 129 | SystemManagementInterrupt, 130 | Reserved1, 131 | NonMaskableInterrupt, 132 | Init, 133 | Reserved2, 134 | ExtInt, 135 | } 136 | 137 | impl TryFrom for DeliveryMode { 138 | type Error = (); 139 | 140 | fn try_from(value: u8) -> Result { 141 | match value { 142 | 0b000 => Ok(DeliveryMode::Fixed), 143 | 0b001 => Ok(DeliveryMode::LowestPriority), 144 | 0b010 => Ok(DeliveryMode::SystemManagementInterrupt), 145 | 0b011 => Ok(DeliveryMode::Reserved1), 146 | 0b100 => Ok(DeliveryMode::NonMaskableInterrupt), 147 | 0b101 => Ok(DeliveryMode::Init), 148 | 0b110 => Ok(DeliveryMode::Reserved2), 149 | 0b111 => Ok(DeliveryMode::ExtInt), 150 | _other => Err(()), 151 | } 152 | } 153 | } 154 | 155 | impl TryFrom for DeliveryMode { 156 | type Error = (); 157 | 158 | fn try_from(value: u32) -> Result { 159 | let value = u8::try_from(value).map_err(|_err| ())?; 160 | DeliveryMode::try_from(value) 161 | } 162 | } 163 | 164 | impl Into for DeliveryMode { 165 | fn into(self) -> u8 { 166 | match self { 167 | DeliveryMode::Fixed => 0b000, 168 | DeliveryMode::LowestPriority => 0b001, 169 | DeliveryMode::SystemManagementInterrupt => 0b010, 170 | DeliveryMode::Reserved1 => 0b011, 171 | DeliveryMode::NonMaskableInterrupt => 0b100, 172 | DeliveryMode::Init => 0b101, 173 | DeliveryMode::Reserved2 => 0b110, 174 | DeliveryMode::ExtInt => 0b111, 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(unsafe_block_in_unsafe_fn)] 3 | #![deny(unsafe_op_in_unsafe_fn)] 4 | 5 | use volatile::Volatile; 6 | 7 | pub mod io_apic; 8 | pub mod registers; 9 | 10 | pub struct ApicBase { 11 | base_addr: *mut u8, 12 | } 13 | 14 | impl ApicBase { 15 | /// base address must have 'static lifetime 16 | pub const unsafe fn new(base_addr: *mut ()) -> Self { 17 | Self { 18 | base_addr: base_addr.cast(), 19 | } 20 | } 21 | 22 | pub fn id(&mut self) -> Volatile<&mut registers::Id> { 23 | unsafe { self.offset(Offset::Id) } 24 | } 25 | 26 | pub fn version(&mut self) -> Volatile<&mut registers::Version> { 27 | unsafe { self.offset(Offset::Version) } 28 | } 29 | 30 | pub fn extended_apic_feature(&mut self) -> Volatile<&mut registers::ExtendedApicFeature> { 31 | unsafe { self.offset(Offset::ExtendedApicFeature) } 32 | } 33 | 34 | pub fn extended_apic_control(&mut self) -> Volatile<&mut registers::ExtendedApicControl> { 35 | unsafe { self.offset(Offset::ExtendedApicControl) } 36 | } 37 | 38 | pub fn spurious_interrupt_vector( 39 | &mut self, 40 | ) -> Volatile<&mut registers::SpuriousInterruptVector> { 41 | unsafe { self.offset(Offset::SpuriousInterruptVector) } 42 | } 43 | 44 | pub fn timer_local_vector_table_entry( 45 | &mut self, 46 | ) -> Volatile<&mut registers::TimerLocalVectorTableEntry> { 47 | unsafe { self.offset(Offset::TimerLocalVectorTableEntry) } 48 | } 49 | 50 | pub fn timer_initial_count(&mut self) -> Volatile<&mut registers::TimerInitialCount> { 51 | unsafe { self.offset(Offset::TimerInitialCount) } 52 | } 53 | 54 | pub fn timer_divide_configuration( 55 | &mut self, 56 | ) -> Volatile<&mut registers::TimerDivideConfiguration> { 57 | unsafe { self.offset(Offset::TimerDivideConfiguration) } 58 | } 59 | 60 | pub fn end_of_interrupt(&self) -> &'static registers::EndOfInterrupt { 61 | let ptr = self.offset_ptr(Offset::EndOfInterrupt).cast(); 62 | unsafe { &*ptr } 63 | } 64 | 65 | unsafe fn offset(&mut self, offset: Offset) -> Volatile<&mut T> { 66 | let ptr = self.offset_ptr(offset).cast(); 67 | Volatile::new(unsafe { &mut *ptr }) 68 | } 69 | 70 | fn offset_ptr(&self, offset: Offset) -> *mut u8 { 71 | self.base_addr.wrapping_add(offset as usize) 72 | } 73 | } 74 | 75 | #[repr(usize)] 76 | pub enum Offset { 77 | Id = 0x20, 78 | Version = 0x30, 79 | TaskPriority = 0x80, 80 | ArbitrationPriority = 0x90, 81 | ProcessorPriority = 0xa0, 82 | EndOfInterrupt = 0xb0, 83 | RemoteRead = 0xc0, 84 | LocalDestination = 0xd0, 85 | DestinationFormat = 0xe0, 86 | SpuriousInterruptVector = 0xf0, 87 | InService = 0x100, 88 | TriggerMode = 0x180, 89 | InterruptRequest = 0x200, 90 | ErrorStatus = 0x280, 91 | InterruptCommand = 0x300, 92 | TimerLocalVectorTableEntry = 0x320, 93 | ThermalLocalVectorTableEntry = 0x330, 94 | PerformanceCounterLocalVectorTableEntry = 0x340, 95 | LocalInterrupt0VectorTableEntry = 0x350, 96 | LocalInterrupt1VectorTableEntry = 0x360, 97 | ErrorVectorTableEntry = 0x370, 98 | TimerInitialCount = 0x380, 99 | TimerCurrentCount = 0x390, 100 | TimerDivideConfiguration = 0x3e0, 101 | ExtendedApicFeature = 0x400, 102 | ExtendedApicControl = 0x410, 103 | SpecificEndOfInterrupt = 0x420, 104 | InterruptEnable = 0x480, 105 | ExtendedInterruptLocalVectorTable = 0x500, 106 | } 107 | -------------------------------------------------------------------------------- /src/registers.rs: -------------------------------------------------------------------------------- 1 | use core::{convert::TryInto, sync::atomic::AtomicU32, sync::atomic::Ordering}; 2 | 3 | use bit_field::BitField; 4 | 5 | #[derive(Debug, Copy, Clone)] 6 | #[repr(transparent)] 7 | pub struct Id(u32); 8 | 9 | impl Id { 10 | /// Returns the unique APIC ID value assigned to this specific CPU core 11 | pub fn id(&self) -> u8 { 12 | self.0.get_bits(24..).try_into().unwrap() 13 | } 14 | } 15 | 16 | #[derive(Debug, Copy, Clone)] 17 | #[repr(transparent)] 18 | pub struct Version(u32); 19 | 20 | impl Version { 21 | /// Indicates the version number of the APIC implementation 22 | pub fn version(&self) -> u8 { 23 | self.0.get_bits(..8).try_into().unwrap() 24 | } 25 | 26 | /// Specifies the number of entries in the localvector table minus one 27 | pub fn max_lvt_entries(&self) -> u8 { 28 | self.0.get_bits(16..24).try_into().unwrap() 29 | } 30 | 31 | /// Indicates thepresence of an extended APIC register space 32 | pub fn extended_apic_register_space_present(&self) -> bool { 33 | self.0.get_bit(31) 34 | } 35 | } 36 | 37 | #[derive(Debug, Copy, Clone)] 38 | #[repr(transparent)] 39 | pub struct ExtendedApicFeature(u32); 40 | 41 | impl ExtendedApicFeature { 42 | /// Specifies the number of extended local vector tableregisters in the local APIC 43 | pub fn extended_lvt_count(&self) -> u8 { 44 | self.0.get_bits(16..24).try_into().unwrap() 45 | } 46 | 47 | /// Indicates that the processor is capable ofsupporting an 8-bit APIC ID 48 | pub fn extended_apic_id_capability(&self) -> bool { 49 | self.0.get_bit(2) 50 | } 51 | 52 | /// Indicates that the Specific End Of Interrupt Register is present 53 | pub fn specific_end_of_interrupt_capable(&self) -> bool { 54 | self.0.get_bit(1) 55 | } 56 | 57 | /// Indicates that the Interrupt EnableRegisters are present 58 | pub fn interrupt_enable_register_capable(&self) -> bool { 59 | self.0.get_bit(1) 60 | } 61 | } 62 | 63 | #[derive(Debug, Copy, Clone)] 64 | #[repr(transparent)] 65 | pub struct ExtendedApicControl(u32); 66 | 67 | impl ExtendedApicControl { 68 | pub fn extended_apic_id_enabled(&self) -> bool { 69 | self.0.get_bit(2) 70 | } 71 | 72 | pub fn enable_extended_apic_id(&mut self, enable: bool) { 73 | self.0.set_bit(2, enable); 74 | } 75 | 76 | pub fn specific_end_of_interrupt_generation_enabled(&self) -> bool { 77 | self.0.get_bit(1) 78 | } 79 | 80 | pub fn enable_specific_end_of_interrupt_generation(&mut self, enable: bool) { 81 | self.0.set_bit(1, enable); 82 | } 83 | 84 | pub fn interrupt_enable_registers_enabled(&self) -> bool { 85 | self.0.get_bit(0) 86 | } 87 | 88 | pub fn enable_interrupt_enable_registers(&mut self, enable: bool) { 89 | self.0.set_bit(0, enable); 90 | } 91 | } 92 | 93 | #[derive(Debug, Copy, Clone)] 94 | #[repr(transparent)] 95 | pub struct SpuriousInterruptVector(u32); 96 | 97 | impl SpuriousInterruptVector { 98 | pub fn vector(&self) -> u8 { 99 | self.0.get_bits(..8).try_into().unwrap() 100 | } 101 | 102 | pub fn set_vector(&mut self, vector: u8) { 103 | self.0.set_bits(..8, vector.into()); 104 | } 105 | 106 | pub fn apic_software_enabled(&self) -> bool { 107 | self.0.get_bit(8) 108 | } 109 | 110 | pub fn enable_apic_software(&mut self, enable: bool) { 111 | self.0.set_bit(8, enable); 112 | } 113 | 114 | pub fn focus_cpu_core_checking(&self) -> bool { 115 | self.0.get_bit(9) 116 | } 117 | 118 | pub fn set_focus_cpu_core_checking(&mut self, value: bool) { 119 | self.0.set_bit(9, value); 120 | } 121 | } 122 | 123 | #[derive(Debug, Copy, Clone)] 124 | #[repr(transparent)] 125 | pub struct TimerLocalVectorTableEntry(u32); 126 | 127 | impl TimerLocalVectorTableEntry { 128 | pub fn vector(&self) -> u8 { 129 | self.0.get_bits(..8).try_into().unwrap() 130 | } 131 | 132 | pub fn set_vector(&mut self, vector: u8) { 133 | self.0.set_bits(..8, vector.into()); 134 | } 135 | 136 | pub fn delivery_status(&self) -> bool { 137 | self.0.get_bit(12) 138 | } 139 | 140 | pub fn mask(&self) -> bool { 141 | self.0.get_bit(16) 142 | } 143 | 144 | pub fn set_mask(&mut self, disable: bool) { 145 | self.0.set_bit(16, disable); 146 | } 147 | 148 | pub fn timer_mode(&self) -> bool { 149 | self.0.get_bit(17) 150 | } 151 | 152 | pub fn set_timer_mode(&mut self, periodic: bool) { 153 | self.0.set_bit(17, periodic); 154 | } 155 | } 156 | 157 | #[derive(Debug, Copy, Clone)] 158 | #[repr(transparent)] 159 | pub struct TimerInitialCount(u32); 160 | 161 | impl TimerInitialCount { 162 | pub fn get(&self) -> u32 { 163 | self.0 164 | } 165 | 166 | pub fn set(&mut self, inital_count: u32) { 167 | self.0 = inital_count 168 | } 169 | } 170 | 171 | #[derive(Debug, Copy, Clone)] 172 | #[repr(transparent)] 173 | pub struct TimerDivideConfiguration(u32); 174 | 175 | impl TimerDivideConfiguration { 176 | pub fn get(&self) -> TimerDivideConfigurationValue { 177 | let bit_0 = self.0.get_bit(0); 178 | let bit_1 = self.0.get_bit(1); 179 | let bit_3 = self.0.get_bit(3); 180 | match (bit_3, bit_1, bit_0) { 181 | (false, false, false) => TimerDivideConfigurationValue::Divide2, 182 | (false, false, true) => TimerDivideConfigurationValue::Divide4, 183 | (false, true, false) => TimerDivideConfigurationValue::Divide8, 184 | (false, true, true) => TimerDivideConfigurationValue::Divide16, 185 | (true, false, false) => TimerDivideConfigurationValue::Divide32, 186 | (true, false, true) => TimerDivideConfigurationValue::Divide64, 187 | (true, true, false) => TimerDivideConfigurationValue::Divide128, 188 | (true, true, true) => TimerDivideConfigurationValue::Divide1, 189 | } 190 | } 191 | 192 | pub fn set(&mut self, value: TimerDivideConfigurationValue) { 193 | let (bit_3, bit_1, bit_0) = match value { 194 | TimerDivideConfigurationValue::Divide2 => (false, false, false), 195 | TimerDivideConfigurationValue::Divide4 => (false, false, true), 196 | TimerDivideConfigurationValue::Divide8 => (false, true, false), 197 | TimerDivideConfigurationValue::Divide16 => (false, true, true), 198 | TimerDivideConfigurationValue::Divide32 => (true, false, false), 199 | TimerDivideConfigurationValue::Divide64 => (true, false, true), 200 | TimerDivideConfigurationValue::Divide128 => (true, true, false), 201 | TimerDivideConfigurationValue::Divide1 => (true, true, true), 202 | }; 203 | self.0.set_bit(0, bit_0); 204 | self.0.set_bit(1, bit_1); 205 | self.0.set_bit(3, bit_3); 206 | } 207 | } 208 | 209 | #[derive(Debug, Copy, Clone)] 210 | pub enum TimerDivideConfigurationValue { 211 | Divide1, 212 | Divide2, 213 | Divide4, 214 | Divide8, 215 | Divide16, 216 | Divide32, 217 | Divide64, 218 | Divide128, 219 | } 220 | 221 | #[derive(Debug)] 222 | #[repr(transparent)] 223 | pub struct EndOfInterrupt(AtomicU32); 224 | 225 | impl EndOfInterrupt { 226 | pub fn signal(&self) { 227 | self.0.store(0, Ordering::Release); 228 | } 229 | } 230 | --------------------------------------------------------------------------------