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 5 use acpi_tables::aml; 6 use acpi_tables::aml::Aml; 7 use anyhow::anyhow; 8 use anyhow::Result; 9 use base::MemoryMapping; 10 use base::MemoryMappingBuilder; 11 use base::Protection; 12 use base::SharedMemory; 13 14 pub const SHM_OFFSET: u32 = 0x1000; 15 pub const SHM_SIZE: u32 = 0x1000; 16 17 pub struct DeviceVcfgRegister { 18 offset: u32, 19 shm: SharedMemory, 20 } 21 22 impl DeviceVcfgRegister { new(offset: u32) -> Result<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 create_shm_mmap(&self) -> Option<MemoryMapping>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 39 impl Aml for DeviceVcfgRegister { to_aml_bytes(&self, bytes: &mut Vec<u8>)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 100 pub struct DsmMethod {} 101 102 const ACPI_TYPE_INT: &dyn Aml = &1_usize; 103 const ACPI_TYPE_STRING: &dyn Aml = &2_usize; 104 const ACPI_TYPE_BUFFER: &dyn Aml = &3_usize; 105 const 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. 120 impl Aml for DsmMethod { to_aml_bytes(&self, aml: &mut Vec<u8>)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 492 pub struct PowerResourceMethod {} 493 494 impl Aml for PowerResourceMethod { to_aml_bytes(&self, aml: &mut Vec<u8>)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 536 pub struct GpeScope {} 537 538 impl GpeScope { cast_to_aml_bytes(&self, aml: &mut Vec<u8>, gpe_nr: u32, notification_path: &str)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{:02X}", gpe_nr).as_str().into(), 544 0, 545 false, 546 vec![ 547 &aml::Store::new( 548 &aml::Local(0), 549 &aml::Path::new(format!("{}.NOTY", notification_path).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 } 558