mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
5070e8960a
CORE-17256 - Implement support for complex input buffers. This will enable the IDE driver with some hack to execute the _STM (channel timing settings) control method. - Fix a memory leak. - Fix returned status code. - Correctly calculate the output buffer size. - Improve buffer validation. - Implement support for async control requests. - Implement local reference conversion. - Implement support for subpackage argument conversion. - Place some code into INIT section.
862 lines
23 KiB
C
862 lines
23 KiB
C
#ifndef UNIT_TEST
|
|
#include "precomp.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
#endif /* UNIT_TEST */
|
|
|
|
#define AcpiVerifyInBuffer(Stack, Length) \
|
|
((Stack)->Parameters.DeviceIoControl.InputBufferLength >= Length)
|
|
|
|
#define AcpiVerifyOutBuffer(Stack, Length) \
|
|
((Stack)->Parameters.DeviceIoControl.OutputBufferLength >= Length)
|
|
|
|
#define TAG_ACPI_PARAMETERS_LIST 'OpcA'
|
|
#define TAG_ACPI_PACKAGE_LIST 'PpcA'
|
|
|
|
/**
|
|
* Null terminated ACPI name for the object.
|
|
* For example, _DSM, LNKB, etc.
|
|
*/
|
|
#define ACPI_OBJECT_NAME_LENGTH (4 + 1)
|
|
|
|
/**
|
|
* Maximum number of nested package structures supported by the driver.
|
|
* This should be enough to cover all the existing ACPI methods.
|
|
*/
|
|
#define ACPI_MAX_PACKAGE_DEPTH 5
|
|
|
|
/**
|
|
* @brief Performs translation from the supplied object reference
|
|
* into a string method argument.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
EvalConvertObjectReference(
|
|
_Out_ PACPI_METHOD_ARGUMENT Argument,
|
|
_In_ ACPI_OBJECT* Reference)
|
|
{
|
|
ACPI_BUFFER OutName;
|
|
ACPI_STATUS AcpiStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
Argument->Type = ACPI_METHOD_ARGUMENT_STRING;
|
|
Argument->DataLength = ACPI_OBJECT_NAME_LENGTH;
|
|
|
|
/* Convert the object handle to an ACPI name */
|
|
OutName.Length = ACPI_OBJECT_NAME_LENGTH;
|
|
OutName.Pointer = &Argument->Data[0];
|
|
|
|
AcpiStatus = AcpiGetName(Reference->Reference.Handle, ACPI_SINGLE_NAME, &OutName);
|
|
if (!ACPI_SUCCESS(AcpiStatus))
|
|
{
|
|
DPRINT1("AcpiGetName() failed on %p with status 0x%04lx\n",
|
|
Reference->Reference.Handle,
|
|
AcpiStatus);
|
|
|
|
ASSERT(FALSE);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Calculates the number of bytes needed for returned method argument
|
|
* based on the type of an ACPI_OBJECT structure.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
EvalGetElementSize(
|
|
_In_ ACPI_OBJECT* Obj,
|
|
_In_ ULONG Depth,
|
|
_Out_opt_ PULONG Count,
|
|
_Out_ PULONG Size)
|
|
{
|
|
ULONG TotalCount, TotalLength;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (Depth >= ACPI_MAX_PACKAGE_DEPTH)
|
|
{
|
|
ASSERT(FALSE);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
switch (Obj->Type)
|
|
{
|
|
case ACPI_TYPE_INTEGER:
|
|
{
|
|
TotalLength = ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));
|
|
TotalCount = 1;
|
|
break;
|
|
}
|
|
|
|
case ACPI_TYPE_STRING:
|
|
{
|
|
TotalLength = ACPI_METHOD_ARGUMENT_LENGTH(Obj->String.Length + sizeof(UCHAR));
|
|
TotalCount = 1;
|
|
break;
|
|
}
|
|
|
|
case ACPI_TYPE_BUFFER:
|
|
{
|
|
TotalLength = ACPI_METHOD_ARGUMENT_LENGTH(Obj->Buffer.Length);
|
|
TotalCount = 1;
|
|
break;
|
|
}
|
|
|
|
case ACPI_TYPE_PACKAGE:
|
|
{
|
|
ULONG i, TotalPackageLength;
|
|
|
|
/* Get the size of the current packet */
|
|
TotalPackageLength = 0;
|
|
for (i = 0; i < Obj->Package.Count; i++)
|
|
{
|
|
ULONG ElementSize;
|
|
|
|
Status = EvalGetElementSize(&Obj->Package.Elements[i],
|
|
Depth + 1,
|
|
NULL,
|
|
&ElementSize);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
TotalPackageLength += ElementSize;
|
|
}
|
|
|
|
/* Check if we need to wrap the list of elements into a package */
|
|
if (Depth > 0)
|
|
{
|
|
TotalPackageLength = ACPI_METHOD_ARGUMENT_LENGTH(TotalPackageLength);
|
|
}
|
|
|
|
TotalLength = TotalPackageLength;
|
|
TotalCount = Obj->Package.Count;
|
|
break;
|
|
}
|
|
|
|
case ACPI_TYPE_LOCAL_REFERENCE:
|
|
{
|
|
TotalLength = ACPI_METHOD_ARGUMENT_LENGTH(ACPI_OBJECT_NAME_LENGTH);
|
|
TotalCount = 1;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPRINT1("Unsupported element type %lu\n", Obj->Type);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
if (Count)
|
|
*Count = TotalCount;
|
|
|
|
*Size = TotalLength;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Performs translation from the supplied ACPI_OBJECT structure into a method argument.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
EvalConvertEvaluationResults(
|
|
_Out_ ACPI_METHOD_ARGUMENT* Argument,
|
|
_In_ ULONG Depth,
|
|
_In_ ACPI_OBJECT* Obj)
|
|
{
|
|
ACPI_METHOD_ARGUMENT *Ptr;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (Depth >= ACPI_MAX_PACKAGE_DEPTH)
|
|
{
|
|
ASSERT(FALSE);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Ptr = Argument;
|
|
switch (Obj->Type)
|
|
{
|
|
case ACPI_TYPE_INTEGER:
|
|
{
|
|
ACPI_METHOD_SET_ARGUMENT_INTEGER(Ptr, Obj->Integer.Value);
|
|
break;
|
|
}
|
|
|
|
case ACPI_TYPE_STRING:
|
|
{
|
|
ACPI_METHOD_SET_ARGUMENT_STRING(Ptr, Obj->String.Pointer);
|
|
break;
|
|
}
|
|
|
|
case ACPI_TYPE_BUFFER:
|
|
{
|
|
ACPI_METHOD_SET_ARGUMENT_BUFFER(Ptr, Obj->Buffer.Pointer, Obj->Buffer.Length);
|
|
break;
|
|
}
|
|
|
|
case ACPI_TYPE_PACKAGE:
|
|
{
|
|
ULONG i;
|
|
|
|
/* Check if we need to wrap the list of elements into a package */
|
|
if (Depth > 0)
|
|
{
|
|
ULONG TotalPackageLength;
|
|
|
|
/* Get the size of the current packet */
|
|
TotalPackageLength = 0;
|
|
for (i = 0; i < Obj->Package.Count; i++)
|
|
{
|
|
ULONG ElementSize;
|
|
|
|
Status = EvalGetElementSize(&Obj->Package.Elements[i],
|
|
Depth + 1,
|
|
NULL,
|
|
&ElementSize);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
TotalPackageLength += ElementSize;
|
|
}
|
|
|
|
/* Start a new package */
|
|
Argument->Type = ACPI_METHOD_ARGUMENT_PACKAGE;
|
|
Argument->DataLength = TotalPackageLength;
|
|
|
|
Ptr = (PACPI_METHOD_ARGUMENT)Ptr->Data;
|
|
}
|
|
|
|
for (i = 0; i < Obj->Package.Count; i++)
|
|
{
|
|
Status = EvalConvertEvaluationResults(Ptr, Depth + 1, &Obj->Package.Elements[i]);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
Ptr = ACPI_METHOD_NEXT_ARGUMENT(Ptr);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ACPI_TYPE_LOCAL_REFERENCE:
|
|
{
|
|
Status = EvalConvertObjectReference(Ptr, Obj);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPRINT1("Unsupported element type %lu\n", Obj->Type);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the number of sub-objects (elements) in a package.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
ULONG
|
|
EvalGetPackageCount(
|
|
_In_ PACPI_METHOD_ARGUMENT Package,
|
|
_In_ PACPI_METHOD_ARGUMENT PackageArgument,
|
|
_In_ ULONG DataLength)
|
|
{
|
|
ACPI_METHOD_ARGUMENT* Ptr;
|
|
ULONG TotalLength = 0, TotalCount = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Empty package */
|
|
if (DataLength < ACPI_METHOD_ARGUMENT_LENGTH(0) || Package->Argument == 0)
|
|
return 0;
|
|
|
|
Ptr = PackageArgument;
|
|
while (TotalLength < DataLength)
|
|
{
|
|
TotalLength += ACPI_METHOD_ARGUMENT_LENGTH_FROM_ARGUMENT(Ptr);
|
|
TotalCount++;
|
|
|
|
Ptr = ACPI_METHOD_NEXT_ARGUMENT(Ptr);
|
|
}
|
|
|
|
return TotalCount;
|
|
}
|
|
|
|
/**
|
|
* @brief Performs translation from the supplied method argument into an ACPI_OBJECT structure.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
EvalConvertParameterObjects(
|
|
_Out_ ACPI_OBJECT* Arg,
|
|
_In_ ULONG Depth,
|
|
_In_ PACPI_METHOD_ARGUMENT Argument,
|
|
_In_ PIO_STACK_LOCATION IoStack,
|
|
_In_ ULONG Offset)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (Depth >= ACPI_MAX_PACKAGE_DEPTH)
|
|
{
|
|
ASSERT(FALSE);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
/* Validate that the method argument fits into the buffer */
|
|
if (Depth > 0)
|
|
{
|
|
Offset += ACPI_METHOD_ARGUMENT_LENGTH_FROM_ARGUMENT(Argument);
|
|
|
|
if (!AcpiVerifyInBuffer(IoStack, Offset))
|
|
{
|
|
DPRINT1("Argument buffer outside of argument bounds\n");
|
|
return STATUS_ACPI_INVALID_ARGTYPE;
|
|
}
|
|
}
|
|
|
|
switch (Argument->Type)
|
|
{
|
|
case ACPI_METHOD_ARGUMENT_INTEGER:
|
|
{
|
|
Arg->Type = ACPI_TYPE_INTEGER;
|
|
Arg->Integer.Value = (ULONG64)Argument->Argument;
|
|
break;
|
|
}
|
|
|
|
case ACPI_METHOD_ARGUMENT_STRING:
|
|
{
|
|
/*
|
|
* FIXME: Add tests and remove this.
|
|
* We should either default to an empty string, or reject the IOCTL.
|
|
*/
|
|
ASSERT(Argument->DataLength >= sizeof(UCHAR));
|
|
if (Argument->DataLength < sizeof(UCHAR))
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
Arg->Type = ACPI_TYPE_STRING;
|
|
Arg->String.Pointer = (PCHAR)&Argument->Data[0];
|
|
Arg->String.Length = Argument->DataLength - sizeof(UCHAR);
|
|
break;
|
|
}
|
|
|
|
case ACPI_METHOD_ARGUMENT_BUFFER:
|
|
{
|
|
Arg->Type = ACPI_TYPE_BUFFER;
|
|
Arg->Buffer.Pointer = &Argument->Data[0];
|
|
Arg->Buffer.Length = Argument->DataLength;
|
|
break;
|
|
}
|
|
|
|
case ACPI_METHOD_ARGUMENT_PACKAGE:
|
|
{
|
|
ULONG i, PackageSize;
|
|
NTSTATUS Status;
|
|
PACPI_METHOD_ARGUMENT PackageArgument;
|
|
|
|
Arg->Type = ACPI_TYPE_PACKAGE;
|
|
|
|
Arg->Package.Count = EvalGetPackageCount(Argument,
|
|
(PACPI_METHOD_ARGUMENT)Argument->Data,
|
|
Argument->DataLength);
|
|
/* Empty package, nothing more to convert */
|
|
if (Arg->Package.Count == 0)
|
|
{
|
|
Arg->Package.Elements = NULL;
|
|
break;
|
|
}
|
|
|
|
Status = RtlULongMult(Arg->Package.Count, sizeof(*Arg), &PackageSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Invalid package count 0x%lx\n", Arg->Package.Count);
|
|
return STATUS_ACPI_INCORRECT_ARGUMENT_COUNT;
|
|
}
|
|
|
|
Arg->Package.Elements = ExAllocatePoolUninitialized(NonPagedPool,
|
|
PackageSize,
|
|
TAG_ACPI_PACKAGE_LIST);
|
|
if (!Arg->Package.Elements)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
PackageArgument = (PACPI_METHOD_ARGUMENT)Argument->Data;
|
|
for (i = 0; i < Arg->Package.Count; i++)
|
|
{
|
|
Status = EvalConvertParameterObjects(&Arg->Package.Elements[i],
|
|
Depth + 1,
|
|
PackageArgument,
|
|
IoStack,
|
|
Offset);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePoolWithTag(Arg->Package.Elements, TAG_ACPI_PACKAGE_LIST);
|
|
return Status;
|
|
}
|
|
|
|
PackageArgument = ACPI_METHOD_NEXT_ARGUMENT(PackageArgument);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPRINT1("Unknown argument type %u\n", Argument->Type);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Creates a counted array of ACPI_OBJECTs from the given input buffer.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
EvalCreateParametersList(
|
|
_In_ PIRP Irp,
|
|
_In_ PIO_STACK_LOCATION IoStack,
|
|
_In_ PACPI_EVAL_INPUT_BUFFER EvalInputBuffer,
|
|
_Out_ ACPI_OBJECT_LIST* ParamList)
|
|
{
|
|
ACPI_OBJECT* Arg;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!AcpiVerifyInBuffer(IoStack, RTL_SIZEOF_THROUGH_FIELD(ACPI_EVAL_INPUT_BUFFER, Signature)))
|
|
{
|
|
DPRINT1("Buffer too small\n");
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
switch (EvalInputBuffer->Signature)
|
|
{
|
|
case ACPI_EVAL_INPUT_BUFFER_SIGNATURE:
|
|
{
|
|
if (!AcpiVerifyInBuffer(IoStack, sizeof(*EvalInputBuffer)))
|
|
{
|
|
DPRINT1("Buffer too small\n");
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
ParamList->Count = 0;
|
|
break;
|
|
}
|
|
|
|
case ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_SIGNATURE:
|
|
{
|
|
PACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER SimpleInt;
|
|
|
|
if (!AcpiVerifyInBuffer(IoStack, sizeof(*SimpleInt)))
|
|
{
|
|
DPRINT1("Buffer too small\n");
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
Arg = ExAllocatePoolUninitialized(NonPagedPool, sizeof(*Arg), TAG_ACPI_PARAMETERS_LIST);
|
|
if (!Arg)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
ParamList->Count = 1;
|
|
ParamList->Pointer = Arg;
|
|
|
|
SimpleInt = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
Arg->Type = ACPI_TYPE_INTEGER;
|
|
Arg->Integer.Value = (ULONG64)SimpleInt->IntegerArgument;
|
|
break;
|
|
}
|
|
|
|
case ACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING_SIGNATURE:
|
|
{
|
|
PACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING SimpleStr;
|
|
|
|
if (!AcpiVerifyInBuffer(IoStack, sizeof(*SimpleStr)))
|
|
{
|
|
DPRINT1("Buffer too small\n");
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
Arg = ExAllocatePoolUninitialized(NonPagedPool, sizeof(*Arg), TAG_ACPI_PARAMETERS_LIST);
|
|
if (!Arg)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
ParamList->Count = 1;
|
|
ParamList->Pointer = Arg;
|
|
|
|
SimpleStr = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
Arg->Type = ACPI_TYPE_STRING;
|
|
Arg->String.Pointer = (PCHAR)SimpleStr->String;
|
|
Arg->String.Length = SimpleStr->StringLength;
|
|
break;
|
|
}
|
|
|
|
case ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE:
|
|
{
|
|
PACPI_EVAL_INPUT_BUFFER_COMPLEX ComplexBuffer;
|
|
PACPI_METHOD_ARGUMENT Argument;
|
|
ULONG i, Length, Offset, ArgumentsSize;
|
|
NTSTATUS Status;
|
|
|
|
if (!AcpiVerifyInBuffer(IoStack, sizeof(*ComplexBuffer)))
|
|
{
|
|
DPRINT1("Buffer too small\n");
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
ComplexBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
ParamList->Count = ComplexBuffer->ArgumentCount;
|
|
if (ParamList->Count == 0)
|
|
{
|
|
DPRINT1("No arguments\n");
|
|
return STATUS_ACPI_INCORRECT_ARGUMENT_COUNT;
|
|
}
|
|
|
|
Status = RtlULongMult(ParamList->Count, sizeof(*Arg), &ArgumentsSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Invalid argument count 0x%lx\n", ParamList->Count);
|
|
return STATUS_ACPI_INCORRECT_ARGUMENT_COUNT;
|
|
}
|
|
|
|
Arg = ExAllocatePoolUninitialized(NonPagedPool,
|
|
ArgumentsSize,
|
|
TAG_ACPI_PARAMETERS_LIST);
|
|
if (!Arg)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
ParamList->Pointer = Arg;
|
|
|
|
Argument = ComplexBuffer->Argument;
|
|
Length = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument);
|
|
|
|
for (i = 0; i < ParamList->Count; i++)
|
|
{
|
|
Offset = Length;
|
|
Length += ACPI_METHOD_ARGUMENT_LENGTH_FROM_ARGUMENT(Argument);
|
|
|
|
if (!AcpiVerifyInBuffer(IoStack, Length))
|
|
{
|
|
DPRINT1("Argument buffer outside of argument bounds\n");
|
|
|
|
ExFreePoolWithTag(ParamList->Pointer, TAG_ACPI_PARAMETERS_LIST);
|
|
return STATUS_ACPI_INVALID_ARGTYPE;
|
|
}
|
|
|
|
Status = EvalConvertParameterObjects(Arg, 0, Argument, IoStack, Offset);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExFreePoolWithTag(ParamList->Pointer, TAG_ACPI_PARAMETERS_LIST);
|
|
return Status;
|
|
}
|
|
|
|
Arg++;
|
|
Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPRINT1("Unsupported input buffer signature: 0x%lx\n", EvalInputBuffer->Signature);
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Deallocates the memory for all sub-objects (elements) in a package.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
EvalFreeParameterArgument(
|
|
_In_ ACPI_OBJECT* Arg,
|
|
_In_ ULONG Depth)
|
|
{
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (Depth >= ACPI_MAX_PACKAGE_DEPTH)
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
if (Arg->Type == ACPI_TYPE_PACKAGE)
|
|
{
|
|
for (i = 0; i < Arg->Package.Count; i++)
|
|
{
|
|
EvalFreeParameterArgument(&Arg->Package.Elements[i], Depth + 1);
|
|
}
|
|
|
|
/* Check if the package isn't empty, and free it */
|
|
if (Arg->Package.Elements)
|
|
ExFreePoolWithTag(Arg->Package.Elements, TAG_ACPI_PACKAGE_LIST);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Deallocates the given array of ACPI_OBJECTs.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
EvalFreeParametersList(
|
|
_In_ ACPI_OBJECT_LIST* ParamList)
|
|
{
|
|
ACPI_OBJECT* Arg;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
Arg = ParamList->Pointer;
|
|
for (i = 0; i < ParamList->Count; i++)
|
|
{
|
|
EvalFreeParameterArgument(Arg++, 0);
|
|
}
|
|
|
|
ExFreePoolWithTag(ParamList->Pointer, TAG_ACPI_PARAMETERS_LIST);
|
|
}
|
|
|
|
/**
|
|
* @brief Converts the provided value of ACPI_STATUS to NTSTATUS return value.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
EvalAcpiStatusToNtStatus(
|
|
_In_ ACPI_STATUS AcpiStatus)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (ACPI_ENV_EXCEPTION(AcpiStatus))
|
|
{
|
|
switch (AcpiStatus)
|
|
{
|
|
case AE_NOT_FOUND:
|
|
case AE_NOT_EXIST:
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
case AE_NO_MEMORY:
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
case AE_SUPPORT:
|
|
return STATUS_ACPI_INCORRECT_ARGUMENT_COUNT;
|
|
|
|
case AE_TIME:
|
|
return STATUS_IO_TIMEOUT;
|
|
|
|
case AE_NO_HARDWARE_RESPONSE:
|
|
return STATUS_IO_DEVICE_ERROR;
|
|
|
|
case AE_STACK_OVERFLOW:
|
|
return STATUS_ACPI_STACK_OVERFLOW;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ACPI_AML_EXCEPTION(AcpiStatus))
|
|
{
|
|
switch (AcpiStatus)
|
|
{
|
|
case AE_AML_UNINITIALIZED_ARG:
|
|
return STATUS_ACPI_INCORRECT_ARGUMENT_COUNT;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
/**
|
|
* @brief Evaluates an ACPI namespace object.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
ACPI_STATUS
|
|
EvalEvaluateObject(
|
|
_In_ PPDO_DEVICE_DATA DeviceData,
|
|
_In_ PACPI_EVAL_INPUT_BUFFER EvalInputBuffer,
|
|
_In_ ACPI_OBJECT_LIST* ParamList,
|
|
_In_ ACPI_BUFFER* ReturnBuffer)
|
|
{
|
|
CHAR MethodName[ACPI_OBJECT_NAME_LENGTH];
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlCopyMemory(MethodName, EvalInputBuffer->MethodName, ACPI_OBJECT_NAME_LENGTH);
|
|
MethodName[ACPI_OBJECT_NAME_LENGTH - 1] = ANSI_NULL;
|
|
|
|
return AcpiEvaluateObject(DeviceData->AcpiHandle, MethodName, ParamList, ReturnBuffer);
|
|
}
|
|
|
|
/**
|
|
* @brief Writes the results from the evaluation into the output IRP buffer.
|
|
*/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
EvalCreateOutputArguments(
|
|
_In_ PIRP Irp,
|
|
_In_ PIO_STACK_LOCATION IoStack,
|
|
_In_ ACPI_BUFFER* ReturnBuffer)
|
|
{
|
|
ACPI_OBJECT* Obj;
|
|
ULONG ExtraParamLength, OutputBufSize;
|
|
PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
|
|
NTSTATUS Status;
|
|
ULONG Count;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* If we didn't get anything back then we're done */
|
|
if (!ReturnBuffer->Pointer || ReturnBuffer->Length == 0)
|
|
return STATUS_SUCCESS;
|
|
|
|
/* No output buffer is provided, we're done */
|
|
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength == 0)
|
|
return STATUS_SUCCESS;
|
|
|
|
if (!AcpiVerifyOutBuffer(IoStack, sizeof(*OutputBuffer)))
|
|
{
|
|
DPRINT1("Buffer too small\n");
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
Obj = ReturnBuffer->Pointer;
|
|
|
|
Status = EvalGetElementSize(Obj, 0, &Count, &ExtraParamLength);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
OutputBufSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + ExtraParamLength;
|
|
|
|
#ifdef UNIT_TEST
|
|
OutputBuffer = Irp->OutputBuffer;
|
|
#else
|
|
OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
#endif
|
|
OutputBuffer->Signature = ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE;
|
|
OutputBuffer->Length = OutputBufSize;
|
|
OutputBuffer->Count = Count;
|
|
|
|
if (!AcpiVerifyOutBuffer(IoStack, OutputBufSize))
|
|
{
|
|
DPRINT("Buffer too small (%lu/%lu)\n",
|
|
IoStack->Parameters.DeviceIoControl.OutputBufferLength,
|
|
OutputBufSize);
|
|
|
|
Irp->IoStatus.Information = OutputBufSize;
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
Status = EvalConvertEvaluationResults(OutputBuffer->Argument, 0, Obj);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
Irp->IoStatus.Information = OutputBufSize;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
NTAPI
|
|
Bus_PDO_EvalMethod(
|
|
_In_ PPDO_DEVICE_DATA DeviceData,
|
|
_Inout_ PIRP Irp)
|
|
{
|
|
PIO_STACK_LOCATION IoStack;
|
|
PACPI_EVAL_INPUT_BUFFER EvalInputBuffer;
|
|
ACPI_OBJECT_LIST ParamList;
|
|
ACPI_STATUS AcpiStatus;
|
|
NTSTATUS Status;
|
|
ACPI_BUFFER ReturnBuffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
|
|
PAGED_CODE();
|
|
|
|
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
|
EvalInputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
Status = EvalCreateParametersList(Irp, IoStack, EvalInputBuffer, &ParamList);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
AcpiStatus = EvalEvaluateObject(DeviceData, EvalInputBuffer, &ParamList, &ReturnBuffer);
|
|
|
|
if (ParamList.Count != 0)
|
|
EvalFreeParametersList(&ParamList);
|
|
|
|
if (!ACPI_SUCCESS(AcpiStatus))
|
|
{
|
|
DPRINT("Query method '%.4s' failed on %p with status 0x%04lx\n",
|
|
EvalInputBuffer->MethodName,
|
|
DeviceData->AcpiHandle,
|
|
AcpiStatus);
|
|
|
|
return EvalAcpiStatusToNtStatus(AcpiStatus);
|
|
}
|
|
|
|
Status = EvalCreateOutputArguments(Irp, IoStack, &ReturnBuffer);
|
|
|
|
if (ReturnBuffer.Pointer)
|
|
AcpiOsFree(ReturnBuffer.Pointer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
#ifndef UNIT_TEST
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
NTAPI
|
|
Bus_PDO_EvalMethodWorker(
|
|
_In_ PVOID Parameter)
|
|
{
|
|
PEVAL_WORKITEM_DATA WorkItemData = Parameter;
|
|
NTSTATUS Status;
|
|
PIRP Irp;
|
|
|
|
PAGED_CODE();
|
|
|
|
Irp = WorkItemData->Irp;
|
|
|
|
Status = Bus_PDO_EvalMethod(WorkItemData->DeviceData, Irp);
|
|
|
|
ExFreePoolWithTag(WorkItemData, 'ipcA');
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
#endif /* UNIT_TEST */
|