From 5070e8960aed88d55cc9b7970dbe7bcb9a775d1a Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 1 Apr 2024 21:21:35 +0500 Subject: [PATCH] [ACPI] Fix object evaluation (#6302) 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. --- drivers/bus/acpi/eval.c | 1014 ++++++++++--- drivers/bus/acpi/include/acpisys.h | 23 +- drivers/bus/acpi/main.c | 53 +- drivers/bus/acpi/precomp.h | 1 + .../apitests/acpi/Bus_PDO_EvalMethod.c | 1349 +++++++++++++++++ modules/rostests/apitests/acpi/CMakeLists.txt | 1 + modules/rostests/apitests/acpi/testlist.c | 2 + 7 files changed, 2217 insertions(+), 226 deletions(-) create mode 100644 modules/rostests/apitests/acpi/Bus_PDO_EvalMethod.c diff --git a/drivers/bus/acpi/eval.c b/drivers/bus/acpi/eval.c index aeef1535cc5..d2b94dd7c04 100644 --- a/drivers/bus/acpi/eval.c +++ b/drivers/bus/acpi/eval.c @@ -1,255 +1,861 @@ +#ifndef UNIT_TEST #include "precomp.h" #define NDEBUG #include +#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; - TotalLength = 0; - for (i = 0; i < Package->Package.Count; i++) + 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)) { - 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: { - case ACPI_TYPE_INTEGER: - DPRINT("Integer %lu\n", sizeof(ACPI_METHOD_ARGUMENT)); - ACPI_METHOD_SET_ARGUMENT_INTEGER(Ptr, Package->Package.Elements[i].Integer.Value); - break; - - 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); - break; - - default: - DPRINT1("Unsupported element type %lu\n", Package->Package.Elements[i].Type); - return STATUS_UNSUCCESSFUL; + ACPI_METHOD_SET_ARGUMENT_INTEGER(Ptr, Obj->Integer.Value); + break; } - Ptr = ACPI_METHOD_NEXT_ARGUMENT(Ptr); + 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(PPDO_DEVICE_DATA DeviceData, - PIRP Irp) +Bus_PDO_EvalMethod( + _In_ PPDO_DEVICE_DATA DeviceData, + _Inout_ PIRP Irp) { - 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]; + 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 }; - if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG)) - return STATUS_INVALID_PARAMETER; + PAGED_CODE(); - Signature = *((PULONG)Irp->AssociatedIrp.SystemBuffer); + IoStack = IoGetCurrentIrpStackLocation(Irp); + EvalInputBuffer = 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; + Status = EvalCreateParametersList(Irp, IoStack, EvalInputBuffer, &ParamList); + if (!NT_SUCCESS(Status)) + return Status; - ParamList.Count = 0; - break; + AcpiStatus = EvalEvaluateObject(DeviceData, EvalInputBuffer, &ParamList, &ReturnBuffer); - case ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_SIGNATURE: - SimpleInt = Irp->AssociatedIrp.SystemBuffer; + if (ParamList.Count != 0) + EvalFreeParametersList(&ParamList); - if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER)) - return STATUS_INVALID_PARAMETER; + if (!ACPI_SUCCESS(AcpiStatus)) + { + DPRINT("Query method '%.4s' failed on %p with status 0x%04lx\n", + EvalInputBuffer->MethodName, + DeviceData->AcpiHandle, + AcpiStatus); - ParamList.Count = 1; + return EvalAcpiStatusToNtStatus(AcpiStatus); + } - ParamList.Pointer = ExAllocatePoolWithTag(NonPagedPool, sizeof(ACPI_OBJECT), 'OpcA'); - if (!ParamList.Pointer) return STATUS_INSUFFICIENT_RESOURCES; + Status = EvalCreateOutputArguments(Irp, IoStack, &ReturnBuffer); - ParamList.Pointer[0].Type = ACPI_TYPE_INTEGER; - ParamList.Pointer[0].Integer.Value = SimpleInt->IntegerArgument; - break; + if (ReturnBuffer.Pointer) + AcpiOsFree(ReturnBuffer.Pointer); - 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; - } + 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 */ diff --git a/drivers/bus/acpi/include/acpisys.h b/drivers/bus/acpi/include/acpisys.h index d7553cbd546..00015050620 100644 --- a/drivers/bus/acpi/include/acpisys.h +++ b/drivers/bus/acpi/include/acpisys.h @@ -73,6 +73,13 @@ typedef struct _FDO_DEVICE_DATA } FDO_DEVICE_DATA, *PFDO_DEVICE_DATA; +typedef struct _EVAL_WORKITEM_DATA +{ + PPDO_DEVICE_DATA DeviceData; + PIRP Irp; + WORK_QUEUE_ITEM WorkQueueItem; +} EVAL_WORKITEM_DATA, *PEVAL_WORKITEM_DATA; + #define FDO_FROM_PDO(pdoData) \ ((PFDO_DEVICE_DATA) (pdoData)->ParentFdo->DeviceExtension) @@ -93,10 +100,15 @@ NTSTATUS ACPIEnumerateDevices( PFDO_DEVICE_DATA DeviceExtension); +CODE_SEG("PAGE") +WORKER_THREAD_ROUTINE Bus_PDO_EvalMethodWorker; + +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); NTSTATUS NTAPI @@ -115,13 +127,6 @@ PnPMinorFunctionString ( UCHAR MinorFunction ); -NTSTATUS -NTAPI -Bus_AddDevice( - PDRIVER_OBJECT DriverObject, - PDEVICE_OBJECT PhysicalDeviceObject - ); - NTSTATUS Bus_SendIrpSynchronously ( PDEVICE_OBJECT DeviceObject, diff --git a/drivers/bus/acpi/main.c b/drivers/bus/acpi/main.c index e7c43b5069c..8ee5af04204 100644 --- a/drivers/bus/acpi/main.c +++ b/drivers/bus/acpi/main.c @@ -5,18 +5,11 @@ #define NDEBUG #include -NTSTATUS -NTAPI -DriverEntry ( - PDRIVER_OBJECT DriverObject, - PUNICODE_STRING RegistryPath - ); +CODE_SEG("INIT") +DRIVER_INITIALIZE DriverEntry; -#ifdef ALLOC_PRAGMA -#pragma alloc_text (INIT, DriverEntry) -#pragma alloc_text (PAGE, Bus_AddDevice) - -#endif +CODE_SEG("PAGE") +DRIVER_ADD_DEVICE Bus_AddDevice; extern struct acpi_device *sleep_button; extern struct acpi_device *power_button; @@ -26,6 +19,7 @@ LPWSTR ProcessorIdString = NULL; LPWSTR ProcessorNameString = NULL; +CODE_SEG("PAGE") NTSTATUS NTAPI Bus_AddDevice( @@ -234,8 +228,6 @@ ACPIDispatchDeviceControl( ULONG Caps = 0; HANDLE ThreadHandle; - PAGED_CODE (); - irpStack = IoGetCurrentIrpStackLocation (Irp); ASSERT (IRP_MJ_DEVICE_CONTROL == irpStack->MajorFunction); @@ -247,10 +239,41 @@ ACPIDispatchDeviceControl( { switch (irpStack->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_ACPI_ASYNC_EVAL_METHOD: + { + ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); + + if (KeGetCurrentIrql() == DISPATCH_LEVEL) + { + PEVAL_WORKITEM_DATA workItemData; + + workItemData = ExAllocatePoolUninitialized(NonPagedPool, + sizeof(*workItemData), + 'ipcA'); + if (!workItemData) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + workItemData->Irp = Irp; + workItemData->DeviceData = (PPDO_DEVICE_DATA)commonData; + + ExInitializeWorkItem(&workItemData->WorkQueueItem, + Bus_PDO_EvalMethodWorker, + workItemData); + ExQueueWorkItem(&workItemData->WorkQueueItem, DelayedWorkQueue); + + status = STATUS_PENDING; + break; + } + __fallthrough; + } case IOCTL_ACPI_EVAL_METHOD: + { status = Bus_PDO_EvalMethod((PPDO_DEVICE_DATA)commonData, Irp); break; + } case IOCTL_GET_SYS_BUTTON_CAPS: if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) @@ -336,6 +359,7 @@ ACPIDispatchDeviceControl( } static +CODE_SEG("INIT") NTSTATUS AcpiRegOpenKey(IN HANDLE ParentKeyHandle, IN LPCWSTR KeyName, @@ -359,6 +383,7 @@ AcpiRegOpenKey(IN HANDLE ParentKeyHandle, } static +CODE_SEG("INIT") NTSTATUS AcpiRegQueryValue(IN HANDLE KeyHandle, IN LPWSTR ValueName, @@ -449,6 +474,7 @@ AcpiRegQueryValue(IN HANDLE KeyHandle, } static +CODE_SEG("INIT") NTSTATUS GetProcessorInformation(VOID) { @@ -682,6 +708,7 @@ done: return Status; } +CODE_SEG("INIT") NTSTATUS NTAPI DriverEntry ( diff --git a/drivers/bus/acpi/precomp.h b/drivers/bus/acpi/precomp.h index 43c15b6a49f..5158df769ad 100644 --- a/drivers/bus/acpi/precomp.h +++ b/drivers/bus/acpi/precomp.h @@ -15,5 +15,6 @@ #include #include #include +#include #endif /* _ACPI_PCH_ */ diff --git a/modules/rostests/apitests/acpi/Bus_PDO_EvalMethod.c b/modules/rostests/apitests/acpi/Bus_PDO_EvalMethod.c new file mode 100644 index 00000000000..e191a9ad252 --- /dev/null +++ b/modules/rostests/apitests/acpi/Bus_PDO_EvalMethod.c @@ -0,0 +1,1349 @@ +/* + * 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 + +#define WIN32_NO_STATUS +#include + +#define UNIT_TEST +#include + +#include +#include +#include + +/* 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 +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 + +#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); +} diff --git a/modules/rostests/apitests/acpi/CMakeLists.txt b/modules/rostests/apitests/acpi/CMakeLists.txt index 8092262c0ff..082872c58ee 100644 --- a/modules/rostests/apitests/acpi/CMakeLists.txt +++ b/modules/rostests/apitests/acpi/CMakeLists.txt @@ -2,6 +2,7 @@ include_directories(${REACTOS_SOURCE_DIR}/drivers/bus/acpi/acpica/include) list(APPEND SOURCE + Bus_PDO_EvalMethod.c Bus_PDO_QueryResourceRequirements.c testlist.c) diff --git a/modules/rostests/apitests/acpi/testlist.c b/modules/rostests/apitests/acpi/testlist.c index e5fb3bc6e41..6dbac0eca3c 100644 --- a/modules/rostests/apitests/acpi/testlist.c +++ b/modules/rostests/apitests/acpi/testlist.c @@ -1,10 +1,12 @@ #define STANDALONE #include +extern void func_Bus_PDO_EvalMethod(void); extern void func_Bus_PDO_QueryResourceRequirements(void); const struct test winetest_testlist[] = { + { "Bus_PDO_EvalMethod", func_Bus_PDO_EvalMethod }, { "Bus_PDO_QueryResourceRequirements", func_Bus_PDO_QueryResourceRequirements }, { 0, 0 } };