577 lines
15 KiB
C
577 lines
15 KiB
C
#include "spsim.h"
|
||
|
||
#define rgzMultiFunctionAdapter L"\\Registry\\Machine\\Hardware\\Description\\System\\MultifunctionAdapter"
|
||
#define rgzAcpiConfigurationData L"Configuration Data"
|
||
#define rgzAcpiIdentifier L"Identifier"
|
||
#define rgzBIOSIdentifier L"ACPI BIOS"
|
||
|
||
typedef struct {
|
||
ULONGLONG Base;
|
||
ULONGLONG Length;
|
||
ULONGLONG Type;
|
||
} ACPI_E820_ENTRY, *PACPI_E820_ENTRY;
|
||
|
||
typedef struct _ACPI_BIOS_MULTI_NODE {
|
||
PHYSICAL_ADDRESS RsdtAddress; // 64-bit physical address of RSDT
|
||
ULONGLONG Count;
|
||
ACPI_E820_ENTRY E820Entry[1];
|
||
} ACPI_BIOS_MULTI_NODE, *PACPI_BIOS_MULTI_NODE;
|
||
|
||
typedef enum {
|
||
AcpiAddressRangeMemory = 1,
|
||
AcpiAddressRangeReserved,
|
||
AcpiAddressRangeACPI,
|
||
AcpiAddressRangeNVS,
|
||
AcpiAddressRangeMaximum,
|
||
} ACPI_BIOS_E820_TYPE, *PACPI_BIOS_E820_TYPE;
|
||
|
||
NTSTATUS
|
||
SpSimGetRegistryValue(
|
||
IN HANDLE KeyHandle,
|
||
IN PWSTR ValueName,
|
||
OUT PKEY_VALUE_PARTIAL_INFORMATION *Information
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to retrieve the data for a registry key's value.
|
||
This is done by querying the value of the key with a zero-length buffer
|
||
to determine the size of the value, and then allocating a buffer and
|
||
actually querying the value into the buffer.
|
||
|
||
It is the responsibility of the caller to free the buffer.
|
||
|
||
Arguments:
|
||
|
||
KeyHandle - Supplies the key handle whose value is to be queried
|
||
|
||
ValueName - Supplies the null-terminated Unicode name of the value.
|
||
|
||
Information - Returns a pointer to the allocated data buffer.
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of the query operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNICODE_STRING unicodeString;
|
||
NTSTATUS status;
|
||
PKEY_VALUE_PARTIAL_INFORMATION infoBuffer;
|
||
ULONG keyValueLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
RtlInitUnicodeString( &unicodeString, ValueName );
|
||
|
||
//
|
||
// Figure out how big the data value is so that a buffer of the
|
||
// appropriate size can be allocated.
|
||
//
|
||
|
||
status = ZwQueryValueKey( KeyHandle,
|
||
&unicodeString,
|
||
KeyValuePartialInformation,
|
||
(PVOID) NULL,
|
||
0,
|
||
&keyValueLength );
|
||
if (status != STATUS_BUFFER_OVERFLOW &&
|
||
status != STATUS_BUFFER_TOO_SMALL) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Allocate a buffer large enough to contain the entire key data value.
|
||
//
|
||
|
||
infoBuffer = ExAllocatePool(NonPagedPool,
|
||
keyValueLength);
|
||
if (!infoBuffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Query the data for the key value.
|
||
//
|
||
|
||
status = ZwQueryValueKey( KeyHandle,
|
||
&unicodeString,
|
||
KeyValuePartialInformation,
|
||
infoBuffer,
|
||
keyValueLength,
|
||
&keyValueLength );
|
||
if (!NT_SUCCESS( status )) {
|
||
ExFreePool( infoBuffer );
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Everything worked, so simply return the address of the allocated
|
||
// buffer to the caller, who is now responsible for freeing it.
|
||
//
|
||
|
||
*Information = infoBuffer;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
// insert pragmas here
|
||
|
||
NTSTATUS
|
||
SpSimRetrieveE820Data(
|
||
OUT PACPI_BIOS_MULTI_NODE *AcpiMulti
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function looks into the registry to find the ACPI RSDT,
|
||
which was stored there by ntdetect.com.
|
||
|
||
Arguments:
|
||
|
||
AcpiMulti - ...
|
||
|
||
Return Value:
|
||
|
||
A NTSTATUS code to indicate the result of the initialization.
|
||
|
||
--*/
|
||
{
|
||
UNICODE_STRING unicodeString, unicodeValueName, biosId;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
HANDLE hMFunc, hBus;
|
||
WCHAR wbuffer[10];
|
||
ULONG i, length;
|
||
PWSTR p;
|
||
PKEY_VALUE_PARTIAL_INFORMATION valueInfo;
|
||
NTSTATUS status;
|
||
BOOLEAN same;
|
||
PCM_PARTIAL_RESOURCE_LIST prl;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR prd;
|
||
PACPI_BIOS_MULTI_NODE multiNode;
|
||
ULONG multiNodeSize;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Look in the registry for the "ACPI BIOS bus" data
|
||
//
|
||
|
||
RtlInitUnicodeString (&unicodeString, rgzMultiFunctionAdapter);
|
||
InitializeObjectAttributes (&objectAttributes,
|
||
&unicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL, // handle
|
||
NULL);
|
||
|
||
|
||
status = ZwOpenKey (&hMFunc, KEY_READ, &objectAttributes);
|
||
if (!NT_SUCCESS(status)) {
|
||
DbgPrint("AcpiBios:Can not open MultifunctionAdapter registry key.\n");
|
||
return status;
|
||
}
|
||
|
||
unicodeString.Buffer = wbuffer;
|
||
unicodeString.MaximumLength = sizeof(wbuffer);
|
||
RtlInitUnicodeString(&biosId, rgzBIOSIdentifier);
|
||
|
||
for (i = 0; TRUE; i++) {
|
||
RtlIntegerToUnicodeString (i, 10, &unicodeString);
|
||
InitializeObjectAttributes (
|
||
&objectAttributes,
|
||
&unicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
hMFunc,
|
||
NULL);
|
||
|
||
status = ZwOpenKey (&hBus, KEY_READ, &objectAttributes);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Out of Multifunction adapter entries...
|
||
//
|
||
|
||
DbgPrint("AcpiBios: ACPI BIOS MultifunctionAdapter registry key not found.\n");
|
||
ZwClose (hMFunc);
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Check the Indentifier to see if this is an ACPI BIOS entry
|
||
//
|
||
|
||
status = SpSimGetRegistryValue (hBus, rgzAcpiIdentifier, &valueInfo);
|
||
if (!NT_SUCCESS (status)) {
|
||
ZwClose (hBus);
|
||
continue;
|
||
}
|
||
|
||
p = (PWSTR) ((PUCHAR) valueInfo->Data);
|
||
unicodeValueName.Buffer = p;
|
||
unicodeValueName.MaximumLength = (USHORT)valueInfo->DataLength;
|
||
length = valueInfo->DataLength;
|
||
|
||
//
|
||
// Determine the real length of the ID string
|
||
//
|
||
|
||
while (length) {
|
||
if (p[length / sizeof(WCHAR) - 1] == UNICODE_NULL) {
|
||
length -= 2;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
unicodeValueName.Length = (USHORT)length;
|
||
same = RtlEqualUnicodeString(&biosId, &unicodeValueName, TRUE);
|
||
ExFreePool(valueInfo);
|
||
if (!same) {
|
||
ZwClose (hBus);
|
||
continue;
|
||
}
|
||
|
||
status = SpSimGetRegistryValue(hBus, rgzAcpiConfigurationData, &valueInfo);
|
||
ZwClose (hBus);
|
||
if (!NT_SUCCESS(status)) {
|
||
continue ;
|
||
}
|
||
|
||
prl = (PCM_PARTIAL_RESOURCE_LIST)(valueInfo->Data);
|
||
prd = &prl->PartialDescriptors[0];
|
||
multiNode = (PACPI_BIOS_MULTI_NODE)((PCHAR) prd + sizeof(CM_PARTIAL_RESOURCE_LIST));
|
||
|
||
|
||
break;
|
||
}
|
||
|
||
multiNodeSize = sizeof(ACPI_BIOS_MULTI_NODE) +
|
||
((ULONG)(multiNode->Count - 1) * sizeof(ACPI_E820_ENTRY));
|
||
|
||
*AcpiMulti = (PACPI_BIOS_MULTI_NODE)
|
||
ExAllocatePool(NonPagedPool,
|
||
multiNodeSize);
|
||
if (*AcpiMulti == NULL) {
|
||
ExFreePool(valueInfo);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(*AcpiMulti, multiNode, multiNodeSize);
|
||
|
||
ExFreePool(valueInfo);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
SpSimFillMemoryDescs(
|
||
PACPI_BIOS_MULTI_NODE E820Data,
|
||
ULONGLONG memUnit,
|
||
PMEM_REGION_DESCRIPTOR MemRegions
|
||
)
|
||
{
|
||
ULONG i, descCount;
|
||
|
||
#undef min
|
||
#define min(a,b) (a < b ? a : b)
|
||
|
||
descCount = 0;
|
||
for (i = 0; i < E820Data->Count; i++) {
|
||
if (E820Data->E820Entry[i].Type != AcpiAddressRangeMemory) {
|
||
continue;
|
||
}
|
||
if (E820Data->E820Entry[i].Length > memUnit) {
|
||
ULONGLONG remains, base, extra;
|
||
|
||
extra = E820Data->E820Entry[i].Length & (PAGE_SIZE - 1);
|
||
remains = E820Data->E820Entry[i].Length - extra;
|
||
base = (E820Data->E820Entry[i].Base + (PAGE_SIZE - 1)) &
|
||
~(PAGE_SIZE - 1);
|
||
|
||
while (remains) {
|
||
MemRegions[descCount].Addr = (ULONG) base;
|
||
MemRegions[descCount].Length = (ULONG) min(remains, memUnit);
|
||
descCount++;
|
||
base += min(remains, memUnit);
|
||
remains -= min(remains, memUnit);
|
||
}
|
||
} else {
|
||
MemRegions[descCount].Addr = (ULONG) E820Data->E820Entry[i].Base;
|
||
MemRegions[descCount].Length = (ULONG) E820Data->E820Entry[i].Length;
|
||
|
||
descCount++;
|
||
}
|
||
}
|
||
}
|
||
ULONG
|
||
SpSimCalculateMemoryDescCount(
|
||
PACPI_BIOS_MULTI_NODE E820Data,
|
||
ULONGLONG memUnit
|
||
)
|
||
{
|
||
ULONG i, descCount;
|
||
|
||
descCount = 0;
|
||
for (i = 0; i < E820Data->Count; i++) {
|
||
if (E820Data->E820Entry[i].Type != AcpiAddressRangeMemory) {
|
||
continue;
|
||
}
|
||
ASSERT((0xFFFFFFFF00000000 & E820Data->E820Entry[i].Base) == 0);
|
||
|
||
if (E820Data->E820Entry[i].Length > memUnit) {
|
||
|
||
descCount += (ULONG) (E820Data->E820Entry[i].Length / memUnit);
|
||
if ((E820Data->E820Entry[i].Length % memUnit) != 0) {
|
||
descCount++;
|
||
}
|
||
} else {
|
||
descCount++;
|
||
}
|
||
}
|
||
return descCount;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SpSimCreateMemOpRegion(
|
||
IN PSPSIM_EXTENSION SpSim
|
||
)
|
||
{
|
||
PACPI_BIOS_MULTI_NODE E820Data;
|
||
ULONG i, descCount, memUnit = MIN_LARGE_DESC;
|
||
NTSTATUS status;
|
||
|
||
status = SpSimRetrieveE820Data(&E820Data);
|
||
if (!NT_SUCCESS(status)) {
|
||
SpSim->MemOpRegionValues = NULL;
|
||
return status;
|
||
}
|
||
|
||
ASSERT(E820Data);
|
||
|
||
descCount = descCount = SpSimCalculateMemoryDescCount(E820Data, memUnit);
|
||
while (descCount > (MAX_MEMORY_OBJ * MAX_MEMORY_DESC_PER_OBJ)) {
|
||
memUnit = memUnit << 1;
|
||
descCount = SpSimCalculateMemoryDescCount(E820Data, memUnit);
|
||
}
|
||
|
||
SpSim->MemOpRegionValues =
|
||
ExAllocatePool(NonPagedPool,
|
||
sizeof(MEM_REGION_DESCRIPTOR) * descCount);
|
||
if (SpSim->MemOpRegionValues == NULL) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto out;
|
||
}
|
||
RtlZeroMemory(SpSim->MemOpRegionValues,
|
||
sizeof(MEM_REGION_DESCRIPTOR) * descCount);
|
||
|
||
SpSimFillMemoryDescs(E820Data, memUnit, SpSim->MemOpRegionValues);
|
||
|
||
SpSim->MemCount = descCount;
|
||
|
||
out:
|
||
ExFreePool(E820Data);
|
||
if (!NT_SUCCESS(status)) {
|
||
if (SpSim->MemOpRegionValues) {
|
||
ExFreePool(SpSim->MemOpRegionValues);
|
||
SpSim->MemOpRegionValues = NULL;
|
||
}
|
||
SpSim->MemCount = 0;
|
||
}
|
||
return status;
|
||
}
|
||
|
||
VOID
|
||
SpSimDeleteMemOpRegion(
|
||
IN PSPSIM_EXTENSION SpSim
|
||
)
|
||
{
|
||
if (SpSim->MemOpRegionValues) {
|
||
ExFreePool(SpSim->MemOpRegionValues);
|
||
SpSim->MemOpRegionValues = NULL;
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
SpSimMemOpRegionReadWrite(
|
||
PSPSIM_EXTENSION SpSim,
|
||
ULONG AccessType,
|
||
ULONG Offset,
|
||
ULONG Size,
|
||
PUCHAR Data
|
||
)
|
||
{
|
||
ULONG i, limit;
|
||
PUCHAR current;
|
||
|
||
ASSERT((Offset & 3) == 0);
|
||
ASSERT((Size & 3) == 0);
|
||
|
||
if (SpSim->MemOpRegionValues == NULL) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
limit = sizeof(MEM_REGION_DESCRIPTOR)*SpSim->MemCount;
|
||
|
||
// We're going to define this op region to return all zeros if you
|
||
// access beyond that which we've been able to initialize using
|
||
// the E820 data.
|
||
|
||
if (Offset >= limit) {
|
||
RtlZeroMemory(Data, Size);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (Offset + Size > limit) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
ASSERT(Offset < limit);
|
||
|
||
/// XXX if the asserts hold then this should get fixed.
|
||
|
||
current = ((PUCHAR) (SpSim->MemOpRegionValues)) + Offset;
|
||
|
||
if (AccessType & ACPI_OPREGION_WRITE) {
|
||
for (i = 0 ; i < Size; i++) {
|
||
*current++ = *Data++;
|
||
}
|
||
} else {
|
||
for (i = 0 ; i < Size; i++) {
|
||
*Data++ = *current++;
|
||
}
|
||
}
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
EXPORT
|
||
SpSimMemOpRegionHandler (
|
||
ULONG AccessType,
|
||
PVOID OpRegion,
|
||
ULONG Address,
|
||
ULONG Size,
|
||
PULONG Data,
|
||
ULONG_PTR Context,
|
||
PACPI_OPREGION_CALLBACK CompletionHandler,
|
||
PVOID CompletionContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles requests to service the
|
||
SPSIM operation region contained within this driver
|
||
|
||
Arguments:
|
||
|
||
AccessType - Read or Write data
|
||
OpRegion - Operation region object
|
||
Address - Address within the EC address space
|
||
Size - Number of bytes to transfer
|
||
Data - Data buffer to transfer to/from
|
||
Context - SpSim
|
||
CompletionHandler - AMLI handler to call when operation is complete
|
||
CompletionContext - Context to pass to the AMLI handler
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
|
||
status = SpSimMemOpRegionReadWrite((PSPSIM_EXTENSION) Context,
|
||
AccessType,
|
||
Address,
|
||
Size,
|
||
(PUCHAR)Data);
|
||
return status;
|
||
}
|
||
NTSTATUS
|
||
SpSimInstallMemOpRegionHandler(
|
||
IN OUT PSPSIM_EXTENSION SpSim
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This calls the ACPI driver to install itself as the op region
|
||
handler for the Mem region. It also allocates the memory for the
|
||
opregion itself.
|
||
|
||
Arguments:
|
||
|
||
pSpSimData - Pointer to the SpSim extension
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
|
||
status=RegisterOpRegionHandler (
|
||
SpSim->AttachedDevice,
|
||
ACPI_OPREGION_ACCESS_AS_COOKED,
|
||
MEM_OPREGION,
|
||
SpSimMemOpRegionHandler,
|
||
SpSim,
|
||
0,
|
||
&SpSim->MemOpRegion
|
||
);
|
||
|
||
//
|
||
// Check the status code
|
||
//
|
||
if(!NT_SUCCESS(status)) {
|
||
SpSim->MemOpRegion = NULL;
|
||
DbgPrint("Not successful in installing:=%x\n", status);
|
||
return status;
|
||
}
|
||
|
||
// XXXX
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
SpSimRemoveMemOpRegionHandler (
|
||
IN OUT PSPSIM_EXTENSION SpSim
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Uninstalls itself as the opregion handler.
|
||
|
||
Arguments:
|
||
|
||
SpSim - Pointer to the SpSim extension
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PIRP irp;
|
||
|
||
if (SpSim->MemOpRegion != NULL) {
|
||
status = DeRegisterOpRegionHandler (
|
||
SpSim->AttachedDevice,
|
||
SpSim->MemOpRegion
|
||
);
|
||
SpSim->MemOpRegion = NULL;
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
return status;
|
||
}
|