diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/i386/kvm/apic.c | 3 | ||||
-rw-r--r-- | hw/i386/xen/xen_apic.c | 3 | ||||
-rw-r--r-- | hw/intc/apic.c | 62 | ||||
-rw-r--r-- | hw/intc/apic_common.c | 13 |
4 files changed, 71 insertions, 10 deletions
diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index 1e89ca0..a72c28e 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -95,9 +95,10 @@ void kvm_get_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic) apic_next_timer(s, s->initial_count_load_time); } -static void kvm_apic_set_base(APICCommonState *s, uint64_t val) +static int kvm_apic_set_base(APICCommonState *s, uint64_t val) { s->apicbase = val; + return 0; } static void kvm_apic_set_tpr(APICCommonState *s, uint8_t val) diff --git a/hw/i386/xen/xen_apic.c b/hw/i386/xen/xen_apic.c index 7c7a60b..101e16a 100644 --- a/hw/i386/xen/xen_apic.c +++ b/hw/i386/xen/xen_apic.c @@ -49,8 +49,9 @@ static void xen_apic_realize(DeviceState *dev, Error **errp) msi_nonbroken = true; } -static void xen_apic_set_base(APICCommonState *s, uint64_t val) +static int xen_apic_set_base(APICCommonState *s, uint64_t val) { + return 0; } static void xen_apic_set_tpr(APICCommonState *s, uint8_t val) diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 178fb26..1d887d6 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -308,8 +308,49 @@ bool is_x2apic_mode(DeviceState *dev) return s->apicbase & MSR_IA32_APICBASE_EXTD; } -static void apic_set_base(APICCommonState *s, uint64_t val) +static int apic_set_base_check(APICCommonState *s, uint64_t val) { + /* Enable x2apic when x2apic is not supported by CPU */ + if (!cpu_has_x2apic_feature(&s->cpu->env) && + val & MSR_IA32_APICBASE_EXTD) { + return -1; + } + + /* + * Transition into invalid state + * (s->apicbase & MSR_IA32_APICBASE_ENABLE == 0) && + * (s->apicbase & MSR_IA32_APICBASE_EXTD) == 1 + */ + if (!(val & MSR_IA32_APICBASE_ENABLE) && + (val & MSR_IA32_APICBASE_EXTD)) { + return -1; + } + + /* Invalid transition from disabled mode to x2APIC */ + if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) && + !(s->apicbase & MSR_IA32_APICBASE_EXTD) && + (val & MSR_IA32_APICBASE_ENABLE) && + (val & MSR_IA32_APICBASE_EXTD)) { + return -1; + } + + /* Invalid transition from x2APIC to xAPIC */ + if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) && + (s->apicbase & MSR_IA32_APICBASE_EXTD) && + (val & MSR_IA32_APICBASE_ENABLE) && + !(val & MSR_IA32_APICBASE_EXTD)) { + return -1; + } + + return 0; +} + +static int apic_set_base(APICCommonState *s, uint64_t val) +{ + if (apic_set_base_check(s, val) < 0) { + return -1; + } + s->apicbase = (val & 0xfffff000) | (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); /* if disabled, cannot be enabled again */ @@ -318,6 +359,25 @@ static void apic_set_base(APICCommonState *s, uint64_t val) cpu_clear_apic_feature(&s->cpu->env); s->spurious_vec &= ~APIC_SV_ENABLE; } + + /* Transition from disabled mode to xAPIC */ + if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) && + (val & MSR_IA32_APICBASE_ENABLE)) { + s->apicbase |= MSR_IA32_APICBASE_ENABLE; + cpu_set_apic_feature(&s->cpu->env); + } + + /* Transition from xAPIC to x2APIC */ + if (cpu_has_x2apic_feature(&s->cpu->env) && + !(s->apicbase & MSR_IA32_APICBASE_EXTD) && + (val & MSR_IA32_APICBASE_EXTD)) { + s->apicbase |= MSR_IA32_APICBASE_EXTD; + + s->log_dest = ((s->initial_apic_id & 0xffff0) << 16) | + (1 << (s->initial_apic_id & 0xf)); + } + + return 0; } static void apic_set_tpr(APICCommonState *s, uint8_t val) diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 3c43ac9..16ab40a 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -35,20 +35,19 @@ bool apic_report_tpr_access; -void cpu_set_apic_base(DeviceState *dev, uint64_t val) +int cpu_set_apic_base(DeviceState *dev, uint64_t val) { trace_cpu_set_apic_base(val); if (dev) { APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - /* switching to x2APIC, reset possibly modified xAPIC ID */ - if (!(s->apicbase & MSR_IA32_APICBASE_EXTD) && - (val & MSR_IA32_APICBASE_EXTD)) { - s->id = s->initial_apic_id; - } - info->set_base(s, val); + /* Reset possibly modified xAPIC ID */ + s->id = s->initial_apic_id; + return info->set_base(s, val); } + + return 0; } uint64_t cpu_get_apic_base(DeviceState *dev) |