On 2026/05/29 18:39, Igor Mammedov wrote:
On Thu, 28 May 2026 00:05:44 +0900
Akihiko Odaki <[email protected]> wrote:

Relax the lower bound of the HighMem MMIO region size to make it more
likely that the region fits in the PA space.

The lower bound was especially problematic on Apple M2. The current
lower bound, 512 GiB, is too big to fit in the limited PA space on the
hardware. That makes the HighMem MMIO region unavailable and limits the
PCIe MMIO space to the base MMIO region. The base MMIO region is not big
enough to contain a large hostmem window configured for virtio-gpu-gl-pci
with DRM native context, for example.

Relax the lower bound to 64 KiB, matching the memory map comment that
says "devices should generally be placed at multiples of 0x10000, to
accommodate guests using 64K pages." Also, automatically lower the
default HighMem MMIO region size to fit in the PA space for the latest
machine version; the older machine versions retain the previous default
HighMem MMIO region size for compatibility.

How useful/practical 64K window would be?
Should it be reasonably large (like 64Mb or similar), or mirror lower windows 
size?
CCing Gerd,
to look at it from firmware POV.

Practically it's not useful. I just followed the existing convention described in the comment instead of inventing another lower bound.


wrt 64K magic value, it opencoded in several places. I'd use a macro for that.

The new occurrences are:
- One numeric literal
- One string literal
- One documentation

They cannot be cleanly shared. The existing memory map contains many hardcoded values that are aligned by 64 KiB, but I think they are better to stay as is. For example, the size of VIRT_CXL_HOST can be rewritten as MEMMAP_GRANULARITY * 16 instead of 64 * KiB * 16, but if you do so, that size will vary when changing the granularity, which is probably not what we want. We actually need a fixed value that satisfies the requirement of CXL and is consistent with old versions of the machine.

Regards,
Akihiko Odaki



Signed-off-by: Akihiko Odaki <[email protected]>
---
  docs/system/arm/virt.rst |  2 +-
  include/hw/arm/virt.h    |  1 +
  hw/arm/virt.c            | 22 ++++++++++++++--------
  3 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst
index f811e662d68d..c8284ce2fa0e 100644
--- a/docs/system/arm/virt.rst
+++ b/docs/system/arm/virt.rst
@@ -149,7 +149,7 @@ highmem-mmio
highmem-mmio-size
    Set the high memory region size for PCI MMIO. Must be a power of 2 and
-  greater than or equal to the default size (512G).
+  greater than or equal to 64 KiB.
gic-version
    Specify the version of the Generic Interrupt Controller (GIC) to provide.
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 6c8ba8f3185b..7aa289d3936f 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -143,6 +143,7 @@ typedef enum VirtGICType {
  struct VirtMachineClass {
      MachineClass parent;
      hwaddr min_highmem_base;
+    hwaddr high_pcie_mmio_size;
      bool no_tcg_its;
      bool no_highmem_compact;
      bool no_kvm_steal_time;
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 78a85a966ad7..dd97b0dd6a42 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -222,8 +222,7 @@ static const MemMapEntry base_memmap[] = {
  };
/* Update the docs for highmem-mmio-size when changing this default */
-#define DEFAULT_HIGH_PCIE_MMIO_SIZE_GB 512
-#define DEFAULT_HIGH_PCIE_MMIO_SIZE (DEFAULT_HIGH_PCIE_MMIO_SIZE_GB * GiB)
+#define MAX_DEFAULT_HIGH_PCIE_MMIO_SIZE (512 * GiB)
/*
   * Highmem IO Regions: This memory map is floating, located after the RAM.
@@ -249,7 +248,7 @@ static MemMapEntry extended_memmap[] = {
      [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 },
+    [VIRT_HIGH_PCIE_MMIO] =     { 0x0, 0 },
      /* Any CXL Fixed memory windows come here */
  };
@@ -2448,9 +2447,14 @@ static void virt_set_high_memmap(VirtMachineState *vms, for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) {
          region_enabled = virt_get_high_memmap_enabled(vms, i);
-        region_base = ROUND_UP(base, extended_memmap[i].size);
          region_size = extended_memmap[i].size;
+ if (i == VIRT_HIGH_PCIE_MMIO && !region_size) {
+            region_size = CLAMP(pow2floor(BIT_ULL(pa_bits) - base),
+                                64 * KiB, MAX_DEFAULT_HIGH_PCIE_MMIO_SIZE);
+        }
+
+        region_base = ROUND_UP(base, region_size);
          vms->memmap[i].base = region_base;
          vms->memmap[i].size = region_size;
@@ -3340,11 +3344,9 @@ static void virt_set_highmem_mmio_size(Object *obj, Visitor *v,
          return;
      }
- if (size < DEFAULT_HIGH_PCIE_MMIO_SIZE) {
-        char *sz = size_to_str(DEFAULT_HIGH_PCIE_MMIO_SIZE);
+    if (size < 64 * KiB) {
          error_setg(errp, "highmem-mmio-size cannot be set to a lower value "
-                         "than the default (%s)", sz);
-        g_free(sz);
+                         "than 64 KiB");
          return;
      }
@@ -4271,6 +4273,8 @@ static void virt_instance_init(Object *obj)
      VirtMachineState *vms = VIRT_MACHINE(obj);
      VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
+ extended_memmap[VIRT_HIGH_PCIE_MMIO].size = vmc->high_pcie_mmio_size;
+
      /* EL3 is disabled by default on virt: this makes us consistent
       * between KVM and TCG for this board, and it also allows us to
       * boot UEFI blobs which assume no TrustZone support.
@@ -4372,6 +4376,8 @@ static void virt_machine_11_0_options(MachineClass *mc)
       * < 255GiB we keep the legacy memory map.
       */
      vmc->min_highmem_base = base_memmap[VIRT_MEM].base + 
LEGACY_RAMLIMIT_BYTES;
+
+    vmc->high_pcie_mmio_size = MAX_DEFAULT_HIGH_PCIE_MMIO_SIZE;
  }
  DEFINE_VIRT_MACHINE(11, 0)



Reply via email to