xref: /reactos/drivers/bus/acpi/eval.c (revision 9393fc32)
1 #include "precomp.h"
2 
3 #define NDEBUG
4 #include <debug.h>
5 
6 static
7 NTSTATUS
8 GetPackageSize(ACPI_OBJECT *Package,
9                PULONG Count,
10                PULONG Size)
11 {
12     ULONG Length, RawLength, TotalLength;
13     UINT32 i;
14 
15     TotalLength = 0;
16     for (i = 0; i < Package->Package.Count; i++)
17     {
18         switch (Package->Package.Elements[i].Type)
19         {
20             case ACPI_TYPE_INTEGER:
21                 Length = sizeof(ACPI_METHOD_ARGUMENT);
22                 DPRINT("Integer %lu -> %lu: %lu\n", sizeof(ULONG), Length, Package->Package.Elements[i].Integer.Value);
23                 TotalLength += Length;
24                 break;
25 
26             case ACPI_TYPE_STRING:
27                 RawLength = Package->Package.Elements[i].String.Length + 1;
28                 Length = sizeof(ACPI_METHOD_ARGUMENT);
29                 if (RawLength > sizeof(ULONG))
30                     Length += RawLength - sizeof(ULONG);
31                 DPRINT("String %lu -> %lu: '%s'\n", RawLength, Length, Package->Package.Elements[i].String.Pointer);
32                 TotalLength += Length;
33                 break;
34 
35             default:
36                 DPRINT1("Unsupported element type %lu\n", Package->Package.Elements[i].Type);
37                 return STATUS_UNSUCCESSFUL;
38         }
39     }
40 
41     *Count = Package->Package.Count;
42     *Size = TotalLength;
43 
44     return STATUS_SUCCESS;
45 }
46 
47 
48 static
49 NTSTATUS
50 ConvertPackageArguments(ACPI_METHOD_ARGUMENT *Argument,
51                         ACPI_OBJECT *Package)
52 {
53     ACPI_METHOD_ARGUMENT *Ptr;
54     UINT32 i;
55 
56     Ptr = Argument;
57     for (i = 0; i < Package->Package.Count; i++)
58     {
59         switch (Package->Package.Elements[i].Type)
60         {
61             case ACPI_TYPE_INTEGER:
62                 DPRINT("Integer %lu\n", sizeof(ACPI_METHOD_ARGUMENT));
63                 ACPI_METHOD_SET_ARGUMENT_INTEGER(Ptr, Package->Package.Elements[i].Integer.Value);
64                 break;
65 
66             case ACPI_TYPE_STRING:
67                 DPRINT("String %lu\n", Package->Package.Elements[i].String.Length);
68                 ACPI_METHOD_SET_ARGUMENT_STRING(Ptr, Package->Package.Elements[i].String.Pointer);
69                 break;
70 
71             default:
72                 DPRINT1("Unsupported element type %lu\n", Package->Package.Elements[i].Type);
73                 return STATUS_UNSUCCESSFUL;
74         }
75 
76         Ptr = ACPI_METHOD_NEXT_ARGUMENT(Ptr);
77     }
78 
79     return STATUS_SUCCESS;
80 }
81 
82 
83 NTSTATUS
84 NTAPI
85 Bus_PDO_EvalMethod(PPDO_DEVICE_DATA DeviceData,
86                    PIRP Irp)
87 {
88   ULONG Signature;
89   NTSTATUS Status;
90   ACPI_OBJECT_LIST ParamList;
91   PACPI_EVAL_INPUT_BUFFER EvalInputBuff = Irp->AssociatedIrp.SystemBuffer;
92   ACPI_BUFFER RetBuff = {ACPI_ALLOCATE_BUFFER, NULL};
93   PACPI_EVAL_OUTPUT_BUFFER OutputBuf;
94   PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
95   ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER *SimpleInt;
96   ACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING *SimpleStr;
97   CHAR MethodName[5];
98 
99   if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
100       return STATUS_INVALID_PARAMETER;
101 
102   Signature = *((PULONG)Irp->AssociatedIrp.SystemBuffer);
103 
104   switch (Signature)
105   {
106      case ACPI_EVAL_INPUT_BUFFER_SIGNATURE:
107         if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ACPI_EVAL_INPUT_BUFFER))
108             return STATUS_INVALID_PARAMETER;
109 
110         ParamList.Count = 0;
111         break;
112 
113      case ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_SIGNATURE:
114         SimpleInt = Irp->AssociatedIrp.SystemBuffer;
115 
116         if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER))
117             return STATUS_INVALID_PARAMETER;
118 
119         ParamList.Count = 1;
120 
121         ParamList.Pointer = ExAllocatePoolWithTag(NonPagedPool, sizeof(ACPI_OBJECT), 'OpcA');
122         if (!ParamList.Pointer) return STATUS_INSUFFICIENT_RESOURCES;
123 
124         ParamList.Pointer[0].Type = ACPI_TYPE_INTEGER;
125         ParamList.Pointer[0].Integer.Value = SimpleInt->IntegerArgument;
126         break;
127 
128      case ACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING_SIGNATURE:
129         SimpleStr = Irp->AssociatedIrp.SystemBuffer;
130 
131         if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ACPI_EVAL_INPUT_BUFFER_SIMPLE_STRING))
132             return STATUS_INVALID_PARAMETER;
133 
134         ParamList.Count = 1;
135 
136         ParamList.Pointer = ExAllocatePoolWithTag(NonPagedPool, sizeof(ACPI_OBJECT), 'OpcA');
137         if (!ParamList.Pointer) return STATUS_INSUFFICIENT_RESOURCES;
138 
139         ParamList.Pointer[0].String.Pointer = (CHAR*)SimpleStr->String;
140         ParamList.Pointer[0].String.Length = SimpleStr->StringLength;
141         break;
142 
143      default:
144         DPRINT1("Unsupported input buffer signature: %d\n", Signature);
145         return STATUS_NOT_IMPLEMENTED;
146   }
147 
148   RtlCopyMemory(MethodName,
149                 EvalInputBuff->MethodName,
150                 sizeof(EvalInputBuff->MethodName));
151   MethodName[4] = ANSI_NULL;
152   Status = AcpiEvaluateObject(DeviceData->AcpiHandle,
153                               MethodName,
154                               &ParamList,
155                               &RetBuff);
156 
157   if (ParamList.Count != 0)
158       ExFreePoolWithTag(ParamList.Pointer, 'OpcA');
159 
160   if (ACPI_SUCCESS(Status))
161   {
162       ACPI_OBJECT *Obj = RetBuff.Pointer;
163       ULONG ExtraParamLength = 0;
164       ULONG Count = 1;
165 
166       /* If we didn't get anything back then we're done */
167       if (!RetBuff.Pointer || RetBuff.Length == 0)
168           return STATUS_SUCCESS;
169 
170       switch (Obj->Type)
171       {
172           case ACPI_TYPE_INTEGER:
173              ExtraParamLength = sizeof(ACPI_METHOD_ARGUMENT);
174              break;
175 
176           case ACPI_TYPE_STRING:
177              ExtraParamLength = sizeof(ACPI_METHOD_ARGUMENT);
178              if (Obj->String.Length + 1 > sizeof(ULONG))
179                  ExtraParamLength += Obj->String.Length + 1 - sizeof(ULONG);
180              break;
181 
182           case ACPI_TYPE_BUFFER:
183              ExtraParamLength = sizeof(ACPI_METHOD_ARGUMENT);
184              if (Obj->Buffer.Length > sizeof(ULONG))
185                  ExtraParamLength += Obj->Buffer.Length + 1 - sizeof(ULONG);
186              break;
187 
188           case ACPI_TYPE_PACKAGE:
189              Status = GetPackageSize(Obj, &Count, &ExtraParamLength);
190              if (!NT_SUCCESS(Status))
191                  return Status;
192              break;
193 
194           default:
195              ASSERT(FALSE);
196              return STATUS_UNSUCCESSFUL;
197       }
198 
199       DPRINT("ExtraParamLength %lu\n", ExtraParamLength);
200       OutputBuf = ExAllocatePoolWithTag(NonPagedPool,
201                                         sizeof(ACPI_EVAL_OUTPUT_BUFFER) - sizeof(ACPI_METHOD_ARGUMENT) + ExtraParamLength,
202                                         'BpcA');
203       if (!OutputBuf)
204           return STATUS_INSUFFICIENT_RESOURCES;
205 
206       OutputBuf->Signature = ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE;
207       OutputBuf->Length = ExtraParamLength;
208       OutputBuf->Count = Count;
209 
210       switch (Obj->Type)
211       {
212           case ACPI_TYPE_INTEGER:
213              ACPI_METHOD_SET_ARGUMENT_INTEGER(OutputBuf->Argument, Obj->Integer.Value);
214              break;
215 
216           case ACPI_TYPE_STRING:
217              ACPI_METHOD_SET_ARGUMENT_STRING(OutputBuf->Argument, Obj->String.Pointer);
218              break;
219 
220           case ACPI_TYPE_BUFFER:
221              ACPI_METHOD_SET_ARGUMENT_BUFFER(OutputBuf->Argument, Obj->Buffer.Pointer, Obj->Buffer.Length);
222              break;
223 
224           case ACPI_TYPE_PACKAGE:
225              Status = ConvertPackageArguments(OutputBuf->Argument, Obj);
226              if (!NT_SUCCESS(Status))
227                  return Status;
228              break;
229 
230           default:
231              ASSERT(FALSE);
232              return STATUS_UNSUCCESSFUL;
233       }
234 
235       if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(ACPI_EVAL_OUTPUT_BUFFER) - sizeof(ACPI_METHOD_ARGUMENT) +
236                                                                   ExtraParamLength)
237       {
238           RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, OutputBuf, sizeof(ACPI_EVAL_OUTPUT_BUFFER) - sizeof(ACPI_METHOD_ARGUMENT) +
239                                                                     ExtraParamLength);
240           Irp->IoStatus.Information = sizeof(ACPI_EVAL_OUTPUT_BUFFER) - sizeof(ACPI_METHOD_ARGUMENT) + ExtraParamLength;
241           ExFreePoolWithTag(OutputBuf, 'BpcA');
242           return STATUS_SUCCESS;
243       }
244       else
245       {
246           ExFreePoolWithTag(OutputBuf, 'BpcA');
247           return STATUS_BUFFER_TOO_SMALL;
248       }
249   }
250   else
251   {
252       DPRINT1("Query method %4s failed on %p\n", EvalInputBuff->MethodName, DeviceData->AcpiHandle);
253       return STATUS_UNSUCCESSFUL;
254   }
255 }
256