@@ -1,4 +1,5 @@
obj-y += asid.o
+obj-y += avic.o
obj-y += emulate.o
obj-bin-y += entry.o
obj-y += intr.o
new file mode 100644
@@ -0,0 +1,217 @@
+#include <xen/domain_page.h>
+#include <xen/sched.h>
+#include <xen/stdbool.h>
+#include <asm/acpi.h>
+#include <asm/apicdef.h>
+#include <asm/event.h>
+#include <asm/p2m.h>
+#include <asm/page.h>
+#include <asm/hvm/nestedhvm.h>
+#include <asm/hvm/svm/avic.h>
+#include <asm/hvm/vlapic.h>
+#include <asm/hvm/emulate.h>
+#include <asm/hvm/support.h>
+
+/* NOTE: Current max index allowed for physical APIC ID table is 255 */
+#define AVIC_PHY_APIC_ID_MAX 0xFF
+
+#define AVIC_DOORBELL 0xc001011b
+#define AVIC_HPA_MASK ~((0xFFFULL << 52) || 0xFFF)
+#define AVIC_APIC_BAR_MASK 0xFFFFFFFFFF000ULL
+
+bool_t svm_avic = 0;
+boolean_param("svm-avic", svm_avic);
+
+static struct svm_avic_phy_ait_entry *
+avic_get_phy_ait_entry(struct vcpu *v, int index)
+{
+ struct svm_avic_phy_ait_entry *avic_phy_ait;
+ struct svm_domain *d = &v->domain->arch.hvm_domain.svm;
+
+ if ( !d->avic_phy_ait_mfn )
+ return NULL;
+
+ /**
+ * Note: APIC ID = 0xff is used for broadcast.
+ * APIC ID > 0xff is reserved.
+ */
+ if ( index >= 0xff )
+ return NULL;
+
+ avic_phy_ait = mfn_to_virt(d->avic_phy_ait_mfn);
+
+ return &avic_phy_ait[index];
+}
+
+/***************************************************************
+ * AVIC APIs
+ */
+int svm_avic_dom_init(struct domain *d)
+{
+ int ret = 0;
+ struct page_info *pg;
+ unsigned long mfn;
+
+ if ( !svm_avic )
+ return 0;
+
+ /**
+ * Note:
+ * AVIC hardware walks the nested page table to check permissions,
+ * but does not use the SPA address specified in the leaf page
+ * table entry since it uses address in the AVIC_BACKING_PAGE pointer
+ * field of the VMCB. Therefore, we set up a dummy page here _mfn(0).
+ */
+ if ( !d->arch.hvm_domain.svm.avic_access_page_done )
+ {
+ set_mmio_p2m_entry(d, paddr_to_pfn(APIC_DEFAULT_PHYS_BASE),
+ _mfn(0), PAGE_ORDER_4K,
+ p2m_get_hostp2m(d)->default_access);
+ d->arch.hvm_domain.svm.avic_access_page_done = true;
+ }
+
+ /* Init AVIC log APIC ID table */
+ pg = alloc_domheap_page(d, MEMF_no_owner);
+ if ( !pg )
+ {
+ dprintk(XENLOG_ERR, "alloc AVIC logical APIC ID table error: %d\n",
+ d->domain_id);
+ ret = -ENOMEM;
+ goto err_out;
+ }
+ mfn = page_to_mfn(pg);
+ clear_domain_page(_mfn(mfn));
+ d->arch.hvm_domain.svm.avic_log_ait_mfn = mfn;
+
+ /* Init AVIC phy APIC ID table */
+ pg = alloc_domheap_page(d, MEMF_no_owner);
+ if ( !pg )
+ {
+ dprintk(XENLOG_ERR, "alloc AVIC physical APIC ID table error: %d\n",
+ d->domain_id);
+ ret = -ENOMEM;
+ goto err_out;
+ }
+ mfn = page_to_mfn(pg);
+ clear_domain_page(_mfn(mfn));
+ d->arch.hvm_domain.svm.avic_phy_ait_mfn = mfn;
+
+ return ret;
+err_out:
+ svm_avic_dom_destroy(d);
+ return ret;
+}
+
+void svm_avic_dom_destroy(struct domain *d)
+{
+ if ( !svm_avic )
+ return;
+
+ if ( d->arch.hvm_domain.svm.avic_phy_ait_mfn )
+ {
+ free_domheap_page(mfn_to_page(d->arch.hvm_domain.svm.avic_phy_ait_mfn));
+ d->arch.hvm_domain.svm.avic_phy_ait_mfn = 0;
+ }
+
+ if ( d->arch.hvm_domain.svm.avic_log_ait_mfn )
+ {
+ free_domheap_page(mfn_to_page(d->arch.hvm_domain.svm.avic_log_ait_mfn));
+ d->arch.hvm_domain.svm.avic_log_ait_mfn = 0;
+ }
+}
+
+/**
+ * Note: At this point, vlapic->regs_page is already initialized.
+ */
+int svm_avic_init_vcpu(struct vcpu *v)
+{
+ struct vlapic *vlapic = vcpu_vlapic(v);
+ struct arch_svm_struct *s = &v->arch.hvm_svm;
+
+ if ( svm_avic )
+ s->avic_bk_pg = vlapic->regs_page;
+ return 0;
+}
+
+void svm_avic_destroy_vcpu(struct vcpu *v)
+{
+ struct arch_svm_struct *s = &v->arch.hvm_svm;
+
+ if ( svm_avic && s->avic_bk_pg )
+ s->avic_bk_pg = NULL;
+}
+
+bool_t svm_avic_vcpu_enabled(struct vcpu *v)
+{
+ struct arch_svm_struct *s = &v->arch.hvm_svm;
+ struct vmcb_struct *vmcb = s->vmcb;
+
+ return ( svm_avic && vmcb->_vintr.fields.avic_enable);
+}
+
+static inline u32 *
+avic_get_bk_page_entry(struct vcpu *v, u32 offset)
+{
+ struct arch_svm_struct *s = &v->arch.hvm_svm;
+ struct page_info *pg = s->avic_bk_pg;
+ char *tmp;
+
+ if ( !pg )
+ return NULL;
+
+ tmp = (char *) page_to_virt(pg);
+ return (u32*)(tmp+offset);
+}
+
+void svm_avic_update_vapic_bar(struct vcpu *v, uint64_t data)
+{
+ struct arch_svm_struct *s = &v->arch.hvm_svm;
+
+ s->vmcb->avic_vapic_bar = data & AVIC_APIC_BAR_MASK;
+ s->vmcb->cleanbits.fields.avic = 0;
+}
+
+int svm_avic_init_vmcb(struct vcpu *v)
+{
+ paddr_t ma;
+ u32 apic_id_reg;
+ struct arch_svm_struct *s = &v->arch.hvm_svm;
+ struct vmcb_struct *vmcb = s->vmcb;
+ struct svm_domain *d = &v->domain->arch.hvm_domain.svm;
+ struct svm_avic_phy_ait_entry entry;
+
+ if ( !svm_avic )
+ return 0;
+
+ vmcb->avic_bk_pg_pa = page_to_maddr(s->avic_bk_pg) & AVIC_HPA_MASK;
+ ma = d->avic_log_ait_mfn;
+ vmcb->avic_log_apic_id = (ma << PAGE_SHIFT) & AVIC_HPA_MASK;
+ ma = d->avic_phy_ait_mfn;
+ vmcb->avic_phy_apic_id = (ma << PAGE_SHIFT) & AVIC_HPA_MASK;
+ vmcb->avic_phy_apic_id |= AVIC_PHY_APIC_ID_MAX;
+
+ dprintk(XENLOG_DEBUG, "SVM: %s: bpa=%#llx, lpa=%#llx, ppa=%#llx\n",
+ __func__, (unsigned long long)vmcb->avic_bk_pg_pa,
+ (unsigned long long) vmcb->avic_log_apic_id,
+ (unsigned long long) vmcb->avic_phy_apic_id);
+
+
+ apic_id_reg = *avic_get_bk_page_entry(v, APIC_ID);
+ s->avic_phy_id_cache = avic_get_phy_ait_entry(v, apic_id_reg >> 24);
+ if ( !s->avic_phy_id_cache )
+ return -EINVAL;
+
+ entry = *(s->avic_phy_id_cache);
+ smp_rmb();
+ entry.bk_pg_ptr = (vmcb->avic_bk_pg_pa >> 12) & 0xffffffffff;
+ entry.is_running= 0;
+ entry.valid = 1;
+ *(s->avic_phy_id_cache) = entry;
+ smp_wmb();
+
+ svm_avic_update_vapic_bar(v, APIC_DEFAULT_PHYS_BASE);
+
+ vmcb->_vintr.fields.avic_enable = 1;
+
+ return 0;
+}
@@ -48,6 +48,7 @@
#include <asm/hvm/svm/asid.h>
#include <asm/hvm/svm/svm.h>
#include <asm/hvm/svm/vmcb.h>
+#include <asm/hvm/svm/avic.h>
#include <asm/hvm/svm/emulate.h>
#include <asm/hvm/svm/intr.h>
#include <asm/hvm/svm/svmdebug.h>
@@ -1164,11 +1165,12 @@ void svm_host_osvw_init()
static int svm_domain_initialise(struct domain *d)
{
- return 0;
+ return svm_avic_dom_init(d);
}
static void svm_domain_destroy(struct domain *d)
{
+ svm_avic_dom_destroy(d);
}
static int svm_vcpu_initialise(struct vcpu *v)
@@ -1181,6 +1183,13 @@ static int svm_vcpu_initialise(struct vcpu *v)
v->arch.hvm_svm.launch_core = -1;
+ if ( (rc = svm_avic_init_vcpu(v)) != 0 )
+ {
+ dprintk(XENLOG_WARNING,
+ "Failed to initiazlize AVIC vcpu.\n");
+ return rc;
+ }
+
if ( (rc = svm_create_vmcb(v)) != 0 )
{
dprintk(XENLOG_WARNING,
@@ -1200,6 +1209,7 @@ static int svm_vcpu_initialise(struct vcpu *v)
static void svm_vcpu_destroy(struct vcpu *v)
{
+ svm_avic_destroy_vcpu(v);
vpmu_destroy(v);
svm_destroy_vmcb(v);
passive_domain_destroy(v);
@@ -1464,6 +1474,7 @@ const struct hvm_function_table * __init start_svm(void)
P(cpu_has_svm_decode, "DecodeAssists");
P(cpu_has_pause_filter, "Pause-Intercept Filter");
P(cpu_has_tsc_ratio, "TSC Rate MSR");
+ P(cpu_has_svm_avic, "AVIC");
#undef P
if ( !printed )
@@ -1809,6 +1820,10 @@ static int svm_msr_write_intercept(unsigned int msr, uint64_t msr_content)
switch ( msr )
{
+ case MSR_IA32_APICBASE:
+ if ( svm_avic_vcpu_enabled(v) )
+ svm_avic_update_vapic_bar(v, msr_content);
+ break;
case MSR_IA32_SYSENTER_CS:
case MSR_IA32_SYSENTER_ESP:
case MSR_IA32_SYSENTER_EIP:
@@ -28,6 +28,7 @@
#include <asm/msr-index.h>
#include <asm/p2m.h>
#include <asm/hvm/support.h>
+#include <asm/hvm/svm/avic.h>
#include <asm/hvm/svm/svm.h>
#include <asm/hvm/svm/svmdebug.h>
@@ -225,6 +226,8 @@ static int construct_vmcb(struct vcpu *v)
vmcb->_general1_intercepts |= GENERAL1_INTERCEPT_PAUSE;
}
+ svm_avic_init_vmcb(v);
+
vmcb->cleanbits.bytes = 0;
return 0;
new file mode 100644
@@ -0,0 +1,40 @@
+#ifndef _SVM_AVIC_H_
+#define _SVM_AVIC_H_
+
+enum avic_incmp_ipi_err_code {
+ AVIC_INCMP_IPI_ERR_INVALID_INT_TYPE,
+ AVIC_INCMP_IPI_ERR_TARGET_NOT_RUN,
+ AVIC_INCMP_IPI_ERR_INV_TARGET,
+ AVIC_INCMP_IPI_ERR_INV_BK_PAGE,
+};
+
+struct __attribute__ ((__packed__))
+svm_avic_log_ait_entry {
+ u32 guest_phy_apic_id : 8;
+ u32 res : 23;
+ u32 valid : 1;
+};
+
+struct __attribute__ ((__packed__))
+svm_avic_phy_ait_entry {
+ u64 host_phy_apic_id : 8;
+ u64 res1 : 4;
+ u64 bk_pg_ptr : 40;
+ u64 res2 : 10;
+ u64 is_running : 1;
+ u64 valid : 1;
+};
+
+extern bool_t svm_avic;
+
+int svm_avic_dom_init(struct domain *d);
+void svm_avic_dom_destroy(struct domain *d);
+
+int svm_avic_init_vcpu(struct vcpu *v);
+void svm_avic_destroy_vcpu(struct vcpu *v);
+bool_t svm_avic_vcpu_enabled(struct vcpu *v);
+
+void svm_avic_update_vapic_bar(struct vcpu *v,uint64_t data);
+int svm_avic_init_vmcb(struct vcpu *v);
+
+#endif /* _SVM_AVIC_H_ */
@@ -81,6 +81,7 @@ extern u32 svm_feature_flags;
#define SVM_FEATURE_FLUSHBYASID 6 /* TLB flush by ASID support */
#define SVM_FEATURE_DECODEASSISTS 7 /* Decode assists support */
#define SVM_FEATURE_PAUSEFILTER 10 /* Pause intercept filter support */
+#define SVM_FEATURE_AVIC 13 /* AVIC support */
#define cpu_has_svm_feature(f) test_bit(f, &svm_feature_flags)
#define cpu_has_svm_npt cpu_has_svm_feature(SVM_FEATURE_NPT)
@@ -91,6 +92,7 @@ extern u32 svm_feature_flags;
#define cpu_has_svm_decode cpu_has_svm_feature(SVM_FEATURE_DECODEASSISTS)
#define cpu_has_pause_filter cpu_has_svm_feature(SVM_FEATURE_PAUSEFILTER)
#define cpu_has_tsc_ratio cpu_has_svm_feature(SVM_FEATURE_TSCRATEMSR)
+#define cpu_has_svm_avic cpu_has_svm_feature(SVM_FEATURE_AVIC)
#define SVM_PAUSEFILTER_INIT 3000
@@ -496,6 +496,12 @@ struct __packed vmcb_struct {
};
struct svm_domain {
+ u32 avic_max_vcpu_id;
+ bool_t avic_access_page_done;
+ paddr_t avic_log_ait_mfn;
+ paddr_t avic_phy_ait_mfn;
+ u32 ldr_reg;
+ u32 ldr_mode;
};
struct arch_svm_struct {
@@ -529,6 +535,10 @@ struct arch_svm_struct {
u64 length;
u64 status;
} osvw;
+
+ /* AVIC APIC backing page */
+ struct page_info *avic_bk_pg;
+ struct svm_avic_phy_ait_entry *avic_phy_id_cache;
};
struct vmcb_struct *alloc_vmcb(void);
Introduce AVIC base initialization code. This includes: * Setting up per-VM data structures. * Setting up per-vCPU data structure. * Initializing AVIC-related VMCB bit fields. This patch also introduces a new Xen parameter (svm-avic), which can be used to enable/disable AVIC support. Currently, this svm-avic is disabled by default. Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> --- xen/arch/x86/hvm/svm/Makefile | 1 + xen/arch/x86/hvm/svm/avic.c | 217 +++++++++++++++++++++++++++++++++++++ xen/arch/x86/hvm/svm/svm.c | 17 ++- xen/arch/x86/hvm/svm/vmcb.c | 3 + xen/include/asm-x86/hvm/svm/avic.h | 40 +++++++ xen/include/asm-x86/hvm/svm/svm.h | 2 + xen/include/asm-x86/hvm/svm/vmcb.h | 10 ++ 7 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 xen/arch/x86/hvm/svm/avic.c create mode 100644 xen/include/asm-x86/hvm/svm/avic.h