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

Re: [Xen-devel] [PATCH v5 2/4] x86/HVM: fix ID handling of x2APIC emulation



On 24/09/14 16:35, Jan Beulich wrote:
> - properly change ID when switching into x2APIC mode (instead of
>   mimicking necessary behavior in hvm_x2apic_msr_read())
> - correctly (meaningfully) set LDR (so far it ended up being 1 on all
>   vCPU-s)
> - even if we don't support more than 128 vCPU-s in a HVM guest for now,
>   we should properly handle IDs as 32-bit values (i.e. not ignore the
>   top 24 bits)
> - with that, properly do cluster ID and bit mask check in
>   vlapic_match_logical_addr()
>
> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>

Much clearer with some of the changes moved to patch 3.

Reviewed-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>

> ---
> v5: Convert optional checks done in lapic_load_fixup() to trigger a
>     printk() instead of preventing the fixup. Move unrelated type
>     changes to 3rd patch.
> v4: Replace unpause-based approach with one latching most recently
>     loaded values.
> v2: Some changes broken out to separate patch. Correct ID and
>     LDR after domain restore (if necessary); as stated previously the
>     only compatibility problem this creates is when migrating a VM _to_
>     an unfixed (i.e. old) hypervisor, a scenario which supposedly isn't
>     supported.
>
> --- a/xen/arch/x86/hvm/vlapic.c
> +++ b/xen/arch/x86/hvm/vlapic.c
> @@ -173,18 +173,17 @@ uint32_t vlapic_set_ppr(struct vlapic *v
>     return ppr;
>  }
>  
> -static int vlapic_match_logical_addr(struct vlapic *vlapic, uint8_t mda)
> +static int vlapic_match_logical_addr(struct vlapic *vlapic, uint32_t mda)
>  {
>      int result = 0;
> -    uint32_t logical_id;
> +    uint32_t logical_id = vlapic_get_reg(vlapic, APIC_LDR);
>  
>      if ( vlapic_x2apic_mode(vlapic) )
> -    {
> -        logical_id = vlapic_get_reg(vlapic, APIC_LDR);
> -        return !!(logical_id & mda);
> -    }
> +        return ((logical_id >> 16) == (mda >> 16)) &&
> +               (uint16_t)(logical_id & mda);
>  
> -    logical_id = GET_xAPIC_LOGICAL_ID(vlapic_get_reg(vlapic, APIC_LDR));
> +    logical_id = GET_xAPIC_LOGICAL_ID(logical_id);
> +    mda = (uint8_t)mda;
>  
>      switch ( vlapic_get_reg(vlapic, APIC_DFR) )
>      {
> @@ -208,7 +207,7 @@ static int vlapic_match_logical_addr(str
>  
>  bool_t vlapic_match_dest(
>      struct vlapic *target, struct vlapic *source,
> -    int short_hand, uint8_t dest, uint8_t dest_mode)
> +    int short_hand, uint32_t dest, uint8_t dest_mode)
>  {
>      HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "target %p, source %p, dest %#x, "
>                  "dest_mode %#x, short_hand %#x",
> @@ -219,7 +218,8 @@ bool_t vlapic_match_dest(
>      case APIC_DEST_NOSHORT:
>          if ( dest_mode )
>              return vlapic_match_logical_addr(target, dest);
> -        return ((dest == 0xFF) || (dest == VLAPIC_ID(target)));
> +        return (dest == _VLAPIC_ID(target, 0xffffffff)) ||
> +               (dest == VLAPIC_ID(target));
>  
>      case APIC_DEST_SELF:
>          return (target == source);
> @@ -353,7 +353,7 @@ static void vlapic_accept_irq(struct vcp
>  
>  struct vlapic *vlapic_lowest_prio(
>      struct domain *d, struct vlapic *source,
> -    int short_hand, uint8_t dest, uint8_t dest_mode)
> +    int short_hand, uint32_t dest, uint8_t dest_mode)
>  {
>      int old = d->arch.hvm_domain.irq.round_robin_prev_vcpu;
>      uint32_t ppr, target_ppr = UINT_MAX;
> @@ -438,9 +438,7 @@ void vlapic_ipi(
>  
>      HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "icr = 0x%08x:%08x", icr_high, icr_low);
>  
> -    dest = (vlapic_x2apic_mode(vlapic)
> -            ? icr_high
> -            : GET_xAPIC_DEST_FIELD(icr_high));
> +    dest = _VLAPIC_ID(vlapic, icr_high);
>  
>      switch ( icr_low & APIC_MODE_MASK )
>      {
> @@ -619,10 +617,6 @@ int hvm_x2apic_msr_read(struct vcpu *v, 
>      vlapic_read_aligned(vlapic, offset, &low);
>      switch ( offset )
>      {
> -    case APIC_ID:
> -        low = GET_xAPIC_ID(low);
> -        break;
> -
>      case APIC_ICR:
>          vlapic_read_aligned(vlapic, APIC_ICR2, &high);
>          break;
> @@ -656,6 +650,8 @@ static int vlapic_reg_write(struct vcpu 
>      struct vlapic *vlapic = vcpu_vlapic(v);
>      int rc = X86EMUL_OKAY;
>  
> +    memset(&vlapic->loaded, 0, sizeof(vlapic->loaded));
> +
>      switch ( offset )
>      {
>      case APIC_ID:
> @@ -960,6 +956,15 @@ const struct hvm_mmio_handler vlapic_mmi
>      .write_handler = vlapic_write
>  };
>  
> +static void set_x2apic_id(struct vlapic *vlapic)
> +{
> +    u32 id = vlapic_vcpu(vlapic)->vcpu_id;
> +    u32 ldr = ((id & ~0xf) << 12) | (1 << (id & 0xf));
> +
> +    vlapic_set_reg(vlapic, APIC_ID, id * 2);
> +    vlapic_set_reg(vlapic, APIC_LDR, ldr);
> +}
> +
>  bool_t vlapic_msr_set(struct vlapic *vlapic, uint64_t value)
>  {
>      if ( (vlapic->hw.apic_base_msr ^ value) & MSR_IA32_APICBASE_ENABLE )
> @@ -985,13 +990,10 @@ bool_t vlapic_msr_set(struct vlapic *vla
>          return 0;
>  
>      vlapic->hw.apic_base_msr = value;
> +    memset(&vlapic->loaded, 0, sizeof(vlapic->loaded));
>  
>      if ( vlapic_x2apic_mode(vlapic) )
> -    {
> -        u32 id = vlapic_get_reg(vlapic, APIC_ID);
> -        u32 ldr = ((id & ~0xf) << 16) | (1 << (id & 0xf));
> -        vlapic_set_reg(vlapic, APIC_LDR, ldr);
> -    }
> +        set_x2apic_id(vlapic);
>  
>      vmx_vlapic_msr_changed(vlapic_vcpu(vlapic));
>  
> @@ -1263,6 +1265,35 @@ static int lapic_save_regs(struct domain
>      return rc;
>  }
>  
> +/*
> + * Following lapic_load_hidden()/lapic_load_regs() we may need to
> + * correct ID and LDR when they come from an old, broken hypervisor.
> + */
> +static void lapic_load_fixup(struct vlapic *vlapic)
> +{
> +    uint32_t id = vlapic->loaded.id;
> +
> +    if ( vlapic_x2apic_mode(vlapic) && id && vlapic->loaded.ldr == 1 )
> +    {
> +        /*
> +         * This is optional: ID != 0 contradicts LDR == 1. It's being added
> +         * to aid in eventual debugging of issues arising from the fixup done
> +         * here, but can be dropped as soon as it is found to conflict with
> +         * other (future) changes.
> +         */
> +        if ( GET_xAPIC_ID(id) != vlapic_vcpu(vlapic)->vcpu_id * 2 ||
> +             id != SET_xAPIC_ID(GET_xAPIC_ID(id)) )
> +            printk(XENLOG_G_WARNING "%pv: bogus APIC ID %#x loaded\n",
> +                   vlapic_vcpu(vlapic), id);
> +        set_x2apic_id(vlapic);
> +    }
> +    else /* Undo an eventual earlier fixup. */
> +    {
> +        vlapic_set_reg(vlapic, APIC_ID, id);
> +        vlapic_set_reg(vlapic, APIC_LDR, vlapic->loaded.ldr);
> +    }
> +}
> +
>  static int lapic_load_hidden(struct domain *d, hvm_domain_context_t *h)
>  {
>      uint16_t vcpuid;
> @@ -1282,6 +1313,10 @@ static int lapic_load_hidden(struct doma
>      if ( hvm_load_entry_zeroextend(LAPIC, h, &s->hw) != 0 ) 
>          return -EINVAL;
>  
> +    s->loaded.hw = 1;
> +    if ( s->loaded.regs )
> +        lapic_load_fixup(s);
> +
>      if ( !(s->hw.apic_base_msr & MSR_IA32_APICBASE_ENABLE) &&
>           unlikely(vlapic_x2apic_mode(s)) )
>          return -EINVAL;
> @@ -1310,6 +1345,12 @@ static int lapic_load_regs(struct domain
>      if ( hvm_load_entry(LAPIC_REGS, h, s->regs) != 0 ) 
>          return -EINVAL;
>  
> +    s->loaded.id = vlapic_get_reg(s, APIC_ID);
> +    s->loaded.ldr = vlapic_get_reg(s, APIC_LDR);
> +    s->loaded.regs = 1;
> +    if ( s->loaded.hw )
> +        lapic_load_fixup(s);
> +
>      if ( hvm_funcs.process_isr )
>          hvm_funcs.process_isr(vlapic_find_highest_isr(s), v);
>  
> --- a/xen/include/asm-x86/hvm/vlapic.h
> +++ b/xen/include/asm-x86/hvm/vlapic.h
> @@ -30,8 +30,9 @@
>  #define vlapic_vcpu(x)   (container_of((x), struct vcpu, 
> arch.hvm_vcpu.vlapic))
>  #define vlapic_domain(x) (vlapic_vcpu(x)->domain)
>  
> -#define VLAPIC_ID(vlapic)   \
> -    (GET_xAPIC_ID(vlapic_get_reg((vlapic), APIC_ID)))
> +#define _VLAPIC_ID(vlapic, id) (vlapic_x2apic_mode(vlapic) \
> +                                ? (id) : GET_xAPIC_ID(id))
> +#define VLAPIC_ID(vlapic) _VLAPIC_ID(vlapic, vlapic_get_reg(vlapic, APIC_ID))
>  
>  /*
>   * APIC can be disabled in two ways:
> @@ -70,6 +71,10 @@
>  struct vlapic {
>      struct hvm_hw_lapic      hw;
>      struct hvm_hw_lapic_regs *regs;
> +    struct {
> +        bool_t               hw, regs;
> +        uint32_t             id, ldr;
> +    }                        loaded;
>      struct periodic_time     pt;
>      s_time_t                 timer_last_update;
>      struct page_info         *regs_page;
> @@ -124,10 +129,10 @@ int vlapic_apicv_write(struct vcpu *v, u
>  
>  struct vlapic *vlapic_lowest_prio(
>      struct domain *d, struct vlapic *source,
> -    int short_hand, uint8_t dest, uint8_t dest_mode);
> +    int short_hand, uint32_t dest, uint8_t dest_mode);
>  
>  bool_t vlapic_match_dest(
>      struct vlapic *target, struct vlapic *source,
> -    int short_hand, uint8_t dest, uint8_t dest_mode);
> +    int short_hand, uint32_t dest, uint8_t dest_mode);
>  
>  #endif /* __ASM_X86_HVM_VLAPIC_H__ */
>
>


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

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