hw/arm/virt: Basic CXL enablement on pci_expander_bridge instances pxb-cxl

Code based on i386/pc enablement.
The memory layout places space for 16 host bridge register regions after
the GIC_REDIST2 in the extended memmap. This is a hole in the current
map so adding them here has no impact on placement of other memory regions
(tested with enough CPUs for GIC_REDIST2 to be in use.)
The high memory map is GiB aligned so the hole is there whatever the
size of memory or device_memory below this point.

The CFMWs are placed above the extended memmap. Note the existing
variable highest_gpa is the highest GPA that has been allocated at
a particular point in setting up the memory map. Whilst this caused
some confusion in review there are existing comments explaining this
so nothing is added.

The cxl_devices_state.host_mr provides a small space in which to place
the individual host bridge register regions for whatever host bridges are
allocated via -device pxb-cxl on the command line. The existing dynamic
sysbus infrastructure is not reused because pxb-cxl is a PCI device not
a sysbus one but these registers are directly in the main memory map,
not the PCI address space.

Only create the CEDT table if cxl=on set for the machine. Default to off.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Tested-by: Itaru Kitayama <itaru.kitayama@fujitsu.com>
Tested-by: Li Zhijian <lizhijian@fujitsu.com>
Message-id: 20250703104110.992379-4-Jonathan.Cameron@huawei.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Jonathan Cameron 2025-07-03 11:41:08 +01:00 committed by Peter Maydell
parent 584f722eb3
commit 9d8ade51a2
4 changed files with 77 additions and 0 deletions

View file

@ -31,6 +31,7 @@ Supported devices
The virt board supports:
- PCI/PCIe devices
- CXL Fixed memory windows, root bridges and devices.
- Flash memory
- Either one or two PL011 UARTs for the NonSecure World
- An RTC
@ -189,6 +190,14 @@ ras
acpi
Set ``on``/``off``/``auto`` to enable/disable ACPI.
cxl
Set ``on``/``off`` to enable/disable CXL. More details in
:doc:`../devices/cxl`. The default is off.
cxl-fmw
Array of CXL fixed memory windows describing fixed address routing to
target CXL host bridges. See :doc:`../devices/cxl`.
dtb-randomness
Set ``on``/``off`` to pass random seeds via the guest DTB
rng-seed and kaslr-seed nodes (in both "/chosen" and

View file

@ -39,10 +39,12 @@
#include "hw/acpi/aml-build.h"
#include "hw/acpi/utils.h"
#include "hw/acpi/pci.h"
#include "hw/acpi/cxl.h"
#include "hw/acpi/memory_hotplug.h"
#include "hw/acpi/generic_event_device.h"
#include "hw/acpi/tpm.h"
#include "hw/acpi/hmat.h"
#include "hw/cxl/cxl.h"
#include "hw/pci/pcie_host.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
@ -119,10 +121,29 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
aml_append(scope, dev);
}
static void build_acpi0017(Aml *table)
{
Aml *dev, *scope, *method;
scope = aml_scope("_SB");
dev = aml_device("CXLM");
aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0017")));
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(0x0B)));
aml_append(dev, method);
build_cxl_dsm_method(dev);
aml_append(scope, dev);
aml_append(table, scope);
}
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
uint32_t irq, VirtMachineState *vms)
{
int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam);
bool cxl_present = false;
PCIBus *bus = vms->bus;
struct GPEXConfig cfg = {
.mmio32 = memmap[VIRT_PCIE_MMIO],
.pio = memmap[VIRT_PCIE_PIO],
@ -136,6 +157,14 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
}
acpi_dsdt_add_gpex(scope, &cfg);
QLIST_FOREACH(bus, &vms->bus->child, sibling) {
if (pci_bus_is_cxl(bus)) {
cxl_present = true;
}
}
if (cxl_present) {
build_acpi0017(scope);
}
}
static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap,
@ -1027,6 +1056,11 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
}
}
if (vms->cxl_devices_state.is_enabled) {
cxl_build_cedt(table_offsets, tables_blob, tables->linker,
vms->oem_id, vms->oem_table_id, &vms->cxl_devices_state);
}
if (ms->nvdimms_state->is_enabled) {
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker,
ms->nvdimms_state, ms->ram_slots, vms->oem_id,

View file

@ -57,6 +57,7 @@
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "hw/pci-host/gpex.h"
#include "hw/pci-bridge/pci_expander_bridge.h"
#include "hw/virtio/virtio-pci.h"
#include "hw/core/sysbus-fdt.h"
#include "hw/platform-bus.h"
@ -86,6 +87,8 @@
#include "hw/virtio/virtio-md-pci.h"
#include "hw/virtio/virtio-iommu.h"
#include "hw/char/pl011.h"
#include "hw/cxl/cxl.h"
#include "hw/cxl/cxl_host.h"
#include "qemu/guest-random.h"
static GlobalProperty arm_virt_compat[] = {
@ -220,9 +223,11 @@ static const MemMapEntry base_memmap[] = {
static MemMapEntry extended_memmap[] = {
/* Additional 64 MB redist region (can contain up to 512 redistributors) */
[VIRT_HIGH_GIC_REDIST2] = { 0x0, 64 * MiB },
[VIRT_CXL_HOST] = { 0x0, 64 * KiB * 16 }, /* 16 UID */
[VIRT_HIGH_PCIE_ECAM] = { 0x0, 256 * MiB },
/* Second PCIe window */
[VIRT_HIGH_PCIE_MMIO] = { 0x0, DEFAULT_HIGH_PCIE_MMIO_SIZE },
/* Any CXL Fixed memory windows come here */
};
static const int a15irqmap[] = {
@ -1623,6 +1628,17 @@ static void create_pcie(VirtMachineState *vms)
}
}
static void create_cxl_host_reg_region(VirtMachineState *vms)
{
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *mr = &vms->cxl_devices_state.host_mr;
memory_region_init(mr, OBJECT(vms), "cxl_host_reg",
vms->memmap[VIRT_CXL_HOST].size);
memory_region_add_subregion(sysmem, vms->memmap[VIRT_CXL_HOST].base, mr);
vms->highmem_cxl = true;
}
static void create_platform_bus(VirtMachineState *vms)
{
DeviceState *dev;
@ -1739,6 +1755,12 @@ void virt_machine_done(Notifier *notifier, void *data)
struct arm_boot_info *info = &vms->bootinfo;
AddressSpace *as = arm_boot_address_space(cpu, info);
cxl_hook_up_pxb_registers(vms->bus, &vms->cxl_devices_state,
&error_fatal);
if (vms->cxl_devices_state.is_enabled) {
cxl_fmws_link_targets(&error_fatal);
}
/*
* If the user provided a dtb, we assume the dynamic sysbus nodes
* already are integrated there. This corresponds to a use case where
@ -1785,6 +1807,7 @@ static inline bool *virt_get_high_memmap_enabled(VirtMachineState *vms,
{
bool *enabled_array[] = {
&vms->highmem_redists,
&vms->highmem_cxl,
&vms->highmem_ecam,
&vms->highmem_mmio,
};
@ -1892,6 +1915,9 @@ static void virt_set_memmap(VirtMachineState *vms, int pa_bits)
if (device_memory_size > 0) {
machine_memory_devices_init(ms, device_memory_base, device_memory_size);
}
vms->highest_gpa = cxl_fmws_set_memmap(ROUND_UP(vms->highest_gpa + 1,
256 * MiB),
BIT_ULL(pa_bits)) - 1;
}
static VirtGICType finalize_gic_version_do(const char *accel_name,
@ -2343,6 +2369,8 @@ static void machvirt_init(MachineState *machine)
memory_region_add_subregion(sysmem, vms->memmap[VIRT_MEM].base,
machine->ram);
cxl_fmws_update_mmio();
virt_flash_fdt(vms, sysmem, secure_sysmem ?: sysmem);
create_gic(vms, sysmem);
@ -2398,6 +2426,7 @@ static void machvirt_init(MachineState *machine)
create_rtc(vms);
create_pcie(vms);
create_cxl_host_reg_region(vms);
if (has_ged && aarch64 && firmware_loaded && virt_is_acpi_enabled(vms)) {
vms->acpi_dev = create_acpi_ged(vms);
@ -3364,6 +3393,7 @@ static void virt_instance_init(Object *obj)
vms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
vms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
cxl_machine_init(obj, &vms->cxl_devices_state);
}
static const TypeInfo virt_machine_info = {

View file

@ -36,6 +36,7 @@
#include "hw/arm/boot.h"
#include "hw/arm/bsa.h"
#include "hw/block/flash.h"
#include "hw/cxl/cxl.h"
#include "system/kvm.h"
#include "hw/intc/arm_gicv3_common.h"
#include "qom/object.h"
@ -85,6 +86,7 @@ enum {
/* indices of IO regions located after the RAM */
enum {
VIRT_HIGH_GIC_REDIST2 = VIRT_LOWMEMMAP_LAST,
VIRT_CXL_HOST,
VIRT_HIGH_PCIE_ECAM,
VIRT_HIGH_PCIE_MMIO,
};
@ -140,6 +142,7 @@ struct VirtMachineState {
bool secure;
bool highmem;
bool highmem_compact;
bool highmem_cxl;
bool highmem_ecam;
bool highmem_mmio;
bool highmem_redists;
@ -174,6 +177,7 @@ struct VirtMachineState {
char *oem_id;
char *oem_table_id;
bool ns_el2_virt_timer_irq;
CXLState cxl_devices_state;
};
#define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)