All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine
  2026-06-02  7:41 [PATCH 0/4] hw/riscv/virt: Add CXL support and fix virtio DMA into CXL memory Chen Pei
@ 2026-06-02  7:41 ` Chen Pei
  2026-06-09 12:41   ` Jonathan Cameron
  0 siblings, 1 reply; 9+ messages in thread
From: Chen Pei @ 2026-06-02  7:41 UTC (permalink / raw)
  To: pbonzini, palmer, alistair.francis, liwei1518, daniel.barboza,
	zhiwei_liu, chao.liu.zevorn, sunilvl, jonathan.cameron, fan.ni,
	guoren
  Cc: qemu-riscv, qemu-devel, Chen Pei

Enable CXL support on the RISC-V virt machine following the same
approach used by the ARM virt machine:
- Add PXB and ACPI_CXL Kconfig selections
- Add CXLState and PCIBus pointer to RISCVVirtState
- Register CXL machine properties via cxl_machine_init()
- Create CXL host register region above the PCIe high MMIO region
- Call cxl_hook_up_pxb_registers() and cxl_fmws_link_targets() at
  machine_done time
- Map Fixed Memory Windows above the CXL host register region
- Add ACPI0017 device in DSDT and build CEDT table in virt-acpi-build.c

Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
---
 hw/riscv/Kconfig           |  2 ++
 hw/riscv/virt-acpi-build.c | 20 ++++++++++++++++++
 hw/riscv/virt.c            | 42 ++++++++++++++++++++++++++++++++++++++
 include/hw/riscv/virt.h    |  3 +++
 4 files changed, 67 insertions(+)

diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 2518b04175..ebd0355f09 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -66,8 +66,10 @@ config RISCV_VIRT
     select VIRTIO_MMIO
     select FW_CFG_DMA
     select PLATFORM_BUS
+    select PXB
     select ACPI
     select ACPI_PCI
+    select ACPI_CXL
 
 config SHAKTI_C
     bool
diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c
index 413d47d70e..309d64b322 100644
--- a/hw/riscv/virt-acpi-build.c
+++ b/hw/riscv/virt-acpi-build.c
@@ -29,12 +29,16 @@
 #include "hw/acpi/aml-build.h"
 #include "hw/acpi/pci.h"
 #include "hw/acpi/utils.h"
+#include "hw/acpi/cxl.h"
 #include "hw/intc/riscv_aclint.h"
 #include "hw/nvram/fw_cfg_acpi.h"
 #include "hw/pci-host/gpex.h"
+#include "hw/pci/pci_bus.h"
 #include "hw/riscv/virt.h"
 #include "hw/riscv/numa.h"
 #include "hw/virtio/virtio-acpi.h"
+#include "hw/cxl/cxl.h"
+#include "hw/cxl/cxl_host.h"
 #include "kvm/kvm_riscv.h"
 #include "migration/vmstate.h"
 #include "qapi/error.h"
@@ -503,6 +507,17 @@ static void build_dsdt(GArray *table_data,
         acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES * 2);
     }
 
+    if (s->cxl_devices_state.is_enabled) {
+        Aml *cxl_dev = aml_device("CXLM");
+        aml_append(cxl_dev, aml_name_decl("_HID", aml_string("ACPI0017")));
+        Aml *method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+        aml_append(method, aml_return(aml_int(0x0B)));
+        aml_append(cxl_dev, method);
+        build_cxl_dsm_method(cxl_dev);
+
+        aml_append(scope, cxl_dev);
+    }
+
     aml_append(dsdt, scope);
 
     /* copy AML table into ACPI tables blob and patch header there */
@@ -910,6 +925,11 @@ static void virt_acpi_build(RISCVVirtState *s, AcpiBuildTables *tables)
                    s->oem_table_id);
     }
 
+    if (s->cxl_devices_state.is_enabled) {
+        cxl_build_cedt(table_offsets, tables_blob, tables->linker,
+                       s->oem_id, s->oem_table_id, &s->cxl_devices_state);
+    }
+
     if (ms->numa_state->num_nodes > 0) {
         acpi_add_table(table_offsets, tables_blob);
         build_srat(tables_blob, tables->linker, s);
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index ce64eaaef7..899f632de7 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -55,6 +55,8 @@
 #include "hw/pci/pci.h"
 #include "hw/pci-host/gpex.h"
 #include "hw/display/ramfb.h"
+#include "hw/cxl/cxl.h"
+#include "hw/cxl/cxl_host.h"
 #include "hw/acpi/aml-build.h"
 #include "qapi/qapi-visit-common.h"
 #include "hw/virtio/virtio-iommu.h"
@@ -1259,9 +1261,27 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
     }
 
     GPEX_HOST(dev)->gpex_cfg.bus = PCI_HOST_BRIDGE(dev)->bus;
+    s->bus = PCI_HOST_BRIDGE(dev)->bus;
     return dev;
 }
 
+static void create_cxl_host_reg_region(RISCVVirtState *s)
+{
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *mr = &s->cxl_devices_state.host_mr;
+    hwaddr base;
+
+    if (!s->cxl_devices_state.is_enabled) {
+        return;
+    }
+
+    base = virt_high_pcie_memmap.base + virt_high_pcie_memmap.size;
+    base = ROUND_UP(base, 64 * KiB);
+
+    memory_region_init(mr, OBJECT(s), "cxl_host_reg", 64 * KiB * 16);
+    memory_region_add_subregion(sysmem, base, mr);
+}
+
 static FWCfgState *create_fw_cfg(const MachineState *ms, hwaddr base)
 {
     FWCfgState *fw_cfg;
@@ -1426,6 +1446,15 @@ static void virt_machine_done(Notifier *notifier, void *data)
                                      machine_done);
     MachineState *machine = MACHINE(s);
     hwaddr start_addr = s->memmap[VIRT_DRAM].base;
+
+    if (s->bus) {
+        cxl_hook_up_pxb_registers(s->bus, &s->cxl_devices_state,
+                                  &error_fatal);
+    }
+
+    if (s->cxl_devices_state.is_enabled) {
+        cxl_fmws_link_targets(&error_fatal);
+    }
     hwaddr firmware_end_addr;
     vaddr kernel_start_addr;
     const char *firmware_name = riscv_default_firmware_name(&s->soc[0]);
@@ -1663,6 +1692,17 @@ static void virt_machine_init(MachineState *machine)
             ROUND_UP(virt_high_pcie_memmap.base, virt_high_pcie_memmap.size);
     }
 
+    create_cxl_host_reg_region(s);
+
+    if (s->cxl_devices_state.is_enabled) {
+        hwaddr cxl_base = virt_high_pcie_memmap.base +
+                          virt_high_pcie_memmap.size;
+        cxl_base += memory_region_size(&s->cxl_devices_state.host_mr);
+        cxl_base = ROUND_UP(cxl_base, 256 * MiB);
+        cxl_fmws_set_memmap(cxl_base, UINT64_MAX);
+        cxl_fmws_update_mmio();
+    }
+
     /* register system main memory (actual RAM) */
     memory_region_add_subregion(system_memory, s->memmap[VIRT_DRAM].base,
                                 machine->ram);
@@ -1769,6 +1809,8 @@ static void virt_machine_instance_init(Object *obj)
     s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
     s->acpi = ON_OFF_AUTO_AUTO;
     s->iommu_sys = ON_OFF_AUTO_AUTO;
+
+    cxl_machine_init(obj, &s->cxl_devices_state);
 }
 
 static char *virt_get_aia_guests(Object *obj, Error **errp)
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index 18a2a323a3..c3201588bb 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -24,6 +24,7 @@
 #include "hw/core/sysbus.h"
 #include "hw/block/flash.h"
 #include "hw/intc/riscv_imsic.h"
+#include "hw/cxl/cxl.h"
 
 #define VIRT_CPUS_MAX_BITS             9
 #define VIRT_CPUS_MAX                  (1 << VIRT_CPUS_MAX_BITS)
@@ -64,6 +65,8 @@ struct RISCVVirtState {
     struct GPEXHost *gpex_host;
     OnOffAuto iommu_sys;
     uint16_t pci_iommu_bdf;
+    CXLState cxl_devices_state;
+    PCIBus *bus;
 };
 
 enum {
-- 
2.50.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine
  2026-06-02  7:41 ` [PATCH 1/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
@ 2026-06-09 12:41   ` Jonathan Cameron
  2026-06-10 12:46     ` Chen Pei
  0 siblings, 1 reply; 9+ messages in thread
From: Jonathan Cameron @ 2026-06-09 12:41 UTC (permalink / raw)
  To: Chen Pei
  Cc: pbonzini, palmer, alistair.francis, liwei1518, daniel.barboza,
	zhiwei_liu, chao.liu.zevorn, sunilvl, jonathan.cameron, fan.ni,
	guoren, qemu-riscv, qemu-devel

On Tue,  2 Jun 2026 15:41:24 +0800
Chen Pei <cp0613@linux.alibaba.com> wrote:

> Enable CXL support on the RISC-V virt machine following the same
> approach used by the ARM virt machine:
> - Add PXB and ACPI_CXL Kconfig selections
> - Add CXLState and PCIBus pointer to RISCVVirtState
> - Register CXL machine properties via cxl_machine_init()
> - Create CXL host register region above the PCIe high MMIO region
> - Call cxl_hook_up_pxb_registers() and cxl_fmws_link_targets() at
>   machine_done time
> - Map Fixed Memory Windows above the CXL host register region
> - Add ACPI0017 device in DSDT and build CEDT table in virt-acpi-build.c
> 
> Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
Just really minor stuff inline. Been a while since we added an architecture
but all looks fine to me.

Reviewed-by: Jonathan Cameron <jic23@kernel.org>

> diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
> index ce64eaaef7..899f632de7 100644
> --- a/hw/riscv/virt.c
> +++ b/hw/riscv/virt.c
> @@ -55,6 +55,8 @@
>  #include "hw/pci/pci.h"
>  #include "hw/pci-host/gpex.h"
>  #include "hw/display/ramfb.h"
> +#include "hw/cxl/cxl.h"
> +#include "hw/cxl/cxl_host.h"
>  #include "hw/acpi/aml-build.h"
>  #include "qapi/qapi-visit-common.h"
>  #include "hw/virtio/virtio-iommu.h"
> @@ -1259,9 +1261,27 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
>      }
>  
>      GPEX_HOST(dev)->gpex_cfg.bus = PCI_HOST_BRIDGE(dev)->bus;
> +    s->bus = PCI_HOST_BRIDGE(dev)->bus;
>      return dev;
>  }

>  static FWCfgState *create_fw_cfg(const MachineState *ms, hwaddr base)
>  {
>      FWCfgState *fw_cfg;
> @@ -1426,6 +1446,15 @@ static void virt_machine_done(Notifier *notifier, void *data)
>                                       machine_done);
>      MachineState *machine = MACHINE(s);
>      hwaddr start_addr = s->memmap[VIRT_DRAM].base;
> +
> +    if (s->bus) {

There is a guard against !s->bus inside the function so you should be
able to do this unconditionally.

> +        cxl_hook_up_pxb_registers(s->bus, &s->cxl_devices_state,
> +                                  &error_fatal);
> +    }
> +
> +    if (s->cxl_devices_state.is_enabled) {
> +        cxl_fmws_link_targets(&error_fatal);
> +    }
>      hwaddr firmware_end_addr;
>      vaddr kernel_start_addr;
>      const char *firmware_name = riscv_default_firmware_name(&s->soc[0]);
> @@ -1663,6 +1692,17 @@ static void virt_machine_init(MachineState *machine)
>              ROUND_UP(virt_high_pcie_memmap.base, virt_high_pcie_memmap.size);
>      }
>  
> +    create_cxl_host_reg_region(s);
> +
> +    if (s->cxl_devices_state.is_enabled) {
> +        hwaddr cxl_base = virt_high_pcie_memmap.base +
> +                          virt_high_pcie_memmap.size;
> +        cxl_base += memory_region_size(&s->cxl_devices_state.host_mr);
> +        cxl_base = ROUND_UP(cxl_base, 256 * MiB);
> +        cxl_fmws_set_memmap(cxl_base, UINT64_MAX);
> +        cxl_fmws_update_mmio();
> +    }
> +
>      /* register system main memory (actual RAM) */
>      memory_region_add_subregion(system_memory, s->memmap[VIRT_DRAM].base,
>                                  machine->ram);
> @@ -1769,6 +1809,8 @@ static void virt_machine_instance_init(Object *obj)
>      s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
>      s->acpi = ON_OFF_AUTO_AUTO;
>      s->iommu_sys = ON_OFF_AUTO_AUTO;
> +
> +    cxl_machine_init(obj, &s->cxl_devices_state);
>  }
>  
>  static char *virt_get_aia_guests(Object *obj, Error **errp)
> diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
> index 18a2a323a3..c3201588bb 100644
> --- a/include/hw/riscv/virt.h
> +++ b/include/hw/riscv/virt.h
> @@ -24,6 +24,7 @@
>  #include "hw/core/sysbus.h"
>  #include "hw/block/flash.h"
>  #include "hw/intc/riscv_imsic.h"
> +#include "hw/cxl/cxl.h"
>  
>  #define VIRT_CPUS_MAX_BITS             9
>  #define VIRT_CPUS_MAX                  (1 << VIRT_CPUS_MAX_BITS)
> @@ -64,6 +65,8 @@ struct RISCVVirtState {
>      struct GPEXHost *gpex_host;
>      OnOffAuto iommu_sys;
>      uint16_t pci_iommu_bdf;
> +    CXLState cxl_devices_state;
> +    PCIBus *bus;

That's a very vague bit of naming.  I'd make it explicit what PCIBus this is.

>  };
>  
>  enum {



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine
  2026-06-09 12:41   ` Jonathan Cameron
@ 2026-06-10 12:46     ` Chen Pei
  0 siblings, 0 replies; 9+ messages in thread
From: Chen Pei @ 2026-06-10 12:46 UTC (permalink / raw)
  To: jic23
  Cc: alistair.francis, chao.liu.zevorn, cp0613, daniel.barboza, fan.ni,
	guoren, jonathan.cameron, liwei1518, palmer, pbonzini, qemu-devel,
	qemu-riscv, sunilvl, zhiwei_liu

On Tue, 9 Jun 2026 13:41:01 +0100, Jonathan Cameron <jic23@kernel.org> wrote:

Hi Jonathan,

Thanks for the review.

> > @@ -1426,6 +1446,15 @@ static void virt_machine_done(Notifier *notifier, void *data)
> >                                       machine_done);
> >      MachineState *machine = MACHINE(s);
> >      hwaddr start_addr = s->memmap[VIRT_DRAM].base;
> > +
> > +    if (s->bus) {
> 
> There is a guard against !s->bus inside the function so you should be
> able to do this unconditionally.

You're right. Will drop the outer if (s->bus) guard in v2.

> > @@ -64,6 +65,8 @@ struct RISCVVirtState {
> >      struct GPEXHost *gpex_host;
> >      OnOffAuto iommu_sys;
> >      uint16_t pci_iommu_bdf;
> > +    CXLState cxl_devices_state;
> > +    PCIBus *bus;
> 
> That's a very vague bit of naming.  I'd make it explicit what PCIBus this is.

Agreed. Will rename it to `pci_bus` in v2 to make it clear this is
the root bus of the PCIe host bridge (PCI0).

Best,
Pei


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine
@ 2026-08-21  8:19 Chen Pei
  2026-08-21  8:19 ` [PATCH 1/4] " Chen Pei
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Chen Pei @ 2026-08-21  8:19 UTC (permalink / raw)
  To: palmer, alistair.francis, mst, imammedo, sunilvl, jic23
  Cc: pbonzini, liwei1518, daniel.barboza, zhiwei_liu, chao.liu,
	anisinha, dave.jiang, alison.schofield, junjie.cao, guoren,
	qemu-riscv, qemu-devel, linux-cxl

This series adds CXL support to the RISC-V virt machine, following the
approach used by the ARM virt machine: CXL host bridges (pxb-cxl) are
described as ACPI0016 devices, an ACPI0017 (CXLM) device is added to the
DSDT, and a CEDT table is built.  A bios-tables test is added to pin down
the generated ACPI tables.

Prerequisite
------------
The v2 posting depended on Alireza Sanaee's v8 series [1] (the
performant non-interleaved CFMW lookup path).  That series has since
been merged upstream, so this version has no outstanding prerequisite
and applies directly on current upstream master.

Changes since v2
----------------
  - hw/riscv/virt: The MMIO-window patch no longer touches the common
    gpex code (Igor).  The gpex_cfg.cxl_mmio32 field and the is_cxl
    static _CRS branch are dropped; the ACPI0016 _CRS now comes from the
    generic build_crs() path.  Since EDK2 does not enumerate the pxb-cxl
    expander bridge, riscv simulates the firmware PCI initialization
    (reserved window + depth-first bridge bus numbers) and re-applies it
    via a reset handler.
  - hw/riscv/virt: Drops the machine-global window/bus-range synthesis
    that did not scale past one pxb-cxl (Junjie).  The series targets a
    single CXL host bridge for now, documented in the commit message and
    a TODO.
  - hw/riscv/virt: CXL host register region and FMW setup folded into a
    single cxl_host_state_init() helper; redundant braces removed in
    create_fdt_pcie() (Daniel).
  - hw/riscv/virt-acpi-build: the _DEP commit message documents the
    kernel-compatibility behaviour (Alistair).
  - Carried review tags: Sunil V L's Reviewed-by on the _DEP patch and
    Alistair Francis's Acked-by on the test.

Changes since v1
---------------
  - hw/riscv/virt: PCIBus *bus renamed to PCIBus *pci_bus (Jonathan).
  - hw/riscv/virt: Dropped outer if (s->pci_bus) guard around
    cxl_hook_up_pxb_registers(); the function already handles a NULL
    bus internally (Jonathan).
  - hw/riscv/virt-acpi-build: All s->bus references updated to
    s->pci_bus; iasl -d decompiled DSDT fragment added to commit
    message.
  - hw/riscv/virt,gpex: Commit message expanded with PCI-to-PCI Bridge
    Spec §3.2.5.8/9/10 citations (Jonathan).
  - Original patch 4 ("Map committed HDM decoder ranges as RAM for
    direct DMA") dropped; superseded by Alireza Sanaee's v8 series [1],
    which is now merged upstream and thus no longer a prerequisite.
  - New patch 4: RISC-V ACPI bios-tables test for CXL, with golden AML
    files generated and included.

[1] https://lore.kernel.org/qemu-devel/20260318171918.146-1-alireza.sanaee@huawei.com/

Test
----
Built riscv64-softmmu and ran the new bios-tables test together with the
existing riscv64 ACPI tests; all pass.  Also booted an RVA22 kernel with
pxb-cxl + cxl-rp + cxl-type3 + a CFMW: the root port and Type3 device
enumerate (0000:0c:00.0 / 0000:0d:00.0) and 'cxl list' reports the
memdev (4 GiB) and the CFMWS root decoder.

QEMU invocation (CXL-relevant options shown):

    qemu-system-riscv64 \
        -M virt,aia=aplic-imsic,acpi=on,cxl=on \
        -cpu rv64 -smp 2 -m 4G,maxmem=8G,slots=8 \
        -object memory-backend-ram,id=vmem0,share=on,size=4G \
        -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \
        -device cxl-rp,port=0,bus=cxl.1,id=rp0,chassis=0,slot=2 \
        -device cxl-type3,bus=rp0,volatile-memdev=vmem0,id=cxl-vmem0 \
        -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G \
        ...

Verification (total system memory grows by 4 GiB after onlining):

    # cxl list
    # cxl enable-memdev mem0
    # cxl create-region -m -t ram -d decoder0.0 -w 1 mem0 -s 4G
    # daxctl online-memory dax0.0
    # free -h

Chen Pei (4):
  hw/riscv/virt: Add CXL support to the RISC-V virt machine
  hw/riscv/virt-acpi-build: Add _DEP to ACPI0017 for CXL host bridge
    dependency
  hw/riscv/virt: Provide a 32-bit MMIO window for CXL host bridges
  tests/qtest: Add RISC-V ACPI bios tables test for CXL

 hw/riscv/Kconfig                      |   2 +
 hw/riscv/virt-acpi-build.c            |  52 +++++++
 hw/riscv/virt.c                       | 191 +++++++++++++++++++++++++-
 include/hw/riscv/virt.h               |   3 +
 tests/data/acpi/riscv64/virt/CEDT.cxl | Bin 0 -> 108 bytes
 tests/data/acpi/riscv64/virt/DSDT.cxl | Bin 0 -> 6212 bytes
 tests/qtest/bios-tables-test.c        |  54 ++++++++
 7 files changed, 301 insertions(+), 1 deletion(-)
 create mode 100644 tests/data/acpi/riscv64/virt/CEDT.cxl
 create mode 100644 tests/data/acpi/riscv64/virt/DSDT.cxl

-- 
2.50.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine
  2026-08-21  8:19 [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
@ 2026-08-21  8:19 ` Chen Pei
  2026-08-21  8:19 ` [PATCH 2/4] hw/riscv/virt-acpi-build: Add _DEP to ACPI0017 for CXL host bridge dependency Chen Pei
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Chen Pei @ 2026-08-21  8:19 UTC (permalink / raw)
  To: palmer, alistair.francis, mst, imammedo, sunilvl, jic23
  Cc: pbonzini, liwei1518, daniel.barboza, zhiwei_liu, chao.liu,
	anisinha, dave.jiang, alison.schofield, junjie.cao, guoren,
	qemu-riscv, qemu-devel, linux-cxl

Enable CXL support on the RISC-V virt machine following the same
approach used by the ARM virt machine:
- Add PXB and ACPI_CXL Kconfig selections
- Add CXLState and PCIBus pointer to RISCVVirtState
- Register CXL machine properties via cxl_machine_init()
- Create CXL host register region above the PCIe high MMIO region
- Call cxl_hook_up_pxb_registers() and cxl_fmws_link_targets() at
  machine_done time
- Map Fixed Memory Windows above the CXL host register region
- Add ACPI0017 device in DSDT and build CEDT table in virt-acpi-build.c

Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
---
 hw/riscv/Kconfig           |  2 ++
 hw/riscv/virt-acpi-build.c | 20 ++++++++++++++++++++
 hw/riscv/virt.c            | 37 +++++++++++++++++++++++++++++++++++++
 include/hw/riscv/virt.h    |  3 +++
 4 files changed, 62 insertions(+)

diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index de37c08cae..0ba3cd9e15 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -66,8 +66,10 @@ config RISCV_VIRT
     select VIRTIO_MMIO
     select FW_CFG_DMA
     select PLATFORM_BUS
+    select PXB
     select ACPI
     select ACPI_PCI
+    select ACPI_CXL
 
 config SHAKTI_C
     bool
diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c
index 59c454f4f9..3188cdc5bf 100644
--- a/hw/riscv/virt-acpi-build.c
+++ b/hw/riscv/virt-acpi-build.c
@@ -29,12 +29,16 @@
 #include "hw/acpi/aml-build.h"
 #include "hw/acpi/pci.h"
 #include "hw/acpi/utils.h"
+#include "hw/acpi/cxl.h"
 #include "hw/intc/riscv_aclint.h"
 #include "hw/nvram/fw_cfg_acpi.h"
 #include "hw/pci-host/gpex.h"
+#include "hw/pci/pci_bus.h"
 #include "hw/riscv/virt.h"
 #include "hw/riscv/numa.h"
 #include "hw/virtio/virtio-acpi.h"
+#include "hw/cxl/cxl.h"
+#include "hw/cxl/cxl_host.h"
 #include "kvm/kvm_riscv.h"
 #include "migration/vmstate.h"
 #include "qapi/error.h"
@@ -514,6 +518,17 @@ static void build_dsdt(GArray *table_data,
         acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + s->num_sources * 2);
     }
 
+    if (s->cxl_devices_state.is_enabled) {
+        Aml *cxl_dev = aml_device("CXLM");
+        aml_append(cxl_dev, aml_name_decl("_HID", aml_string("ACPI0017")));
+        Aml *method = aml_method("_STA", 0, AML_NOTSERIALIZED);
+        aml_append(method, aml_return(aml_int(0x0B)));
+        aml_append(cxl_dev, method);
+        build_cxl_dsm_method(cxl_dev);
+
+        aml_append(scope, cxl_dev);
+    }
+
     aml_append(dsdt, scope);
 
     /* copy AML table into ACPI tables blob and patch header there */
@@ -921,6 +936,11 @@ static void virt_acpi_build(RISCVVirtState *s, AcpiBuildTables *tables)
                    s->oem_table_id);
     }
 
+    if (s->cxl_devices_state.is_enabled) {
+        cxl_build_cedt(table_offsets, tables_blob, tables->linker,
+                       s->oem_id, s->oem_table_id, &s->cxl_devices_state);
+    }
+
     if (ms->numa_state->num_nodes > 0) {
         acpi_add_table(table_offsets, tables_blob);
         build_srat(tables_blob, tables->linker, s);
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 51bac47a91..e08c76af4d 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -55,6 +55,8 @@
 #include "hw/pci/pci.h"
 #include "hw/pci-host/gpex.h"
 #include "hw/display/ramfb.h"
+#include "hw/cxl/cxl.h"
+#include "hw/cxl/cxl_host.h"
 #include "hw/acpi/aml-build.h"
 #include "qapi/qapi-visit-common.h"
 #include "hw/virtio/virtio-iommu.h"
@@ -1101,9 +1103,33 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
     }
 
     GPEX_HOST(dev)->gpex_cfg.bus = PCI_HOST_BRIDGE(dev)->bus;
+    s->pci_bus = PCI_HOST_BRIDGE(dev)->bus;
     return dev;
 }
 
+static void cxl_host_state_init(RISCVVirtState *s)
+{
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *mr = &s->cxl_devices_state.host_mr;
+    hwaddr base;
+
+    if (!s->cxl_devices_state.is_enabled) {
+        return;
+    }
+
+    base = virt_high_pcie_memmap.base + virt_high_pcie_memmap.size;
+    base = ROUND_UP(base, 64 * KiB);
+
+    memory_region_init(mr, OBJECT(s), "cxl_host_reg", 64 * KiB * 16);
+    memory_region_add_subregion(sysmem, base, mr);
+
+    /* Map the Fixed Memory Windows above the CXL host register region. */
+    base += memory_region_size(mr);
+    base = ROUND_UP(base, 256 * MiB);
+    cxl_fmws_set_memmap(base, UINT64_MAX);
+    cxl_fmws_update_mmio();
+}
+
 static FWCfgState *create_fw_cfg(const MachineState *ms, hwaddr base)
 {
     FWCfgState *fw_cfg;
@@ -1205,6 +1231,13 @@ static void virt_machine_done(Notifier *notifier, void *data)
                                      machine_done);
     MachineState *machine = MACHINE(s);
     hwaddr start_addr = s->memmap[VIRT_DRAM].base;
+
+    cxl_hook_up_pxb_registers(s->pci_bus, &s->cxl_devices_state,
+                              &error_fatal);
+
+    if (s->cxl_devices_state.is_enabled) {
+        cxl_fmws_link_targets(&error_fatal);
+    }
     hwaddr firmware_end_addr;
     vaddr kernel_start_addr;
     const char *firmware_name = riscv_default_firmware_name(&s->soc[0]);
@@ -1451,6 +1484,8 @@ static void virt_machine_init(MachineState *machine)
             ROUND_UP(virt_high_pcie_memmap.base, virt_high_pcie_memmap.size);
     }
 
+    cxl_host_state_init(s);
+
     /* register system main memory (actual RAM) */
     memory_region_add_subregion(system_memory, s->memmap[VIRT_DRAM].base,
                                 machine->ram);
@@ -1558,6 +1593,8 @@ static void virt_machine_instance_init(Object *obj)
     s->acpi = ON_OFF_AUTO_AUTO;
     s->iommu_sys = ON_OFF_AUTO_AUTO;
     s->num_sources = VIRT_IRQCHIP_NUM_SOURCES;
+
+    cxl_machine_init(obj, &s->cxl_devices_state);
 }
 
 static char *virt_get_aia_guests(Object *obj, Error **errp)
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index 36a2def410..4cf930ab9f 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -24,6 +24,7 @@
 #include "hw/core/sysbus.h"
 #include "hw/block/flash.h"
 #include "hw/intc/riscv_imsic.h"
+#include "hw/cxl/cxl.h"
 
 #define VIRT_CPUS_MAX_BITS             9
 #define VIRT_CPUS_MAX                  (1 << VIRT_CPUS_MAX_BITS)
@@ -65,6 +66,8 @@ struct RISCVVirtState {
     OnOffAuto iommu_sys;
     uint16_t pci_iommu_bdf;
     uint16_t num_sources;
+    CXLState cxl_devices_state;
+    PCIBus *pci_bus;
 };
 
 enum {
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/4] hw/riscv/virt-acpi-build: Add _DEP to ACPI0017 for CXL host bridge dependency
  2026-08-21  8:19 [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
  2026-08-21  8:19 ` [PATCH 1/4] " Chen Pei
@ 2026-08-21  8:19 ` Chen Pei
  2026-08-21  8:19 ` [PATCH 3/4] hw/riscv/virt: Provide a 32-bit MMIO window for CXL host bridges Chen Pei
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Chen Pei @ 2026-08-21  8:19 UTC (permalink / raw)
  To: palmer, alistair.francis, mst, imammedo, sunilvl, jic23
  Cc: pbonzini, liwei1518, daniel.barboza, zhiwei_liu, chao.liu,
	anisinha, dave.jiang, alison.schofield, junjie.cao, guoren,
	qemu-riscv, qemu-devel, linux-cxl, Sunil V L

On RISC-V QEMU virt platform with CXL enabled, the probe ordering
of acpi_pci_root (ACPI0016) and cxl_acpi (ACPI0017) is not
guaranteed. If cxl_acpi probes before acpi_pci_root has attached
the CXL host bridges, the CXL port topology will be incomplete
because to_cxl_host_bridge() silently skips devices whose PCI root
is not yet ready.

Add a _DEP object to the ACPI0017 device in the DSDT, declaring
its dependency on the ACPI0016 CXL host bridge devices. This tells
the OS to defer ACPI0017 enumeration until all ACPI0016 devices
have been attached by acpi_pci_root.

This requires a corresponding kernel change to call
acpi_dev_clear_dependencies() in acpi_pci_root_add().

Note on kernel compatibility: this is a "new QEMU feature requires a
sufficiently new kernel" situation.  Kernels that process _DEP but
lack the acpi_dev_clear_dependencies() call will leave the CXLM device
deferred, so CXL memory is unusable there; kernels old enough to ignore
_DEP entirely behave exactly as before this change.  In neither case is
existing (non-CXL) PCIe affected.

The resulting DSDT fragment (iasl -d output) for a single CXL host
bridge at bus 0x01 looks like:

  Device (CXLM)
  {
      Name (_HID, "ACPI0017")
      Name (_DEP, Package (0x01)
      {
          \_SB.PC01
      })
      Method (_STA, 0, NotSerialized)
      {
          Return (0x0B)
      }
      ...
  }

Reviewed-by: Sunil V L <sunilvl@oss.qualcomm.com>
Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
---
 hw/riscv/virt-acpi-build.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c
index 3188cdc5bf..56292f04a7 100644
--- a/hw/riscv/virt-acpi-build.c
+++ b/hw/riscv/virt-acpi-build.c
@@ -521,6 +521,38 @@ static void build_dsdt(GArray *table_data,
     if (s->cxl_devices_state.is_enabled) {
         Aml *cxl_dev = aml_device("CXLM");
         aml_append(cxl_dev, aml_name_decl("_HID", aml_string("ACPI0017")));
+
+        /*
+         * Declare a _DEP on every ACPI0016 CXL host bridge so the OS
+         * defers ACPI0017 enumeration until acpi_pci_root has attached
+         * the CXL host bridges. Without this, cxl_acpi may probe before
+         * to_cxl_host_bridge() can resolve the PCI root and the CXL
+         * port topology comes up empty.
+         */
+        if (s->pci_bus) {
+            PCIBus *bus;
+            uint32_t num_cxl_hbs = 0;
+
+            QLIST_FOREACH(bus, &s->pci_bus->child, sibling) {
+                if (pci_bus_is_root(bus) && pci_bus_is_cxl(bus)) {
+                    num_cxl_hbs++;
+                }
+            }
+
+            if (num_cxl_hbs > 0) {
+                Aml *dep_pkg = aml_package(num_cxl_hbs);
+
+                QLIST_FOREACH(bus, &s->pci_bus->child, sibling) {
+                    if (pci_bus_is_root(bus) && pci_bus_is_cxl(bus)) {
+                        aml_append(dep_pkg,
+                                   aml_name("\\_SB.PC%.02X",
+                                            pci_bus_num(bus)));
+                    }
+                }
+                aml_append(cxl_dev, aml_name_decl("_DEP", dep_pkg));
+            }
+        }
+
         Aml *method = aml_method("_STA", 0, AML_NOTSERIALIZED);
         aml_append(method, aml_return(aml_int(0x0B)));
         aml_append(cxl_dev, method);
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/4] hw/riscv/virt: Provide a 32-bit MMIO window for CXL host bridges
  2026-08-21  8:19 [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
  2026-08-21  8:19 ` [PATCH 1/4] " Chen Pei
  2026-08-21  8:19 ` [PATCH 2/4] hw/riscv/virt-acpi-build: Add _DEP to ACPI0017 for CXL host bridge dependency Chen Pei
@ 2026-08-21  8:19 ` Chen Pei
  2026-08-21  8:19 ` [PATCH 4/4] tests/qtest: Add RISC-V ACPI bios tables test for CXL Chen Pei
  2026-08-21  8:43 ` [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
  4 siblings, 0 replies; 9+ messages in thread
From: Chen Pei @ 2026-08-21  8:19 UTC (permalink / raw)
  To: palmer, alistair.francis, mst, imammedo, sunilvl, jic23
  Cc: pbonzini, liwei1518, daniel.barboza, zhiwei_liu, chao.liu,
	anisinha, dave.jiang, alison.schofield, junjie.cao, guoren,
	qemu-riscv, qemu-devel, linux-cxl

CXL component register BAR (BAR0 on CXL Root Port and Type3 device)
and the CXL device register BAR (BAR2 on Type3 device) are declared
as 64-bit non-prefetchable memory.  Per the PCI-to-PCI Bridge
Architecture Specification Rev 1.2 (PCI-SIG, 2003):

  - §3.2.5.8 (Memory Base/Limit): the non-prefetchable window covers
    only 32-bit addresses (AD[31:20]); the Type 1 header defines no
    upper-32-bit extension for it.
  - §3.2.5.9 (Prefetchable Memory Base/Limit): the bottom 4 bits
    encode 64-bit support (01h), but this applies exclusively to the
    *prefetchable* window.
  - §3.2.5.10 (Prefetchable Base/Limit Upper 32 Bits): optional
    registers for AD[63:32] of the prefetchable range only.

The architecture therefore allows a 64-bit window only when it is also
prefetchable; there is no 64-bit non-prefetchable form.  PCIe inherits
this Type 1 header layout unchanged.  Linux thus places 64-bit
non-prefetchable BARs in the 32-bit non-prefetchable bridge window,
which requires the bridge to own enough address space below 4 GiB.

On RISC-V virt the 32-bit PCIe MMIO range (1 GiB at 0x40000000) is
currently consumed entirely by PCI0, so CXL host bridges (ACPI0016)
have no non-prefetchable window and Linux fails to assign these BARs.

Reserve the top 256 MiB of the 32-bit MMIO window exclusively for CXL
host bridges, keeping all of the logic in the riscv code:

- Reduce the FDT 'ranges' for PCI0 by 256 MiB so that device-tree
  driven firmware does not hand that range to PCI0.
- The CXL host bridge _CRS is produced by the generic build_crs()
  path.  EDK2's PciBusDxe only recurses into PCI-to-PCI bridges, while
  the pxb-cxl expander bridge presents as a class 0x0600 host bridge
  with a type-0 header, so firmware never enumerates behind it and
  leaves the CXL root port's window and bus-number registers unset;
  build_crs() would therefore return an empty _CRS.  Simulate the
  firmware PCI initialization in virt_cxl_init_bridge_windows(): assign
  the reserved window to the root port's memory window, and allocate
  primary/secondary/subordinate bus numbers depth-first.  build_crs()
  then emits both the memory range and a correct bus-range resource
  (without a bus range Linux only claims the host-bridge bus and cannot
  enumerate the devices behind the root port), and the range is
  excluded from PCI0's _CRS.  A reset handler re-applies the state, as
  a PCI reset clears those registers.

This targets a single pxb-cxl (one CXL host bridge), which matches the
bios-tables test added later in the series.  With more than one
pxb-cxl the single reserved window would be claimed by every ACPI0016
bridge, so it would need to be partitioned per bridge; that is left as
future work (see the TODO in virt_cxl_init_bridge_windows()).

Verified by booting an RVA22 kernel: the CXL root port and Type3 device
are enumerated (0000:0c:00.0 / 0000:0d:00.0) and 'cxl list' reports the
memdev and the CFMWS root decoder.

Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
---
 hw/riscv/virt.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 153 insertions(+), 1 deletion(-)

diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index e08c76af4d..af83603293 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -48,11 +48,13 @@
 #include "chardev/char.h"
 #include "system/device_tree.h"
 #include "system/system.h"
+#include "system/reset.h"
 #include "system/tcg.h"
 #include "system/kvm.h"
 #include "system/tpm.h"
 #include "system/qtest.h"
 #include "hw/pci/pci.h"
+#include "hw/pci/pci_bridge.h"
 #include "hw/pci-host/gpex.h"
 #include "hw/display/ramfb.h"
 #include "hw/cxl/cxl.h"
@@ -115,6 +117,9 @@ static const MemMapEntry virt_memmap[] = {
 /* PCIe high mmio for RV64, size is fixed but base depends on top of RAM */
 #define VIRT64_HIGH_PCIE_MMIO_SIZE  (16 * GiB)
 
+/* 32-bit MMIO range carved out of VIRT_PCIE_MMIO for CXL host bridges */
+#define VIRT_CXL_MMIO32_SIZE        (256 * MiB)
+
 static MemMapEntry virt_high_pcie_memmap;
 
 #define VIRT_FLASH_SECTOR_SIZE (256 * KiB)
@@ -727,6 +732,18 @@ static void create_fdt_pcie(RISCVVirtState *s,
 {
     g_autofree char *name = NULL;
     MachineState *ms = MACHINE(s);
+    /*
+     * When CXL is enabled, reserve the last 256 MiB of the 32-bit MMIO
+     * window for CXL host bridges and exclude it from the main PCIe host
+     * bridge's FDT 'ranges' so UEFI's PciHostBridgeDxe does not allocate
+     * that range to PCI0.  The CXL host bridge _CRS declares this range
+     * independently.
+     */
+    hwaddr mmio32_size = s->memmap[VIRT_PCIE_MMIO].size;
+
+    if (s->cxl_devices_state.is_enabled) {
+        mmio32_size -= VIRT_CXL_MMIO32_SIZE;
+    }
 
     name = g_strdup_printf("/soc/pci@%"HWADDR_PRIx,
                            s->memmap[VIRT_PCIE_ECAM].base);
@@ -752,7 +769,7 @@ static void create_fdt_pcie(RISCVVirtState *s,
         2, s->memmap[VIRT_PCIE_PIO].base, 2, s->memmap[VIRT_PCIE_PIO].size,
         1, FDT_PCI_RANGE_MMIO,
         2, s->memmap[VIRT_PCIE_MMIO].base,
-        2, s->memmap[VIRT_PCIE_MMIO].base, 2, s->memmap[VIRT_PCIE_MMIO].size,
+        2, s->memmap[VIRT_PCIE_MMIO].base, 2, mmio32_size,
         1, FDT_PCI_RANGE_MMIO_64BIT,
         2, virt_high_pcie_memmap.base,
         2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size);
@@ -1130,6 +1147,132 @@ static void cxl_host_state_init(RISCVVirtState *s)
     cxl_fmws_update_mmio();
 }
 
+/*
+ * Assign PCI bus numbers to the bridges under @bus in depth-first order,
+ * the way firmware does during enumeration.  QEMU leaves the secondary and
+ * subordinate bus registers at 0 until firmware programs them, so without
+ * this build_crs() would see an empty bus range for the CXL host bridge.
+ * @bus itself is numbered @bus_num.  Returns the highest bus number used.
+ */
+static int virt_cxl_assign_bus_numbers(PCIBus *bus, int bus_num)
+{
+    int max_bus = bus_num;
+    int next_bus = bus_num + 1;
+    int devfn;
+
+    for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+        PCIDevice *dev = bus->devices[devfn];
+        PCIBus *sec_bus;
+        int subordinate;
+
+        if (!dev) {
+            continue;
+        }
+        if ((dev->config[PCI_HEADER_TYPE] &
+             ~PCI_HEADER_TYPE_MULTI_FUNCTION) != PCI_HEADER_TYPE_BRIDGE) {
+            continue;
+        }
+
+        sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev));
+        subordinate = virt_cxl_assign_bus_numbers(sec_bus, next_bus);
+
+        pci_set_byte(dev->config + PCI_PRIMARY_BUS, bus_num);
+        pci_set_byte(dev->config + PCI_SECONDARY_BUS, next_bus);
+        pci_set_byte(dev->config + PCI_SUBORDINATE_BUS, subordinate);
+
+        if (subordinate > max_bus) {
+            max_bus = subordinate;
+        }
+        next_bus = subordinate + 1;
+    }
+
+    return max_bus;
+}
+
+/*
+ * Simulate the PCI resource initialization that firmware (UEFI) would
+ * normally perform for the CXL host bridge.
+ *
+ * EDK2's PciBusDxe only recurses into devices that look like PCI-to-PCI
+ * bridges, while the pxb-cxl expander bridge presents as a class 0x0600
+ * host bridge with a standard (type 0) header, so the firmware neither
+ * enumerates behind it nor assigns it a memory window or bus numbers.  As a
+ * result the generic build_crs() path would produce an empty _CRS for the
+ * CXL host bridge (ACPI0016).
+ *
+ * Program each CXL root port the way firmware would:
+ *  - assign the reserved 32-bit MMIO range (see create_fdt_pcie()) to its
+ *    memory window, so build_crs() emits that range in the host bridge _CRS
+ *    and excludes it from PCI0's _CRS;
+ *  - set the primary/secondary/subordinate bus numbers, so build_crs()
+ *    emits a correct bus-range resource.  Without a bus range Linux only
+ *    claims the single host-bridge bus and cannot enumerate the devices
+ *    behind the root port.
+ *
+ * TODO: this handles a single pxb-cxl with a single root port, which is
+ * the supported topology for now.  With multiple CXL host bridges or root
+ * ports the reserved window would need to be partitioned per bridge.
+ */
+static void virt_cxl_init_bridge_windows(RISCVVirtState *s)
+{
+    hwaddr cxl_mmio_base, cxl_mmio_limit;
+    PCIBus *bus;
+
+    if (!s->cxl_devices_state.is_enabled) {
+        return;
+    }
+
+    cxl_mmio_base = s->memmap[VIRT_PCIE_MMIO].base +
+                    s->memmap[VIRT_PCIE_MMIO].size - VIRT_CXL_MMIO32_SIZE;
+    cxl_mmio_limit = cxl_mmio_base + VIRT_CXL_MMIO32_SIZE - 1;
+
+    QLIST_FOREACH(bus, &s->pci_bus->child, sibling) {
+        int devfn;
+
+        if (!pci_bus_is_root(bus) || !pci_bus_is_cxl(bus)) {
+            continue;
+        }
+
+        /* Assign bus numbers for the whole CXL subtree (firmware would). */
+        virt_cxl_assign_bus_numbers(bus, pci_bus_num(bus));
+
+        /* Assign the reserved MMIO window to the root-port bridge. */
+        for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+            PCIDevice *dev = bus->devices[devfn];
+            PCIBridge *br;
+
+            if (!dev) {
+                continue;
+            }
+            if ((dev->config[PCI_HEADER_TYPE] &
+                 ~PCI_HEADER_TYPE_MULTI_FUNCTION) != PCI_HEADER_TYPE_BRIDGE) {
+                continue;
+            }
+
+            br = PCI_BRIDGE(dev);
+
+            pci_set_word(dev->config + PCI_MEMORY_BASE,
+                         (cxl_mmio_base >> 16) & PCI_MEMORY_RANGE_MASK);
+            pci_set_word(dev->config + PCI_MEMORY_LIMIT,
+                         (cxl_mmio_limit >> 16) & PCI_MEMORY_RANGE_MASK);
+            pci_word_test_and_set_mask(dev->config + PCI_COMMAND,
+                                       PCI_COMMAND_MEMORY);
+            pci_bridge_update_mappings(br);
+            break;
+        }
+    }
+}
+
+/*
+ * A PCI reset clears the bridge window and bus-number registers programmed
+ * above, so re-apply them after every reset, the way firmware would during
+ * its PCI enumeration.
+ */
+static void virt_cxl_reset_bridge_windows(void *opaque)
+{
+    virt_cxl_init_bridge_windows(RISCV_VIRT_MACHINE(opaque));
+}
+
 static FWCfgState *create_fw_cfg(const MachineState *ms, hwaddr base)
 {
     FWCfgState *fw_cfg;
@@ -1238,6 +1381,15 @@ static void virt_machine_done(Notifier *notifier, void *data)
     if (s->cxl_devices_state.is_enabled) {
         cxl_fmws_link_targets(&error_fatal);
     }
+
+    /*
+     * Assign the CXL bridge memory window (simulating firmware) so the
+     * ACPI _CRS built for the CXL host bridge is populated.  Do it here for
+     * the initial build, and register a reset handler so the window is
+     * re-applied after any PCI reset (which clears the bridge registers).
+     */
+    virt_cxl_init_bridge_windows(s);
+    qemu_register_reset(virt_cxl_reset_bridge_windows, s);
     hwaddr firmware_end_addr;
     vaddr kernel_start_addr;
     const char *firmware_name = riscv_default_firmware_name(&s->soc[0]);
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 4/4] tests/qtest: Add RISC-V ACPI bios tables test for CXL
  2026-08-21  8:19 [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
                   ` (2 preceding siblings ...)
  2026-08-21  8:19 ` [PATCH 3/4] hw/riscv/virt: Provide a 32-bit MMIO window for CXL host bridges Chen Pei
@ 2026-08-21  8:19 ` Chen Pei
  2026-08-21  8:43 ` [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
  4 siblings, 0 replies; 9+ messages in thread
From: Chen Pei @ 2026-08-21  8:19 UTC (permalink / raw)
  To: palmer, alistair.francis, mst, imammedo, sunilvl, jic23
  Cc: pbonzini, liwei1518, daniel.barboza, zhiwei_liu, chao.liu,
	anisinha, dave.jiang, alison.schofield, junjie.cao, guoren,
	qemu-riscv, qemu-devel, linux-cxl

Add test_acpi_riscv64_virt_tcg_cxl() to verify that enabling CXL on
the RISC-V virt machine produces correct ACPI tables, including the
ACPI0017 CXLM device with _DEP in the DSDT and the CEDT table.

The test boots with cxl=on, one pxb-cxl bus (bus_nr=12), a CXL root
port, a cxl-type3 persistent memory device, and a fixed memory window,
mirroring the existing x86 q35 CXL test pattern.

Since pxb-cxl is a root bus, using -cdrom causes QEMU to auto-plug the
cdrom drive into pxb-cxl, triggering "Only PCI/PCIe bridges can be
plugged into pxb-cxl".  The ISO is instead attached explicitly via a
virtio-scsi-pci controller on pcie.0, following the same approach as
test_acpi_aarch64_virt_tcg_pxb().

Acked-by: Alistair Francis <alistair.francis@wdc.com>
Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
---
 tests/data/acpi/riscv64/virt/CEDT.cxl | Bin 0 -> 108 bytes
 tests/data/acpi/riscv64/virt/DSDT.cxl | Bin 0 -> 6212 bytes
 tests/qtest/bios-tables-test.c        |  54 ++++++++++++++++++++++++++
 3 files changed, 54 insertions(+)
 create mode 100644 tests/data/acpi/riscv64/virt/CEDT.cxl
 create mode 100644 tests/data/acpi/riscv64/virt/DSDT.cxl

diff --git a/tests/data/acpi/riscv64/virt/CEDT.cxl b/tests/data/acpi/riscv64/virt/CEDT.cxl
new file mode 100644
index 0000000000000000000000000000000000000000..81d746dfb09ccb147e26bb64060404ca3191d097
GIT binary patch
literal 108
zc-nJzbqUE~U|?VjaPoKd2v%^42yj*a0!E-1hz+6{7!(+IKx!BefCEf2LP-snumA^?
O0aMEg#QH!C(hmS*@&^F`

literal 0
Hc-jL100001

diff --git a/tests/data/acpi/riscv64/virt/DSDT.cxl b/tests/data/acpi/riscv64/virt/DSDT.cxl
new file mode 100644
index 0000000000000000000000000000000000000000..4587f682c0c2053d4fb01b566e8e1782220c6bd5
GIT binary patch
literal 6212
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
C^C(OJ

literal 0
Hc-jL100001

diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index 5cc526510a..d2b18d8ecb 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -2214,6 +2214,56 @@ static void test_acpi_riscv64_virt_tcg(void)
     free_test_data(&data);
 }
 
+#ifdef CONFIG_POSIX
+static void test_acpi_riscv64_virt_tcg_cxl(void)
+{
+    gchar *tmp_path = g_dir_make_tmp("qemu-test-cxl.XXXXXX", NULL);
+    gchar *params;
+
+    test_data data = {
+        .machine = "virt",
+        .arch = "riscv64",
+        .tcg_only = true,
+        .uefi_fl1 = "pc-bios/edk2-riscv-code.fd",
+        .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd",
+        .ram_start = 0x80000000ULL,
+        .scan_len = 128ULL * MiB,
+        .variant = ".cxl",
+    };
+
+    /*
+     * While using -cdrom, the cdrom would auto-plug into pxb-cxl because
+     * its bus is also a root bus, triggering "Only PCI/PCIe bridges can be
+     * plugged into pxb-cxl".  Attach the ISO explicitly to a scsi controller
+     * on pcie.0 instead, following the same pattern as
+     * test_acpi_aarch64_virt_tcg_pxb().
+     */
+    params = g_strdup_printf("-cpu rva22s64"
+                             " -machine cxl=on"
+                             " -device pcie-root-port,chassis=1,id=pci.1,bus=pcie.0"
+                             " -device virtio-scsi-pci,id=scsi0,bus=pci.1"
+                             " -drive file=tests/data/uefi-boot-images/"
+                             "bios-tables-test.riscv64.iso.qcow2,"
+                             "if=none,media=cdrom,id=drive-scsi0-0-0-1,readonly=on"
+                             " -device scsi-cd,bus=scsi0.0,scsi-id=0,"
+                             "drive=drive-scsi0-0-0-1,id=scsi0-0-0-1,bootindex=1"
+                             " -object memory-backend-file,id=cxl-mem1,mem-path=%s,size=256M"
+                             " -object memory-backend-file,id=lsa1,mem-path=%s,size=256M"
+                             " -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1"
+                             " -device cxl-rp,port=0,bus=cxl.1,id=rp1,chassis=0,slot=2"
+                             " -device cxl-type3,bus=rp1,persistent-memdev=cxl-mem1,lsa=lsa1"
+                             " -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,"
+                             "cxl-fmw.0.interleave-granularity=8k",
+                             tmp_path, tmp_path);
+    test_acpi_one(params, &data);
+
+    g_free(params);
+    g_assert(g_rmdir(tmp_path) == 0);
+    g_free(tmp_path);
+    free_test_data(&data);
+}
+#endif /* CONFIG_POSIX */
+
 static void test_acpi_aarch64_virt_tcg(void)
 {
     test_data data = {
@@ -2963,6 +3013,10 @@ int main(int argc, char *argv[])
                            test_acpi_riscv64_virt_tcg_numamem);
             qtest_add_func("acpi/virt/acpispcr",
                            test_acpi_riscv64_virt_tcg_acpi_spcr);
+#ifdef CONFIG_POSIX
+            qtest_add_func("acpi/virt/cxl",
+                           test_acpi_riscv64_virt_tcg_cxl);
+#endif
         }
     } else if (strcmp(arch, "loongarch64") == 0) {
         if (has_tcg && qtest_has_machine("virt")) {
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine
  2026-08-21  8:19 [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
                   ` (3 preceding siblings ...)
  2026-08-21  8:19 ` [PATCH 4/4] tests/qtest: Add RISC-V ACPI bios tables test for CXL Chen Pei
@ 2026-08-21  8:43 ` Chen Pei
  4 siblings, 0 replies; 9+ messages in thread
From: Chen Pei @ 2026-08-21  8:43 UTC (permalink / raw)
  To: palmer, alistair.francis, mst, imammedo, sunilvl, jic23
  Cc: pbonzini, liwei1518, daniel.barboza, zhiwei_liu, chao.liu,
	anisinha, dave.jiang, alison.schofield, junjie.cao, guoren,
	qemu-riscv, qemu-devel, linux-cxl

Hi all,

Apologies, I forgot to add the version tag in the subject prefix.  This
is actually v3 of the series; the changelog against v2 and v1 is in the
cover letter.  Please ignore the missing "v3" tag for now -- it will be
tagged correctly in the next revision.

Sorry for the noise.

Thanks,
Pei

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-08-21  8:43 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-21  8:19 [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
2026-08-21  8:19 ` [PATCH 1/4] " Chen Pei
2026-08-21  8:19 ` [PATCH 2/4] hw/riscv/virt-acpi-build: Add _DEP to ACPI0017 for CXL host bridge dependency Chen Pei
2026-08-21  8:19 ` [PATCH 3/4] hw/riscv/virt: Provide a 32-bit MMIO window for CXL host bridges Chen Pei
2026-08-21  8:19 ` [PATCH 4/4] tests/qtest: Add RISC-V ACPI bios tables test for CXL Chen Pei
2026-08-21  8:43 ` [PATCH 0/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
  -- strict thread matches above, loose matches on Subject: below --
2026-06-02  7:41 [PATCH 0/4] hw/riscv/virt: Add CXL support and fix virtio DMA into CXL memory Chen Pei
2026-06-02  7:41 ` [PATCH 1/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
2026-06-09 12:41   ` Jonathan Cameron
2026-06-10 12:46     ` Chen Pei

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.