/*++ Copyright (c) 2000 Microsoft Corporation Module Name: method.c Abstract: This module implements code to find and evaluate ACPI objects. Author: Jake Oshins (3/18/00) - create file Environment: Kernel mode Notes: Revision History: --*/ #include "processor.h" #include "acpiioct.h" #include "ntacpi.h" #include #include "apic.inc" #include "..\eventmsg.h" #define rgzMultiFunctionAdapter L"\\Registry\\Machine\\Hardware\\Description\\System\\MultifunctionAdapter" #define rgzAcpiConfigurationData L"Configuration Data" #define rgzAcpiIdentifier L"Identifier" #define rgzBIOSIdentifier L"ACPI BIOS" const WCHAR CCSEnumRegKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum"; const WCHAR FriendlyNameRegKey[] = L"FriendlyName"; const WCHAR EnumKeyName[] = L"Enum"; extern FADT HalpFixedAcpiDescTable; extern ULONG HalpThrottleScale; extern WMI_EVENT PStateEvent; extern WMI_EVENT NewPStatesEvent; extern WMI_EVENT NewCStatesEvent; // toddcar 4/24/01 ISSUE // when we support CStates and Throttle States on MP machines // these values need to be in the device extension. // GEN_ADDR PCntAddress; GEN_ADDR C2Address; GEN_ADDR C3Address; // // Well known virtual address of local processor apic // #define LOCALAPIC 0xfffe0000 #define pLocalApic ((ULONG volatile *) UlongToPtr(LOCALAPIC)) NTSTATUS AcpiParseGenRegDesc( IN PUCHAR Buffer, OUT PGEN_ADDR *GenericAddress ); NTSTATUS AcpiFindRsdt ( OUT PACPI_BIOS_MULTI_NODE *AcpiMulti ); VOID AcpiNotify80CallbackWorker( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ); VOID AcpiNotify81CallbackWorker( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ); #if DBG VOID DumpCStates( PACPI_CST_PACKAGE CStates ); #else #define DumpCStates(_x_) #endif #ifdef ALLOC_PRAGMA #pragma alloc_text (PAGE, AcpiEvaluateCst) #pragma alloc_text (PAGE, AcpiEvaluateMethod) #pragma alloc_text (PAGE, AcpiEvaluatePct) #pragma alloc_text (PAGE, AcpiEvaluatePpc) #pragma alloc_text (PAGE, AcpiEvaluateProcessorObject) #pragma alloc_text (PAGE, AcpiEvaluatePss) #pragma alloc_text (PAGE, AcpiEvaluatePtc) #pragma alloc_text (PAGE, AcpiFindRsdt) #pragma alloc_text (PAGE, AcpiNotify80CallbackWorker) #pragma alloc_text (PAGE, AcpiParseGenRegDesc) #pragma alloc_text (PAGE, AcquireAcpiInterfaces) #pragma alloc_text (PAGE, GetRegistryValue) #pragma alloc_text (PAGE, GetAcpiTable) #pragma alloc_text (PAGE, InitializeAcpi2PStatesGeneric) #pragma alloc_text (PAGE, ReleaseAcpiInterfaces) #pragma alloc_text (PAGE, InitializeAcpi2IoSpaceCstates) #endif NTSTATUS AcpiEvaluateMethod ( IN PFDO_DATA DeviceExtension, IN PCHAR MethodName, IN PVOID InputBuffer OPTIONAL, OUT PVOID *OutputBuffer ) /* Routine Description: This routine sends an IRP to ACPI to evaluate a method. Arguments: MethodName - String identifying the method InputBuffer - Arguments for the method. If specified, the method name must match MethodName OutputBuffer- Return value(s) from method Return Value: NTSTATUS --*/ #define CONTROL_METHOD_BUFFER_SIZE 0x1024 { ACPI_EVAL_INPUT_BUFFER inputBuffer; NTSTATUS status; PIRP irp = NULL; KEVENT irpCompleted; IO_STATUS_BLOCK statusBlock; ULONG inputBufLen; DebugEnter(); PAGED_CODE(); if (!InputBuffer) { // // The caller didn't specify an input buffer. So // build one without any arguments out of the MethodName. // ASSERT(strlen(MethodName) <= 4); if (strlen(MethodName) > 4) { return STATUS_INVALID_PARAMETER_1; } inputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE; strncpy(inputBuffer.MethodName, MethodName, sizeof(inputBuffer.MethodName)); InputBuffer = &inputBuffer; } // // Figure out how big the input buffer is. // switch(((PACPI_EVAL_INPUT_BUFFER)InputBuffer)->Signature) { case ACPI_EVAL_INPUT_BUFFER_SIGNATURE: inputBufLen = sizeof(ACPI_EVAL_INPUT_BUFFER); break; case ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_SIGNATURE: inputBufLen = sizeof(ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER); break; case ACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING_SIGNATURE: inputBufLen = sizeof(ACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING) + ((PACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING)InputBuffer)->StringLength - 1; break; case ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE: inputBufLen = ((PACPI_EVAL_INPUT_BUFFER_COMPLEX)InputBuffer)->Size; break; default: return STATUS_INVALID_PARAMETER_2; } KeInitializeEvent(&irpCompleted, NotificationEvent, FALSE); // // Allocate 1K for the output buffer. That should handle // everything that is necessary for ACPI 2.0 processor objects. // *OutputBuffer = ExAllocatePoolWithTag(PagedPool, CONTROL_METHOD_BUFFER_SIZE, PROCESSOR_POOL_TAG); if (!*OutputBuffer) { return STATUS_INSUFFICIENT_RESOURCES; } // // Build the IRP. // irp = IoBuildDeviceIoControlRequest(IOCTL_ACPI_EVAL_METHOD, DeviceExtension->NextLowerDriver, InputBuffer, inputBufLen, *OutputBuffer, CONTROL_METHOD_BUFFER_SIZE, FALSE, &irpCompleted, &statusBlock); if (!irp) { ExFreePool(*OutputBuffer); return STATUS_INSUFFICIENT_RESOURCES; } irp->IoStatus.Status = STATUS_NOT_SUPPORTED; irp->IoStatus.Information = 0; status = IoCallDriver(DeviceExtension->NextLowerDriver, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&irpCompleted, Executive, KernelMode, FALSE, NULL); status = statusBlock.Status; } if (!NT_SUCCESS(status)) { ExFreePool(*OutputBuffer); } return status; } NTSTATUS AcpiEvaluateProcessorObject ( IN PFDO_DATA DeviceExtension, OUT PVOID *OutputBuffer ) /* Routine Description: This routine sends an IRP to ACPI to evaluate a processor object. Arguments: OutputBuffer- Return value(s) from object Return Value: NTSTATUS --*/ { NTSTATUS status; PIRP irp = NULL; KEVENT irpCompleted; IO_STATUS_BLOCK statusBlock; ULONG inputBufLen; DebugEnter(); PAGED_CODE(); KeInitializeEvent(&irpCompleted, NotificationEvent, FALSE); // // Allocate 1K for the output buffer. That should handle // everything that is necessary for ACPI 2.0 processor objects. // *OutputBuffer = ExAllocatePoolWithTag(PagedPool, sizeof(PROCESSOR_OBJECT_INFO), PROCESSOR_POOL_TAG); if (!*OutputBuffer) { return STATUS_INSUFFICIENT_RESOURCES; } // // Build the IRP. // irp = IoBuildDeviceIoControlRequest(IOCTL_GET_PROCESSOR_OBJ_INFO, DeviceExtension->NextLowerDriver, NULL, 0, *OutputBuffer, sizeof(PROCESSOR_OBJECT_INFO), FALSE, &irpCompleted, &statusBlock); if (!irp) { ExFreePool(*OutputBuffer); return STATUS_INSUFFICIENT_RESOURCES; } irp->IoStatus.Status = STATUS_NOT_SUPPORTED; irp->IoStatus.Information = 0; status = IoCallDriver(DeviceExtension->NextLowerDriver, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&irpCompleted, Executive, KernelMode, FALSE, NULL); status = statusBlock.Status; } if (!NT_SUCCESS(status)) { ExFreePool(*OutputBuffer); } return status; } NTSTATUS AcpiParseGenRegDesc( IN PUCHAR Buffer, OUT PGEN_ADDR *GenericAddress ) /*++ Routine Description: Arguments: Return Value: --*/ { DebugEnter(); PAGED_CODE(); if ((Buffer[0] != 0x82) || ((Buffer[1] != 0x0b) && (Buffer[1] != 0x0c)) || (Buffer[2] != 0)) { // // The buffer is not a Generic Register Descriptor. // DebugPrint((WARN, "ACPI BIOS error: _PTC object was not a Generic Register Descriptor\n")); return STATUS_NOT_FOUND; } // // The thing passes the sanity test. // *GenericAddress = ExAllocatePoolWithTag(PagedPool, sizeof(GEN_ADDR), PROCESSOR_POOL_TAG); if (!*GenericAddress) { return STATUS_INSUFFICIENT_RESOURCES; } // // toddcar - 10/31/2000 - TEMP // Need to remove this code once new Acpi2.0 bios's change to // reflect new register descriptor type. Defined in Acpi 2.0 errata 1.1 // if (Buffer[1] == 0x0b) { (*GenericAddress)->AddressSpaceID = Buffer[3]; (*GenericAddress)->BitWidth = Buffer[4]; (*GenericAddress)->BitOffset = Buffer[5]; (*GenericAddress)->Reserved = 0; RtlCopyMemory(&(*GenericAddress)->Address.QuadPart, &(Buffer[6]), sizeof(PHYSICAL_ADDRESS)); } else { RtlCopyMemory(*GenericAddress, &(Buffer[3]), sizeof(GEN_ADDR)); } return STATUS_SUCCESS; } NTSTATUS AcpiEvaluatePtc( IN PFDO_DATA DeviceExtension, OUT PGEN_ADDR *Address ) /*++ Routine Description: Arguments: Return Value: --*/ { PACPI_EVAL_OUTPUT_BUFFER ptcBuffer; NTSTATUS status; DebugEnter(); PAGED_CODE(); status = AcpiEvaluateMethod(DeviceExtension, "_PTC", NULL, &ptcBuffer); if (!NT_SUCCESS(status)) { return status; } ASSERT(ptcBuffer->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE); // // Sanity check the output buffer. (ACPI BIOSes can often be // wrong. // if (ptcBuffer->Count != 1) { DebugPrint((WARN, "ACPI BIOS error: _PTC object returned multiple objects\n")); status = STATUS_NOT_FOUND; goto AcpiEvaluatePtcExit; } if (ptcBuffer->Argument[0].Type != ACPI_METHOD_ARGUMENT_BUFFER) { DebugPrint((WARN, "ACPI BIOS error: _PTC object didn't return a buffer\n")); status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluatePtcExit; } if (ptcBuffer->Argument[0].DataLength != sizeof(GEN_ADDR) + 2) { // // The buffer is not the right size. // DebugPrint((WARN, "ACPI BIOS error: _PTC object returned a buffer of the wrong size\n")); status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluatePtcExit; } status = AcpiParseGenRegDesc(ptcBuffer->Argument[0].Data, Address); AcpiEvaluatePtcExit: ExFreePool(ptcBuffer); return status; } NTSTATUS AcpiEvaluateCst( IN PFDO_DATA DeviceExtension, OUT PACPI_CST_PACKAGE *CStates ) /* Routine Description: This routine finds and evaluates the _CST object in an ACPI 2.0 namespace. It returns the information in non-paged pool, as C-states must be entered and exited at DISPATCH_LEVEL. Arguments: DeviceExtension - FDO_DATA CStates - pointer to be filled in with return data Return Value: NTSTATUS --*/ { PACPI_EVAL_OUTPUT_BUFFER output; PACPI_METHOD_ARGUMENT arg, subArg; NTSTATUS status; ULONG cstateCount = 0; ULONG subElement; ULONG size; ULONG totalCStates; DebugEnter(); PAGED_CODE(); DebugAssert(CStates); *CStates = NULL; status = AcpiEvaluateMethod(DeviceExtension, "_CST", NULL, &output); if (!NT_SUCCESS(status)) { return status; } DebugAssert(output->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE); // // Parse the output buffer, figuring out what we got. See chapter // 8.3.2 of the ACPI 2.0 spec for details. // if (output->Count == 0) { // // There was nothing in the object. // status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluateCstExit; } // // The first object should be an integer that lists the number of // C-states. // if (output->Argument[0].Type != ACPI_METHOD_ARGUMENT_INTEGER) { // // The first element in the _CST package wasn't an // integer. // status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluateCstExit; } ASSERT(output->Argument[0].DataLength == sizeof(ULONG)); totalCStates = output->Argument[0].Argument; size = ((totalCStates - 1) * sizeof(ACPI_CST_DESCRIPTOR)) + sizeof(ACPI_CST_PACKAGE); *CStates = ExAllocatePoolWithTag(NonPagedPool, size, PROCESSOR_POOL_TAG); if (!*CStates) { status = STATUS_INSUFFICIENT_RESOURCES; goto AcpiEvaluateCstExit; } RtlZeroMemory(*CStates, size); (*CStates)->NumCStates = (UCHAR) totalCStates; // // Get second data element, should be a package // arg = &output->Argument[1]; while ((PUCHAR)arg < ((PUCHAR)output + output->Length)) { // // Crack the packages. // if (arg->Type == ACPI_METHOD_ARGUMENT_PACKAGE) { subArg = (PACPI_METHOD_ARGUMENT)(arg->Data); subElement = 0; // toddcar - 1/21/2001 - ISSUE // Currently there is no way to know if one our _CST // packages contained too few elements. // while ((PUCHAR)subArg < ((PUCHAR)(arg->Data) + arg->DataLength)) { // // In Chapter 8.3.2 of ACPI 2.0, these packages are // defined as having four elements each: // // C State_Register - Generic Register Descriptor // C State_Type - byte // Latency - word // Power_Consumption - dword // switch (subElement) { case 0: // // Looking at the buffer // ASSERT(subArg->Type == ACPI_METHOD_ARGUMENT_BUFFER); ASSERT(subArg->DataLength >= sizeof(ACPI_GENERIC_REGISTER_DESC)); if ((subArg->DataLength < sizeof(ACPI_GENERIC_REGISTER_DESC)) || (subArg->Type != ACPI_METHOD_ARGUMENT_BUFFER)) { DebugAssert(!"ACPI Bios Error: _CST Package[0] must be type Generic Register Descriptor"); status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluateCstExit; } RtlCopyMemory(&(*CStates)->State[cstateCount].Register, &(subArg->Data[3]), sizeof(GEN_ADDR)); break; case 1: if (subArg->Type != ACPI_METHOD_ARGUMENT_INTEGER) { DebugAssert(!"ACPI Bios Error: _CST Package item [1] must be type INTEGER"); status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluateCstExit; } ASSERT(!(subArg->Argument & 0xffffff00)); (*CStates)->State[cstateCount].StateType = (UCHAR)subArg->Argument; break; case 2: if (subArg->Type != ACPI_METHOD_ARGUMENT_INTEGER) { DebugAssert(!"ACPI Bios Error: _CST Package item[2] must be type INTEGER"); status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluateCstExit; } ASSERT(!(subArg->Argument & 0xffff0000)); (*CStates)->State[cstateCount].Latency = (USHORT)subArg->Argument; break; case 3: if (subArg->Type != ACPI_METHOD_ARGUMENT_INTEGER) { DebugAssert(!"ACPI Bios Error: _CST Package item[3] must be type INTEGER"); status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluateCstExit; } (*CStates)->State[cstateCount].PowerConsumption = subArg->Argument; break; default: // // There were more than four elements in the package. // ASSERT(FALSE); status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluateCstExit; } subArg = ACPI_METHOD_NEXT_ARGUMENT(subArg); subElement++; } } else { // // There was an object that wasn't a package. // DebugAssert(!"ACPI Bios Error: _CST[2..n] must be type PACKAGE"); status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluateCstExit; } arg = ACPI_METHOD_NEXT_ARGUMENT(arg); cstateCount++; } ASSERT(cstateCount == (output->Count - 1)); DumpCStates(*CStates); AcpiEvaluateCstExit: if (!NT_SUCCESS(status) && (*CStates != NULL)) { ExFreePool(*CStates); *CStates = NULL; } ExFreePool(output); DebugExitStatus(status); return status; } NTSTATUS AcpiEvaluatePct( IN PFDO_DATA DeviceExtension, OUT PACPI_PCT_PACKAGE *Address ) /*++ Routine Description: Arguments: Return Value: --*/ { PACPI_EVAL_OUTPUT_BUFFER pctBuffer; PACPI_METHOD_ARGUMENT arg; PGEN_ADDR genAddr; NTSTATUS status; ULONG pass = 0; DebugEnter(); PAGED_CODE(); ASSERT(Address); *Address = 0; status = AcpiEvaluateMethod(DeviceExtension, "_PCT", NULL, &pctBuffer); if (!NT_SUCCESS(status)) { return status; } ASSERT(pctBuffer->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE); // // Sanity check the output buffer. (ACPI BIOSes can often be // wrong. // if (pctBuffer->Count != 2) { DebugPrint((WARN, "ACPI BIOS error: _PCT object didn't return two objects\n")); status = STATUS_NOT_FOUND; goto AcpiEvaluatePctExit; } *Address = ExAllocatePoolWithTag(NonPagedPool, sizeof(ACPI_PCT_PACKAGE), PROCESSOR_POOL_TAG); if (!*Address) { status = STATUS_INSUFFICIENT_RESOURCES; goto AcpiEvaluatePctExit; } RtlZeroMemory(*Address, sizeof(ACPI_PCT_PACKAGE)); // // Traverse the package, parsing the elements. // arg = (PACPI_METHOD_ARGUMENT)pctBuffer->Argument; while ((PUCHAR)arg < (PUCHAR)pctBuffer + pctBuffer->Length) { if (arg->Type != ACPI_METHOD_ARGUMENT_BUFFER) { DebugPrint((WARN, "ACPI BIOS error: _PCT object didn't return a buffer\n")); status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluatePctExit; } if (arg->DataLength < sizeof(GEN_ADDR) + 2) { // // The buffer is not the right size. // DebugPrint((WARN, "ACPI BIOS error: _PCT object returned a buffer of the wrong size\n")); status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluatePctExit; } if (pass > 1) { // // Too many things in the package. // status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluatePctExit; } // // Both package elements should contain generic addresses. So parse one. // status = AcpiParseGenRegDesc(arg->Data, &genAddr); if (!NT_SUCCESS(status)) { goto AcpiEvaluatePctExit; } switch (pass) { case 0: // // The first object in a _PCT should be the Perf Control Register // RtlCopyMemory(&((*Address)->Control), genAddr, sizeof(*genAddr)); break; case 1: // // The second object in a _PCT should be the Perf Status Register // RtlCopyMemory(&((*Address)->Status), genAddr, sizeof(*genAddr)); } ExFreePool(genAddr); arg = ACPI_METHOD_NEXT_ARGUMENT(arg); pass++; } AcpiEvaluatePctExit: if (!NT_SUCCESS(status)) { if (*Address) { ExFreePool(*Address); *Address = NULL; } } ExFreePool(pctBuffer); return status; } NTSTATUS AcpiEvaluatePss( IN PFDO_DATA DeviceExtension, OUT PACPI_PSS_PACKAGE *Address ) /*++ Routine Description: Arguments: Return Value: --*/ { PACPI_EVAL_OUTPUT_BUFFER pssBuffer; PACPI_METHOD_ARGUMENT arg, subArg; NTSTATUS status; ULONG subElem, pState = 0; static UCHAR fieldOffsets[] = { FIELD_OFFSET(ACPI_PSS_DESCRIPTOR, CoreFrequency), FIELD_OFFSET(ACPI_PSS_DESCRIPTOR, Power), FIELD_OFFSET(ACPI_PSS_DESCRIPTOR, Latency), FIELD_OFFSET(ACPI_PSS_DESCRIPTOR, BmLatency), FIELD_OFFSET(ACPI_PSS_DESCRIPTOR, Control), FIELD_OFFSET(ACPI_PSS_DESCRIPTOR, Status) }; DebugEnter(); PAGED_CODE(); ASSERT(Address); *Address = 0; status = AcpiEvaluateMethod(DeviceExtension, "_PSS", NULL, &pssBuffer); if (!NT_SUCCESS(status)) { return status; } ASSERT(pssBuffer->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE); // // The _PSS object is a package of packages. So the number // of objects in the _PCT method will be the number of // sub-packages. The amount of memory we need is calculated // from that. // *Address = ExAllocatePoolWithTag(NonPagedPool, sizeof(ACPI_PSS_PACKAGE) + (sizeof(ACPI_PSS_DESCRIPTOR) * (pssBuffer->Count - 1)), PROCESSOR_POOL_TAG); if (!*Address) { status = STATUS_INSUFFICIENT_RESOURCES; goto AcpiEvaluatePssExit; } (*Address)->NumPStates = (UCHAR)pssBuffer->Count; // // Traverse the package, parsing the elements. // arg = (PACPI_METHOD_ARGUMENT)pssBuffer->Argument; while ((PUCHAR)arg < (PUCHAR)pssBuffer + pssBuffer->Length) { // // Each element in a _PSS should be a package. // if (arg->Type != ACPI_METHOD_ARGUMENT_PACKAGE) { status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluatePssExit; } // // Traverse the inner package. // subElem = 0; subArg = (PACPI_METHOD_ARGUMENT)arg->Data; while ((PUCHAR)subArg < ((PUCHAR)arg) + arg->DataLength) { // // All the elements in the inner packages of // a _PSS object should be integers. // if (subArg->Type != ACPI_METHOD_ARGUMENT_INTEGER) { status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluatePssExit; } if (subElem > 5) { // // There are too many elements in this package. // status = STATUS_ACPI_INVALID_ARGTYPE; goto AcpiEvaluatePssExit; } // // The next step is to fill in the proper field in the P-State // table. Do this by indexing across pState and subElem. // *(PULONG)(((PUCHAR)&(*Address)->State[pState]) + fieldOffsets[subElem]) = subArg->Argument; subArg = ACPI_METHOD_NEXT_ARGUMENT(subArg); subElem++; } arg = ACPI_METHOD_NEXT_ARGUMENT(arg); pState++; } ASSERT(pState == (*Address)->NumPStates); status = STATUS_SUCCESS; AcpiEvaluatePssExit: if (!NT_SUCCESS(status)) { if (*Address) ExFreePool(*Address); } ExFreePool(pssBuffer); return status; } NTSTATUS AcpiEvaluatePpc( IN PFDO_DATA DeviceExtension, OUT ULONG *AvailablePerformanceStates ) /*++ Routine Description: Arguments: Return Value: --*/ { PACPI_EVAL_OUTPUT_BUFFER ppcBuffer; NTSTATUS status; DebugEnter(); PAGED_CODE(); ASSERT(AvailablePerformanceStates); *AvailablePerformanceStates = 0; status = AcpiEvaluateMethod(DeviceExtension, "_PPC", NULL, &ppcBuffer); if (!NT_SUCCESS(status)) { return status; } ASSERT(ppcBuffer->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE); // // The _PPC object is an integer. // ASSERT(ppcBuffer->Count == 1); ASSERT(ppcBuffer->Argument[0].Type == ACPI_METHOD_ARGUMENT_INTEGER); *AvailablePerformanceStates = ppcBuffer->Argument[0].Argument; ExFreePool(ppcBuffer); return status; } NTSTATUS InitializeAcpi2PStatesGeneric( IN PFDO_DATA DeviceExtension ) /*++ Routine Description: This routine evaluates _PSS and _PCT, then builds the performance state array. Note: The caller must hold PerfStateLock. Arguments: DeviceExtension Return Value: A NTSTATUS code to indicate the result of the initialization. --*/ { NTSTATUS status; PACPI_PCT_PACKAGE pctPackage = NULL; DebugEnter(); PAGED_CODE(); // // We automatically fail to use the Acpi 2.0 interface // if (Globals.HackFlags & DISABLE_ACPI20_INTERFACE_FLAG) { DebugPrint((ERROR, " Acpi 2.0 Interface Disabled\n")); return STATUS_NOT_FOUND; } // // Fill in the DeviceExtension with _PSS and _PCT. // status = AcpiEvaluatePss(DeviceExtension, &DeviceExtension->PssPackage); if (!NT_SUCCESS(status)) { goto InitializeAcpiPerformanceStatesExit; } status = AcpiEvaluatePct(DeviceExtension, &pctPackage); if (!NT_SUCCESS(status)) { goto InitializeAcpiPerformanceStatesExit; } RtlCopyMemory(&(DeviceExtension->PctPackage), pctPackage, sizeof(ACPI_PCT_PACKAGE)); // // The _PCT object may have pointed to registers in Memory space. // If so, we need virtual addresses for these physical addresses. // if (DeviceExtension->PctPackage.Control.AddressSpaceID == AcpiGenericSpaceMemory) { DeviceExtension->PctPackage.Control.Address.QuadPart = (ULONG_PTR) MmMapIoSpace(DeviceExtension->PctPackage.Control.Address, DeviceExtension->PctPackage.Control.BitWidth / 8, MmNonCached); if (!DeviceExtension->PctPackage.Control.Address.QuadPart) { status = STATUS_INVALID_PARAMETER; goto InitializeAcpiPerformanceStatesExit; } } if (DeviceExtension->PctPackage.Status.AddressSpaceID == AcpiGenericSpaceMemory) { DeviceExtension->PctPackage.Status.Address.QuadPart = (ULONG_PTR) MmMapIoSpace(DeviceExtension->PctPackage.Status.Address, DeviceExtension->PctPackage.Status.BitWidth / 8, MmNonCached); if (!DeviceExtension->PctPackage.Status.Address.QuadPart) { status = STATUS_INVALID_PARAMETER; goto InitializeAcpiPerformanceStatesExit; } } // // Merge these states in with other available states. // status = MergePerformanceStates(DeviceExtension); // // Notify the bios we are taking control // if (NT_SUCCESS(status)) { AssumeProcessorPerformanceControl(); } InitializeAcpiPerformanceStatesExit: if (!NT_SUCCESS(status)) { // // Something went wrong. Blow away the mess. // if (DeviceExtension->PssPackage) { ExFreePool(DeviceExtension->PssPackage); DeviceExtension->PssPackage = NULL; } } if (pctPackage) { ExFreePool(pctPackage); } DebugExitStatus(status); return status; } NTSTATUS AcpiFindRsdt ( 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: RsdtPtr - Pointer to a buffer that contains the ACPI Root System Description Pointer Structure. The caller is responsible for freeing this buffer. Note: This is returned in non-paged pool. 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; PLEGACY_GEYSERVILLE_INT15 int15Info; DebugEnter(); 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)) { DebugPrint((ERROR, "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... // DebugPrint((ERROR, "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 = GetRegistryValue (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 = GetRegistryValue(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)) + sizeof(LEGACY_GEYSERVILLE_INT15); *AcpiMulti = (PACPI_BIOS_MULTI_NODE) ExAllocatePoolWithTag(NonPagedPool, multiNodeSize, PROCESSOR_POOL_TAG); if (*AcpiMulti == NULL) { ExFreePool(valueInfo); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(*AcpiMulti, multiNodeSize); RtlCopyMemory(*AcpiMulti, multiNode, multiNodeSize - sizeof(LEGACY_GEYSERVILLE_INT15)); // // Geyserville BIOS information is appended to the E820 entries. Unfortunately, // there is no way to know if it is there. So wrap the code in a try/except. // try { int15Info = (PLEGACY_GEYSERVILLE_INT15)&(multiNode->E820Entry[multiNode->Count]); if (int15Info->Signature == 'GS') { // // This BIOS supports Geyserville. // RtlCopyMemory(((PUCHAR)*AcpiMulti + multiNodeSize - sizeof(LEGACY_GEYSERVILLE_INT15)), int15Info, sizeof(LEGACY_GEYSERVILLE_INT15)); } } except (EXCEPTION_EXECUTE_HANDLER) { *((PUSHORT)((PUCHAR)*AcpiMulti + multiNodeSize - sizeof(LEGACY_GEYSERVILLE_INT15))) = 0; } ExFreePool(valueInfo); return STATUS_SUCCESS; } NTSTATUS GetRegistryValue( 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; //DebugEnter(); 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 = ExAllocatePoolWithTag(PagedPool, keyValueLength, PROCESSOR_POOL_TAG); 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; } PVOID GetAcpiTable( IN ULONG Signature ) /*++ Routine Description: This routine will retrieve any table referenced in the ACPI RSDT. Arguments: Signature - Target table signature Return Value: pointer to a copy of the table, or NULL if not found --*/ { PACPI_BIOS_MULTI_NODE multiNode; NTSTATUS status; ULONG entry, rsdtEntries; PDESCRIPTION_HEADER header; PHYSICAL_ADDRESS physicalAddr; PRSDT rsdt; PVOID table = NULL; DebugEnter(); PAGED_CODE(); status = AcpiFindRsdt(&multiNode); if (!NT_SUCCESS(status)) { return NULL; } rsdt = MmMapIoSpace(multiNode->RsdtAddress, sizeof(RSDT) + (100 * sizeof(PHYSICAL_ADDRESS)), MmCached); ExFreePool(multiNode); if (!rsdt) { return NULL; } // // Do a sanity check on the RSDT. // if ((rsdt->Header.Signature != RSDT_SIGNATURE) && (rsdt->Header.Signature != XSDT_SIGNATURE)) { goto GetAcpiTableEnd; } // // Calculate the number of entries in the RSDT. // rsdtEntries = rsdt->Header.Signature == XSDT_SIGNATURE ? NumTableEntriesFromXSDTPointer(rsdt) : NumTableEntriesFromRSDTPointer(rsdt); // // Look down the pointer in each entry to see if it points to // the table we are looking for. // for (entry = 0; entry < rsdtEntries; entry++) { // // BUGBUG: should the highpart always be zero ? ie: what about PAE & // WIN64 ? are other places in this module also susceptible to this ? // if (rsdt->Header.Signature == XSDT_SIGNATURE) { physicalAddr = ((PXSDT)rsdt)->Tables[entry]; } else { physicalAddr.HighPart = 0; physicalAddr.LowPart = (ULONG)rsdt->Tables[entry]; } header = MmMapIoSpace(physicalAddr, PAGE_SIZE * 2, MmCached); if (!header) { goto GetAcpiTableEnd; } if (header->Signature == Signature) { break; } MmUnmapIoSpace(header, PAGE_SIZE * 2); } if (entry == rsdtEntries) { goto GetAcpiTableEnd; } table = ExAllocatePoolWithTag(PagedPool, header->Length, PROCESSOR_POOL_TAG); if (table) { RtlCopyMemory(table, header, header->Length); } MmUnmapIoSpace(header, PAGE_SIZE * 2); GetAcpiTableEnd: MmUnmapIoSpace(rsdt, sizeof(RSDT) + (100 * sizeof(PHYSICAL_ADDRESS))); return table; } NTSTATUS AcquireAcpiInterfaces( PFDO_DATA DeviceExtension ) /*++ Routine Description: This routine sends an IRP to the ACPI driver to get the funtion pointer table for the standard ACPI direct-call interfaces. Arguments: DeviceExtension Return Value: NTSTATUS --*/ { KEVENT event; NTSTATUS status, callbackStatus; PIRP irp; IO_STATUS_BLOCK ioStatusBlock; PIO_STACK_LOCATION irpStack; PACPI_INTERFACE_STANDARD acpiInterfaces = NULL; DebugEnter(); PAGED_CODE(); ASSERT(DeviceExtension->DevicePnPState == NotStarted); ASSERT(DeviceExtension->AcpiInterfaces == NULL); KeInitializeEvent( &event, NotificationEvent, FALSE ); acpiInterfaces = ExAllocatePoolWithTag(PagedPool, sizeof(ACPI_INTERFACE_STANDARD), PROCESSOR_POOL_TAG); if (!acpiInterfaces) { return STATUS_INSUFFICIENT_RESOURCES; } irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, DeviceExtension->NextLowerDriver, NULL, 0, NULL, &event, &ioStatusBlock); if (!irp) { status = STATUS_INSUFFICIENT_RESOURCES; goto AcquireAcpiInterfacesExit; } irpStack = IoGetNextIrpStackLocation(irp); irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE; irpStack->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_ACPI_INTERFACE_STANDARD; irpStack->Parameters.QueryInterface.Size = sizeof(ACPI_INTERFACE_STANDARD); irpStack->Parameters.QueryInterface.Version = 1; irpStack->Parameters.QueryInterface.Interface = (PINTERFACE) acpiInterfaces; irpStack->Parameters.QueryInterface.InterfaceSpecificData = NULL; // // Initialize the status to error in case the ACPI driver decides not to // set it correctly. // irp->IoStatus.Status = STATUS_NOT_SUPPORTED ; status = IoCallDriver( DeviceExtension->NextLowerDriver, irp ); if (!NT_SUCCESS(status)) { goto AcquireAcpiInterfacesExit; } if (status == STATUS_PENDING) { KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL ); status = ioStatusBlock.Status; } if (NT_SUCCESS(status)) { DeviceExtension->AcpiInterfaces = acpiInterfaces; // // Reference the interface. // if (DeviceExtension->AcpiInterfaces->InterfaceReference) { DeviceExtension->AcpiInterfaces->InterfaceReference(DeviceExtension->AcpiInterfaces->Context); } // // Register for notification callbacks. // callbackStatus = DeviceExtension->AcpiInterfaces->RegisterForDeviceNotifications( DeviceExtension->UnderlyingPDO, AcpiNotifyCallback, DeviceExtension ); if (!NT_SUCCESS(callbackStatus)) { DebugAssert(!"AcpiInterfaces->RegisterForDeviceNotifications() Failed!"); if (DeviceExtension->AcpiInterfaces->InterfaceDereference) { DeviceExtension->AcpiInterfaces->InterfaceDereference(DeviceExtension->AcpiInterfaces->Context); } DeviceExtension->AcpiInterfaces = NULL; status = callbackStatus; goto AcquireAcpiInterfacesExit; } } AcquireAcpiInterfacesExit: if (!NT_SUCCESS(status)) { if (acpiInterfaces) { ExFreePool(acpiInterfaces); } } return status; } NTSTATUS ReleaseAcpiInterfaces( PFDO_DATA DeviceExtension ) /*++ Routine Description: This routine releases the ACPI interfaces. Arguments: DeviceExtension Return Value: NTSTATUS --*/ { DebugEnter(); PAGED_CODE(); ASSERT(DeviceExtension->DevicePnPState == Deleted); ASSERT(DeviceExtension->AcpiInterfaces != NULL); // // Unregister for device notification. // DeviceExtension->AcpiInterfaces->UnregisterForDeviceNotifications( DeviceExtension->UnderlyingPDO, AcpiNotifyCallback ); // // Dereference the interface. // DeviceExtension->AcpiInterfaces->InterfaceDereference(DeviceExtension->AcpiInterfaces->Context); DeviceExtension->AcpiInterfaces = NULL; return STATUS_SUCCESS; } VOID AcpiNotifyCallback( PVOID Context, ULONG NotifyCode ) /*++ Routine Description: Arguments: Return Value: --*/ { PFDO_DATA DeviceExtension = (PFDO_DATA)Context; PIO_WORKITEM workItem; DebugEnter(); if ((DeviceExtension->DevicePnPState != Started) || (DeviceExtension->LegacyInterface)) { // // Ignore notifications that come in while the device // isn't started, or if we are using the legacy interface. // return; } // // Allocate work item // workItem = IoAllocateWorkItem(DeviceExtension->Self); if (!workItem) { DebugPrint((ERROR, "IoAllocateWorkItem() Failed!\n")); return; // STATUS_INSUFFICIENT_RESOURCES } switch (NotifyCode) { case 0x80: IoQueueWorkItem(workItem, AcpiNotify80CallbackWorker, DelayedWorkQueue, workItem); break; case 0x81: IoQueueWorkItem(workItem, AcpiNotify81CallbackWorker, DelayedWorkQueue, workItem); break; default: DebugPrint((ERROR, "Unrecognized Notify code (0x%x)\n", NotifyCode)); IoFreeWorkItem(workItem); break; } return; } VOID AcpiNotify80CallbackWorker( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) /*++ Routine Description: Arguments: DeviceObject - Context - If we were called as part of a WorkItem, "Context" is a pointer to the WorkItem, otherwise, this value is NULL Return Value: --*/ { NTSTATUS status; PFDO_DATA DeviceExtension = (PFDO_DATA) DeviceObject->DeviceExtension; PPROCESSOR_PERFORMANCE_STATES oldPerfStates; PROCESSOR_PERFORMANCE_STATES nullPerfStates = {NULL, 0, 0, 0, {0,0,0}}; DebugEnter(); PAGED_CODE(); // // if called as WorkItem, free worker resources // if (Context) { IoFreeWorkItem((PIO_WORKITEM) Context); } if (!DeviceExtension->PssPackage) { // // This machine has no _PSS package, so // this notification shouldn't do anything. // return; } AcquireProcessorPerfStateLock(DeviceExtension); // // Register zero ACPI 2.0 performance states with the // kernel so that no state gets invoked while we're // screwing around with the DeviceExtension. // oldPerfStates = DeviceExtension->PerfStates; DeviceExtension->PerfStates = &nullPerfStates; status = RegisterStateHandlers(DeviceExtension); // // Need to put the orginal states back // DeviceExtension->PerfStates = oldPerfStates; if (!NT_SUCCESS(status)) { DebugAssert(!"RegisterStateHandlers(NULL PerfStates) Failed!"); goto AcpiNotify80CallbackWorkerExit; } // // Calculate currently available states. // NOTE: MergePerformanceStates will invalidate CurrentPerfState // status = MergePerformanceStates(DeviceExtension); if (!NT_SUCCESS(status)) { goto AcpiNotify80CallbackWorkerExit; } // // Register new perf states with the kernel. // status = RegisterStateHandlers(DeviceExtension); ASSERT(NT_SUCCESS(status)); AcpiNotify80CallbackWorkerExit: if (!NT_SUCCESS(status)) { // // Something went wrong. Blow away the mess. // if (DeviceExtension->PerfStates) { ExFreePool(DeviceExtension->PerfStates); DeviceExtension->PerfStates = NULL; DeviceExtension->CurrentPerfState = INVALID_PERF_STATE; } } ReleaseProcessorPerfStateLock(DeviceExtension); // // Notify anyone who might be interested // ProcessorFireWmiEvent(DeviceExtension, &NewPStatesEvent, &DeviceExtension->PpcResult); } VOID AcpiNotify81CallbackWorker( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status; PFDO_DATA DeviceExtension = (PFDO_DATA) DeviceObject->DeviceExtension; PPROCESSOR_IDLE_STATES oldCStates; PROCESSOR_IDLE_STATES nullCStates = {0,{0,0,{0,0,0,0,{0,0}},NULL}}; DebugEnter(); PAGED_CODE(); // // Free worker resources // IoFreeWorkItem((PIO_WORKITEM) Context); if (!DeviceExtension->CstPresent) { // // This machine has no _CST package, so // this notification shouldn't do anything. // return; } AcquireProcessorPerfStateLock(DeviceExtension); // // Register zero ACPI 2.0 performance states with the // kernel so that no state gets invoked while we're // screwing around with the DeviceExtension. // oldCStates = DeviceExtension->CStates; DeviceExtension->CStates = &nullCStates; status = RegisterStateHandlers(DeviceExtension); // // restore previous CStates // DeviceExtension->CStates = oldCStates; if (!NT_SUCCESS(status)) { DebugAssert(!"RegisterStateHandlers(NULL CStates) Failed!"); goto AcpiNotify81CallbackWorkerExit; } // // Calculate currently available Cstates. // status = InitializeAcpi2IoSpaceCstates(DeviceExtension); if (!NT_SUCCESS(status)) { goto AcpiNotify81CallbackWorkerExit; } // // Register new perf states with the kernel. // status = RegisterStateHandlers(DeviceExtension); ASSERT(NT_SUCCESS(status)); AcpiNotify81CallbackWorkerExit: if (!NT_SUCCESS(status)) { // // Something went wrong. Blow away the mess. // if (DeviceExtension->CStates) { ExFreePool(DeviceExtension->CStates); DeviceExtension->CStates = NULL; } } ReleaseProcessorPerfStateLock(DeviceExtension); // // Notify anyone who might be interested // ProcessorFireWmiEvent(DeviceExtension, &NewCStatesEvent, NULL); } NTSTATUS Acpi2PerfStateTransitionGeneric( IN PFDO_DATA DeviceExtension, IN ULONG State ) /*++ Routine Description: This routine changes the performance state of the processor based on ACPI 2.0 performance state objects. NOTE: This function only understands I/O and Memory addresses, not FFH addresses. Arguments: State - Index into _PSS Return Value: none --*/ { ULONG statusValue = 0; NTSTATUS status = STATUS_SUCCESS; DebugEnter(); DebugAssert(State >= DeviceExtension->PpcResult); DebugAssert(State < DeviceExtension->PssPackage->NumPStates); DebugAssert(DeviceExtension->PctPackage.Control.Address.QuadPart); DebugAssert(DeviceExtension->PctPackage.Status.Address.QuadPart); // // Write Control value // WriteGenAddr(&DeviceExtension->PctPackage.Control, DeviceExtension->PssPackage->State[State].Control); // // Get Status Value // statusValue = ReadGenAddr(&DeviceExtension->PctPackage.Status); // // Check to see if the status register matches what we expect. // if (statusValue != DeviceExtension->PssPackage->State[State].Status) { DebugPrint((ERROR, "Acpi2PerfStateTransitionGeneric: Transition failed! Expected 0x%x status value, recieved 0x%x\n", DeviceExtension->PssPackage->State[State].Status, statusValue)); status = STATUS_UNSUCCESSFUL; } DebugExitStatus(status); return status; } NTSTATUS AcpiPerfStateTransition ( IN PFDO_DATA DeviceExtension, IN ULONG State ) /*++ Routine Description: Arguments: State - Index into DeviceExtension->PerfStates Return Value: --*/ { // // Legacy drivers may use the PssPackage variable, so // we first check for legacy, all legacy drivers have // this flag set. // if (DeviceExtension->LegacyInterface) { return AcpiLegacyPerfStateTransition(DeviceExtension, State); } else if (DeviceExtension->PssPackage) { // // The State index passed in reflects an index in the current PerfStates // registered with the kernel. Must convert it to an index into the _PSS. // return Acpi2PerfStateTransition(DeviceExtension, State + DeviceExtension->PpcResult); } return STATUS_NOT_IMPLEMENTED; } NTSTATUS InitializeAcpi2IoSpaceCstates( IN PFDO_DATA DeviceExtension ) /*++ Routine Description: This function looks to see if there is an ACPI 2.0 _CST object in the namespace, and, if it is present and _does not_ contain CStates that reference funtionly fixed hardware registers, it replaces the functions found by InitializeAcpi1Cstates. This generic driver has no knowledge of processor specific registers Note: This is a little bit ridiculous, as the generic processor driver can't possibly know how to use a C-state that it couldn't find via ACPI 1.0 means. Never-the-less, we should respect what we find in a _CST, if for no other reason than that this code may be used as an example in a more complex driver. Further note: This function leaves the filling in of throttling functions to the InitializePerformanceStates functions. Arguments: DeviceExtension Return Value: A NTSTATUS code to indicate the result of the initialization. --*/ { #define HIGHEST_SUPPORTED_CSTATE 3 PPROCESSOR_IDLE_STATES iStates; PACPI_CST_PACKAGE cstData = NULL; NTSTATUS status; ULONG i; UCHAR cState; ULONG size; DebugEnter(); PAGED_CODE(); // // Find the _CST // status = AcpiEvaluateCst(DeviceExtension, &cstData); if (!NT_SUCCESS(status)) { goto InitializeAcpi2IoSpaceCstatesExit; } // // The namespace contains a _CST package. So we should // use it instead of ACPI 1.0 C-states. // if (DeviceExtension->CStates) { // // There were 1.0 C-states. Get rid of them. // ExFreePool(DeviceExtension->CStates); DeviceExtension->CStates = NULL; } // // Currently we only support 3 C States. We can't allocate based on // the number of _CST cstates, as there may be more than we support // size = (sizeof(PROCESSOR_IDLE_STATE) * (HIGHEST_SUPPORTED_CSTATE - 1)) + sizeof(PROCESSOR_IDLE_STATES); iStates = ExAllocatePoolWithTag(NonPagedPool, size, PROCESSOR_POOL_TAG); if (!iStates) { status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeAcpi2IoSpaceCstatesExit; } // // Collect Acpi 2.0 CState info // DeviceExtension->CStates = iStates; DeviceExtension->CstPresent = TRUE; // // We always support C1. // iStates->State[0].StateType = 1; RtlZeroMemory(&(iStates->State[0].Register), sizeof(GEN_ADDR)); iStates->State[0].Latency = 0; iStates->State[0].IdleHandler = AcpiC1Idle; // // We only support C2 & C3 on UP machines // if (!Globals.SingleProcessorProfile) { goto InitializeAcpi2IoSpaceCstatesExit; } // // Hunt through the _CST package looking for supported (and useful) states. // NOTE: if the _CST contains multiple definitions for C2 or C3, we will // use deepest state, ie the one that offers the greatest power savings. // // // Start looking at C2 // cState = 2; for (i = 0; i < cstData->NumCStates; i++) { if (cstData->State[i].StateType == cState) { DebugPrint((INFO, "Found CState C%u\n", cState)); // // Look ahead to see if another identicle C state with greater power // savings exists. // while((i+1 < cstData->NumCStates) && (cstData->State[i+1].StateType == cState) && (cstData->State[i+1].PowerConsumption < cstData->State[i].PowerConsumption) && (cstData->State[i+1].Register.AddressSpaceID == AcpiGenericSpaceIO)) { i++; } // // We have found a state in the package that matches the one we're // looking for. See if we think that it's usable. This function // only knows how to use ACPI 1.0-compatible C-states. So anything // that is not in I/O space is out of bounds. // if (cstData->State[i].Register.AddressSpaceID != AcpiGenericSpaceIO) { DebugPrint((ERROR, "InitializeAcpi2IoSpaceCstates() only supports CStates in I/O space\n")); continue; } iStates->State[cState - 1].StateType = cState; iStates->State[cState - 1].Register = cstData->State[i].Register; iStates->State[cState - 1].Latency = cstData->State[i].Latency; switch (cState) { case 2: iStates->State[cState - 1].IdleHandler = Acpi2C2Idle; C2Address = iStates->State[cState - 1].Register; break; case 3: iStates->State[cState - 1].IdleHandler = Acpi2C3ArbdisIdle; C3Address = iStates->State[cState - 1].Register; break; default: DebugAssert(!"Found Unsupported CState") break; } // // Look for next state // cState++; // // if we found C3, then we are finished // if (cState > HIGHEST_SUPPORTED_CSTATE) { break; } } } // // "Count" represents the highest supported C State found, // must be < MAX_IDLE_HANDLERS. // iStates->Count = cState - 1; InitializeAcpi2IoSpaceCstatesExit: if (cstData) { ExFreePool(cstData); } DebugExitStatus(status); return status; } VOID AssumeProcessorPerformanceControl ( VOID ) /*++ Routine Description: Arguments: Return Value: --*/ { DebugEnter(); DebugAssert(HalpFixedAcpiDescTable.smi_cmd_io_port); // // In Acpi 2.0, the FADT->pstate_control contains the magic value to write to // the SMI Command port to turn off bios control of processor performance control // if (HalpFixedAcpiDescTable.pstate_control) { WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR) HalpFixedAcpiDescTable.smi_cmd_io_port, HalpFixedAcpiDescTable.pstate_control); } } VOID AssumeCStateControl ( VOID ) /*++ Routine Description: Arguments: Return Value: --*/ { DebugEnter(); DebugAssert(HalpFixedAcpiDescTable.smi_cmd_io_port); // // In Acpi 2.0, the FADT->cstate_control contains the magic value to write to // the SMI Command port to turn off bios control of Acpi 2.0 Cstates // if (HalpFixedAcpiDescTable.cstate_control) { WRITE_PORT_UCHAR((PUCHAR)(ULONG_PTR) HalpFixedAcpiDescTable.smi_cmd_io_port, HalpFixedAcpiDescTable.cstate_control); } } NTSTATUS GetRegistryDwordValue ( IN PWCHAR RegKey, IN PWCHAR ValueName, OUT PULONG RegData ) /*++ Routine Description: Arguments: Return Value: NTSTATUS --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; ULONG_PTR zero = 0; RTL_QUERY_REGISTRY_TABLE paramTable[2] = {0}; // terminate with null table entry paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = ValueName; paramTable[0].EntryContext = RegData; paramTable[0].DefaultType = REG_DWORD; paramTable[0].DefaultData = &zero; paramTable[0].DefaultLength = sizeof(zero); ntStatus = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, RegKey, ¶mTable[0], NULL, // Context NULL); // Environment return ntStatus; } NTSTATUS SetRegistryStringValue ( IN PWCHAR RegKey, IN PWCHAR ValueName, IN PWCHAR String ) /*++ Routine Description: Arguments: Return Value: NTSTATUS --*/ { return RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, RegKey, ValueName, REG_SZ, String, (wcslen(String)+1) * sizeof(WCHAR)); } NTSTATUS GetRegistryStringValue ( IN PWCHAR RegKey, IN PWCHAR ValueName, OUT PUNICODE_STRING RegString ) /*++ Routine Description: Arguments: Return Value: NTSTATUS --*/ { NTSTATUS status; ULONG_PTR zero = 0; RTL_QUERY_REGISTRY_TABLE paramTable[2] = {0}; // terminate with null table entry DebugEnter(); DebugAssert(RegString); RtlZeroMemory(RegString, sizeof(UNICODE_STRING)); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = ValueName; paramTable[0].EntryContext = RegString; paramTable[0].DefaultType = REG_SZ; paramTable[0].DefaultData = &zero; paramTable[0].DefaultLength = sizeof(zero); status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, RegKey, ¶mTable[0], NULL, // Context NULL); // Environment DebugExitStatus(status); return status; } #ifdef _X86_ NTSTATUS FASTCALL SetPerfLevelGeneric( IN UCHAR Throttle, IN PFDO_DATA DeviceExtension ) /*++ Routine Description: Arguments: Return Value: --*/ { ULONG newState, lowestPerfState; ULONG throttleValue; NTSTATUS status = STATUS_SUCCESS; //DebugEnter(); DebugAssert(DeviceExtension); DebugPrint((TRACE, "SetPerfLevelGeneric: Throttling to %u%%\n", Throttle)); // // Save Throttle uncase we aren't able to Throttle // DeviceExtension->LastRequestedThrottle = Throttle; // // Run through the performance states looking for one // that matches this throttling level. // for (newState = 0; newState < DeviceExtension->PerfStates->Count; newState++) { if (DeviceExtension->PerfStates->State[newState].PercentFrequency <= Throttle) { DebugPrint((TRACE, " Found Match! PerfState = %u, Freq %u%%\n", newState, DeviceExtension->PerfStates->State[newState].PercentFrequency)); break; } } if (newState >= DeviceExtension->PerfStates->Count) { DebugPrint((ERROR, "Couldn't find match for throttle request of %u%%\n", Throttle)); status = STATUS_UNSUCCESSFUL; goto SetPerfLevelGenericExit; } if (newState == DeviceExtension->CurrentPerfState) { // // No work to do. // goto SetPerfLevelGenericExit; } // // NOTE: The current state maybe invalid ie. 0xff, this happens notify(0x80). // lowestPerfState = DeviceExtension->LowestPerfState; if (newState <= lowestPerfState) { // // If throttling is on, turn it off. // if (DeviceExtension->ThrottleValue) { ProcessorThrottle((UCHAR)HalpThrottleScale); DeviceExtension->ThrottleValue = 0; } status = AcpiPerfStateTransition(DeviceExtension, newState); } else { // // Throttle states/percentages are build from the lowest Perf State, make // sure we are currently in the lowest perf state. // if (DeviceExtension->CurrentPerfState != lowestPerfState) { AcpiPerfStateTransition(DeviceExtension, lowestPerfState); } // // this state is a throttle state, so throttle even if the Transition to // low volts fails. // throttleValue = HalpThrottleScale - (newState - lowestPerfState); DebugAssert(throttleValue); DebugAssert(HalpThrottleScale != throttleValue); ProcessorThrottle((UCHAR)throttleValue); DeviceExtension->ThrottleValue = throttleValue; status = STATUS_SUCCESS; } // // Keep track of the state we just set. // DeviceExtension->LastTransitionResult = status; if (NT_SUCCESS(status)) { DeviceExtension->CurrentPerfState = newState; } // // Notify any interested parties // if (PStateEvent.Enabled) { PSTATE_EVENT data; data.State = newState; data.Status = status; data.Latency = 0; // latency data.Speed = DeviceExtension->PerfStates->State[newState].Frequency; ProcessorFireWmiEvent(DeviceExtension, &PStateEvent, &data); } SetPerfLevelGenericExit: return status; } NTSTATUS FASTCALL SetThrottleLevelGeneric ( IN UCHAR Throttle, IN PFDO_DATA DeviceExtension ) /*++ Routine Description: Arguments: Return Value: --*/ { UCHAR adjustedState; UCHAR state; UCHAR numAcpi2PerfStates; DebugEnter(); DebugAssert(DeviceExtension); DebugPrint((TRACE, " Throttling to %u%%\n", Throttle)); // // Save Throttle in case we aren't able to Throttle // DeviceExtension->LastRequestedThrottle = Throttle; // // Run through the performance states looking for one // that matches this throttling level. // for (state = 0; state < DeviceExtension->PerfStates->Count; state++) { if (DeviceExtension->PerfStates->State[state].PercentFrequency <= Throttle) { break; } } // // We didn't find a match, or HalpThrottleScale is incorrect. // Either case is a problem. // if ((state >= DeviceExtension->PerfStates->Count) || (state >= HalpThrottleScale)) { DebugAssert(!"SetThrottleLevel() Invalid state!"); return STATUS_UNSUCCESSFUL; } if (state == DeviceExtension->CurrentPerfState) { return STATUS_SUCCESS; } // // if state == 0, then we are turning stop throttling off. // ProcessorThrottle((UCHAR)HalpThrottleScale - state); DeviceExtension->ThrottleValue = HalpThrottleScale - state; DeviceExtension->CurrentPerfState = state; // // Notify any interested parties // if (PStateEvent.Enabled) { PSTATE_EVENT data; data.State = state; data.Status = STATUS_SUCCESS; data.Latency = 0; // latency data.Speed = DeviceExtension->PerfStates->State[state].Frequency; ProcessorFireWmiEvent(DeviceExtension, &PStateEvent, &data); } return STATUS_SUCCESS; } #endif NTSTATUS MergePerformanceStatesGeneric( IN PFDO_DATA DeviceExtension ) /*++ Routine Description: This routine looks at the performance states in the device extension. Note: The caller must hold PerfStateLock. Arguments: DeviceExtension Return Value: A NTSTATUS code to indicate the result of the initialization. NOTE: This is called during START_DEVICE, and after recieving a Notify(0x80) on the processor. --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG oldBuffSize, newBuffSize, state; ULONG availablePerfStates, numThrottlingStates; ULONG lowestPerfState, lowestPerfStateFreq; ULONG maxFreq, maxTransitionLatency = 0; PPROCESSOR_PERFORMANCE_STATES newPerfStates; DebugEnter(); PAGED_CODE(); // // Find out how many performance states this machine currently supports // by evaluating the ACPI 2.0 _PPC object. // if (DeviceExtension->PssPackage) { status = BuildAvailablePerfStatesFromPss(DeviceExtension); if (!NT_SUCCESS(status)) { goto MergePerformanceStatesExit; } } // // We may have already found Acpi 2.0 or Legacy performance states // we need to add those to any duty-cycle throttling states supported. // So allocate a buffer big enough to hold them all. // DebugAssert(DeviceExtension->PerfStates); availablePerfStates = DeviceExtension->PerfStates->Count; oldBuffSize = sizeof(PROCESSOR_PERFORMANCE_STATES) + (sizeof(PROCESSOR_PERFORMANCE_STATE) * (availablePerfStates - 1)); // // Calculate addition supported throttling states // numThrottlingStates = GetNumThrottleSettings(DeviceExtension); availablePerfStates += numThrottlingStates; newBuffSize = oldBuffSize + (sizeof(PROCESSOR_PERFORMANCE_STATE) * numThrottlingStates); newPerfStates = ExAllocatePoolWithTag(NonPagedPool, newBuffSize, PROCESSOR_POOL_TAG); if (!newPerfStates) { status = STATUS_INSUFFICIENT_RESOURCES; goto MergePerformanceStatesExit; } RtlZeroMemory(newPerfStates, newBuffSize); DebugAssert(newBuffSize >= oldBuffSize); RtlCopyMemory(newPerfStates, DeviceExtension->PerfStates, oldBuffSize); // // Figure out which performance states to keep. At present, // we believe that there is no advantage to throttling at any // voltage other than the lowest. E.g. we will drop voltage until // there is no more voltage to drop, throttling after that. // maxFreq = GetMaxProcFrequency(DeviceExtension); // // Now cycle through any throttling states, appending them if appropriate. // PerfStates->Count - 1 is the lowest perf state. This state will be the one // used to calculate frequency and power values from for throttling states. // lowestPerfState = DeviceExtension->PerfStates->Count - 1; lowestPerfStateFreq = DeviceExtension->PerfStates->State[lowestPerfState].Frequency; for (state = lowestPerfState + 1; state < numThrottlingStates + lowestPerfState; state++) { // // Frequency is some fraction of the lowest perf state frequency. // newPerfStates->State[state].Frequency = lowestPerfStateFreq * (numThrottlingStates - (state - lowestPerfState)) / numThrottlingStates; newPerfStates->State[state].PercentFrequency = (UCHAR)PERCENT_TO_PERF_LEVEL((newPerfStates->State[state].Frequency * 100) / maxFreq); DebugAssert(newPerfStates->State[state].PercentFrequency <= 100); // // Mark this state as a Throttling State // newPerfStates->State[state].Flags = PROCESSOR_STATE_TYPE_THROTTLE; // // Stop processing when we have found all states greater than 200mhz or // 25% of max speed // #define LOWEST_USABLE_FREQUENCY 200 #define REQUIRED_THROTTLE_LEVEL 25 if ((newPerfStates->State[state].Frequency < LOWEST_USABLE_FREQUENCY) && (newPerfStates->State[state].PercentFrequency < REQUIRED_THROTTLE_LEVEL)) { DebugPrint((INFO, "Droping all Perf States after state %u: Freq=%u Percent=%u\n", state, newPerfStates->State[state].Frequency, newPerfStates->State[state].PercentFrequency)); break; } } newPerfStates->Count = (UCHAR) state; // // Replace old perf states with new. // ExFreePool(DeviceExtension->PerfStates); DeviceExtension->PerfStates = newPerfStates; DeviceExtension->CurrentPerfState = INVALID_PERF_STATE; DumpProcessorPerfStates(newPerfStates); MergePerformanceStatesExit: DebugExitStatus(status); return status; } NTSTATUS BuildAvailablePerfStatesFromPss ( PFDO_DATA DeviceExtension ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status; ULONG availablePerfStates, ppcResult, perfStatesSize; ULONG maxFreq, maxTransitionLatency = 0, state; DebugEnter(); DebugAssert(DeviceExtension->PssPackage); // // if this is a legacy system, simulate the _PPC method // if (DeviceExtension->LegacyInterface) { status = STATUS_SUCCESS; ppcResult = DeviceExtension->PpcResult; } else { status = AcpiEvaluatePpc(DeviceExtension, &ppcResult); } if (!NT_SUCCESS(status)) { goto BuildAvailablePerfStatesFromPssExit; } if (ppcResult > (ULONG)(DeviceExtension->PssPackage->NumPStates - 1)) { // // Log Error // QueueEventLogWrite(DeviceExtension, PROCESSOR_PCT_ERROR, ppcResult); // // change ppcResult to valid value // ppcResult = DeviceExtension->PssPackage->NumPStates - 1; } DeviceExtension->PpcResult = ppcResult; availablePerfStates = DeviceExtension->PssPackage->NumPStates - ppcResult; DeviceExtension->LowestPerfState = availablePerfStates - 1; DeviceExtension->CurrentPerfState = INVALID_PERF_STATE; // // if there were already PerfStates, they were probably acpi 1.0 type // throttling, so we will blow them away, because they will be recreated // in MergePerformanceStates() // if (DeviceExtension->PerfStates) { ExFreePool(DeviceExtension->PerfStates); } perfStatesSize = sizeof(PROCESSOR_PERFORMANCE_STATES) + (sizeof(PROCESSOR_PERFORMANCE_STATE) * (availablePerfStates - 1)); DeviceExtension->PerfStates = ExAllocatePoolWithTag(PagedPool, perfStatesSize, PROCESSOR_POOL_TAG); if (!DeviceExtension->PerfStates) { status = STATUS_INSUFFICIENT_RESOURCES; goto BuildAvailablePerfStatesFromPssExit; } RtlZeroMemory(DeviceExtension->PerfStates, perfStatesSize); maxFreq = GetMaxProcFrequency(DeviceExtension); for (state = 0; state < availablePerfStates; state++) { // // Fill in the _PPC states, top down. // DeviceExtension->PerfStates->State[state].Frequency = DeviceExtension->PssPackage->State[state + ppcResult].CoreFrequency; DeviceExtension->PerfStates->State[state].PercentFrequency = PERCENT_TO_PERF_LEVEL( (DeviceExtension->PerfStates->State[state].Frequency * 100) / maxFreq); // // Mark this state as a Performance State // DeviceExtension->PerfStates->State[state].Flags = PROCESSOR_STATE_TYPE_PERFORMANCE; maxTransitionLatency = MAX(maxTransitionLatency, DeviceExtension->PssPackage->State[state + ppcResult].Latency); } DeviceExtension->PerfStates->TransitionLatency = maxTransitionLatency; DeviceExtension->PerfStates->TransitionFunction = SetPerfLevel; DeviceExtension->PerfStates->Count = state; BuildAvailablePerfStatesFromPssExit: DebugExitStatus(status); return status; } ULONG GetMaxProcFrequency( PFDO_DATA DeviceExtension ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status; ULONG frequency = 0; static ULONG regSpeed = 0; DebugEnter(); PAGED_CODE(); // // First we check for Acpi 2.0 info, then NonAcpi info, and if we haven't // yet gathered either of those, then we use the CPU speed from the registy. // if (DeviceExtension->PssPackage) { frequency = DeviceExtension->PssPackage->State[0].CoreFrequency; } else if (DeviceExtension->LegacyInterface) { GetLegacyMaxProcFrequency(&frequency); } else { // // Retrieve cpu speed from the registry. // if (!regSpeed) { status = GetRegistryDwordValue(CPU0_REG_KEY, L"~MHz", ®Speed); } frequency = regSpeed; } // // We couldn't find the max speed, so we will have to guess. // if (!frequency) { frequency = 650; // a reasonable guess? } DebugExitValue(frequency); return frequency; } NTSTATUS SaveCurrentStateGoToLowVolts( IN PFDO_DATA DevExt ) /*++ Routine Description: Arguments: Return Value: --*/ { ULONG targetState; DebugEnter(); // // Throttling should be off, and if we have perf states, then we // should be at the lowest state. // if (DevExt->PerfStates && (DevExt->CurrentPerfState != INVALID_PERF_STATE)) { AcquireProcessorPerfStateLock(DevExt); // // Save current throttle percentage // DebugAssert(DevExt->CurrentPerfState < DevExt->PerfStates->Count); DevExt->SavedState = DevExt->PerfStates->State[DevExt->CurrentPerfState].PercentFrequency; // // Go to lowest Performance state // targetState = DevExt->LowestPerfState; if (DevExt->PerfStates->TransitionFunction) { DevExt->PerfStates->TransitionFunction( (UCHAR)DevExt->PerfStates->State[targetState].PercentFrequency); } ReleaseProcessorPerfStateLock(DevExt); } return STATUS_SUCCESS; } NTSTATUS RestoreToSavedPerformanceState( IN PFDO_DATA DeviceExtension ) /*++ Routine Description: Arguments: Return Value: --*/ { // // BUG 615135: remove code that restores save processor performance state, // as it causes the kernel to get out of sync. // return STATUS_SUCCESS; } NTSTATUS SetProcessorPerformanceState( IN ULONG TargetPerfState, IN PFDO_DATA DeviceExtension ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status; DebugEnter(); DebugAssert(DeviceExtension); DebugPrint((TRACE, "Transitioning to state 0x%x\n", TargetPerfState)); if (TargetPerfState < DeviceExtension->PerfStates->Count) { DeviceExtension->PerfStates->TransitionFunction( (UCHAR)DeviceExtension->PerfStates->State[TargetPerfState].PercentFrequency); status = STATUS_SUCCESS; } else { // // not a vaild state // DebugPrint((TRACE, "%u is not a valid Processor Performance State\n")); status = STATUS_UNSUCCESSFUL; } return status; } NTSTATUS QueueEventLogWrite( IN PFDO_DATA DeviceExtension, IN ULONG EventErrorCode, IN ULONG EventValue ) /*++ Routine Description: Arguments: Return Value: --*/ { PEVENT_LOG_CONTEXT context; context = ExAllocatePoolWithTag(NonPagedPool, sizeof(EVENT_LOG_CONTEXT), PROCESSOR_POOL_TAG); if (!context) { return STATUS_INSUFFICIENT_RESOURCES; } context->EventErrorCode = EventErrorCode; context->EventValue = EventValue; context->WorkItem = IoAllocateWorkItem(DeviceExtension->Self); if (context->WorkItem) { // // Log error to event log // IoQueueWorkItem(context->WorkItem, ProcessEventLogEntry, DelayedWorkQueue, context); return STATUS_SUCCESS; } else { DebugPrint((ERROR, "IoAllocateWorkItem() Failed!\n")); ExFreePool(context); return STATUS_INSUFFICIENT_RESOURCES; } } VOID ProcessEventLogEntry ( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) /*++ Routine Description: Arguments: Return Value: --*/ { WCHAR eventLogValue[11]; UNICODE_STRING eventLogErrorString; PEVENT_LOG_CONTEXT workItem = (PEVENT_LOG_CONTEXT) Context; PVOID string = NULL; ULONG stringCount = 0; DebugEnter(); DebugAssert(Context); PAGED_CODE(); // // Free worker resources // IoFreeWorkItem(workItem->WorkItem); switch (workItem->EventErrorCode) { case PROCESSOR_PCT_ERROR: case PROCESSOR_INIT_TRANSITION_FAILURE: eventLogErrorString.Buffer = eventLogValue; eventLogErrorString.MaximumLength = sizeof(eventLogValue); RtlIntegerToUnicodeString(workItem->EventValue, 10, &eventLogErrorString); string = &eventLogErrorString.Buffer; stringCount = 1; break; case PROCESSOR_LEGACY_INTERFACE_FAILURE: case PROCESSOR_INITIALIZATION_FAILURE: case PROCESSOR_REINITIALIZATION_FAILURE: // // no strings // break; default: DebugPrint((ERROR, "ProcessEventLogEntry: Unknown EventErrorCode\n")); goto ProcessEventLogEntryExit; } // // Write Event // WriteEventLogEntry(DeviceObject, workItem->EventErrorCode, string, stringCount, NULL, 0); ProcessEventLogEntryExit: ExFreePool(Context); } NTSTATUS PowerStateHandlerNotificationRegistration ( IN PENTER_STATE_NOTIFY_HANDLER NotifyHandler, IN PVOID Context, IN BOOLEAN Register ) /*++ Routine Description: Arguments: Return Value: --*/ { POWER_STATE_NOTIFY_HANDLER newHandler = {0}; NTSTATUS status; DebugEnter(); if (Register) { DebugAssert(NotifyHandler); newHandler.Handler = NotifyHandler; newHandler.Context = Context; } status = ZwPowerInformation(SystemPowerStateNotifyHandler, &newHandler, sizeof(POWER_STATE_NOTIFY_HANDLER), NULL, 0); DebugExitStatus(status); return status; } NTSTATUS ProcessMultipleApicDescTable( PPROCESSOR_INFO ProcInfo ) /*++ Routine Description: Arguments: Return Value: --*/ { PMAPIC mapicTable = NULL; PUCHAR apicTable; PUCHAR mapicTableEnd; UCHAR apicType; UCHAR apicSize; DebugEnter(); DebugAssert(ProcInfo); DebugAssert(ProcInfo->TotalProcessors == 0); // // Get MAPIC table // mapicTable = GetAcpiTable(APIC_SIGNATURE); if (!mapicTable) { return STATUS_UNSUCCESSFUL; } // // Start of MAPIC tables // apicTable = (PUCHAR) mapicTable->APICTables; // // Calculate end of MAPIC table. // mapicTableEnd = (PUCHAR) mapicTable + mapicTable->Header.Length; // // Walk through each APIC table // while ((apicTable + sizeof(PROCLOCALAPIC)) <= mapicTableEnd) { // // individual apic tables have common header // apicType = ((PAPICTABLE)apicTable)->Type; apicSize = ((PAPICTABLE)apicTable)->Length; // // Sanity check // if (!apicSize) { DebugPrint((ERROR, "ProcessMultipleApicDescTable() table size = 0\n")); break; } if (apicType == PROCESSOR_LOCAL_APIC && apicSize == PROCESSOR_LOCAL_APIC_LENGTH) { PPROCLOCALAPIC procLocalApic = (PPROCLOCALAPIC) apicTable; // toddcar - 12/08/2000: TEMP // Should implement better method to map between processorid and ApicId. // // // save Processor ID to APIC ID mappings // ProcInfo->ProcIdToApicId[procLocalApic->ACPIProcessorID] = procLocalApic->APICID; ProcInfo->TotalProcessors++; DebugAssert(ProcInfo->TotalProcessors < MAX_PROCESSOR_VALUE); if (procLocalApic->Flags & PLAF_ENABLED) { ProcInfo->ActiveProcessors++; } } apicTable += apicSize; } // // Allocated by GetAcpiTable() // if (mapicTable) { ExFreePool(mapicTable); } return STATUS_SUCCESS; } extern _inline ULONG GetApicId( VOID ) { // // Well known virtual address of local processor apic // return ((pLocalApic[LU_ID_REGISTER] & APIC_ID_MASK) >> APIC_ID_SHIFT); } NTSTATUS SetProcessorFriendlyName ( PFDO_DATA DeviceExtension ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status = STATUS_UNSUCCESSFUL; PWCHAR driverEnumKey; PWCHAR instanceId; PWCHAR deviceRegKey; ULONG size; PUCHAR cpuBrandString = NULL; PUCHAR tmpBrandString = NULL; ANSI_STRING ansiCpuString; UNICODE_STRING unicodeCpuString; UNICODE_STRING fullDeviceId; DebugEnter(); // // if we already have the Processor Brand String, // we will use it. // if (!Globals.ProcessorBrandString) { // // Get size needed // status = GetProcessorBrandString(NULL, &size); if (status == STATUS_NOT_SUPPORTED || !size) { goto SetProcessorFriendlyNameExit; } // // alloc some memory // cpuBrandString = ExAllocatePoolWithTag(PagedPool, size, PROCESSOR_POOL_TAG); if (!cpuBrandString) { status = STATUS_INSUFFICIENT_RESOURCES; goto SetProcessorFriendlyNameExit; } // // Get Brand String // status = GetProcessorBrandString(cpuBrandString, &size); if (!NT_SUCCESS(status)) { goto SetProcessorFriendlyNameExit; } // // need to save orig pointer for the free // tmpBrandString = cpuBrandString; // // some Processors include leading spaces, removed them // while (tmpBrandString[0] == 0x20) { tmpBrandString++; } // // Convert ansi string to wide // RtlInitAnsiString(&ansiCpuString, tmpBrandString); status = RtlAnsiStringToUnicodeString(&unicodeCpuString, &ansiCpuString, TRUE); if (!NT_SUCCESS(status)) { goto SetProcessorFriendlyNameExit; } Globals.ProcessorBrandString = unicodeCpuString.Buffer; } // // construct registy path for current pocessor device // // // Construct driver enum path... // HKLM\Machine\System\CurrentControlSet\Services\P3+\+Enum // 2 == "\" + NULL // size = Globals.RegistryPath.Length + ((wcslen(EnumKeyName) + 2) * sizeof(WCHAR)); driverEnumKey = ExAllocatePoolWithTag(PagedPool, size, PROCESSOR_POOL_TAG); if (!driverEnumKey) { status = STATUS_INSUFFICIENT_RESOURCES; goto SetProcessorFriendlyNameExit; } // // construct enum registry path for current device // _snwprintf(driverEnumKey, size, L"%s\\%s", Globals.RegistryPath.Buffer, EnumKeyName); // // Get Instance Id string // status = GetInstanceId(DeviceExtension, &instanceId); if (!NT_SUCCESS(status)) { goto SetProcessorFriendlyNameExit; } // // get Bus\DeviceId\InstanceId path from driver's enum key // GetRegistryStringValue(driverEnumKey, instanceId, &fullDeviceId); ExFreePool(driverEnumKey); ExFreePool(instanceId); // allocated inside GetInstanceId // // alloc enough memory for entire regkey path // 2 == "\" + NULL // size = fullDeviceId.Length + ((wcslen(CCSEnumRegKey) + 2) * sizeof(WCHAR)); deviceRegKey = ExAllocatePoolWithTag(PagedPool, size, PROCESSOR_POOL_TAG); if (!deviceRegKey) { status = STATUS_INSUFFICIENT_RESOURCES; goto SetProcessorFriendlyNameExit; } // // construct enum registry path for current device // _snwprintf(deviceRegKey, size, L"%s\\%s", CCSEnumRegKey, fullDeviceId.Buffer); // // create "FriendlyName" regkey for processor device // status = SetRegistryStringValue(deviceRegKey, (PWCHAR)FriendlyNameRegKey, Globals.ProcessorBrandString); ExFreePool(deviceRegKey); ExFreePool(fullDeviceId.Buffer); SetProcessorFriendlyNameExit: if (cpuBrandString) { ExFreePool(cpuBrandString); } DebugExitStatus(status); return status; } NTSTATUS GetHardwareId( PFDO_DATA DeviceExtension ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status; KEVENT event; PIRP irp; IO_STATUS_BLOCK ioStatusBlock; PIO_STACK_LOCATION irpStack; DebugEnter(); KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, DeviceExtension->NextLowerDriver, NULL, 0, NULL, &event, &ioStatusBlock); if (!irp) { status = STATUS_INSUFFICIENT_RESOURCES; goto GetHardwareIdExit; } irpStack = IoGetNextIrpStackLocation(irp); irpStack->MinorFunction = IRP_MN_QUERY_ID; irpStack->Parameters.QueryId.IdType = BusQueryDeviceID; irp->IoStatus.Status = STATUS_NOT_SUPPORTED; status = IoCallDriver(DeviceExtension->NextLowerDriver, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatusBlock.Status; } if (NT_SUCCESS(status)) { DebugPrint((ERROR, "DeviceId == %S\n", ioStatusBlock.Information)); } GetHardwareIdExit: DebugExitStatus(status); return status; } NTSTATUS GetInstanceId( PFDO_DATA DeviceExtension, PWCHAR *InstanceId ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status; KEVENT event; PIRP irp; ULONG idSize; IO_STATUS_BLOCK ioStatusBlock; PIO_STACK_LOCATION irpStack; DebugEnter(); DebugAssert(InstanceId); KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, DeviceExtension->NextLowerDriver, NULL, 0, NULL, &event, &ioStatusBlock); if (!irp) { status = STATUS_INSUFFICIENT_RESOURCES; goto GetHardwareIdExit; } irpStack = IoGetNextIrpStackLocation(irp); irpStack->MinorFunction = IRP_MN_QUERY_ID; irpStack->Parameters.QueryId.IdType = BusQueryInstanceID; irp->IoStatus.Status = STATUS_NOT_SUPPORTED; status = IoCallDriver(DeviceExtension->NextLowerDriver, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatusBlock.Status; } // // remove leading white spaces // if (NT_SUCCESS(status)) { PWCHAR idString = (PWCHAR)ioStatusBlock.Information; // // remove leading white space // while(idString[0] == 0x20) { idString++; } idSize = (wcslen(idString) + 1) * sizeof(WCHAR); *InstanceId = ExAllocatePoolWithTag(PagedPool, idSize, PROCESSOR_POOL_TAG); if (!(*InstanceId)) { status = STATUS_INSUFFICIENT_RESOURCES; goto GetHardwareIdExit; } RtlCopyMemory(*InstanceId, idString, idSize); // // Free ID structure // ExFreePool((PWCHAR)ioStatusBlock.Information); } GetHardwareIdExit: DebugExitStatus(status); return status; } __inline NTSTATUS AcquireProcessorPerfStateLock ( IN PFDO_DATA DevExt ) /*++ Routine Description: Arguments: Return Value: --*/ { return KeWaitForSingleObject(&DevExt->PerfStateLock, Executive, KernelMode, FALSE, NULL); } __inline NTSTATUS ReleaseProcessorPerfStateLock ( IN PFDO_DATA DevExt ) /*++ Routine Description: Arguments: Return Value: --*/ { return KeSetEvent(&DevExt->PerfStateLock, IO_NO_INCREMENT, FALSE); } #if DBG VOID DumpCStates( PACPI_CST_PACKAGE CStates ) { ULONG x; PACPI_CST_DESCRIPTOR cState; DebugAssert(CStates); DebugPrint((TRACE, "\n")); DebugPrint((TRACE, "_CST:\n")); DebugPrint((TRACE, "Found %u C-states\n", CStates->NumCStates)); DebugPrint((TRACE, "\n")); for (x=0; x < CStates->NumCStates; x++) { cState = &CStates->State[x]; DebugPrint((TRACE, "State #%u:\n", x)); DebugPrint((TRACE, " Register:\n")); DebugPrint((TRACE, " AddressSpaceID: 0x%x\n", cState->Register.AddressSpaceID)); DebugPrint((TRACE, " BitWidth: 0x%x\n", cState->Register.BitWidth)); DebugPrint((TRACE, " BitOffset: 0x%x\n", cState->Register.BitOffset)); DebugPrint((TRACE, " Reserved: 0x%x\n", cState->Register.Reserved)); DebugPrint((TRACE, " Address: 0x%I64x\n", cState->Register.Address)); DebugPrint((TRACE, "\n")); DebugPrint((TRACE, " State Type: C%u\n", cState->StateType)); DebugPrint((TRACE, " Latency: %u us\n", cState->Latency)); DebugPrint((TRACE, " Power Consumption: %u mW\n", cState->PowerConsumption)); DebugPrint((TRACE, "\n")); } } #endif