summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlResourceDataParser.c
blob: 7f78ed51bf2ef2acdd1c2448b2af8b9facfdf5a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/** @file
  AML Resource Data Parser.

  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

  @par Glossary:
  - Rd or RD   - Resource Data
  - Rds or RDS - Resource Data Small
  - Rdl or RDL - Resource Data Large
**/

#include <Parser/AmlResourceDataParser.h>

#include <AmlCoreInterface.h>
#include <AmlDbgPrint/AmlDbgPrint.h>
#include <Tree/AmlNode.h>
#include <Tree/AmlTree.h>

/** Get the size of a resource data element using a stream.

  If the resource data element is of the large type, the Header
  is expected to be at least 3 bytes long.

  The use of a stream makes this function safer
  than the version without stream.

  @param  [in]  FStream     Forward stream pointing to a resource data
                            element.
                            The stream must not be at its end.

  @return The size of the resource data element.
          Zero if error.
**/
UINT32
EFIAPI
AmlRdStreamGetRdSize (
  IN  CONST AML_STREAM    * FStream
  )
{
  CONST AML_RD_HEADER   * CurrRdElement;

  if (!IS_STREAM (FStream)        ||
      IS_END_OF_STREAM (FStream)  ||
      !IS_STREAM_FORWARD (FStream)) {
    ASSERT (0);
    return 0;
  }

  CurrRdElement = (CONST AML_RD_HEADER*)AmlStreamGetCurrPos (FStream);
  if (CurrRdElement == NULL) {
    ASSERT (0);
    return 0;
  }

  // If the resource data element is of the large type, check for overflow.
  if (AML_RD_IS_LARGE (CurrRdElement) &&
      (AmlStreamGetFreeSpace (FStream) <
         sizeof (ACPI_LARGE_RESOURCE_HEADER))) {
    return 0;
  }

  return AmlRdGetSize (CurrRdElement);
}

/** Check the nesting of resource data elements that are dependent
    function descriptors.

  @param  [in]  FStream             Forward stream pointing to a resource data
                                    element. The stream is not
                                    modified/progressing.
                                    The stream must not be at its end.
  @param  [in, out] InFunctionDesc  Pointer holding the nesting of the
                                    resource data buffer.
                                    InFunctionDesc holds TRUE if the resource
                                    data at the address of Buffer is currently
                                    in a dependent function descriptor list.

  @retval FALSE   The Header being parsed is ending a function descriptor
                  list when none started. This should not be possible for a
                  resource data buffer.
  @retval TRUE    Otherwise.
**/
STATIC
BOOLEAN
EFIAPI
AmlRdCheckFunctionDescNesting (
  IN      CONST AML_STREAM    * FStream,
  IN  OUT       BOOLEAN       * InFunctionDesc
  )
{
  CONST AML_RD_HEADER   * CurrRdElement;

  if (!IS_STREAM (FStream)        ||
      IS_END_OF_STREAM (FStream)  ||
      (InFunctionDesc == NULL)) {
    ASSERT (0);
    return FALSE;
  }

  CurrRdElement = AmlStreamGetCurrPos (FStream);
  if (CurrRdElement == NULL) {
    ASSERT (0);
    return FALSE;
  }

  // Starting a dependent function descriptor.
  // It is possible to start one when one has already started.
  if (AmlRdCompareDescId (
        CurrRdElement,
        AML_RD_BUILD_SMALL_DESC_ID (
          ACPI_SMALL_START_DEPENDENT_DESCRIPTOR_NAME))) {
    *InFunctionDesc = TRUE;
    return TRUE;
  }

  // Ending a dependent function descriptor.
  if (AmlRdCompareDescId (
        CurrRdElement,
        AML_RD_BUILD_SMALL_DESC_ID (
          ACPI_SMALL_END_DEPENDENT_DESCRIPTOR_NAME))) {
    if (*InFunctionDesc) {
      *InFunctionDesc = FALSE;
      return TRUE;
    }

    // It should not be possible to end a dependent function descriptor
    // when none started.
    return FALSE;
  }

  return TRUE;
}

/** Check whether the input stream is pointing to a valid list
    of resource data elements.

  The check is based on the size of resource data elements.
  This means that a buffer can pass this check with non-existing descriptor Ids
  that have a correct size.

  A list of resource data elements can contain one unique resource data
  element, without an end tag resource data. This is the case for
  a FieldList.

  @param  [in]  FStream   Forward stream ideally pointing to a resource
                          data element. The stream is not
                          modified/progressing.
                          The stream must not be at its end.

  @retval TRUE    The buffer is holding a valid list of resource data elements.
  @retval FALSE   Otherwise.
**/
BOOLEAN
EFIAPI
AmlRdIsResourceDataBuffer (
  IN  CONST AML_STREAM    * FStream
  )
{
  EFI_STATUS              Status;
  UINT32                  FreeSpace;
  AML_STREAM              SubStream;
  CONST AML_RD_HEADER   * CurrRdElement;
  UINT32                  CurrRdElementSize;
  BOOLEAN                 InFunctionDesc;

  if (!IS_STREAM (FStream)        ||
      IS_END_OF_STREAM (FStream)  ||
      !IS_STREAM_FORWARD (FStream)) {
    ASSERT (0);
    return FALSE;
  }

  // Create a sub-stream from the input stream to leave it untouched.
  Status = AmlStreamInitSubStream (FStream, &SubStream);
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return FALSE;
  }

  CurrRdElement = AmlStreamGetCurrPos (&SubStream);
  if (CurrRdElement == NULL) {
    ASSERT (0);
    return FALSE;
  }

  // The first element cannot be an end tag.
  if (AmlRdCompareDescId (
       CurrRdElement,
       AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) {
    return FALSE;
  }

  InFunctionDesc = FALSE;
  while (TRUE) {
    FreeSpace = AmlStreamGetFreeSpace (&SubStream);
    CurrRdElement = AmlStreamGetCurrPos (&SubStream);
    CurrRdElementSize = AmlRdStreamGetRdSize (&SubStream);
    if ((FreeSpace == 0)          ||
        (CurrRdElement == NULL)   ||
        (CurrRdElementSize == 0)) {
      return FALSE;
    }

    if (!AmlRdCheckFunctionDescNesting (&SubStream, &InFunctionDesc)) {
      return FALSE;
    }

    if (CurrRdElementSize > FreeSpace) {
      return FALSE;
    } else if (CurrRdElementSize == FreeSpace) {
      return TRUE;
    }

    // @todo Might want to check the CRC when available.
    // An end tag resource data element must be the last element of the list.
    // Thus the function should have already returned.
    if (AmlRdCompareDescId (
          CurrRdElement,
          AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) {
      return FALSE;
    }

    Status = AmlStreamProgress (&SubStream, CurrRdElementSize);
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return FALSE;
    }
  } // while

  return FALSE;
}

/** Parse a ResourceDataBuffer.

  For each resource data element, create a data node
  and add them to the variable list of arguments of the BufferNode.

  The input stream is expected to point to a valid list of resource data
  elements. A function is available to check it for the caller.

  @param  [in]  BufferNode    Buffer node.
  @param  [in]  FStream       Forward stream pointing to a resource data
                              element.
                              The stream must not be at its end.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  @retval EFI_OUT_OF_RESOURCES    Could not allocate memory.
**/
EFI_STATUS
EFIAPI
AmlParseResourceData (
  IN  AML_OBJECT_NODE   * BufferNode,
  IN  AML_STREAM        * FStream
  )
{
  EFI_STATUS              Status;
  AML_DATA_NODE         * NewNode;
  UINT32                  FreeSpace;
  CONST AML_RD_HEADER   * CurrRdElement;
  UINT32                  CurrRdElementSize;

  // Check that BufferNode is an ObjectNode and has a ByteList.
  if (!AmlNodeHasAttribute (BufferNode, AML_HAS_BYTE_LIST)  ||
      !IS_STREAM (FStream)                                  ||
      IS_END_OF_STREAM (FStream)                            ||
      !IS_STREAM_FORWARD (FStream)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Iterate through the resource data elements and create nodes.
  // We assume the Buffer has already been validated as a list of
  // resource data elements, so less checks are made.
  while (TRUE) {
    FreeSpace = AmlStreamGetFreeSpace (FStream);
    if (FreeSpace == 0) {
      break;
    }

    CurrRdElement = (CONST AML_RD_HEADER*)AmlStreamGetCurrPos (FStream);
    CurrRdElementSize = AmlRdStreamGetRdSize (FStream);

    Status = AmlCreateDataNode (
               EAmlNodeDataTypeResourceData,
               (CONST UINT8*)CurrRdElement,
               CurrRdElementSize,
               &NewNode
               );
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }

    Status = AmlVarListAddTailInternal (
               (AML_NODE_HEADER*)BufferNode,
               (AML_NODE_HEADER*)NewNode
               );
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      AmlDeleteTree ((AML_NODE_HEADER*)NewNode);
      return Status;
    }

    Status = AmlStreamProgress (FStream, CurrRdElementSize);
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }

    AMLDBG_DUMP_RAW (CurrRdElement, CurrRdElementSize);

    // Exit the loop when finding the resource data end tag.
    if (AmlRdCompareDescId (
          CurrRdElement,
          AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) {
      if (FreeSpace != CurrRdElementSize) {
        ASSERT (0);
        return EFI_INVALID_PARAMETER;
      }
      break;
    }
  } // while

  return EFI_SUCCESS;
}