diff mbox

[RFC,5/9] x86/HVM/SVM: Add AVIC initialization code

Message ID 1474264368-4104-6-git-send-email-suravee.suthikulpanit@amd.com (mailing list archive)
State New, archived
Headers show

Commit Message

Suravee Suthikulpanit Sept. 19, 2016, 5:52 a.m. UTC
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

Comments

Konrad Rzeszutek Wilk Oct. 12, 2016, 8:02 p.m. UTC | #1
On Mon, Sep 19, 2016 at 12:52:44AM -0500, Suravee Suthikulpanit wrote:
> 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.

Oddly enough you didn't CC the SVM maintainer: Boris Ostrovsky on all
these patches.

Adding him here.
> 
> 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
> 
> diff --git a/xen/arch/x86/hvm/svm/Makefile b/xen/arch/x86/hvm/svm/Makefile
> index 760d295..e0e4a59 100644
> --- a/xen/arch/x86/hvm/svm/Makefile
> +++ b/xen/arch/x86/hvm/svm/Makefile
> @@ -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
> diff --git a/xen/arch/x86/hvm/svm/avic.c b/xen/arch/x86/hvm/svm/avic.c
> new file mode 100644
> index 0000000..70bac69
> --- /dev/null
> +++ b/xen/arch/x86/hvm/svm/avic.c
> @@ -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>

Since this a new file could you kindly sort these? Also do you need a copyright
header at the top?

> +
> +/* 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);

Why do you want to have it disabled by default?

Also you are missing an patch to the docs/misc/xen-command-line where you would
document what this parameter does.

> +
> +static struct svm_avic_phy_ait_entry *
> +avic_get_phy_ait_entry(struct vcpu *v, int index)

Could I convience you use 'const struct vcpu *v, unsigned 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;
> +
> +    /**

This '**' is not the standard style. Could you use only one '*' please?

> +    * 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];
> +}
> +
> +/***************************************************************

Ditto
> + * 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",

How about "%d: AVIC logical .. could not be allocated" ?
> +                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);

Ditto.

You could also use gdprintk instead?

> +        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:

Add an extra space in front of the label please.

> +    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)

bool please
> +{
> +    struct arch_svm_struct *s = &v->arch.hvm_svm;
> +    struct vmcb_struct *vmcb = s->vmcb;
> +
> +    return ( svm_avic && vmcb->_vintr.fields.avic_enable);

Could you drop the () please?

> +}
> +
> +static inline u32 *
> +avic_get_bk_page_entry(struct vcpu *v, u32 offset)

const on 'struct vcpu' please?
> +{
> +    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);

Extra space not needed.
> +    return (u32*)(tmp+offset);

Perhaps:

	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",

I think you can drop the 'SVM:' part. The __func__ gives enough details.

> +           __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);

Is this also part of the keyboard handler? Perhaps that information should
be presented there?
> +
> +
> +    apic_id_reg = *avic_get_bk_page_entry(v, APIC_ID);

Um, what if it returns NULL? You would derefencing NULL. Perhaps check for that first?

> +    s->avic_phy_id_cache = avic_get_phy_ait_entry(v, apic_id_reg >> 24);
> +    if ( !s->avic_phy_id_cache )
> +        return -EINVAL;

You don't want to unroll the entries that got set earlier? avic_[bk_pg_pa,phy_apic_id,log_apic_id] ?

> +
> +    entry = *(s->avic_phy_id_cache);
> +    smp_rmb();
> +    entry.bk_pg_ptr = (vmcb->avic_bk_pg_pa >> 12) & 0xffffffffff;

Should the 0xff.. have some soft of macro?

Also the 12 looks suspicious like some other macro value.
> +    entry.is_running= 0;

Missing space.
> +    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;
> +}

Please also add:


/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */
> diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
> index 679e615..bcb7df4 100644
> --- a/xen/arch/x86/hvm/svm/svm.c
> +++ b/xen/arch/x86/hvm/svm/svm.c
> @@ -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:
> diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c
> index 9ea014f..9ee7fc7 100644
> --- a/xen/arch/x86/hvm/svm/vmcb.c
> +++ b/xen/arch/x86/hvm/svm/vmcb.c
> @@ -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;
> diff --git a/xen/include/asm-x86/hvm/svm/avic.h b/xen/include/asm-x86/hvm/svm/avic.h
> new file mode 100644
> index 0000000..9508486
> --- /dev/null
> +++ b/xen/include/asm-x86/hvm/svm/avic.h
> @@ -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_ */
> diff --git a/xen/include/asm-x86/hvm/svm/svm.h b/xen/include/asm-x86/hvm/svm/svm.h
> index c954b7e..fea61bb 100644
> --- a/xen/include/asm-x86/hvm/svm/svm.h
> +++ b/xen/include/asm-x86/hvm/svm/svm.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
>  
> diff --git a/xen/include/asm-x86/hvm/svm/vmcb.h b/xen/include/asm-x86/hvm/svm/vmcb.h
> index 768e9fb..a42c034 100644
> --- a/xen/include/asm-x86/hvm/svm/vmcb.h
> +++ b/xen/include/asm-x86/hvm/svm/vmcb.h
> @@ -496,6 +496,12 @@ struct __packed vmcb_struct {
>  };
>  
>  struct svm_domain {
> +    u32 avic_max_vcpu_id;
> +    bool_t avic_access_page_done;

bool pls
> +    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);
> -- 
> 1.9.1
> 
> 
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.xen.org
> https://lists.xen.org/xen-devel
Konrad Rzeszutek Wilk Oct. 14, 2016, 2:03 p.m. UTC | #2
. snip..

> diff --git a/xen/arch/x86/hvm/svm/avic.c b/xen/arch/x86/hvm/svm/avic.c
> new file mode 100644
> index 0000000..70bac69
> --- /dev/null
> +++ b/xen/arch/x86/hvm/svm/avic.c
> @@ -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;

static ? And also make it 'bool'

> +boolean_param("svm-avic", svm_avic);
> +
> +static struct svm_avic_phy_ait_entry *

This name resolves to 'SVM AVIC Physical APIC ID Table Entry'

That 'ait' keeps throwing me off as it sounds like 'eat' to me.

Perhaps  'avic_phy_apic_id_ent' ?

In other words s/ait/apic_id/ and s/svm_avic/avic/ followed by
s/_id_entry/id_ent/ ?


> +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).

s/here/for APIC/


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

s/log/logical/
> +    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 */

physical ?

> +    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;
> +}
> +
.. snip..
> +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;

s/ait/apic_id/ ?

> +    vmcb->avic_phy_apic_id = (ma << PAGE_SHIFT) & AVIC_HPA_MASK;
> +    vmcb->avic_phy_apic_id |= AVIC_PHY_APIC_ID_MAX;
> +

After reading the spec these tables make a bit more sense - one to
translate logical APIC -> physical APIC id, and the last one to
translate to host APIC.

It may be good to include just an simple ASCII flow of how say
IPI for a two vCPU guest would go?
Suravee Suthikulpanit Nov. 17, 2016, 4:05 p.m. UTC | #3
Konrad,

Thanks for the review comments. Got one quick question below.

On 10/12/16 15:02, Konrad Rzeszutek Wilk wrote:
>> +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",
> I think you can drop the 'SVM:' part. The __func__ gives enough details.
>
>> > +           __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);
> Is this also part of the keyboard handler? Perhaps that information should
> be presented there?

I'm not sure what you mean by keyboard handler. I assume you mean the 
spacing for the indentation the front should align with above line?

Thanks,
S.
Suravee Suthikulpanit Nov. 17, 2016, 4:55 p.m. UTC | #4
Konrad,

On 10/12/16 15:02, Konrad Rzeszutek Wilk wrote:
>> +
>> > +/* 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);
> Why do you want to have it disabled by default?

svm-avic has not yet fully supported with nested virtualization yet. So, 
I didn't want to enable this by default. However, when everything is 
stable, I am planning to enable this by default.

Thanks,
Suravee
Konrad Rzeszutek Wilk Nov. 17, 2016, 5:18 p.m. UTC | #5
On Thu, Nov 17, 2016 at 10:05:58AM -0600, Suravee Suthikulpanit wrote:
> Konrad,
> 
> Thanks for the review comments. Got one quick question below.
> 
> On 10/12/16 15:02, Konrad Rzeszutek Wilk wrote:
> > > +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",
> > I think you can drop the 'SVM:' part. The __func__ gives enough details.
> > 
> > > > +           __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);
> > Is this also part of the keyboard handler? Perhaps that information should
> > be presented there?
> 
> I'm not sure what you mean by keyboard handler. I assume you mean the
> spacing for the indentation the front should align with above line?

Well I would ditch them. And if you really want them then make the
keyboard handler, aka svm_vmcb_dump function.
> 
> Thanks,
> S.
Konrad Rzeszutek Wilk Nov. 17, 2016, 5:19 p.m. UTC | #6
On Thu, Nov 17, 2016 at 10:55:52AM -0600, Suravee Suthikulpanit wrote:
> Konrad,
> 
> On 10/12/16 15:02, Konrad Rzeszutek Wilk wrote:
> > > +
> > > > +/* 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);
> > Why do you want to have it disabled by default?
> 
> svm-avic has not yet fully supported with nested virtualization yet. So, I
> didn't want to enable this by default. However, when everything is stable, I
> am planning to enable this by default.

Ah, could you put an comment above the param to explain this?
Thanks!
Suravee Suthikulpanit Nov. 17, 2016, 6:32 p.m. UTC | #7
On 11/17/16 11:18, Konrad Rzeszutek Wilk wrote:
> On Thu, Nov 17, 2016 at 10:05:58AM -0600, Suravee Suthikulpanit wrote:
>> Konrad,
>>
>> Thanks for the review comments. Got one quick question below.
>>
>> On 10/12/16 15:02, Konrad Rzeszutek Wilk wrote:
>>>> +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",
>>> I think you can drop the 'SVM:' part. The __func__ gives enough details.
>>>
>>>>> +           __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);
>>> Is this also part of the keyboard handler? Perhaps that information should
>>> be presented there?
>>
>> I'm not sure what you mean by keyboard handler. I assume you mean the
>> spacing for the indentation the front should align with above line?
>
> Well I would ditch them. And if you really want them then make the
> keyboard handler, aka svm_vmcb_dump function.
>>
>> Thanks,
>> S.

Ahh.. the xl debug-keys stuff. Got it. No problem. I can ditch this.

S
Jan Beulich Dec. 22, 2016, 11:16 a.m. UTC | #8
>>> On 19.09.16 at 07:52, <suravee.suthikulpanit@amd.com> wrote:
> +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;

Why this copying? Can't consuming code not use vlapic->regs_page?

> +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;

Please avoid such open coded constants.

Jan
Suravee Suthikulpanit Dec. 28, 2016, 3:36 a.m. UTC | #9
On 12/22/16 18:16, Jan Beulich wrote:
>>>> On 19.09.16 at 07:52, <suravee.suthikulpanit@amd.com> wrote:
>> +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;
> Why this copying? Can't consuming code not use vlapic->regs_page?
>

Hm.. good point. I'll get rid of avic_bk_pg and just use the regs_page.

Thanks,
Suravee
diff mbox

Patch

diff --git a/xen/arch/x86/hvm/svm/Makefile b/xen/arch/x86/hvm/svm/Makefile
index 760d295..e0e4a59 100644
--- a/xen/arch/x86/hvm/svm/Makefile
+++ b/xen/arch/x86/hvm/svm/Makefile
@@ -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
diff --git a/xen/arch/x86/hvm/svm/avic.c b/xen/arch/x86/hvm/svm/avic.c
new file mode 100644
index 0000000..70bac69
--- /dev/null
+++ b/xen/arch/x86/hvm/svm/avic.c
@@ -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;
+}
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index 679e615..bcb7df4 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -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:
diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c
index 9ea014f..9ee7fc7 100644
--- a/xen/arch/x86/hvm/svm/vmcb.c
+++ b/xen/arch/x86/hvm/svm/vmcb.c
@@ -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;
diff --git a/xen/include/asm-x86/hvm/svm/avic.h b/xen/include/asm-x86/hvm/svm/avic.h
new file mode 100644
index 0000000..9508486
--- /dev/null
+++ b/xen/include/asm-x86/hvm/svm/avic.h
@@ -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_ */
diff --git a/xen/include/asm-x86/hvm/svm/svm.h b/xen/include/asm-x86/hvm/svm/svm.h
index c954b7e..fea61bb 100644
--- a/xen/include/asm-x86/hvm/svm/svm.h
+++ b/xen/include/asm-x86/hvm/svm/svm.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
 
diff --git a/xen/include/asm-x86/hvm/svm/vmcb.h b/xen/include/asm-x86/hvm/svm/vmcb.h
index 768e9fb..a42c034 100644
--- a/xen/include/asm-x86/hvm/svm/vmcb.h
+++ b/xen/include/asm-x86/hvm/svm/vmcb.h
@@ -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);