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}