Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

576 lines
15 KiB

#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;
}