4113 lines
91 KiB
C
4113 lines
91 KiB
C
/*++
|
||
|
||
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 <wdmguid.h>
|
||
#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
|