1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /******************************************************************************
3 *
4 * Module Name: exconcat - Concatenate-type AML operators
5 *
6 * Copyright (C) 2000 - 2023, Intel Corp.
7 *
8 *****************************************************************************/
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
acpi_ex_do_concatenate(union acpi_operand_object * operand0,union acpi_operand_object * operand1,union acpi_operand_object ** actual_return_desc,struct acpi_walk_state * walk_state)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,
125 ACPI_IMPLICIT_CONVERSION);
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
acpi_ex_convert_to_object_type_string(union acpi_operand_object * obj_desc,union acpi_operand_object ** result_desc)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
309 return_desc = acpi_ut_create_string_object(((acpi_size)strlen(type_string) + 9)); /* 9 For "[ Object]" */
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
acpi_ex_concat_template(union acpi_operand_object * operand0,union acpi_operand_object * operand1,union acpi_operand_object ** actual_return_desc,struct acpi_walk_state * walk_state)338 acpi_ex_concat_template(union acpi_operand_object *operand0,
339 union acpi_operand_object *operand1,
340 union acpi_operand_object **actual_return_desc,
341 struct acpi_walk_state *walk_state)
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 }
406