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