xref: /netbsd-src/sys/external/bsd/acpica/dist/compiler/aslcodegen.c (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1 /******************************************************************************
2  *
3  * Module Name: aslcodegen - AML code generation
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2019, Intel Corp.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions, and the following disclaimer,
16  *    without modification.
17  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18  *    substantially similar to the "NO WARRANTY" disclaimer below
19  *    ("Disclaimer") and any redistribution must be conditioned upon
20  *    including a substantially similar Disclaimer requirement for further
21  *    binary redistribution.
22  * 3. Neither the names of the above-listed copyright holders nor the names
23  *    of any contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * Alternatively, this software may be distributed under the terms of the
27  * GNU General Public License ("GPL") version 2 as published by the Free
28  * Software Foundation.
29  *
30  * NO WARRANTY
31  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41  * POSSIBILITY OF SUCH DAMAGES.
42  */
43 
44 #include "aslcompiler.h"
45 #include "aslcompiler.y.h"
46 #include "amlcode.h"
47 #include "acconvert.h"
48 
49 #define _COMPONENT          ACPI_COMPILER
50         ACPI_MODULE_NAME    ("aslcodegen")
51 
52 /* Local prototypes */
53 
54 static ACPI_STATUS
55 CgAmlWriteWalk (
56     ACPI_PARSE_OBJECT       *Op,
57     UINT32                  Level,
58     void                    *Context);
59 
60 static void
61 CgWriteAmlOpcode (
62     ACPI_PARSE_OBJECT       *Op);
63 
64 static void
65 CgWriteTableHeader (
66     ACPI_PARSE_OBJECT       *Op);
67 
68 static void
69 CgWriteNode (
70     ACPI_PARSE_OBJECT       *Op);
71 
72 static void
73 CgUpdateHeader (
74     ACPI_PARSE_OBJECT       *Op);
75 
76 
77 /*******************************************************************************
78  *
79  * FUNCTION:    CgGenerateAmlOutput
80  *
81  * PARAMETERS:  None.
82  *
83  * RETURN:      None
84  *
85  * DESCRIPTION: Generate AML code. Currently generates the listing file
86  *              simultaneously.
87  *
88  ******************************************************************************/
89 
90 void
91 CgGenerateAmlOutput (
92     void)
93 {
94 
95     /* Generate the AML output file */
96 
97     TrWalkParseTree (AslGbl_CurrentDB,
98         ASL_WALK_VISIT_DOWNWARD | ASL_WALK_VISIT_DB_SEPARATELY,
99         CgAmlWriteWalk, NULL, NULL);
100 
101     DbgPrint (ASL_TREE_OUTPUT, ASL_PARSE_TREE_HEADER2);
102     CgUpdateHeader (AslGbl_CurrentDB);
103 }
104 
105 
106 /*******************************************************************************
107  *
108  * FUNCTION:    CgAmlWriteWalk
109  *
110  * PARAMETERS:  ASL_WALK_CALLBACK
111  *
112  * RETURN:      Status
113  *
114  * DESCRIPTION: Parse tree walk to generate the AML code.
115  *
116  ******************************************************************************/
117 
118 static ACPI_STATUS
119 CgAmlWriteWalk (
120     ACPI_PARSE_OBJECT       *Op,
121     UINT32                  Level,
122     void                    *Context)
123 {
124 
125     /* Generate the AML for this node */
126 
127     CgWriteNode (Op);
128 
129     if (!AslGbl_DebugFlag)
130     {
131         return (AE_OK);
132     }
133 
134     /* Print header at level 0. Alignment assumes 32-bit pointers */
135 
136     if (!Level)
137     {
138         DbgPrint (ASL_TREE_OUTPUT,
139             "\nFinal parse tree used for AML output:\n");
140         DbgPrint (ASL_TREE_OUTPUT, ASL_PARSE_TREE_HEADER2);
141     }
142 
143     /* Dump ParseOp name and possible value */
144 
145     switch (Op->Asl.ParseOpcode)
146     {
147     case PARSEOP_NAMESEG:
148     case PARSEOP_NAMESTRING:
149     case PARSEOP_METHODCALL:
150     case PARSEOP_STRING_LITERAL:
151 
152         UtDumpStringOp (Op, Level);
153         break;
154 
155     default:
156 
157         UtDumpBasicOp (Op, Level);
158         break;
159     }
160 
161     DbgPrint (ASL_TREE_OUTPUT, ASL_PARSE_TREE_DEBUG2,
162         /* 1  */ (UINT32) Op->Asl.Value.Integer,
163         /* 2  */ Op->Asl.ParseOpcode,
164         /* 3  */ Op->Asl.AmlOpcode,
165         /* 4  */ Op->Asl.AmlOpcodeLength,
166         /* 5  */ Op->Asl.AmlPkgLenBytes,
167         /* 6  */ Op->Asl.AmlLength,
168         /* 7  */ Op->Asl.AmlSubtreeLength,
169         /* 8  */ Op->Asl.Parent ? Op->Asl.Parent->Asl.AmlSubtreeLength : 0,
170         /* 9  */ Op,
171         /* 10 */ Op->Asl.Parent,
172         /* 11 */ Op->Asl.Child,
173         /* 12 */ Op->Asl.Next,
174         /* 13 */ Op->Asl.CompileFlags,
175         /* 14 */ Op->Asl.AcpiBtype,
176         /* 15 */ Op->Asl.FinalAmlLength,
177         /* 16 */ Op->Asl.Column,
178         /* 17 */ Op->Asl.LineNumber,
179         /* 18 */ Op->Asl.EndLine,
180         /* 19 */ Op->Asl.LogicalLineNumber,
181         /* 20 */ Op->Asl.EndLogicalLine);
182 
183     TrPrintOpFlags (Op->Asl.CompileFlags, ASL_TREE_OUTPUT);
184     DbgPrint (ASL_TREE_OUTPUT, "\n");
185     return (AE_OK);
186 }
187 
188 
189 /*******************************************************************************
190  *
191  * FUNCTION:    CgLocalWriteAmlData
192  *
193  * PARAMETERS:  Op              - Current parse op
194  *              Buffer          - Buffer to write
195  *              Length          - Size of data in buffer
196  *
197  * RETURN:      None
198  *
199  * DESCRIPTION: Write a buffer of AML data to the AML output file.
200  *
201  ******************************************************************************/
202 
203 void
204 CgLocalWriteAmlData (
205     ACPI_PARSE_OBJECT       *Op,
206     void                    *Buffer,
207     UINT32                  Length)
208 {
209 
210     /* Write the raw data to the AML file */
211 
212     FlWriteFile (ASL_FILE_AML_OUTPUT, Buffer, Length);
213 
214     /* Update the final AML length for this node (used for listings) */
215 
216     if (Op)
217     {
218         Op->Asl.FinalAmlLength += Length;
219     }
220 }
221 
222 
223 /*******************************************************************************
224  *
225  * FUNCTION:    CgWriteAmlOpcode
226  *
227  * PARAMETERS:  Op            - Parse node with an AML opcode
228  *
229  * RETURN:      None.
230  *
231  * DESCRIPTION: Write the AML opcode corresponding to a parse node.
232  *
233  ******************************************************************************/
234 
235 static void
236 CgWriteAmlOpcode (
237     ACPI_PARSE_OBJECT       *Op)
238 {
239     UINT8                   PkgLenFirstByte;
240     UINT32                  i;
241     union {
242         UINT16                  Opcode;
243         UINT8                   OpcodeBytes[2];
244     } Aml;
245     union {
246         UINT32                  Len;
247         UINT8                   LenBytes[4];
248     } PkgLen;
249 
250 
251     /* We expect some DEFAULT_ARGs, just ignore them */
252 
253     if (Op->Asl.ParseOpcode == PARSEOP_DEFAULT_ARG)
254     {
255         return;
256     }
257 
258     /*
259      * Before printing the bytecode, generate comment byte codes
260      * associated with this node.
261      */
262     if (AcpiGbl_CaptureComments)
263     {
264         CgWriteAmlComment(Op);
265     }
266 
267     switch (Op->Asl.AmlOpcode)
268     {
269     case AML_UNASSIGNED_OPCODE:
270 
271         /* These opcodes should not get here */
272 
273         printf ("Found a node with an unassigned AML opcode\n");
274         FlPrintFile (ASL_FILE_STDERR,
275             "Found a node with an unassigned AML opcode\n");
276         return;
277 
278     case AML_INT_RESERVEDFIELD_OP:
279 
280         /* Special opcodes for within a field definition */
281 
282         Aml.Opcode = AML_FIELD_OFFSET_OP;
283         break;
284 
285     case AML_INT_ACCESSFIELD_OP:
286 
287         Aml.Opcode = AML_FIELD_ACCESS_OP;
288         break;
289 
290     case AML_INT_CONNECTION_OP:
291 
292         Aml.Opcode = AML_FIELD_CONNECTION_OP;
293         break;
294 
295     default:
296 
297         Aml.Opcode = Op->Asl.AmlOpcode;
298         break;
299     }
300 
301 
302     switch (Aml.Opcode)
303     {
304     case AML_PACKAGE_LENGTH:
305 
306         /* Value is the length to be encoded (Used in field definitions) */
307 
308         PkgLen.Len = (UINT32) Op->Asl.Value.Integer;
309         break;
310 
311     default:
312 
313         /* Check for two-byte opcode */
314 
315         if (Aml.Opcode > 0x00FF)
316         {
317             /* Write the high byte first */
318 
319             CgLocalWriteAmlData (Op, &Aml.OpcodeBytes[1], 1);
320         }
321 
322         CgLocalWriteAmlData (Op, &Aml.OpcodeBytes[0], 1);
323 
324         /* Subtreelength doesn't include length of package length bytes */
325 
326         PkgLen.Len = Op->Asl.AmlSubtreeLength + Op->Asl.AmlPkgLenBytes;
327         break;
328     }
329 
330     /* Does this opcode have an associated "PackageLength" field? */
331 
332     if (Op->Asl.CompileFlags & OP_AML_PACKAGE)
333     {
334         if (Op->Asl.AmlPkgLenBytes == 1)
335         {
336             /* Simplest case -- no bytes to follow, just write the count */
337 
338             CgLocalWriteAmlData (Op, &PkgLen.LenBytes[0], 1);
339         }
340         else if (Op->Asl.AmlPkgLenBytes != 0)
341         {
342             /*
343              * Encode the "bytes to follow" in the first byte, top two bits.
344              * The low-order nybble of the length is in the bottom 4 bits
345              */
346             PkgLenFirstByte = (UINT8)
347                 (((UINT32) (Op->Asl.AmlPkgLenBytes - 1) << 6) |
348                 (PkgLen.LenBytes[0] & 0x0F));
349 
350             CgLocalWriteAmlData (Op, &PkgLenFirstByte, 1);
351 
352             /*
353              * Shift the length over by the 4 bits we just stuffed
354              * in the first byte
355              */
356             PkgLen.Len >>= 4;
357 
358             /*
359              * Now we can write the remaining bytes -
360              * either 1, 2, or 3 bytes
361              */
362             for (i = 0; i < (UINT32) (Op->Asl.AmlPkgLenBytes - 1); i++)
363             {
364                 CgLocalWriteAmlData (Op, &PkgLen.LenBytes[i], 1);
365             }
366         }
367     }
368 
369     switch (Aml.Opcode)
370     {
371     case AML_BYTE_OP:
372 
373         CgLocalWriteAmlData (Op, &Op->Asl.Value.Integer, 1);
374         break;
375 
376     case AML_WORD_OP:
377 
378         CgLocalWriteAmlData (Op, &Op->Asl.Value.Integer, 2);
379        break;
380 
381     case AML_DWORD_OP:
382 
383         CgLocalWriteAmlData (Op, &Op->Asl.Value.Integer, 4);
384         break;
385 
386     case AML_QWORD_OP:
387 
388         CgLocalWriteAmlData (Op, &Op->Asl.Value.Integer, 8);
389         break;
390 
391     case AML_STRING_OP:
392 
393         CgLocalWriteAmlData (Op, Op->Asl.Value.String, Op->Asl.AmlLength);
394         break;
395 
396     default:
397 
398         /* All data opcodes must appear above */
399 
400         break;
401     }
402 }
403 
404 
405 /*******************************************************************************
406  *
407  * FUNCTION:    CgWriteTableHeader
408  *
409  * PARAMETERS:  Op        - The DEFINITIONBLOCK node
410  *
411  * RETURN:      None
412  *
413  * DESCRIPTION: Write a table header corresponding to the DEFINITIONBLOCK
414  *
415  * NOTE: Input strings should be validated before this function is invoked.
416  *
417  ******************************************************************************/
418 
419 static void
420 CgWriteTableHeader (
421     ACPI_PARSE_OBJECT       *Op)
422 {
423     ACPI_PARSE_OBJECT       *Child;
424     UINT32                  CommentLength;
425     ACPI_COMMENT_NODE       *Current;
426 
427 
428     memset (&AslGbl_TableHeader, 0, sizeof (ACPI_TABLE_HEADER));
429 
430     /* AML filename */
431 
432     Child = Op->Asl.Child;
433 
434     /* Signature */
435 
436     Child = Child->Asl.Next;
437 
438     /*
439      * For ASL-/ASL+ converter: replace the table signature with
440      * "XXXX" and save the original table signature. This results in an AML
441      * file with the signature "XXXX". The converter should remove this AML
442      * file. In the event where this AML file does not get deleted, the
443      * "XXXX" table signature prevents this AML file from running on the AML
444      * interpreter.
445      */
446     if (AcpiGbl_CaptureComments)
447     {
448         ACPI_COPY_NAMESEG (AcpiGbl_TableSig, Child->Asl.Value.String);
449         Child->Asl.Value.String = ACPI_SIG_XXXX;
450     }
451 
452     ACPI_COPY_NAMESEG (AslGbl_TableHeader.Signature, Child->Asl.Value.String);
453 
454     /* Revision */
455 
456     Child = Child->Asl.Next;
457     AslGbl_TableHeader.Revision = (UINT8) Child->Asl.Value.Integer;
458 
459     /* Command-line Revision override */
460 
461     if (AslGbl_RevisionOverride)
462     {
463         AslGbl_TableHeader.Revision = AslGbl_RevisionOverride;
464     }
465 
466     /* OEMID */
467 
468     Child = Child->Asl.Next;
469     memcpy (AslGbl_TableHeader.OemId, Child->Asl.Value.String,
470         strlen (Child->Asl.Value.String));
471 
472     /* OEM TableID */
473 
474     Child = Child->Asl.Next;
475     memcpy (AslGbl_TableHeader.OemTableId, Child->Asl.Value.String,
476         strlen (Child->Asl.Value.String));
477 
478     /* OEM Revision */
479 
480     Child = Child->Asl.Next;
481     AslGbl_TableHeader.OemRevision = (UINT32) Child->Asl.Value.Integer;
482 
483     /* Compiler ID */
484 
485     ACPI_COPY_NAMESEG (AslGbl_TableHeader.AslCompilerId, ASL_CREATOR_ID);
486 
487     /* Compiler version */
488 
489     AslGbl_TableHeader.AslCompilerRevision = ACPI_CA_VERSION;
490 
491     /* Table length. Checksum zero for now, will rewrite later */
492 
493     AslGbl_TableHeader.Length = sizeof (ACPI_TABLE_HEADER) +
494         Op->Asl.AmlSubtreeLength;
495 
496     /* Calculate the comment lengths for this definition block parseOp */
497 
498     if (AcpiGbl_CaptureComments)
499     {
500         CvDbgPrint ("Calculating comment lengths for %s in write header\n",
501             Op->Asl.ParseOpName);
502 
503         /*
504          * Take the filename without extensions, add 3 for the new extension
505          * and another 3 for the a908 bytecode and null terminator.
506          */
507         AslGbl_TableHeader.Length += strrchr (AslGbl_ParseTreeRoot->Asl.Filename, '.')
508             - AslGbl_ParseTreeRoot->Asl.Filename + 1 + 3 + 3;
509 
510         Op->Asl.AmlSubtreeLength +=
511             strlen (AslGbl_ParseTreeRoot->Asl.Filename) + 3;
512 
513         CvDbgPrint ("     Length: %u\n",
514             (UINT32) strlen (AslGbl_ParseTreeRoot->Asl.Filename) + 3);
515 
516         if (Op->Asl.CommentList)
517         {
518             Current = Op->Asl.CommentList;
519             while (Current)
520             {
521                 CommentLength = strlen (Current->Comment)+3;
522                 CvDbgPrint ("Length of standard comment): %d\n", CommentLength);
523                 CvDbgPrint ("    Comment string: %s\n\n", Current->Comment);
524                 AslGbl_TableHeader.Length += CommentLength;
525                 Op->Asl.AmlSubtreeLength += CommentLength;
526                 Current = Current->Next;
527                 CvDbgPrint ("    Length: %u\n", CommentLength);
528             }
529         }
530         if (Op->Asl.CloseBraceComment)
531         {
532             CommentLength = strlen (Op->Asl.CloseBraceComment)+3;
533             CvDbgPrint ("Length of inline comment +3: %d\n", CommentLength);
534             CvDbgPrint ("    Comment string: %s\n\n", Op->Asl.CloseBraceComment);
535             AslGbl_TableHeader.Length += CommentLength;
536             Op->Asl.AmlSubtreeLength += CommentLength;
537             CvDbgPrint ("    Length: %u\n", CommentLength);
538         }
539     }
540 
541     AslGbl_TableHeader.Checksum = 0;
542     Op->Asl.FinalAmlOffset = ftell (AslGbl_Files[ASL_FILE_AML_OUTPUT].Handle);
543 
544     /* Write entire header and clear the table header global */
545 
546     CgLocalWriteAmlData (Op, &AslGbl_TableHeader, sizeof (ACPI_TABLE_HEADER));
547     memset (&AslGbl_TableHeader, 0, sizeof (ACPI_TABLE_HEADER));
548 }
549 
550 
551 /*******************************************************************************
552  *
553  * FUNCTION:    CgUpdateHeader
554  *
555  * PARAMETERS:  Op                  - Op for the Definition Block
556  *
557  * RETURN:      None.
558  *
559  * DESCRIPTION: Complete the ACPI table by calculating the checksum and
560  *              re-writing the header for the input definition block
561  *
562  ******************************************************************************/
563 
564 static void
565 CgUpdateHeader (
566     ACPI_PARSE_OBJECT       *Op)
567 {
568     signed char             Sum;
569     UINT32                  i;
570     UINT32                  Length;
571     UINT8                   FileByte;
572     UINT8                   Checksum;
573 
574 
575     /* Calculate the checksum over the entire definition block */
576 
577     Sum = 0;
578     Length = sizeof (ACPI_TABLE_HEADER) + Op->Asl.AmlSubtreeLength;
579     FlSeekFile (ASL_FILE_AML_OUTPUT, Op->Asl.FinalAmlOffset);
580 
581     for (i = 0; i < Length; i++)
582     {
583         if (FlReadFile (ASL_FILE_AML_OUTPUT, &FileByte, 1) != AE_OK)
584         {
585             AslError (ASL_ERROR, ASL_MSG_COMPILER_INTERNAL, NULL,
586                 "Table length is greater than size of the input file");
587             return;
588         }
589 
590         Sum = (signed char) (Sum + FileByte);
591     }
592 
593     Checksum = (UINT8) (0 - Sum);
594 
595     /* Re-write the the checksum byte */
596 
597     FlSeekFile (ASL_FILE_AML_OUTPUT, Op->Asl.FinalAmlOffset +
598         ACPI_OFFSET (ACPI_TABLE_HEADER, Checksum));
599 
600     FlWriteFile (ASL_FILE_AML_OUTPUT, &Checksum, 1);
601 
602     /*
603      * Seek to the end of the file. This is done to support multiple file
604      * compilation. Doing this simplifies other parts of the codebase because
605      * it eliminates the need to seek for a different starting place.
606      */
607     FlSeekFile (ASL_FILE_AML_OUTPUT, Op->Asl.FinalAmlOffset + Length);
608 }
609 
610 
611 /*******************************************************************************
612  *
613  * FUNCTION:    CgWriteNode
614  *
615  * PARAMETERS:  Op            - Parse node to write.
616  *
617  * RETURN:      None.
618  *
619  * DESCRIPTION: Write the AML that corresponds to a parse node.
620  *
621  ******************************************************************************/
622 
623 static void
624 CgWriteNode (
625     ACPI_PARSE_OBJECT       *Op)
626 {
627     ASL_RESOURCE_NODE       *Rnode;
628 
629 
630     /* Write all comments here. */
631 
632     if (AcpiGbl_CaptureComments)
633     {
634         CgWriteAmlComment(Op);
635     }
636 
637     /* Always check for DEFAULT_ARG and other "Noop" nodes */
638     /* TBD: this may not be the best place for this check */
639 
640     if ((Op->Asl.ParseOpcode == PARSEOP_DEFAULT_ARG)  ||
641         (Op->Asl.ParseOpcode == PARSEOP_INCLUDE)      ||
642         (Op->Asl.ParseOpcode == PARSEOP_INCLUDE_END))
643     {
644         return;
645     }
646 
647     Op->Asl.FinalAmlLength = 0;
648 
649     switch (Op->Asl.AmlOpcode)
650     {
651     case AML_RAW_DATA_BYTE:
652     case AML_RAW_DATA_WORD:
653     case AML_RAW_DATA_DWORD:
654     case AML_RAW_DATA_QWORD:
655 
656         CgLocalWriteAmlData (Op, &Op->Asl.Value.Integer, Op->Asl.AmlLength);
657         return;
658 
659 
660     case AML_RAW_DATA_BUFFER:
661 
662         CgLocalWriteAmlData (Op, Op->Asl.Value.Buffer, Op->Asl.AmlLength);
663         return;
664 
665 
666     case AML_RAW_DATA_CHAIN:
667 
668         Rnode = ACPI_CAST_PTR (ASL_RESOURCE_NODE, Op->Asl.Value.Buffer);
669         while (Rnode)
670         {
671             CgLocalWriteAmlData (Op, Rnode->Buffer, Rnode->BufferLength);
672             Rnode = Rnode->Next;
673         }
674         return;
675 
676     default:
677 
678         /* Internal data opcodes must all appear above */
679 
680         break;
681     }
682 
683     switch (Op->Asl.ParseOpcode)
684     {
685     case PARSEOP_DEFAULT_ARG:
686 
687         break;
688 
689     case PARSEOP_DEFINITION_BLOCK:
690 
691         CgWriteTableHeader (Op);
692         if (AcpiGbl_CaptureComments)
693         {
694             CgWriteAmlDefBlockComment (Op);
695         }
696         break;
697 
698     case PARSEOP_NAMESEG:
699     case PARSEOP_NAMESTRING:
700     case PARSEOP_METHODCALL:
701 
702         CgLocalWriteAmlData (Op, Op->Asl.Value.String, Op->Asl.AmlLength);
703         break;
704 
705     default:
706 
707         CgWriteAmlOpcode (Op);
708         break;
709     }
710 }
711