[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v1 07/28] x86/hvm: avic: add VMCB state initialization


  • To: dmukhin@xxxxxxxx, xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>
  • Date: Fri, 6 Nov 2026 15:00:47 +0000
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=citrix.com; dmarc=pass action=none header.from=citrix.com; dkim=pass header.d=citrix.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=2ujMVwmoPIMqHU3izph4uPFOL+L+KDgKZi9rxOQqbFA=; b=au1OAs2WM3e/FPrNJkrEmZa/NgnBZyGNK2333kZqSi/i92HBHKzM6FIyIpOmHlSLgws/SLRe43dO2XfIIa4AT1qmmPKBWm60pTGXgkmCRSLZKbpPFzt0x2b2BKyAhdvNz1mzWApHk+df+apUI5uu7XuNeQWhawbA1B85u1oCXoaOyvLBtId6b5Ighh/aUeN4SiPL+ke5QEq7+UUxVOu0jTv3avoiHIjN0E9c+ukdwEqGu8Lrczx2I+z1IKCJ1C+0A0FZKF+OvnRIwHvOJYWemFLIczbyT7K/h2Vz7c8oz4QRJLIxaaGKx1YRj3qS1BN6VHyFvKZQ2BQz7jXcUxvnvQ==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=QPqtu4x9L7rWaGCl+VeldGC/cncUKAjEkiGewqP65Hsy39eyj66hLlXmVTDTYRlcKzioEhSbO4o8v33xV4FKWMD3lSXwfbrZueO02X8KxwaS8vcJraj+9SinvafJCxTGCEfmYspsDDVPNtwVH5BUubwPgZD5CNOqDHNNQizBcGyUjkscTB9xLaT0e2lTTIFWM6s2UX7Z7TH+WTg8QMxZJebsYYNY1Sy4C8vJskuCImByHEkouVMsgx9bPBBIzef1Yh8vMx9GDw1owplX0LH83eeQf5IloF6edp/wkX93gBD72tUwkbToo4UCegNk6LP1wtXFoSPHxvjmfrnfKItr7w==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=citrix.com header.i="@citrix.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Cc: andrew.cooper3@xxxxxxxxxx, jason.andryuk@xxxxxxx, jbeulich@xxxxxxxx, roger@xxxxxxxxxxxxxx, teddy.astie@xxxxxxxxxx, dmukhin@xxxxxxx
  • Delivery-date: Fri, 06 Nov 2026 15:00:59 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

On 11/5/26 1:33 AM, dmukhin@xxxxxxxx wrote:
From: Denis Mukhin <dmukhin@xxxxxxxx>

Update SVM VMCB with AVIC-related data types as per [1].

Install AVIC backing page and logical/physical APIC ID tables mappings
into VMCB.

Set VMCB 'avic_enable' bit to 1 in case of successful initialization.

[1] AMD APM Volumes 1-5 "15.29 Advanced Virtual Interrupt Controller".
Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx>
---
  xen/arch/x86/hvm/svm/avic.c | 110 ++++++++++++++++++++++++++++++++++++
  xen/arch/x86/hvm/svm/avic.h |   3 +
  xen/arch/x86/hvm/svm/vmcb.c |   7 +++
  xen/arch/x86/hvm/svm/vmcb.h |  19 +++++--
  4 files changed, 135 insertions(+), 4 deletions(-)

diff --git a/xen/arch/x86/hvm/svm/avic.c b/xen/arch/x86/hvm/svm/avic.c
index 2c0e113ae34e..bc98661a7057 100644
--- a/xen/arch/x86/hvm/svm/avic.c
+++ b/xen/arch/x86/hvm/svm/avic.c
@@ -9,6 +9,16 @@
  #include <xen/param.h>
#include "avic.h"
+#include "vmcb.h"
+
+#define AVIC_PHYSICAL_ID_ENTRY_BACKING_PAGE_MASK        GENMASK_ULL(51, 12U)
+#define AVIC_PHYSICAL_ID_TABLE_SIZE_MASK                (0xFFULL)
+
+/*
+ * For AVIC, the max index allowed for physical APIC ID table is 0xfe (254), as
+ * 0xff is a broadcast to all CPUs, i.e. can't be targeted individually.
+ */
+#define AVIC_MAX_PHYSICAL_ID                            (0xFEU)
bool __read_mostly svm_avic_enable; @@ -125,6 +135,106 @@ void svm_avic_domain_relinquish_resources(struct domain *d)
      }
  }
+static bool avic_vcpu_supported(struct vcpu *v)
+{
+    struct domain *d = v->domain;
+    unsigned int id;
+
+    if ( !svm_avic_enable || !has_vlapic(d) )
+        return false;
+
+    if ( d->max_vcpus * 2 >= AVIC_PHYSICAL_ID_TABLE_SIZE_MASK )
+    {
+        gprintk(XENLOG_WARNING,
+                "%pv: AVIC: unsupported domain max_vcpus %u\n",
+                v, d->max_vcpus);
+        return false;
+    }
+
+    id = GET_xAPIC_ID(vlapic_reg_read(v, APIC_ID));
+    if ( id > AVIC_MAX_PHYSICAL_ID )
+    {
+        gprintk(XENLOG_WARNING,
+                "%pv: AVIC: unsupported APIC ID %u\n",
+                v, id);
+        return false;
+    }
+
+    return true;
+}
+
+static void avic_vcpu_enable(const struct vcpu *v, bool enable)
+{
+    const struct svm_vcpu *sv = &v->arch.hvm.svm;
+    struct vmcb_struct *vmcb = sv->vmcb;
+
+    vmcb->_vintr.fields.x2avic_enable = 0;
+    vmcb->_vintr.fields.avic_enable = enable;
+    vmcb->cleanbits.avic = 0;
+
+    printk(XENLOG_INFO
+           "%pv: AVIC: %s: apic_id=%#x avic_physical_id=%#"PRIx64" 
max_vcpus=%u\n",
+           v,
+           enable ? "enable" : "disable",
+           GET_xAPIC_ID(vlapic_reg_read(v, APIC_ID)),
+           sv->avic_physical_id ? ACCESS_ONCE(*sv->avic_physical_id) :
+                                  0xDEADDEADUL,
+           v->domain->max_vcpus);
+}
+
+int svm_avic_vmcb_create(struct vcpu *v)
+{
+    struct domain *d = v->domain;
+    struct svm_domain *sd = &d->arch.hvm.svm;
+    struct svm_vcpu *sv = &v->arch.hvm.svm;
+    struct vmcb_struct *vmcb = sv->vmcb;
+    const struct vlapic *vlapic = vcpu_vlapic(v);
+    mfn_t mfn;
+    uint64_t entry;
+    unsigned int id;
+
+    if ( !avic_vcpu_supported(v) )
+        return 0;
+
+    vmcb->avic_vapic_bar = APIC_DEFAULT_PHYS_BASE;
+    vmcb->avic_backing_page = page_to_maddr(vlapic->regs_page);
+
+    mfn = domain_page_map_to_mfn(sd->avic_logical_id_table);
+    vmcb->avic_logical_id_table = mfn_to_maddr(mfn);
+
+    mfn = domain_page_map_to_mfn(sd->avic_physical_id_table);
+    vmcb->avic_physical_id_table = mfn_to_maddr(mfn);
+    vmcb->avic_physical_id_table |= (d->max_vcpus * 2) &
+                                    AVIC_PHYSICAL_ID_TABLE_SIZE_MASK;
+
+    id = GET_xAPIC_ID(vlapic_reg_read(v, APIC_ID));
+
+    sv->avic_physical_id = &sd->avic_physical_id_table[id];
+
+    entry = vmcb->avic_backing_page & AVIC_PHYSICAL_ID_ENTRY_BACKING_PAGE_MASK;
+    entry |= AVIC_PHYSICAL_ID_ENTRY_VALID_MASK;
+
+    ACCESS_ONCE(*sv->avic_physical_id) = entry;
+
+    avic_vcpu_enable(v, true);
+
+    return 0;
+}
+
+void svm_avic_vmcb_destroy(struct vcpu *v)
+{
+    struct svm_vcpu *sv = &v->arch.hvm.svm;
+    struct vmcb_struct *vmcb = sv->vmcb;
+
+    if ( svm_avic_vcpu_enabled(v) )
+        avic_vcpu_enable(v, false);
+
+    vmcb->avic_physical_id_table = 0;
+    vmcb->avic_logical_id_table = 0;
+    vmcb->avic_backing_page = 0;
+    vmcb->avic_vapic_bar = 0;
+}
+
  /*
   * Local variables:
   * mode: C
diff --git a/xen/arch/x86/hvm/svm/avic.h b/xen/arch/x86/hvm/svm/avic.h
index 3da62f59b0cf..1084ae683944 100644
--- a/xen/arch/x86/hvm/svm/avic.h
+++ b/xen/arch/x86/hvm/svm/avic.h
@@ -11,4 +11,7 @@ extern bool svm_avic_enable;
  int svm_avic_domain_initialise(struct domain *d);
  void svm_avic_domain_relinquish_resources(struct domain *d);
+int svm_avic_vmcb_create(struct vcpu *v);
+void svm_avic_vmcb_destroy(struct vcpu *v);
+
  #endif /* __SVM_AVIC_H__ */
diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c
index 3ec5bdddf588..c4282bfd6c6e 100644
--- a/xen/arch/x86/hvm/svm/vmcb.c
+++ b/xen/arch/x86/hvm/svm/vmcb.c
@@ -20,6 +20,7 @@
  #include <asm/p2m.h>
  #include <asm/spec_ctrl.h>
+#include "avic.h"
  #include "svm.h"
  #include "vmcb.h"
@@ -215,6 +216,10 @@ int svm_create_vmcb(struct vcpu *v)
      if ( rc )
          return rc;
+ rc = svm_avic_vmcb_create(v);
+    if ( rc )
+        return rc;
+
      paddr = virt_to_maddr(nv->nv_n1vmcx);
      nv->nv_n1vmcx_pa = paddr;
      svm->vmcb_pa = paddr;
@@ -227,6 +232,8 @@ void svm_destroy_vmcb(struct vcpu *v)
      struct nestedvcpu *nv = &vcpu_nestedhvm(v);
      struct svm_vcpu *svm = &v->arch.hvm.svm;
+ svm_avic_vmcb_destroy(v);
+
      if ( nv->nv_n1vmcx != NULL )
          free_vmcb(nv->nv_n1vmcx);
diff --git a/xen/arch/x86/hvm/svm/vmcb.h b/xen/arch/x86/hvm/svm/vmcb.h
index 8b351679fac5..1ee5da053a9a 100644
--- a/xen/arch/x86/hvm/svm/vmcb.h
+++ b/xen/arch/x86/hvm/svm/vmcb.h
@@ -346,7 +346,9 @@ typedef union
          u64 intr_masking: 1;
          u64 vgif_enable:  1;
          u64 vnmi_enable:  1;
-        u64 :             5;
+        u64 rsvd2:        3;
+        u64 x2avic_enable:1;
+        u64 avic_enable:  1;  /* bit 31 */
          u64 vector:       8;
          u64 rsvd3:       24;
      } fields;
@@ -362,6 +364,10 @@ typedef union
      uint64_t raw;
  } virt_ext_t;
+/*
+ * AMD64 Achitecture's Programmer's Manual, Volume 2
+ * 15.15.3 VMCB Clean Field
+ */
  typedef union
  {
      struct {
@@ -377,7 +383,7 @@ typedef union
          bool seg:1;        /* 8:  cs, ds, es, ss, cpl */
          bool cr2:1;        /* 9:  cr2 */
          bool lbr:1;        /* 10: debugctlmsr, last{branch,int}{to,from}ip */
-        bool :1;
+        bool avic:1;       /* 11: AVIC */
          bool cet:1;        /* 12: msr_s_set, ssp, msr_isst */
      };
      uint32_t raw;
@@ -469,7 +475,8 @@ struct vmcb_struct {
          };
          uint64_t _np_ctrl;
      };
-    uint64_t res08[2];
+    uint64_t avic_vapic_bar;    /* offset 0x98 */
+    uint64_t res08;             /* offset 0xA0 */
      intinfo_t event_inj;        /* offset 0xA8 */
      uint64_t _h_cr3;            /* offset 0xB0 - cleanbit 4 */
      virt_ext_t virt_ext;        /* offset 0xB8 */
@@ -478,7 +485,11 @@ struct vmcb_struct {
      uint64_t nextrip;           /* offset 0xC8 */
      uint8_t  guest_ins_len;     /* offset 0xD0 */
      uint8_t  guest_ins[15];     /* offset 0xD1 */
-    uint64_t res10a[8];         /* offset 0xE0 */
+    uint64_t avic_backing_page;      /* offset 0xE0 */
+    uint64_t res09a;                 /* offset 0xE8 */
+    uint64_t avic_logical_id_table;  /* offset 0xF0 */
+    uint64_t avic_physical_id_table; /* offset 0xF8 */
+    uint64_t res10a[4];         /* offset 0x100 pad to save area */
      uint16_t bus_lock_count;    /* offset 0x120 */
      uint16_t res10b[3];         /* offset 0x122 */
      uint64_t res10c[91];        /* offset 0x128 pad to save area */

The avic cleanbit is defined above but then you don't actually make use
of it.  The fields to which the cleanbit applies should be marked and
have accessors defined.

In principle, it shouldn't make a difference since AFAICT these fields
are only written during VMCB creation when all the cleanbits are cleared
anyway but it makes sense for consistency.

Ross



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.