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

Junjie Cao <[email protected]>
Newsgroups org.kernel.vger.linux-cxl,org.nongnu.qemu-devel,org.nongnu.qemu-riscv
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.