+#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
-GetPackageSize(ACPI_OBJECT *Package,
- PULONG Count,
- PULONG Size)
+EvalConvertObjectReference(
+ _Out_ PACPI_METHOD_ARGUMENT Argument,
+ _In_ ACPI_OBJECT* Reference)
{
- ULONG Length, RawLength, TotalLength;
- UINT32 i;
+ 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];
- TotalLength = 0;
- for (i = 0; i < Package->Package.Count; i++)
+ AcpiStatus = AcpiGetName(Reference->Reference.Handle, ACPI_SINGLE_NAME, &OutName);
+ if (!ACPI_SUCCESS(AcpiStatus))
{
- switch (Package->Package.Elements[i].Type)
+ 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:
{
- case ACPI_TYPE_INTEGER:
- Length = sizeof(ACPI_METHOD_ARGUMENT);
- DPRINT("Integer %lu -> %lu: %lu\n", sizeof(ULONG), Length, Package->Package.Elements[i].Integer.Value);
- TotalLength += Length;
- break;
+ TotalLength = ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));
+ TotalCount = 1;
+ break;
+ }
- case ACPI_TYPE_STRING:
- RawLength = Package->Package.Elements[i].String.Length + 1;
- Length = sizeof(ACPI_METHOD_ARGUMENT);
- if (RawLength > sizeof(ULONG))
- Length += RawLength - sizeof(ULONG);
- DPRINT("String %lu -> %lu: '%s'\n", RawLength, Length, Package->Package.Elements[i].String.Pointer);
- TotalLength += Length;
- break;
+ case ACPI_TYPE_STRING:
+ {
+ TotalLength = ACPI_METHOD_ARGUMENT_LENGTH(Obj->String.Length + sizeof(UCHAR));
+ TotalCount = 1;
+ break;
+ }
- default:
- DPRINT1("Unsupported element type %lu\n", Package->Package.Elements[i].Type);
- return STATUS_UNSUCCESSFUL;
+ 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;
}
}
- *Count = Package->Package.Count;
+ 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
-ConvertPackageArguments(ACPI_METHOD_ARGUMENT *Argument,
- ACPI_OBJECT *Package)
+EvalConvertEvaluationResults(
+ _Out_ ACPI_METHOD_ARGUMENT* Argument,
+ _In_ ULONG Depth,
+ _In_ ACPI_OBJECT* Obj)
{
ACPI_METHOD_ARGUMENT *Ptr;
- UINT32 i;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ if (Depth >= ACPI_MAX_PACKAGE_DEPTH)
+ {
+ ASSERT(FALSE);
+ return STATUS_UNSUCCESSFUL;
+ }
Ptr = Argument;
- for (i = 0; i < Package->Package.Count; i++)
+ switch (Obj->Type)
{
- switch (Package->Package.Elements[i].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:
{
- case ACPI_TYPE_INTEGER:
- DPRINT("Integer %lu\n", sizeof(ACPI_METHOD_ARGUMENT));
- ACPI_METHOD_SET_ARGUMENT_INTEGER(Ptr, Package->Package.Elements[i].Integer.Value);
+ 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;
- case ACPI_TYPE_STRING:
- DPRINT("String %lu\n", Package->Package.Elements[i].String.Length);
- ACPI_METHOD_SET_ARGUMENT_STRING(Ptr, Package->Package.Elements[i].String.Pointer);
+ 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:
- DPRINT1("Unsupported element type %lu\n", Package->Package.Elements[i].Type);
- return STATUS_UNSUCCESSFUL;
+ break;
}
+ }
- Ptr = ACPI_METHOD_NEXT_ARGUMENT(Ptr);
+ 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(PPDO_DEVICE_DATA DeviceData,
- PIRP Irp)
+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)
{
- ULONG Signature;
- NTSTATUS Status;
- ACPI_OBJECT_LIST ParamList;
- PACPI_EVAL_INPUT_BUFFER EvalInputBuff = Irp->AssociatedIrp.SystemBuffer;
- ACPI_BUFFER RetBuff = {ACPI_ALLOCATE_BUFFER, NULL};
- PACPI_EVAL_OUTPUT_BUFFER OutputBuf;
- PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
- ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER *SimpleInt;
- ACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING *SimpleStr;
- CHAR MethodName[5];
-
- if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
- return STATUS_INVALID_PARAMETER;
-
- Signature = *((PULONG)Irp->AssociatedIrp.SystemBuffer);
-
- switch (Signature)
- {
- case ACPI_EVAL_INPUT_BUFFER_SIGNATURE:
- if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ACPI_EVAL_INPUT_BUFFER))
- return STATUS_INVALID_PARAMETER;
-
- ParamList.Count = 0;
- break;
-
- case ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_SIGNATURE:
- SimpleInt = Irp->AssociatedIrp.SystemBuffer;
-
- if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER))
- return STATUS_INVALID_PARAMETER;
-
- ParamList.Count = 1;
-
- ParamList.Pointer = ExAllocatePoolWithTag(NonPagedPool, sizeof(ACPI_OBJECT), 'OpcA');
- if (!ParamList.Pointer) return STATUS_INSUFFICIENT_RESOURCES;
-
- ParamList.Pointer[0].Type = ACPI_TYPE_INTEGER;
- ParamList.Pointer[0].Integer.Value = SimpleInt->IntegerArgument;
- break;
-
- case ACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING_SIGNATURE:
- SimpleStr = Irp->AssociatedIrp.SystemBuffer;
-
- if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING))
- return STATUS_INVALID_PARAMETER;
-
- ParamList.Count = 1;
-
- ParamList.Pointer = ExAllocatePoolWithTag(NonPagedPool, sizeof(ACPI_OBJECT), 'OpcA');
- if (!ParamList.Pointer) return STATUS_INSUFFICIENT_RESOURCES;
-
- ParamList.Pointer[0].String.Pointer = (CHAR*)SimpleStr->String;
- ParamList.Pointer[0].String.Length = SimpleStr->StringLength;
- break;
-
- default:
- DPRINT1("Unsupported input buffer signature: %d\n", Signature);
- return STATUS_NOT_IMPLEMENTED;
- }
-
- RtlCopyMemory(MethodName,
- EvalInputBuff->MethodName,
- sizeof(EvalInputBuff->MethodName));
- MethodName[4] = ANSI_NULL;
- Status = AcpiEvaluateObject(DeviceData->AcpiHandle,
- MethodName,
- &ParamList,
- &RetBuff);
-
- if (ParamList.Count != 0)
- ExFreePoolWithTag(ParamList.Pointer, 'OpcA');
-
- if (ACPI_SUCCESS(Status))
- {
- ACPI_OBJECT *Obj = RetBuff.Pointer;
- ULONG ExtraParamLength = 0;
- ULONG Count = 1;
-
- /* If we didn't get anything back then we're done */
- if (!RetBuff.Pointer || RetBuff.Length == 0)
- return STATUS_SUCCESS;
-
- switch (Obj->Type)
- {
- case ACPI_TYPE_INTEGER:
- ExtraParamLength = sizeof(ACPI_METHOD_ARGUMENT);
- break;
-
- case ACPI_TYPE_STRING:
- ExtraParamLength = sizeof(ACPI_METHOD_ARGUMENT);
- if (Obj->String.Length + 1 > sizeof(ULONG))
- ExtraParamLength += Obj->String.Length + 1 - sizeof(ULONG);
- break;
-
- case ACPI_TYPE_BUFFER:
- ExtraParamLength = sizeof(ACPI_METHOD_ARGUMENT);
- if (Obj->Buffer.Length > sizeof(ULONG))
- ExtraParamLength += Obj->Buffer.Length + 1 - sizeof(ULONG);
- break;
-
- case ACPI_TYPE_PACKAGE:
- Status = GetPackageSize(Obj, &Count, &ExtraParamLength);
- if (!NT_SUCCESS(Status))
- return Status;
- break;
-
- default:
- ASSERT(FALSE);
- return STATUS_UNSUCCESSFUL;
- }
-
- DPRINT("ExtraParamLength %lu\n", ExtraParamLength);
- OutputBuf = ExAllocatePoolWithTag(NonPagedPool,
- sizeof(ACPI_EVAL_OUTPUT_BUFFER) - sizeof(ACPI_METHOD_ARGUMENT) + ExtraParamLength,
- 'BpcA');
- if (!OutputBuf)
- return STATUS_INSUFFICIENT_RESOURCES;
-
- OutputBuf->Signature = ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE;
- OutputBuf->Length = ExtraParamLength;
- OutputBuf->Count = Count;
-
- switch (Obj->Type)
- {
- case ACPI_TYPE_INTEGER:
- ACPI_METHOD_SET_ARGUMENT_INTEGER(OutputBuf->Argument, Obj->Integer.Value);
- break;
-
- case ACPI_TYPE_STRING:
- ACPI_METHOD_SET_ARGUMENT_STRING(OutputBuf->Argument, Obj->String.Pointer);
- break;
-
- case ACPI_TYPE_BUFFER:
- ACPI_METHOD_SET_ARGUMENT_BUFFER(OutputBuf->Argument, Obj->Buffer.Pointer, Obj->Buffer.Length);
- break;
-
- case ACPI_TYPE_PACKAGE:
- Status = ConvertPackageArguments(OutputBuf->Argument, Obj);
- if (!NT_SUCCESS(Status))
- return Status;
- break;
-
- default:
- ASSERT(FALSE);
- return STATUS_UNSUCCESSFUL;
- }
-
- if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(ACPI_EVAL_OUTPUT_BUFFER) - sizeof(ACPI_METHOD_ARGUMENT) +
- ExtraParamLength)
- {
- RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, OutputBuf, sizeof(ACPI_EVAL_OUTPUT_BUFFER) - sizeof(ACPI_METHOD_ARGUMENT) +
- ExtraParamLength);
- Irp->IoStatus.Information = sizeof(ACPI_EVAL_OUTPUT_BUFFER) - sizeof(ACPI_METHOD_ARGUMENT) + ExtraParamLength;
- ExFreePoolWithTag(OutputBuf, 'BpcA');
- return STATUS_SUCCESS;
- }
- else
- {
- ExFreePoolWithTag(OutputBuf, 'BpcA');
- return STATUS_BUFFER_TOO_SMALL;
- }
- }
- else
- {
- DPRINT1("Query method %4s failed on %p\n", EvalInputBuff->MethodName, DeviceData->AcpiHandle);
- return STATUS_UNSUCCESSFUL;
- }
+ 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 */
--- /dev/null
+/*
+ * PROJECT: ReactOS API Tests
+ * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE: Unit Tests for acpi!Bus_PDO_EvalMethod (IOCTL_ACPI_EVAL_METHOD handler)
+ * COPYRIGHT: Copyright 2024 Dmitry Borisov (di.sean@protonmail.com)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <ndk/rtlfuncs.h>
+
+#define UNIT_TEST
+#include <acpi.h>
+
+#include <acpiioct.h>
+#include <ntintsafe.h>
+#include <initguid.h>
+
+/* TEST DEFINITIONS ***********************************************************/
+
+#define ok_eq_str_ex(entry, value, expected) \
+ ok(!strcmp(value, expected), \
+ "Line %lu: " #value " = \"%s\", expected \"%s\"\n", (entry)->Line, value, expected)
+
+#define ok_eq_print_ex(entry, value, expected, spec) \
+ ok((value) == (expected), \
+ "Line %lu: " #value " = " spec ", expected " spec "\n", (entry)->Line, value, expected)
+
+#define ok_not_print_ex(entry, value, expected, spec) \
+ ok((value) != (expected), \
+ "Line %lu: " #value " = " spec ", expected " spec "\n", (entry)->Line, value, expected)
+
+#define ok_eq_hex_ex(entry, value, expected) ok_eq_print_ex(entry, value, expected, "0x%08lx")
+#define ok_eq_pointer_ex(entry, value, expected) ok_eq_print_ex(entry, value, expected, "%p")
+#define ok_eq_int_ex(entry, value, expected) ok_eq_print_ex(entry, value, expected, "%d")
+#define ok_eq_uint_ex(entry, value, expected) ok_eq_print_ex(entry, value, expected, "%u")
+#define ok_eq_ulong_ex(entry, value, expected) ok_eq_print_ex(entry, value, expected, "%lu")
+#define ok_eq_ulonglong_ex(entry, value, expected) ok_eq_print_ex(entry, value, expected, "%I64u")
+
+#define ok_not_pointer_ex(entry, value, expected) ok_not_print_ex(entry, value, expected, "%p")
+
+typedef struct _EVAL_TEST_ENTRY
+{
+ ULONG Line;
+
+ ULONG Flags;
+#define STM_TEST_FLAG_INVALID_ARG_3_1 (1 << 0 )
+#define STM_TEST_FLAG_INVALID_ARG_3_2 (1 << 1 )
+#define STM_TEST_FLAG_INVALID_ARG_3_4 (1 << 2 )
+#define STM_TEST_FLAG_INVALID_ARG_3_5 (1 << 3 )
+#define STM_TEST_FLAG_INVALID_SIZE_1 (1 << 4 )
+#define STM_TEST_FLAG_LARGE_ARG_BUFFER (1 << 5 )
+#define STM_TEST_FLAG_CHANGE_ARG_COUNT (1 << 6 )
+#define STM_TEST_FLAG_SUB_IN_BUFFER (1 << 7 )
+#define STM_TEST_FLAG_SUB_IRP_BUFFER (1 << 8 )
+#define STM_TEST_FLAG_SET_IN_BUFFER (1 << 9 )
+#define STM_TEST_FLAG_SET_IRP_BUFFER (1 << 10)
+#define STM_TEST_FLAG_BAD_ARG_TYPE (1 << 11)
+
+#define GTM_TEST_FLAG_BAD_SIGNARUTE (1 << 0)
+#define GTM_TEST_FLAG_BUFFER_HAS_SIGNARUTE (1 << 1)
+#define GTM_TEST_FLAG_BUFFER_HAS_COUNT (1 << 2)
+#define GTM_TEST_FLAG_BUFFER_HAS_LENGTH (1 << 3)
+#define GTM_TEST_FLAG_ARG_HAS_BUFFER_TYPE (1 << 4)
+#define GTM_TEST_FLAG_ARG_HAS_DATA_LENGTH (1 << 5)
+#define GTM_TEST_FLAG_INC_OUT_BUFFER (1 << 6)
+#define GTM_TEST_FLAG_DEC_OUT_BUFFER (1 << 7)
+#define GTM_TEST_FLAG_SET_OUT_BUFFER (1 << 8)
+
+#define GTM_TEST_FLAG_METHOD_SUCCESS \
+ (GTM_TEST_FLAG_BUFFER_HAS_SIGNARUTE | \
+ GTM_TEST_FLAG_BUFFER_HAS_COUNT | GTM_TEST_FLAG_BUFFER_HAS_LENGTH | \
+ GTM_TEST_FLAG_ARG_HAS_BUFFER_TYPE | GTM_TEST_FLAG_ARG_HAS_DATA_LENGTH)
+
+#define DSM_TEST_FLAG_EMPTY_PACKAGE (1 << 0)
+#define DSM_TEST_FLAG_LARGE_SUB_PACKAGE_BUFFER (1 << 1)
+
+ NTSTATUS Status;
+ ULONG Value;
+} EVAL_TEST_ENTRY;
+
+/* KERNEL DEFINITIONS (MOCK) **************************************************/
+
+#define PAGED_CODE()
+#define CODE_SEG(...)
+#define DPRINT(...) do { if (0) DbgPrint(__VA_ARGS__); } while (0)
+#define DPRINT1(...) do { if (0) DbgPrint(__VA_ARGS__); } while (0)
+
+typedef struct _IO_STACK_LOCATION
+{
+ union
+ {
+ struct
+ {
+ ULONG OutputBufferLength;
+ ULONG InputBufferLength;
+ } DeviceIoControl;
+ } Parameters;
+} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
+
+typedef struct _IRP
+{
+ union
+ {
+ PVOID SystemBuffer;
+ } AssociatedIrp;
+ IO_STATUS_BLOCK IoStatus;
+
+ PVOID OutputBuffer;
+ IO_STACK_LOCATION MyStack;
+} IRP, *PIRP;
+
+static LONG DrvpBlocksAllocated = 0;
+
+#define ExAllocatePoolUninitialized ExAllocatePoolWithTag
+#define NonPagedPool 1
+static
+PVOID
+ExAllocatePoolWithTag(ULONG PoolType, SIZE_T NumberOfBytes, ULONG Tag)
+{
+ PULONG_PTR Mem;
+
+ Mem = HeapAlloc(GetProcessHeap(), 0, NumberOfBytes + 2 * sizeof(PVOID));
+ Mem[0] = NumberOfBytes;
+ Mem[1] = Tag;
+
+ ++DrvpBlocksAllocated;
+
+ return (PVOID)(Mem + 2);
+}
+
+static
+VOID
+ExFreePoolWithTag(PVOID MemPtr, ULONG Tag)
+{
+ PULONG_PTR Mem = MemPtr;
+
+ Mem -= 2;
+ ok(Mem[1] == Tag, "Tag is %lx, expected %lx\n", Tag, Mem[1]);
+ HeapFree(GetProcessHeap(), 0, Mem);
+
+ --DrvpBlocksAllocated;
+}
+
+static
+PIO_STACK_LOCATION
+IoGetCurrentIrpStackLocation(PIRP Irp)
+{
+ return &Irp->MyStack;
+}
+
+/* ACPI DEFINITIONS ***********************************************************/
+
+#include <pshpack1.h>
+typedef struct _IDE_ACPI_TIMING_MODE_BLOCK
+{
+ struct
+ {
+ ULONG PioSpeed;
+ ULONG DmaSpeed;
+ } Drive[2];
+
+ ULONG ModeFlags;
+} IDE_ACPI_TIMING_MODE_BLOCK, *PIDE_ACPI_TIMING_MODE_BLOCK;
+#include <poppack.h>
+
+#define STM_ID_BLOCK_SIZE 512
+
+/* ACPI DEFINITIONS (MOCK) ****************************************************/
+
+#define FAKE_SB_NAMESPACE_ACPI_HANDLE 0xFF0F0001
+#define FAKE_INTB_ACPI_HANDLE 0xFF0F0002
+#define FAKE_INTC_ACPI_HANDLE 0xFF0F0003
+
+typedef struct _PDO_DEVICE_DATA
+{
+ HANDLE AcpiHandle;
+} PDO_DEVICE_DATA, *PPDO_DEVICE_DATA;
+
+typedef struct _GTM_OBJECT_BUFFER
+{
+ ACPI_OBJECT Obj;
+ IDE_ACPI_TIMING_MODE_BLOCK TimingMode;
+} GTM_OBJECT_BUFFER, *PGTM_OBJECT_BUFFER;
+
+typedef struct _BIF_OBJECT_BUFFER
+{
+ ACPI_OBJECT Obj;
+ ACPI_OBJECT BatteryInformation[13];
+ CHAR Buffer1[1 + 1];
+ CHAR Buffer2[1 + 1];
+ CHAR Buffer3[4 + 1];
+ CHAR Buffer4[7 + 1];
+} BIF_OBJECT_BUFFER, *PBIF_OBJECT_BUFFER;
+
+typedef struct _PCL_OBJECT_BUFFER
+{
+ ACPI_OBJECT Obj;
+} PCL_OBJECT_BUFFER, *PPCL_OBJECT_BUFFER;
+
+typedef struct _PRT_OBJECT_BUFFER
+{
+ ACPI_OBJECT Obj;
+ ACPI_OBJECT PackageContainer[2];
+ ACPI_OBJECT Package1[4];
+ ACPI_OBJECT Package2[4];
+} PRT_OBJECT_BUFFER, *PPRT_OBJECT_BUFFER;
+
+DEFINE_GUID(MY_DSM_GUID,
+ 0xB76E0B40, 0x3EC6, 0x4DBD, 0x8A, 0xCB, 0x8B, 0xCA, 0x65, 0xB8, 0xBC, 0x70);
+
+static const ULONG DrvpBifIntegerFields[9] =
+{
+ 0, 50000, 50000, 1, 10000, 100, 50, 1, 1
+};
+
+static const ULONG DrvpMyDsmIntegerFields[3] =
+{
+ 0xAAAAAAAA, 0xBBBBBBBB, 0xDDDDDDDD
+};
+
+static const EVAL_TEST_ENTRY* DrvpEvalTestEntry;
+
+void *
+AcpiOsAllocate (
+ ACPI_SIZE Size)
+{
+ return ExAllocatePoolWithTag(NonPagedPool, Size, 'FFUB');
+}
+
+void
+AcpiOsFree (
+ void * Memory)
+{
+ ExFreePoolWithTag(Memory, 'FFUB');
+}
+
+ACPI_STATUS
+AcpiGetName (
+ ACPI_HANDLE Handle,
+ UINT32 NameType,
+ ACPI_BUFFER *Buffer)
+{
+ /* We don't support anything else */
+ ok(NameType == ACPI_SINGLE_NAME, "Unexpected call to %s\n", __FUNCTION__);
+
+ /* Return a NULL-terminated string */
+ if (Buffer->Length < (4 + 1))
+ return AE_BUFFER_OVERFLOW;
+
+ switch ((ULONG_PTR)Handle)
+ {
+ case FAKE_SB_NAMESPACE_ACPI_HANDLE:
+ RtlCopyMemory(Buffer->Pointer, "_SB_", sizeof("_SB_"));
+ break;
+
+ case FAKE_INTB_ACPI_HANDLE:
+ RtlCopyMemory(Buffer->Pointer, "LNKB", sizeof("LNKB"));
+ break;
+
+ case FAKE_INTC_ACPI_HANDLE:
+ RtlCopyMemory(Buffer->Pointer, "LNKC", sizeof("LNKC"));
+ break;
+
+ default:
+ return AE_BAD_PARAMETER;
+ }
+
+ return AE_OK;
+}
+
+ACPI_STATUS
+AcpiEvaluateObject (
+ ACPI_HANDLE Object,
+ ACPI_STRING Pathname,
+ ACPI_OBJECT_LIST *ParameterObjects,
+ ACPI_BUFFER *ReturnObjectBuffer)
+{
+ UNREFERENCED_PARAMETER(Object);
+
+ /* We don't support anything else */
+ ok(ReturnObjectBuffer->Length == ACPI_ALLOCATE_BUFFER,
+ "Unexpected call to %s\n", __FUNCTION__);
+
+ if (strcmp(Pathname, "_STM") == 0)
+ {
+ ACPI_OBJECT* Arg;
+ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode;
+
+ if (ParameterObjects->Count > 3)
+ return AE_AML_UNINITIALIZED_ARG;
+
+ if (ParameterObjects->Count != 3)
+ return AE_OK;
+
+ /* Argument 1 */
+ {
+ Arg = ParameterObjects->Pointer;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Type, ACPI_TYPE_BUFFER);
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Buffer.Length, sizeof(*TimingMode));
+
+ TimingMode = (PIDE_ACPI_TIMING_MODE_BLOCK)Arg->Buffer.Pointer;
+
+ ok_eq_ulong_ex(DrvpEvalTestEntry, TimingMode->Drive[0].PioSpeed, 508LU);
+ ok_eq_ulong_ex(DrvpEvalTestEntry, TimingMode->Drive[0].DmaSpeed, 120LU);
+ ok_eq_ulong_ex(DrvpEvalTestEntry, TimingMode->Drive[1].PioSpeed, 240LU);
+ ok_eq_ulong_ex(DrvpEvalTestEntry, TimingMode->Drive[1].DmaSpeed, 180LU);
+ ok_eq_hex_ex(DrvpEvalTestEntry, TimingMode->ModeFlags, 0x10LU);
+ }
+ /* Argument 2 */
+ {
+ ++Arg;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Type, ACPI_TYPE_BUFFER);
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Buffer.Length, STM_ID_BLOCK_SIZE);
+ }
+ /* Argument 3 */
+ {
+ ++Arg;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Type, ACPI_TYPE_BUFFER);
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Buffer.Length, STM_ID_BLOCK_SIZE);
+ }
+
+ return AE_OK;
+ }
+ else if (strcmp(Pathname, "_GTM") == 0)
+ {
+ PGTM_OBJECT_BUFFER ReturnObject;
+ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode;
+
+ ReturnObject = AcpiOsAllocate(sizeof(*ReturnObject));
+ if (!ReturnObject)
+ return AE_NO_MEMORY;
+
+ /*
+ * VPC 2007 output
+ * AcpiGetHandle(NULL, "\\_SB.PCI0.IDE0.CHN1", &ObjHandle);
+ * AcpiEvaluateObject(ObjHandle, "_GTM", NULL, &ReturnObj);
+ */
+ TimingMode = &ReturnObject->TimingMode;
+ TimingMode->Drive[0].PioSpeed = 900;
+ TimingMode->Drive[0].DmaSpeed = 900;
+ TimingMode->Drive[1].PioSpeed = 120;
+ TimingMode->Drive[1].DmaSpeed = 120;
+ TimingMode->ModeFlags = 0x12;
+
+ ReturnObject->Obj.Type = ACPI_TYPE_BUFFER;
+ ReturnObject->Obj.Buffer.Length = sizeof(*TimingMode);
+ ReturnObject->Obj.Buffer.Pointer = (PUCHAR)TimingMode;
+
+ ReturnObjectBuffer->Pointer = ReturnObject;
+ ReturnObjectBuffer->Length = sizeof(*ReturnObject);
+ return AE_OK;
+ }
+ else if (strcmp(Pathname, "_BIF") == 0)
+ {
+ PBIF_OBJECT_BUFFER ReturnObject;
+ ACPI_OBJECT* BatteryInformation;
+ ULONG i;
+
+ ReturnObject = AcpiOsAllocate(sizeof(*ReturnObject));
+ if (!ReturnObject)
+ return AE_NO_MEMORY;
+
+ /*
+ * Vbox 7.0 output
+ * AcpiGetHandle(NULL, "\\_SB.PCI0.BAT0", &ObjHandle);
+ * AcpiEvaluateObject(ObjHandle, "_BIF", NULL, &ReturnObj);
+ */
+ BatteryInformation = &ReturnObject->BatteryInformation[0];
+ for (i = 0; i < RTL_NUMBER_OF(DrvpBifIntegerFields); ++i)
+ {
+ BatteryInformation[i].Integer.Type = ACPI_TYPE_INTEGER;
+ BatteryInformation[i].Integer.Value = DrvpBifIntegerFields[i];
+ }
+ BatteryInformation[i].String.Type = ACPI_TYPE_STRING;
+ BatteryInformation[i].String.Length = 1; /* Excluding the trailing null */
+ BatteryInformation[i].String.Pointer = &ReturnObject->Buffer1[0];
+ RtlCopyMemory(BatteryInformation[i].String.Pointer, "1", sizeof("1"));
+ ++i;
+ BatteryInformation[i].String.Type = ACPI_TYPE_STRING;
+ BatteryInformation[i].String.Length = 1;
+ BatteryInformation[i].String.Pointer = &ReturnObject->Buffer2[0];
+ RtlCopyMemory(BatteryInformation[i].String.Pointer, "0", sizeof("0"));
+ ++i;
+ BatteryInformation[i].String.Type = ACPI_TYPE_STRING;
+ BatteryInformation[i].String.Length = 4;
+ BatteryInformation[i].String.Pointer = &ReturnObject->Buffer3[0];
+ RtlCopyMemory(BatteryInformation[i].String.Pointer, "VBOX", sizeof("VBOX"));
+ ++i;
+ BatteryInformation[i].String.Type = ACPI_TYPE_STRING;
+ BatteryInformation[i].String.Length = 7;
+ BatteryInformation[i].String.Pointer = &ReturnObject->Buffer4[0];
+ RtlCopyMemory(BatteryInformation[i].String.Pointer, "innotek", sizeof("innotek"));
+
+ ReturnObject->Obj.Type = ACPI_TYPE_PACKAGE;
+ ReturnObject->Obj.Package.Count = 13;
+ ReturnObject->Obj.Package.Elements = BatteryInformation;
+
+ ReturnObjectBuffer->Pointer = ReturnObject;
+ ReturnObjectBuffer->Length = sizeof(*ReturnObject);
+ return AE_OK;
+ }
+ else if (strcmp(Pathname, "_PCL") == 0)
+ {
+ PPCL_OBJECT_BUFFER ReturnObject;
+
+ ReturnObject = AcpiOsAllocate(sizeof(*ReturnObject));
+ if (!ReturnObject)
+ return AE_NO_MEMORY;
+
+ /*
+ * Vbox 7.0 output
+ * AcpiGetHandle(NULL, "\\_SB.PCI0.AC", &ObjHandle);
+ * AcpiEvaluateObject(ObjHandle, "_PCL", NULL, &ReturnObj);
+ */
+ ReturnObject->Obj.Reference.Type = ACPI_TYPE_LOCAL_REFERENCE;
+ ReturnObject->Obj.Reference.ActualType = ACPI_TYPE_DEVICE;
+ ReturnObject->Obj.Reference.Handle = (ACPI_HANDLE)(ULONG_PTR)FAKE_SB_NAMESPACE_ACPI_HANDLE;
+
+ ReturnObjectBuffer->Pointer = ReturnObject;
+ ReturnObjectBuffer->Length = sizeof(*ReturnObject);
+ return AE_OK;
+ }
+ else if (strcmp(Pathname, "_PRT") == 0)
+ {
+ PPRT_OBJECT_BUFFER ReturnObject;
+ ULONG i;
+
+ ReturnObject = AcpiOsAllocate(sizeof(*ReturnObject));
+ if (!ReturnObject)
+ return AE_NO_MEMORY;
+
+ /*
+ * Vbox 7.0 output
+ * AcpiGetHandle(NULL, "\\_SB.PCI0", &ObjHandle);
+ * AcpiEvaluateObject(ObjHandle, "_PRT", NULL, &ReturnObj);
+ *
+ * NOTE: To avoid similar copies of code executed and tested over and over again
+ * we return 2 packages. The original method returns 120 packages.
+ */
+ ReturnObject->Obj.Type = ACPI_TYPE_PACKAGE;
+ ReturnObject->Obj.Package.Count = 2;
+ ReturnObject->Obj.Package.Elements = &ReturnObject->PackageContainer[0];
+
+ i = 0;
+ ReturnObject->PackageContainer[i].Type = ACPI_TYPE_PACKAGE;
+ ReturnObject->PackageContainer[i].Package.Count = 4;
+ ReturnObject->PackageContainer[i].Package.Elements = &ReturnObject->Package1[0];
+ ++i;
+ ReturnObject->PackageContainer[i].Type = ACPI_TYPE_PACKAGE;
+ ReturnObject->PackageContainer[i].Package.Count = 4;
+ ReturnObject->PackageContainer[i].Package.Elements = &ReturnObject->Package2[0];
+
+ /* Package 1 */
+ i = 0;
+ ReturnObject->Package1[i].Integer.Type = ACPI_TYPE_INTEGER;
+ ReturnObject->Package1[i].Integer.Value = 0x0002FFFF;
+ ++i;
+ ReturnObject->Package1[i].Integer.Type = ACPI_TYPE_INTEGER;
+ ReturnObject->Package1[i].Integer.Value = 0x00000000;
+ ++i;
+ ReturnObject->Package1[i].Reference.Type = ACPI_TYPE_LOCAL_REFERENCE;
+ ReturnObject->Package1[i].Reference.ActualType = ACPI_TYPE_DEVICE;
+ ReturnObject->Package1[i].Reference.Handle = (ACPI_HANDLE)(ULONG_PTR)FAKE_INTB_ACPI_HANDLE;
+ ++i;
+ ReturnObject->Package1[i].Integer.Type = ACPI_TYPE_INTEGER;
+ ReturnObject->Package1[i].Integer.Value = 0x00000000;
+
+ /* Package 2 */
+ i = 0;
+ ReturnObject->Package2[i].Integer.Type = ACPI_TYPE_INTEGER;
+ ReturnObject->Package2[i].Integer.Value = 0x0002FFFF;
+ ++i;
+ ReturnObject->Package2[i].Integer.Type = ACPI_TYPE_INTEGER;
+ ReturnObject->Package2[i].Integer.Value = 0x00000001;
+ ++i;
+ ReturnObject->Package2[i].Reference.Type = ACPI_TYPE_LOCAL_REFERENCE;
+ ReturnObject->Package2[i].Reference.ActualType = ACPI_TYPE_DEVICE;
+ ReturnObject->Package2[i].Reference.Handle = (ACPI_HANDLE)(ULONG_PTR)FAKE_INTC_ACPI_HANDLE;
+ ++i;
+ ReturnObject->Package2[i].Integer.Type = ACPI_TYPE_INTEGER;
+ ReturnObject->Package2[i].Integer.Value = 0x00000000;
+
+ ReturnObjectBuffer->Pointer = ReturnObject;
+ ReturnObjectBuffer->Length = sizeof(*ReturnObject);
+ return AE_OK;
+ }
+ else if (strcmp(Pathname, "_DSM") == 0)
+ {
+ ACPI_OBJECT* Arg;
+
+ /* Assumed object count per the spec */
+ ok_eq_uint(ParameterObjects->Count, 4);
+
+ if (ParameterObjects->Count != 4)
+ return AE_AML_UNINITIALIZED_ARG;
+
+ /* Argument 1 */
+ {
+ Arg = ParameterObjects->Pointer;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Type, ACPI_TYPE_BUFFER);
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Buffer.Length, sizeof(GUID));
+ }
+
+ /* NOTE: This UUID doesn't exist, for testing purposes only */
+ if (IsEqualGUID(Arg->Buffer.Pointer, &MY_DSM_GUID))
+ {
+ /* Argument 2 */
+ {
+ ++Arg;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Type, ACPI_TYPE_INTEGER);
+ ok_eq_ulonglong_ex(DrvpEvalTestEntry, Arg->Integer.Value, 1ULL);
+ }
+ /* Argument 3 */
+ {
+ ++Arg;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Type, ACPI_TYPE_INTEGER);
+ ok_eq_ulonglong_ex(DrvpEvalTestEntry, Arg->Integer.Value, 2ULL);
+ }
+ /* Argument 4 */
+ {
+ ++Arg;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Type, ACPI_TYPE_PACKAGE);
+
+ if (DrvpEvalTestEntry->Flags & DSM_TEST_FLAG_EMPTY_PACKAGE)
+ {
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Package.Count, 0);
+ ok_eq_pointer_ex(DrvpEvalTestEntry, Arg->Package.Elements, NULL);
+ }
+ else
+ {
+ ACPI_OBJECT* PackageArg;
+ ACPI_OBJECT* PackageArg2;
+ ULONG i;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Package.Count, 4);
+ ok_not_pointer_ex(DrvpEvalTestEntry, Arg->Package.Elements, NULL);
+
+ if (!Arg->Package.Elements)
+ return AE_AML_UNINITIALIZED_ARG;
+
+ /* Package 1 Arguments 1-2 */
+ PackageArg = Arg->Package.Elements;
+ for (i = 0; i < RTL_NUMBER_OF(DrvpMyDsmIntegerFields) - 1; i++)
+ {
+ ok_eq_uint_ex(DrvpEvalTestEntry, PackageArg->Type, ACPI_TYPE_INTEGER);
+ ok_eq_ulonglong_ex(DrvpEvalTestEntry,
+ PackageArg->Integer.Value,
+ (ULONG64)DrvpMyDsmIntegerFields[i]);
+
+ ++PackageArg;
+ }
+
+ /* Package 1 Argument 3 */
+ {
+ Arg = PackageArg;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Type, ACPI_TYPE_PACKAGE);
+ ok_eq_uint_ex(DrvpEvalTestEntry, Arg->Package.Count, 1);
+
+ /* Package 2 Argument 1 */
+ PackageArg2 = Arg->Package.Elements;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, PackageArg2->Type, ACPI_TYPE_STRING);
+
+ /* Excluding the trailing null */
+ ok_eq_uint_ex(DrvpEvalTestEntry,
+ PackageArg2->String.Length,
+ (sizeof("1_TESTDATATESTDATA_2") - 1));
+ ok_eq_int_ex(DrvpEvalTestEntry,
+ memcmp(PackageArg2->String.Pointer,
+ "1_TESTDATATESTDATA_2",
+ sizeof("1_TESTDATATESTDATA_2") - 1),
+ 0);
+ }
+ /* Package 1 Argument 4 */
+ {
+ ++PackageArg;
+
+ ok_eq_uint_ex(DrvpEvalTestEntry, PackageArg->Type, ACPI_TYPE_INTEGER);
+ ok_eq_ulonglong_ex(DrvpEvalTestEntry,
+ PackageArg->Integer.Value,
+ (ULONG64)DrvpMyDsmIntegerFields[2]);
+ }
+ }
+ }
+
+ return AE_OK;
+ }
+ }
+
+ return AE_NOT_FOUND;
+}
+
+#include "../../../../drivers/bus/acpi/eval.c"
+
+/* GLOBALS ********************************************************************/
+
+/* 2 ID blocks + timings + room for the test data */
+#define STM_MAX_BUFFER_SIZE \
+ (FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) + \
+ 2 * STM_ID_BLOCK_SIZE + sizeof(IDE_ACPI_TIMING_MODE_BLOCK) + 0x100)
+
+#define GTM_MAX_BUFFER_SIZE \
+ (FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + \
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof(IDE_ACPI_TIMING_MODE_BLOCK)) + 0x50)
+
+static const EVAL_TEST_ENTRY DrvpSmtTests[] =
+{
+ { __LINE__, 0, STATUS_SUCCESS },
+ { __LINE__, STM_TEST_FLAG_INVALID_SIZE_1, STATUS_ACPI_INVALID_ARGTYPE },
+ { __LINE__, STM_TEST_FLAG_LARGE_ARG_BUFFER, STATUS_ACPI_INVALID_ARGTYPE },
+ { __LINE__, STM_TEST_FLAG_SUB_IN_BUFFER, STATUS_SUCCESS, 1 },
+ { __LINE__, STM_TEST_FLAG_SUB_IN_BUFFER, STATUS_SUCCESS, 9 },
+ { __LINE__, STM_TEST_FLAG_SUB_IRP_BUFFER, STATUS_SUCCESS, 1 },
+ { __LINE__, STM_TEST_FLAG_SUB_IRP_BUFFER, STATUS_SUCCESS, 9 },
+ { __LINE__, STM_TEST_FLAG_SET_IN_BUFFER, STATUS_SUCCESS, 0 },
+ { __LINE__, STM_TEST_FLAG_SET_IRP_BUFFER, STATUS_INFO_LENGTH_MISMATCH, 0 },
+ { __LINE__, STM_TEST_FLAG_SET_IRP_BUFFER, STATUS_INFO_LENGTH_MISMATCH,
+ RTL_SIZEOF_THROUGH_FIELD(ACPI_EVAL_INPUT_BUFFER, Signature) - 2 },
+ { __LINE__, STM_TEST_FLAG_SET_IRP_BUFFER, STATUS_INFO_LENGTH_MISMATCH,
+ RTL_SIZEOF_THROUGH_FIELD(ACPI_EVAL_INPUT_BUFFER, Signature) - 1 },
+ { __LINE__, STM_TEST_FLAG_SET_IRP_BUFFER, STATUS_INFO_LENGTH_MISMATCH,
+ RTL_SIZEOF_THROUGH_FIELD(ACPI_EVAL_INPUT_BUFFER, Signature) },
+ { __LINE__, STM_TEST_FLAG_SET_IRP_BUFFER, STATUS_INFO_LENGTH_MISMATCH,
+ sizeof(ACPI_EVAL_INPUT_BUFFER) - 2 },
+ { __LINE__, STM_TEST_FLAG_SET_IRP_BUFFER, STATUS_INFO_LENGTH_MISMATCH,
+ sizeof(ACPI_EVAL_INPUT_BUFFER) - 1 },
+ { __LINE__, STM_TEST_FLAG_SET_IRP_BUFFER, STATUS_INSUFFICIENT_RESOURCES,
+ sizeof(ACPI_EVAL_INPUT_BUFFER) },
+ { __LINE__, STM_TEST_FLAG_SET_IRP_BUFFER, STATUS_INSUFFICIENT_RESOURCES,
+ sizeof(ACPI_EVAL_INPUT_BUFFER) + 1 },
+ { __LINE__, STM_TEST_FLAG_SET_IRP_BUFFER, STATUS_INSUFFICIENT_RESOURCES,
+ sizeof(ACPI_EVAL_INPUT_BUFFER) + 2 },
+ { __LINE__, STM_TEST_FLAG_BAD_ARG_TYPE, STATUS_SUCCESS, 0 },
+ { __LINE__, STM_TEST_FLAG_CHANGE_ARG_COUNT, STATUS_ACPI_INCORRECT_ARGUMENT_COUNT, 0 },
+
+#if 0
+ /*
+ * The return status depends on AML interpreter implementation
+ * and testing it is not practical, keeping this for reference only.
+ */
+ { __LINE__, STM_TEST_FLAG_INVALID_ARG_3_1, STATUS_SUCCESS },
+ { __LINE__, STM_TEST_FLAG_INVALID_ARG_3_2, STATUS_ACPI_INVALID_ARGTYPE },
+ { __LINE__, STM_TEST_FLAG_INVALID_ARG_3_4, STATUS_SUCCESS },
+ { __LINE__, STM_TEST_FLAG_INVALID_ARG_3_5, STATUS_SUCCESS },
+ { __LINE__, STM_TEST_FLAG_CHANGE_ARG_COUNT, STATUS_ACPI_INCORRECT_ARGUMENT_COUNT, 30 },
+ { __LINE__, STM_TEST_FLAG_CHANGE_ARG_COUNT, STATUS_ACPI_INCORRECT_ARGUMENT_COUNT, 2 },
+#endif
+};
+
+static const EVAL_TEST_ENTRY DrvpGtmTests[] =
+{
+ { __LINE__, GTM_TEST_FLAG_METHOD_SUCCESS, STATUS_SUCCESS },
+ { __LINE__, GTM_TEST_FLAG_METHOD_SUCCESS |
+ GTM_TEST_FLAG_INC_OUT_BUFFER, STATUS_SUCCESS, 1 },
+ { __LINE__, GTM_TEST_FLAG_METHOD_SUCCESS |
+ GTM_TEST_FLAG_DEC_OUT_BUFFER, STATUS_BUFFER_OVERFLOW, 1 },
+ { __LINE__, GTM_TEST_FLAG_SET_OUT_BUFFER, STATUS_SUCCESS, 0 },
+ { __LINE__, GTM_TEST_FLAG_SET_OUT_BUFFER, STATUS_BUFFER_TOO_SMALL, 1 },
+ { __LINE__, GTM_TEST_FLAG_SET_OUT_BUFFER, STATUS_BUFFER_TOO_SMALL,
+ FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) - 1 },
+ { __LINE__, GTM_TEST_FLAG_SET_OUT_BUFFER, STATUS_BUFFER_TOO_SMALL,
+ FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) },
+ { __LINE__, GTM_TEST_FLAG_SET_OUT_BUFFER, STATUS_BUFFER_TOO_SMALL,
+ FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + 1 },
+ { __LINE__, GTM_TEST_FLAG_SET_OUT_BUFFER, STATUS_BUFFER_TOO_SMALL,
+ sizeof(ACPI_EVAL_OUTPUT_BUFFER) - 1 },
+ { __LINE__, GTM_TEST_FLAG_BUFFER_HAS_SIGNARUTE |
+ GTM_TEST_FLAG_BUFFER_HAS_COUNT |
+ GTM_TEST_FLAG_BUFFER_HAS_LENGTH |
+ GTM_TEST_FLAG_SET_OUT_BUFFER,
+ STATUS_BUFFER_OVERFLOW,
+ sizeof(ACPI_EVAL_OUTPUT_BUFFER) },
+ { __LINE__, GTM_TEST_FLAG_BUFFER_HAS_SIGNARUTE |
+ GTM_TEST_FLAG_BUFFER_HAS_COUNT |
+ GTM_TEST_FLAG_BUFFER_HAS_LENGTH |
+ GTM_TEST_FLAG_SET_OUT_BUFFER,
+ STATUS_BUFFER_OVERFLOW,
+ sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 1 },
+
+ /* Pass an invalid signature */
+ { __LINE__, GTM_TEST_FLAG_BAD_SIGNARUTE, STATUS_INVALID_PARAMETER_1 },
+ { __LINE__, GTM_TEST_FLAG_BAD_SIGNARUTE | GTM_TEST_FLAG_SET_OUT_BUFFER,
+ STATUS_INVALID_PARAMETER_1, 0 },
+ { __LINE__, GTM_TEST_FLAG_BAD_SIGNARUTE | GTM_TEST_FLAG_SET_OUT_BUFFER,
+ STATUS_BUFFER_TOO_SMALL,
+ sizeof(ACPI_EVAL_OUTPUT_BUFFER) - 1 },
+ { __LINE__, GTM_TEST_FLAG_BAD_SIGNARUTE | GTM_TEST_FLAG_SET_OUT_BUFFER,
+ STATUS_INVALID_PARAMETER_1,
+ sizeof(ACPI_EVAL_OUTPUT_BUFFER) },
+};
+
+static const EVAL_TEST_ENTRY DrvpBifTests[] =
+{
+ { __LINE__, 0, STATUS_SUCCESS },
+};
+
+static const EVAL_TEST_ENTRY DrvpPclTests[] =
+{
+ { __LINE__, 0, STATUS_SUCCESS },
+};
+
+static const EVAL_TEST_ENTRY DrvpPrtTests[] =
+{
+ { __LINE__, 0, STATUS_SUCCESS },
+};
+
+static const EVAL_TEST_ENTRY DrvpDsmTests[] =
+{
+ { __LINE__, 0, STATUS_SUCCESS },
+ { __LINE__, DSM_TEST_FLAG_EMPTY_PACKAGE, STATUS_SUCCESS },
+ { __LINE__, DSM_TEST_FLAG_LARGE_SUB_PACKAGE_BUFFER, STATUS_ACPI_INVALID_ARGTYPE },
+};
+
+/* FUNCTIONS ******************************************************************/
+
+static
+NTSTATUS
+DrvCallAcpiDriver(
+ _In_ PVOID InputBuffer,
+ _In_ ULONG InputBufferLength,
+ _Out_opt_ PACPI_EVAL_OUTPUT_BUFFER OutputBuffer,
+ _In_ ULONG OutputBufferLength)
+{
+ PDO_DEVICE_DATA DeviceData;
+ IRP Irp;
+
+ DeviceData.AcpiHandle = NULL;
+
+ Irp.AssociatedIrp.SystemBuffer = InputBuffer;
+ Irp.OutputBuffer = OutputBuffer;
+
+ Irp.MyStack.Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
+ Irp.MyStack.Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
+
+ return Bus_PDO_EvalMethod(&DeviceData, &Irp);
+}
+
+static
+VOID
+DrvEvaluateStmObject(
+ _In_ const EVAL_TEST_ENTRY* TestEntry,
+ _In_ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode,
+ _In_ PUCHAR IdBlock,
+ _In_ PACPI_EVAL_INPUT_BUFFER_COMPLEX InputBuffer)
+{
+ PACPI_METHOD_ARGUMENT Argument, Argument2, Argument3;
+ NTSTATUS Status;
+ ULONG InputBufferSize, IrpBufferSize;
+
+ InputBufferSize = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) +
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*TimingMode)) +
+ ACPI_METHOD_ARGUMENT_LENGTH(STM_ID_BLOCK_SIZE) +
+ ACPI_METHOD_ARGUMENT_LENGTH(STM_ID_BLOCK_SIZE);
+
+ if (TestEntry->Flags & STM_TEST_FLAG_INVALID_SIZE_1)
+ {
+ InputBufferSize -= ACPI_METHOD_ARGUMENT_LENGTH(STM_ID_BLOCK_SIZE) +
+ ACPI_METHOD_ARGUMENT_LENGTH(STM_ID_BLOCK_SIZE);
+ }
+
+ InputBuffer->MethodNameAsUlong = 'MTS_'; // _STM
+ InputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
+ if (TestEntry->Flags & STM_TEST_FLAG_SUB_IN_BUFFER)
+ {
+ InputBuffer->Size = InputBufferSize - TestEntry->Value;
+ }
+ else if (TestEntry->Flags & STM_TEST_FLAG_SET_IN_BUFFER)
+ {
+ InputBuffer->Size = TestEntry->Value;
+ }
+ else
+ {
+ InputBuffer->Size = InputBufferSize;
+ }
+
+ if (TestEntry->Flags & STM_TEST_FLAG_CHANGE_ARG_COUNT)
+ {
+ InputBuffer->ArgumentCount = TestEntry->Value;
+ }
+ else
+ {
+ InputBuffer->ArgumentCount = 3;
+ }
+
+ /* Argument 1: The channel timing information block */
+ Argument = InputBuffer->Argument;
+ ACPI_METHOD_SET_ARGUMENT_BUFFER(Argument, TimingMode, sizeof(*TimingMode));
+
+ /* Argument 2: The ATA drive ID block */
+ Argument2 = ACPI_METHOD_NEXT_ARGUMENT(Argument);
+ ACPI_METHOD_SET_ARGUMENT_BUFFER(Argument2, IdBlock, STM_ID_BLOCK_SIZE);
+
+ /* Argument 3: The ATA drive ID block */
+ Argument3 = ACPI_METHOD_NEXT_ARGUMENT(Argument2);
+ ACPI_METHOD_SET_ARGUMENT_BUFFER(Argument3, IdBlock, STM_ID_BLOCK_SIZE);
+
+ if (TestEntry->Flags & STM_TEST_FLAG_BAD_ARG_TYPE)
+ {
+ Argument3->Type = 0xFFFF;
+ }
+
+ if (TestEntry->Flags & STM_TEST_FLAG_LARGE_ARG_BUFFER)
+ {
+ Argument2->DataLength = STM_ID_BLOCK_SIZE * 2;
+ Argument3->DataLength = STM_ID_BLOCK_SIZE * 2;
+ }
+
+ if (TestEntry->Flags & STM_TEST_FLAG_INVALID_ARG_3_1)
+ {
+ ACPI_METHOD_SET_ARGUMENT_STRING(Argument3, IdBlock);
+ }
+ else if (TestEntry->Flags & STM_TEST_FLAG_INVALID_ARG_3_2)
+ {
+ ACPI_METHOD_SET_ARGUMENT_INTEGER(Argument3, 0xDEADBEEF);
+ }
+ else if (TestEntry->Flags & STM_TEST_FLAG_INVALID_ARG_3_4)
+ {
+ Argument3->DataLength += 5;
+ }
+ else if (TestEntry->Flags & STM_TEST_FLAG_INVALID_ARG_3_5)
+ {
+ Argument3->DataLength -= 5;
+ }
+
+ if (TestEntry->Flags & STM_TEST_FLAG_SUB_IRP_BUFFER)
+ {
+ IrpBufferSize = InputBufferSize - TestEntry->Value;
+ }
+ else if (TestEntry->Flags & STM_TEST_FLAG_SET_IRP_BUFFER)
+ {
+ IrpBufferSize = TestEntry->Value;
+ }
+ else
+ {
+ IrpBufferSize = InputBufferSize;
+ }
+
+ /* Evaluate the _STM method */
+ DrvpEvalTestEntry = TestEntry;
+ Status = DrvCallAcpiDriver(InputBuffer, IrpBufferSize, NULL, 0);
+
+ ok_eq_hex_ex(TestEntry, Status, TestEntry->Status);
+}
+
+static
+VOID
+DrvTestComplexBuffer(VOID)
+{
+ IDE_ACPI_TIMING_MODE_BLOCK TimingMode;
+ UCHAR IdBlock[STM_ID_BLOCK_SIZE];
+ ULONG i;
+ UCHAR Buffer[STM_MAX_BUFFER_SIZE];
+ PACPI_EVAL_INPUT_BUFFER_COMPLEX InputBuffer = (PACPI_EVAL_INPUT_BUFFER_COMPLEX)Buffer;
+
+ /* Initialize method arguments */
+ RtlZeroMemory(IdBlock, sizeof(IdBlock));
+ TimingMode.Drive[0].PioSpeed = 508;
+ TimingMode.Drive[0].DmaSpeed = 120;
+ TimingMode.Drive[1].PioSpeed = 240;
+ TimingMode.Drive[1].DmaSpeed = 180;
+ TimingMode.ModeFlags = 0x10;
+
+ for (i = 0; i < RTL_NUMBER_OF(DrvpSmtTests); ++i)
+ {
+ DrvEvaluateStmObject(&DrvpSmtTests[i], &TimingMode, IdBlock, InputBuffer);
+ }
+}
+
+static
+VOID
+DrvEvaluateGtmObject(
+ _In_ const EVAL_TEST_ENTRY* TestEntry,
+ _In_ PACPI_EVAL_OUTPUT_BUFFER OutputBuffer)
+{
+ ACPI_EVAL_INPUT_BUFFER InputBuffer;
+ ULONG OutputBufferSize;
+ NTSTATUS Status;
+ PACPI_METHOD_ARGUMENT Argument;
+ ULONG Signature, Count, Length;
+ USHORT Type, DataLength;
+ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode;
+
+ InputBuffer.MethodNameAsUlong = 'MTG_'; // _GTM
+ if (TestEntry->Flags & GTM_TEST_FLAG_BAD_SIGNARUTE)
+ InputBuffer.Signature = 'BAD0';
+ else
+ InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
+
+ OutputBufferSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*TimingMode));
+
+ if (TestEntry->Flags & GTM_TEST_FLAG_INC_OUT_BUFFER)
+ {
+ OutputBufferSize += TestEntry->Value;
+ }
+ else if (TestEntry->Flags & GTM_TEST_FLAG_DEC_OUT_BUFFER)
+ {
+ OutputBufferSize -= TestEntry->Value;
+ }
+ else if (TestEntry->Flags & GTM_TEST_FLAG_SET_OUT_BUFFER)
+ {
+ OutputBufferSize = TestEntry->Value;
+ }
+
+ /* Evaluate the _GTM method */
+ Status = DrvCallAcpiDriver(&InputBuffer, sizeof(InputBuffer), OutputBuffer, OutputBufferSize);
+
+ ok_eq_hex_ex(TestEntry, Status, TestEntry->Status);
+
+ if (TestEntry->Flags & GTM_TEST_FLAG_BUFFER_HAS_SIGNARUTE)
+ Signature = ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE;
+ else
+ Signature = 0;
+ ok_eq_hex_ex(TestEntry, OutputBuffer->Signature, Signature);
+
+ if (TestEntry->Flags & GTM_TEST_FLAG_BUFFER_HAS_COUNT)
+ Count = 1;
+ else
+ Count = 0;
+ ok_eq_ulong_ex(TestEntry, OutputBuffer->Count, Count);
+
+ if (TestEntry->Flags & GTM_TEST_FLAG_BUFFER_HAS_LENGTH)
+ {
+ Length = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*TimingMode));
+ }
+ else
+ {
+ Length = 0;
+ }
+ ok_eq_ulong_ex(TestEntry, OutputBuffer->Length, Length);
+
+ Argument = OutputBuffer->Argument;
+ if (TestEntry->Flags & GTM_TEST_FLAG_ARG_HAS_BUFFER_TYPE)
+ Type = ACPI_METHOD_ARGUMENT_BUFFER;
+ else
+ Type = ACPI_METHOD_ARGUMENT_INTEGER;
+ ok_eq_uint_ex(TestEntry, Argument->Type, Type);
+
+ if (TestEntry->Flags & GTM_TEST_FLAG_ARG_HAS_DATA_LENGTH)
+ DataLength = sizeof(ACPI_EVAL_OUTPUT_BUFFER);
+ else
+ DataLength = 0;
+ ok_eq_uint_ex(TestEntry, Argument->DataLength, DataLength);
+
+ if ((TestEntry->Flags & GTM_TEST_FLAG_ARG_HAS_BUFFER_TYPE) && NT_SUCCESS(TestEntry->Status))
+ {
+ TimingMode = (PIDE_ACPI_TIMING_MODE_BLOCK)Argument->Data;
+
+ ok_eq_ulong_ex(TestEntry, TimingMode->Drive[0].PioSpeed, 900LU);
+ ok_eq_ulong_ex(TestEntry, TimingMode->Drive[0].DmaSpeed, 900LU);
+ ok_eq_ulong_ex(TestEntry, TimingMode->Drive[1].PioSpeed, 120LU);
+ ok_eq_ulong_ex(TestEntry, TimingMode->Drive[1].DmaSpeed, 120LU);
+ ok_eq_hex_ex(TestEntry, TimingMode->ModeFlags, 0x12LU);
+ }
+}
+
+static
+VOID
+DrvTestInputBuffer(VOID)
+{
+ UCHAR Buffer[GTM_MAX_BUFFER_SIZE];
+ ULONG i;
+ PACPI_EVAL_OUTPUT_BUFFER OutputBuffer = (PACPI_EVAL_OUTPUT_BUFFER)Buffer;
+
+ for (i = 0; i < RTL_NUMBER_OF(DrvpGtmTests); ++i)
+ {
+ RtlZeroMemory(Buffer, sizeof(Buffer));
+
+ DrvEvaluateGtmObject(&DrvpGtmTests[i], OutputBuffer);
+ }
+}
+
+static
+VOID
+DrvEvaluateBifObject(
+ _In_ const EVAL_TEST_ENTRY* TestEntry,
+ _In_ PACPI_EVAL_OUTPUT_BUFFER OutputBuffer)
+{
+ ACPI_EVAL_INPUT_BUFFER InputBuffer;
+ ULONG i, OutputBufferSize;
+ NTSTATUS Status;
+ PACPI_METHOD_ARGUMENT Argument;
+
+ InputBuffer.MethodNameAsUlong = 'FIB_'; // _BIF
+ InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
+
+ OutputBufferSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) * 9 +
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof("1")) +
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof("0")) +
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof("VBOX")) +
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof("innotek"));
+
+ /* Evaluate the _BIF method */
+ Status = DrvCallAcpiDriver(&InputBuffer, sizeof(InputBuffer), OutputBuffer, OutputBufferSize);
+
+ ok_eq_hex_ex(TestEntry, Status, TestEntry->Status);
+ ok_eq_hex_ex(TestEntry, OutputBuffer->Signature, (ULONG)ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE);
+ ok_eq_ulong_ex(TestEntry, OutputBuffer->Count, 13LU);
+ ok_eq_ulong_ex(TestEntry, OutputBuffer->Length, OutputBufferSize);
+
+ /* Arguments 1-9 */
+ Argument = OutputBuffer->Argument;
+ for (i = 0; i < RTL_NUMBER_OF(DrvpBifIntegerFields); ++i)
+ {
+ ok_eq_uint_ex(TestEntry, Argument->Type, ACPI_METHOD_ARGUMENT_INTEGER);
+ ok_eq_uint_ex(TestEntry, Argument->DataLength, sizeof(ULONG));
+ ok_eq_ulong_ex(TestEntry, Argument->Argument, DrvpBifIntegerFields[i]);
+
+ Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
+ }
+ /* Argument 10 */
+ {
+ ok_eq_uint_ex(TestEntry, Argument->Type, ACPI_METHOD_ARGUMENT_STRING);
+ ok_eq_uint_ex(TestEntry, Argument->DataLength, sizeof("1")); // Including the trailing null
+ ok_eq_str_ex(TestEntry, (PCSTR)Argument->Data, "1");
+ }
+ /* Argument 11 */
+ {
+ Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
+
+ ok_eq_uint_ex(TestEntry, Argument->Type, ACPI_METHOD_ARGUMENT_STRING);
+ ok_eq_uint_ex(TestEntry, Argument->DataLength, sizeof("0"));
+ ok_eq_str_ex(TestEntry, (PCSTR)Argument->Data, "0");
+ }
+ /* Argument 12 */
+ {
+ Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
+
+ ok_eq_uint_ex(TestEntry, Argument->Type, ACPI_METHOD_ARGUMENT_STRING);
+ ok_eq_uint_ex(TestEntry, Argument->DataLength, sizeof("VBOX"));
+ ok_eq_str_ex(TestEntry, (PCSTR)Argument->Data, "VBOX");
+ }
+ /* Argument 13 */
+ {
+ Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
+
+ ok_eq_uint_ex(TestEntry, Argument->Type, ACPI_METHOD_ARGUMENT_STRING);
+ ok_eq_uint_ex(TestEntry, Argument->DataLength, sizeof("innotek"));
+ ok_eq_str_ex(TestEntry, (PCSTR)Argument->Data, "innotek");
+ }
+}
+
+static
+VOID
+DrvTestPackageReturnValueAndStringData(VOID)
+{
+ UCHAR Buffer[0x100];
+ ULONG i;
+ PACPI_EVAL_OUTPUT_BUFFER OutputBuffer = (PACPI_EVAL_OUTPUT_BUFFER)Buffer;
+
+ for (i = 0; i < RTL_NUMBER_OF(DrvpBifTests); ++i)
+ {
+ RtlZeroMemory(Buffer, sizeof(Buffer));
+
+ DrvEvaluateBifObject(&DrvpBifTests[i], OutputBuffer);
+ }
+}
+
+static
+VOID
+DrvEvaluatePclObject(
+ _In_ const EVAL_TEST_ENTRY* TestEntry,
+ _In_ PACPI_EVAL_OUTPUT_BUFFER OutputBuffer)
+{
+ ACPI_EVAL_INPUT_BUFFER InputBuffer;
+ ULONG OutputBufferSize;
+ NTSTATUS Status;
+ PACPI_METHOD_ARGUMENT Argument;
+
+ InputBuffer.MethodNameAsUlong = 'LCP_'; // _PCL
+ InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
+
+ OutputBufferSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof("ABCD")); // ACPI name for the object
+
+ /* Evaluate the _PCL method */
+ Status = DrvCallAcpiDriver(&InputBuffer, sizeof(InputBuffer), OutputBuffer, OutputBufferSize);
+
+ ok_eq_hex_ex(TestEntry, Status, TestEntry->Status);
+ ok_eq_hex_ex(TestEntry, OutputBuffer->Signature, (ULONG)ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE);
+ ok_eq_ulong_ex(TestEntry, OutputBuffer->Count, 1LU);
+ ok_eq_ulong_ex(TestEntry, OutputBuffer->Length, OutputBufferSize);
+
+ Argument = OutputBuffer->Argument;
+
+ ok_eq_uint_ex(TestEntry, Argument->Type, ACPI_METHOD_ARGUMENT_STRING);
+ ok_eq_uint_ex(TestEntry, Argument->DataLength, sizeof("ABCD"));
+ ok_eq_str_ex(TestEntry, (PCSTR)Argument->Data, "_SB_");
+}
+
+static
+VOID
+DrvTestReferenceReturnValue(VOID)
+{
+ UCHAR Buffer[0x100];
+ ULONG i;
+ PACPI_EVAL_OUTPUT_BUFFER OutputBuffer = (PACPI_EVAL_OUTPUT_BUFFER)Buffer;
+
+ for (i = 0; i < RTL_NUMBER_OF(DrvpPclTests); ++i)
+ {
+ RtlZeroMemory(Buffer, sizeof(Buffer));
+
+ DrvEvaluatePclObject(&DrvpPclTests[i], OutputBuffer);
+ }
+}
+
+static
+VOID
+DrvEvaluatePrtObject(
+ _In_ const EVAL_TEST_ENTRY* TestEntry,
+ _In_ PACPI_EVAL_OUTPUT_BUFFER OutputBuffer)
+{
+ ACPI_EVAL_INPUT_BUFFER InputBuffer;
+ ULONG PackageNum, ArgNum, OutputBufferSize;
+ NTSTATUS Status;
+ PACPI_METHOD_ARGUMENT Argument, PackageArgument;
+
+ InputBuffer.MethodNameAsUlong = 'TRP_'; // _PRT
+ InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
+
+#define PRT_PACKAGE_ENTRY_SIZE \
+ (ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) * 3 + \
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof("LNKB")))
+
+ OutputBufferSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) +
+ ACPI_METHOD_ARGUMENT_LENGTH(PRT_PACKAGE_ENTRY_SIZE) * 2;
+
+ /* Evaluate the _PRT method */
+ Status = DrvCallAcpiDriver(&InputBuffer, sizeof(InputBuffer), OutputBuffer, OutputBufferSize);
+
+ ok_eq_hex_ex(TestEntry, Status, TestEntry->Status);
+ ok_eq_hex_ex(TestEntry, OutputBuffer->Signature, (ULONG)ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE);
+ ok_eq_ulong_ex(TestEntry, OutputBuffer->Count, 2LU);
+ ok_eq_ulong_ex(TestEntry, OutputBuffer->Length, OutputBufferSize);
+
+ Argument = OutputBuffer->Argument;
+ for (PackageNum = 0; PackageNum < 2; PackageNum++)
+ {
+ ok_eq_uint_ex(TestEntry, Argument->Type, ACPI_METHOD_ARGUMENT_PACKAGE);
+ ok_eq_uint_ex(TestEntry, Argument->DataLength, (USHORT)PRT_PACKAGE_ENTRY_SIZE);
+
+ PackageArgument = (PACPI_METHOD_ARGUMENT)Argument->Data;
+ for (ArgNum = 0; ArgNum < 4; ArgNum++)
+ {
+ if (ArgNum != 2)
+ {
+ ULONG ExpectedValue;
+
+ ok_eq_uint_ex(TestEntry, PackageArgument->Type, ACPI_METHOD_ARGUMENT_INTEGER);
+ ok_eq_uint_ex(TestEntry, PackageArgument->DataLength, sizeof(ULONG));
+
+ if (ArgNum == 0)
+ {
+ ExpectedValue = 0x0002FFFF;
+ }
+ else
+ {
+ if ((PackageNum == 1) && (ArgNum == 1))
+ ExpectedValue = 0x00000001;
+ else
+ ExpectedValue = 0x00000000;
+ }
+ ok_eq_ulong_ex(TestEntry, PackageArgument->Argument, ExpectedValue);
+ }
+ else
+ {
+ ok_eq_uint_ex(TestEntry, PackageArgument->Type, ACPI_METHOD_ARGUMENT_STRING);
+ ok_eq_uint_ex(TestEntry, PackageArgument->DataLength, sizeof("ABCD"));
+ ok_eq_str_ex(TestEntry, (PCSTR)PackageArgument->Data,
+ (PackageNum == 0) ? "LNKB" : "LNKC");
+ }
+
+ PackageArgument = ACPI_METHOD_NEXT_ARGUMENT(PackageArgument);
+ }
+
+ Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
+ }
+}
+
+static
+VOID
+DrvTestNestedPackageReturnValue(VOID)
+{
+ UCHAR Buffer[0x100];
+ ULONG i;
+ PACPI_EVAL_OUTPUT_BUFFER OutputBuffer = (PACPI_EVAL_OUTPUT_BUFFER)Buffer;
+
+ for (i = 0; i < RTL_NUMBER_OF(DrvpPclTests); ++i)
+ {
+ RtlZeroMemory(Buffer, sizeof(Buffer));
+
+ DrvEvaluatePrtObject(&DrvpPrtTests[i], OutputBuffer);
+ }
+}
+
+static
+VOID
+DrvEvaluateDsmObject(
+ _In_ const EVAL_TEST_ENTRY* TestEntry)
+{
+#define MY_DSM_SUB_PACKAGE_ENTRY_SIZE \
+ (ACPI_METHOD_ARGUMENT_LENGTH(sizeof("1_TESTDATATESTDATA_2")))
+
+#define MY_DSM_PACKAGE_ENTRY_SIZE \
+ (ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) * 3 + \
+ ACPI_METHOD_ARGUMENT_LENGTH(MY_DSM_SUB_PACKAGE_ENTRY_SIZE))
+
+#define MY_DSM_BUFFER_SIZE \
+ (FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) + \
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof(GUID)) + \
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) + \
+ ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) + \
+ ACPI_METHOD_ARGUMENT_LENGTH(MY_DSM_PACKAGE_ENTRY_SIZE))
+
+ UCHAR Buffer[MY_DSM_BUFFER_SIZE];
+ ULONG InputSize;
+ NTSTATUS Status;
+ PACPI_METHOD_ARGUMENT Argument, PackageArgument, PackageArgument2;
+ PACPI_EVAL_INPUT_BUFFER_COMPLEX InputBuffer = (PACPI_EVAL_INPUT_BUFFER_COMPLEX)Buffer;
+
+ RtlZeroMemory(Buffer, sizeof(Buffer));
+
+ InputSize = MY_DSM_BUFFER_SIZE;
+ if (TestEntry->Flags & DSM_TEST_FLAG_EMPTY_PACKAGE)
+ {
+ InputSize -= ACPI_METHOD_ARGUMENT_LENGTH(MY_DSM_PACKAGE_ENTRY_SIZE);
+ InputSize += ACPI_METHOD_ARGUMENT_LENGTH(ACPI_METHOD_ARGUMENT_LENGTH(0));
+ }
+
+ InputBuffer->MethodNameAsUlong = 'MSD_'; // _DSM
+ InputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE;
+ InputBuffer->ArgumentCount = 4;
+ InputBuffer->Size = InputSize;
+
+ /* Argument 1: The UUID */
+ Argument = InputBuffer->Argument;
+ ACPI_METHOD_SET_ARGUMENT_BUFFER(Argument, &MY_DSM_GUID, sizeof(GUID));
+
+ /* Argument 2: The Revision ID */
+ Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
+ ACPI_METHOD_SET_ARGUMENT_INTEGER(Argument, 1);
+
+ /* Argument 3: The Function Index */
+ Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
+ ACPI_METHOD_SET_ARGUMENT_INTEGER(Argument, 2);
+
+ /* Argument 4: The device-specific package */
+ Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
+ Argument->Type = ACPI_METHOD_ARGUMENT_PACKAGE;
+ if (TestEntry->Flags & DSM_TEST_FLAG_EMPTY_PACKAGE)
+ {
+ /* Empty package */
+ Argument->DataLength = ACPI_METHOD_ARGUMENT_LENGTH(0);
+ Argument->Argument = 0;
+ }
+ else
+ {
+ Argument->DataLength = MY_DSM_PACKAGE_ENTRY_SIZE;
+
+ /* Package 1 Argument 1: Some test data */
+ PackageArgument = (PACPI_METHOD_ARGUMENT)Argument->Data;
+ ACPI_METHOD_SET_ARGUMENT_INTEGER(PackageArgument, DrvpMyDsmIntegerFields[0]);
+
+ /* Package 1 Argument 2: Some test data */
+ PackageArgument = ACPI_METHOD_NEXT_ARGUMENT(PackageArgument);
+ ACPI_METHOD_SET_ARGUMENT_INTEGER(PackageArgument, DrvpMyDsmIntegerFields[1]);
+
+ /* Package 1 Argument 3: Start a new subpackage */
+ PackageArgument = ACPI_METHOD_NEXT_ARGUMENT(PackageArgument);
+ PackageArgument->Type = ACPI_METHOD_ARGUMENT_PACKAGE;
+ PackageArgument->DataLength = MY_DSM_SUB_PACKAGE_ENTRY_SIZE;
+
+ /* Package 2 Argument 1: Some test data */
+ PackageArgument2 = (PACPI_METHOD_ARGUMENT)PackageArgument->Data;
+ ACPI_METHOD_SET_ARGUMENT_STRING(PackageArgument2, "1_TESTDATATESTDATA_2");
+
+ if (TestEntry->Flags & DSM_TEST_FLAG_LARGE_SUB_PACKAGE_BUFFER)
+ {
+ PackageArgument2->DataLength = 32768;
+ }
+ else
+ {
+ /* Package 1 Argument 4: Some test data */
+ PackageArgument = ACPI_METHOD_NEXT_ARGUMENT(PackageArgument);
+ ACPI_METHOD_SET_ARGUMENT_INTEGER(PackageArgument, DrvpMyDsmIntegerFields[2]);
+ }
+ }
+
+ /* Evaluate the _DSM method */
+ DrvpEvalTestEntry = TestEntry;
+ Status = DrvCallAcpiDriver(InputBuffer, InputSize, NULL, 0);
+
+ ok_eq_hex_ex(TestEntry, Status, TestEntry->Status);
+}
+
+static
+VOID
+DrvTestPackageInputValue(VOID)
+{
+ ULONG i;
+
+ for (i = 0; i < RTL_NUMBER_OF(DrvpDsmTests); ++i)
+ {
+ DrvEvaluateDsmObject(&DrvpDsmTests[i]);
+ }
+}
+
+static
+VOID
+DrvTestUnknownMethod(VOID)
+{
+ NTSTATUS Status;
+ ACPI_EVAL_INPUT_BUFFER InputBuffer;
+
+ InputBuffer.MethodNameAsUlong = 'FFF_'; // _FFF
+ InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
+
+ /* Try to evaluate some unsupported control method */
+ Status = DrvCallAcpiDriver(&InputBuffer, sizeof(InputBuffer), NULL, 0);
+
+ ok_eq_hex(Status, STATUS_OBJECT_NAME_NOT_FOUND);
+}
+
+START_TEST(Bus_PDO_EvalMethod)
+{
+ DrvTestComplexBuffer();
+ DrvTestInputBuffer();
+ DrvTestPackageReturnValueAndStringData();
+ DrvTestReferenceReturnValue();
+ DrvTestNestedPackageReturnValue();
+ DrvTestPackageInputValue();
+ DrvTestUnknownMethod();
+
+ ok(DrvpBlocksAllocated == 0, "Leaking memory %ld blocks\n", DrvpBlocksAllocated);
+}