devices/pci/acpi.rs
1// Copyright 2022 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use acpi_tables::aml;
6use acpi_tables::aml::Aml;
7use anyhow::anyhow;
8use anyhow::Result;
9use base::MemoryMapping;
10use base::MemoryMappingBuilder;
11use base::Protection;
12use base::SharedMemory;
13
14pub const SHM_OFFSET: u32 = 0x1000;
15pub const SHM_SIZE: u32 = 0x1000;
16
17pub struct DeviceVcfgRegister {
18 offset: u32,
19 shm: SharedMemory,
20}
21
22impl DeviceVcfgRegister {
23 pub fn new(offset: u32) -> Result<DeviceVcfgRegister> {
24 let shm = SharedMemory::new("VCFG register", SHM_SIZE as u64)
25 .map_err(|_| anyhow!("failed to create shared memory"))?;
26 Ok(DeviceVcfgRegister { offset, shm })
27 }
28
29 pub fn create_shm_mmap(&self) -> Option<MemoryMapping> {
30 MemoryMappingBuilder::new(SHM_SIZE as usize)
31 .from_shared_memory(&self.shm)
32 .offset(0)
33 .protection(Protection::read_write())
34 .build()
35 .ok()
36 }
37}
38
39impl Aml for DeviceVcfgRegister {
40 fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
41 aml::OpRegion::new(
42 "VREG".into(),
43 aml::OpRegionSpace::SystemMemory,
44 &aml::Add::new(&aml::ZERO, &aml::Name::new_field_name("VCFG"), &self.offset),
45 &4096_usize,
46 )
47 .to_aml_bytes(bytes);
48 aml::Field::new(
49 "VREG".into(),
50 aml::FieldAccessType::DWord,
51 aml::FieldLockRule::Lock,
52 aml::FieldUpdateRule::Preserve,
53 vec![
54 aml::FieldEntry::Named(*b"PFPM", 32),
55 aml::FieldEntry::Named(*b"PDSM", 32),
56 aml::FieldEntry::Named(*b"NOTY", 32),
57 ],
58 )
59 .to_aml_bytes(bytes);
60 aml::OpRegion::new(
61 "SHAM".into(),
62 aml::OpRegionSpace::SystemMemory,
63 &aml::Add::new(
64 &aml::ZERO,
65 &aml::Name::new_field_name("VCFG"),
66 &(self.offset + SHM_OFFSET),
67 ),
68 &SHM_SIZE,
69 )
70 .to_aml_bytes(bytes);
71 aml::Field::new(
72 "SHAM".into(),
73 aml::FieldAccessType::Any,
74 aml::FieldLockRule::Lock,
75 aml::FieldUpdateRule::Preserve,
76 vec![
77 aml::FieldEntry::Named(*b"DSM0", 128),
78 aml::FieldEntry::Named(*b"DSM1", 64),
79 aml::FieldEntry::Named(*b"DSM2", 64),
80 aml::FieldEntry::Named(*b"DSM3", 16384),
81 ],
82 )
83 .to_aml_bytes(bytes);
84 aml::Field::new(
85 "SHAM".into(),
86 aml::FieldAccessType::DWord,
87 aml::FieldLockRule::Lock,
88 aml::FieldUpdateRule::Preserve,
89 vec![
90 aml::FieldEntry::Reserved(256),
91 aml::FieldEntry::Named(*b"RTTP", 32),
92 aml::FieldEntry::Named(*b"RTSZ", 32),
93 aml::FieldEntry::Named(*b"RTDT", 16576),
94 ],
95 )
96 .to_aml_bytes(bytes);
97 }
98}
99
100pub struct DsmMethod {}
101
102const ACPI_TYPE_INT: &dyn Aml = &1_usize;
103const ACPI_TYPE_STRING: &dyn Aml = &2_usize;
104const ACPI_TYPE_BUFFER: &dyn Aml = &3_usize;
105const ACPI_TYPE_PACKAGE: &dyn Aml = &4_usize;
106
107// The ACPI _DSM methods are described under:
108// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/09_ACPI-Defined_Devices_and_Device-Specific_Objects/ACPIdefined_Devices_and_DeviceSpecificObjects.html?highlight=_dsm#dsm-device-specific-method
109//
110// Since the guest does not have access to native ACPI tables, whenever native driver for the
111// pass-through device, which resides in guest, evaluates _DSM methods, such evaluation needs to be
112// propagated to the host which can do the actual job.
113//
114// Below snippet generates AML code, which implements virtual _DSM method in guest ACPI tables.
115// Its role is to collect and pass guest _DSM arguments into host (through shared memory). When all
116// arguments are saved in shared memory, access to PDSM is issued which causes a trap to VMM. As a
117// consequence VMM can read passed _DSM arguments and pass them further (through dedicated IOCTL)
118// to the host kernel, which can actually evaluate the ACPI _DSM method using native tables. The
119// results are passed back from ioctl to VMM and further to the guest through shared memory.
120impl Aml for DsmMethod {
121 fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
122 aml::Method::new(
123 "_DSM".into(),
124 4,
125 true,
126 vec![
127 &aml::Store::new(&aml::Name::new_field_name("DSM0"), &aml::Arg(0)),
128 &aml::Store::new(&aml::Name::new_field_name("DSM1"), &aml::Arg(1)),
129 &aml::Store::new(&aml::Name::new_field_name("DSM2"), &aml::Arg(2)),
130 &aml::Store::new(&aml::Local(2), &aml::ObjectType::new(&aml::Arg(3))),
131 &aml::Store::new(&aml::Local(1), &aml::SizeOf::new(&aml::Arg(3))),
132 &aml::Store::new(&aml::Local(0), &aml::BufferTerm::new(&16384_usize)),
133 &aml::If::new(
134 &aml::Equal::new(&aml::Local(2), ACPI_TYPE_BUFFER),
135 vec![
136 &aml::CreateDWordField::new(
137 &aml::Name::new_field_name("BFTP"),
138 &aml::Local(0),
139 &0_usize,
140 ),
141 &aml::CreateDWordField::new(
142 &aml::Name::new_field_name("BFSZ"),
143 &aml::Local(0),
144 &4_usize,
145 ),
146 &aml::CreateField::new(
147 &aml::Name::new_field_name("BFDT"),
148 &aml::Local(0),
149 &(8_usize * 8_usize),
150 &aml::Multiply::new(&aml::ZERO, &aml::Local(1), &8_usize),
151 ),
152 &aml::Store::new(&aml::Name::new_field_name("BFTP"), ACPI_TYPE_BUFFER),
153 &aml::Store::new(&aml::Name::new_field_name("BFSZ"), &aml::Local(1)),
154 &aml::Store::new(&aml::Name::new_field_name("BFDT"), &aml::Arg(3)),
155 ],
156 ),
157 &aml::Else::new(vec![
158 &aml::If::new(
159 &aml::Equal::new(&aml::Local(2), ACPI_TYPE_PACKAGE),
160 vec![
161 &aml::Store::new(&aml::Local(5), &aml::ZERO),
162 &aml::CreateDWordField::new(
163 &aml::Name::new_field_name("PKTP"),
164 &aml::Local(0),
165 &aml::Local(5),
166 ),
167 &aml::Store::new(&aml::Name::new_field_name("PKTP"), ACPI_TYPE_PACKAGE),
168 &aml::Add::new(&aml::Local(5), &aml::Local(5), &4_usize),
169 &aml::CreateDWordField::new(
170 &aml::Name::new_field_name("PKSZ"),
171 &aml::Local(0),
172 &aml::Local(5),
173 ),
174 &aml::Store::new(&aml::Name::new_field_name("PKSZ"), &aml::Local(1)),
175 &aml::Add::new(&aml::Local(5), &aml::Local(5), &4_usize),
176 &aml::Store::new(&aml::Local(2), &aml::ZERO),
177 &aml::While::new(
178 &aml::LessThan::new(&aml::Local(2), &aml::Local(1)),
179 vec![
180 &aml::Store::new(
181 &aml::Local(3),
182 &aml::DeRefOf::new(&aml::Index::new(
183 &aml::ZERO,
184 &aml::Arg(3),
185 &aml::Local(2),
186 )),
187 ),
188 &aml::Store::new(
189 &aml::Local(4),
190 &aml::ObjectType::new(&aml::Local(3)),
191 ),
192 &aml::Store::new(
193 &aml::Local(6),
194 &aml::SizeOf::new(&aml::Local(3)),
195 ),
196 &aml::CreateDWordField::new(
197 &aml::Name::new_field_name("OUTP"),
198 &aml::Local(0),
199 &aml::Local(5),
200 ),
201 &aml::Store::new(
202 &aml::Name::new_field_name("OUTP"),
203 &aml::Local(4),
204 ),
205 &aml::Add::new(&aml::Local(5), &aml::Local(5), &4_usize),
206 &aml::CreateDWordField::new(
207 &aml::Name::new_field_name("OUSZ"),
208 &aml::Local(0),
209 &aml::Local(5),
210 ),
211 &aml::Store::new(
212 &aml::Name::new_field_name("OUSZ"),
213 &aml::Local(6),
214 ),
215 &aml::Add::new(&aml::Local(5), &aml::Local(5), &4_usize),
216 &aml::If::new(
217 &aml::Equal::new(&aml::Local(4), ACPI_TYPE_INT),
218 vec![
219 &aml::CreateQWordField::new(
220 &aml::Name::new_field_name("OUDT"),
221 &aml::Local(0),
222 &aml::Local(5),
223 ),
224 &aml::Store::new(
225 &aml::Name::new_field_name("OUDT"),
226 &aml::Local(3),
227 ),
228 &aml::Add::new(
229 &aml::Local(5),
230 &aml::Local(5),
231 &8_usize,
232 ),
233 ],
234 ),
235 &aml::Else::new(vec![
236 &aml::If::new(
237 &aml::Equal::new(&aml::Local(4), ACPI_TYPE_STRING),
238 vec![
239 &aml::CreateField::new(
240 &aml::Name::new_field_name("OSDT"),
241 &aml::Local(0),
242 &aml::Multiply::new(
243 &aml::ZERO,
244 &aml::Local(5),
245 &8_usize,
246 ),
247 &aml::Multiply::new(
248 &aml::ZERO,
249 &aml::Local(6),
250 &8_usize,
251 ),
252 ),
253 &aml::Store::new(
254 &aml::Name::new_field_name("OSDT"),
255 &aml::Local(3),
256 ),
257 &aml::And::new(
258 &aml::Local(7),
259 &aml::Local(6),
260 &7_usize,
261 ),
262 &aml::If::new(
263 &aml::NotEqual::new(&aml::Local(7), &aml::ZERO),
264 vec![&aml::Add::new(
265 &aml::Local(6),
266 &aml::Local(6),
267 &8_usize,
268 )],
269 ),
270 &aml::Subtract::new(
271 &aml::Local(6),
272 &aml::Local(6),
273 &aml::Local(7),
274 ),
275 &aml::Add::new(
276 &aml::Local(5),
277 &aml::Local(5),
278 &aml::Local(6),
279 ),
280 ],
281 ),
282 &aml::Else::new(vec![&aml::If::new(
283 &aml::Equal::new(&aml::Local(4), ACPI_TYPE_BUFFER),
284 vec![
285 &aml::CreateField::new(
286 &aml::Name::new_field_name("OBDT"),
287 &aml::Local(0),
288 &aml::Multiply::new(
289 &aml::ZERO,
290 &aml::Local(5),
291 &8_usize,
292 ),
293 &aml::Multiply::new(
294 &aml::ZERO,
295 &aml::Local(6),
296 &8_usize,
297 ),
298 ),
299 &aml::Store::new(
300 &aml::Name::new_field_name("OBDT"),
301 &aml::Local(3),
302 ),
303 &aml::And::new(
304 &aml::Local(7),
305 &aml::Local(6),
306 &7_usize,
307 ),
308 &aml::If::new(
309 &aml::NotEqual::new(&aml::Local(7), &aml::ZERO),
310 vec![&aml::Add::new(
311 &aml::Local(6),
312 &aml::Local(6),
313 &8_usize,
314 )],
315 ),
316 &aml::Subtract::new(
317 &aml::Local(6),
318 &aml::Local(6),
319 &aml::Local(7),
320 ),
321 &aml::Add::new(
322 &aml::Local(5),
323 &aml::Local(5),
324 &aml::Local(6),
325 ),
326 ],
327 )]),
328 ]),
329 &aml::Add::new(&aml::Local(2), &aml::Local(2), &aml::ONE),
330 ],
331 ),
332 ],
333 ),
334 &aml::Else::new(vec![&aml::Return::new(&aml::ZERO)]),
335 ]),
336 &aml::Store::new(&aml::Name::new_field_name("DSM3"), &aml::Local(0)),
337 // All DSM arguments are written to shared memory, lets access PDSM which will trap
338 // to VMM which can process it further. The result will be stored in shared memory.
339 &aml::Store::new(&aml::Name::new_field_name("PDSM"), &aml::ZERO),
340 // Lets start converting the _DSM result stored in shared memory into proper format
341 // which will allow to return result in desired format to the guest caller.
342 &aml::Store::new(
343 &aml::Local(0),
344 &aml::ToInteger::new(&aml::ZERO, &aml::Name::new_field_name("RTTP")),
345 ),
346 &aml::If::new(
347 &aml::Equal::new(&aml::Local(0), ACPI_TYPE_INT),
348 vec![&aml::Return::new(&aml::ToInteger::new(
349 &aml::ZERO,
350 &aml::Name::new_field_name("RTDT"),
351 ))],
352 ),
353 &aml::Else::new(vec![
354 &aml::If::new(
355 &aml::Equal::new(&aml::Local(0), ACPI_TYPE_STRING),
356 vec![&aml::Return::new(&aml::ToString::new(
357 &aml::ZERO,
358 &aml::Name::new_field_name("RTDT"),
359 &aml::ONES,
360 ))],
361 ),
362 &aml::Else::new(vec![
363 &aml::If::new(
364 &aml::Equal::new(&aml::Local(0), ACPI_TYPE_BUFFER),
365 vec![&aml::Return::new(&aml::Mid::new(
366 &aml::Name::new_field_name("RTDT"),
367 &0_usize,
368 &aml::ToInteger::new(
369 &aml::ZERO,
370 &aml::Name::new_field_name("RTSZ"),
371 ),
372 &aml::ZERO,
373 ))],
374 ),
375 &aml::Else::new(vec![
376 &aml::If::new(
377 &aml::Equal::new(&aml::Local(0), ACPI_TYPE_PACKAGE),
378 vec![
379 &aml::Store::new(&aml::Local(0), &aml::ZERO),
380 &aml::Store::new(
381 &aml::Local(1),
382 &aml::ToInteger::new(
383 &aml::ZERO,
384 &aml::Name::new_field_name("RTSZ"),
385 ),
386 ),
387 &aml::Store::new(
388 &aml::Local(2),
389 &aml::VarPackageTerm::new(&aml::Local(1)),
390 ),
391 &aml::Store::new(&aml::Local(3), &aml::ZERO),
392 &aml::While::new(
393 &aml::LessThan::new(&aml::Local(0), &aml::Local(1)),
394 vec![
395 &aml::Store::new(
396 &aml::Local(4),
397 &aml::ToInteger::new(
398 &aml::ZERO,
399 &aml::Mid::new(
400 &aml::Name::new_field_name("RTDT"),
401 &aml::Local(3),
402 &4_usize,
403 &aml::ZERO,
404 ),
405 ),
406 ),
407 &aml::Add::new(
408 &aml::Local(3),
409 &aml::Local(3),
410 &4_usize,
411 ),
412 &aml::Store::new(
413 &aml::Local(5),
414 &aml::ToInteger::new(
415 &aml::ZERO,
416 &aml::Mid::new(
417 &aml::Name::new_field_name("RTDT"),
418 &aml::Local(3),
419 &4_usize,
420 &aml::ZERO,
421 ),
422 ),
423 ),
424 &aml::Add::new(
425 &aml::Local(3),
426 &aml::Local(3),
427 &4_usize,
428 ),
429 &aml::Store::new(
430 &aml::Local(6),
431 &aml::Mid::new(
432 &aml::Name::new_field_name("RTDT"),
433 &aml::Local(3),
434 &aml::Local(5),
435 &aml::ZERO,
436 ),
437 ),
438 &aml::Add::new(
439 &aml::Local(3),
440 &aml::Local(3),
441 &aml::Local(5),
442 ),
443 &aml::If::new(
444 &aml::Equal::new(&aml::Local(4), ACPI_TYPE_INT),
445 vec![&aml::Store::new(
446 &aml::Local(6),
447 &aml::ToInteger::new(
448 &aml::ZERO,
449 &aml::Local(6),
450 ),
451 )],
452 ),
453 &aml::Else::new(vec![&aml::If::new(
454 &aml::Equal::new(&aml::Local(4), ACPI_TYPE_STRING),
455 vec![&aml::Store::new(
456 &aml::Local(6),
457 &aml::ToString::new(
458 &aml::ZERO,
459 &aml::Local(6),
460 &aml::ONES,
461 ),
462 )],
463 )]),
464 &aml::Store::new(
465 &aml::Index::new(
466 &aml::ZERO,
467 &aml::Local(2),
468 &aml::Local(0),
469 ),
470 &aml::Local(6),
471 ),
472 &aml::Add::new(
473 &aml::Local(0),
474 &aml::Local(0),
475 &aml::ONE,
476 ),
477 ],
478 ),
479 &aml::Return::new(&aml::Local(2)),
480 ],
481 ),
482 &aml::Else::new(vec![&aml::Return::new(&aml::ZERO)]),
483 ]),
484 ]),
485 ]),
486 ],
487 )
488 .to_aml_bytes(aml);
489 }
490}
491
492pub struct PowerResourceMethod {}
493
494impl Aml for PowerResourceMethod {
495 fn to_aml_bytes(&self, aml: &mut Vec<u8>) {
496 aml::PowerResource::new(
497 "PRIC".into(),
498 0u8,
499 0u16,
500 vec![
501 &aml::Name::new("_STA".into(), &aml::ONE),
502 &aml::Method::new(
503 "_ON_".into(),
504 0,
505 true,
506 vec![
507 &aml::Store::new(&aml::Name::new_field_name("PFPM"), &aml::ONE),
508 &aml::Store::new(&aml::Name::new_field_name("_STA"), &aml::ONE),
509 ],
510 ),
511 &aml::Method::new(
512 "_OFF".into(),
513 0,
514 true,
515 vec![
516 &aml::Store::new(&aml::Name::new_field_name("_STA"), &aml::ZERO),
517 &aml::Store::new(&aml::Name::new_field_name("PFPM"), &aml::ZERO),
518 ],
519 ),
520 ],
521 )
522 .to_aml_bytes(aml);
523 aml::Name::new(
524 "_PR0".into(),
525 &aml::Package::new(vec![&aml::Name::new_field_name("PRIC")]),
526 )
527 .to_aml_bytes(aml);
528 aml::Name::new(
529 "_PR3".into(),
530 &aml::Package::new(vec![&aml::Name::new_field_name("PRIC")]),
531 )
532 .to_aml_bytes(aml);
533 }
534}
535
536pub struct GpeScope {}
537
538impl GpeScope {
539 pub fn cast_to_aml_bytes(&self, aml: &mut Vec<u8>, gpe_nr: u32, notification_path: &str) {
540 aml::Scope::new(
541 "_GPE".into(),
542 vec![&aml::Method::new(
543 format!("_E{gpe_nr:02X}").as_str().into(),
544 0,
545 false,
546 vec![
547 &aml::Store::new(
548 &aml::Local(0),
549 &aml::Path::new(format!("{notification_path}.NOTY").as_str()),
550 ),
551 &aml::Notify::new(&aml::Path::new(notification_path), &aml::Local(0)),
552 ],
553 )],
554 )
555 .to_aml_bytes(aml);
556 }
557}