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