Skip to content

Commit 404ab12

Browse files
committed
Implement basic IO on mapped GASs
1 parent 588a602 commit 404ab12

File tree

2 files changed

+131
-3
lines changed

2 files changed

+131
-3
lines changed

src/address.rs

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! ACPI defines a Generic Address Structure (GAS), which provides a versatile way to describe register locations
22
//! in a wide range of address spaces.
33
4-
use crate::AcpiError;
4+
use crate::{AcpiError, Handler, PhysicalMapping};
5+
use log::warn;
56

67
/// This is the raw form of a Generic Address Structure, and follows the layout found in the ACPI tables.
78
#[derive(Clone, Copy, Debug)]
@@ -28,8 +29,8 @@ impl RawGenericAddress {
2829
pub enum AddressSpace {
2930
SystemMemory,
3031
SystemIo,
31-
/// Describes a register in the configuration space of a PCI device in segment `0`, on bus `0`. The `address`
32-
/// field is of the format:
32+
/// Describes a register in the configuration space of a PCI device in segment `0`, on bus `0`.
33+
/// The `address` field is of the format:
3334
/// ```ignore
3435
/// 64 48 32 16 0
3536
/// +---------------+---------------+---------------+---------------+
@@ -117,3 +118,126 @@ impl GenericAddress {
117118
StandardAccessSize::try_from(self.access_size)
118119
}
119120
}
121+
122+
pub struct MappedGas<H: Handler> {
123+
gas: GenericAddress,
124+
handler: H,
125+
mapping: Option<PhysicalMapping<H, u8>>,
126+
}
127+
128+
impl<H> MappedGas<H>
129+
where
130+
H: Handler,
131+
{
132+
pub unsafe fn map_gas(gas: GenericAddress, handler: &H) -> Result<MappedGas<H>, AcpiError> {
133+
match gas.address_space {
134+
AddressSpace::SystemMemory => {
135+
// TODO: how to know total size needed?
136+
let mapping = unsafe { handler.map_physical_region(gas.address as usize, 0x1000) };
137+
Ok(MappedGas { gas, handler: handler.clone(), mapping: Some(mapping) })
138+
}
139+
AddressSpace::SystemIo => Ok(MappedGas { gas, handler: handler.clone(), mapping: None }),
140+
other => {
141+
warn!("Tried to map GAS of unsupported type {:?}", other);
142+
Err(AcpiError::LibUnimplemented)
143+
}
144+
}
145+
}
146+
147+
pub fn read(&self) -> Result<u64, AcpiError> {
148+
/*
149+
* TODO: this is only correct for basic GASs that require a single access. Extend it to
150+
* support bit offsets and multiple reads etc.
151+
*/
152+
let access_size_bits = gas_decode_access_bit_width(self.gas)?;
153+
match self.gas.address_space {
154+
AddressSpace::SystemMemory => {
155+
let mapping = self.mapping.as_ref().unwrap();
156+
let value = match access_size_bits {
157+
8 => unsafe { core::ptr::read_volatile(mapping.virtual_start.as_ptr() as *const u8) as u64 },
158+
16 => unsafe { core::ptr::read_volatile(mapping.virtual_start.as_ptr() as *const u16) as u64 },
159+
32 => unsafe { core::ptr::read_volatile(mapping.virtual_start.as_ptr() as *const u32) as u64 },
160+
64 => unsafe { core::ptr::read_volatile(mapping.virtual_start.as_ptr() as *const u64) },
161+
_ => panic!(),
162+
};
163+
Ok(value)
164+
}
165+
AddressSpace::SystemIo => {
166+
let value = match access_size_bits {
167+
8 => self.handler.read_io_u8(self.gas.address as u16) as u64,
168+
16 => self.handler.read_io_u16(self.gas.address as u16) as u64,
169+
32 => self.handler.read_io_u32(self.gas.address as u16) as u64,
170+
_ => panic!(),
171+
};
172+
Ok(value)
173+
}
174+
_ => unimplemented!(),
175+
}
176+
}
177+
178+
pub fn write(&self, value: u64) -> Result<(), AcpiError> {
179+
// TODO: see above
180+
let access_size_bits = gas_decode_access_bit_width(self.gas)?;
181+
match self.gas.address_space {
182+
AddressSpace::SystemMemory => {
183+
let mapping = self.mapping.as_ref().unwrap();
184+
match access_size_bits {
185+
8 => unsafe {
186+
core::ptr::write_volatile(mapping.virtual_start.as_ptr() as *mut u8, value as u8);
187+
},
188+
16 => unsafe {
189+
core::ptr::write_volatile(mapping.virtual_start.as_ptr() as *mut u16, value as u16);
190+
},
191+
32 => unsafe {
192+
core::ptr::write_volatile(mapping.virtual_start.as_ptr() as *mut u32, value as u32);
193+
},
194+
64 => unsafe { core::ptr::write_volatile(mapping.virtual_start.as_ptr() as *mut u64, value) },
195+
_ => panic!(),
196+
}
197+
Ok(())
198+
}
199+
AddressSpace::SystemIo => {
200+
match access_size_bits {
201+
8 => self.handler.write_io_u8(self.gas.address as u16, value as u8),
202+
16 => self.handler.write_io_u16(self.gas.address as u16, value as u16),
203+
32 => self.handler.write_io_u32(self.gas.address as u16, value as u32),
204+
_ => panic!(),
205+
}
206+
Ok(())
207+
}
208+
_ => unimplemented!(),
209+
}
210+
}
211+
}
212+
213+
/// Returns the access size that should be made for a given `GenericAddress`, in bits.
214+
fn gas_decode_access_bit_width(gas: GenericAddress) -> Result<u8, AcpiError> {
215+
/*
216+
* This is more complex than it should be - we follow ACPICA to try and work with quirky
217+
* firmwares.
218+
*
219+
* We should actually ignore the access sizes for normal registers (they tend to be unspecified
220+
* in my experience anyway) and base our accesses on the width of the register. Only if a
221+
* register has a bit width that cannot be accessed as a single native access do we look at the
222+
* access size.
223+
*
224+
* We use a third method, based on the alignment of the address, for registers that have
225+
* non-zero bit offsets. These are not typically encountered in normal registers - they very
226+
* often mean the GAS has come from APEI (ACPI Platform Error Interface), and so needs speical
227+
* handling.
228+
*/
229+
if gas.bit_offset == 0 && [8, 16, 32, 64].contains(&gas.bit_width) {
230+
Ok(gas.bit_width)
231+
} else if gas.access_size != 0 {
232+
match gas.access_size {
233+
1 => Ok(8),
234+
2 => Ok(16),
235+
3 => Ok(32),
236+
4 => Ok(64),
237+
_ => Err(AcpiError::InvalidGenericAddress),
238+
}
239+
} else {
240+
// TODO: work out access size based on alignment of the address
241+
todo!()
242+
}
243+
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ pub enum AcpiError {
267267
#[cfg(feature = "alloc")]
268268
Aml(aml::AmlError),
269269

270+
/// This is emitted to signal that the library does not support the requested behaviour. This
271+
/// should eventually never be emitted.
272+
LibUnimplemented,
273+
270274
/// This can be returned by the host (user of the library) to signal that required behaviour
271275
/// has not been implemented. This will cause the error to be propagated back to the host if an
272276
/// operation that requires that behaviour is performed.

0 commit comments

Comments
 (0)