xref: /openbmc/qemu/hw/acpi/aml-build.c (revision 661875e948ece4723ebe4e7628060c27cad06df1)
1 /* Support for generating ACPI tables and passing them to Guests
2  *
3  * Copyright (C) 2015 Red Hat Inc
4  *
5  * Author: Michael S. Tsirkin <mst@redhat.com>
6  * Author: Igor Mammedov <imammedo@redhat.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12 
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17 
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <assert.h>
25 #include <stdbool.h>
26 #include <string.h>
27 #include "hw/acpi/aml-build.h"
28 
29 GArray *build_alloc_array(void)
30 {
31     return g_array_new(false, true /* clear */, 1);
32 }
33 
34 void build_free_array(GArray *array)
35 {
36     g_array_free(array, true);
37 }
38 
39 void build_prepend_byte(GArray *array, uint8_t val)
40 {
41     g_array_prepend_val(array, val);
42 }
43 
44 void build_append_byte(GArray *array, uint8_t val)
45 {
46     g_array_append_val(array, val);
47 }
48 
49 void build_append_array(GArray *array, GArray *val)
50 {
51     g_array_append_vals(array, val->data, val->len);
52 }
53 
54 #define ACPI_NAMESEG_LEN 4
55 
56 static void
57 build_append_nameseg(GArray *array, const char *seg)
58 {
59     /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */
60     int len;
61 
62     len = strlen(seg);
63     assert(len <= ACPI_NAMESEG_LEN);
64 
65     g_array_append_vals(array, seg, len);
66     /* Pad up to ACPI_NAMESEG_LEN characters if necessary. */
67     g_array_append_vals(array, "____", ACPI_NAMESEG_LEN - len);
68 }
69 
70 static void
71 build_append_namestringv(GArray *array, const char *format, va_list ap)
72 {
73     /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */
74     char *s;
75     int len;
76     va_list va_len;
77     char **segs;
78     char **segs_iter;
79     int seg_count = 0;
80 
81     va_copy(va_len, ap);
82     len = vsnprintf(NULL, 0, format, va_len);
83     va_end(va_len);
84     len += 1;
85     s = g_new(typeof(*s), len);
86 
87     len = vsnprintf(s, len, format, ap);
88 
89     segs = g_strsplit(s, ".", 0);
90     g_free(s);
91 
92     /* count segments */
93     segs_iter = segs;
94     while (*segs_iter) {
95         ++segs_iter;
96         ++seg_count;
97     }
98     /*
99      * ACPI 5.0 spec: 20.2.2 Name Objects Encoding:
100      * "SegCount can be from 1 to 255"
101      */
102     assert(seg_count > 0 && seg_count <= 255);
103 
104     /* handle RootPath || PrefixPath */
105     s = *segs;
106     while (*s == '\\' || *s == '^') {
107         build_append_byte(array, *s);
108         ++s;
109     }
110 
111     switch (seg_count) {
112     case 1:
113         if (!*s) {
114             build_append_byte(array, 0x0); /* NullName */
115         } else {
116             build_append_nameseg(array, s);
117         }
118         break;
119 
120     case 2:
121         build_append_byte(array, 0x2E); /* DualNamePrefix */
122         build_append_nameseg(array, s);
123         build_append_nameseg(array, segs[1]);
124         break;
125     default:
126         build_append_byte(array, 0x2F); /* MultiNamePrefix */
127         build_append_byte(array, seg_count);
128 
129         /* handle the 1st segment manually due to prefix/root path */
130         build_append_nameseg(array, s);
131 
132         /* add the rest of segments */
133         segs_iter = segs + 1;
134         while (*segs_iter) {
135             build_append_nameseg(array, *segs_iter);
136             ++segs_iter;
137         }
138         break;
139     }
140     g_strfreev(segs);
141 }
142 
143 void build_append_namestring(GArray *array, const char *format, ...)
144 {
145     va_list ap;
146 
147     va_start(ap, format);
148     build_append_namestringv(array, format, ap);
149     va_end(ap);
150 }
151 
152 /* 5.4 Definition Block Encoding */
153 enum {
154     PACKAGE_LENGTH_1BYTE_SHIFT = 6, /* Up to 63 - use extra 2 bits. */
155     PACKAGE_LENGTH_2BYTE_SHIFT = 4,
156     PACKAGE_LENGTH_3BYTE_SHIFT = 12,
157     PACKAGE_LENGTH_4BYTE_SHIFT = 20,
158 };
159 
160 void build_prepend_package_length(GArray *package)
161 {
162     uint8_t byte;
163     unsigned length = package->len;
164     unsigned length_bytes;
165 
166     if (length + 1 < (1 << PACKAGE_LENGTH_1BYTE_SHIFT)) {
167         length_bytes = 1;
168     } else if (length + 2 < (1 << PACKAGE_LENGTH_3BYTE_SHIFT)) {
169         length_bytes = 2;
170     } else if (length + 3 < (1 << PACKAGE_LENGTH_4BYTE_SHIFT)) {
171         length_bytes = 3;
172     } else {
173         length_bytes = 4;
174     }
175 
176     /* PkgLength is the length of the inclusive length of the data. */
177     length += length_bytes;
178 
179     switch (length_bytes) {
180     case 1:
181         byte = length;
182         build_prepend_byte(package, byte);
183         return;
184     case 4:
185         byte = length >> PACKAGE_LENGTH_4BYTE_SHIFT;
186         build_prepend_byte(package, byte);
187         length &= (1 << PACKAGE_LENGTH_4BYTE_SHIFT) - 1;
188         /* fall through */
189     case 3:
190         byte = length >> PACKAGE_LENGTH_3BYTE_SHIFT;
191         build_prepend_byte(package, byte);
192         length &= (1 << PACKAGE_LENGTH_3BYTE_SHIFT) - 1;
193         /* fall through */
194     case 2:
195         byte = length >> PACKAGE_LENGTH_2BYTE_SHIFT;
196         build_prepend_byte(package, byte);
197         length &= (1 << PACKAGE_LENGTH_2BYTE_SHIFT) - 1;
198         /* fall through */
199     }
200     /*
201      * Most significant two bits of byte zero indicate how many following bytes
202      * are in PkgLength encoding.
203      */
204     byte = ((length_bytes - 1) << PACKAGE_LENGTH_1BYTE_SHIFT) | length;
205     build_prepend_byte(package, byte);
206 }
207 
208 void build_package(GArray *package, uint8_t op)
209 {
210     build_prepend_package_length(package);
211     build_prepend_byte(package, op);
212 }
213 
214 void build_extop_package(GArray *package, uint8_t op)
215 {
216     build_package(package, op);
217     build_prepend_byte(package, 0x5B); /* ExtOpPrefix */
218 }
219 
220 void build_append_value(GArray *table, uint32_t value, int size)
221 {
222     uint8_t prefix;
223     int i;
224 
225     switch (size) {
226     case 1:
227         prefix = 0x0A; /* BytePrefix */
228         break;
229     case 2:
230         prefix = 0x0B; /* WordPrefix */
231         break;
232     case 4:
233         prefix = 0x0C; /* DWordPrefix */
234         break;
235     default:
236         assert(0);
237         return;
238     }
239     build_append_byte(table, prefix);
240     for (i = 0; i < size; ++i) {
241         build_append_byte(table, value & 0xFF);
242         value = value >> 8;
243     }
244 }
245 
246 void build_append_int(GArray *table, uint32_t value)
247 {
248     if (value == 0x00) {
249         build_append_byte(table, 0x00); /* ZeroOp */
250     } else if (value == 0x01) {
251         build_append_byte(table, 0x01); /* OneOp */
252     } else if (value <= 0xFF) {
253         build_append_value(table, value, 1);
254     } else if (value <= 0xFFFF) {
255         build_append_value(table, value, 2);
256     } else {
257         build_append_value(table, value, 4);
258     }
259 }
260