From ab388a98148e5b44ba0b6bc0269fdb983b4a6838 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 16 Oct 2011 11:59:30 +0200 Subject: apic: Stop timer on reset All LVTs are masked on reset, so the timer becomes ineffective. Letting it tick nevertheless is harmless, but will at least create a spurious trace event. Signed-off-by: Jan Kiszka --- hw/apic.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'hw/apic.c') diff --git a/hw/apic.c b/hw/apic.c index 9d0f460b58..4b97b17dbe 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -528,6 +528,8 @@ void apic_init_reset(DeviceState *d) s->initial_count_load_time = 0; s->next_time = 0; s->wait_for_sipi = 1; + + qemu_del_timer(s->timer); } static void apic_startup(APICState *s, int vector_num) -- cgit 1.4.1 From 02c091953cd8c24db46649ad2862b9648c50f865 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 18 Oct 2011 00:00:06 +0800 Subject: apic: Inject external NMI events via LINT1 On real hardware, NMI button events are injected via the LINT1 line of the APICs. E.g. kdump expect this wiring and gets upset if the per-APIC LINT1 mask is not respected, i.e. if NMIs are injected to VCPUs that should not receive them. Change the APIC emulation code to reflect this. Based on qemu-kvm patch by Lai Jiangshan. CC: Lai Jiangshan Reported-by: Kenji Kaneshige Signed-off-by: Jan Kiszka --- cpus.c | 6 +++++- hw/apic.c | 7 +++++++ hw/apic.h | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'hw/apic.c') diff --git a/cpus.c b/cpus.c index 2dae549225..f45a438b29 100644 --- a/cpus.c +++ b/cpus.c @@ -1217,7 +1217,11 @@ void qmp_inject_nmi(Error **errp) CPUState *env; for (env = first_cpu; env != NULL; env = env->next_cpu) { - cpu_interrupt(env, CPU_INTERRUPT_NMI); + if (!env->apic_state) { + cpu_interrupt(env, CPU_INTERRUPT_NMI); + } else { + apic_deliver_nmi(env->apic_state); + } } #else error_set(errp, QERR_UNSUPPORTED); diff --git a/hw/apic.c b/hw/apic.c index 4b97b17dbe..b9d733c31a 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -205,6 +205,13 @@ void apic_deliver_pic_intr(DeviceState *d, int level) } } +void apic_deliver_nmi(DeviceState *d) +{ + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + + apic_local_deliver(s, APIC_LVT_LINT1); +} + #define foreach_apic(apic, deliver_bitmask, code) \ {\ int __i, __j, __mask;\ diff --git a/hw/apic.h b/hw/apic.h index a5c910fe0a..a62d83ba9f 100644 --- a/hw/apic.h +++ b/hw/apic.h @@ -8,6 +8,7 @@ void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t trigger_mode); int apic_accept_pic_intr(DeviceState *s); void apic_deliver_pic_intr(DeviceState *s, int level); +void apic_deliver_nmi(DeviceState *d); int apic_get_interrupt(DeviceState *s); void apic_reset_irq_delivered(void); int apic_get_irq_delivered(void); -- cgit 1.4.1 From 343270ea8777fa95ce2c287fc00c2eaa53255265 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 13 Dec 2011 15:39:04 +0100 Subject: apic: Introduce apic_report_irq_delivered The in-kernel i8259 and IOAPIC backends for KVM will need this, so encapsulate the shared bits. Signed-off-by: Jan Kiszka --- hw/apic.c | 11 ++++++++--- hw/apic.h | 1 + trace-events | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'hw/apic.c') diff --git a/hw/apic.c b/hw/apic.c index b9d733c31a..bec493bf63 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -413,6 +413,13 @@ static void apic_update_irq(APICState *s) } } +void apic_report_irq_delivered(int delivered) +{ + apic_irq_delivered += delivered; + + trace_apic_report_irq_delivered(apic_irq_delivered); +} + void apic_reset_irq_delivered(void) { trace_apic_reset_irq_delivered(apic_irq_delivered); @@ -429,9 +436,7 @@ int apic_get_irq_delivered(void) static void apic_set_irq(APICState *s, int vector_num, int trigger_mode) { - apic_irq_delivered += !get_bit(s->irr, vector_num); - - trace_apic_set_irq(apic_irq_delivered); + apic_report_irq_delivered(!get_bit(s->irr, vector_num)); set_bit(s->irr, vector_num); if (trigger_mode) diff --git a/hw/apic.h b/hw/apic.h index a62d83ba9f..8173d8a790 100644 --- a/hw/apic.h +++ b/hw/apic.h @@ -10,6 +10,7 @@ int apic_accept_pic_intr(DeviceState *s); void apic_deliver_pic_intr(DeviceState *s, int level); void apic_deliver_nmi(DeviceState *d); int apic_get_interrupt(DeviceState *s); +void apic_report_irq_delivered(int delivered); void apic_reset_irq_delivered(void); int apic_get_irq_delivered(void); void cpu_set_apic_base(DeviceState *s, uint64_t val); diff --git a/trace-events b/trace-events index c18435bbe1..5a260d6a09 100644 --- a/trace-events +++ b/trace-events @@ -95,9 +95,9 @@ cpu_get_apic_base(uint64_t val) "%016"PRIx64 apic_mem_readl(uint64_t addr, uint32_t val) "%"PRIx64" = %08x" apic_mem_writel(uint64_t addr, uint32_t val) "%"PRIx64" = %08x" # coalescing +apic_report_irq_delivered(int apic_irq_delivered) "coalescing %d" apic_reset_irq_delivered(int apic_irq_delivered) "old coalescing %d" apic_get_irq_delivered(int apic_irq_delivered) "returning coalescing %d" -apic_set_irq(int apic_irq_delivered) "coalescing %d" # hw/cs4231.c cs4231_mem_readl_dreg(uint32_t reg, uint32_t ret) "read dreg %d: 0x%02x" -- cgit 1.4.1 From dae01685280cef9b70ade9167340b5373eada9e8 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 16 Oct 2011 11:16:36 +0200 Subject: apic: Factor out base class for KVM reuse The KVM in-kernel APIC model will reuse parts of the user space model while providing the same frontend view to guest and most management interfaces. Factor out an APIC base class to encapsulate those parts that will be shared by user space and KVM model. This class offers callback hooks for init, base/tpr setting, and the external NMI delivery that will be set via APICCommonInfo structure and implemented specifically in the subclasses. Signed-off-by: Jan Kiszka --- Makefile.target | 2 +- hw/apic.c | 338 +++++++---------------------------------------------- hw/apic.h | 1 - hw/apic_common.c | 252 +++++++++++++++++++++++++++++++++++++++ hw/apic_internal.h | 112 ++++++++++++++++++ 5 files changed, 406 insertions(+), 299 deletions(-) create mode 100644 hw/apic_common.c create mode 100644 hw/apic_internal.h (limited to 'hw/apic.c') diff --git a/Makefile.target b/Makefile.target index 0451b637d6..4446273cd9 100644 --- a/Makefile.target +++ b/Makefile.target @@ -228,7 +228,7 @@ obj-y += device-hotplug.o # Hardware support obj-i386-y += vga.o obj-i386-y += mc146818rtc.o pc.o -obj-i386-y += cirrus_vga.o sga.o apic.o ioapic.o piix_pci.o +obj-i386-y += cirrus_vga.o sga.o apic_common.o apic.o ioapic.o piix_pci.o obj-i386-y += vmport.o obj-i386-y += pci-hotplug.o smbios.o wdt_ib700.o obj-i386-y += debugcon.o multiboot.o diff --git a/hw/apic.c b/hw/apic.c index bec493bf63..387a46940a 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -16,53 +16,13 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see */ -#include "hw.h" +#include "apic_internal.h" #include "apic.h" #include "ioapic.h" -#include "qemu-timer.h" #include "host-utils.h" -#include "sysbus.h" #include "trace.h" #include "pc.h" -/* APIC Local Vector Table */ -#define APIC_LVT_TIMER 0 -#define APIC_LVT_THERMAL 1 -#define APIC_LVT_PERFORM 2 -#define APIC_LVT_LINT0 3 -#define APIC_LVT_LINT1 4 -#define APIC_LVT_ERROR 5 -#define APIC_LVT_NB 6 - -/* APIC delivery modes */ -#define APIC_DM_FIXED 0 -#define APIC_DM_LOWPRI 1 -#define APIC_DM_SMI 2 -#define APIC_DM_NMI 4 -#define APIC_DM_INIT 5 -#define APIC_DM_SIPI 6 -#define APIC_DM_EXTINT 7 - -/* APIC destination mode */ -#define APIC_DESTMODE_FLAT 0xf -#define APIC_DESTMODE_CLUSTER 1 - -#define APIC_TRIGGER_EDGE 0 -#define APIC_TRIGGER_LEVEL 1 - -#define APIC_LVT_TIMER_PERIODIC (1<<17) -#define APIC_LVT_MASKED (1<<16) -#define APIC_LVT_LEVEL_TRIGGER (1<<15) -#define APIC_LVT_REMOTE_IRR (1<<14) -#define APIC_INPUT_POLARITY (1<<13) -#define APIC_SEND_PENDING (1<<12) - -#define ESR_ILLEGAL_ADDRESS (1 << 7) - -#define APIC_SV_DIRECTED_IO (1<<12) -#define APIC_SV_ENABLE (1<<8) - -#define MAX_APICS 255 #define MAX_APIC_WORDS 8 /* Intel APIC constants: from include/asm/msidef.h */ @@ -75,43 +35,10 @@ #define MSI_ADDR_DEST_ID_SHIFT 12 #define MSI_ADDR_DEST_ID_MASK 0x00ffff0 -#define MSI_ADDR_SIZE 0x100000 - -typedef struct APICState APICState; - -struct APICState { - SysBusDevice busdev; - MemoryRegion io_memory; - void *cpu_env; - uint32_t apicbase; - uint8_t id; - uint8_t arb_id; - uint8_t tpr; - uint32_t spurious_vec; - uint8_t log_dest; - uint8_t dest_mode; - uint32_t isr[8]; /* in service register */ - uint32_t tmr[8]; /* trigger mode register */ - uint32_t irr[8]; /* interrupt request register */ - uint32_t lvt[APIC_LVT_NB]; - uint32_t esr; /* error register */ - uint32_t icr[2]; - - uint32_t divide_conf; - int count_shift; - uint32_t initial_count; - int64_t initial_count_load_time, next_time; - uint32_t idx; - QEMUTimer *timer; - int sipi_vector; - int wait_for_sipi; -}; - -static APICState *local_apics[MAX_APICS + 1]; -static int apic_irq_delivered; +static APICCommonState *local_apics[MAX_APICS + 1]; -static void apic_set_irq(APICState *s, int vector_num, int trigger_mode); -static void apic_update_irq(APICState *s); +static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode); +static void apic_update_irq(APICCommonState *s); static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, uint8_t dest, uint8_t dest_mode); @@ -151,7 +78,7 @@ static inline int get_bit(uint32_t *tab, int index) return !!(tab[i] & mask); } -static void apic_local_deliver(APICState *s, int vector) +static void apic_local_deliver(APICCommonState *s, int vector) { uint32_t lvt = s->lvt[vector]; int trigger_mode; @@ -185,7 +112,7 @@ static void apic_local_deliver(APICState *s, int vector) void apic_deliver_pic_intr(DeviceState *d, int level) { - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); if (level) { apic_local_deliver(s, APIC_LVT_LINT0); @@ -205,10 +132,8 @@ void apic_deliver_pic_intr(DeviceState *d, int level) } } -void apic_deliver_nmi(DeviceState *d) +static void apic_external_nmi(APICCommonState *s) { - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); - apic_local_deliver(s, APIC_LVT_LINT1); } @@ -234,7 +159,7 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask, uint8_t delivery_mode, uint8_t vector_num, uint8_t trigger_mode) { - APICState *apic_iter; + APICCommonState *apic_iter; switch (delivery_mode) { case APIC_DM_LOWPRI: @@ -300,14 +225,8 @@ void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, trigger_mode); } -void cpu_set_apic_base(DeviceState *d, uint64_t val) +static void apic_set_base(APICCommonState *s, uint64_t val) { - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); - - trace_cpu_set_apic_base(val); - - if (!s) - return; s->apicbase = (val & 0xfffff000) | (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); /* if disabled, cannot be enabled again */ @@ -318,32 +237,12 @@ void cpu_set_apic_base(DeviceState *d, uint64_t val) } } -uint64_t cpu_get_apic_base(DeviceState *d) +static void apic_set_tpr(APICCommonState *s, uint8_t val) { - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); - - trace_cpu_get_apic_base(s ? (uint64_t)s->apicbase: 0); - - return s ? s->apicbase : 0; -} - -void cpu_set_apic_tpr(DeviceState *d, uint8_t val) -{ - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); - - if (!s) - return; s->tpr = (val & 0x0f) << 4; apic_update_irq(s); } -uint8_t cpu_get_apic_tpr(DeviceState *d) -{ - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); - - return s ? s->tpr >> 4 : 0; -} - /* return -1 if no bit is set */ static int get_highest_priority_int(uint32_t *tab) { @@ -356,7 +255,7 @@ static int get_highest_priority_int(uint32_t *tab) return -1; } -static int apic_get_ppr(APICState *s) +static int apic_get_ppr(APICCommonState *s) { int tpr, isrv, ppr; @@ -372,7 +271,7 @@ static int apic_get_ppr(APICState *s) return ppr; } -static int apic_get_arb_pri(APICState *s) +static int apic_get_arb_pri(APICCommonState *s) { /* XXX: arbitration */ return 0; @@ -384,7 +283,7 @@ static int apic_get_arb_pri(APICState *s) * 0 - no interrupt, * >0 - interrupt number */ -static int apic_irq_pending(APICState *s) +static int apic_irq_pending(APICCommonState *s) { int irrv, ppr; irrv = get_highest_priority_int(s->irr); @@ -400,7 +299,7 @@ static int apic_irq_pending(APICState *s) } /* signal the CPU if an irq is pending */ -static void apic_update_irq(APICState *s) +static void apic_update_irq(APICCommonState *s) { if (!(s->spurious_vec & APIC_SV_ENABLE)) { return; @@ -413,28 +312,7 @@ static void apic_update_irq(APICState *s) } } -void apic_report_irq_delivered(int delivered) -{ - apic_irq_delivered += delivered; - - trace_apic_report_irq_delivered(apic_irq_delivered); -} - -void apic_reset_irq_delivered(void) -{ - trace_apic_reset_irq_delivered(apic_irq_delivered); - - apic_irq_delivered = 0; -} - -int apic_get_irq_delivered(void) -{ - trace_apic_get_irq_delivered(apic_irq_delivered); - - return apic_irq_delivered; -} - -static void apic_set_irq(APICState *s, int vector_num, int trigger_mode) +static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode) { apic_report_irq_delivered(!get_bit(s->irr, vector_num)); @@ -446,7 +324,7 @@ static void apic_set_irq(APICState *s, int vector_num, int trigger_mode) apic_update_irq(s); } -static void apic_eoi(APICState *s) +static void apic_eoi(APICCommonState *s) { int isrv; isrv = get_highest_priority_int(s->isr); @@ -461,7 +339,7 @@ static void apic_eoi(APICState *s) static int apic_find_dest(uint8_t dest) { - APICState *apic = local_apics[dest]; + APICCommonState *apic = local_apics[dest]; int i; if (apic && apic->id == dest) @@ -481,7 +359,7 @@ static int apic_find_dest(uint8_t dest) static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, uint8_t dest, uint8_t dest_mode) { - APICState *apic_iter; + APICCommonState *apic_iter; int i; if (dest_mode == 0) { @@ -515,36 +393,7 @@ static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, } } -void apic_init_reset(DeviceState *d) -{ - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); - int i; - - if (!s) - return; - - s->tpr = 0; - s->spurious_vec = 0xff; - s->log_dest = 0; - s->dest_mode = 0xf; - memset(s->isr, 0, sizeof(s->isr)); - memset(s->tmr, 0, sizeof(s->tmr)); - memset(s->irr, 0, sizeof(s->irr)); - for(i = 0; i < APIC_LVT_NB; i++) - s->lvt[i] = 1 << 16; /* mask LVT */ - s->esr = 0; - memset(s->icr, 0, sizeof(s->icr)); - s->divide_conf = 0; - s->count_shift = 0; - s->initial_count = 0; - s->initial_count_load_time = 0; - s->next_time = 0; - s->wait_for_sipi = 1; - - qemu_del_timer(s->timer); -} - -static void apic_startup(APICState *s, int vector_num) +static void apic_startup(APICCommonState *s, int vector_num) { s->sipi_vector = vector_num; cpu_interrupt(s->cpu_env, CPU_INTERRUPT_SIPI); @@ -552,7 +401,7 @@ static void apic_startup(APICState *s, int vector_num) void apic_sipi(DeviceState *d) { - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_SIPI); @@ -566,10 +415,10 @@ static void apic_deliver(DeviceState *d, uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t trigger_mode) { - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); uint32_t deliver_bitmask[MAX_APIC_WORDS]; int dest_shorthand = (s->icr[0] >> 18) & 3; - APICState *apic_iter; + APICCommonState *apic_iter; switch (dest_shorthand) { case 0: @@ -612,7 +461,7 @@ static void apic_deliver(DeviceState *d, uint8_t dest, uint8_t dest_mode, int apic_get_interrupt(DeviceState *d) { - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); int intno; /* if the APIC is installed or enabled, we let the 8259 handle the @@ -637,7 +486,7 @@ int apic_get_interrupt(DeviceState *d) int apic_accept_pic_intr(DeviceState *d) { - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); uint32_t lvt0; if (!s) @@ -652,7 +501,7 @@ int apic_accept_pic_intr(DeviceState *d) return 0; } -static uint32_t apic_get_current_count(APICState *s) +static uint32_t apic_get_current_count(APICCommonState *s) { int64_t d; uint32_t val; @@ -670,7 +519,7 @@ static uint32_t apic_get_current_count(APICState *s) return val; } -static void apic_timer_update(APICState *s, int64_t current_time) +static void apic_timer_update(APICCommonState *s, int64_t current_time) { int64_t next_time, d; @@ -697,7 +546,7 @@ static void apic_timer_update(APICState *s, int64_t current_time) static void apic_timer(void *opaque) { - APICState *s = opaque; + APICCommonState *s = opaque; apic_local_deliver(s, APIC_LVT_TIMER); apic_timer_update(s, s->next_time); @@ -724,7 +573,7 @@ static void apic_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) { DeviceState *d; - APICState *s; + APICCommonState *s; uint32_t val; int index; @@ -732,7 +581,7 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) if (!d) { return 0; } - s = DO_UPCAST(APICState, busdev.qdev, d); + s = DO_UPCAST(APICCommonState, busdev.qdev, d); index = (addr >> 4) & 0xff; switch(index) { @@ -815,7 +664,7 @@ static void apic_send_msi(target_phys_addr_t addr, uint32_t data) static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { DeviceState *d; - APICState *s; + APICCommonState *s; int index = (addr >> 4) & 0xff; if (addr > 0xfff || !index) { /* MSI and MMIO APIC are at the same memory location, @@ -831,7 +680,7 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) if (!d) { return; } - s = DO_UPCAST(APICState, busdev.qdev, d); + s = DO_UPCAST(APICCommonState, busdev.qdev, d); trace_apic_mem_writel(addr, val); @@ -904,96 +753,6 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) } } -/* This function is only used for old state version 1 and 2 */ -static int apic_load_old(QEMUFile *f, void *opaque, int version_id) -{ - APICState *s = opaque; - int i; - - if (version_id > 2) - return -EINVAL; - - /* XXX: what if the base changes? (registered memory regions) */ - qemu_get_be32s(f, &s->apicbase); - qemu_get_8s(f, &s->id); - qemu_get_8s(f, &s->arb_id); - qemu_get_8s(f, &s->tpr); - qemu_get_be32s(f, &s->spurious_vec); - qemu_get_8s(f, &s->log_dest); - qemu_get_8s(f, &s->dest_mode); - for (i = 0; i < 8; i++) { - qemu_get_be32s(f, &s->isr[i]); - qemu_get_be32s(f, &s->tmr[i]); - qemu_get_be32s(f, &s->irr[i]); - } - for (i = 0; i < APIC_LVT_NB; i++) { - qemu_get_be32s(f, &s->lvt[i]); - } - qemu_get_be32s(f, &s->esr); - qemu_get_be32s(f, &s->icr[0]); - qemu_get_be32s(f, &s->icr[1]); - qemu_get_be32s(f, &s->divide_conf); - s->count_shift=qemu_get_be32(f); - qemu_get_be32s(f, &s->initial_count); - s->initial_count_load_time=qemu_get_be64(f); - s->next_time=qemu_get_be64(f); - - if (version_id >= 2) - qemu_get_timer(f, s->timer); - return 0; -} - -static const VMStateDescription vmstate_apic = { - .name = "apic", - .version_id = 3, - .minimum_version_id = 3, - .minimum_version_id_old = 1, - .load_state_old = apic_load_old, - .fields = (VMStateField []) { - VMSTATE_UINT32(apicbase, APICState), - VMSTATE_UINT8(id, APICState), - VMSTATE_UINT8(arb_id, APICState), - VMSTATE_UINT8(tpr, APICState), - VMSTATE_UINT32(spurious_vec, APICState), - VMSTATE_UINT8(log_dest, APICState), - VMSTATE_UINT8(dest_mode, APICState), - VMSTATE_UINT32_ARRAY(isr, APICState, 8), - VMSTATE_UINT32_ARRAY(tmr, APICState, 8), - VMSTATE_UINT32_ARRAY(irr, APICState, 8), - VMSTATE_UINT32_ARRAY(lvt, APICState, APIC_LVT_NB), - VMSTATE_UINT32(esr, APICState), - VMSTATE_UINT32_ARRAY(icr, APICState, 2), - VMSTATE_UINT32(divide_conf, APICState), - VMSTATE_INT32(count_shift, APICState), - VMSTATE_UINT32(initial_count, APICState), - VMSTATE_INT64(initial_count_load_time, APICState), - VMSTATE_INT64(next_time, APICState), - VMSTATE_TIMER(timer, APICState), - VMSTATE_END_OF_LIST() - } -}; - -static void apic_reset(DeviceState *d) -{ - APICState *s = DO_UPCAST(APICState, busdev.qdev, d); - int bsp; - - bsp = cpu_is_bsp(s->cpu_env); - s->apicbase = 0xfee00000 | - (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE; - - apic_init_reset(d); - - if (bsp) { - /* - * LINT0 delivery mode on CPU #0 is set to ExtInt at initialization - * time typically by BIOS, so PIC interrupt can be delivered to the - * processor when local APIC is enabled. - */ - s->lvt[APIC_LVT_LINT0] = 0x700; - } -} - static const MemoryRegionOps apic_io_ops = { .old_mmio = { .read = { apic_mem_readb, apic_mem_readw, apic_mem_readl, }, @@ -1002,41 +761,26 @@ static const MemoryRegionOps apic_io_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int apic_init1(SysBusDevice *dev) +static void apic_init(APICCommonState *s) { - APICState *s = FROM_SYSBUS(APICState, dev); - static int last_apic_idx; - - if (last_apic_idx >= MAX_APICS) { - return -1; - } - memory_region_init_io(&s->io_memory, &apic_io_ops, s, "apic", - MSI_ADDR_SIZE); - sysbus_init_mmio(dev, &s->io_memory); + memory_region_init_io(&s->io_memory, &apic_io_ops, s, "apic-msi", + MSI_SPACE_SIZE); s->timer = qemu_new_timer_ns(vm_clock, apic_timer, s); - s->idx = last_apic_idx++; local_apics[s->idx] = s; - return 0; } -static SysBusDeviceInfo apic_info = { - .init = apic_init1, - .qdev.name = "apic", - .qdev.size = sizeof(APICState), - .qdev.vmsd = &vmstate_apic, - .qdev.reset = apic_reset, - .qdev.no_user = 1, - .qdev.props = (Property[]) { - DEFINE_PROP_UINT8("id", APICState, id, -1), - DEFINE_PROP_PTR("cpu_env", APICState, cpu_env), - DEFINE_PROP_END_OF_LIST(), - } +static APICCommonInfo apic_info = { + .busdev.qdev.name = "apic", + .init = apic_init, + .set_base = apic_set_base, + .set_tpr = apic_set_tpr, + .external_nmi = apic_external_nmi, }; static void apic_register_devices(void) { - sysbus_register_withprop(&apic_info); + apic_qdev_register(&apic_info); } device_init(apic_register_devices) diff --git a/hw/apic.h b/hw/apic.h index 8173d8a790..a62d83ba9f 100644 --- a/hw/apic.h +++ b/hw/apic.h @@ -10,7 +10,6 @@ int apic_accept_pic_intr(DeviceState *s); void apic_deliver_pic_intr(DeviceState *s, int level); void apic_deliver_nmi(DeviceState *d); int apic_get_interrupt(DeviceState *s); -void apic_report_irq_delivered(int delivered); void apic_reset_irq_delivered(void); int apic_get_irq_delivered(void); void cpu_set_apic_base(DeviceState *s, uint64_t val); diff --git a/hw/apic_common.c b/hw/apic_common.c new file mode 100644 index 0000000000..eef977ff1c --- /dev/null +++ b/hw/apic_common.c @@ -0,0 +1,252 @@ +/* + * APIC support - common bits of emulated and KVM kernel model + * + * Copyright (c) 2004-2005 Fabrice Bellard + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ +#include "apic.h" +#include "apic_internal.h" +#include "trace.h" + +static int apic_irq_delivered; + +void cpu_set_apic_base(DeviceState *d, uint64_t val) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + APICCommonInfo *info; + + trace_cpu_set_apic_base(val); + + if (s) { + info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info); + info->set_base(s, val); + } +} + +uint64_t cpu_get_apic_base(DeviceState *d) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + + trace_cpu_get_apic_base(s ? (uint64_t)s->apicbase : 0); + + return s ? s->apicbase : 0; +} + +void cpu_set_apic_tpr(DeviceState *d, uint8_t val) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + APICCommonInfo *info; + + if (s) { + info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info); + info->set_tpr(s, val); + } +} + +uint8_t cpu_get_apic_tpr(DeviceState *d) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + + return s ? s->tpr >> 4 : 0; +} + +void apic_report_irq_delivered(int delivered) +{ + apic_irq_delivered += delivered; + + trace_apic_report_irq_delivered(apic_irq_delivered); +} + +void apic_reset_irq_delivered(void) +{ + trace_apic_reset_irq_delivered(apic_irq_delivered); + + apic_irq_delivered = 0; +} + +int apic_get_irq_delivered(void) +{ + trace_apic_get_irq_delivered(apic_irq_delivered); + + return apic_irq_delivered; +} + +void apic_deliver_nmi(DeviceState *d) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + APICCommonInfo *info; + + info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info); + info->external_nmi(s); +} + +void apic_init_reset(DeviceState *d) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + int i; + + if (!s) { + return; + } + s->tpr = 0; + s->spurious_vec = 0xff; + s->log_dest = 0; + s->dest_mode = 0xf; + memset(s->isr, 0, sizeof(s->isr)); + memset(s->tmr, 0, sizeof(s->tmr)); + memset(s->irr, 0, sizeof(s->irr)); + for (i = 0; i < APIC_LVT_NB; i++) { + s->lvt[i] = APIC_LVT_MASKED; + } + s->esr = 0; + memset(s->icr, 0, sizeof(s->icr)); + s->divide_conf = 0; + s->count_shift = 0; + s->initial_count = 0; + s->initial_count_load_time = 0; + s->next_time = 0; + s->wait_for_sipi = 1; + + qemu_del_timer(s->timer); +} + +static void apic_reset_common(DeviceState *d) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + bool bsp; + + bsp = cpu_is_bsp(s->cpu_env); + s->apicbase = 0xfee00000 | + (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE; + + apic_init_reset(d); + + if (bsp) { + /* + * LINT0 delivery mode on CPU #0 is set to ExtInt at initialization + * time typically by BIOS, so PIC interrupt can be delivered to the + * processor when local APIC is enabled. + */ + s->lvt[APIC_LVT_LINT0] = 0x700; + } +} + +/* This function is only used for old state version 1 and 2 */ +static int apic_load_old(QEMUFile *f, void *opaque, int version_id) +{ + APICCommonState *s = opaque; + int i; + + if (version_id > 2) { + return -EINVAL; + } + + /* XXX: what if the base changes? (registered memory regions) */ + qemu_get_be32s(f, &s->apicbase); + qemu_get_8s(f, &s->id); + qemu_get_8s(f, &s->arb_id); + qemu_get_8s(f, &s->tpr); + qemu_get_be32s(f, &s->spurious_vec); + qemu_get_8s(f, &s->log_dest); + qemu_get_8s(f, &s->dest_mode); + for (i = 0; i < 8; i++) { + qemu_get_be32s(f, &s->isr[i]); + qemu_get_be32s(f, &s->tmr[i]); + qemu_get_be32s(f, &s->irr[i]); + } + for (i = 0; i < APIC_LVT_NB; i++) { + qemu_get_be32s(f, &s->lvt[i]); + } + qemu_get_be32s(f, &s->esr); + qemu_get_be32s(f, &s->icr[0]); + qemu_get_be32s(f, &s->icr[1]); + qemu_get_be32s(f, &s->divide_conf); + s->count_shift = qemu_get_be32(f); + qemu_get_be32s(f, &s->initial_count); + s->initial_count_load_time = qemu_get_be64(f); + s->next_time = qemu_get_be64(f); + + if (version_id >= 2) { + qemu_get_timer(f, s->timer); + } + return 0; +} + +static int apic_init_common(SysBusDevice *dev) +{ + APICCommonState *s = FROM_SYSBUS(APICCommonState, dev); + APICCommonInfo *info; + static int apic_no; + + if (apic_no >= MAX_APICS) { + return -1; + } + s->idx = apic_no++; + + info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info); + info->init(s); + + sysbus_init_mmio(&s->busdev, &s->io_memory); + return 0; +} + +static const VMStateDescription vmstate_apic_common = { + .name = "apic", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 1, + .load_state_old = apic_load_old, + .fields = (VMStateField[]) { + VMSTATE_UINT32(apicbase, APICCommonState), + VMSTATE_UINT8(id, APICCommonState), + VMSTATE_UINT8(arb_id, APICCommonState), + VMSTATE_UINT8(tpr, APICCommonState), + VMSTATE_UINT32(spurious_vec, APICCommonState), + VMSTATE_UINT8(log_dest, APICCommonState), + VMSTATE_UINT8(dest_mode, APICCommonState), + VMSTATE_UINT32_ARRAY(isr, APICCommonState, 8), + VMSTATE_UINT32_ARRAY(tmr, APICCommonState, 8), + VMSTATE_UINT32_ARRAY(irr, APICCommonState, 8), + VMSTATE_UINT32_ARRAY(lvt, APICCommonState, APIC_LVT_NB), + VMSTATE_UINT32(esr, APICCommonState), + VMSTATE_UINT32_ARRAY(icr, APICCommonState, 2), + VMSTATE_UINT32(divide_conf, APICCommonState), + VMSTATE_INT32(count_shift, APICCommonState), + VMSTATE_UINT32(initial_count, APICCommonState), + VMSTATE_INT64(initial_count_load_time, APICCommonState), + VMSTATE_INT64(next_time, APICCommonState), + VMSTATE_TIMER(timer, APICCommonState), + VMSTATE_END_OF_LIST() + } +}; + +static Property apic_properties_common[] = { + DEFINE_PROP_UINT8("id", APICCommonState, id, -1), + DEFINE_PROP_PTR("cpu_env", APICCommonState, cpu_env), + DEFINE_PROP_END_OF_LIST(), +}; + + +void apic_qdev_register(APICCommonInfo *info) +{ + info->busdev.init = apic_init_common; + info->busdev.qdev.size = sizeof(APICCommonState), + info->busdev.qdev.vmsd = &vmstate_apic_common; + info->busdev.qdev.reset = apic_reset_common; + info->busdev.qdev.no_user = 1; + info->busdev.qdev.props = apic_properties_common; + sysbus_register_withprop(&info->busdev); +} diff --git a/hw/apic_internal.h b/hw/apic_internal.h new file mode 100644 index 0000000000..a7433fb0e6 --- /dev/null +++ b/hw/apic_internal.h @@ -0,0 +1,112 @@ +/* + * APIC support - internal interfaces + * + * Copyright (c) 2004-2005 Fabrice Bellard + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ +#ifndef QEMU_APIC_INTERNAL_H +#define QEMU_APIC_INTERNAL_H + +#include "memory.h" +#include "sysbus.h" +#include "qemu-timer.h" + +/* APIC Local Vector Table */ +#define APIC_LVT_TIMER 0 +#define APIC_LVT_THERMAL 1 +#define APIC_LVT_PERFORM 2 +#define APIC_LVT_LINT0 3 +#define APIC_LVT_LINT1 4 +#define APIC_LVT_ERROR 5 +#define APIC_LVT_NB 6 + +/* APIC delivery modes */ +#define APIC_DM_FIXED 0 +#define APIC_DM_LOWPRI 1 +#define APIC_DM_SMI 2 +#define APIC_DM_NMI 4 +#define APIC_DM_INIT 5 +#define APIC_DM_SIPI 6 +#define APIC_DM_EXTINT 7 + +/* APIC destination mode */ +#define APIC_DESTMODE_FLAT 0xf +#define APIC_DESTMODE_CLUSTER 1 + +#define APIC_TRIGGER_EDGE 0 +#define APIC_TRIGGER_LEVEL 1 + +#define APIC_LVT_TIMER_PERIODIC (1<<17) +#define APIC_LVT_MASKED (1<<16) +#define APIC_LVT_LEVEL_TRIGGER (1<<15) +#define APIC_LVT_REMOTE_IRR (1<<14) +#define APIC_INPUT_POLARITY (1<<13) +#define APIC_SEND_PENDING (1<<12) + +#define ESR_ILLEGAL_ADDRESS (1 << 7) + +#define APIC_SV_DIRECTED_IO (1<<12) +#define APIC_SV_ENABLE (1<<8) + +#define MAX_APICS 255 + +#define MSI_SPACE_SIZE 0x100000 + +typedef struct APICCommonState APICCommonState; + +struct APICCommonState { + SysBusDevice busdev; + MemoryRegion io_memory; + void *cpu_env; + uint32_t apicbase; + uint8_t id; + uint8_t arb_id; + uint8_t tpr; + uint32_t spurious_vec; + uint8_t log_dest; + uint8_t dest_mode; + uint32_t isr[8]; /* in service register */ + uint32_t tmr[8]; /* trigger mode register */ + uint32_t irr[8]; /* interrupt request register */ + uint32_t lvt[APIC_LVT_NB]; + uint32_t esr; /* error register */ + uint32_t icr[2]; + + uint32_t divide_conf; + int count_shift; + uint32_t initial_count; + int64_t initial_count_load_time; + int64_t next_time; + int idx; + QEMUTimer *timer; + int sipi_vector; + int wait_for_sipi; +}; + +typedef struct APICCommonInfo APICCommonInfo; + +struct APICCommonInfo { + SysBusDeviceInfo busdev; + void (*init)(APICCommonState *s); + void (*set_base)(APICCommonState *s, uint64_t val); + void (*set_tpr)(APICCommonState *s, uint8_t val); + void (*external_nmi)(APICCommonState *s); +}; + +void apic_report_irq_delivered(int delivered); +void apic_qdev_register(APICCommonInfo *info); + +#endif /* !QEMU_APIC_INTERNAL_H */ -- cgit 1.4.1 From 7a380ca350f84b5b99391da20a2b4ea505b0524d Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 16 Oct 2011 12:19:12 +0200 Subject: apic: Open-code timer save/restore To enable migration between accelerated and non-accelerated APIC models, we will need to handle the timer saving and restoring specially and can no longer rely on the automatics of VMSTATE_TIMER. Specifically, accelerated model will not start any QEMUTimer. This patch therefore factors out the generic bits into apic_next_timer and use a post-load callback to implemented model-specific logic. Signed-off-by: Jan Kiszka --- hw/apic.c | 30 ++++++++++++------------------ hw/apic_common.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- hw/apic_internal.h | 3 +++ 3 files changed, 67 insertions(+), 20 deletions(-) (limited to 'hw/apic.c') diff --git a/hw/apic.c b/hw/apic.c index 387a46940a..e59c964083 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -521,25 +521,9 @@ static uint32_t apic_get_current_count(APICCommonState *s) static void apic_timer_update(APICCommonState *s, int64_t current_time) { - int64_t next_time, d; - - if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) { - d = (current_time - s->initial_count_load_time) >> - s->count_shift; - if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { - if (!s->initial_count) - goto no_timer; - d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * ((uint64_t)s->initial_count + 1); - } else { - if (d >= s->initial_count) - goto no_timer; - d = (uint64_t)s->initial_count + 1; - } - next_time = s->initial_count_load_time + (d << s->count_shift); - qemu_mod_timer(s->timer, next_time); - s->next_time = next_time; + if (apic_next_timer(s, current_time)) { + qemu_mod_timer(s->timer, s->next_time); } else { - no_timer: qemu_del_timer(s->timer); } } @@ -753,6 +737,15 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) } } +static void apic_post_load(APICCommonState *s) +{ + if (s->timer_expiry != -1) { + qemu_mod_timer(s->timer, s->timer_expiry); + } else { + qemu_del_timer(s->timer); + } +} + static const MemoryRegionOps apic_io_ops = { .old_mmio = { .read = { apic_mem_readb, apic_mem_readw, apic_mem_readl, }, @@ -776,6 +769,7 @@ static APICCommonInfo apic_info = { .set_base = apic_set_base, .set_tpr = apic_set_tpr, .external_nmi = apic_external_nmi, + .post_load = apic_post_load, }; static void apic_register_devices(void) diff --git a/hw/apic_common.c b/hw/apic_common.c index eef977ff1c..e05369caab 100644 --- a/hw/apic_common.c +++ b/hw/apic_common.c @@ -93,6 +93,39 @@ void apic_deliver_nmi(DeviceState *d) info->external_nmi(s); } +bool apic_next_timer(APICCommonState *s, int64_t current_time) +{ + int64_t d; + + /* We need to store the timer state separately to support APIC + * implementations that maintain a non-QEMU timer, e.g. inside the + * host kernel. This open-coded state allows us to migrate between + * both models. */ + s->timer_expiry = -1; + + if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED) { + return false; + } + + d = (current_time - s->initial_count_load_time) >> s->count_shift; + + if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { + if (!s->initial_count) { + return false; + } + d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * + ((uint64_t)s->initial_count + 1); + } else { + if (d >= s->initial_count) { + return false; + } + d = (uint64_t)s->initial_count + 1; + } + s->next_time = s->initial_count_load_time + (d << s->count_shift); + s->timer_expiry = s->next_time; + return true; +} + void apic_init_reset(DeviceState *d) { APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); @@ -120,7 +153,10 @@ void apic_init_reset(DeviceState *d) s->next_time = 0; s->wait_for_sipi = 1; - qemu_del_timer(s->timer); + if (s->timer) { + qemu_del_timer(s->timer); + } + s->timer_expiry = -1; } static void apic_reset_common(DeviceState *d) @@ -203,12 +239,25 @@ static int apic_init_common(SysBusDevice *dev) return 0; } +static int apic_dispatch_post_load(void *opaque, int version_id) +{ + APICCommonState *s = opaque; + APICCommonInfo *info = + DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info); + + if (info->post_load) { + info->post_load(s); + } + return 0; +} + static const VMStateDescription vmstate_apic_common = { .name = "apic", .version_id = 3, .minimum_version_id = 3, .minimum_version_id_old = 1, .load_state_old = apic_load_old, + .post_load = apic_dispatch_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(apicbase, APICCommonState), VMSTATE_UINT8(id, APICCommonState), @@ -228,7 +277,8 @@ static const VMStateDescription vmstate_apic_common = { VMSTATE_UINT32(initial_count, APICCommonState), VMSTATE_INT64(initial_count_load_time, APICCommonState), VMSTATE_INT64(next_time, APICCommonState), - VMSTATE_TIMER(timer, APICCommonState), + VMSTATE_INT64(timer_expiry, + APICCommonState), /* open-coded timer state */ VMSTATE_END_OF_LIST() } }; diff --git a/hw/apic_internal.h b/hw/apic_internal.h index a7433fb0e6..1db4f061b5 100644 --- a/hw/apic_internal.h +++ b/hw/apic_internal.h @@ -92,6 +92,7 @@ struct APICCommonState { int64_t next_time; int idx; QEMUTimer *timer; + int64_t timer_expiry; int sipi_vector; int wait_for_sipi; }; @@ -104,9 +105,11 @@ struct APICCommonInfo { void (*set_base)(APICCommonState *s, uint64_t val); void (*set_tpr)(APICCommonState *s, uint8_t val); void (*external_nmi)(APICCommonState *s); + void (*post_load)(APICCommonState *s); }; void apic_report_irq_delivered(int delivered); void apic_qdev_register(APICCommonInfo *info); +bool apic_next_timer(APICCommonState *s, int64_t current_time); #endif /* !QEMU_APIC_INTERNAL_H */ -- cgit 1.4.1