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 {
2829pub 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+ }
0 commit comments