DevACPI.cpp revision b3d11a7c36de451aa57386fbcd874f020d870ecc
/* $Id$ */
/** @file
* DevACPI - Advanced Configuration and Power Interface (ACPI) Device.
*/
/*
* Copyright (C) 2006-2009 Sun Microsystems, Inc.
*
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_ACPI
#ifdef IN_RING3
#endif /* IN_RING3 */
#include "../Builtins.h"
#ifdef LOG_ENABLED
# define DEBUG_ACPI
#endif
#if defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
#endif /* !IN_RING3 */
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#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
{
SYSTEM_INFO_INDEX_END = 15,
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
{
/** 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;
/** Physical base address of the MADT table. */
/** Size of the MADT table */
/** Array of flags of attached CPUs */
/** Which CPU to check for the locked status. */
/** Mask of locked CPUs (used by the guest) */
/** Flag whether CPU hot plugging is enabled */
bool fCpuHotPlug;
/** Aligning IBase. */
bool afAlignment[4];
/** 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
{
};
# 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.
*/
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 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
PDMBOTHCBDECL(int) acpiPm1aEnRead( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) acpiPM1aEnWrite( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
PDMBOTHCBDECL(int) acpiPm1aStsRead( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) acpiPM1aStsWrite( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
PDMBOTHCBDECL(int) acpiPm1aCtlRead( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) acpiPM1aCtlWrite( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
PDMBOTHCBDECL(int) acpiSmiWrite( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
PDMBOTHCBDECL(int) acpiBatIndexWrite( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
PDMBOTHCBDECL(int) acpiBatDataRead( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) acpiSysInfoDataRead( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) acpiSysInfoDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
PDMBOTHCBDECL(int) acpiGpe0EnRead( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) acpiGpe0EnWrite( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
PDMBOTHCBDECL(int) acpiGpe0StsRead( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) acpiGpe0StsWrite( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
PDMBOTHCBDECL(int) acpiResetWrite( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
# ifdef DEBUG_ACPI
PDMBOTHCBDECL(int) acpiDhexWrite( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
PDMBOTHCBDECL(int) acpiDchrWrite( PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
# endif
#endif /* IN_RING3 */
#ifdef IN_RING3
{
if (offset == -1)
return 0;
}
/* Simple acpiChecksum: all the bytes must add up to 0. */
{
return -sum;
}
{
}
{
g->u8AccessSize = u8AccessSize;
}
{
}
/** Differentiated System Description Table (DSDT) */
{
}
/** Firmware ACPI Control Structure (FACS) */
{
}
/** Fixed ACPI Description Table (FADT aka FACP) */
static void acpiSetupFADT(ACPIState *s, RTGCPHYS32 addr_acpi1, RTGCPHYS32 addr_acpi2, uint32_t facs_addr, uint32_t dsdt_addr)
{
/* 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 (s->fCpuHotPlug)
/* Now the ACPI 1.0 version. */
}
/**
* 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.
*/
{
if (!rsdt)
for (unsigned int i = 0; i < nb_entries; ++i)
{
}
return VINF_SUCCESS;
}
/** Extended System Description Table. */
{
if (!xsdt)
return VERR_NO_TMP_MEMORY;
for (unsigned int i = 0; i < nb_entries; ++i)
{
}
return VINF_SUCCESS;
}
/** Root System Description Pointer (RSDP) */
{
/* ACPI 1.0 part (RSDT */
/* ACPI 2.0 part (XSDT) */
}
/**
* Multiple APIC Description Table.
*
* @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!!!!!
*/
{
{
lapic++;
}
/** @todo is this the right id? */
ioapic->u8Reserved = 0;
s->GCPhysMADTBase = addr;
}
/* SCI IRQ */
{
}
{
}
{
}
{
}
{
}
{
if (gpe0_level(s))
return;
old_level = pm1a_level(s);
acpiSetIrq(s, new_level);
}
{
if (pm1a_level(s))
return;
acpiSetIrq(s, new_level);
}
static int acpiPowerDown(ACPIState *s)
{
if (RT_FAILURE(rc))
return rc;
}
/** Converts a ACPI port interface pointer to an ACPI state pointer. */
#define IACPIPORT_2_ACPISTATE(pInterface) ( (ACPIState*)((uintptr_t)pInterface - RT_OFFSETOF(ACPIState, IACPIPort)) )
/**
* Send an ACPI power off event.
*
* @returns VBox status code
* @param pInterface Pointer to the interface structure containing the called function pointer.
*/
{
s->fPowerButtonHandled = false;
return VINF_SUCCESS;
}
/**
* Check if the ACPI power button event was handled.
*
* @returns VBox status code
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param pfHandled Return true if the power button event was handled by the guest.
*/
{
*pfHandled = s->fPowerButtonHandled;
return VINF_SUCCESS;
}
/**
* Check if the Guest entered into G0 (working) or G1 (sleeping).
*
* @returns VBox status code
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param pfEntered Return true if the guest entered the ACPI mode.
*/
{
return VINF_SUCCESS;
}
{
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;
}
/* PM1a_EVT_BLK enable */
{
return val;
}
{
}
/* PM1a_EVT_BLK status */
{
return val;
}
{
if (val & PWRBTN_STS)
s->fPowerButtonHandled = true; /* Remember that the guest handled the last power button event */
}
/* PM1a_CTL_BLK */
{
return val;
}
{
if (uSleepState != s->uSleepState)
{
s->uSleepState = uSleepState;
switch (uSleepState)
{
case 0x00: /* S0 */
break;
case 0x05: /* S5 */
LogRel(("Entering S5 (power down)\n"));
return acpiPowerDown(s);
default:
break;
}
}
return VINF_SUCCESS;
}
/* GPE0_BLK */
{
return val;
}
{
}
{
return val;
}
{
}
{
int rc = VINF_SUCCESS;
if (val == ACPI_RESET_REG_VAL)
{
# ifndef IN_RING3
# else /* IN_RING3 */
# endif /* !IN_RING3 */
}
return rc;
}
/* SMI */
{
if (val == ACPI_ENABLE)
else if (val == ACPI_DISABLE)
else
}
static uint32_t find_rsdp_space(void)
{
return 0xe0000;
}
static int acpiPMTimerReset(ACPIState *s)
{
return VINF_SUCCESS;
}
{
Log(("acpi: pm timer sts %#x (%d), en %#x (%d)\n",
acpiPMTimerReset(s);
}
/**
* _BST method.
*/
static int acpiFetchBatteryStatus(ACPIState *s)
{
uint32_t *p = s->au8BatteryInfo;
bool fPresent; /* battery present? */
int rc;
if (!s->pDrv)
return VINF_SUCCESS;
/* default values */
/* did we get a valid battery state? */
p[BAT_STATUS_PRESENT_RATE] = 0; /* mV */
return VINF_SUCCESS;
}
/**
* _BIF method.
*/
static int acpiFetchBatteryInfo(ACPIState *s)
{
uint32_t *p = s->au8BatteryInfo;
p[BAT_INFO_UNITS] = 0; /* mWh */
return VINF_SUCCESS;
}
/**
* _STA method.
*/
{
bool fPresent; /* battery present? */
int rc;
if (!s->pDrv)
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 */
}
{
/* query the current power source from the host driver */
if (!s->pDrv)
return AC_ONLINE;
}
PDMBOTHCBDECL(int) acpiBatIndexWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 4:
u32 >>= s->u8IndexShift;
/* see comment at the declaration of u8IndexShift */
{
s->u8IndexShift = 2;
u32 >>= 2;
}
s->uBatteryIndex = u32;
break;
default:
break;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiBatDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
switch (cb)
{
case 4:
switch (s->uBatteryIndex)
{
case BAT_STATUS_STATE:
case BAT_STATUS_PRESENT_RATE:
break;
case BAT_INFO_UNITS:
case BAT_INFO_DESIGN_CAPACITY:
case BAT_INFO_TECHNOLOGY:
case BAT_INFO_DESIGN_VOLTAGE:
break;
case BAT_DEVICE_STATUS:
*pu32 = acpiGetBatteryDeviceStatus(s);
break;
case BAT_POWER_SOURCE:
*pu32 = acpiGetPowerSource(s);
break;
default:
break;
}
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiSysInfoIndexWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 4:
s->uSystemInfoIndex = u32;
else
{
/* see comment at the declaration of u8IndexShift */
if (s->u8IndexShift == 0)
{
{
s->u8IndexShift = 2;
}
}
u32 >>= s->u8IndexShift;
s->uSystemInfoIndex = u32;
}
break;
default:
break;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiSysInfoDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
switch (cb)
{
case 4:
switch (s->uSystemInfoIndex)
{
break;
break;
*pu32 = s->u8UseIOApic;
break;
: 0;
break;
/* no need to show this device in the UI */
: 0;
break;
: 0;
break;
?
: 0;
: 0;
break;
{
{
}
else
{
AssertMsgFailed(("ACPI: CPU lock check protocol violation\n"));
/* Always return locked status just to be safe */
*pu32 = 1;
}
break;
}
{
*pu32 = s->GCPhysMADTBase;
break;
}
{
break;
}
/* Solaris 9 tries to read from this index */
*pu32 = 0;
break;
default:
break;
}
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiSysInfoDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
if (cb == 4)
{
switch (s->uSystemInfoIndex)
{
s->u8IndexShift = 0;
break;
case SYSTEM_INFO_INDEX_VALID:
s->u8IndexShift = 2;
break;
s->idCpuLockCheck = u32;
else
else
break;
default:
AssertMsgFailed(("Port=%#x cb=%d u32=%#x system_index=%#x\n",
break;
}
}
else
return VINF_SUCCESS;
}
* here! */
/* IO Helpers */
PDMBOTHCBDECL(int) acpiPm1aEnRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
switch (cb)
{
case 2:
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiPm1aStsRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
switch (cb)
{
case 2:
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiPm1aCtlRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
switch (cb)
{
case 2:
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiPM1aEnWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 2:
break;
default:
break;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiPM1aStsWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 2:
break;
default:
break;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiPM1aCtlWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 2:
default:
break;
}
return VINF_SUCCESS;
}
#endif /* IN_RING3 */
/**
*/
PDMBOTHCBDECL(int) acpiPMTmrRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb == 4)
{
return VINF_SUCCESS;
}
return VERR_IOM_IOPORT_UNUSED;
}
#ifdef IN_RING3
PDMBOTHCBDECL(int) acpiGpe0StsRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
switch (cb)
{
case 1:
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiGpe0EnRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
switch (cb)
{
case 1:
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiGpe0StsWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 1:
break;
default:
break;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiGpe0EnWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 1:
break;
default:
break;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiSmiWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 1:
break;
default:
break;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiResetWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 1:
default:
break;
}
return VINF_SUCCESS;
}
#ifdef DEBUG_ACPI
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:
break;
}
return VINF_SUCCESS;
}
PDMBOTHCBDECL(int) acpiDchrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (cb)
{
case 1:
break;
default:
break;
}
return VINF_SUCCESS;
}
#endif /* DEBUG_ACPI */
{
int rc = VINF_SUCCESS;
do { \
rc = PDMDevHlpIOPortRegister(pThis->pDevIns, acpiPmPort(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 GC stuff */
if (pThis->fGCEnabled)
{
}
/* register R0 stuff */
if (pThis->fR0Enabled)
{
}
return rc;
}
{
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;
}
/**
* Saved state structure description, version 4.
*/
static const SSMFIELD g_AcpiSavedStateFields4[] =
{
};
/**
* Saved state structure description, version 5.
*/
static const SSMFIELD g_AcpiSavedStateFields5[] =
{
};
{
}
{
/*
* Unregister PM handlers, will register with actual base
* after state successfully loaded.
*/
int rc = acpiUnregisterPmHandlers(s);
if (RT_FAILURE(rc))
return rc;
switch (uVersion)
{
case 4:
/** @todo Provide saner defaults for fields not found in saved state. */
break;
case 5:
break;
default:
}
if (RT_SUCCESS(rc))
{
rc = acpiRegisterPmHandlers(s);
if (RT_FAILURE(rc))
return rc;
rc = acpiFetchBatteryStatus(s);
if (RT_FAILURE(rc))
return rc;
rc = acpiFetchBatteryInfo(s);
if (RT_FAILURE(rc))
return rc;
rc = acpiPMTimerReset(s);
if (RT_FAILURE(rc))
return rc;
}
return rc;
}
/**
* Queries an interface to the driver.
*
* @returns Pointer to interface.
* @returns NULL if the interface was not supported by the driver.
* @param pInterface Pointer to this interface structure.
* @param enmInterface The requested interface identification.
* @thread Any thread.
*/
{
switch (enmInterface)
{
case PDMINTERFACE_BASE:
case PDMINTERFACE_ACPI_PORT:
default:
return NULL;
}
}
/**
* Create the ACPI tables.
*/
static int acpiPlantTables(ACPIState *s)
{
int rc;
RTGCPHYS32 rsdt_addr, xsdt_addr, fadt_acpi1_addr, fadt_acpi2_addr, facs_addr, dsdt_addr, last_addr, apic_addr = 0;
if (s->u8UseIOApic)
cAddr++; /* MADT */
if (RT_FAILURE(rc))
N_("Configuration error: Querying "
"\"RamSize\" as integer failed"));
rc = CFGMR3QueryU32Def(s->pDevIns->pCfgHandle, "RamHoleSize", &cbRamHole, MM_RAM_HOLE_SIZE_DEFAULT);
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"RamHoleSize\" as integer failed"));
/*
* Calc the sizes for the high and low regions.
*/
{
/* Note: This is also enforced by DevPcBios.cpp. */
}
rsdt_addr = 0;
/** @todo ACPI 3.0 doc says it needs to be aligned on a 64 byte boundary. */
if (s->u8UseIOApic)
{
/**
* @todo nike: maybe some refactoring needed to compute tables layout,
* but as this code is executed only once it doesn't make sense to optimize much
*/
}
else
{
}
if (RT_FAILURE(rc))
return rc;
if (last_addr > 0x10000)
N_("Error: ACPI tables > 64KB"));
Log(("FACS 0x%08X FADT (1.0) 0x%08X, FADT (2+) 0x%08X\n", facs_addr + addend, fadt_acpi1_addr + addend, fadt_acpi2_addr + addend));
acpiSetupFADT(s, fadt_acpi1_addr + addend, fadt_acpi2_addr + addend, facs_addr + addend, dsdt_addr + addend);
if (s->u8UseIOApic)
{
}
if (RT_FAILURE(rc))
return rc;
}
{
{
int rc;
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;
}
{
}
static void acpiPciConfigWrite(PPCIDEVICE pPciDev, uint32_t Address, uint32_t u32Value, unsigned cb)
{
/* PMREGMISC written */
if (Address == 0x80)
{
/* Check Power Management IO Space Enable (PMIOSE) bit */
{
int rc;
uNewBase &= 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 */
return 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
*/
/* Enable the lapic */
acpiSetupMADT(s, s->GCPhysMADTBase);
/* 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 */
{
AssertMsgReturnVoid(!(VMCPUSET_IS_PRESENT(&s->CpuSetLocked, iLUN)), ("CPU is still locked by the guest\n"));
/* Disable the CPU */
/* Update the lapic state */
acpiSetupMADT(s, s->GCPhysMADTBase);
/* Notify the guest */
}
}
{
s->pm1a_en = 0;
s->pm1a_sts = 0;
s->pm1a_ctl = 0;
acpiPMTimerReset(s);
s->uBatteryIndex = 0;
s->uSystemInfoIndex = 0;
s->gpe0_en = 0;
s->gpe0_sts = 0;
s->uSleepState = 0;
/** @todo Should we really reset PM base? */
acpiPlantTables(s);
}
/**
* Relocates the GC pointer members.
*/
{
}
/**
* Construct a device instance for a VM.
*
* @returns VBox status.
* @param pDevIns The device instance data.
* If the registration structure is needed, pDevIns->pDevReg points to it.
* @param iInstance Instance number. Use this to figure out which registers and such to use.
* The device number is also found in pDevIns->iInstance, but since it's
* likely to be freqently used PDM passes it as parameter.
* @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
* of the device instance. It's also found in pDevIns->pCfgHandle, but like
* iInstance it's expected to be used a bit in this function.
*/
{
/* Validate and read the configuration. */
"RamSize\0"
"RamHoleSize\0"
"IOAPIC\0"
"NumCPUs\0"
"GCEnabled\0"
"R0Enabled\0"
"HpetEnabled\0"
"SmcEnabled\0"
"FdcEnabled\0"
"ShowRtc\0"
"ShowCpu\0"
"CpuHotPlug\0"
"AmlFilePath\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 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 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)
s->fGCEnabled = true;
else if (RT_FAILURE(rc))
N_("Configuration error: Failed to read \"GCEnabled\""));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
s->fR0Enabled = true;
else if (RT_FAILURE(rc))
N_("configuration error: failed to read R0Enabled as boolean"));
/*
* Interfaces
*/
/* IBase */
/* IACPIPort */
VMCPUSET_EMPTY(&s->CpuSetAttached);
VMCPUSET_EMPTY(&s->CpuSetLocked);
VMCPUSET_ADD(&s->CpuSetAttached, 0);
VMCPUSET_ADD(&s->CpuSetLocked, 0);
/* Try to attach the other CPUs */
for (unsigned i = 1; i < s->cCpus; i++)
{
if (s->fCpuHotPlug)
{
if (RT_SUCCESS(rc))
{
VMCPUSET_ADD(&s->CpuSetAttached, i);
VMCPUSET_ADD(&s->CpuSetLocked, i);
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. */
VMCPUSET_ADD(&s->CpuSetAttached, i);
VMCPUSET_ADD(&s->CpuSetLocked, i);
}
}
/* Set default port base */
s->uPmIoPortBase = PM_PORT_BASE;
/* */
if (!rsdp_addr)
N_("Can not find space for RSDP. ACPI is disabled"));
rc = acpiPlantTables(s);
if (RT_FAILURE(rc))
return rc;
PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "ACPI RSDP");
if (RT_FAILURE(rc))
return rc;
rc = acpiRegisterPmHandlers(s);
if (RT_FAILURE(rc))
return rc;
do { \
if (RT_FAILURE(rc)) \
return rc; \
} while (0)
#ifdef DEBUG_ACPI
#endif
#undef R
if (RT_FAILURE(rc))
{
return rc;
}
acpiPMTimerReset(s);
/* See p. 50 of PIIX4 manual */
#if 0
#endif
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
/*
* Get the corresponding connector interface
*/
if (RT_SUCCESS(rc))
{
s->pDrv = (PPDMIACPICONNECTOR)s->pDrvBase->pfnQueryInterface(s->pDrvBase, PDMINTERFACE_ACPI_CONNECTOR);
if (!s->pDrv)
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 */
/* szDeviceName */
"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 */
NULL,
/* pfnAttach */
/* pfnDetach */
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */