Re: [PATCH v2 3/4] hw/riscv/virt, gpex: Provide 32-bit MMIO window for CXL host bridges

Junjie Cao <[email protected]> Wed, 22 Jul 2026 21:52:38 +0800
Newsgroups org.nongnu.qemu-riscv,org.kernel.vger.linux-cxl,org.nongnu.qemu-devel
Message-ID <[email protected]>
Hi Chen Pei,

On Thu, 18 Jun 2026 17:38:25 +0800, Chen Pei wrote:
> +            if (is_cxl && cfg->cxl_mmio32.size) {
> +                uint64_t cxl_base = cfg->cxl_mmio32.base;
> +                uint64_t cxl_size = cfg->cxl_mmio32.size;
> +                crs = aml_resource_template();
> +                /* 32-bit MMIO range for CXL devices */
> +                aml_append(crs,
> +                    aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
> +                                     AML_MAX_FIXED, AML_NON_CACHEABLE,
> +                                     AML_READ_WRITE, 0,
> +                                     cxl_base, cxl_base + cxl_size - 1,
> +                                     0, cxl_size));

cfg->cxl_mmio32 is a single range on the main GPEX host, but this block
runs once per CXL host bridge in the QLIST_FOREACH(bus, &bus->child)
loop, so with more than one pxb-cxl every ACPI0016 gets a _CRS that
declares the *same* [cxl_base, cxl_base + 256 MiB) window.

I realise build_crs() returns nothing usable for these bridges at
ACPI-build time (the BARs aren't assigned yet, per your commit message),
which is why the static path exists -- but it hands that one shared
cfg->cxl_mmio32 to every CXL bridge.

I checked the generated DSDT for a two-pxb-cxl riscv64 config (cxl.1 at
bus_nr=12, cxl.2 at bus_nr=200): both ACPI0016 host bridges (PC0C and
PCC8) come up with the identical producer window

    DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, ...)
        0x70000000,   // Range Minimum
        0x7FFFFFFF,   // Range Maximum
        0x10000000,   // Length

while PCI0 is correctly shrunk to 0x40000000-0x6FFFFFFF. That 256 MiB
window is the only memory range either ACPI0016 advertises, so every BAR
behind the two bridges has to come from it; and since two host bridges
can't both decode the same range, at most one of them can actually
claim it. Multiple pxb-cxl is a supported topology in QEMU generally.
Partitioning the window per bridge may be one way out, but whether
that's the right model is your call -- is supporting more than one
pxb-cxl in scope for this series, or is a single CXL host bridge the
intended target for now? If the latter, a note to that effect in the
commit message would help.

One smaller thing about the hand-built template: the bus range is
hard-coded to [bus_num, bus_num + 15], so with cxl.1 at bus_nr=12 and
cxl.2 at bus_nr=16 the two _CRS come out as [0x0C,0x1B] and [0x10,0x1F],
which overlap on buses 0x10-0x1B. The real width depends on how many
buses the pxb-cxl spans, which isn't known at ACPI-build time (pxb-cxl
only carries a bus_nr). build_crs() is no drop-in here either: with the
static path disabled I saw the range collapse to a single bus
[bus_num, bus_num], since no subordinate buses are enumerated yet -- so
it under-sizes rather than over-sizes.

Both really trace back to the same spot: the static branch synthesises
the bridge's window and bus range from machine-global constants
(cfg->cxl_mmio32 and the fixed +15) rather than anything per-bridge, so
neither scales past one pxb-cxl. Going static is fair enough given
build_crs() has nothing to offer at build time -- as above, it supplies
neither the window nor a usable bus range -- so the real question is
just where the per-bridge window size and bus span should come from.

Many thanks,
Junjie