]>
Commit | Line | Data |
---|---|---|
95857638 | 1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
890b090e BM |
2 | /****************************************************************************** |
3 | * | |
4 | * Module Name: exconcat - Concatenate-type AML operators | |
5 | * | |
800ba7c5 | 6 | * Copyright (C) 2000 - 2020, Intel Corp. |
890b090e | 7 | * |
95857638 | 8 | *****************************************************************************/ |
890b090e BM |
9 | |
10 | #include <acpi/acpi.h> | |
11 | #include "accommon.h" | |
12 | #include "acinterp.h" | |
13 | #include "amlresrc.h" | |
14 | ||
15 | #define _COMPONENT ACPI_EXECUTER | |
16 | ACPI_MODULE_NAME("exconcat") | |
17 | ||
18 | /* Local Prototypes */ | |
19 | static acpi_status | |
20 | acpi_ex_convert_to_object_type_string(union acpi_operand_object *obj_desc, | |
21 | union acpi_operand_object **result_desc); | |
22 | ||
23 | /******************************************************************************* | |
24 | * | |
25 | * FUNCTION: acpi_ex_do_concatenate | |
26 | * | |
27 | * PARAMETERS: operand0 - First source object | |
28 | * operand1 - Second source object | |
29 | * actual_return_desc - Where to place the return object | |
30 | * walk_state - Current walk state | |
31 | * | |
32 | * RETURN: Status | |
33 | * | |
34 | * DESCRIPTION: Concatenate two objects with the ACPI-defined conversion | |
35 | * rules as necessary. | |
36 | * NOTE: | |
37 | * Per the ACPI spec (up to 6.1), Concatenate only supports Integer, | |
38 | * String, and Buffer objects. However, we support all objects here | |
39 | * as an extension. This improves the usefulness of both Concatenate | |
40 | * and the Printf/Fprintf macros. The extension returns a string | |
41 | * describing the object type for the other objects. | |
42 | * 02/2016. | |
43 | * | |
44 | ******************************************************************************/ | |
45 | ||
46 | acpi_status | |
47 | acpi_ex_do_concatenate(union acpi_operand_object *operand0, | |
48 | union acpi_operand_object *operand1, | |
49 | union acpi_operand_object **actual_return_desc, | |
50 | struct acpi_walk_state *walk_state) | |
51 | { | |
52 | union acpi_operand_object *local_operand0 = operand0; | |
53 | union acpi_operand_object *local_operand1 = operand1; | |
54 | union acpi_operand_object *temp_operand1 = NULL; | |
55 | union acpi_operand_object *return_desc; | |
56 | char *buffer; | |
57 | acpi_object_type operand0_type; | |
58 | acpi_object_type operand1_type; | |
59 | acpi_status status; | |
60 | ||
61 | ACPI_FUNCTION_TRACE(ex_do_concatenate); | |
62 | ||
63 | /* Operand 0 preprocessing */ | |
64 | ||
65 | switch (operand0->common.type) { | |
66 | case ACPI_TYPE_INTEGER: | |
67 | case ACPI_TYPE_STRING: | |
68 | case ACPI_TYPE_BUFFER: | |
69 | ||
70 | operand0_type = operand0->common.type; | |
71 | break; | |
72 | ||
73 | default: | |
74 | ||
75 | /* For all other types, get the "object type" string */ | |
76 | ||
77 | status = | |
78 | acpi_ex_convert_to_object_type_string(operand0, | |
79 | &local_operand0); | |
80 | if (ACPI_FAILURE(status)) { | |
81 | goto cleanup; | |
82 | } | |
83 | ||
84 | operand0_type = ACPI_TYPE_STRING; | |
85 | break; | |
86 | } | |
87 | ||
88 | /* Operand 1 preprocessing */ | |
89 | ||
90 | switch (operand1->common.type) { | |
91 | case ACPI_TYPE_INTEGER: | |
92 | case ACPI_TYPE_STRING: | |
93 | case ACPI_TYPE_BUFFER: | |
94 | ||
95 | operand1_type = operand1->common.type; | |
96 | break; | |
97 | ||
98 | default: | |
99 | ||
100 | /* For all other types, get the "object type" string */ | |
101 | ||
102 | status = | |
103 | acpi_ex_convert_to_object_type_string(operand1, | |
104 | &local_operand1); | |
105 | if (ACPI_FAILURE(status)) { | |
106 | goto cleanup; | |
107 | } | |
108 | ||
109 | operand1_type = ACPI_TYPE_STRING; | |
110 | break; | |
111 | } | |
112 | ||
113 | /* | |
114 | * Convert the second operand if necessary. The first operand (0) | |
115 | * determines the type of the second operand (1) (See the Data Types | |
116 | * section of the ACPI specification). Both object types are | |
117 | * guaranteed to be either Integer/String/Buffer by the operand | |
118 | * resolution mechanism. | |
119 | */ | |
120 | switch (operand0_type) { | |
121 | case ACPI_TYPE_INTEGER: | |
122 | ||
123 | status = | |
124 | acpi_ex_convert_to_integer(local_operand1, &temp_operand1, | |
fe97d287 | 125 | ACPI_IMPLICIT_CONVERSION); |
890b090e BM |
126 | break; |
127 | ||
128 | case ACPI_TYPE_BUFFER: | |
129 | ||
130 | status = | |
131 | acpi_ex_convert_to_buffer(local_operand1, &temp_operand1); | |
132 | break; | |
133 | ||
134 | case ACPI_TYPE_STRING: | |
135 | ||
136 | switch (operand1_type) { | |
137 | case ACPI_TYPE_INTEGER: | |
138 | case ACPI_TYPE_STRING: | |
139 | case ACPI_TYPE_BUFFER: | |
140 | ||
141 | /* Other types have already been converted to string */ | |
142 | ||
143 | status = | |
144 | acpi_ex_convert_to_string(local_operand1, | |
145 | &temp_operand1, | |
146 | ACPI_IMPLICIT_CONVERT_HEX); | |
147 | break; | |
148 | ||
149 | default: | |
150 | ||
151 | status = AE_OK; | |
152 | break; | |
153 | } | |
154 | break; | |
155 | ||
156 | default: | |
157 | ||
158 | ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", | |
159 | operand0->common.type)); | |
160 | status = AE_AML_INTERNAL; | |
161 | } | |
162 | ||
163 | if (ACPI_FAILURE(status)) { | |
164 | goto cleanup; | |
165 | } | |
166 | ||
167 | /* Take care with any newly created operand objects */ | |
168 | ||
169 | if ((local_operand1 != operand1) && (local_operand1 != temp_operand1)) { | |
170 | acpi_ut_remove_reference(local_operand1); | |
171 | } | |
172 | ||
173 | local_operand1 = temp_operand1; | |
174 | ||
175 | /* | |
176 | * Both operands are now known to be the same object type | |
177 | * (Both are Integer, String, or Buffer), and we can now perform | |
178 | * the concatenation. | |
179 | * | |
180 | * There are three cases to handle, as per the ACPI spec: | |
181 | * | |
182 | * 1) Two Integers concatenated to produce a new Buffer | |
183 | * 2) Two Strings concatenated to produce a new String | |
184 | * 3) Two Buffers concatenated to produce a new Buffer | |
185 | */ | |
186 | switch (operand0_type) { | |
187 | case ACPI_TYPE_INTEGER: | |
188 | ||
189 | /* Result of two Integers is a Buffer */ | |
190 | /* Need enough buffer space for two integers */ | |
191 | ||
192 | return_desc = acpi_ut_create_buffer_object((acpi_size) | |
193 | ACPI_MUL_2 | |
194 | (acpi_gbl_integer_byte_width)); | |
195 | if (!return_desc) { | |
196 | status = AE_NO_MEMORY; | |
197 | goto cleanup; | |
198 | } | |
199 | ||
200 | buffer = (char *)return_desc->buffer.pointer; | |
201 | ||
202 | /* Copy the first integer, LSB first */ | |
203 | ||
204 | memcpy(buffer, &operand0->integer.value, | |
205 | acpi_gbl_integer_byte_width); | |
206 | ||
207 | /* Copy the second integer (LSB first) after the first */ | |
208 | ||
209 | memcpy(buffer + acpi_gbl_integer_byte_width, | |
210 | &local_operand1->integer.value, | |
211 | acpi_gbl_integer_byte_width); | |
212 | break; | |
213 | ||
214 | case ACPI_TYPE_STRING: | |
215 | ||
216 | /* Result of two Strings is a String */ | |
217 | ||
218 | return_desc = acpi_ut_create_string_object(((acpi_size) | |
219 | local_operand0-> | |
220 | string.length + | |
221 | local_operand1-> | |
222 | string.length)); | |
223 | if (!return_desc) { | |
224 | status = AE_NO_MEMORY; | |
225 | goto cleanup; | |
226 | } | |
227 | ||
228 | buffer = return_desc->string.pointer; | |
229 | ||
230 | /* Concatenate the strings */ | |
231 | ||
232 | strcpy(buffer, local_operand0->string.pointer); | |
233 | strcat(buffer, local_operand1->string.pointer); | |
234 | break; | |
235 | ||
236 | case ACPI_TYPE_BUFFER: | |
237 | ||
238 | /* Result of two Buffers is a Buffer */ | |
239 | ||
240 | return_desc = acpi_ut_create_buffer_object(((acpi_size) | |
241 | operand0->buffer. | |
242 | length + | |
243 | local_operand1-> | |
244 | buffer.length)); | |
245 | if (!return_desc) { | |
246 | status = AE_NO_MEMORY; | |
247 | goto cleanup; | |
248 | } | |
249 | ||
250 | buffer = (char *)return_desc->buffer.pointer; | |
251 | ||
252 | /* Concatenate the buffers */ | |
253 | ||
254 | memcpy(buffer, operand0->buffer.pointer, | |
255 | operand0->buffer.length); | |
256 | memcpy(buffer + operand0->buffer.length, | |
257 | local_operand1->buffer.pointer, | |
258 | local_operand1->buffer.length); | |
259 | break; | |
260 | ||
261 | default: | |
262 | ||
263 | /* Invalid object type, should not happen here */ | |
264 | ||
265 | ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", | |
266 | operand0->common.type)); | |
267 | status = AE_AML_INTERNAL; | |
268 | goto cleanup; | |
269 | } | |
270 | ||
271 | *actual_return_desc = return_desc; | |
272 | ||
273 | cleanup: | |
274 | if (local_operand0 != operand0) { | |
275 | acpi_ut_remove_reference(local_operand0); | |
276 | } | |
277 | ||
278 | if (local_operand1 != operand1) { | |
279 | acpi_ut_remove_reference(local_operand1); | |
280 | } | |
281 | ||
282 | return_ACPI_STATUS(status); | |
283 | } | |
284 | ||
285 | /******************************************************************************* | |
286 | * | |
287 | * FUNCTION: acpi_ex_convert_to_object_type_string | |
288 | * | |
289 | * PARAMETERS: obj_desc - Object to be converted | |
290 | * return_desc - Where to place the return object | |
291 | * | |
292 | * RETURN: Status | |
293 | * | |
294 | * DESCRIPTION: Convert an object of arbitrary type to a string object that | |
295 | * contains the namestring for the object. Used for the | |
296 | * concatenate operator. | |
297 | * | |
298 | ******************************************************************************/ | |
299 | ||
300 | static acpi_status | |
301 | acpi_ex_convert_to_object_type_string(union acpi_operand_object *obj_desc, | |
302 | union acpi_operand_object **result_desc) | |
303 | { | |
304 | union acpi_operand_object *return_desc; | |
305 | const char *type_string; | |
306 | ||
307 | type_string = acpi_ut_get_type_name(obj_desc->common.type); | |
308 | ||
f5c1e1c5 | 309 | return_desc = acpi_ut_create_string_object(((acpi_size)strlen(type_string) + 9)); /* 9 For "[ Object]" */ |
890b090e BM |
310 | if (!return_desc) { |
311 | return (AE_NO_MEMORY); | |
312 | } | |
313 | ||
314 | strcpy(return_desc->string.pointer, "["); | |
315 | strcat(return_desc->string.pointer, type_string); | |
316 | strcat(return_desc->string.pointer, " Object]"); | |
317 | ||
318 | *result_desc = return_desc; | |
319 | return (AE_OK); | |
320 | } | |
321 | ||
322 | /******************************************************************************* | |
323 | * | |
324 | * FUNCTION: acpi_ex_concat_template | |
325 | * | |
326 | * PARAMETERS: operand0 - First source object | |
327 | * operand1 - Second source object | |
328 | * actual_return_desc - Where to place the return object | |
329 | * walk_state - Current walk state | |
330 | * | |
331 | * RETURN: Status | |
332 | * | |
333 | * DESCRIPTION: Concatenate two resource templates | |
334 | * | |
335 | ******************************************************************************/ | |
336 | ||
337 | acpi_status | |
338 | acpi_ex_concat_template(union acpi_operand_object *operand0, | |
339 | union acpi_operand_object *operand1, | |
340 | union acpi_operand_object **actual_return_desc, | |
f5c1e1c5 | 341 | struct acpi_walk_state *walk_state) |
890b090e BM |
342 | { |
343 | acpi_status status; | |
344 | union acpi_operand_object *return_desc; | |
345 | u8 *new_buf; | |
346 | u8 *end_tag; | |
347 | acpi_size length0; | |
348 | acpi_size length1; | |
349 | acpi_size new_length; | |
350 | ||
351 | ACPI_FUNCTION_TRACE(ex_concat_template); | |
352 | ||
353 | /* | |
354 | * Find the end_tag descriptor in each resource template. | |
355 | * Note1: returned pointers point TO the end_tag, not past it. | |
356 | * Note2: zero-length buffers are allowed; treated like one end_tag | |
357 | */ | |
358 | ||
359 | /* Get the length of the first resource template */ | |
360 | ||
361 | status = acpi_ut_get_resource_end_tag(operand0, &end_tag); | |
362 | if (ACPI_FAILURE(status)) { | |
363 | return_ACPI_STATUS(status); | |
364 | } | |
365 | ||
366 | length0 = ACPI_PTR_DIFF(end_tag, operand0->buffer.pointer); | |
367 | ||
368 | /* Get the length of the second resource template */ | |
369 | ||
370 | status = acpi_ut_get_resource_end_tag(operand1, &end_tag); | |
371 | if (ACPI_FAILURE(status)) { | |
372 | return_ACPI_STATUS(status); | |
373 | } | |
374 | ||
375 | length1 = ACPI_PTR_DIFF(end_tag, operand1->buffer.pointer); | |
376 | ||
377 | /* Combine both lengths, minimum size will be 2 for end_tag */ | |
378 | ||
379 | new_length = length0 + length1 + sizeof(struct aml_resource_end_tag); | |
380 | ||
381 | /* Create a new buffer object for the result (with one end_tag) */ | |
382 | ||
383 | return_desc = acpi_ut_create_buffer_object(new_length); | |
384 | if (!return_desc) { | |
385 | return_ACPI_STATUS(AE_NO_MEMORY); | |
386 | } | |
387 | ||
388 | /* | |
389 | * Copy the templates to the new buffer, 0 first, then 1 follows. One | |
390 | * end_tag descriptor is copied from Operand1. | |
391 | */ | |
392 | new_buf = return_desc->buffer.pointer; | |
393 | memcpy(new_buf, operand0->buffer.pointer, length0); | |
394 | memcpy(new_buf + length0, operand1->buffer.pointer, length1); | |
395 | ||
396 | /* Insert end_tag and set the checksum to zero, means "ignore checksum" */ | |
397 | ||
398 | new_buf[new_length - 1] = 0; | |
399 | new_buf[new_length - 2] = ACPI_RESOURCE_NAME_END_TAG | 1; | |
400 | ||
401 | /* Return the completed resource template */ | |
402 | ||
403 | *actual_return_desc = return_desc; | |
404 | return_ACPI_STATUS(AE_OK); | |
405 | } |