|
| 1 | +.. _ACPI-virt-HLD: |
| 2 | + |
| 3 | +ACPI Virtualization High Level Design |
| 4 | +##################################### |
| 5 | + |
| 6 | +ACPI introduction |
| 7 | +***************** |
| 8 | + |
| 9 | +Advanced Configuration and Power Interface (ACPI) provides an open |
| 10 | +standard that operating systems can use to discover and configure |
| 11 | +computer hardware components to perform power management for example, by |
| 12 | +monitoring status and putting unused components to sleep. |
| 13 | + |
| 14 | +Functions implemented by ACPI include: |
| 15 | + |
| 16 | +- System/Device/Processor power management |
| 17 | +- Device/Processor performance management |
| 18 | +- Configuration / Plug and Play |
| 19 | +- System event |
| 20 | +- Battery management |
| 21 | +- Thermal management |
| 22 | + |
| 23 | +ACPI enumerates and lists the different DMA engines in the platform, and |
| 24 | +device scope relationships between PCI devices and which DMA engine |
| 25 | +controls them. All critical functions depend on ACPI tables. Here's an |
| 26 | +example on an Apollo Lake platform (APL) with Linux installed: |
| 27 | + |
| 28 | +.. code-block:: none |
| 29 | +
|
| 30 | + root@:Dom0 ~ $ ls /sys/firmware/acpi/tables/ |
| 31 | + APIC data DMAR DSDT dynamic FACP FACS HPET MCFG NHLT TPM2 |
| 32 | +
|
| 33 | +These tables provide different information and functions: |
| 34 | + |
| 35 | +- Advanced Programmable Interrupt Controller (APIC) for Symmetric |
| 36 | + Multiprocessor systems (SMP), |
| 37 | +- DMA remapping (DMAR) for Intel |reg| Virtualization Technology for |
| 38 | + Directed I/O (VT-d), |
| 39 | +- Non-HD Audio Link Table (NHLT) for supporting audio device, |
| 40 | +- and Differentiated System Description Table (DSDT) for system |
| 41 | + configuration info. DSDT is a major ACPI table used to describe what |
| 42 | + peripherals the machine has, and information on PCI IRQ mappings and |
| 43 | + power management |
| 44 | + |
| 45 | +Most of the ACPI functionality is provided in ACPI Machine Language |
| 46 | +(AML) bytecode stored in the ACPI tables. To make use of these tables, |
| 47 | +Linux implements an interpreter for the AML bytecode. At BIOS |
| 48 | +development time, the AML bytecode is compiled from the ASL (ACPI Source |
| 49 | +Language) code. The ``iasl`` command is used to disassemble the ACPI table |
| 50 | +and display its contents: |
| 51 | + |
| 52 | +.. code-block:: none |
| 53 | +
|
| 54 | + root@:Dom0 ~ $ cp /sys/firmware/acpi/tables/DMAR . |
| 55 | + root@:Dom0 ~ $ iasl -d DMAR |
| 56 | +
|
| 57 | + Intel ACPI Component Architecture |
| 58 | + ASL+ Optimizing Compiler/Disassembler version 20170728 |
| 59 | + Copyright (c) 2000 - 2017 Intel Corporation |
| 60 | + Input file DMAR, Length 0xB0 (176) bytes |
| 61 | + ACPI: DMAR 0x0000000000000000 0000B0 (v01 INTEL BDW 00000001 INTL 00000001) |
| 62 | + Acpi Data Table [DMAR] decoded |
| 63 | + Formatted output: DMAR.dsl - 5286 bytes |
| 64 | +
|
| 65 | + root@:Dom0 ~ $ cat DMAR.dsl |
| 66 | + [000h 0000 4] Signature : "DMAR" [DMA Remapping table] |
| 67 | + [004h 0004 4] Table Length : 000000B0 |
| 68 | + [008h 0008 1] Revision : 01 |
| 69 | + ... |
| 70 | + [030h 0048 2] Subtable Type : 0000 [Hardware Unit Definition] |
| 71 | + [032h 0050 2] Length : 0018 |
| 72 | + [034h 0052 1] Flags : 00 |
| 73 | + [035h 0053 1] Reserved : 00 |
| 74 | + [036h 0054 2] PCI Segment Number : 0000 |
| 75 | + [038h 0056 8] Register Base Address : 00000000FED64000 |
| 76 | +
|
| 77 | +From the displayed ASL, we can see some generic table fields, such as |
| 78 | +the version information, and one VTd remapping engine description with |
| 79 | +FED64000 as base address. |
| 80 | + |
| 81 | +We can modify DMAR.dsl and assemble it again to AML: |
| 82 | + |
| 83 | +.. code-block:: none |
| 84 | +
|
| 85 | + root@:Dom0 ~ $ iasl DMAR.dsl |
| 86 | + Intel ACPI Component Architecture |
| 87 | + ASL+ Optimizing Compiler/Disassembler version 20170728 |
| 88 | + Copyright (c) 2000 - 2017 Intel Corporation |
| 89 | + Table Input: DMAR.dsl - 113 lines, 5286 bytes, 72 fields |
| 90 | + Binary Output: DMAR.aml - 176 bytes |
| 91 | + Compilation complete. 0 Errors, 0 Warnings, 0 Remarks |
| 92 | +
|
| 93 | +We can see the new AML file ``DMAR.aml`` is created. |
| 94 | + |
| 95 | +There are many ACPI tables in the system, linked together via table |
| 96 | +pointers. In all ACPI-compatible system, the OS can enumerate all |
| 97 | +needed tables starting with the Root System Description Pointer (RSDP) |
| 98 | +provided at a known place in the system low address space, and pointing |
| 99 | +to an XSDT (Extended System Description Table). The following picture |
| 100 | +shows a typical ACPI table layout in an Intel APL platform: |
| 101 | + |
| 102 | +.. figure:: images/acpi-image1.png |
| 103 | + :width: 700px |
| 104 | + :align: center |
| 105 | + :name: acpi-layout |
| 106 | + |
| 107 | + Typical ACPI table layout in an Intel APL platform |
| 108 | + |
| 109 | + |
| 110 | +ACPI virtualization |
| 111 | +******************* |
| 112 | + |
| 113 | +Most modern OSes requires ACPI, so ACRN provides ACPI virtualization to |
| 114 | +emulate an ACPI-capable virtual platform for the guest OS. To achieve |
| 115 | +this, there are two options, depending on physical device and ACPI |
| 116 | +resources are abstracted: Partitioning and Emulation. |
| 117 | + |
| 118 | +Partitioning |
| 119 | +============ |
| 120 | + |
| 121 | +One option is to assign and partition physical devices and ACPI |
| 122 | +resources among all guest OSes. That means each guest OS owns specific |
| 123 | +devices with passthrough, such as shown below: |
| 124 | + |
| 125 | ++--------------------------+--------------------------+--------------------------+ |
| 126 | +| PCI Devices | VM0(Cluster VM) | VM1(IVI VM) | |
| 127 | ++--------------------------+--------------------------+--------------------------+ |
| 128 | +| I2C | I2C3, I2C0 | I2C1, I2C2, I2C4, I2C5, | |
| 129 | +| | | I2C6, I2C7 | |
| 130 | ++--------------------------+--------------------------+--------------------------+ |
| 131 | +| SPI | SPI1 | SPI0, SPI2 | |
| 132 | ++--------------------------+--------------------------+--------------------------+ |
| 133 | +| USB | | USB-Host (xHCI) and | |
| 134 | +| | | USB-Device (xDCI) | |
| 135 | ++--------------------------+--------------------------+--------------------------+ |
| 136 | +| SDIO | | SDIO | |
| 137 | ++--------------------------+--------------------------+--------------------------+ |
| 138 | +| IPU | | IPU | |
| 139 | ++--------------------------+--------------------------+--------------------------+ |
| 140 | +| Ethernet | Ethernet | | |
| 141 | ++--------------------------+--------------------------+--------------------------+ |
| 142 | +| WIFI | | WIFI | |
| 143 | ++--------------------------+--------------------------+--------------------------+ |
| 144 | +| Bluetooth | | Bluetooth | |
| 145 | ++--------------------------+--------------------------+--------------------------+ |
| 146 | +| Audio | | Audio | |
| 147 | ++--------------------------+--------------------------+--------------------------+ |
| 148 | +| GPIO | GPIO | | |
| 149 | ++--------------------------+--------------------------+--------------------------+ |
| 150 | +| UART | UART | | |
| 151 | ++--------------------------+--------------------------+--------------------------+ |
| 152 | + |
| 153 | +In an early ACRN development phase, partitioning was used for |
| 154 | +simplicity. To implement partitioning, we need to hack the PCI logic to |
| 155 | +make different VMs see a different subset of devices, and create one |
| 156 | +copy of the ACPI tables for each of them, as shown in the following |
| 157 | +picture: |
| 158 | + |
| 159 | +.. figure:: images/acpi-image3.png |
| 160 | + :width: 900px |
| 161 | + :align: center |
| 162 | + |
| 163 | +For each VM, its ACPI tables are standalone copies and not related to |
| 164 | +other VMs. Opregion also needs to be copied for different VM. |
| 165 | + |
| 166 | +For each table, we make modifications, based on the physical table, to |
| 167 | +reflect the assigned devices to a particular VM. In the picture below, |
| 168 | +we can see keep SP2(0:19.1) for VM0, and SP1(0:19.0)/SP3(0:19.2) for |
| 169 | +VM1. Any time a partition policy changes, we need to modify both tables |
| 170 | +again, including dissembling, modification, and assembling, which is |
| 171 | +tricky and bug-prone. |
| 172 | + |
| 173 | +.. figure:: images/acpi-image2.png |
| 174 | + :width: 900px |
| 175 | + :align: center |
| 176 | + |
| 177 | +Emulation |
| 178 | +--------- |
| 179 | + |
| 180 | +A second option is for the SOS (VM0) to "own" all devices and emulate a |
| 181 | +set of virtual devices for each of the UOS (VM1). This is the most |
| 182 | +popular model for virtualization, as show below. ACRN currently uses |
| 183 | +device emulation plus some device passthrough for UOS. |
| 184 | + |
| 185 | +.. figure:: images/acpi-image5.png |
| 186 | + :width: 400px |
| 187 | + :align: center |
| 188 | + |
| 189 | +Regarding ACPI virtualization in ACRN, different policy are used for |
| 190 | +different components: |
| 191 | + |
| 192 | +- Hypervisor - ACPI is transparent to the Hypervisor, which has no |
| 193 | + knowledge of ACPI at all. |
| 194 | +- SOS - All ACPI resources are physically owned by the SOS, which |
| 195 | + enumerates all ACPI tables and devices. |
| 196 | +- UOS - Virtual ACPI resources exposed by the device model are owned by |
| 197 | + UOS. |
| 198 | + |
| 199 | +Source for the ACPI emulation code for the device model is found in |
| 200 | +``hw/platform/acpi/acpi.c``. |
| 201 | + |
| 202 | +Each entry in ``basl_ftables`` is related to each virtual ACPI table, |
| 203 | +including following elements: |
| 204 | + |
| 205 | +- wsect - output handler to write related ACPI table contents to |
| 206 | + specific file |
| 207 | +- offset - related ACPI table offset in the memory |
| 208 | +- valid - dynamically indicate if this table is needed |
| 209 | + |
| 210 | +.. code-block:: c |
| 211 | +
|
| 212 | + static struct { |
| 213 | + int (*wsect)(FILE *fp, struct vmctx *ctx); |
| 214 | + uint64_t offset; |
| 215 | + bool valid; |
| 216 | + } basl_ftables[] = { |
| 217 | + { basl_fwrite_rsdp, 0, true }, |
| 218 | + { basl_fwrite_rsdt, RSDT_OFFSET, true }, |
| 219 | + { basl_fwrite_xsdt, XSDT_OFFSET, true }, |
| 220 | + { basl_fwrite_madt, MADT_OFFSET, true }, |
| 221 | + { basl_fwrite_fadt, FADT_OFFSET, true }, |
| 222 | + { basl_fwrite_hpet, HPET_OFFSET, true }, |
| 223 | + { basl_fwrite_mcfg, MCFG_OFFSET, true }, |
| 224 | + { basl_fwrite_facs, FACS_OFFSET, true }, |
| 225 | + { basl_fwrite_nhlt, NHLT_OFFSET, false }, /*valid with audio ptdev*/ |
| 226 | + { basl_fwrite_dsdt, DSDT_OFFSET, true } |
| 227 | + }; |
| 228 | +
|
| 229 | +The main function to create virtual ACPI tables is ``acpi_build`` that |
| 230 | +calls ``basl_compile`` for each table and performs the following: |
| 231 | + |
| 232 | +#. create two temp files: infile and outfile |
| 233 | +#. with output handler, write table contents stream to infile |
| 234 | +#. use ``iasl`` tool to assemble infile into outfile |
| 235 | +#. load outfile contents to the required memory offset |
| 236 | + |
| 237 | +.. code-block:: c |
| 238 | +
|
| 239 | + static int |
| 240 | + basl_compile(struct vmctx *ctx, |
| 241 | + int (*fwrite_section)(FILE *, struct vmctx *), |
| 242 | + uint64_t offset) |
| 243 | + { |
| 244 | + struct basl_fio io[2]; |
| 245 | + static char iaslbuf[3*MAXPATHLEN + 10]; |
| 246 | + int err; |
| 247 | +
|
| 248 | + err = basl_start(&io[0], &io[1]); |
| 249 | + if (!err) { |
| 250 | + err = (*fwrite_section)(io[0].fp, ctx); |
| 251 | +
|
| 252 | + if (!err) { |
| 253 | + /* |
| 254 | + * iasl sends the results of the compilation to |
| 255 | + * stdout. Shut this down by using the shell to |
| 256 | + * redirect stdout to /dev/null, unless the user |
| 257 | + * has requested verbose output for debugging |
| 258 | + * purposes |
| 259 | + */ |
| 260 | + if (basl_verbose_iasl) |
| 261 | + snprintf(iaslbuf, sizeof(iaslbuf), |
| 262 | + "%s -p %s %s", |
| 263 | + ASL_COMPILER, |
| 264 | + io[1].f_name, io[0].f_name); |
| 265 | + else |
| 266 | + snprintf(iaslbuf, sizeof(iaslbuf), |
| 267 | + "/bin/sh -c \"%s -p %s %s\" 1> /dev/null", |
| 268 | + ASL_COMPILER, |
| 269 | + io[1].f_name, io[0].f_name); |
| 270 | +
|
| 271 | + err = system(iaslbuf); |
| 272 | +
|
| 273 | + if (!err) { |
| 274 | + /* |
| 275 | + * Copy the aml output file into guest |
| 276 | + * memory at the specified location |
| 277 | + */ |
| 278 | + err = basl_load(ctx, io[1].fd, offset); |
| 279 | + } else |
| 280 | + err = -1; |
| 281 | + } |
| 282 | + basl_end(&io[0], &io[1]); |
| 283 | + } |
| 284 | +
|
| 285 | +After processing each entry, the virtual ACPI tables are present in UOS |
| 286 | +memory. |
| 287 | + |
| 288 | +For pass-through devices in UOS, we likely need to add some ACPI |
| 289 | +description in the UOS virtual DSDT table. There is one hook |
| 290 | +(``passthrough_write_dsdt``) in ``hw/pci/passthrough.c`` for it. The following |
| 291 | +source code shows calls to different functions to add different contents |
| 292 | +for each vendor and device id. |
| 293 | + |
| 294 | +.. code-block:: c |
| 295 | +
|
| 296 | + static void |
| 297 | + passthru_write_dsdt(struct pci_vdev *dev) |
| 298 | + { |
| 299 | + struct passthru_dev *ptdev = (struct passthru_dev *) dev->arg; |
| 300 | + uint32_t vendor = 0, device = 0; |
| 301 | +
|
| 302 | + vendor = read_config(ptdev->phys_dev, PCIR_VENDOR, 2); |
| 303 | +
|
| 304 | + if (vendor != 0x8086) |
| 305 | + return; |
| 306 | +
|
| 307 | + device = read_config(ptdev->phys_dev, PCIR_DEVICE, 2); |
| 308 | +
|
| 309 | + /* Provides ACPI extra info */ |
| 310 | + if (device == 0x5aaa) |
| 311 | + /* XDCI @ 00:15.1 to enable ADB */ |
| 312 | + write_dsdt_xhci(dev); |
| 313 | + else if (device == 0x5ab4) |
| 314 | + /* HDAC @ 00:17.0 as codec */ |
| 315 | + write_dsdt_hdac(dev); |
| 316 | + else if (device == 0x5a98) |
| 317 | + /* HDAS @ 00:e.0 */ |
| 318 | + write_dsdt_hdas(dev); |
| 319 | + else if (device == 0x5aac) |
| 320 | + /* i2c @ 00:16.0 for ipu */ |
| 321 | + write_dsdt_ipu_i2c(dev); |
| 322 | + else if (device == 0x5abc) |
| 323 | + /* URT1 @ 00:18.0 for bluetooth*/ |
| 324 | + write_dsdt_urt1(dev); |
| 325 | +
|
| 326 | + } |
| 327 | +
|
| 328 | +For instance, ``write_dsdt_urt1`` provides ACPI contents for Bluetooth |
| 329 | +UART device when pass-throughed to the UOS. It provides virtual PCI |
| 330 | +device/function as ``_ADR``, with other descriptions possible for Bluetooth |
| 331 | +UART enumeration. |
| 332 | + |
| 333 | +.. code-block:: c |
| 334 | +
|
| 335 | + static void |
| 336 | + write_dsdt_urt1(struct pci_vdev *dev) |
| 337 | + { |
| 338 | + printf("write virt-%x:%x.%x in dsdt for URT1 @ 00:18.0\n", |
| 339 | + dev->bus, |
| 340 | + dev->slot, |
| 341 | + dev->func); |
| 342 | + dsdt_line("Device (URT1)"); |
| 343 | + dsdt_line("{"); |
| 344 | + dsdt_line(" Name (_ADR, 0x%04X%04X)", dev->slot, dev->func); |
| 345 | + dsdt_line(" Name (_DDN, \"Intel(R) HS-UART Controller #1\")"); |
| 346 | + dsdt_line(" Name (_UID, One)"); |
| 347 | + dsdt_line(" Name (RBUF, ResourceTemplate ()"); |
| 348 | + dsdt_line(" {"); |
| 349 | + dsdt_line(" })"); |
| 350 | + dsdt_line(" Method (_CRS, 0, NotSerialized)"); |
| 351 | + dsdt_line(" {"); |
| 352 | + dsdt_line(" Return (RBUF)"); |
| 353 | + dsdt_line(" }"); |
| 354 | + dsdt_line("}"); |
| 355 | + } |
| 356 | +
|
| 357 | +This document introduces basic ACPI virtualization. Other topics such as |
| 358 | +power management virtualization, adds more requirement for ACPI, and |
| 359 | +will be discussed in the power management documentation. |
0 commit comments