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 */