2015-05-29 12:28:54 +02:00
|
|
|
/* Support for generating ACPI tables and passing them to Guests
|
|
|
|
*
|
|
|
|
* ARM virt ACPI generation
|
|
|
|
*
|
|
|
|
* Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
|
|
|
|
* Copyright (C) 2006 Fabrice Bellard
|
|
|
|
* Copyright (C) 2013 Red Hat Inc
|
|
|
|
*
|
|
|
|
* Author: Michael S. Tsirkin <mst@redhat.com>
|
|
|
|
*
|
|
|
|
* Copyright (c) 2015 HUAWEI TECHNOLOGIES CO.,LTD.
|
|
|
|
*
|
|
|
|
* Author: Shannon Zhao <zhaoshenglong@huawei.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2015-12-07 17:23:45 +01:00
|
|
|
#include "qemu/osdep.h"
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 09:01:28 +01:00
|
|
|
#include "qapi/error.h"
|
2015-05-29 12:28:54 +02:00
|
|
|
#include "qemu/bitmap.h"
|
|
|
|
#include "trace.h"
|
2019-07-09 17:20:52 +02:00
|
|
|
#include "hw/core/cpu.h"
|
2016-10-11 08:56:52 +02:00
|
|
|
#include "target/arm/cpu.h"
|
2015-05-29 12:28:54 +02:00
|
|
|
#include "hw/acpi/acpi-defs.h"
|
|
|
|
#include "hw/acpi/acpi.h"
|
|
|
|
#include "hw/nvram/fw_cfg.h"
|
|
|
|
#include "hw/acpi/bios-linker-loader.h"
|
|
|
|
#include "hw/acpi/aml-build.h"
|
2019-06-10 03:18:30 +02:00
|
|
|
#include "hw/acpi/utils.h"
|
2019-04-19 02:30:51 +02:00
|
|
|
#include "hw/acpi/pci.h"
|
2019-09-18 15:06:27 +02:00
|
|
|
#include "hw/acpi/memory_hotplug.h"
|
|
|
|
#include "hw/acpi/generic_event_device.h"
|
2020-06-01 11:57:36 +02:00
|
|
|
#include "hw/acpi/tpm.h"
|
2015-05-29 12:28:57 +02:00
|
|
|
#include "hw/pci/pcie_host.h"
|
2015-05-29 12:28:59 +02:00
|
|
|
#include "hw/pci/pci.h"
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
#include "hw/pci/pci_bus.h"
|
2020-09-28 12:42:47 +02:00
|
|
|
#include "hw/pci-host/gpex.h"
|
2017-01-09 12:40:22 +01:00
|
|
|
#include "hw/arm/virt.h"
|
2020-04-21 14:59:30 +02:00
|
|
|
#include "hw/mem/nvdimm.h"
|
2020-06-22 16:06:19 +02:00
|
|
|
#include "hw/platform-bus.h"
|
2016-05-12 14:22:28 +02:00
|
|
|
#include "sysemu/numa.h"
|
2019-08-12 07:23:38 +02:00
|
|
|
#include "sysemu/reset.h"
|
2020-06-01 11:57:36 +02:00
|
|
|
#include "sysemu/tpm.h"
|
2016-10-04 14:28:09 +02:00
|
|
|
#include "kvm_arm.h"
|
2019-08-12 07:23:45 +02:00
|
|
|
#include "migration/vmstate.h"
|
2020-05-12 05:06:03 +02:00
|
|
|
#include "hw/acpi/ghes.h"
|
2015-05-29 12:28:54 +02:00
|
|
|
|
2015-05-29 12:28:55 +02:00
|
|
|
#define ARM_SPI_BASE 32
|
|
|
|
|
2020-11-19 02:48:38 +01:00
|
|
|
#define ACPI_BUILD_TABLE_SIZE 0x20000
|
|
|
|
|
2020-12-15 18:48:15 +01:00
|
|
|
static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms)
|
2015-05-29 12:28:55 +02:00
|
|
|
{
|
2020-12-15 18:48:15 +01:00
|
|
|
MachineState *ms = MACHINE(vms);
|
2015-05-29 12:28:55 +02:00
|
|
|
uint16_t i;
|
|
|
|
|
2020-12-15 18:48:15 +01:00
|
|
|
for (i = 0; i < ms->smp.cpus; i++) {
|
2016-09-06 20:52:17 +02:00
|
|
|
Aml *dev = aml_device("C%.03X", i);
|
2015-05-29 12:28:55 +02:00
|
|
|
aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007")));
|
|
|
|
aml_append(dev, aml_name_decl("_UID", aml_int(i)));
|
|
|
|
aml_append(scope, dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
2015-12-17 14:37:13 +01:00
|
|
|
uint32_t uart_irq)
|
2015-05-29 12:28:55 +02:00
|
|
|
{
|
|
|
|
Aml *dev = aml_device("COM0");
|
|
|
|
aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011")));
|
|
|
|
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
|
|
|
|
|
|
|
|
Aml *crs = aml_resource_template();
|
|
|
|
aml_append(crs, aml_memory32_fixed(uart_memmap->base,
|
|
|
|
uart_memmap->size, AML_READ_WRITE));
|
|
|
|
aml_append(crs,
|
|
|
|
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
2015-12-17 14:37:13 +01:00
|
|
|
AML_EXCLUSIVE, &uart_irq, 1));
|
2015-05-29 12:28:55 +02:00
|
|
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
2015-06-15 19:06:11 +02:00
|
|
|
|
2015-05-29 12:28:55 +02:00
|
|
|
aml_append(scope, dev);
|
|
|
|
}
|
|
|
|
|
2016-02-19 19:20:28 +01:00
|
|
|
static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap)
|
|
|
|
{
|
|
|
|
Aml *dev = aml_device("FWCF");
|
|
|
|
aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
|
|
|
|
/* device present, functioning, decoding, not shown in UI */
|
|
|
|
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
2017-02-10 18:40:29 +01:00
|
|
|
aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
|
2016-02-19 19:20:28 +01:00
|
|
|
|
|
|
|
Aml *crs = aml_resource_template();
|
|
|
|
aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base,
|
|
|
|
fw_cfg_memmap->size, AML_READ_WRITE));
|
|
|
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
|
|
|
aml_append(scope, dev);
|
|
|
|
}
|
|
|
|
|
2015-05-29 12:28:55 +02:00
|
|
|
static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
|
|
|
|
{
|
|
|
|
Aml *dev, *crs;
|
|
|
|
hwaddr base = flash_memmap->base;
|
2015-09-24 02:29:37 +02:00
|
|
|
hwaddr size = flash_memmap->size / 2;
|
2015-05-29 12:28:55 +02:00
|
|
|
|
|
|
|
dev = aml_device("FLS0");
|
|
|
|
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
|
|
|
|
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
|
|
|
|
|
|
|
|
crs = aml_resource_template();
|
|
|
|
aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE));
|
|
|
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
|
|
|
aml_append(scope, dev);
|
|
|
|
|
|
|
|
dev = aml_device("FLS1");
|
|
|
|
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
|
|
|
|
aml_append(dev, aml_name_decl("_UID", aml_int(1)));
|
|
|
|
crs = aml_resource_template();
|
|
|
|
aml_append(crs, aml_memory32_fixed(base + size, size, AML_READ_WRITE));
|
|
|
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
|
|
|
aml_append(scope, dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void acpi_dsdt_add_virtio(Aml *scope,
|
|
|
|
const MemMapEntry *virtio_mmio_memmap,
|
2015-12-17 14:37:13 +01:00
|
|
|
uint32_t mmio_irq, int num)
|
2015-05-29 12:28:55 +02:00
|
|
|
{
|
|
|
|
hwaddr base = virtio_mmio_memmap->base;
|
|
|
|
hwaddr size = virtio_mmio_memmap->size;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < num; i++) {
|
2015-12-17 14:37:13 +01:00
|
|
|
uint32_t irq = mmio_irq + i;
|
2015-05-29 12:28:55 +02:00
|
|
|
Aml *dev = aml_device("VR%02u", i);
|
|
|
|
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005")));
|
|
|
|
aml_append(dev, aml_name_decl("_UID", aml_int(i)));
|
2017-02-10 18:40:29 +01:00
|
|
|
aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
|
2015-05-29 12:28:55 +02:00
|
|
|
|
|
|
|
Aml *crs = aml_resource_template();
|
|
|
|
aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE));
|
|
|
|
aml_append(crs,
|
|
|
|
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
2015-12-17 14:37:13 +01:00
|
|
|
AML_EXCLUSIVE, &irq, 1));
|
2015-05-29 12:28:55 +02:00
|
|
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
|
|
|
aml_append(scope, dev);
|
|
|
|
base += size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-17 14:37:13 +01:00
|
|
|
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
|
2020-11-19 02:48:37 +01:00
|
|
|
uint32_t irq, bool use_highmem, bool highmem_ecam,
|
|
|
|
VirtMachineState *vms)
|
2015-05-29 12:28:59 +02:00
|
|
|
{
|
2018-06-22 14:28:37 +02:00
|
|
|
int ecam_id = VIRT_ECAM_ID(highmem_ecam);
|
2020-09-28 12:42:47 +02:00
|
|
|
struct GPEXConfig cfg = {
|
|
|
|
.mmio32 = memmap[VIRT_PCIE_MMIO],
|
|
|
|
.pio = memmap[VIRT_PCIE_PIO],
|
|
|
|
.ecam = memmap[ecam_id],
|
|
|
|
.irq = irq,
|
2020-11-19 02:48:37 +01:00
|
|
|
.bus = vms->bus,
|
2020-09-28 12:42:47 +02:00
|
|
|
};
|
2015-05-29 12:28:59 +02:00
|
|
|
|
2015-09-07 11:39:29 +02:00
|
|
|
if (use_highmem) {
|
2020-09-28 12:42:47 +02:00
|
|
|
cfg.mmio64 = memmap[VIRT_HIGH_PCIE_MMIO];
|
2015-09-07 11:39:29 +02:00
|
|
|
}
|
|
|
|
|
2020-09-28 12:42:47 +02:00
|
|
|
acpi_dsdt_add_gpex(scope, &cfg);
|
2015-05-29 12:28:59 +02:00
|
|
|
}
|
|
|
|
|
2015-12-17 14:37:14 +01:00
|
|
|
static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap,
|
|
|
|
uint32_t gpio_irq)
|
|
|
|
{
|
|
|
|
Aml *dev = aml_device("GPO0");
|
|
|
|
aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0061")));
|
|
|
|
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
|
|
|
|
|
|
|
|
Aml *crs = aml_resource_template();
|
|
|
|
aml_append(crs, aml_memory32_fixed(gpio_memmap->base, gpio_memmap->size,
|
|
|
|
AML_READ_WRITE));
|
|
|
|
aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
|
|
|
AML_EXCLUSIVE, &gpio_irq, 1));
|
|
|
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
2015-12-17 14:37:14 +01:00
|
|
|
|
|
|
|
Aml *aei = aml_resource_template();
|
|
|
|
/* Pin 3 for power button */
|
|
|
|
const uint32_t pin_list[1] = {3};
|
|
|
|
aml_append(aei, aml_gpio_int(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
|
|
|
|
AML_EXCLUSIVE, AML_PULL_UP, 0, pin_list, 1,
|
|
|
|
"GPO0", NULL, 0));
|
|
|
|
aml_append(dev, aml_name_decl("_AEI", aei));
|
|
|
|
|
|
|
|
/* _E03 is handle for power button */
|
|
|
|
Aml *method = aml_method("_E03", 0, AML_NOTSERIALIZED);
|
|
|
|
aml_append(method, aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
|
|
|
|
aml_int(0x80)));
|
|
|
|
aml_append(dev, method);
|
2015-12-17 14:37:14 +01:00
|
|
|
aml_append(scope, dev);
|
|
|
|
}
|
|
|
|
|
2021-06-15 16:21:18 +02:00
|
|
|
#ifdef CONFIG_TPM
|
2020-06-22 16:06:19 +02:00
|
|
|
static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms)
|
|
|
|
{
|
|
|
|
PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);
|
|
|
|
hwaddr pbus_base = vms->memmap[VIRT_PLATFORM_BUS].base;
|
|
|
|
SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find());
|
|
|
|
MemoryRegion *sbdev_mr;
|
|
|
|
hwaddr tpm_base;
|
|
|
|
|
|
|
|
if (!sbdev) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
|
|
|
|
assert(tpm_base != -1);
|
|
|
|
|
|
|
|
tpm_base += pbus_base;
|
|
|
|
|
|
|
|
sbdev_mr = sysbus_mmio_get_region(sbdev, 0);
|
|
|
|
|
|
|
|
Aml *dev = aml_device("TPM0");
|
|
|
|
aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
|
|
|
|
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
|
|
|
|
|
|
|
|
Aml *crs = aml_resource_template();
|
|
|
|
aml_append(crs,
|
|
|
|
aml_memory32_fixed(tpm_base,
|
|
|
|
(uint32_t)memory_region_size(sbdev_mr),
|
|
|
|
AML_READ_WRITE));
|
|
|
|
aml_append(dev, aml_name_decl("_CRS", crs));
|
|
|
|
aml_append(scope, dev);
|
|
|
|
}
|
2021-06-15 16:21:18 +02:00
|
|
|
#endif
|
2020-06-22 16:06:19 +02:00
|
|
|
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
/* Build the iort ID mapping to SMMUv3 for a given PCI host bridge */
|
|
|
|
static int
|
|
|
|
iort_host_bridges(Object *obj, void *opaque)
|
|
|
|
{
|
|
|
|
GArray *idmap_blob = opaque;
|
|
|
|
|
|
|
|
if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) {
|
|
|
|
PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus;
|
|
|
|
|
|
|
|
if (bus && !pci_bus_bypass_iommu(bus)) {
|
|
|
|
int min_bus, max_bus;
|
|
|
|
|
|
|
|
pci_bus_range(bus, &min_bus, &max_bus);
|
|
|
|
|
|
|
|
AcpiIortIdMapping idmap = {
|
|
|
|
.input_base = min_bus << 8,
|
|
|
|
.id_count = (max_bus - min_bus + 1) << 8,
|
|
|
|
};
|
|
|
|
g_array_append_val(idmap_blob, idmap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int iort_idmap_compare(gconstpointer a, gconstpointer b)
|
|
|
|
{
|
|
|
|
AcpiIortIdMapping *idmap_a = (AcpiIortIdMapping *)a;
|
|
|
|
AcpiIortIdMapping *idmap_b = (AcpiIortIdMapping *)b;
|
|
|
|
|
|
|
|
return idmap_a->input_base - idmap_b->input_base;
|
|
|
|
}
|
|
|
|
|
2021-09-24 14:27:55 +02:00
|
|
|
/*
|
|
|
|
* Input Output Remapping Table (IORT)
|
|
|
|
* Conforms to "IO Remapping Table System Software on ARM Platforms",
|
|
|
|
* Document number: ARM DEN 0049B, October 2015
|
|
|
|
*/
|
2016-10-24 17:26:54 +02:00
|
|
|
static void
|
2018-05-04 19:05:52 +02:00
|
|
|
build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
2016-10-24 17:26:54 +02:00
|
|
|
{
|
2021-09-24 14:27:55 +02:00
|
|
|
int i, nb_nodes, rc_mapping_count;
|
2016-10-24 17:26:54 +02:00
|
|
|
AcpiIortIdMapping *idmap;
|
|
|
|
AcpiIortItsGroup *its;
|
2018-05-04 19:05:52 +02:00
|
|
|
AcpiIortSmmu3 *smmu;
|
2016-10-24 17:26:54 +02:00
|
|
|
AcpiIortRC *rc;
|
2021-09-24 14:27:55 +02:00
|
|
|
const uint32_t iort_node_offset = 48;
|
|
|
|
size_t node_size, smmu_offset = 0;
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
GArray *smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
|
|
|
|
GArray *its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
|
2016-10-24 17:26:54 +02:00
|
|
|
|
2021-09-24 14:27:55 +02:00
|
|
|
AcpiTable table = { .sig = "IORT", .rev = 0, .oem_id = vms->oem_id,
|
|
|
|
.oem_table_id = vms->oem_table_id };
|
2016-10-24 17:26:54 +02:00
|
|
|
|
2018-05-04 19:05:52 +02:00
|
|
|
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
AcpiIortIdMapping next_range = {0};
|
|
|
|
|
|
|
|
object_child_foreach_recursive(object_get_root(),
|
|
|
|
iort_host_bridges, smmu_idmaps);
|
|
|
|
|
|
|
|
/* Sort the smmu idmap by input_base */
|
|
|
|
g_array_sort(smmu_idmaps, iort_idmap_compare);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Split the whole RIDs by mapping from RC to SMMU,
|
|
|
|
* build the ID mapping from RC to ITS directly.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < smmu_idmaps->len; i++) {
|
|
|
|
idmap = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i);
|
|
|
|
|
|
|
|
if (next_range.input_base < idmap->input_base) {
|
|
|
|
next_range.id_count = idmap->input_base - next_range.input_base;
|
|
|
|
g_array_append_val(its_idmaps, next_range);
|
|
|
|
}
|
|
|
|
|
|
|
|
next_range.input_base = idmap->input_base + idmap->id_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Append the last RC -> ITS ID mapping */
|
|
|
|
if (next_range.input_base < 0xFFFF) {
|
|
|
|
next_range.id_count = 0xFFFF - next_range.input_base;
|
|
|
|
g_array_append_val(its_idmaps, next_range);
|
|
|
|
}
|
|
|
|
|
2018-05-04 19:05:52 +02:00
|
|
|
nb_nodes = 3; /* RC, ITS, SMMUv3 */
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
rc_mapping_count = smmu_idmaps->len + its_idmaps->len;
|
2018-05-04 19:05:52 +02:00
|
|
|
} else {
|
|
|
|
nb_nodes = 2; /* RC, ITS */
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
rc_mapping_count = 1;
|
2018-05-04 19:05:52 +02:00
|
|
|
}
|
|
|
|
|
2021-09-24 14:27:55 +02:00
|
|
|
/* Table 2 The IORT */
|
|
|
|
acpi_table_begin(&table, table_data);
|
|
|
|
/* Number of IORT Nodes */
|
|
|
|
build_append_int_noprefix(table_data, nb_nodes, 4);
|
|
|
|
/* Offset to Array of IORT Nodes */
|
|
|
|
build_append_int_noprefix(table_data, iort_node_offset, 4);
|
|
|
|
build_append_int_noprefix(table_data, 0, 4); /* Reserved */
|
2016-10-24 17:26:54 +02:00
|
|
|
|
|
|
|
/* ITS group node */
|
|
|
|
node_size = sizeof(*its) + sizeof(uint32_t);
|
|
|
|
its = acpi_data_push(table_data, node_size);
|
|
|
|
|
|
|
|
its->type = ACPI_IORT_NODE_ITS_GROUP;
|
|
|
|
its->length = cpu_to_le16(node_size);
|
|
|
|
its->its_count = cpu_to_le32(1);
|
|
|
|
its->identifiers[0] = 0; /* MADT translation_id */
|
|
|
|
|
2018-05-04 19:05:52 +02:00
|
|
|
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
|
2019-03-15 12:12:28 +01:00
|
|
|
int irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE;
|
2018-05-04 19:05:52 +02:00
|
|
|
|
|
|
|
/* SMMUv3 node */
|
2018-05-31 15:50:53 +02:00
|
|
|
smmu_offset = iort_node_offset + node_size;
|
2018-05-04 19:05:52 +02:00
|
|
|
node_size = sizeof(*smmu) + sizeof(*idmap);
|
|
|
|
smmu = acpi_data_push(table_data, node_size);
|
|
|
|
|
|
|
|
smmu->type = ACPI_IORT_NODE_SMMU_V3;
|
|
|
|
smmu->length = cpu_to_le16(node_size);
|
|
|
|
smmu->mapping_count = cpu_to_le32(1);
|
|
|
|
smmu->mapping_offset = cpu_to_le32(sizeof(*smmu));
|
|
|
|
smmu->base_address = cpu_to_le64(vms->memmap[VIRT_SMMU].base);
|
2019-01-21 11:23:11 +01:00
|
|
|
smmu->flags = cpu_to_le32(ACPI_IORT_SMMU_V3_COHACC_OVERRIDE);
|
2018-05-04 19:05:52 +02:00
|
|
|
smmu->event_gsiv = cpu_to_le32(irq);
|
|
|
|
smmu->pri_gsiv = cpu_to_le32(irq + 1);
|
2021-04-02 10:47:31 +02:00
|
|
|
smmu->sync_gsiv = cpu_to_le32(irq + 2);
|
|
|
|
smmu->gerr_gsiv = cpu_to_le32(irq + 3);
|
2018-05-04 19:05:52 +02:00
|
|
|
|
|
|
|
/* Identity RID mapping covering the whole input RID range */
|
|
|
|
idmap = &smmu->id_mapping_array[0];
|
|
|
|
idmap->input_base = 0;
|
|
|
|
idmap->id_count = cpu_to_le32(0xFFFF);
|
|
|
|
idmap->output_base = 0;
|
|
|
|
/* output IORT node is the ITS group node (the first node) */
|
2018-05-31 15:50:53 +02:00
|
|
|
idmap->output_reference = cpu_to_le32(iort_node_offset);
|
2018-05-04 19:05:52 +02:00
|
|
|
}
|
|
|
|
|
2016-10-24 17:26:54 +02:00
|
|
|
/* Root Complex Node */
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
node_size = sizeof(*rc) + sizeof(*idmap) * rc_mapping_count;
|
2016-10-24 17:26:54 +02:00
|
|
|
rc = acpi_data_push(table_data, node_size);
|
|
|
|
|
|
|
|
rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
|
|
|
|
rc->length = cpu_to_le16(node_size);
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
rc->mapping_count = cpu_to_le32(rc_mapping_count);
|
2016-10-24 17:26:54 +02:00
|
|
|
rc->mapping_offset = cpu_to_le32(sizeof(*rc));
|
|
|
|
|
|
|
|
/* fully coherent device */
|
|
|
|
rc->memory_properties.cache_coherency = cpu_to_le32(1);
|
|
|
|
rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */
|
|
|
|
rc->pci_segment_number = 0; /* MCFG pci_segment */
|
|
|
|
|
2018-05-04 19:05:52 +02:00
|
|
|
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
AcpiIortIdMapping *range;
|
|
|
|
|
|
|
|
/* translated RIDs connect to SMMUv3 node: RC -> SMMUv3 -> ITS */
|
|
|
|
for (i = 0; i < smmu_idmaps->len; i++) {
|
|
|
|
idmap = &rc->id_mapping_array[i];
|
|
|
|
range = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i);
|
|
|
|
|
|
|
|
idmap->input_base = cpu_to_le32(range->input_base);
|
|
|
|
idmap->id_count = cpu_to_le32(range->id_count);
|
|
|
|
idmap->output_base = cpu_to_le32(range->input_base);
|
|
|
|
/* output IORT node is the smmuv3 node */
|
|
|
|
idmap->output_reference = cpu_to_le32(smmu_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bypassed RIDs connect to ITS group node directly: RC -> ITS */
|
|
|
|
for (i = 0; i < its_idmaps->len; i++) {
|
|
|
|
idmap = &rc->id_mapping_array[smmu_idmaps->len + i];
|
|
|
|
range = &g_array_index(its_idmaps, AcpiIortIdMapping, i);
|
|
|
|
|
|
|
|
idmap->input_base = cpu_to_le32(range->input_base);
|
|
|
|
idmap->id_count = cpu_to_le32(range->id_count);
|
|
|
|
idmap->output_base = cpu_to_le32(range->input_base);
|
|
|
|
/* output IORT node is the ITS group node (the first node) */
|
|
|
|
idmap->output_reference = cpu_to_le32(iort_node_offset);
|
|
|
|
}
|
2018-05-04 19:05:52 +02:00
|
|
|
} else {
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
/* Identity RID mapping covering the whole input RID range */
|
|
|
|
idmap = &rc->id_mapping_array[0];
|
|
|
|
idmap->input_base = cpu_to_le32(0);
|
|
|
|
idmap->id_count = cpu_to_le32(0xFFFF);
|
|
|
|
idmap->output_base = cpu_to_le32(0);
|
2018-05-04 19:05:52 +02:00
|
|
|
/* output IORT node is the ITS group node (the first node) */
|
2018-05-31 15:50:53 +02:00
|
|
|
idmap->output_reference = cpu_to_le32(iort_node_offset);
|
2018-05-04 19:05:52 +02:00
|
|
|
}
|
2016-10-24 17:26:54 +02:00
|
|
|
|
2021-09-24 14:27:55 +02:00
|
|
|
acpi_table_end(linker, &table);
|
hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true \
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-07-08 14:55:16 +02:00
|
|
|
g_array_free(smmu_idmaps, true);
|
|
|
|
g_array_free(its_idmaps, true);
|
2016-10-24 17:26:54 +02:00
|
|
|
}
|
|
|
|
|
2015-06-15 19:06:11 +02:00
|
|
|
static void
|
2017-01-09 12:40:22 +01:00
|
|
|
build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
2015-06-15 19:06:11 +02:00
|
|
|
{
|
|
|
|
AcpiSerialPortConsoleRedirection *spcr;
|
2017-01-09 12:40:22 +01:00
|
|
|
const MemMapEntry *uart_memmap = &vms->memmap[VIRT_UART];
|
|
|
|
int irq = vms->irqmap[VIRT_UART] + ARM_SPI_BASE;
|
2018-01-11 14:25:34 +01:00
|
|
|
int spcr_start = table_data->len;
|
2015-06-15 19:06:11 +02:00
|
|
|
|
|
|
|
spcr = acpi_data_push(table_data, sizeof(*spcr));
|
|
|
|
|
|
|
|
spcr->interface_type = 0x3; /* ARM PL011 UART */
|
|
|
|
|
|
|
|
spcr->base_address.space_id = AML_SYSTEM_MEMORY;
|
|
|
|
spcr->base_address.bit_width = 8;
|
|
|
|
spcr->base_address.bit_offset = 0;
|
|
|
|
spcr->base_address.access_width = 1;
|
|
|
|
spcr->base_address.address = cpu_to_le64(uart_memmap->base);
|
|
|
|
|
|
|
|
spcr->interrupt_types = (1 << 3); /* Bit[3] ARMH GIC interrupt */
|
|
|
|
spcr->gsi = cpu_to_le32(irq); /* Global System Interrupt */
|
|
|
|
|
|
|
|
spcr->baud = 3; /* Baud Rate: 3 = 9600 */
|
|
|
|
spcr->parity = 0; /* No Parity */
|
|
|
|
spcr->stopbits = 1; /* 1 Stop bit */
|
|
|
|
spcr->flowctrl = (1 << 1); /* Bit[1] = RTS/CTS hardware flow control */
|
|
|
|
spcr->term_type = 0; /* Terminal Type: 0 = VT100 */
|
|
|
|
|
|
|
|
spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */
|
|
|
|
spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */
|
|
|
|
|
2018-01-11 14:25:34 +01:00
|
|
|
build_header(linker, table_data, (void *)(table_data->data + spcr_start),
|
2021-01-19 01:32:13 +01:00
|
|
|
"SPCR", table_data->len - spcr_start, 2, vms->oem_id,
|
|
|
|
vms->oem_table_id);
|
2015-06-15 19:06:11 +02:00
|
|
|
}
|
|
|
|
|
2021-09-24 14:27:44 +02:00
|
|
|
/*
|
|
|
|
* ACPI spec, Revision 5.1
|
|
|
|
* 5.2.16 System Resource Affinity Table (SRAT)
|
|
|
|
*/
|
2016-05-12 14:22:28 +02:00
|
|
|
static void
|
2017-01-09 12:40:22 +01:00
|
|
|
build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
2016-05-12 14:22:28 +02:00
|
|
|
{
|
2021-09-24 14:27:43 +02:00
|
|
|
int i;
|
2016-05-12 14:22:28 +02:00
|
|
|
uint64_t mem_base;
|
2017-05-10 13:29:54 +02:00
|
|
|
MachineClass *mc = MACHINE_GET_CLASS(vms);
|
2019-08-09 08:57:22 +02:00
|
|
|
MachineState *ms = MACHINE(vms);
|
|
|
|
const CPUArchIdList *cpu_list = mc->possible_cpu_arch_ids(ms);
|
2021-09-24 14:27:43 +02:00
|
|
|
AcpiTable table = { .sig = "SRAT", .rev = 3, .oem_id = vms->oem_id,
|
|
|
|
.oem_table_id = vms->oem_table_id };
|
2016-05-12 14:22:28 +02:00
|
|
|
|
2021-09-24 14:27:43 +02:00
|
|
|
acpi_table_begin(&table, table_data);
|
|
|
|
build_append_int_noprefix(table_data, 1, 4); /* Reserved */
|
|
|
|
build_append_int_noprefix(table_data, 0, 8); /* Reserved */
|
2016-05-12 14:22:28 +02:00
|
|
|
|
2017-05-10 13:29:54 +02:00
|
|
|
for (i = 0; i < cpu_list->len; ++i) {
|
2021-09-24 14:27:44 +02:00
|
|
|
uint32_t nodeid = cpu_list->cpus[i].props.node_id;
|
|
|
|
/*
|
|
|
|
* 5.2.16.4 GICC Affinity Structure
|
|
|
|
*/
|
|
|
|
build_append_int_noprefix(table_data, 3, 1); /* Type */
|
|
|
|
build_append_int_noprefix(table_data, 18, 1); /* Length */
|
|
|
|
build_append_int_noprefix(table_data, nodeid, 4); /* Proximity Domain */
|
|
|
|
build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */
|
|
|
|
/* Flags, Table 5-76 */
|
|
|
|
build_append_int_noprefix(table_data, 1 /* Enabled */, 4);
|
|
|
|
build_append_int_noprefix(table_data, 0, 4); /* Clock Domain */
|
2016-05-12 14:22:28 +02:00
|
|
|
}
|
|
|
|
|
2017-01-09 12:40:22 +01:00
|
|
|
mem_base = vms->memmap[VIRT_MEM].base;
|
2019-08-09 08:57:22 +02:00
|
|
|
for (i = 0; i < ms->numa_state->num_nodes; ++i) {
|
2019-08-09 08:57:24 +02:00
|
|
|
if (ms->numa_state->nodes[i].node_mem > 0) {
|
2021-09-24 14:27:44 +02:00
|
|
|
build_srat_memory(table_data, mem_base,
|
2019-08-09 08:57:24 +02:00
|
|
|
ms->numa_state->nodes[i].node_mem, i,
|
2018-09-25 15:02:30 +02:00
|
|
|
MEM_AFFINITY_ENABLED);
|
2019-08-09 08:57:24 +02:00
|
|
|
mem_base += ms->numa_state->nodes[i].node_mem;
|
2018-09-25 15:02:30 +02:00
|
|
|
}
|
2016-05-12 14:22:28 +02:00
|
|
|
}
|
|
|
|
|
hw/acpi/nvdimm: add a helper to augment SRAT generation
NVDIMMs can belong to their own proximity domains, as described by the
NFIT. In such cases, the SRAT needs to have Memory Affinity structures
in the SRAT for these NVDIMMs, otherwise Linux doesn't populate node
data structures properly during NUMA initialization. See the following
for an example failure case.
https://lore.kernel.org/linux-nvdimm/20200416225438.15208-1-vishal.l.verma@intel.com/
Introduce a new helper, nvdimm_build_srat(), and call it for both the
i386 and arm versions of 'build_srat()' to augment the SRAT with
memory affinity information for NVDIMMs.
The relevant command line options to exercise this are below. Nodes 0-1
contain CPUs and regular memory, and nodes 2-3 are the NVDIMM address
space.
-object memory-backend-ram,id=mem0,size=2048M
-numa node,nodeid=0,memdev=mem0,
-numa cpu,node-id=0,socket-id=0
-object memory-backend-ram,id=mem1,size=2048M
-numa node,nodeid=1,memdev=mem1,
-numa cpu,node-id=1,socket-id=1
-numa node,nodeid=2,
-object memory-backend-file,id=nvmem0,share,mem-path=nvdimm-0,size=16384M,align=1G
-device nvdimm,memdev=nvmem0,id=nv0,label-size=2M,node=2
-numa node,nodeid=3,
-object memory-backend-file,id=nvmem1,share,mem-path=nvdimm-1,size=16384M,align=1G
-device nvdimm,memdev=nvmem1,id=nv1,label-size=2M,node=3
Cc: Jingqi Liu <jingqi.liu@intel.com>
Cc: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Jingqi Liu <jingqi.liu@intel.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Message-Id: <20200606000911.9896-3-vishal.l.verma@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2020-06-06 02:09:10 +02:00
|
|
|
if (ms->nvdimms_state->is_enabled) {
|
|
|
|
nvdimm_build_srat(table_data);
|
|
|
|
}
|
|
|
|
|
2019-09-18 15:06:28 +02:00
|
|
|
if (ms->device_memory) {
|
2021-09-24 14:27:44 +02:00
|
|
|
build_srat_memory(table_data, ms->device_memory->base,
|
2019-09-18 15:06:28 +02:00
|
|
|
memory_region_size(&ms->device_memory->mr),
|
|
|
|
ms->numa_state->num_nodes - 1,
|
|
|
|
MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
|
|
|
|
}
|
|
|
|
|
2021-09-24 14:27:43 +02:00
|
|
|
acpi_table_end(linker, &table);
|
2016-05-12 14:22:28 +02:00
|
|
|
}
|
|
|
|
|
2015-05-29 12:28:56 +02:00
|
|
|
/* GTDT */
|
|
|
|
static void
|
2017-01-09 12:40:23 +01:00
|
|
|
build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
2015-05-29 12:28:56 +02:00
|
|
|
{
|
2017-01-09 12:40:23 +01:00
|
|
|
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
2015-05-29 12:28:56 +02:00
|
|
|
int gtdt_start = table_data->len;
|
|
|
|
AcpiGenericTimerTable *gtdt;
|
2017-01-09 12:40:23 +01:00
|
|
|
uint32_t irqflags;
|
|
|
|
|
|
|
|
if (vmc->claim_edge_triggered_timers) {
|
|
|
|
irqflags = ACPI_GTDT_INTERRUPT_MODE_EDGE;
|
|
|
|
} else {
|
|
|
|
irqflags = ACPI_GTDT_INTERRUPT_MODE_LEVEL;
|
|
|
|
}
|
2015-05-29 12:28:56 +02:00
|
|
|
|
|
|
|
gtdt = acpi_data_push(table_data, sizeof *gtdt);
|
|
|
|
/* The interrupt values are the same with the device tree when adding 16 */
|
2017-01-09 12:40:21 +01:00
|
|
|
gtdt->secure_el1_interrupt = cpu_to_le32(ARCH_TIMER_S_EL1_IRQ + 16);
|
2017-01-09 12:40:23 +01:00
|
|
|
gtdt->secure_el1_flags = cpu_to_le32(irqflags);
|
2015-05-29 12:28:56 +02:00
|
|
|
|
2017-01-09 12:40:21 +01:00
|
|
|
gtdt->non_secure_el1_interrupt = cpu_to_le32(ARCH_TIMER_NS_EL1_IRQ + 16);
|
2017-01-09 12:40:23 +01:00
|
|
|
gtdt->non_secure_el1_flags = cpu_to_le32(irqflags |
|
2017-01-09 12:40:21 +01:00
|
|
|
ACPI_GTDT_CAP_ALWAYS_ON);
|
2015-05-29 12:28:56 +02:00
|
|
|
|
2017-01-09 12:40:21 +01:00
|
|
|
gtdt->virtual_timer_interrupt = cpu_to_le32(ARCH_TIMER_VIRT_IRQ + 16);
|
2017-01-09 12:40:23 +01:00
|
|
|
gtdt->virtual_timer_flags = cpu_to_le32(irqflags);
|
2015-05-29 12:28:56 +02:00
|
|
|
|
2017-01-09 12:40:21 +01:00
|
|
|
gtdt->non_secure_el2_interrupt = cpu_to_le32(ARCH_TIMER_NS_EL2_IRQ + 16);
|
2017-01-09 12:40:23 +01:00
|
|
|
gtdt->non_secure_el2_flags = cpu_to_le32(irqflags);
|
2015-05-29 12:28:56 +02:00
|
|
|
|
|
|
|
build_header(linker, table_data,
|
|
|
|
(void *)(table_data->data + gtdt_start), "GTDT",
|
2021-01-19 01:32:13 +01:00
|
|
|
table_data->len - gtdt_start, 2, vms->oem_id,
|
|
|
|
vms->oem_table_id);
|
2015-05-29 12:28:56 +02:00
|
|
|
}
|
|
|
|
|
2021-09-24 14:27:48 +02:00
|
|
|
/*
|
2021-09-24 14:27:52 +02:00
|
|
|
* ACPI spec, Revision 5.1 Errata A
|
2021-09-24 14:27:48 +02:00
|
|
|
* 5.2.12 Multiple APIC Description Table (MADT)
|
|
|
|
*/
|
2021-09-24 14:27:52 +02:00
|
|
|
static void build_append_gicr(GArray *table_data, uint64_t base, uint32_t size)
|
|
|
|
{
|
|
|
|
build_append_int_noprefix(table_data, 0xE, 1); /* Type */
|
|
|
|
build_append_int_noprefix(table_data, 16, 1); /* Length */
|
|
|
|
build_append_int_noprefix(table_data, 0, 2); /* Reserved */
|
|
|
|
/* Discovery Range Base Addres */
|
|
|
|
build_append_int_noprefix(table_data, base, 8);
|
|
|
|
build_append_int_noprefix(table_data, size, 4); /* Discovery Range Length */
|
|
|
|
}
|
|
|
|
|
2015-05-29 12:28:56 +02:00
|
|
|
static void
|
2017-01-09 12:40:22 +01:00
|
|
|
build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
2015-05-29 12:28:56 +02:00
|
|
|
{
|
2021-09-24 14:27:52 +02:00
|
|
|
int i;
|
2017-01-09 12:40:22 +01:00
|
|
|
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
|
|
|
const MemMapEntry *memmap = vms->memmap;
|
2021-09-24 14:27:48 +02:00
|
|
|
AcpiTable table = { .sig = "APIC", .rev = 3, .oem_id = vms->oem_id,
|
|
|
|
.oem_table_id = vms->oem_table_id };
|
2015-05-29 12:28:56 +02:00
|
|
|
|
2021-09-24 14:27:48 +02:00
|
|
|
acpi_table_begin(&table, table_data);
|
|
|
|
/* Local Interrupt Controller Address */
|
|
|
|
build_append_int_noprefix(table_data, 0, 4);
|
2021-09-24 14:27:52 +02:00
|
|
|
build_append_int_noprefix(table_data, 0, 4); /* Flags */
|
|
|
|
|
|
|
|
/* 5.2.12.15 GIC Distributor Structure */
|
|
|
|
build_append_int_noprefix(table_data, 0xC, 1); /* Type */
|
|
|
|
build_append_int_noprefix(table_data, 24, 1); /* Length */
|
|
|
|
build_append_int_noprefix(table_data, 0, 2); /* Reserved */
|
|
|
|
build_append_int_noprefix(table_data, 0, 4); /* GIC ID */
|
|
|
|
/* Physical Base Address */
|
|
|
|
build_append_int_noprefix(table_data, memmap[VIRT_GIC_DIST].base, 8);
|
|
|
|
build_append_int_noprefix(table_data, 0, 4); /* System Vector Base */
|
|
|
|
/* GIC version */
|
|
|
|
build_append_int_noprefix(table_data, vms->gic_version, 1);
|
|
|
|
build_append_int_noprefix(table_data, 0, 3); /* Reserved */
|
2015-05-29 12:28:56 +02:00
|
|
|
|
2020-12-15 18:48:15 +01:00
|
|
|
for (i = 0; i < MACHINE(vms)->smp.cpus; i++) {
|
2015-11-03 14:49:42 +01:00
|
|
|
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i));
|
2021-09-24 14:27:52 +02:00
|
|
|
uint64_t physical_base_address = 0, gich = 0, gicv = 0;
|
|
|
|
uint32_t vgic_interrupt = vms->virt ? PPI(ARCH_GIC_MAINT_IRQ) : 0;
|
|
|
|
uint32_t pmu_interrupt = arm_feature(&armcpu->env, ARM_FEATURE_PMU) ?
|
|
|
|
PPI(VIRTUAL_PMU_IRQ) : 0;
|
2015-11-03 14:49:42 +01:00
|
|
|
|
2017-01-09 12:40:22 +01:00
|
|
|
if (vms->gic_version == 2) {
|
2021-09-24 14:27:52 +02:00
|
|
|
physical_base_address = memmap[VIRT_GIC_CPU].base;
|
|
|
|
gicv = memmap[VIRT_GIC_VCPU].base;
|
|
|
|
gich = memmap[VIRT_GIC_HYP].base;
|
2015-11-03 14:49:42 +01:00
|
|
|
}
|
2016-06-14 16:59:12 +02:00
|
|
|
|
2021-09-24 14:27:52 +02:00
|
|
|
/* 5.2.12.14 GIC Structure */
|
|
|
|
build_append_int_noprefix(table_data, 0xB, 1); /* Type */
|
|
|
|
build_append_int_noprefix(table_data, 76, 1); /* Length */
|
|
|
|
build_append_int_noprefix(table_data, 0, 2); /* Reserved */
|
|
|
|
build_append_int_noprefix(table_data, i, 4); /* GIC ID */
|
|
|
|
build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */
|
|
|
|
/* Flags */
|
|
|
|
build_append_int_noprefix(table_data, 1, 4); /* Enabled */
|
|
|
|
/* Parking Protocol Version */
|
|
|
|
build_append_int_noprefix(table_data, 0, 4);
|
|
|
|
/* Performance Interrupt GSIV */
|
|
|
|
build_append_int_noprefix(table_data, pmu_interrupt, 4);
|
|
|
|
build_append_int_noprefix(table_data, 0, 8); /* Parked Address */
|
|
|
|
/* Physical Base Address */
|
|
|
|
build_append_int_noprefix(table_data, physical_base_address, 8);
|
|
|
|
build_append_int_noprefix(table_data, gicv, 8); /* GICV */
|
|
|
|
build_append_int_noprefix(table_data, gich, 8); /* GICH */
|
|
|
|
/* VGIC Maintenance interrupt */
|
|
|
|
build_append_int_noprefix(table_data, vgic_interrupt, 4);
|
|
|
|
build_append_int_noprefix(table_data, 0, 8); /* GICR Base Address*/
|
|
|
|
/* MPIDR */
|
|
|
|
build_append_int_noprefix(table_data, armcpu->mp_affinity, 8);
|
2015-11-03 14:49:42 +01:00
|
|
|
}
|
|
|
|
|
2017-01-09 12:40:22 +01:00
|
|
|
if (vms->gic_version == 3) {
|
2021-09-24 14:27:52 +02:00
|
|
|
build_append_gicr(table_data, memmap[VIRT_GIC_REDIST].base,
|
|
|
|
memmap[VIRT_GIC_REDIST].size);
|
|
|
|
if (virt_gicv3_redist_region_count(vms) == 2) {
|
|
|
|
build_append_gicr(table_data, memmap[VIRT_HIGH_GIC_REDIST2].base,
|
|
|
|
memmap[VIRT_HIGH_GIC_REDIST2].size);
|
2018-06-22 14:28:36 +02:00
|
|
|
}
|
|
|
|
|
2017-01-09 12:40:22 +01:00
|
|
|
if (its_class_name() && !vmc->no_its) {
|
2021-09-24 14:27:52 +02:00
|
|
|
/*
|
|
|
|
* FIXME: Structure is from Revision 6.0 where 'GIC Structure'
|
|
|
|
* has additional fields on top of implemented 5.1 Errata A,
|
|
|
|
* to make it consistent with v6.0 we need to bump everything
|
|
|
|
* to v6.0
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* ACPI spec, Revision 6.0 Errata A
|
|
|
|
* (original 6.0 definition has invalid Length)
|
|
|
|
* 5.2.12.18 GIC ITS Structure
|
|
|
|
*/
|
|
|
|
build_append_int_noprefix(table_data, 0xF, 1); /* Type */
|
|
|
|
build_append_int_noprefix(table_data, 20, 1); /* Length */
|
|
|
|
build_append_int_noprefix(table_data, 0, 2); /* Reserved */
|
|
|
|
build_append_int_noprefix(table_data, 0, 4); /* GIC ITS ID */
|
|
|
|
/* Physical Base Address */
|
|
|
|
build_append_int_noprefix(table_data, memmap[VIRT_GIC_ITS].base, 8);
|
|
|
|
build_append_int_noprefix(table_data, 0, 4); /* Reserved */
|
2016-10-04 14:28:09 +02:00
|
|
|
}
|
2015-09-24 02:29:37 +02:00
|
|
|
} else {
|
2021-09-24 14:27:52 +02:00
|
|
|
const uint16_t spi_base = vms->irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE;
|
|
|
|
|
|
|
|
/* 5.2.12.16 GIC MSI Frame Structure */
|
|
|
|
build_append_int_noprefix(table_data, 0xD, 1); /* Type */
|
|
|
|
build_append_int_noprefix(table_data, 24, 1); /* Length */
|
|
|
|
build_append_int_noprefix(table_data, 0, 2); /* Reserved */
|
|
|
|
build_append_int_noprefix(table_data, 0, 4); /* GIC MSI Frame ID */
|
|
|
|
/* Physical Base Address */
|
|
|
|
build_append_int_noprefix(table_data, memmap[VIRT_GIC_V2M].base, 8);
|
|
|
|
build_append_int_noprefix(table_data, 1, 4); /* Flags */
|
|
|
|
/* SPI Count */
|
|
|
|
build_append_int_noprefix(table_data, NUM_GICV2M_SPIS, 2);
|
|
|
|
build_append_int_noprefix(table_data, spi_base, 2); /* SPI Base */
|
2015-09-24 02:29:37 +02:00
|
|
|
}
|
2021-09-24 14:27:48 +02:00
|
|
|
acpi_table_end(linker, &table);
|
2015-05-29 12:28:56 +02:00
|
|
|
}
|
|
|
|
|
2015-05-29 12:28:55 +02:00
|
|
|
/* FADT */
|
2018-02-28 15:23:53 +01:00
|
|
|
static void build_fadt_rev5(GArray *table_data, BIOSLinker *linker,
|
|
|
|
VirtMachineState *vms, unsigned dsdt_tbl_offset)
|
2015-05-29 12:28:55 +02:00
|
|
|
{
|
2018-02-28 15:23:54 +01:00
|
|
|
/* ACPI v5.1 */
|
|
|
|
AcpiFadtData fadt = {
|
|
|
|
.rev = 5,
|
|
|
|
.minor_ver = 1,
|
|
|
|
.flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI,
|
|
|
|
.xdsdt_tbl_offset = &dsdt_tbl_offset,
|
|
|
|
};
|
2017-01-20 12:15:10 +01:00
|
|
|
|
|
|
|
switch (vms->psci_conduit) {
|
|
|
|
case QEMU_PSCI_CONDUIT_DISABLED:
|
2018-02-28 15:23:54 +01:00
|
|
|
fadt.arm_boot_arch = 0;
|
2017-01-20 12:15:10 +01:00
|
|
|
break;
|
|
|
|
case QEMU_PSCI_CONDUIT_HVC:
|
2018-02-28 15:23:54 +01:00
|
|
|
fadt.arm_boot_arch = ACPI_FADT_ARM_PSCI_COMPLIANT |
|
|
|
|
ACPI_FADT_ARM_PSCI_USE_HVC;
|
2017-01-20 12:15:10 +01:00
|
|
|
break;
|
|
|
|
case QEMU_PSCI_CONDUIT_SMC:
|
2018-02-28 15:23:54 +01:00
|
|
|
fadt.arm_boot_arch = ACPI_FADT_ARM_PSCI_COMPLIANT;
|
2017-01-20 12:15:10 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
2015-05-29 12:28:55 +02:00
|
|
|
|
2021-01-19 01:32:13 +01:00
|
|
|
build_fadt(table_data, linker, &fadt, vms->oem_id, vms->oem_table_id);
|
2015-05-29 12:28:55 +02:00
|
|
|
}
|
|
|
|
|
2015-05-29 12:28:55 +02:00
|
|
|
/* DSDT */
|
|
|
|
static void
|
2017-01-09 12:40:22 +01:00
|
|
|
build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
2015-05-29 12:28:55 +02:00
|
|
|
{
|
2020-07-03 17:59:43 +02:00
|
|
|
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
2015-05-29 12:28:55 +02:00
|
|
|
Aml *scope, *dsdt;
|
2019-09-18 15:06:27 +02:00
|
|
|
MachineState *ms = MACHINE(vms);
|
2017-01-09 12:40:22 +01:00
|
|
|
const MemMapEntry *memmap = vms->memmap;
|
|
|
|
const int *irqmap = vms->irqmap;
|
2021-09-24 14:27:54 +02:00
|
|
|
AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = vms->oem_id,
|
|
|
|
.oem_table_id = vms->oem_table_id };
|
2015-05-29 12:28:55 +02:00
|
|
|
|
2021-09-24 14:27:54 +02:00
|
|
|
acpi_table_begin(&table, table_data);
|
2015-05-29 12:28:55 +02:00
|
|
|
dsdt = init_aml_allocator();
|
|
|
|
|
2016-01-15 15:11:31 +01:00
|
|
|
/* When booting the VM with UEFI, UEFI takes ownership of the RTC hardware.
|
|
|
|
* While UEFI can use libfdt to disable the RTC device node in the DTB that
|
|
|
|
* it passes to the OS, it cannot modify AML. Therefore, we won't generate
|
|
|
|
* the RTC ACPI device at all when using UEFI.
|
|
|
|
*/
|
2015-05-29 12:28:55 +02:00
|
|
|
scope = aml_scope("\\_SB");
|
2020-12-15 18:48:15 +01:00
|
|
|
acpi_dsdt_add_cpus(scope, vms);
|
2015-05-29 12:28:55 +02:00
|
|
|
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
|
|
|
|
(irqmap[VIRT_UART] + ARM_SPI_BASE));
|
2020-07-03 17:59:43 +02:00
|
|
|
if (vmc->acpi_expose_flash) {
|
|
|
|
acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]);
|
|
|
|
}
|
2016-02-19 19:20:28 +01:00
|
|
|
acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
|
2015-05-29 12:28:55 +02:00
|
|
|
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
|
|
|
|
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
|
2015-09-07 11:39:29 +02:00
|
|
|
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
|
2020-11-19 02:48:37 +01:00
|
|
|
vms->highmem, vms->highmem_ecam, vms);
|
2019-09-18 15:06:27 +02:00
|
|
|
if (vms->acpi_dev) {
|
|
|
|
build_ged_aml(scope, "\\_SB."GED_DEVICE,
|
|
|
|
HOTPLUG_HANDLER(vms->acpi_dev),
|
|
|
|
irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE, AML_SYSTEM_MEMORY,
|
|
|
|
memmap[VIRT_ACPI_GED].base);
|
2019-09-18 15:06:30 +02:00
|
|
|
} else {
|
|
|
|
acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
|
|
|
|
(irqmap[VIRT_GPIO] + ARM_SPI_BASE));
|
2019-09-18 15:06:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vms->acpi_dev) {
|
|
|
|
uint32_t event = object_property_get_uint(OBJECT(vms->acpi_dev),
|
|
|
|
"ged-event", &error_abort);
|
|
|
|
|
|
|
|
if (event & ACPI_GED_MEM_HOTPLUG_EVT) {
|
|
|
|
build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL,
|
|
|
|
AML_SYSTEM_MEMORY,
|
|
|
|
memmap[VIRT_PCDIMM_ACPI].base);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-17 14:37:14 +01:00
|
|
|
acpi_dsdt_add_power_button(scope);
|
2021-06-15 16:21:18 +02:00
|
|
|
#ifdef CONFIG_TPM
|
2020-06-22 16:06:19 +02:00
|
|
|
acpi_dsdt_add_tpm(scope, vms);
|
2021-06-15 16:21:18 +02:00
|
|
|
#endif
|
2015-05-29 12:28:59 +02:00
|
|
|
|
2015-05-29 12:28:55 +02:00
|
|
|
aml_append(dsdt, scope);
|
|
|
|
|
2021-09-24 14:27:54 +02:00
|
|
|
/* copy AML table into ACPI tables blob */
|
2015-05-29 12:28:55 +02:00
|
|
|
g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
|
2021-09-24 14:27:54 +02:00
|
|
|
|
|
|
|
acpi_table_end(linker, &table);
|
2015-05-29 12:28:55 +02:00
|
|
|
free_aml_allocator();
|
|
|
|
}
|
|
|
|
|
2015-05-29 12:28:54 +02:00
|
|
|
typedef
|
|
|
|
struct AcpiBuildState {
|
|
|
|
/* Copy of table in RAM (for patching). */
|
|
|
|
MemoryRegion *table_mr;
|
|
|
|
MemoryRegion *rsdp_mr;
|
|
|
|
MemoryRegion *linker_mr;
|
|
|
|
/* Is table patched? */
|
|
|
|
bool patched;
|
|
|
|
} AcpiBuildState;
|
|
|
|
|
2020-11-19 02:48:38 +01:00
|
|
|
static void acpi_align_size(GArray *blob, unsigned align)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Align size to multiple of given size. This reduces the chance
|
|
|
|
* we need to change size in the future (breaking cross version migration).
|
|
|
|
*/
|
|
|
|
g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
|
|
|
|
}
|
|
|
|
|
2015-05-29 12:28:54 +02:00
|
|
|
static
|
2017-01-09 12:40:22 +01:00
|
|
|
void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
|
2015-05-29 12:28:54 +02:00
|
|
|
{
|
2017-01-09 12:40:22 +01:00
|
|
|
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
|
2015-05-29 12:28:54 +02:00
|
|
|
GArray *table_offsets;
|
2017-04-10 17:03:50 +02:00
|
|
|
unsigned dsdt, xsdt;
|
2015-05-29 12:28:55 +02:00
|
|
|
GArray *tables_blob = tables->table_data;
|
2019-08-09 08:57:22 +02:00
|
|
|
MachineState *ms = MACHINE(vms);
|
2015-05-29 12:28:54 +02:00
|
|
|
|
|
|
|
table_offsets = g_array_new(false, true /* clear */,
|
|
|
|
sizeof(uint32_t));
|
|
|
|
|
2016-05-19 15:19:26 +02:00
|
|
|
bios_linker_loader_alloc(tables->linker,
|
|
|
|
ACPI_BUILD_TABLE_FILE, tables_blob,
|
2015-05-29 12:28:54 +02:00
|
|
|
64, false /* high memory */);
|
|
|
|
|
2015-05-29 12:28:55 +02:00
|
|
|
/* DSDT is pointed to by FADT */
|
2015-05-29 12:28:55 +02:00
|
|
|
dsdt = tables_blob->len;
|
2017-01-09 12:40:22 +01:00
|
|
|
build_dsdt(tables_blob, tables->linker, vms);
|
2015-05-29 12:28:55 +02:00
|
|
|
|
2015-06-26 15:22:36 +02:00
|
|
|
/* FADT MADT GTDT MCFG SPCR pointed to by RSDT */
|
2015-05-29 12:28:55 +02:00
|
|
|
acpi_add_table(table_offsets, tables_blob);
|
2018-02-28 15:23:53 +01:00
|
|
|
build_fadt_rev5(tables_blob, tables->linker, vms, dsdt);
|
2015-05-29 12:28:55 +02:00
|
|
|
|
2015-05-29 12:28:56 +02:00
|
|
|
acpi_add_table(table_offsets, tables_blob);
|
2017-01-09 12:40:22 +01:00
|
|
|
build_madt(tables_blob, tables->linker, vms);
|
2015-05-29 12:28:56 +02:00
|
|
|
|
2015-05-29 12:28:56 +02:00
|
|
|
acpi_add_table(table_offsets, tables_blob);
|
2017-01-09 12:40:23 +01:00
|
|
|
build_gtdt(tables_blob, tables->linker, vms);
|
2015-05-29 12:28:56 +02:00
|
|
|
|
2015-05-29 12:28:57 +02:00
|
|
|
acpi_add_table(table_offsets, tables_blob);
|
2019-04-19 02:30:51 +02:00
|
|
|
{
|
|
|
|
AcpiMcfgInfo mcfg = {
|
|
|
|
.base = vms->memmap[VIRT_ECAM_ID(vms->highmem_ecam)].base,
|
|
|
|
.size = vms->memmap[VIRT_ECAM_ID(vms->highmem_ecam)].size,
|
|
|
|
};
|
2021-01-19 01:32:13 +01:00
|
|
|
build_mcfg(tables_blob, tables->linker, &mcfg, vms->oem_id,
|
|
|
|
vms->oem_table_id);
|
2019-04-19 02:30:51 +02:00
|
|
|
}
|
2015-05-29 12:28:57 +02:00
|
|
|
|
2015-06-15 19:06:11 +02:00
|
|
|
acpi_add_table(table_offsets, tables_blob);
|
2017-01-09 12:40:22 +01:00
|
|
|
build_spcr(tables_blob, tables->linker, vms);
|
2015-06-15 19:06:11 +02:00
|
|
|
|
2020-05-12 05:06:03 +02:00
|
|
|
if (vms->ras) {
|
|
|
|
build_ghes_error_table(tables->hardware_errors, tables->linker);
|
2020-05-12 05:06:04 +02:00
|
|
|
acpi_add_table(table_offsets, tables_blob);
|
2021-01-19 01:32:13 +01:00
|
|
|
acpi_build_hest(tables_blob, tables->linker, vms->oem_id,
|
|
|
|
vms->oem_table_id);
|
2020-05-12 05:06:03 +02:00
|
|
|
}
|
|
|
|
|
2019-08-09 08:57:22 +02:00
|
|
|
if (ms->numa_state->num_nodes > 0) {
|
2016-05-12 14:22:28 +02:00
|
|
|
acpi_add_table(table_offsets, tables_blob);
|
2017-01-09 12:40:22 +01:00
|
|
|
build_srat(tables_blob, tables->linker, vms);
|
2019-08-09 08:57:23 +02:00
|
|
|
if (ms->numa_state->have_numa_distance) {
|
2017-06-02 12:51:49 +02:00
|
|
|
acpi_add_table(table_offsets, tables_blob);
|
2021-01-19 01:32:13 +01:00
|
|
|
build_slit(tables_blob, tables->linker, ms, vms->oem_id,
|
|
|
|
vms->oem_table_id);
|
2017-06-02 12:51:49 +02:00
|
|
|
}
|
2016-05-12 14:22:28 +02:00
|
|
|
}
|
|
|
|
|
2020-04-21 14:59:30 +02:00
|
|
|
if (ms->nvdimms_state->is_enabled) {
|
|
|
|
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker,
|
2021-01-19 01:32:13 +01:00
|
|
|
ms->nvdimms_state, ms->ram_slots, vms->oem_id,
|
|
|
|
vms->oem_table_id);
|
2020-04-21 14:59:30 +02:00
|
|
|
}
|
|
|
|
|
2017-01-09 12:40:22 +01:00
|
|
|
if (its_class_name() && !vmc->no_its) {
|
2016-10-24 17:26:54 +02:00
|
|
|
acpi_add_table(table_offsets, tables_blob);
|
2018-05-04 19:05:52 +02:00
|
|
|
build_iort(tables_blob, tables->linker, vms);
|
2016-10-24 17:26:54 +02:00
|
|
|
}
|
|
|
|
|
2021-06-15 16:21:18 +02:00
|
|
|
#ifdef CONFIG_TPM
|
2020-06-01 11:57:36 +02:00
|
|
|
if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) {
|
|
|
|
acpi_add_table(table_offsets, tables_blob);
|
2021-01-19 01:32:13 +01:00
|
|
|
build_tpm2(tables_blob, tables->linker, tables->tcpalog, vms->oem_id,
|
|
|
|
vms->oem_table_id);
|
2020-06-01 11:57:36 +02:00
|
|
|
}
|
2021-06-15 16:21:18 +02:00
|
|
|
#endif
|
2020-06-01 11:57:36 +02:00
|
|
|
|
2017-04-10 17:03:50 +02:00
|
|
|
/* XSDT is pointed to by RSDP */
|
|
|
|
xsdt = tables_blob->len;
|
2021-01-19 01:32:13 +01:00
|
|
|
build_xsdt(tables_blob, tables->linker, table_offsets, vms->oem_id,
|
|
|
|
vms->oem_table_id);
|
2015-05-29 12:28:56 +02:00
|
|
|
|
2015-05-29 12:28:56 +02:00
|
|
|
/* RSDP is in FSEG memory, so allocate it separately */
|
2018-12-17 11:48:34 +01:00
|
|
|
{
|
|
|
|
AcpiRsdpData rsdp_data = {
|
|
|
|
.revision = 2,
|
2021-01-19 01:32:13 +01:00
|
|
|
.oem_id = vms->oem_id,
|
2018-12-17 11:48:34 +01:00
|
|
|
.xsdt_tbl_offset = &xsdt,
|
|
|
|
.rsdt_tbl_offset = NULL,
|
|
|
|
};
|
|
|
|
build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
|
|
|
|
}
|
2015-05-29 12:28:56 +02:00
|
|
|
|
2020-11-19 02:48:38 +01:00
|
|
|
/*
|
|
|
|
* The align size is 128, warn if 64k is not enough therefore
|
|
|
|
* the align size could be resized.
|
|
|
|
*/
|
|
|
|
if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) {
|
|
|
|
warn_report("ACPI table size %u exceeds %d bytes,"
|
|
|
|
" migration may not work",
|
|
|
|
tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2);
|
|
|
|
error_printf("Try removing CPUs, NUMA nodes, memory slots"
|
|
|
|
" or PCI bridges.");
|
|
|
|
}
|
|
|
|
acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE);
|
|
|
|
|
|
|
|
|
2015-05-29 12:28:54 +02:00
|
|
|
/* Cleanup memory that's no longer used. */
|
|
|
|
g_array_free(table_offsets, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void acpi_ram_update(MemoryRegion *mr, GArray *data)
|
|
|
|
{
|
|
|
|
uint32_t size = acpi_data_len(data);
|
|
|
|
|
|
|
|
/* Make sure RAM size is correct - in case it got changed
|
|
|
|
* e.g. by migration */
|
|
|
|
memory_region_ram_resize(mr, size, &error_abort);
|
|
|
|
|
|
|
|
memcpy(memory_region_get_ram_ptr(mr), data->data, size);
|
|
|
|
memory_region_set_dirty(mr, 0, size);
|
|
|
|
}
|
|
|
|
|
2015-11-05 15:32:49 +01:00
|
|
|
static void virt_acpi_build_update(void *build_opaque)
|
2015-05-29 12:28:54 +02:00
|
|
|
{
|
|
|
|
AcpiBuildState *build_state = build_opaque;
|
|
|
|
AcpiBuildTables tables;
|
|
|
|
|
|
|
|
/* No state to update or already patched? Nothing to do. */
|
|
|
|
if (!build_state || build_state->patched) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
build_state->patched = true;
|
|
|
|
|
|
|
|
acpi_build_tables_init(&tables);
|
|
|
|
|
2017-01-09 12:40:22 +01:00
|
|
|
virt_acpi_build(VIRT_MACHINE(qdev_get_machine()), &tables);
|
2015-05-29 12:28:54 +02:00
|
|
|
|
|
|
|
acpi_ram_update(build_state->table_mr, tables.table_data);
|
|
|
|
acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
|
2016-05-19 15:19:25 +02:00
|
|
|
acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
|
2015-05-29 12:28:54 +02:00
|
|
|
|
|
|
|
acpi_build_tables_cleanup(&tables, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void virt_acpi_build_reset(void *build_opaque)
|
|
|
|
{
|
|
|
|
AcpiBuildState *build_state = build_opaque;
|
|
|
|
build_state->patched = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const VMStateDescription vmstate_virt_acpi_build = {
|
|
|
|
.name = "virt_acpi_build",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_BOOL(patched, AcpiBuildState),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2017-01-09 12:40:22 +01:00
|
|
|
void virt_acpi_setup(VirtMachineState *vms)
|
2015-05-29 12:28:54 +02:00
|
|
|
{
|
|
|
|
AcpiBuildTables tables;
|
|
|
|
AcpiBuildState *build_state;
|
2020-05-12 05:06:05 +02:00
|
|
|
AcpiGedState *acpi_ged_state;
|
2015-05-29 12:28:54 +02:00
|
|
|
|
2017-01-09 12:40:23 +01:00
|
|
|
if (!vms->fw_cfg) {
|
2015-05-29 12:28:54 +02:00
|
|
|
trace_virt_acpi_setup();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-20 11:01:36 +01:00
|
|
|
if (!virt_is_acpi_enabled(vms)) {
|
2015-05-29 12:28:54 +02:00
|
|
|
trace_virt_acpi_setup();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
build_state = g_malloc0(sizeof *build_state);
|
|
|
|
|
|
|
|
acpi_build_tables_init(&tables);
|
2017-01-09 12:40:22 +01:00
|
|
|
virt_acpi_build(vms, &tables);
|
2015-05-29 12:28:54 +02:00
|
|
|
|
|
|
|
/* Now expose it all to Guest */
|
2019-06-10 03:18:30 +02:00
|
|
|
build_state->table_mr = acpi_add_rom_blob(virt_acpi_build_update,
|
|
|
|
build_state, tables.table_data,
|
2021-03-04 11:55:53 +01:00
|
|
|
ACPI_BUILD_TABLE_FILE);
|
2015-05-29 12:28:54 +02:00
|
|
|
assert(build_state->table_mr != NULL);
|
|
|
|
|
2021-03-04 11:55:53 +01:00
|
|
|
build_state->linker_mr = acpi_add_rom_blob(virt_acpi_build_update,
|
|
|
|
build_state,
|
|
|
|
tables.linker->cmd_blob,
|
|
|
|
ACPI_BUILD_LOADER_FILE);
|
2015-05-29 12:28:54 +02:00
|
|
|
|
2017-01-09 12:40:23 +01:00
|
|
|
fw_cfg_add_file(vms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data,
|
|
|
|
acpi_data_len(tables.tcpalog));
|
2015-05-29 12:28:54 +02:00
|
|
|
|
2020-05-12 05:06:05 +02:00
|
|
|
if (vms->ras) {
|
|
|
|
assert(vms->acpi_dev);
|
|
|
|
acpi_ged_state = ACPI_GED(vms->acpi_dev);
|
|
|
|
acpi_ghes_add_fw_cfg(&acpi_ged_state->ghes_state,
|
|
|
|
vms->fw_cfg, tables.hardware_errors);
|
|
|
|
}
|
|
|
|
|
2019-06-10 03:18:30 +02:00
|
|
|
build_state->rsdp_mr = acpi_add_rom_blob(virt_acpi_build_update,
|
|
|
|
build_state, tables.rsdp,
|
2021-03-04 11:55:53 +01:00
|
|
|
ACPI_BUILD_RSDP_FILE);
|
2015-05-29 12:28:54 +02:00
|
|
|
|
|
|
|
qemu_register_reset(virt_acpi_build_reset, build_state);
|
|
|
|
virt_acpi_build_reset(build_state);
|
|
|
|
vmstate_register(NULL, 0, &vmstate_virt_acpi_build, build_state);
|
|
|
|
|
|
|
|
/* Cleanup tables but don't free the memory: we track it
|
|
|
|
* in build_state.
|
|
|
|
*/
|
|
|
|
acpi_build_tables_cleanup(&tables, false);
|
|
|
|
}
|