1
- use crate :: { AcpiError , } ;
2
- use core:: { fmt, mem:: MaybeUninit , str} ;
1
+ use crate :: { AcpiError , AcpiHandler , AcpiResult , AcpiTable , PhysicalMapping } ;
2
+ use core:: { fmt, mem:: MaybeUninit , str} ;
3
3
4
4
/// Represents a field which may or may not be present within an ACPI structure, depending on the version of ACPI
5
5
/// that a system supports. If the field is not present, it is not safe to treat the data as initialised.
@@ -110,14 +110,10 @@ pub struct SdtHeader {
110
110
}
111
111
112
112
impl SdtHeader {
113
- /// Check that:
114
- /// a) The signature matches the one given
115
- /// b) The checksum of the SDT is valid
116
- ///
117
- /// This assumes that the whole SDT is mapped.
118
- pub fn validate ( & self , signature : Signature ) -> Result < ( ) , AcpiError > {
113
+ /// Whether values of header fields are permitted.
114
+ fn validate_header_fields ( & self , signature : Signature ) -> AcpiResult < ( ) > {
119
115
// Check the signature
120
- if self . signature != signature {
116
+ if self . signature != signature || str :: from_utf8 ( & self . signature . 0 ) . is_err ( ) {
121
117
return Err ( AcpiError :: SdtInvalidSignature ( signature) ) ;
122
118
}
123
119
@@ -131,22 +127,81 @@ impl SdtHeader {
131
127
return Err ( AcpiError :: SdtInvalidTableId ( signature) ) ;
132
128
}
133
129
134
- // Validate the checksum
135
- let self_ptr = self as * const SdtHeader as * const u8 ;
136
- let mut sum: u8 = 0 ;
137
- for offset in 0 ..self . length {
138
- // SAFETY: Read from pointer is valid for the described table length, and all reads are read safely
139
- // via `core::ptr::read_unaligned`.
140
- sum = sum. wrapping_add ( unsafe { self_ptr. offset ( offset as isize ) . read_unaligned ( ) } as u8 ) ;
141
- }
130
+ Ok ( ( ) )
131
+ }
132
+
133
+ /// Whether table is valid according to checksum.
134
+ fn validate_checksum ( & self , signature : Signature ) -> AcpiResult < ( ) > {
135
+ // SAFETY: Entire table is mapped.
136
+ let table_bytes =
137
+ unsafe { core:: slice:: from_raw_parts ( ( self as * const SdtHeader ) . cast :: < u8 > ( ) , self . length as usize ) } ;
138
+ let sum = table_bytes. iter ( ) . fold ( 0u8 , |sum, & byte| sum. wrapping_add ( byte) ) ;
142
139
143
- if sum > 0 {
144
- return Err ( AcpiError :: SdtInvalidChecksum ( signature) ) ;
140
+ if sum == 0 {
141
+ Ok ( ( ) )
142
+ } else {
143
+ Err ( AcpiError :: SdtInvalidChecksum ( signature) )
145
144
}
145
+ }
146
+
147
+ /// Checks that:
148
+ ///
149
+ /// 1. The signature matches the one given.
150
+ /// 2. The values of various fields in the header are allowed.
151
+ /// 3. The checksum of the SDT is valid.
152
+ ///
153
+ /// This assumes that the whole SDT is mapped.
154
+ pub fn validate ( & self , signature : Signature ) -> AcpiResult < ( ) > {
155
+ self . validate_header_fields ( signature) ?;
156
+ self . validate_checksum ( signature) ?;
146
157
147
158
Ok ( ( ) )
148
159
}
149
160
161
+ /// Validates header, proceeding with checking entire table and returning a [`PhysicalMapping`] to it if
162
+ /// successful.
163
+ ///
164
+ /// The same checks are performed as [`SdtHeader::validate`], but `header_mapping` does not have to map the
165
+ /// entire table when calling. This is useful to avoid completely mapping a table that will be immediately
166
+ /// unmapped if it does not have a particular signature or has an invalid header.
167
+ pub ( crate ) fn validate_lazy < H : AcpiHandler , T : AcpiTable > (
168
+ header_mapping : PhysicalMapping < H , Self > ,
169
+ handler : H ,
170
+ ) -> AcpiResult < PhysicalMapping < H , T > > {
171
+ header_mapping. validate_header_fields ( T :: SIGNATURE ) ?;
172
+
173
+ // Reuse `header_mapping` to access the rest of the table if the latter is already mapped entirely
174
+ let table_length = header_mapping. length as usize ;
175
+ let table_mapping = if header_mapping. mapped_length ( ) >= table_length {
176
+ // Avoid requesting table unmap twice (from both `header_mapping` and `table_mapping`)
177
+ let header_mapping = core:: mem:: ManuallyDrop :: new ( header_mapping) ;
178
+
179
+ // SAFETY: `header_mapping` maps entire table.
180
+ unsafe {
181
+ PhysicalMapping :: new (
182
+ header_mapping. physical_start ( ) ,
183
+ header_mapping. virtual_start ( ) . cast :: < T > ( ) ,
184
+ table_length,
185
+ header_mapping. mapped_length ( ) ,
186
+ handler,
187
+ )
188
+ }
189
+ } else {
190
+ // Unmap header as soon as possible
191
+ let table_phys_start = header_mapping. physical_start ( ) ;
192
+ drop ( header_mapping) ;
193
+
194
+ // SAFETY: `table_phys_start` is the physical address of the header and the rest of the table.
195
+ unsafe { handler. map_physical_region ( table_phys_start, table_length) }
196
+ } ;
197
+
198
+ // This is usually redundant compared to simply calling `validate_checksum` but respects custom
199
+ // `AcpiTable::validate` implementations.
200
+ table_mapping. validate ( ) ?;
201
+
202
+ Ok ( table_mapping)
203
+ }
204
+
150
205
pub fn oem_id ( & self ) -> & str {
151
206
// Safe to unwrap because checked in `validate`
152
207
str:: from_utf8 ( & self . oem_id ) . unwrap ( )
0 commit comments