xref: /reactos/drivers/bus/acpi/eval.c (revision 5070e896)
1 #ifndef UNIT_TEST
2 #include "precomp.h"
3 
4 #define NDEBUG
5 #include <debug.h>
6 #endif /* UNIT_TEST */
7 
8 #define AcpiVerifyInBuffer(Stack, Length) \
9     ((Stack)->Parameters.DeviceIoControl.InputBufferLength >= Length)
10 
11 #define AcpiVerifyOutBuffer(Stack, Length) \
12     ((Stack)->Parameters.DeviceIoControl.OutputBufferLength >= Length)
13 
14 #define TAG_ACPI_PARAMETERS_LIST     'OpcA'
15 #define TAG_ACPI_PACKAGE_LIST        'PpcA'
16 
17 /**
18  * Null terminated ACPI name for the object.
19  * For example, _DSM, LNKB, etc.
20  */
21 #define ACPI_OBJECT_NAME_LENGTH      (4 + 1)
22 
23 /**
24  * Maximum number of nested package structures supported by the driver.
25  * This should be enough to cover all the existing ACPI methods.
26  */
27 #define ACPI_MAX_PACKAGE_DEPTH       5
28 
29 /**
30  * @brief Performs translation from the supplied object reference
31  * into a string method argument.
32  */
33 static
34 CODE_SEG("PAGE")
35 NTSTATUS
EvalConvertObjectReference(_Out_ PACPI_METHOD_ARGUMENT Argument,_In_ ACPI_OBJECT * Reference)36 EvalConvertObjectReference(
37     _Out_ PACPI_METHOD_ARGUMENT Argument,
38     _In_ ACPI_OBJECT* Reference)
39 {
40     ACPI_BUFFER OutName;
41     ACPI_STATUS AcpiStatus;
42 
43     PAGED_CODE();
44 
45     Argument->Type = ACPI_METHOD_ARGUMENT_STRING;
46     Argument->DataLength = ACPI_OBJECT_NAME_LENGTH;
47 
48     /* Convert the object handle to an ACPI name */
49     OutName.Length = ACPI_OBJECT_NAME_LENGTH;
50     OutName.Pointer = &Argument->Data[0];
51 
52     AcpiStatus = AcpiGetName(Reference->Reference.Handle, ACPI_SINGLE_NAME, &OutName);
53     if (!ACPI_SUCCESS(AcpiStatus))
54     {
55         DPRINT1("AcpiGetName() failed on %p with status 0x%04lx\n",
56                 Reference->Reference.Handle,
57                 AcpiStatus);
58 
59         ASSERT(FALSE);
60         return STATUS_UNSUCCESSFUL;
61     }
62 
63     return STATUS_SUCCESS;
64 }
65 
66 /**
67  * @brief Calculates the number of bytes needed for returned method argument
68  * based on the type of an ACPI_OBJECT structure.
69  */
70 static
71 CODE_SEG("PAGE")
72 NTSTATUS
EvalGetElementSize(_In_ ACPI_OBJECT * Obj,_In_ ULONG Depth,_Out_opt_ PULONG Count,_Out_ PULONG Size)73 EvalGetElementSize(
74     _In_ ACPI_OBJECT* Obj,
75     _In_ ULONG Depth,
76     _Out_opt_ PULONG Count,
77     _Out_ PULONG Size)
78 {
79     ULONG TotalCount, TotalLength;
80     NTSTATUS Status;
81 
82     PAGED_CODE();
83 
84     if (Depth >= ACPI_MAX_PACKAGE_DEPTH)
85     {
86         ASSERT(FALSE);
87         return STATUS_UNSUCCESSFUL;
88     }
89 
90     switch (Obj->Type)
91     {
92         case ACPI_TYPE_INTEGER:
93         {
94             TotalLength = ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG));
95             TotalCount = 1;
96             break;
97         }
98 
99         case ACPI_TYPE_STRING:
100         {
101             TotalLength = ACPI_METHOD_ARGUMENT_LENGTH(Obj->String.Length + sizeof(UCHAR));
102             TotalCount = 1;
103             break;
104         }
105 
106         case ACPI_TYPE_BUFFER:
107         {
108             TotalLength = ACPI_METHOD_ARGUMENT_LENGTH(Obj->Buffer.Length);
109             TotalCount = 1;
110             break;
111         }
112 
113         case ACPI_TYPE_PACKAGE:
114         {
115             ULONG i, TotalPackageLength;
116 
117             /* Get the size of the current packet */
118             TotalPackageLength = 0;
119             for (i = 0; i < Obj->Package.Count; i++)
120             {
121                 ULONG ElementSize;
122 
123                 Status = EvalGetElementSize(&Obj->Package.Elements[i],
124                                             Depth + 1,
125                                             NULL,
126                                             &ElementSize);
127                 if (!NT_SUCCESS(Status))
128                     return Status;
129 
130                 TotalPackageLength += ElementSize;
131             }
132 
133             /* Check if we need to wrap the list of elements into a package */
134             if (Depth > 0)
135             {
136                 TotalPackageLength = ACPI_METHOD_ARGUMENT_LENGTH(TotalPackageLength);
137             }
138 
139             TotalLength = TotalPackageLength;
140             TotalCount = Obj->Package.Count;
141             break;
142         }
143 
144         case ACPI_TYPE_LOCAL_REFERENCE:
145         {
146             TotalLength = ACPI_METHOD_ARGUMENT_LENGTH(ACPI_OBJECT_NAME_LENGTH);
147             TotalCount = 1;
148             break;
149         }
150 
151         default:
152         {
153             DPRINT1("Unsupported element type %lu\n", Obj->Type);
154             return STATUS_UNSUCCESSFUL;
155         }
156     }
157 
158     if (Count)
159         *Count = TotalCount;
160 
161     *Size = TotalLength;
162 
163     return STATUS_SUCCESS;
164 }
165 
166 /**
167  * @brief Performs translation from the supplied ACPI_OBJECT structure into a method argument.
168  */
169 static
170 CODE_SEG("PAGE")
171 NTSTATUS
EvalConvertEvaluationResults(_Out_ ACPI_METHOD_ARGUMENT * Argument,_In_ ULONG Depth,_In_ ACPI_OBJECT * Obj)172 EvalConvertEvaluationResults(
173     _Out_ ACPI_METHOD_ARGUMENT* Argument,
174     _In_ ULONG Depth,
175     _In_ ACPI_OBJECT* Obj)
176 {
177     ACPI_METHOD_ARGUMENT *Ptr;
178     NTSTATUS Status;
179 
180     PAGED_CODE();
181 
182     if (Depth >= ACPI_MAX_PACKAGE_DEPTH)
183     {
184         ASSERT(FALSE);
185         return STATUS_UNSUCCESSFUL;
186     }
187 
188     Ptr = Argument;
189     switch (Obj->Type)
190     {
191         case ACPI_TYPE_INTEGER:
192         {
193             ACPI_METHOD_SET_ARGUMENT_INTEGER(Ptr, Obj->Integer.Value);
194             break;
195         }
196 
197         case ACPI_TYPE_STRING:
198         {
199             ACPI_METHOD_SET_ARGUMENT_STRING(Ptr, Obj->String.Pointer);
200             break;
201         }
202 
203         case ACPI_TYPE_BUFFER:
204         {
205             ACPI_METHOD_SET_ARGUMENT_BUFFER(Ptr, Obj->Buffer.Pointer, Obj->Buffer.Length);
206             break;
207         }
208 
209         case ACPI_TYPE_PACKAGE:
210         {
211             ULONG i;
212 
213             /* Check if we need to wrap the list of elements into a package */
214             if (Depth > 0)
215             {
216                 ULONG TotalPackageLength;
217 
218                 /* Get the size of the current packet */
219                 TotalPackageLength = 0;
220                 for (i = 0; i < Obj->Package.Count; i++)
221                 {
222                     ULONG ElementSize;
223 
224                     Status = EvalGetElementSize(&Obj->Package.Elements[i],
225                                                 Depth + 1,
226                                                 NULL,
227                                                 &ElementSize);
228                     if (!NT_SUCCESS(Status))
229                         return Status;
230 
231                     TotalPackageLength += ElementSize;
232                 }
233 
234                 /* Start a new package */
235                 Argument->Type = ACPI_METHOD_ARGUMENT_PACKAGE;
236                 Argument->DataLength = TotalPackageLength;
237 
238                 Ptr = (PACPI_METHOD_ARGUMENT)Ptr->Data;
239             }
240 
241             for (i = 0; i < Obj->Package.Count; i++)
242             {
243                 Status = EvalConvertEvaluationResults(Ptr, Depth + 1, &Obj->Package.Elements[i]);
244                 if (!NT_SUCCESS(Status))
245                     return Status;
246 
247                 Ptr = ACPI_METHOD_NEXT_ARGUMENT(Ptr);
248             }
249             break;
250         }
251 
252         case ACPI_TYPE_LOCAL_REFERENCE:
253         {
254             Status = EvalConvertObjectReference(Ptr, Obj);
255             if (!NT_SUCCESS(Status))
256                 return Status;
257             break;
258         }
259 
260         default:
261         {
262             DPRINT1("Unsupported element type %lu\n", Obj->Type);
263             return STATUS_UNSUCCESSFUL;
264         }
265     }
266 
267     return STATUS_SUCCESS;
268 }
269 
270 /**
271  * @brief Returns the number of sub-objects (elements) in a package.
272  */
273 static
274 CODE_SEG("PAGE")
275 ULONG
EvalGetPackageCount(_In_ PACPI_METHOD_ARGUMENT Package,_In_ PACPI_METHOD_ARGUMENT PackageArgument,_In_ ULONG DataLength)276 EvalGetPackageCount(
277     _In_ PACPI_METHOD_ARGUMENT Package,
278     _In_ PACPI_METHOD_ARGUMENT PackageArgument,
279     _In_ ULONG DataLength)
280 {
281     ACPI_METHOD_ARGUMENT* Ptr;
282     ULONG TotalLength = 0, TotalCount = 0;
283 
284     PAGED_CODE();
285 
286     /* Empty package */
287     if (DataLength < ACPI_METHOD_ARGUMENT_LENGTH(0) || Package->Argument == 0)
288         return 0;
289 
290     Ptr = PackageArgument;
291     while (TotalLength < DataLength)
292     {
293         TotalLength += ACPI_METHOD_ARGUMENT_LENGTH_FROM_ARGUMENT(Ptr);
294         TotalCount++;
295 
296         Ptr = ACPI_METHOD_NEXT_ARGUMENT(Ptr);
297     }
298 
299     return TotalCount;
300 }
301 
302 /**
303  * @brief Performs translation from the supplied method argument into an ACPI_OBJECT structure.
304  */
305 static
306 CODE_SEG("PAGE")
307 NTSTATUS
EvalConvertParameterObjects(_Out_ ACPI_OBJECT * Arg,_In_ ULONG Depth,_In_ PACPI_METHOD_ARGUMENT Argument,_In_ PIO_STACK_LOCATION IoStack,_In_ ULONG Offset)308 EvalConvertParameterObjects(
309     _Out_ ACPI_OBJECT* Arg,
310     _In_ ULONG Depth,
311     _In_ PACPI_METHOD_ARGUMENT Argument,
312     _In_ PIO_STACK_LOCATION IoStack,
313     _In_ ULONG Offset)
314 {
315     PAGED_CODE();
316 
317     if (Depth >= ACPI_MAX_PACKAGE_DEPTH)
318     {
319         ASSERT(FALSE);
320         return STATUS_UNSUCCESSFUL;
321     }
322 
323     /* Validate that the method argument fits into the buffer */
324     if (Depth > 0)
325     {
326         Offset += ACPI_METHOD_ARGUMENT_LENGTH_FROM_ARGUMENT(Argument);
327 
328         if (!AcpiVerifyInBuffer(IoStack, Offset))
329         {
330             DPRINT1("Argument buffer outside of argument bounds\n");
331             return STATUS_ACPI_INVALID_ARGTYPE;
332         }
333     }
334 
335     switch (Argument->Type)
336     {
337         case ACPI_METHOD_ARGUMENT_INTEGER:
338         {
339             Arg->Type = ACPI_TYPE_INTEGER;
340             Arg->Integer.Value = (ULONG64)Argument->Argument;
341             break;
342         }
343 
344         case ACPI_METHOD_ARGUMENT_STRING:
345         {
346             /*
347              * FIXME: Add tests and remove this.
348              * We should either default to an empty string, or reject the IOCTL.
349              */
350             ASSERT(Argument->DataLength >= sizeof(UCHAR));
351             if (Argument->DataLength < sizeof(UCHAR))
352             {
353                 return STATUS_NOT_IMPLEMENTED;
354             }
355 
356             Arg->Type = ACPI_TYPE_STRING;
357             Arg->String.Pointer = (PCHAR)&Argument->Data[0];
358             Arg->String.Length = Argument->DataLength - sizeof(UCHAR);
359             break;
360         }
361 
362         case ACPI_METHOD_ARGUMENT_BUFFER:
363         {
364             Arg->Type = ACPI_TYPE_BUFFER;
365             Arg->Buffer.Pointer = &Argument->Data[0];
366             Arg->Buffer.Length = Argument->DataLength;
367             break;
368         }
369 
370         case ACPI_METHOD_ARGUMENT_PACKAGE:
371         {
372             ULONG i, PackageSize;
373             NTSTATUS Status;
374             PACPI_METHOD_ARGUMENT PackageArgument;
375 
376             Arg->Type = ACPI_TYPE_PACKAGE;
377 
378             Arg->Package.Count = EvalGetPackageCount(Argument,
379                                                      (PACPI_METHOD_ARGUMENT)Argument->Data,
380                                                      Argument->DataLength);
381             /* Empty package, nothing more to convert */
382             if (Arg->Package.Count == 0)
383             {
384                 Arg->Package.Elements = NULL;
385                 break;
386             }
387 
388             Status = RtlULongMult(Arg->Package.Count, sizeof(*Arg), &PackageSize);
389             if (!NT_SUCCESS(Status))
390             {
391                 DPRINT1("Invalid package count 0x%lx\n", Arg->Package.Count);
392                 return STATUS_ACPI_INCORRECT_ARGUMENT_COUNT;
393             }
394 
395             Arg->Package.Elements = ExAllocatePoolUninitialized(NonPagedPool,
396                                                                 PackageSize,
397                                                                 TAG_ACPI_PACKAGE_LIST);
398             if (!Arg->Package.Elements)
399                 return STATUS_INSUFFICIENT_RESOURCES;
400 
401             PackageArgument = (PACPI_METHOD_ARGUMENT)Argument->Data;
402             for (i = 0; i < Arg->Package.Count; i++)
403             {
404                 Status = EvalConvertParameterObjects(&Arg->Package.Elements[i],
405                                                      Depth + 1,
406                                                      PackageArgument,
407                                                      IoStack,
408                                                      Offset);
409                 if (!NT_SUCCESS(Status))
410                 {
411                     ExFreePoolWithTag(Arg->Package.Elements, TAG_ACPI_PACKAGE_LIST);
412                     return Status;
413                 }
414 
415                 PackageArgument = ACPI_METHOD_NEXT_ARGUMENT(PackageArgument);
416             }
417             break;
418         }
419 
420         default:
421         {
422             DPRINT1("Unknown argument type %u\n", Argument->Type);
423             return STATUS_UNSUCCESSFUL;
424         }
425     }
426 
427     return STATUS_SUCCESS;
428 }
429 
430 /**
431  * @brief Creates a counted array of ACPI_OBJECTs from the given input buffer.
432  */
433 static
434 CODE_SEG("PAGE")
435 NTSTATUS
EvalCreateParametersList(_In_ PIRP Irp,_In_ PIO_STACK_LOCATION IoStack,_In_ PACPI_EVAL_INPUT_BUFFER EvalInputBuffer,_Out_ ACPI_OBJECT_LIST * ParamList)436 EvalCreateParametersList(
437     _In_ PIRP Irp,
438     _In_ PIO_STACK_LOCATION IoStack,
439     _In_ PACPI_EVAL_INPUT_BUFFER EvalInputBuffer,
440     _Out_ ACPI_OBJECT_LIST* ParamList)
441 {
442     ACPI_OBJECT* Arg;
443 
444     PAGED_CODE();
445 
446     if (!AcpiVerifyInBuffer(IoStack, RTL_SIZEOF_THROUGH_FIELD(ACPI_EVAL_INPUT_BUFFER, Signature)))
447     {
448         DPRINT1("Buffer too small\n");
449         return STATUS_INFO_LENGTH_MISMATCH;
450     }
451 
452     switch (EvalInputBuffer->Signature)
453     {
454         case ACPI_EVAL_INPUT_BUFFER_SIGNATURE:
455         {
456             if (!AcpiVerifyInBuffer(IoStack, sizeof(*EvalInputBuffer)))
457             {
458                 DPRINT1("Buffer too small\n");
459                 return STATUS_INFO_LENGTH_MISMATCH;
460             }
461 
462             ParamList->Count = 0;
463             break;
464         }
465 
466         case ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_SIGNATURE:
467         {
468             PACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER SimpleInt;
469 
470             if (!AcpiVerifyInBuffer(IoStack, sizeof(*SimpleInt)))
471             {
472                 DPRINT1("Buffer too small\n");
473                 return STATUS_INFO_LENGTH_MISMATCH;
474             }
475 
476             Arg = ExAllocatePoolUninitialized(NonPagedPool, sizeof(*Arg), TAG_ACPI_PARAMETERS_LIST);
477             if (!Arg)
478                 return STATUS_INSUFFICIENT_RESOURCES;
479 
480             ParamList->Count = 1;
481             ParamList->Pointer = Arg;
482 
483             SimpleInt = Irp->AssociatedIrp.SystemBuffer;
484 
485             Arg->Type = ACPI_TYPE_INTEGER;
486             Arg->Integer.Value = (ULONG64)SimpleInt->IntegerArgument;
487             break;
488         }
489 
490         case ACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING_SIGNATURE:
491         {
492             PACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING SimpleStr;
493 
494             if (!AcpiVerifyInBuffer(IoStack, sizeof(*SimpleStr)))
495             {
496                 DPRINT1("Buffer too small\n");
497                 return STATUS_INFO_LENGTH_MISMATCH;
498             }
499 
500             Arg = ExAllocatePoolUninitialized(NonPagedPool, sizeof(*Arg), TAG_ACPI_PARAMETERS_LIST);
501             if (!Arg)
502                 return STATUS_INSUFFICIENT_RESOURCES;
503 
504             ParamList->Count = 1;
505             ParamList->Pointer = Arg;
506 
507             SimpleStr = Irp->AssociatedIrp.SystemBuffer;
508 
509             Arg->Type = ACPI_TYPE_STRING;
510             Arg->String.Pointer = (PCHAR)SimpleStr->String;
511             Arg->String.Length = SimpleStr->StringLength;
512             break;
513         }
514 
515         case ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE:
516         {
517             PACPI_EVAL_INPUT_BUFFER_COMPLEX ComplexBuffer;
518             PACPI_METHOD_ARGUMENT Argument;
519             ULONG i, Length, Offset, ArgumentsSize;
520             NTSTATUS Status;
521 
522             if (!AcpiVerifyInBuffer(IoStack, sizeof(*ComplexBuffer)))
523             {
524                 DPRINT1("Buffer too small\n");
525                 return STATUS_INFO_LENGTH_MISMATCH;
526             }
527 
528             ComplexBuffer = Irp->AssociatedIrp.SystemBuffer;
529 
530             ParamList->Count = ComplexBuffer->ArgumentCount;
531             if (ParamList->Count == 0)
532             {
533                 DPRINT1("No arguments\n");
534                 return STATUS_ACPI_INCORRECT_ARGUMENT_COUNT;
535             }
536 
537             Status = RtlULongMult(ParamList->Count, sizeof(*Arg), &ArgumentsSize);
538             if (!NT_SUCCESS(Status))
539             {
540                 DPRINT1("Invalid argument count 0x%lx\n", ParamList->Count);
541                 return STATUS_ACPI_INCORRECT_ARGUMENT_COUNT;
542             }
543 
544             Arg = ExAllocatePoolUninitialized(NonPagedPool,
545                                               ArgumentsSize,
546                                               TAG_ACPI_PARAMETERS_LIST);
547             if (!Arg)
548                 return STATUS_INSUFFICIENT_RESOURCES;
549 
550             ParamList->Pointer = Arg;
551 
552             Argument = ComplexBuffer->Argument;
553             Length = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument);
554 
555             for (i = 0; i < ParamList->Count; i++)
556             {
557                 Offset = Length;
558                 Length += ACPI_METHOD_ARGUMENT_LENGTH_FROM_ARGUMENT(Argument);
559 
560                 if (!AcpiVerifyInBuffer(IoStack, Length))
561                 {
562                     DPRINT1("Argument buffer outside of argument bounds\n");
563 
564                     ExFreePoolWithTag(ParamList->Pointer, TAG_ACPI_PARAMETERS_LIST);
565                     return STATUS_ACPI_INVALID_ARGTYPE;
566                 }
567 
568                 Status = EvalConvertParameterObjects(Arg, 0, Argument, IoStack, Offset);
569                 if (!NT_SUCCESS(Status))
570                 {
571                     ExFreePoolWithTag(ParamList->Pointer, TAG_ACPI_PARAMETERS_LIST);
572                     return Status;
573                 }
574 
575                 Arg++;
576                 Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument);
577             }
578 
579             break;
580         }
581 
582         default:
583         {
584             DPRINT1("Unsupported input buffer signature: 0x%lx\n", EvalInputBuffer->Signature);
585             return STATUS_INVALID_PARAMETER_1;
586         }
587     }
588 
589     return STATUS_SUCCESS;
590 }
591 
592 /**
593  * @brief Deallocates the memory for all sub-objects (elements) in a package.
594  */
595 static
596 CODE_SEG("PAGE")
597 VOID
EvalFreeParameterArgument(_In_ ACPI_OBJECT * Arg,_In_ ULONG Depth)598 EvalFreeParameterArgument(
599     _In_ ACPI_OBJECT* Arg,
600     _In_ ULONG Depth)
601 {
602     ULONG i;
603 
604     PAGED_CODE();
605 
606     if (Depth >= ACPI_MAX_PACKAGE_DEPTH)
607     {
608         ASSERT(FALSE);
609         return;
610     }
611 
612     if (Arg->Type == ACPI_TYPE_PACKAGE)
613     {
614         for (i = 0; i < Arg->Package.Count; i++)
615         {
616             EvalFreeParameterArgument(&Arg->Package.Elements[i], Depth + 1);
617         }
618 
619         /* Check if the package isn't empty, and free it */
620         if (Arg->Package.Elements)
621             ExFreePoolWithTag(Arg->Package.Elements, TAG_ACPI_PACKAGE_LIST);
622     }
623 }
624 
625 /**
626  * @brief Deallocates the given array of ACPI_OBJECTs.
627  */
628 static
629 CODE_SEG("PAGE")
630 VOID
EvalFreeParametersList(_In_ ACPI_OBJECT_LIST * ParamList)631 EvalFreeParametersList(
632     _In_ ACPI_OBJECT_LIST* ParamList)
633 {
634     ACPI_OBJECT* Arg;
635     ULONG i;
636 
637     PAGED_CODE();
638 
639     Arg = ParamList->Pointer;
640     for (i = 0; i < ParamList->Count; i++)
641     {
642         EvalFreeParameterArgument(Arg++, 0);
643     }
644 
645     ExFreePoolWithTag(ParamList->Pointer, TAG_ACPI_PARAMETERS_LIST);
646 }
647 
648 /**
649  * @brief Converts the provided value of ACPI_STATUS to NTSTATUS return value.
650  */
651 static
652 CODE_SEG("PAGE")
653 NTSTATUS
EvalAcpiStatusToNtStatus(_In_ ACPI_STATUS AcpiStatus)654 EvalAcpiStatusToNtStatus(
655     _In_ ACPI_STATUS AcpiStatus)
656 {
657     PAGED_CODE();
658 
659     if (ACPI_ENV_EXCEPTION(AcpiStatus))
660     {
661         switch (AcpiStatus)
662         {
663             case AE_NOT_FOUND:
664             case AE_NOT_EXIST:
665                 return STATUS_OBJECT_NAME_NOT_FOUND;
666 
667             case AE_NO_MEMORY:
668                 return STATUS_INSUFFICIENT_RESOURCES;
669 
670             case AE_SUPPORT:
671                 return STATUS_ACPI_INCORRECT_ARGUMENT_COUNT;
672 
673             case AE_TIME:
674                 return STATUS_IO_TIMEOUT;
675 
676             case AE_NO_HARDWARE_RESPONSE:
677                 return STATUS_IO_DEVICE_ERROR;
678 
679             case AE_STACK_OVERFLOW:
680                 return STATUS_ACPI_STACK_OVERFLOW;
681 
682             default:
683                 break;
684         }
685     }
686 
687     if (ACPI_AML_EXCEPTION(AcpiStatus))
688     {
689         switch (AcpiStatus)
690         {
691             case AE_AML_UNINITIALIZED_ARG:
692                 return STATUS_ACPI_INCORRECT_ARGUMENT_COUNT;
693 
694             default:
695                 break;
696         }
697     }
698 
699     return STATUS_UNSUCCESSFUL;
700 }
701 
702 /**
703  * @brief Evaluates an ACPI namespace object.
704  */
705 static
706 CODE_SEG("PAGE")
707 ACPI_STATUS
EvalEvaluateObject(_In_ PPDO_DEVICE_DATA DeviceData,_In_ PACPI_EVAL_INPUT_BUFFER EvalInputBuffer,_In_ ACPI_OBJECT_LIST * ParamList,_In_ ACPI_BUFFER * ReturnBuffer)708 EvalEvaluateObject(
709     _In_ PPDO_DEVICE_DATA DeviceData,
710     _In_ PACPI_EVAL_INPUT_BUFFER EvalInputBuffer,
711     _In_ ACPI_OBJECT_LIST* ParamList,
712     _In_ ACPI_BUFFER* ReturnBuffer)
713 {
714     CHAR MethodName[ACPI_OBJECT_NAME_LENGTH];
715 
716     PAGED_CODE();
717 
718     RtlCopyMemory(MethodName, EvalInputBuffer->MethodName, ACPI_OBJECT_NAME_LENGTH);
719     MethodName[ACPI_OBJECT_NAME_LENGTH - 1] = ANSI_NULL;
720 
721     return AcpiEvaluateObject(DeviceData->AcpiHandle, MethodName, ParamList, ReturnBuffer);
722 }
723 
724 /**
725  * @brief Writes the results from the evaluation into the output IRP buffer.
726  */
727 static
728 CODE_SEG("PAGE")
729 NTSTATUS
EvalCreateOutputArguments(_In_ PIRP Irp,_In_ PIO_STACK_LOCATION IoStack,_In_ ACPI_BUFFER * ReturnBuffer)730 EvalCreateOutputArguments(
731     _In_ PIRP Irp,
732     _In_ PIO_STACK_LOCATION IoStack,
733     _In_ ACPI_BUFFER* ReturnBuffer)
734 {
735     ACPI_OBJECT* Obj;
736     ULONG ExtraParamLength, OutputBufSize;
737     PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
738     NTSTATUS Status;
739     ULONG Count;
740 
741     PAGED_CODE();
742 
743     /* If we didn't get anything back then we're done */
744     if (!ReturnBuffer->Pointer || ReturnBuffer->Length == 0)
745         return STATUS_SUCCESS;
746 
747     /* No output buffer is provided, we're done */
748     if (IoStack->Parameters.DeviceIoControl.OutputBufferLength == 0)
749         return STATUS_SUCCESS;
750 
751     if (!AcpiVerifyOutBuffer(IoStack, sizeof(*OutputBuffer)))
752     {
753         DPRINT1("Buffer too small\n");
754 
755         return STATUS_BUFFER_TOO_SMALL;
756     }
757 
758     Obj = ReturnBuffer->Pointer;
759 
760     Status = EvalGetElementSize(Obj, 0, &Count, &ExtraParamLength);
761     if (!NT_SUCCESS(Status))
762         return Status;
763 
764     OutputBufSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + ExtraParamLength;
765 
766 #ifdef UNIT_TEST
767     OutputBuffer = Irp->OutputBuffer;
768 #else
769     OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
770 #endif
771     OutputBuffer->Signature = ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE;
772     OutputBuffer->Length = OutputBufSize;
773     OutputBuffer->Count = Count;
774 
775     if (!AcpiVerifyOutBuffer(IoStack, OutputBufSize))
776     {
777         DPRINT("Buffer too small (%lu/%lu)\n",
778                IoStack->Parameters.DeviceIoControl.OutputBufferLength,
779                OutputBufSize);
780 
781         Irp->IoStatus.Information = OutputBufSize;
782         return STATUS_BUFFER_OVERFLOW;
783     }
784 
785     Status = EvalConvertEvaluationResults(OutputBuffer->Argument, 0, Obj);
786     if (!NT_SUCCESS(Status))
787         return Status;
788 
789     Irp->IoStatus.Information = OutputBufSize;
790     return STATUS_SUCCESS;
791 }
792 
793 CODE_SEG("PAGE")
794 NTSTATUS
795 NTAPI
Bus_PDO_EvalMethod(_In_ PPDO_DEVICE_DATA DeviceData,_Inout_ PIRP Irp)796 Bus_PDO_EvalMethod(
797     _In_ PPDO_DEVICE_DATA DeviceData,
798     _Inout_ PIRP Irp)
799 {
800     PIO_STACK_LOCATION IoStack;
801     PACPI_EVAL_INPUT_BUFFER EvalInputBuffer;
802     ACPI_OBJECT_LIST ParamList;
803     ACPI_STATUS AcpiStatus;
804     NTSTATUS Status;
805     ACPI_BUFFER ReturnBuffer = { ACPI_ALLOCATE_BUFFER, NULL };
806 
807     PAGED_CODE();
808 
809     IoStack = IoGetCurrentIrpStackLocation(Irp);
810     EvalInputBuffer = Irp->AssociatedIrp.SystemBuffer;
811 
812     Status = EvalCreateParametersList(Irp, IoStack, EvalInputBuffer, &ParamList);
813     if (!NT_SUCCESS(Status))
814         return Status;
815 
816     AcpiStatus = EvalEvaluateObject(DeviceData, EvalInputBuffer, &ParamList, &ReturnBuffer);
817 
818     if (ParamList.Count != 0)
819         EvalFreeParametersList(&ParamList);
820 
821     if (!ACPI_SUCCESS(AcpiStatus))
822     {
823         DPRINT("Query method '%.4s' failed on %p with status 0x%04lx\n",
824                EvalInputBuffer->MethodName,
825                DeviceData->AcpiHandle,
826                AcpiStatus);
827 
828         return EvalAcpiStatusToNtStatus(AcpiStatus);
829     }
830 
831     Status = EvalCreateOutputArguments(Irp, IoStack, &ReturnBuffer);
832 
833     if (ReturnBuffer.Pointer)
834         AcpiOsFree(ReturnBuffer.Pointer);
835 
836     return Status;
837 }
838 
839 #ifndef UNIT_TEST
840 CODE_SEG("PAGE")
841 VOID
842 NTAPI
Bus_PDO_EvalMethodWorker(_In_ PVOID Parameter)843 Bus_PDO_EvalMethodWorker(
844     _In_ PVOID Parameter)
845 {
846     PEVAL_WORKITEM_DATA WorkItemData = Parameter;
847     NTSTATUS Status;
848     PIRP Irp;
849 
850     PAGED_CODE();
851 
852     Irp = WorkItemData->Irp;
853 
854     Status = Bus_PDO_EvalMethod(WorkItemData->DeviceData, Irp);
855 
856     ExFreePoolWithTag(WorkItemData, 'ipcA');
857 
858     Irp->IoStatus.Status = Status;
859     IoCompleteRequest(Irp, IO_NO_INCREMENT);
860 }
861 #endif /* UNIT_TEST */
862