1
1
//! ACPI defines a Generic Address Structure (GAS), which provides a versatile way to describe register locations
2
2
//! in a wide range of address spaces.
3
3
4
- use crate :: AcpiError ;
4
+ use crate :: { AcpiError , Handler , PhysicalMapping } ;
5
+ use log:: warn;
5
6
6
7
/// This is the raw form of a Generic Address Structure, and follows the layout found in the ACPI tables.
7
8
#[ derive( Clone , Copy , Debug ) ]
@@ -28,8 +29,8 @@ impl RawGenericAddress {
28
29
pub enum AddressSpace {
29
30
SystemMemory ,
30
31
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:
33
34
/// ```ignore
34
35
/// 64 48 32 16 0
35
36
/// +---------------+---------------+---------------+---------------+
@@ -117,3 +118,126 @@ impl GenericAddress {
117
118
StandardAccessSize :: try_from ( self . access_size )
118
119
}
119
120
}
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