DevACPI.cpp revision 11a0d256636c41e4979411b55f35b58904feac87
/* $Id$ */
/** @file
* DevACPI - Advanced Configuration and Power Interface (ACPI) Device.
*/
/*
* Copyright (C) 2006-2007 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.
*/
#define LOG_GROUP LOG_GROUP_DEV_ACPI
#ifdef IN_RING3
#endif /* IN_RING3 */
#include "../Builtins.h"
#ifdef LOG_ENABLED
# define DEBUG_ACPI
#endif
/* the compiled DSL */
#if defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
#endif /* !IN_RING3 */
#define IO_READ_PROTO(name) \
#define IO_WRITE_PROTO(name) \
#define DEBUG_HEX 0x3000
#define DEBUG_CHR 0x3001
#define PM_TMR_FREQ 3579545
#define PM1a_EVT_BLK 0x00004000
#define PM1a_CTL_BLK 0x00004004
#define PM_TMR_BLK 0x00004008
#define GPE0_BLK 0x00004020
#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_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
struct ACPIState
{
/** Number of logical CPUs in guest */
unsigned int uBatteryIndex;
unsigned int uSystemInfoIndex;
/** 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. */
bool fPowerButtonHandled;
/** ACPI port base interface. */
/** ACPI port interface. */
/** Pointer to the device instance. */
/** Pointer to the driver base interface */
/** Pointer to the driver connector interface */
/* If High Precision Event Timer device should be supported */
/* If System Management Controller device should be supported */
};
#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?) */
#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 VBOX_WITH_SMP_GUESTS
#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() const
{
return (ACPITBLHEADER*)pData;
}
/**
* 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() const
{
}
/**
* Address of APIC flags
*/
inline uint32_t* u32Flags_addr() const
{
}
/**
* Address of per-CPU LAPIC descriptions
*/
inline ACPITBLLAPIC* LApics_addr() const
{
}
/**
* Address of IO APIC description
*/
inline ACPITBLIOAPIC* IOApic_addr() const
{
}
/**
* Size of MADT.
* Note that this function assumes IOApic to be the last field in structure.
*/
{
}
/**
* Raw data of MADT.
*/
{
return pData;
}
/**
* Size of MADT for given ACPI config, useful to compute layout.
*/
{
}
/*
* Constructor, only works in Ring 3, doesn't look like a big deal.
*/
{
pData = 0;
}
{
}
};
#endif /* IN_RING3 */
#else /* !VBOX_WITH_SMP_GUESTS */
/** Multiple APIC Description Table */
struct ACPITBLMADT
{
};
#endif /* !VBOX_WITH_SMP_GUESTS */
#pragma pack()
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
#ifdef IN_RING3
# ifdef DEBUG_ACPI
# endif
#endif
#ifdef IN_RING3
/* 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) */
{
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 */
}
/*
* 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. */
/** @todo All hardcoded, should set this up based on the actual VM config!!!!! */
/** @note APIC without IO-APIC hangs Windows Vista therefore we setup both */
{
#ifdef VBOX_WITH_SMP_GUESTS
{
lapic++;
}
ioapic->u8Reserved = 0;
#else /* !VBOX_WITH_SMP_GUESTS */
/* Don't call this function if u8UseIOApic==false! */
Assert(s->u8UseIOApic);
#endif /* !VBOX_WITH_SMP_GUESTS */
}
/* 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;
}
/**
* 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 void acpiPMTimerReset (ACPIState *s)
{
}
{
Log (("acpi: pm timer sts %#x (%d), en %#x (%d)\n",
acpiPMTimerReset (s);
}
/**
* _BST method.
*/
static void acpiFetchBatteryStatus (ACPIState *s)
{
uint32_t *p = s->au8BatteryInfo;
bool fPresent; /* battery present? */
int rc;
if (!s->pDrv)
return;
/* default values */
/* did we get a valid battery state? */
p[BAT_STATUS_PRESENT_RATE] = 0; /* mV */
}
/**
* _BIF method.
*/
static void acpiFetchBatteryInfo (ACPIState *s)
{
uint32_t *p = s->au8BatteryInfo;
p[BAT_INFO_UNITS] = 0; /* mWh */
}
/**
* _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;
}
{
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;
}
{
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;
}
{
switch (cb) {
case 4:
s->uSystemInfoIndex = u32;
else
{
/* see comment at the declaration of u8IndexShift */
if (s->u8IndexShift == 0)
{
{
{
s->u8IndexShift = 2;
break;
}
}
}
u32 >>= s->u8IndexShift;
s->uSystemInfoIndex = u32;
}
break;
default:
break;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 4:
switch (s->uSystemInfoIndex)
{
*pu32 = s->u64RamSize;
break;
*pu32 = s->u8UseIOApic;
break;
: 0;
break;
/* no need to show this device in the UI */
: 0;
break;
: 0;
break;
/* Solaris 9 tries to read from this index */
*pu32 = 0;
break;
default:
break;
}
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
{
{
switch (s->uSystemInfoIndex)
{
s->u8IndexShift = 0;
break;
case SYSTEM_INFO_INDEX_VALID:
s->u8IndexShift = 2;
break;
default:
AssertMsgFailed(("Port=%#x cb=%d u32=%#x system_index=%#x\n",
break;
}
}
else
return VINF_SUCCESS;
}
/* IO Helpers */
{
switch (cb)
{
case 2:
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 2:
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 2:
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 2:
break;
default:
break;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 2:
break;
default:
break;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 2:
default:
break;
}
return VINF_SUCCESS;
}
#endif /* IN_RING3 */
/**
*/
{
if (cb == 4)
{
return VINF_SUCCESS;
}
return VERR_IOM_IOPORT_UNUSED;
}
#ifdef IN_RING3
{
switch (cb)
{
case 1:
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 1:
break;
default:
return VERR_IOM_IOPORT_UNUSED;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 1:
break;
default:
break;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 1:
break;
default:
break;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 1:
break;
default:
break;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 1:
default:
break;
}
return VINF_SUCCESS;
}
#ifdef DEBUG_ACPI
{
switch (cb)
{
case 1:
break;
case 2:
case 4:
break;
default:
break;
}
return VINF_SUCCESS;
}
{
switch (cb)
{
case 1:
break;
default:
break;
}
return VINF_SUCCESS;
}
#endif /* DEBUG_ACPI */
/**
* Saved state structure description.
*/
static const SSMFIELD g_AcpiSavedStateFields[] =
{
};
{
}
{
int rc;
if (u32Version != 4)
if (RT_SUCCESS (rc))
{
acpiFetchBatteryInfo (s);
acpiPMTimerReset (s);
}
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;
if (s->u8UseIOApic)
cAddr++; /* MADT */
if (RT_FAILURE (rc))
N_("Configuration error: Querying "
"\"RamSize\" as integer failed"));
N_("Configuration error: Invalid \"RamSize\", maximum allowed "
"value is 4095MB"));
rsdt_addr = 0;
if (s->u8UseIOApic)
{
#ifdef VBOX_WITH_SMP_GUESTS
/**
* @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
#endif
}
else
{
}
if (last_addr > 0x10000)
N_("Error: ACPI tables > 64KB"));
if (s->u8UseIOApic)
{
}
if (RT_FAILURE(rc))
return rc;
}
/**
* 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.
*/
{
int rc;
bool fGCEnabled;
bool fR0Enabled;
/* Validate and read the configuration. */
if (!CFGMR3AreValuesValid (pCfgHandle,
"RamSize\0"
"IOAPIC\0"
"NumCPUs\0"
"GCEnabled\0"
"R0Enabled\0"
"FdcEnabled\0"
"HpetEnabled\0"
"SmcEnabled\0"))
N_("Configuration error: Invalid config key for ACPI device"));
/* query whether we are supposed to present an IOAPIC */
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
s->u8UseIOApic = 1;
else if (RT_FAILURE (rc))
N_("Configuration error: Failed to read \"IOAPIC\""));
/* 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\""));
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
/* query whether we are supposed to present an FDC controller */
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
s->u8UseFdc = 1;
else if (RT_FAILURE (rc))
N_("Configuration error: Failed to read \"FdcEnabled\""));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
fGCEnabled = true;
else if (RT_FAILURE (rc))
N_("Configuration error: Failed to read \"GCEnabled\""));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
fR0Enabled = true;
else if (RT_FAILURE(rc))
N_("configuration error: failed to read R0Enabled as boolean"));
/* */
rsdp_addr = find_rsdp_space ();
if (!rsdp_addr)
N_("Can not find space for RSDP. ACPI is disabled"));
rc = acpiPlantTables (s);
if (RT_FAILURE (rc))
return rc;
rc = PDMDevHlpROMRegister (pDevIns, rsdp_addr, 0x1000, s->au8RSDPPage, false /* fShadow */, "ACPI RSDP");
if (RT_FAILURE (rc))
return rc;
do { \
if (RT_FAILURE (rc)) \
return rc; \
} while (0)
#define L (GPE0_BLK_LEN / 2)
#ifdef DEBUG_ACPI
#endif
#undef L
#undef R
/* register GC stuff */
if (fGCEnabled)
{
}
/* register R0 stuff */
if (fR0Enabled)
{
}
if (RT_FAILURE(rc))
{
return rc;
}
acpiPMTimerReset (s);
#if 0 /* The ACPI controller usually has no subsystem ID. */
#endif
if (RT_FAILURE (rc))
return rc;
if (RT_FAILURE(rc))
return rc;
/*
* Interfaces
*/
/* IBase */
/* IACPIPort */
/*
* Get the corresponding connector interface
*/
if (RT_SUCCESS (rc))
{
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
N_("Failed to attach LUN #0"));
return rc;
}
/**
* Relocates the GC pointer members.
*/
{
}
{
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;
acpiPlantTables(s);
}
/**
* 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 */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */