---
CC: Wei Liu <wei.liu2@xxxxxxxxxx>
CC: Stefano Stabellini <sstabellini@xxxxxxxxxx>
CC: Julien Grall <julien.grall@xxxxxxx>
v3:
  * Fix 4092/4096 typo
  * Fix build in NEW_VGIC case
v4:
  * Move the min() into ARM's common arch_sanitise_domain_config()
    implementation.  Simplfy the min() expression code by making MAX_VIRT_CPUS
    be an unsigned constant.
v5:
  * s/max/min
---
  xen/arch/arm/domain.c         | 18 ++++++++++++++++++
  xen/arch/arm/vgic-v2.c        |  1 -
  xen/arch/arm/vgic-v3.c        |  5 -----
  xen/arch/arm/vgic.c           | 16 ++++++++++++++--
  xen/arch/arm/vgic/vgic-init.c |  3 ---
  xen/arch/arm/vgic/vgic.c      | 16 ++++++----------
  xen/arch/x86/domain.c         | 10 ++++++++++
  xen/common/domain.c           | 33 ++++++++++++++++++++-------------
  xen/include/asm-arm/config.h  |  4 ++--
  xen/include/asm-arm/domain.h  |  6 ------
  xen/include/asm-arm/vgic.h    |  5 ++---
  xen/include/asm-x86/domain.h  |  2 --
  12 files changed, 72 insertions(+), 47 deletions(-)
diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
index 71ad1f9..1d926dc 100644
--- a/xen/arch/arm/domain.c
+++ b/xen/arch/arm/domain.c
@@ -601,6 +601,8 @@ void vcpu_switch_to_aarch64_mode(struct vcpu *v)
  
  int arch_sanitise_domain_config(struct xen_domctl_createdomain *config)
  {
+    unsigned int max_vcpus;
+
      if ( config->flags != (XEN_DOMCTL_CDF_hvm_guest | XEN_DOMCTL_CDF_hap) )
      {
          dprintk(XENLOG_INFO, "Unsupported configuration %#x\n", 
config->flags);
@@ -626,6 +628,22 @@ int arch_sanitise_domain_config(struct 
xen_domctl_createdomain *config)
          }
      }
  
+    /* max_vcpus depends on the GIC version, and Xen's compiled limit. */
+    max_vcpus = min(vgic_max_vcpus(config->arch.gic_version), MAX_VIRT_CPUS);
+
+    if ( max_vcpus == 0 )
+    {
+        dprintk(XENLOG_INFO, "Unsupported GIC version\n");
+        return -EINVAL;
+    }
+
+    if ( config->max_vcpus > max_vcpus )
+    {
+        dprintk(XENLOG_INFO, "Requested vCPUs (%u) exceeds max (%u)\n",
+                config->max_vcpus, max_vcpus);
+        return -EINVAL;
+    }
+
      return 0;
  }
  
diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
index bf77899..64b141f 100644
--- a/xen/arch/arm/vgic-v2.c
+++ b/xen/arch/arm/vgic-v2.c
@@ -725,7 +725,6 @@ static const struct vgic_ops vgic_v2_ops = {
      .domain_free = vgic_v2_domain_free,
      .lpi_to_pending = vgic_v2_lpi_to_pending,
      .lpi_get_priority = vgic_v2_lpi_get_priority,
-    .max_vcpus = 8,
  };
  
  int vgic_v2_init(struct domain *d, int *mmio_count)
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index c14bcd8..519cc72 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -1822,11 +1822,6 @@ static const struct vgic_ops v3_ops = {
      .emulate_reg  = vgic_v3_emulate_reg,
      .lpi_to_pending = vgic_v3_lpi_to_pending,
      .lpi_get_priority = vgic_v3_lpi_get_priority,
-    /*
-     * We use both AFF1 and AFF0 in (v)MPIDR. Thus, the max number of CPU
-     * that can be supported is up to 4096(==256*16) in theory.
-     */
-    .max_vcpus = 4096,
  };
  
  int vgic_v3_init(struct domain *d, int *mmio_count)
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index 5a4f082..f2608b0 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -667,9 +667,21 @@ void vgic_free_virq(struct domain *d, unsigned int virq)
      clear_bit(virq, d->arch.vgic.allocated_irqs);
  }
  
-unsigned int vgic_max_vcpus(const struct domain *d)
+unsigned int vgic_max_vcpus(unsigned int domctl_vgic_version)
  {
-    return min_t(unsigned int, MAX_VIRT_CPUS, d->arch.vgic.handler->max_vcpus);
+    switch ( domctl_vgic_version )
+    {
+    case XEN_DOMCTL_CONFIG_GIC_V2:
+        return 8;
+
+#ifdef CONFIG_GICV3
+    case XEN_DOMCTL_CONFIG_GIC_V3:
+        return 4096;
+#endif
+
+    default:
+        return 0;
+    }
  }
  
  /*
diff --git a/xen/arch/arm/vgic/vgic-init.c b/xen/arch/arm/vgic/vgic-init.c
index bfd3d09..62ae553 100644
--- a/xen/arch/arm/vgic/vgic-init.c
+++ b/xen/arch/arm/vgic/vgic-init.c
@@ -112,9 +112,6 @@ int domain_vgic_register(struct domain *d, int *mmio_count)
          BUG();
      }
  
-    if ( d->max_vcpus > domain_max_vcpus(d) )
-        return -E2BIG;
-
      d->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
      d->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
      d->arch.vgic.vgic_redist_base = VGIC_ADDR_UNDEF;
diff --git a/xen/arch/arm/vgic/vgic.c b/xen/arch/arm/vgic/vgic.c
index 7c3cfc5..e2844dc 100644
--- a/xen/arch/arm/vgic/vgic.c
+++ b/xen/arch/arm/vgic/vgic.c
@@ -949,20 +949,16 @@ void vgic_sync_hardware_irq(struct domain *d,
      spin_unlock_irqrestore(&desc->lock, flags);
  }
  
-unsigned int vgic_max_vcpus(const struct domain *d)
+unsigned int vgic_max_vcpus(unsigned int domctl_vgic_version)
  {
-    unsigned int vgic_vcpu_limit;
-
-    switch ( d->arch.vgic.version )
+    switch ( domctl_vgic_version )
      {
-    case GIC_V2:
-        vgic_vcpu_limit = VGIC_V2_MAX_CPUS;
-        break;
+    case XEN_DOMCTL_CONFIG_GIC_V2:
+        return VGIC_V2_MAX_CPUS;
+
      default:
-        BUG();
+        return 0;
      }
-
-    return min_t(unsigned int, MAX_VIRT_CPUS, vgic_vcpu_limit);
  }
  
  #ifdef CONFIG_GICV3
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 272fd84..295b10c 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -421,6 +421,7 @@ void arch_vcpu_destroy(struct vcpu *v)
  int arch_sanitise_domain_config(struct xen_domctl_createdomain *config)
  {
      bool hvm = config->flags & XEN_DOMCTL_CDF_hvm_guest;
+    unsigned int max_vcpus;
  
      if ( hvm ? !hvm_enabled : !IS_ENABLED(CONFIG_PV) )
      {
@@ -428,6 +429,15 @@ int arch_sanitise_domain_config(struct 
xen_domctl_createdomain *config)
          return -EINVAL;
      }
  
+    max_vcpus = hvm ? HVM_MAX_VCPUS : MAX_VIRT_CPUS;
+
+    if ( config->max_vcpus > max_vcpus )
+    {
+        dprintk(XENLOG_INFO, "Requested vCPUs (%u) exceeds max (%u)\n",
+                config->max_vcpus, max_vcpus);
+        return -EINVAL;
+    }
+
      return 0;
  }
  
diff --git a/xen/common/domain.c b/xen/common/domain.c
index f69f405..78cc524 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -300,6 +300,12 @@ static int sanitise_domain_config(struct 
xen_domctl_createdomain *config)
          return -EINVAL;
      }
  
+    if ( config->max_vcpus < 1 )
+    {
+        dprintk(XENLOG_INFO, "No vCPUS\n");
+        return -EINVAL;
+    }
+
      return arch_sanitise_domain_config(config);
  }
  
@@ -345,6 +351,20 @@ struct domain *domain_create(domid_t domid,
  
      TRACE_1D(TRC_DOM0_DOM_ADD, d->domain_id);
  
+    /*
+     * Allocate d->vcpu[] and set ->max_vcpus up early.  Various per-domain
+     * resources want to be sized based on max_vcpus.
+     */
+    if ( !is_system_domain(d) )
+    {
+        err = -ENOMEM;
+        d->vcpu = xzalloc_array(struct vcpu *, config->max_vcpus);
+        if ( !d->vcpu )
+            goto fail;
+
+        d->max_vcpus = config->max_vcpus;
+    }
+
      lock_profile_register_struct(LOCKPROF_TYPE_PERDOM, d, domid, "Domain");
  
      if ( (err = xsm_alloc_security_domain(d)) != 0 )
@@ -396,19 +416,6 @@ struct domain *domain_create(domid_t domid,
  
      if ( !is_idle_domain(d) )
      {
-        /* Check d->max_vcpus and allocate d->vcpu[]. */
-        err = -EINVAL;
-        if ( config->max_vcpus < 1 ||
-             config->max_vcpus > domain_max_vcpus(d) )
-            goto fail;
-
-        err = -ENOMEM;
-        d->vcpu = xzalloc_array(struct vcpu *, config->max_vcpus);
-        if ( !d->vcpu )
-            goto fail;
-
-        d->max_vcpus = config->max_vcpus;
-
          watchdog_domain_init(d);
          init_status |= INIT_watchdog;
  
diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h
index cdae8f6..bc89e84 100644
--- a/xen/include/asm-arm/config.h
+++ b/xen/include/asm-arm/config.h
@@ -41,9 +41,9 @@
  #define OPT_CONSOLE_STR "dtuart"
  
  #ifdef CONFIG_ARM_64
-#define MAX_VIRT_CPUS 128
+#define MAX_VIRT_CPUS 128u
  #else
-#define MAX_VIRT_CPUS 8
+#define MAX_VIRT_CPUS 8u
  #endif
  
  #define INVALID_VCPU_ID MAX_VIRT_CPUS
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index d682307..175de44 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -208,12 +208,6 @@ void vcpu_show_execution_state(struct vcpu *);
  void vcpu_show_registers(const struct vcpu *);
  void vcpu_switch_to_aarch64_mode(struct vcpu *);
  
-/* On ARM, the number of VCPUs is limited by the type of GIC emulated. */
-static inline unsigned int domain_max_vcpus(const struct domain *d)
-{
-    return vgic_max_vcpus(d);
-}
-
  /*
   * Due to the restriction of GICv3, the number of vCPUs in AFF0 is
   * limited to 16, thus only the first 4 bits of AFF0 are legal. We will
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index 56ed5fe..447d24e 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -234,8 +234,6 @@ struct vgic_ops {
      /* lookup the struct pending_irq for a given LPI interrupt */
      struct pending_irq *(*lpi_to_pending)(struct domain *d, unsigned int 
vlpi);
      int (*lpi_get_priority)(struct domain *d, uint32_t vlpi);
-    /* Maximum number of vCPU supported */
-    const unsigned int max_vcpus;
  };
  
  /* Number of ranks of interrupt registers for a domain */
@@ -350,7 +348,8 @@ extern void vgic_clear_pending_irqs(struct vcpu *v);
  
  extern bool vgic_emulate(struct cpu_user_regs *regs, union hsr hsr);
  
-unsigned int vgic_max_vcpus(const struct domain *d);
+/* Maximum vCPUs for a specific vGIC version, or 0 for unsupported. */
+unsigned int vgic_max_vcpus(unsigned int domctl_vgic_version);
  
  void vgic_v2_setup_hw(paddr_t dbase, paddr_t cbase, paddr_t csize,
                        paddr_t vbase, uint32_t aliased_offset);
diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h
index 643e69a..277f99f 100644
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -664,8 +664,6 @@ unsigned long pv_guest_cr4_to_real_cr4(const struct vcpu 
*v);
               X86_CR4_OSXSAVE | X86_CR4_SMEP |               \
               X86_CR4_FSGSBASE | X86_CR4_SMAP | X86_CR4_PCIDE))
  
-#define domain_max_vcpus(d) (is_hvm_domain(d) ? HVM_MAX_VCPUS : MAX_VIRT_CPUS)
-
  static inline struct vcpu_guest_context *alloc_vcpu_guest_context(void)
  {
      return vmalloc(sizeof(struct vcpu_guest_context));