mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4112 lines
91 KiB
4112 lines
91 KiB
/*++
|
|
|
|
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
|