xref: /linux/drivers/acpi/acpica/rslist.c (revision 40d269c000bda9fcd276a0412a9cebd3f6e344c5)
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /*******************************************************************************
3  *
4  * Module Name: rslist - Linked list utilities
5  *
6  ******************************************************************************/
7 
8 #include <acpi/acpi.h>
9 #include "accommon.h"
10 #include "acresrc.h"
11 
12 #define _COMPONENT          ACPI_RESOURCES
13 ACPI_MODULE_NAME("rslist")
14 
15 /*******************************************************************************
16  *
17  * FUNCTION:    acpi_rs_convert_aml_to_resources
18  *
19  * PARAMETERS:  acpi_walk_aml_callback
20  *              resource_ptr            - Pointer to the buffer that will
21  *                                        contain the output structures
22  *
23  * RETURN:      Status
24  *
25  * DESCRIPTION: Convert an AML resource to an internal representation of the
26  *              resource that is aligned and easier to access.
27  *
28  ******************************************************************************/
29 acpi_status
30 acpi_rs_convert_aml_to_resources(u8 * aml,
31 				 u32 length,
32 				 u32 offset, u8 resource_index, void **context)
33 {
34 	struct acpi_resource **resource_ptr =
35 	    ACPI_CAST_INDIRECT_PTR(struct acpi_resource, context);
36 	struct acpi_resource *resource;
37 	union aml_resource *aml_resource;
38 	struct acpi_rsconvert_info *conversion_table;
39 	acpi_status status;
40 
41 	ACPI_FUNCTION_TRACE(rs_convert_aml_to_resources);
42 
43 	/*
44 	 * Check that the input buffer and all subsequent pointers into it
45 	 * are aligned on a native word boundary. Most important on IA64
46 	 */
47 	resource = *resource_ptr;
48 	if (ACPI_IS_MISALIGNED(resource)) {
49 		ACPI_WARNING((AE_INFO,
50 			      "Misaligned resource pointer %p", resource));
51 	}
52 
53 	/* Get the appropriate conversion info table */
54 
55 	aml_resource = ACPI_CAST_PTR(union aml_resource, aml);
56 
57 	if (acpi_ut_get_resource_type(aml) == ACPI_RESOURCE_NAME_SERIAL_BUS) {
58 
59 		/* Avoid undefined behavior: member access within misaligned address */
60 
61 		struct aml_resource_common_serialbus common_serial_bus;
62 		memcpy(&common_serial_bus, aml_resource,
63 		       sizeof(common_serial_bus));
64 
65 		if (common_serial_bus.type > AML_RESOURCE_MAX_SERIALBUSTYPE) {
66 			conversion_table = NULL;
67 		} else {
68 			/* This is an I2C, SPI, UART, or CSI2 serial_bus descriptor */
69 
70 			conversion_table =
71 			    acpi_gbl_convert_resource_serial_bus_dispatch
72 			    [common_serial_bus.type];
73 		}
74 	} else {
75 		conversion_table =
76 		    acpi_gbl_get_resource_dispatch[resource_index];
77 	}
78 
79 	if (!conversion_table) {
80 		ACPI_ERROR((AE_INFO,
81 			    "Invalid/unsupported resource descriptor: Type 0x%2.2X",
82 			    resource_index));
83 		return_ACPI_STATUS(AE_AML_INVALID_RESOURCE_TYPE);
84 	}
85 
86 	/* Convert the AML byte stream resource to a local resource struct */
87 
88 	status =
89 	    acpi_rs_convert_aml_to_resource(resource, aml_resource,
90 					    conversion_table);
91 	if (ACPI_FAILURE(status)) {
92 		ACPI_EXCEPTION((AE_INFO, status,
93 				"Could not convert AML resource (Type 0x%X)",
94 				*aml));
95 		return_ACPI_STATUS(status);
96 	}
97 
98 	if (!resource->length) {
99 		ACPI_EXCEPTION((AE_INFO, status,
100 				"Zero-length resource returned from RsConvertAmlToResource"));
101 	}
102 
103 	ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES,
104 			  "Type %.2X, AmlLength %.2X InternalLength %.2X\n",
105 			  acpi_ut_get_resource_type(aml), length,
106 			  resource->length));
107 
108 	/* Point to the next structure in the output buffer */
109 
110 	*resource_ptr = ACPI_NEXT_RESOURCE(resource);
111 	return_ACPI_STATUS(AE_OK);
112 }
113 
114 /*******************************************************************************
115  *
116  * FUNCTION:    acpi_rs_convert_resources_to_aml
117  *
118  * PARAMETERS:  resource            - Pointer to the resource linked list
119  *              aml_size_needed     - Calculated size of the byte stream
120  *                                    needed from calling acpi_rs_get_aml_length()
121  *                                    The size of the output_buffer is
122  *                                    guaranteed to be >= aml_size_needed
123  *              output_buffer       - Pointer to the buffer that will
124  *                                    contain the byte stream
125  *
126  * RETURN:      Status
127  *
128  * DESCRIPTION: Takes the resource linked list and parses it, creating a
129  *              byte stream of resources in the caller's output buffer
130  *
131  ******************************************************************************/
132 
133 acpi_status
134 acpi_rs_convert_resources_to_aml(struct acpi_resource *resource,
135 				 acpi_size aml_size_needed, u8 * output_buffer)
136 {
137 	u8 *aml = output_buffer;
138 	u8 *end_aml = output_buffer + aml_size_needed;
139 	struct acpi_rsconvert_info *conversion_table;
140 	acpi_status status;
141 
142 	ACPI_FUNCTION_TRACE(rs_convert_resources_to_aml);
143 
144 	/* Walk the resource descriptor list, convert each descriptor */
145 
146 	while (aml < end_aml) {
147 
148 		/* Validate the (internal) Resource Type */
149 
150 		if (resource->type > ACPI_RESOURCE_TYPE_MAX) {
151 			ACPI_ERROR((AE_INFO,
152 				    "Invalid descriptor type (0x%X) in resource list",
153 				    resource->type));
154 			return_ACPI_STATUS(AE_BAD_DATA);
155 		}
156 
157 		/* Sanity check the length. It must not be zero, or we loop forever */
158 
159 		if (!resource->length) {
160 			ACPI_ERROR((AE_INFO,
161 				    "Invalid zero length descriptor in resource list\n"));
162 			return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH);
163 		}
164 
165 		/* Perform the conversion */
166 
167 		if (resource->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
168 			if (resource->data.common_serial_bus.type >
169 			    AML_RESOURCE_MAX_SERIALBUSTYPE) {
170 				conversion_table = NULL;
171 			} else {
172 				/* This is an I2C, SPI, UART or CSI2 serial_bus descriptor */
173 
174 				conversion_table =
175 				    acpi_gbl_convert_resource_serial_bus_dispatch
176 				    [resource->data.common_serial_bus.type];
177 			}
178 		} else {
179 			conversion_table =
180 			    acpi_gbl_set_resource_dispatch[resource->type];
181 		}
182 
183 		if (!conversion_table) {
184 			ACPI_ERROR((AE_INFO,
185 				    "Invalid/unsupported resource descriptor: Type 0x%2.2X",
186 				    resource->type));
187 			return_ACPI_STATUS(AE_AML_INVALID_RESOURCE_TYPE);
188 		}
189 
190 		status = acpi_rs_convert_resource_to_aml(resource,
191 						         ACPI_CAST_PTR(union
192 								       aml_resource,
193 								       aml),
194 							 conversion_table);
195 		if (ACPI_FAILURE(status)) {
196 			ACPI_EXCEPTION((AE_INFO, status,
197 					"Could not convert resource (type 0x%X) to AML",
198 					resource->type));
199 			return_ACPI_STATUS(status);
200 		}
201 
202 		/* Perform final sanity check on the new AML resource descriptor */
203 
204 		status =
205 		    acpi_ut_validate_resource(NULL,
206 					      ACPI_CAST_PTR(union aml_resource,
207 							    aml), NULL);
208 		if (ACPI_FAILURE(status)) {
209 			return_ACPI_STATUS(status);
210 		}
211 
212 		/* Check for end-of-list, normal exit */
213 
214 		if (resource->type == ACPI_RESOURCE_TYPE_END_TAG) {
215 
216 			/* An End Tag indicates the end of the input Resource Template */
217 
218 			return_ACPI_STATUS(AE_OK);
219 		}
220 
221 		/*
222 		 * Extract the total length of the new descriptor and set the
223 		 * Aml to point to the next (output) resource descriptor
224 		 */
225 		aml += acpi_ut_get_descriptor_length(aml);
226 
227 		/* Point to the next input resource descriptor */
228 
229 		resource = ACPI_NEXT_RESOURCE(resource);
230 	}
231 
232 	/* Completed buffer, but did not find an end_tag resource descriptor */
233 
234 	return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG);
235 }
236