DevAPIC.cpp revision f29584bacd2c22a36590e9e79d235e4f6b126571
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * Advanced Programmable Interrupt Controller (APIC) Device and
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * I/O Advanced Programmable Interrupt Controller (IO-APIC) Device.
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * available from http://www.virtualbox.org. This file is free software;
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * you can redistribute it and/or modify it under the terms of the GNU
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * General Public License (GPL) as published by the Free Software
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * additional information or have any questions.
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * --------------------------------------------------------------------
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * This code is based on:
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * apic.c revision 1.5 @@OSETODO
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync/*******************************************************************************
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync* Header Files *
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync*******************************************************************************/
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync/** @def APIC_LOCK
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * Acquires the PDM lock. */
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync int rc2 = PDMCritSectEnter((pThis)->CTX_SUFF(pCritSect), (rcBusy)); \
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync } while (0)
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync/** @def APIC_LOCK_VOID
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync * Acquires the PDM lock and does not expect failure (i.e. ring-3 only!). */
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync int rc2 = PDMCritSectEnter((pThis)->CTX_SUFF(pCritSect), (rcBusy)); \
bbd8414a6c9e8c73fbe9cffd73b5747c0570b118vboxsync } while (0)
return rc2; \
uint32_t i; \
code; \
apic++; \
#ifndef VBOX
#include "vl.h"
#define DEBUG_APIC
#define DEBUG_IOAPIC
#define APIC_LVT_TIMER 0
#define APIC_DM_FIXED 0
#define APIC_TRIGGER_EDGE 0
#ifdef VBOX
typedef struct APICState {
#ifndef VBOX
#ifdef VBOX
int count_shift;
#ifdef VBOX
#ifndef VBOX
bool fTimerArmed;
# ifdef VBOX_WITH_STATISTICS
} APICState;
#ifdef VBOX
# ifdef VBOX_WITH_STATISTICS
struct IOAPICState {
#ifdef VBOX
# ifdef VBOX_WITH_STATISTICS
#ifdef VBOX
# ifdef VBOX_WITH_STATISTICS
# ifdef VBOX_WITH_STATISTICS
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
#ifndef VBOX
static int apic_io_memory;
static int last_apic_id = 0;
#ifdef VBOX
PDMBOTHCBDECL(int) apicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
PDMBOTHCBDECL(int) apicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
PDMBOTHCBDECL(int) apicWriteMSR(PPDMDEVINS pDevIns, VMCPUID iCpu, uint32_t u32Reg, uint64_t u64Value);
PDMBOTHCBDECL(int) apicReadMSR(PPDMDEVINS pDevIns, VMCPUID iCpu, uint32_t u32Reg, uint64_t *pu64Value);
PDMBOTHCBDECL(int) ioapicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
PDMBOTHCBDECL(int) ioapicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
#ifdef VBOX
DECLINLINE(void) cpuSetInterrupt(APICDeviceInfo* dev, APICState *s, PDMAPICIRQ enmType = PDMAPICIRQ_HARDWARE)
# ifdef IN_RING3
vector);
case PDMAPICVERSION_NONE:
case PDMAPICVERSION_APIC:
return MSR_IA32_APICBASE_ENABLE;
case PDMAPICVERSION_X2APIC:
return PDMAPICVERSION_NONE;
return PDMAPICVERSION_NONE;
return PDMAPICVERSION_APIC;
return PDMAPICVERSION_X2APIC;
#ifndef VBOX
LogFlow(("apic_bus_deliver mask=%x mode=%x vector=%x polarity=%x trigger_mode=%x\n", deliver_bitmask, delivery_mode, vector_num, polarity, trigger_mode));
switch (delivery_mode) {
case APIC_DM_LOWPRI:
if (deliver_bitmask)
return VINF_SUCCESS;
case APIC_DM_FIXED:
case APIC_DM_SMI:
return VINF_SUCCESS;
case APIC_DM_NMI:
return VINF_SUCCESS;
case APIC_DM_INIT:
#ifdef VBOX
#ifdef IN_RING3
return VINF_SUCCESS;
return VINF_IOM_HC_MMIO_READ_WRITE;
case APIC_DM_EXTINT:
return VINF_SUCCESS;
#ifdef VBOX
return VINF_SUCCESS;
#ifndef VBOX
#ifdef DEBUG_APIC
s->apicbase =
switch (newMode)
case PDMAPICVERSION_NONE:
case PDMAPICVERSION_APIC:
case PDMAPICVERSION_X2APIC:
#ifndef VBOX
#ifdef DEBUG_APIC
return s->apicbase;
apic_update_irq(s);
unsigned int ret = 0;
#ifdef HOST_I386
return ret;
int i, mask;
int i, mask;
return s->apicbase;
return s->tpr;
PDMBOTHCBDECL(int) apicWriteMSR(PPDMDEVINS pDevIns, VMCPUID idCpu, uint32_t u32Reg, uint64_t u64Value)
return VERR_EM_INTERPRETER;
switch (index)
return rc;
PDMBOTHCBDECL(int) apicReadMSR(PPDMDEVINS pDevIns, VMCPUID idCpu, uint32_t u32Reg, uint64_t *pu64Value)
return VERR_EM_INTERPRETER;
switch (index)
val = 0;
val = 0;
return VINF_SUCCESS;
LogFlow(("apicBusDeliverCallback: pDevIns=%p u8Dest=%#x u8DestMode=%#x u8DeliveryMode=%#x iVector=%#x u8Polarity=%#x u8TriggerMode=%#x\n",
if (tab[i] != 0) {
if (isrv < 0)
isrv = 0;
return ppr;
int isrv;
if (isrv < 0)
isrv = 0;
return isrv;
#ifdef VBOX
if (irrv < 0)
#ifndef VBOX
#ifdef VBOX
if (!dev)
if (irrv < 0)
bool fIrqIsActive = false;
bool fIrqWasActive = false;
LogFlow(("CPU%d: apic_set_irq vector=%x, trigger_mode=%x\n", s->phys_id, vector_num, trigger_mode));
if (trigger_mode)
int isrv;
if (isrv < 0)
#ifndef VBOX
if (dest_mode == 0)
uint32_t i;
apic++;
return mask;
#ifdef IN_RING3
for(i = 0; i < APIC_LVT_NB; i++)
s->tpr = 0;
s->log_dest = 0;
s->esr = 0;
s->divide_conf = 0;
s->count_shift = 0;
s->initial_count = 0;
s->initial_count_load_time = 0;
s->next_time = 0;
#ifdef VBOX
#ifndef VBOX
#ifndef VBOX
LogFlow(("apic_deliver dest=%x dest_mode=%x dest_shorthand=%x delivery_mode=%x vector_num=%x polarity=%x trigger_mode=%x\n", dest, dest_mode, dest_shorthand, delivery_mode, vector_num, polarity, trigger_mode));
switch (dest_shorthand) {
#ifndef VBOX
switch (delivery_mode) {
case APIC_DM_INIT:
#ifndef VBOX
return VINF_SUCCESS;
case APIC_DM_SIPI:
#ifndef VBOX
# ifdef IN_RING3
return VINF_SUCCESS;
return VINF_IOM_HC_MMIO_WRITE;
#ifndef VBOX
if (!dev)
int intno;
if (intno < 0) {
return intno;
int64_t d;
#ifndef VBOX
s->count_shift;
s->count_shift;
if (d >= s->initial_count)
val = 0;
return val;
s->count_shift;
if (d >= s->initial_count)
goto no_timer;
# ifndef VBOX
s->fTimerArmed = true;
# ifndef VBOX
s->fTimerArmed = false;
static void apicTimerSetInitialCount(APICDeviceInfo *dev, APICState *pThis, uint32_t u32NewInitialCount)
|| u32NewInitialCount != 0))
uint64_t cTicks = (TMTimerGet(pThis->CTX_SUFF(pTimer)) - pThis->initial_count_load_time) >> pThis->count_shift;
NextTS = ((cTicks / ((uint64_t)pThis->initial_count + 1)) + 1) * ((uint64_t)pThis->initial_count + 1);
/* Try avoid the assertion in TM.cpp... this isn't perfect! */
# ifdef IN_RING3
#ifndef VBOX
#ifndef VBOX
APICState *s;
int index;
#ifndef VBOX
if (!env)
switch(index) {
val = 0;
#ifdef VBOX
#ifndef VBOX
#ifndef VBOX
#ifndef VBOX
#ifndef VBOX
val = 0;
#ifdef DEBUG_APIC
return val;
#ifndef VBOX
APICState *s;
static int apic_mem_writel(APICDeviceInfo* dev, APICState *s, target_phys_addr_t addr, uint32_t val)
int index;
#ifndef VBOX
if (!env)
#ifdef DEBUG_APIC
switch(index) {
#ifdef VBOX
apic_update_irq(s);
#ifndef VBOX
#ifndef VBOX
#ifndef VBOX
if (n == APIC_LVT_TIMER)
#ifndef VBOX
#ifdef VBOX
return rc;
#ifdef IN_RING3
#ifdef VBOX
for (i = 0; i < APIC_LVT_NB; i++) {
#ifdef VBOX
#ifdef VBOX
return -EINVAL;
switch (version_id)
s->phys_id = 0;
return -EINVAL;
for (i = 0; i < APIC_LVT_NB; i++) {
#ifdef VBOX
#ifndef VBOX
apic_init_ipi(s);
#ifndef VBOX
APICState *s;
apic_init_ipi(s);
if (apic_io_memory == 0) {
first_local_apic = s;
uint8_t i;
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
#ifndef VBOX
dest,
#ifdef VBOX
if (level) {
ioapic_service(s);
#ifdef VBOX
if (level) {
ioapic_service(s);
int index;
switch (s->ioregsel) {
val = 0;
#ifdef DEBUG_IOAPIC
return val;
int index;
#ifdef DEBUG_IOAPIC
switch (s->ioregsel) {
#ifdef VBOX
ioapic_service(s);
#ifdef IN_RING3
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
return -EINVAL;
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
#ifdef VBOX
memset(s, 0, sizeof(*s));
for(i = 0; i < IOAPIC_NUM_PINS; i++)
#ifdef VBOX
if (pDevIns)
if (pIoApicHlp)
#ifndef VBOX
IOAPICState *s;
int io_memory;
return NULL;
ioapic_reset(s);
ioapic_mem_write, s);
PDMBOTHCBDECL(int) apicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
switch (cb)
#ifndef IN_RING3
#ifdef IN_RC
pDevIns->pDevHlpR0->pfnPATMSetMMIOPatchInfo(pDevIns, GCPhysAddr, pDevIns + RT_OFFSETOF(APICState, tpr));
return VINF_PATM_HC_MMIO_PATCH_READ;
return VERR_INTERNAL_ERROR;
return VINF_SUCCESS;
PDMBOTHCBDECL(int) apicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
switch (cb)
int rc;
return rc;
return VERR_INTERNAL_ERROR;
return VINF_SUCCESS;
#ifdef IN_RING3
return VINF_SUCCESS;
static DECLCALLBACK(int) apicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t uVersion, uint32_t uPass)
AssertFailed();
return VINF_SUCCESS;
if (id == 0)
for (i = 0; i < APIC_LVT_NB; i++)
int rc;
uint32_t i;
bool fIOAPIC;
bool fGCEnabled;
bool fR0Enabled;
Log(("APIC: cCpus=%d fR0Enabled=%RTbool fGCEnabled=%RTbool fIOAPIC=%RTbool\n", cCpus, fR0Enabled, fGCEnabled, fIOAPIC));
rc = MMHyperAlloc(pVM, cCpus * sizeof(APICState), 1, MM_TAG_PDM_DEVICE_USER, (void **)&pThis->paLapicsR3);
return VERR_NO_MEMORY;
for (i = 0; i < cCpus; i++)
if (fGCEnabled) {
if (fR0Enabled) {
return rc;
if (fGCEnabled) {
return rc;
if (fR0Enabled) {
return rc;
for (i = 0; i < cCpus; i++) {
return rc;
return rc;
#ifdef VBOX_WITH_STATISTICS
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadGC, STAMTYPE_COUNTER, "/Devices/APIC/MMIOReadGC", STAMUNIT_OCCURENCES, "Number of APIC MMIO reads in GC.");
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadHC, STAMTYPE_COUNTER, "/Devices/APIC/MMIOReadHC", STAMUNIT_OCCURENCES, "Number of APIC MMIO reads in HC.");
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteGC, STAMTYPE_COUNTER, "/Devices/APIC/MMIOWriteGC", STAMUNIT_OCCURENCES, "Number of APIC MMIO writes in GC.");
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteHC, STAMTYPE_COUNTER, "/Devices/APIC/MMIOWriteHC", STAMUNIT_OCCURENCES, "Number of APIC MMIO writes in HC.");
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatClearedActiveIrq,STAMTYPE_COUNTER, "/Devices/APIC/MaskedActiveIRQ", STAMUNIT_OCCURENCES, "Number of cleared irqs.");
for (i = 0; i < cCpus; i++) {
PDMDevHlpSTAMRegisterF(pDevIns, &pApic->StatTimerSetInitialCount, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Calls to apicTimerSetInitialCount.", "/Devices/APIC/%u/TimerSetInitialCount", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pApic->StatTimerSetInitialCountArm, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "TMTimerSetRelative calls.", "/Devices/APIC/%u/TimerSetInitialCount/Arm", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pApic->StatTimerSetInitialCountDisarm, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "TMTimerStop calls.", "/Devices/APIC/%u/TimerSetInitialCount/Disasm", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pApic->StatTimerSetLvt, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Calls to apicTimerSetLvt.", "/Devices/APIC/%u/TimerSetLvt", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pApic->StatTimerSetLvtClearPeriodic, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Clearing APIC_LVT_TIMER_PERIODIC.", "/Devices/APIC/%u/TimerSetLvt/ClearPeriodic", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pApic->StatTimerSetLvtPostponed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "TMTimerStop postponed.", "/Devices/APIC/%u/TimerSetLvt/Postponed", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pApic->StatTimerSetLvtArmed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "TMTimerSet avoided.", "/Devices/APIC/%u/TimerSetLvt/Armed", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pApic->StatTimerSetLvtArm, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "TMTimerSet necessary.", "/Devices/APIC/%u/TimerSetLvt/Arm", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pApic->StatTimerSetLvtArmRetries, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "TMTimerSet retries.", "/Devices/APIC/%u/TimerSetLvt/ArmRetries", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pApic->StatTimerSetLvtNoRelevantChange,STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "No relevant flags changed.", "/Devices/APIC/%u/TimerSetLvt/NoRelevantChange", i);
return VINF_SUCCESS;
PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
sizeof(APICState),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
PDMBOTHCBDECL(int) ioapicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
switch (cb) {
IOAPIC_UNLOCK(s);
return VERR_INTERNAL_ERROR;
IOAPIC_UNLOCK(s);
return VINF_SUCCESS;
PDMBOTHCBDECL(int) ioapicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
switch (cb) {
IOAPIC_UNLOCK(s);
return VERR_INTERNAL_ERROR;
return VINF_SUCCESS;
#ifdef IN_RING3
return VINF_SUCCESS;
static DECLCALLBACK(int) ioapicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t uVersion, uint32_t uPass)
AssertFailed();
return VINF_SUCCESS;
ioapic_reset(s);
IOAPIC_UNLOCK(s);
bool fGCEnabled;
bool fR0Enabled;
int rc;
ioapic_reset(s);
s->id = 0;
return rc;
return rc;
if (fGCEnabled) {
return rc;
if (fR0Enabled) {
return rc;
return rc;
#ifdef VBOX_WITH_STATISTICS
PDMDevHlpSTAMRegister(pDevIns, &s->StatMMIOReadGC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOReadGC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO reads in GC.");
PDMDevHlpSTAMRegister(pDevIns, &s->StatMMIOReadHC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOReadHC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO reads in HC.");
PDMDevHlpSTAMRegister(pDevIns, &s->StatMMIOWriteGC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOWriteGC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO writes in GC.");
PDMDevHlpSTAMRegister(pDevIns, &s->StatMMIOWriteHC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOWriteHC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO writes in HC.");
PDMDevHlpSTAMRegister(pDevIns, &s->StatSetIrqGC, STAMTYPE_COUNTER, "/Devices/IOAPIC/SetIrqGC", STAMUNIT_OCCURENCES, "Number of IOAPIC SetIrq calls in GC.");
PDMDevHlpSTAMRegister(pDevIns, &s->StatSetIrqHC, STAMTYPE_COUNTER, "/Devices/IOAPIC/SetIrqHC", STAMUNIT_OCCURENCES, "Number of IOAPIC SetIrq calls in HC.");
return VINF_SUCCESS;
PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
sizeof(IOAPICState),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,