DevACPI.cpp revision 5835c306f653aa74c80f79f84a32451fe0ba37c0
/* $Id$ */
/** @file
* DevACPI - Advanced Configuration and Power Interface (ACPI) Device.
*/
/*
* Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_ACPI
#include <iprt/asm-math.h>
#ifdef IN_RING3
#endif /* IN_RING3 */
#include "VBoxDD.h"
#ifdef LOG_ENABLED
# define DEBUG_ACPI
#endif
#if defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
#endif /* !IN_RING3 */
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#ifdef IN_RING3
/** Locks the device state, ring-3 only. */
# define DEVACPI_LOCK_R3(a_pThis) \
do { \
} while (0)
#endif
/** Unlocks the device state (all contexts). */
#define DEVACPI_UNLOCK(a_pThis) \
#define DEBUG_HEX 0x3000
#define DEBUG_CHR 0x3001
#define PM_TMR_FREQ 3579545
/* Default base for PM PIIX4 device */
#define PM_PORT_BASE 0x4000
/* Port offsets in PM device */
enum
{
PM1a_EVT_OFFSET = 0x00,
PM1a_CTL_OFFSET = 0x04,
PM_TMR_OFFSET = 0x08,
GPE0_OFFSET = 0x20,
};
#define BAT_INDEX 0x00004040
#define BAT_DATA 0x00004044
#define SYSI_INDEX 0x00004048
#define SYSI_DATA 0x0000404c
#define ACPI_RESET_BLK 0x00004050
/* PM1x status register bits */
/* PM1x enable register bits */
#define IGN_EN 0
/* PM1x control register bits */
#define SLP_TYPx_SHIFT 10
#define SLP_TYPx_MASK 7
#define GPE0_BATTERY_INFO_CHANGED RT_BIT(0)
enum
{
};
enum
{
};
enum
{
SYSTEM_INFO_INDEX_CPU_LOCKED = 11, /**< Contains a flag indicating whether the CPU is locked or not */
SYSTEM_INFO_INDEX_END = 26,
SYSTEM_INFO_INDEX_INVALID = 0x80,
SYSTEM_INFO_INDEX_VALID = 0x200
};
#define AC_OFFLINE 0
#define AC_ONLINE 1
#define BAT_TECH_PRIMARY 1
#define BAT_TECH_SECONDARY 2
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* The ACPI device state.
*/
typedef struct ACPIState
{
/** Critical section protecting the ACPI state. */
/** Number of logical CPUs in guest */
unsigned int uBatteryIndex;
unsigned int uSystemInfoIndex;
/** The number of bytes above 4GB. */
/** The number of bytes below 4GB. */
/** Current ACPI S* state. We support S0 and S5. */
/** This is a workaround for incorrect index field handling by Intels ACPICA.
* The system info _INI method writes to offset 0x200. We either observe a
* write request to index 0x80 (in that case we don't change the index) or a
* write request to offset 0x200 (in that case we divide the index value by
* 4. Note that the _STA method is sometimes called prior to the _INI method
* (ACPI spec 6.3.7, _STA). See the special case for BAT_DEVICE_STATUS in
* acpiBatIndexWrite() for handling this. */
/** provide a floppy controller */
bool fUseFdc;
/** If High Precision Event Timer device should be supported */
bool fUseHpet;
/** If System Management Controller device should be supported */
bool fUseSmc;
/** the guest handled the last power button event */
bool fPowerButtonHandled;
/** If ACPI CPU device should be shown */
bool fShowCpu;
/** If Real Time Clock ACPI object to be shown */
bool fShowRtc;
/** I/O port address of PM device. */
/** Flag whether the GC part of the device is enabled. */
bool fGCEnabled;
/** Flag whether the R0 part of the device is enabled. */
bool fR0Enabled;
/** Array of flags of attached CPUs */
/** Which CPU to check for the locked status. */
/** Mask of locked CPUs (used by the guest). */
/** The CPU event type. */
/** The CPU id affected. */
/** Flag whether CPU hot plugging is enabled. */
bool fCpuHotPlug;
/** If MCFG ACPI table shown to the guest */
bool fUseMcfg;
/** Primary NIC PCI address. */
/** Primary audio card PCI address. */
/** Flag whether S1 power state is enabled. */
bool fS1Enabled;
/** Flag whether S4 power state is enabled. */
bool fS4Enabled;
/** Flag whether S1 triggers a state save. */
bool fSuspendToSavedState;
/** Flag whether to set WAK_STS on resume (restore included). */
bool fSetWakeupOnResume;
/** PCI address of the IO controller device. */
/** PCI address of the host bus controller device. */
/* Physical address of PCI config space MMIO region */
/* Length of PCI config space MMIO region */
/** Serial 0 IRQ number */
/** Serial 1 IRQ number */
/** Serial 0 IO port base */
/** Serial 1 IO port base */
/** ACPI port base interface. */
/** ACPI port interface. */
/** Pointer to the device instance. */
/** Pointer to the driver base interface. */
/** Pointer to the driver connector interface. */
/** Pointer to default PCI config read function. */
/** Pointer to default PCI config write function. */
} ACPIState;
#pragma pack(1)
/** Generic Address Structure (see ACPIspec 3.0, 5.2.3.1) */
struct ACPIGENADDR
{
};
/** Root System Description Pointer */
struct ACPITBLRSDP
{
};
/** System Description Table Header */
struct ACPITBLHEADER
{
};
/** Root System Description Table */
struct ACPITBLRSDT
{
};
/** Extended System Description Table */
struct ACPITBLXSDT
{
};
/** Fixed ACPI Description Table */
struct ACPITBLFADT
{
#define INT_MODEL_MULTIPLE_APIC 2
#define SCI_INT 9
#define SMI_CMD 0x0000442e
#define ACPI_ENABLE 0xa1
#define ACPI_DISABLE 0xa0
state control responsibility */
#define GPE0_BLK_LEN 2
#define GPE1_BLK_LEN 0
#define GPE1_BASE 0
lines from any processors memory caches */
#define FLUSH_SIZE 0 /**< Ignored if WBVIND set in FADT_FLAGS */
#define FLUSH_STRIDE 0 /**< Ignored if WBVIND set in FADT_FLAGS */
(COM too?) */
/** Start of the ACPI 2.0 extension. */
#define ACPI_RESET_REG_VAL 0x10
};
/** Firmware ACPI Control Structure */
struct ACPITBLFACS
{
};
/** Processor Local APIC Structure */
struct ACPITBLLAPIC
{
#define LAPIC_ENABLED 0x1
};
/** I/O APIC Structure */
struct ACPITBLIOAPIC
{
};
/** Interrupt Source Override Structure */
struct ACPITBLISO
{
};
#define NUMBER_OF_IRQ_SOURCE_OVERRIDES 2
/** HPET Descriptor Structure */
struct ACPITBLHPET
{
[31:16] PCI vendor ID of first timer block
[15] legacy replacement IRQ routing capable
[14] reserved
[13] COUNT_SIZE_CAP counter size
[12:8] number of comparators in first timer block
[7:0] hardware rev ID */
lost interrupts while the counter is programmed
to operate in periodic mode. Unit: clock tick. */
};
/** MCFG Descriptor Structure */
typedef struct ACPITBLMCFG
{
} ACPITBLMCFG;
/** Number of such entries can be computed from the whole table length in header */
typedef struct ACPITBLMCFGENTRY
{
# ifdef IN_RING3 /** @todo r=bird: Move this down to where it's used. */
/**
* Multiple APIC Description Table.
*
* This structure looks somewhat convoluted due layout of MADT table in MP case.
* There extpected to be multiple LAPIC records for each CPU, thus we cannot
* use regular C structure and proxy to raw memory instead.
*/
class AcpiTableMADT
{
/**
* All actual data stored in dynamically allocated memory pointed by this field.
*/
/**
* Number of CPU entries in this MADT.
*/
/**
* Number of interrupt overrides.
*/
public:
/**
* Address of ACPI header
*/
inline ACPITBLHEADER *header_addr(void) const
{
return (ACPITBLHEADER *)m_pbData;
}
/**
* Address of local APIC for each CPU. Note that different CPUs address different LAPICs,
* although address is the same for all of them.
*/
inline uint32_t *u32LAPIC_addr(void) const
{
}
/**
* Address of APIC flags
*/
inline uint32_t *u32Flags_addr(void) const
{
}
/**
* Address of ISO description
*/
inline ACPITBLISO *ISO_addr(void) const
{
}
/**
* Address of per-CPU LAPIC descriptions
*/
inline ACPITBLLAPIC *LApics_addr(void) const
{
}
/**
* Address of IO APIC description
*/
inline ACPITBLIOAPIC *IOApic_addr(void) const
{
}
/**
* Size of MADT.
* Note that this function assumes IOApic to be the last field in structure.
*/
{
}
/**
* Raw data of MADT.
*/
{
return m_pbData;
}
/**
* Size of MADT for given ACPI config, useful to compute layout.
*/
{
}
/*
* Constructor, only works in Ring 3, doesn't look like a big deal.
*/
{
}
{
}
};
# endif /* IN_RING3 */
#pragma pack()
#ifndef VBOX_DEVICE_STRUCT_TESTCASE /* exclude the rest of the file */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
PDMBOTHCBDECL(int) acpiPMTmrRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
#ifdef IN_RING3
#endif
#ifdef IN_RING3
/* SCI IRQ */
{
}
{
}
{
}
{
}
{
}
/**
* Used by acpiPM1aStsWrite, acpiPM1aEnWrite, acpiPmTimer,
* acpiPort_PowerBuffonPress and acpiPort_SleepButtonPress to
* update the GPE0.STS and GPE0.EN registers and trigger IRQs.
*
* Caller must hold the state lock.
*
* @param pThis The ACPI instance.
* @param sts The new GPE0.STS value.
* @param en The new GPE0.EN value.
*/
{
if (gpe0_level(pThis))
return;
}
/**
* Used by acpiGpe0StsWrite, acpiGpe0EnWrite, acpiAttach and acpiDetach to
* update the GPE0.STS and GPE0.EN registers and trigger IRQs.
*
* Caller must hold the state lock.
*
* @param pThis The ACPI instance.
* @param sts The new GPE0.STS value.
* @param en The new GPE0.EN value.
*/
{
if (pm1a_level(pThis))
return;
}
/**
* Used by acpiPM1aCtlWrite to power off the VM.
*
* @param pThis The ACPI instance.
* @returns Strict VBox status code.
*/
{
if (RT_FAILURE(rc))
return rc;
}
/**
* Used by acpiPM1aCtlWrite to put the VM to sleep.
*
* @param pThis The ACPI instance.
* @returns Strict VBox status code.
*/
{
/* We must set WAK_STS on resume (includes restore) so the guest knows that
we've woken up and can continue executing code. The guest is probably
reading the PMSTS register in a loop to check this. */
int rc;
pThis->fSetWakeupOnResume = true;
if (pThis->fSuspendToSavedState)
{
if (rc != VERR_NOT_SUPPORTED)
else
{
LogRel(("ACPI: PDMDevHlpVMSuspendSaveAndPowerOff is not supported, falling back to suspend-only\n"));
}
}
else
{
}
return rc;
}
/**
* @interface_method_impl{PDMIACPIPORT,pfnPowerButtonPress}
*/
{
Log(("acpiPort_PowerButtonPress: handled=%d status=%x\n", pThis->fPowerButtonHandled, pThis->pm1a_sts));
pThis->fPowerButtonHandled = false;
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMIACPIPORT,pfnGetPowerButtonHandled}
*/
{
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMIACPIPORT,pfnGetGuestEnteredACPIMode, Check if the
* Guest entered into G0 (working) or G1 (sleeping)}
*/
static DECLCALLBACK(int) acpiPort_GetGuestEnteredACPIMode(PPDMIACPIPORT pInterface, bool *pfEntered)
{
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMIACPIPORT,pfnGetCpuStatus}
*/
static DECLCALLBACK(int) acpiPort_GetCpuStatus(PPDMIACPIPORT pInterface, unsigned uCpu, bool *pfLocked)
{
return VINF_SUCCESS;
}
/**
* Send an ACPI sleep button event.
*
* @returns VBox status code
* @param pInterface Pointer to the interface structure containing the called function pointer.
*/
{
return VINF_SUCCESS;
}
/**
* Used by acpiPmTimer to re-arm the PM timer.
*
* The caller is expected to either hold the clock lock or to have made sure
* the VM is resetting or loading state.
*
* @param pThis The ACPI instance.
* @param uNow The current time.
*/
{
}
/**
* @callback_method_impl{FNTMTIMERDEV, PM Timer callback}
*/
{
Log(("acpi: pm timer sts %#x (%d), en %#x (%d)\n",
}
/**
* _BST method - used by acpiBatDataRead to implement BAT_STATUS_STATE and
* acpiLoadState.
*
* @returns VINF_SUCCESS.
* @param pThis The ACPI instance.
*/
{
bool fPresent; /* battery present? */
int rc;
return VINF_SUCCESS;
/* default values */
/* did we get a valid battery state? */
p[BAT_STATUS_PRESENT_RATE] = 0; /* mV */
return VINF_SUCCESS;
}
/**
* _BIF method - used by acpiBatDataRead to implement BAT_INFO_UNITS and
* acpiLoadState.
*
* @returns VINF_SUCCESS.
* @param pThis The ACPI instance.
*/
{
p[BAT_INFO_UNITS] = 0; /* mWh */
return VINF_SUCCESS;
}
/**
* The _STA method - used by acpiBatDataRead to implement BAT_DEVICE_STATUS.
*
* @returns status mask or 0.
* @param pThis The ACPI instance.
*/
{
bool fPresent; /* battery present? */
int rc;
return 0;
return fPresent
? STA_DEVICE_PRESENT_MASK /* present */
| STA_DEVICE_ENABLED_MASK /* enabled and decodes its resources */
| STA_DEVICE_SHOW_IN_UI_MASK /* should be shown in UI */
| STA_DEVICE_FUNCTIONING_PROPERLY_MASK /* functioning properly */
| STA_BATTERY_PRESENT_MASK /* battery is present */
: 0; /* device not present */
}
/**
* Used by acpiBatDataRead to implement BAT_POWER_SOURCE.
*
* @returns status.
* @param pThis The ACPI instance.
*/
{
/* query the current power source from the host driver */
return AC_ONLINE;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, Battery status index}
*/
PDMBOTHCBDECL(int) acpiBatIndexWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
if (cb != 4)
/* see comment at the declaration of u8IndexShift */
{
u32 >>= 2;
}
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTIN, Battery status data}
*/
PDMBOTHCBDECL(int) acpiBatDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb != 4)
return VERR_IOM_IOPORT_UNUSED;
int rc = VINF_SUCCESS;
switch (pThis->uBatteryIndex)
{
case BAT_STATUS_STATE:
/* fall thru */
case BAT_STATUS_PRESENT_RATE:
break;
case BAT_INFO_UNITS:
/* fall thru */
case BAT_INFO_DESIGN_CAPACITY:
case BAT_INFO_TECHNOLOGY:
case BAT_INFO_DESIGN_VOLTAGE:
break;
case BAT_DEVICE_STATUS:
break;
case BAT_POWER_SOURCE:
break;
default:
rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "cb=%d Port=%u idx=%u\n", cb, Port, pThis->uBatteryIndex);
*pu32 = UINT32_MAX;
break;
}
return rc;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, System info index}
*/
PDMBOTHCBDECL(int) acpiSysInfoIndexWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
if (cb != 4)
else
{
/* see comment at the declaration of u8IndexShift */
{
}
}
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTIN, System info data}
*/
PDMBOTHCBDECL(int) acpiSysInfoDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb != 4)
return VERR_IOM_IOPORT_UNUSED;
int rc = VINF_SUCCESS;
switch (uSystemInfoIndex)
{
break;
break;
break;
: 0;
break;
/* no need to show this device in the UI */
: 0;
break;
: 0;
break;
break;
break;
if (pThis->fS4Enabled)
break;
break;
break;
/** @todo couldn't MCFG be in 64-bit range? */
break;
/** @todo couldn't MCFG be in 64-bit range? */
break;
/* This is only for compatibility with older saved states that
may include ACPI code that read these values. Legacy is
a wonderful thing, isn't it? :-) */
: 0;
break;
: 0;
break;
{
}
else
{
rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "CPU lock check protocol violation (idCpuLockCheck=%#x)\n",
/* Always return locked status just to be safe */
*pu32 = 1;
}
break;
break;
break;
break;
break;
break;
break;
case SYSTEM_INFO_INDEX_END:
/** @todo why isn't this setting any output value? */
break;
/* Solaris 9 tries to read from this index */
*pu32 = 0;
break;
default:
*pu32 = UINT32_MAX;
rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "cb=%d Port=%u idx=%u\n", cb, Port, pThis->uBatteryIndex);
break;
}
return rc;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, System info data}
*/
PDMBOTHCBDECL(int) acpiSysInfoDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
if (cb != 4)
return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "cb=%d Port=%u u32=%#x idx=%u\n", cb, Port, u32, pThis->uSystemInfoIndex);
int rc = VINF_SUCCESS;
switch (pThis->uSystemInfoIndex)
{
pThis->u8IndexShift = 0;
break;
case SYSTEM_INFO_INDEX_VALID:
break;
break;
else
break;
default:
rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "cb=%d Port=%u u32=%#x idx=%u\n", cb, Port, u32, pThis->uSystemInfoIndex);
break;
}
return rc;
}
/**
* @callback_method_impl{FNIOMIOPORTIN, PM1a Enable}
*/
PDMBOTHCBDECL(int) acpiPm1aEnRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb != 2)
return VERR_IOM_IOPORT_UNUSED;
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, PM1a Enable}
*/
PDMBOTHCBDECL(int) acpiPM1aEnWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
u32 &= 0xffff;
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTIN, PM1a Status}
*/
PDMBOTHCBDECL(int) acpiPm1aStsRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb != 2)
{
}
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, PM1a Status}
*/
PDMBOTHCBDECL(int) acpiPM1aStsWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
u32 &= 0xffff;
if (u32 & PWRBTN_STS)
pThis->fPowerButtonHandled = true; /* Remember that the guest handled the last power button event */
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTIN, PM1a Control}
*/
PDMBOTHCBDECL(int) acpiPm1aCtlRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb != 2)
{
}
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, PM1a Control}
*/
PDMBOTHCBDECL(int) acpiPM1aCtlWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
u32 &= 0xffff;
int rc = VINF_SUCCESS;
{
switch (uSleepState)
{
case 0x00: /* S0 */
break;
case 0x01: /* S1 */
if (pThis->fS1Enabled)
{
LogRel(("Entering S1 power state (powered-on suspend)\n"));
break;
}
LogRel(("Ignoring guest attempt to enter S1 power state (powered-on suspend)!\n"));
/* fall thru */
case 0x04: /* S4 */
if (pThis->fS4Enabled)
{
LogRel(("Entering S4 power state (suspend to disk)\n"));
break;
}
LogRel(("Ignoring guest attempt to enter S4 power state (suspend to disk)!\n"));
/* fall thru */
case 0x05: /* S5 */
LogRel(("Entering S5 power state (power down)\n"));
break;
default:
rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Unknown sleep state %#x (u32=%#x)\n", uSleepState, u32);
break;
}
}
return rc;
}
#endif /* IN_RING3 */
/**
* @callback_method_impl{FNIOMIOPORTIN, PMTMR}
*
* @remarks Only I/O port currently implemented in all contexts.
*/
PDMBOTHCBDECL(int) acpiPMTmrRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb != 4)
return VERR_IOM_IOPORT_UNUSED;
/*
* We use the clock lock to serialize access to u64PmTimerInitial and to
* make sure we get a reliable time from the clock.
*/
if (rc == VINF_SUCCESS)
{
/*
* Calculate the return value.
*/
*pu32 = ASMMultU64ByU32DivByU32(u64Elapsed, PM_TMR_FREQ, TMTimerGetFreq(pThis->CTX_SUFF(pPmTimer)));
}
return rc;
}
#ifdef IN_RING3
/**
* @callback_method_impl{FNIOMIOPORTIN, GPE0 Status}
*/
PDMBOTHCBDECL(int) acpiGpe0StsRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb != 1)
{
}
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, GPE0 Status}
*/
PDMBOTHCBDECL(int) acpiGpe0StsWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
if (cb != 1)
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTIN, GPE0 Enable}
*/
PDMBOTHCBDECL(int) acpiGpe0EnRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb != 1)
{
}
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, GPE0 Enable}
*/
PDMBOTHCBDECL(int) acpiGpe0EnWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
if (cb != 1)
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, SMI_CMD}
*/
PDMBOTHCBDECL(int) acpiSmiWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
if (cb != 1)
if (u32 == ACPI_ENABLE)
else if (u32 == ACPI_DISABLE)
else
return VINF_SUCCESS;
}
/**
* @{FNIOMIOPORTOUT, ACPI_RESET_BLK}
*/
PDMBOTHCBDECL(int) acpiResetWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
if (cb != 1)
/* No state locking required. */
int rc = VINF_SUCCESS;
if (u32 == ACPI_RESET_REG_VAL)
else
return rc;
}
# ifdef DEBUG_ACPI
/**
* @callback_method_impl{FNIOMIOPORTOUT, Debug hex value logger}
*/
PDMBOTHCBDECL(int) acpiDhexWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 1:
break;
case 2:
case 4:
break;
default:
}
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT, Debug char logger}
*/
PDMBOTHCBDECL(int) acpiDchrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 1:
break;
default:
}
return VINF_SUCCESS;
}
# endif /* DEBUG_ACPI */
/**
* Used to calculate the value of a PM I/O port.
*
* @returns The actual I/O port value.
* @param pThis The ACPI instance.
* @param offset The offset into the I/O space, or -1 if invalid.
*/
{
if (offset == -1)
return 0;
}
/**
* Called by acpiLoadState and acpiUpdatePmHandlers to register the PM1a, PM
* timer and GPE0 I/O ports.
*
* @returns VBox status code.
* @param pThis The ACPI instance.
*/
{
int rc = VINF_SUCCESS;
do { \
rc = PDMDevHlpIOPortRegister(pThis->pDevIns, acpiCalcPmPort(pThis, offset), cnt, pThis, writer, reader, \
if (RT_FAILURE(rc)) \
return rc; \
} while (0)
#define L (GPE0_BLK_LEN / 2)
#undef L
#undef R
/* register RC stuff */
if (pThis->fGCEnabled)
{
}
/* register R0 stuff */
if (pThis->fR0Enabled)
{
}
return rc;
}
/**
* Called by acpiLoadState and acpiUpdatePmHandlers to unregister the PM1a, PM
* timer and GPE0 I/O ports.
*
* @returns VBox status code.
* @param pThis The ACPI instance.
*/
{
do { \
} while (0)
#define L (GPE0_BLK_LEN / 2)
U(PM1a_EVT_OFFSET, 1);
U(PM1a_CTL_OFFSET, 1);
U(PM_TMR_OFFSET, 1);
U(GPE0_OFFSET + L, L);
U(GPE0_OFFSET, L);
#undef L
#undef U
return VINF_SUCCESS;
}
/**
* Called by acpiPciConfigWrite and acpiReset to change the location of the
* PM1a, PM timer and GPE0 ports.
*
* @returns VBox status code.
*
* @param pThis The ACPI instance.
* @param NewIoPortBase The new base address of the I/O ports.
*/
{
{
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
/* We have to update FADT table acccording to the new base */
if (RT_FAILURE(rc))
return rc;
}
return VINF_SUCCESS;
}
/**
* Saved state structure description, version 4.
*/
static const SSMFIELD g_AcpiSavedStateFields4[] =
{
};
/**
* Saved state structure description, version 5.
*/
static const SSMFIELD g_AcpiSavedStateFields5[] =
{
};
/**
* Saved state structure description, version 6.
*/
static const SSMFIELD g_AcpiSavedStateFields6[] =
{
};
/**
* @callback_method_impl{FNSSMDEVSAVEEXEC}
*/
{
}
/**
* @callback_method_impl{FNSSMDEVLOADEXEC}
*/
{
/*
* Unregister PM handlers, will register with actual base after state
* successfully loaded.
*/
if (RT_FAILURE(rc))
return rc;
switch (uVersion)
{
case 4:
break;
case 5:
break;
case 6:
break;
default:
break;
}
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
}
return rc;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/**
* Calculate the check sum for some ACPI data before planting it.
*
* All the bytes must add up to 0.
*
* @returns check sum.
* @param pvSrc What to check sum.
* @param cbData The amount of data to checksum.
*/
{
return -uSum;
}
/**
* Prepare a ACPI table header.
*/
{
}
/**
* Initialize a generic address structure (ACPIGENADDR).
*/
{
g->u8AccessSize = u8AccessSize;
}
/**
* Wrapper around PDMDevHlpPhysWrite used when planting ACPI tables.
*/
{
}
/**
* Plant the Differentiated System Description Table (DSDT).
*/
{
}
/**
* Plan the Secondary System Description Table (SSDT).
*/
{
}
/**
* Plant the Firmware ACPI Control Structure (FACS).
*/
{
}
/**
* Plant the Fixed ACPI Description Table (FADT aka FACP).
*/
static void acpiSetupFADT(ACPIState *pThis, RTGCPHYS32 GCPhysAcpi1, RTGCPHYS32 GCPhysAcpi2, RTGCPHYS32 GCPhysFacs, RTGCPHYS GCPhysDsdt)
{
/* First the ACPI version 2+ version of the structure. */
fadt.u8S4BIOSReq = 0;
fadt.u8PStateCnt = 0;
fadt.u8PM2CTLLEN = 0;
fadt.u8DutyOffset = 0;
fadt.u8DutyWidth = 0;
fadt.u8DayAlarm = 0;
fadt.u8MonAlarm = 0;
/** @note WBINVD is required for ACPI versions newer than 1.0 */
/* We have to force physical APIC mode or Linux can't use more than 8 CPUs */
if (pThis->fCpuHotPlug)
/* Now the ACPI 1.0 version. */
}
/**
* Plant the root System Description Table.
*
* The RSDT and XSDT tables are basically identical. The only difference is 32
* vs 64 bits addresses for description headers. RSDT is for ACPI 1.0. XSDT for
* ACPI 2.0 and up.
*/
static int acpiSetupRSDT(ACPIState *pThis, RTGCPHYS32 addr, unsigned int nb_entries, uint32_t *addrs)
{
if (!rsdt)
for (unsigned int i = 0; i < nb_entries; ++i)
{
}
return VINF_SUCCESS;
}
/**
* Plant the Extended System Description Table.
*/
static int acpiSetupXSDT(ACPIState *pThis, RTGCPHYS32 addr, unsigned int nb_entries, uint32_t *addrs)
{
if (!xsdt)
return VERR_NO_TMP_MEMORY;
for (unsigned int i = 0; i < nb_entries; ++i)
{
}
return VINF_SUCCESS;
}
/**
* Plant the Root System Description Pointer (RSDP).
*/
{
/* ACPI 1.0 part (RSDT */
/* ACPI 2.0 part (XSDT) */
}
/**
* Plant the Multiple APIC Description Table (MADT).
*
* @note APIC without IO-APIC hangs Windows Vista therefore we setup both.
*
* @todo All hardcoded, should set this up based on the actual VM config!!!!!
*/
{
/* LAPICs records */
{
/** Must match numbering convention in MPTABLES */
lapic++;
}
/* IO-APIC record */
/** Must match MP tables ID */
ioapic->u8Reserved = 0;
/* Interrupt Source Overrides */
/* Flags:
bits[3:2]:
00 conforms to the bus
01 edge-triggered
10 reserved
11 level-triggered
bits[1:0]
00 conforms to the bus
01 active-high
10 reserved
11 active-low */
/* If changing, also update PDMIsaSetIrq() and MPS */
/* Timer interrupt rule IRQ0 to GSI2 */
/* ACPI interrupt rule - IRQ9 to GSI9 */
}
/**
* Plant the High Performance Event Timer (HPET) descriptor.
*/
{
/* Keep base address consistent with appropriate DSDT entry (vbox.dsl) */
0 /* Memory address space */,
64 /* Register bit width */,
0 /* Bit offset */,
0, /* Register access size, is it correct? */
0xfed00000 /* Address */);
hpet.u8Attributes = 0;
}
/**
* Used by acpiPlantTables to plant a MMCONFIG PCI config space access (MCFG)
* descriptor.
*
* @param pThis The ACPI instance.
* @param GCPhysDst Where to plant it.
*/
{
struct
{
} tbl;
uint8_t u8StartBus = 0;
// u16PciSegmentGroup must match _SEG in ACPI table
}
/**
* Used by acpiPlantTables and acpiConstruct.
*
* @returns Guest memory address.
*/
static uint32_t find_rsdp_space(void)
{
return 0xe0000;
}
/**
* Create the ACPI tables in guest memory.
*/
{
int rc;
RTGCPHYS32 GCPhysCur, GCPhysRsdt, GCPhysXsdt, GCPhysFadtAcpi1, GCPhysFadtAcpi2, GCPhysFacs, GCPhysDsdt;
RTGCPHYS32 GCPhysHpet = 0;
RTGCPHYS32 GCPhysApic = 0;
RTGCPHYS32 GCPhysSsdt = 0;
RTGCPHYS32 GCPhysMcfg = 0;
if (pThis->u8UseIOApic)
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"RamSize\" as integer failed"));
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"RamHoleSize\" as integer failed"));
/*
* Calculate the sizes for the high and low regions.
*/
{
/* Note: This is also enforced by DevPcBios.cpp. */
}
GCPhysCur = 0;
if (pThis->u8UseIOApic)
{
GCPhysCur = RT_ALIGN_32(GCPhysCur + AcpiTableMADT::sizeFor(pThis, NUMBER_OF_IRQ_SOURCE_OVERRIDES), 16);
}
{
}
{
/* Assume one entry */
}
void *pvSsdtCode = NULL;
size_t cbSsdtSize = 0;
if (RT_FAILURE(rc))
return rc;
void *pvDsdtCode = NULL;
size_t cbDsdtSize = 0;
if (RT_FAILURE(rc))
return rc;
if (GCPhysCur > 0x10000)
N_("Error: ACPI tables bigger than 64KB"));
Log(("FACS 0x%08X FADT (1.0) 0x%08X, FADT (2+) 0x%08X\n", GCPhysFacs + addend, GCPhysFadtAcpi1 + addend, GCPhysFadtAcpi2 + addend));
if (pThis->u8UseIOApic)
Log(("\n"));
acpiSetupFADT(pThis, GCPhysFadtAcpi1 + addend, GCPhysFadtAcpi2 + addend, GCPhysFacs + addend, GCPhysDsdt + addend);
if (pThis->u8UseIOApic)
{
}
{
}
{
}
if (RT_FAILURE(rc))
return rc;
}
/**
* @callback_method_impl{FNPCICONFIGREAD}
*/
{
}
/**
* @callback_method_impl{FNPCICONFIGWRITE}
*/
static DECLCALLBACK(void) acpiPciConfigWrite(PPCIDEVICE pPciDev, uint32_t Address, uint32_t u32Value, unsigned cb)
{
if (Address == VBOX_PCI_INTERRUPT_LINE)
{
Log(("acpi: ignore interrupt line settings: %d, we'll use hardcoded value %d\n", u32Value, SCI_INT));
}
/* PMREGMISC written */
if (Address == 0x80)
{
/* Check Power Management IO Space Enable (PMIOSE) bit */
{
NewIoPortBase &= 0xffc0;
}
}
}
/**
* Attach a new CPU.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being attached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*
* @remarks This code path is not used during construction.
*/
{
("Hot-plug flag is not set\n"),
/* Check if it was already attached */
int rc = VINF_SUCCESS;
{
if (RT_SUCCESS(rc))
{
/* Enable the CPU */
/*
* Lock the CPU because we don't know if the guest will use it or not.
* Prevents ejection while the CPU is still used
*/
/* Notify the guest */
}
}
return rc;
}
/**
* Detach notification.
*
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
{
("Hot-plug flag is not set\n"));
/* Check if it was already detached */
{
{
/* Disable the CPU */
/* Notify the guest */
}
else
AssertMsgFailed(("CPU is still locked by the guest\n"));
}
}
/**
* @interface_method_impl{PDMDEVREG,pfnResume}
*/
{
if (pThis->fSetWakeupOnResume)
{
Log(("acpiResume: setting WAK_STS\n"));
pThis->fSetWakeupOnResume = false;
}
}
/**
* @interface_method_impl{PDMDEVREG,pfnReset}
*/
{
pThis->uBatteryIndex = 0;
pThis->uSystemInfoIndex = 0;
pThis->uSleepState = 0;
/** @todo Should we really reset PM base? */
}
/**
* @interface_method_impl{PDMDEVREG,pfnRelocate}
*/
{
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
{
/*
* Init data and set defaults.
*/
/** @todo move more of the code up! */
pThis->u32CpuEventType = 0;
/* IBase */
/* IACPIPort */
/* Set the default critical section to NOP (related to the PM timer). */
/*
* Validate and read the configuration.
*/
if (!CFGMR3AreValuesValid(pCfg,
"RamSize\0"
"RamHoleSize\0"
"IOAPIC\0"
"NumCPUs\0"
"GCEnabled\0"
"R0Enabled\0"
"HpetEnabled\0"
"McfgEnabled\0"
"McfgBase\0"
"McfgLength\0"
"SmcEnabled\0"
"FdcEnabled\0"
"ShowRtc\0"
"ShowCpu\0"
"NicPciAddress\0"
"AudioPciAddress\0"
"IocPciAddress\0"
"HostBusPciAddress\0"
"EnableSuspendToDisk\0"
"PowerS1Enabled\0"
"PowerS4Enabled\0"
"CpuHotPlug\0"
"AmlFilePath\0"
"Serial0IoPortBase\0"
"Serial1IoPortBase\0"
"Serial0Irq\0"
"Serial1Irq\0"
))
N_("Configuration error: Invalid config key for ACPI device"));
/* query whether we are supposed to present an IOAPIC */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"IOAPIC\""));
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
/* query whether we are supposed to present an FDC controller */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"FdcEnabled\""));
/* query whether we are supposed to present HPET */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"HpetEnabled\""));
/* query MCFG configuration */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"McfgBase\""));
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"McfgLength\""));
/* query whether we are supposed to present SMC */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"SmcEnabled\""));
/* query whether we are supposed to present RTC object */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"ShowRtc\""));
/* query whether we are supposed to present CPU objects */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"ShowCpu\""));
/* query primary NIC PCI address */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"NicPciAddress\""));
/* query primary NIC PCI address */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"AudioPciAddress\""));
/* query IO controller (southbridge) PCI address */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"IocPciAddress\""));
/* query host bus controller PCI address */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"HostBusPciAddress\""));
/* query whether S1 power state should be exposed */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"PowerS1Enabled\""));
/* query whether S4 power state should be exposed */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"PowerS4Enabled\""));
/* query whether S1 power state should save the VM state */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"EnableSuspendToDisk\""));
/* query whether we are allow CPU hot plugging */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"CpuHotPlug\""));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pThis->fGCEnabled = true;
else if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"GCEnabled\""));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pThis->fR0Enabled = true;
else if (RT_FAILURE(rc))
N_("configuration error: failed to read R0Enabled as boolean"));
/* query serial info */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"Serial0Irq\""));
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"Serial0IoPortBase\""));
/* Serial 1 is enabled, get config data */
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"Serial1Irq\""));
if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"Serial1IoPortBase\""));
/* Try to attach the other CPUs */
{
if (pThis->fCpuHotPlug)
{
if (RT_SUCCESS(rc))
{
Log(("acpi: Attached CPU %u\n", i));
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
Log(("acpi: CPU %u not attached yet\n", i));
else
}
else
{
/* CPU is always attached if hot-plug is not enabled. */
}
}
/* Set default port base */
/*
* FDC and SMC try to use the same non-shareable interrupt (6),
* enable only one device.
*/
/*
* Plant ACPI tables.
*/
if (!GCPhysRsdp)
N_("Can not find space for RSDP. ACPI is disabled"));
if (RT_FAILURE(rc))
return rc;
PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "ACPI RSDP");
if (RT_FAILURE(rc))
return rc;
/*
* Register I/O ports.
*/
if (RT_FAILURE(rc))
return rc;
do { \
if (RT_FAILURE(rc)) \
return rc; \
} while (0)
#ifdef DEBUG_ACPI
#endif
#undef R
/*
* Create the PM timer.
*/
/*
* Set up the PCI device.
*/
/* See p. 50 of PIIX4 manual */
#if 0
int smb_io_base = 0xb100;
#endif
if (RT_FAILURE(rc))
return rc;
/*
* Register the saved state.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Get the corresponding connector interface
*/
if (RT_SUCCESS(rc))
{
N_("LUN #0 doesn't have an ACPI connector interface"));
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
Log(("acpi: %s/%d: warning: no driver attached to LUN #0!\n",
rc = VINF_SUCCESS;
}
else
return rc;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceACPI =
{
/* u32Version */
/* szName */
"acpi",
/* szRCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"Advanced Configuration and Power Interface",
/* fFlags */
/* fClass */
/* cMaxInstances */
~0,
/* cbInstance */
sizeof(ACPIState),
/* pfnConstruct */
/* pfnDestruct */
NULL,
/* pfnRelocate */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
NULL,
/* pfnResume */
/* pfnAttach */
/* pfnDetach */
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */