summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg/Library/Common/AmlLib/Parser/AmlFieldListParser.c
blob: c25ee22dc7c4b22b1448e11cf9bbd4e5e77c4316 (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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/** @file
  AML Field List Parser.

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

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

#include <Parser/AmlFieldListParser.h>

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

/** Parse a field element.

  The field elements this function can parse are one of:
   - ReservedField;
   - AccessField;
   - ConnectField;
   - ExtendedAccessField.
  Indeed, the NamedField field element doesn't have an OpCode. Thus it needs
  to be parsed differently.

  @param  [in]      FieldByteEncoding       Field byte encoding to parse.
  @param  [in, out] FieldNode               Field node to attach the field
                                            element to.
                                            Must have the AML_HAS_FIELD_LIST
                                            attribute.
  @param  [in, out] FStream                 Forward stream pointing to a field
                                            element not being a named field.
                                            The stream must not be at its end.
  @param  [in, out] NameSpaceRefList        List of namespace reference nodes,
                                            allowing to associate an absolute
                                            path to a node in the tree.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  @retval EFI_OUT_OF_RESOURCES    Could not allocate memory.
*/
STATIC
EFI_STATUS
EFIAPI
AmlParseFieldElement (
  IN      CONST AML_BYTE_ENCODING   * FieldByteEncoding,
  IN  OUT       AML_OBJECT_NODE     * FieldNode,
  IN  OUT       AML_STREAM          * FStream,
  IN  OUT       LIST_ENTRY          * NameSpaceRefList
  )
{
  EFI_STATUS          Status;

  UINT8             * CurrPos;
  AML_OBJECT_NODE   * NewNode;

  UINT32              PkgLenOffset;
  UINT32              PkgLenSize;

  // Check whether the node is an Object Node and has a field list.
  // The byte encoding must be a field element.
  if ((FieldByteEncoding == NULL)                                   ||
      ((FieldByteEncoding->Attribute & AML_IS_FIELD_ELEMENT) == 0)  ||
      ((FieldByteEncoding->Attribute & AML_IS_PSEUDO_OPCODE) ==
          AML_IS_PSEUDO_OPCODE)                                     ||
      !AmlNodeHasAttribute (FieldNode, AML_HAS_FIELD_LIST)          ||
      !IS_STREAM (FStream)                                          ||
      IS_END_OF_STREAM (FStream)                                    ||
      !IS_STREAM_FORWARD (FStream)                                  ||
      (NameSpaceRefList == NULL)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  CurrPos = AmlStreamGetCurrPos (FStream);
  if (CurrPos == NULL) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Skip the field opcode (1 byte) as it is already in the FieldByteEncoding.
  DumpRaw (CurrPos, 1);
  Status = AmlStreamProgress (FStream, 1);
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return Status;
  }

  CurrPos = AmlStreamGetCurrPos (FStream);
  if (CurrPos == NULL) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Parse the PkgLen if available.
  PkgLenSize = 0;
  if ((FieldByteEncoding->Attribute & AML_HAS_PKG_LENGTH) ==
        AML_HAS_PKG_LENGTH) {
    PkgLenOffset = AmlGetPkgLength (CurrPos, &PkgLenSize);
    if (PkgLenOffset == 0) {
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }

    // Move stream forward as the PkgLen has been read.
    DumpRaw (CurrPos, PkgLenOffset);
    Status = AmlStreamProgress (FStream, PkgLenOffset);
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }

    // Update the current position as PkgLen has been parsed.
    CurrPos = AmlStreamGetCurrPos (FStream);
  }

  Status = AmlCreateObjectNode (
             FieldByteEncoding,
             PkgLenSize,
             &NewNode
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return Status;
  }

  // Add the FieldElement to the Variable Argument List.
  Status = AmlVarListAddTailInternal (
             (AML_NODE_HEADER*)FieldNode,
             (AML_NODE_HEADER*)NewNode
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    // Delete the sub-tree if the insertion failed.
    // Otherwise its reference will be lost.
    AmlDeleteTree ((AML_NODE_HEADER*)NewNode);
    return Status;
  }

  // Some field elements do not have fixed arguments.
  if (!IS_END_OF_STREAM (FStream)) {
    // Parse the fixed arguments of the field element.
    Status = AmlParseFixedArguments (
              NewNode,
              FStream,
              NameSpaceRefList
              );
    if (EFI_ERROR (Status)) {
      ASSERT (0);
    }
  }

  return Status;
}

/** Parse a named field element.

  Indeed, the NamedField field element doesn't have an OpCode. Thus it needs
  to be parsed differently. NamedField field element start with a char.

  @param  [in]      NamedFieldByteEncoding  Field byte encoding to parse.
  @param  [in, out] FieldNode               Field node to attach the field
                                            element to.
                                            Must have the AML_HAS_FIELD_LIST
                                            attribute.
  @param  [in, out] FStream                 Forward stream pointing to a named
                                            field element.
                                            The stream must not be at its end.
  @param  [in, out] NameSpaceRefList        List of namespace reference nodes,
                                            allowing to associate an absolute
                                            path to a node in the tree.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  @retval EFI_OUT_OF_RESOURCES    Could not allocate memory.
*/
STATIC
EFI_STATUS
EFIAPI
AmlParseNamedFieldElement (
  IN      CONST AML_BYTE_ENCODING   * NamedFieldByteEncoding,
  IN  OUT       AML_OBJECT_NODE     * FieldNode,
  IN  OUT       AML_STREAM          * FStream,
  IN  OUT       LIST_ENTRY          * NameSpaceRefList
)
{
  EFI_STATUS          Status;
  AML_OBJECT_NODE   * NewNode;

  // Check whether the node is an Object Node and has a field list.
  // The byte encoding must be a char.
  if ((NamedFieldByteEncoding == NULL)                              ||
      ((NamedFieldByteEncoding->Attribute & AML_IS_NAME_CHAR) == 0) ||
      !AmlNodeHasAttribute (FieldNode, AML_HAS_FIELD_LIST)          ||
      !IS_STREAM (FStream)                                          ||
      IS_END_OF_STREAM (FStream)                                    ||
      !IS_STREAM_FORWARD (FStream)                                  ||
      (NameSpaceRefList == NULL)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Create a NamedField node.
  Status = AmlCreateObjectNode (
             AmlGetFieldEncodingByOpCode (AML_FIELD_NAMED_OP, 0),
             0,
             &NewNode
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return Status;
  }

  // Add the NamedField node to the variable argument list.
  Status = AmlVarListAddTailInternal (
             (AML_NODE_HEADER*)FieldNode,
             (AML_NODE_HEADER*)NewNode
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    // Delete the sub-tree if the insertion failed.
    // Otherwise its reference will be lost.
    AmlDeleteTree ((AML_NODE_HEADER*)NewNode);
    return Status;
  }

  // Parse the fixed arguments: [0]NameSeg, [1]PkgLen.
  Status = AmlParseFixedArguments (
             NewNode,
             FStream,
             NameSpaceRefList
             );
  if (EFI_ERROR (Status)) {
    ASSERT (0);
    return Status;
  }

  // Add the NamedField to the namespace reference list.
  Status = AmlAddNameSpaceReference (
             NewNode,
             NameSpaceRefList
             );
  ASSERT_EFI_ERROR (Status);

  return Status;
}

/** Parse the FieldList contained in the stream.

  Create an object node for each field element parsed in the field list
  available in the Stream, and add them to the variable list of arguments
  of the FieldNode.

  Nodes that can have a field list are referred as field nodes. They have the
  AML_HAS_FIELD_LIST attribute.

  According to the ACPI 6.3 specification, s20.2.5.2 "Named Objects Encoding",
  field elements can be:
   - NamedField           := NameSeg PkgLength;
   - ReservedField        := 0x00 PkgLength;
   - AccessField          := 0x01 AccessType AccessAttrib;
   - ConnectField         := <0x02 NameString> | <0x02 BufferData>;
   - ExtendedAccessField  := 0x03 AccessType ExtendedAccessAttrib AccessLength.

  A small set of opcodes describes the field elements. They are referred as
  field opcodes. An AML_BYTE_ENCODING table has been created for field OpCodes.
  Field elements:
   - don't have a SubOpCode;
   - have at most 3 fixed arguments (as opposed to 6 for standard AML objects);
   - don't have a variable list of arguments;
   - only the NamedField field element is part of the AML namespace.

  ConnectField's BufferData is a buffer node containing a single
  resource data element.
  NamedField field elements don't have an AML OpCode. NameSeg starts with a
  Char type and can thus be differentiated from the Opcodes for other fields.
  A pseudo OpCode has been created to simplify the parser.

  The branch created from parsing a field node is as:
  (FieldNode)
      \
       |- [FixedArg[0]][FixedArg[1]]                      # Fixed Arguments
       |- {(FieldElement[0])->(FieldElement[1])->...)}    # Variable Arguments

  With FieldElement[n] being one of NamedField, ReservedField, AccessField,
  ConnectField, ExtendedAccessField.

  @param  [in]  FieldNode         Field node.
                                  Must have the AML_HAS_FIELD_LIST
                                  attribute.
  @param  [in]  FStream           Forward stream pointing to a field list.
                                  The stream must not be at its end.
  @param  [in]  NameSpaceRefList  List of namespace reference nodes,
                                  allowing to associate an absolute
                                  path to a node in the tree.

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
  @retval EFI_OUT_OF_RESOURCES    Could not allocate memory.
**/
EFI_STATUS
EFIAPI
AmlParseFieldList (
  IN  AML_OBJECT_NODE   * FieldNode,
  IN  AML_STREAM        * FStream,
  IN  LIST_ENTRY        * NameSpaceRefList
  )
{
  EFI_STATUS                  Status;

  UINT8                     * CurrPos;
  CONST AML_BYTE_ENCODING   * FieldByteEncoding;
  CONST AML_BYTE_ENCODING   * NamedFieldByteEncoding;

  // Check whether the node is an Object Node and has a field list.
  if (!AmlNodeHasAttribute (FieldNode, AML_HAS_FIELD_LIST)  ||
      !IS_STREAM (FStream)                                  ||
      IS_END_OF_STREAM (FStream)                            ||
      !IS_STREAM_FORWARD (FStream)                          ||
      (NameSpaceRefList == NULL)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Iterate through the field elements, creating nodes
  // and adding them to the variable list of elements of Node.
  while (!IS_END_OF_STREAM (FStream)) {
    CurrPos = AmlStreamGetCurrPos (FStream);

    // Check for a field opcode.
    FieldByteEncoding = AmlGetFieldEncoding (CurrPos);
    if (FieldByteEncoding != NULL) {
      Status = AmlParseFieldElement (
                 FieldByteEncoding,
                 FieldNode,
                 FStream,
                 NameSpaceRefList
                 );
      if (EFI_ERROR (Status)) {
        ASSERT (0);
        return Status;
      }
    } else {
      // Handle the case of Pseudo OpCodes.
      // NamedField has a Pseudo OpCode and starts with a NameChar. Therefore,
      // call AmlGetByteEncoding() to check that the encoding is NameChar.
      NamedFieldByteEncoding = AmlGetByteEncoding (CurrPos);
      if ((NamedFieldByteEncoding != NULL) &&
          (NamedFieldByteEncoding->Attribute & AML_IS_NAME_CHAR)) {
        // This is a NamedField field element since it is starting with a char.
        Status = AmlParseNamedFieldElement (
                   NamedFieldByteEncoding,
                   FieldNode,
                   FStream,
                   NameSpaceRefList
                   );
        if (EFI_ERROR (Status)) {
          ASSERT (0);
          return Status;
        }
      } else {
        // A field opcode or an AML byte encoding is expected.
        ASSERT (0);
        return EFI_INVALID_PARAMETER;
      }
    }
  } // while

  return EFI_SUCCESS;
}