xref: /linux/drivers/platform/wmi/marshalling.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1*015b70a6SArmin Wolf // SPDX-License-Identifier: GPL-2.0-or-later
2*015b70a6SArmin Wolf /*
3*015b70a6SArmin Wolf  * ACPI-WMI buffer marshalling.
4*015b70a6SArmin Wolf  *
5*015b70a6SArmin Wolf  * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
6*015b70a6SArmin Wolf  */
7*015b70a6SArmin Wolf 
8*015b70a6SArmin Wolf #include <linux/acpi.h>
9*015b70a6SArmin Wolf #include <linux/align.h>
10*015b70a6SArmin Wolf #include <linux/math.h>
11*015b70a6SArmin Wolf #include <linux/overflow.h>
12*015b70a6SArmin Wolf #include <linux/slab.h>
13*015b70a6SArmin Wolf #include <linux/unaligned.h>
14*015b70a6SArmin Wolf #include <linux/wmi.h>
15*015b70a6SArmin Wolf 
16*015b70a6SArmin Wolf #include <kunit/visibility.h>
17*015b70a6SArmin Wolf 
18*015b70a6SArmin Wolf #include "internal.h"
19*015b70a6SArmin Wolf 
20*015b70a6SArmin Wolf static int wmi_adjust_buffer_length(size_t *length, const union acpi_object *obj)
21*015b70a6SArmin Wolf {
22*015b70a6SArmin Wolf 	size_t alignment, size;
23*015b70a6SArmin Wolf 
24*015b70a6SArmin Wolf 	switch (obj->type) {
25*015b70a6SArmin Wolf 	case ACPI_TYPE_INTEGER:
26*015b70a6SArmin Wolf 		/*
27*015b70a6SArmin Wolf 		 * Integers are threated as 32 bit even if the ACPI DSDT
28*015b70a6SArmin Wolf 		 * declares 64 bit integer width.
29*015b70a6SArmin Wolf 		 */
30*015b70a6SArmin Wolf 		alignment = 4;
31*015b70a6SArmin Wolf 		size = sizeof(u32);
32*015b70a6SArmin Wolf 		break;
33*015b70a6SArmin Wolf 	case ACPI_TYPE_STRING:
34*015b70a6SArmin Wolf 		/*
35*015b70a6SArmin Wolf 		 * Strings begin with a single little-endian 16-bit field containing
36*015b70a6SArmin Wolf 		 * the string length in bytes and are encoded as UTF-16LE with a terminating
37*015b70a6SArmin Wolf 		 * nul character.
38*015b70a6SArmin Wolf 		 */
39*015b70a6SArmin Wolf 		if (obj->string.length + 1 > U16_MAX / 2)
40*015b70a6SArmin Wolf 			return -EOVERFLOW;
41*015b70a6SArmin Wolf 
42*015b70a6SArmin Wolf 		alignment = 2;
43*015b70a6SArmin Wolf 		size = struct_size_t(struct wmi_string, chars, obj->string.length + 1);
44*015b70a6SArmin Wolf 		break;
45*015b70a6SArmin Wolf 	case ACPI_TYPE_BUFFER:
46*015b70a6SArmin Wolf 		/*
47*015b70a6SArmin Wolf 		 * Buffers are copied as-is.
48*015b70a6SArmin Wolf 		 */
49*015b70a6SArmin Wolf 		alignment = 1;
50*015b70a6SArmin Wolf 		size = obj->buffer.length;
51*015b70a6SArmin Wolf 		break;
52*015b70a6SArmin Wolf 	default:
53*015b70a6SArmin Wolf 		return -EPROTO;
54*015b70a6SArmin Wolf 	}
55*015b70a6SArmin Wolf 
56*015b70a6SArmin Wolf 	*length = size_add(ALIGN(*length, alignment), size);
57*015b70a6SArmin Wolf 
58*015b70a6SArmin Wolf 	return 0;
59*015b70a6SArmin Wolf }
60*015b70a6SArmin Wolf 
61*015b70a6SArmin Wolf static int wmi_obj_get_buffer_length(const union acpi_object *obj, size_t *length)
62*015b70a6SArmin Wolf {
63*015b70a6SArmin Wolf 	size_t total = 0;
64*015b70a6SArmin Wolf 	int ret;
65*015b70a6SArmin Wolf 
66*015b70a6SArmin Wolf 	if (obj->type == ACPI_TYPE_PACKAGE) {
67*015b70a6SArmin Wolf 		for (int i = 0; i < obj->package.count; i++) {
68*015b70a6SArmin Wolf 			ret = wmi_adjust_buffer_length(&total, &obj->package.elements[i]);
69*015b70a6SArmin Wolf 			if (ret < 0)
70*015b70a6SArmin Wolf 				return ret;
71*015b70a6SArmin Wolf 		}
72*015b70a6SArmin Wolf 	} else {
73*015b70a6SArmin Wolf 		ret = wmi_adjust_buffer_length(&total, obj);
74*015b70a6SArmin Wolf 		if (ret < 0)
75*015b70a6SArmin Wolf 			return ret;
76*015b70a6SArmin Wolf 	}
77*015b70a6SArmin Wolf 
78*015b70a6SArmin Wolf 	*length = total;
79*015b70a6SArmin Wolf 
80*015b70a6SArmin Wolf 	return 0;
81*015b70a6SArmin Wolf }
82*015b70a6SArmin Wolf 
83*015b70a6SArmin Wolf static int wmi_obj_transform_simple(const union acpi_object *obj, u8 *buffer, size_t *consumed)
84*015b70a6SArmin Wolf {
85*015b70a6SArmin Wolf 	struct wmi_string *string;
86*015b70a6SArmin Wolf 	size_t length;
87*015b70a6SArmin Wolf 	__le32 value;
88*015b70a6SArmin Wolf 	u8 *aligned;
89*015b70a6SArmin Wolf 
90*015b70a6SArmin Wolf 	switch (obj->type) {
91*015b70a6SArmin Wolf 	case ACPI_TYPE_INTEGER:
92*015b70a6SArmin Wolf 		aligned = PTR_ALIGN(buffer, 4);
93*015b70a6SArmin Wolf 		length = sizeof(value);
94*015b70a6SArmin Wolf 
95*015b70a6SArmin Wolf 		value = cpu_to_le32(obj->integer.value);
96*015b70a6SArmin Wolf 		memcpy(aligned, &value, length);
97*015b70a6SArmin Wolf 		break;
98*015b70a6SArmin Wolf 	case ACPI_TYPE_STRING:
99*015b70a6SArmin Wolf 		aligned = PTR_ALIGN(buffer, 2);
100*015b70a6SArmin Wolf 		string = (struct wmi_string *)aligned;
101*015b70a6SArmin Wolf 		length = struct_size(string, chars, obj->string.length + 1);
102*015b70a6SArmin Wolf 
103*015b70a6SArmin Wolf 		/* We do not have to worry about unaligned accesses here as the WMI
104*015b70a6SArmin Wolf 		 * string will already be aligned on a two-byte boundary.
105*015b70a6SArmin Wolf 		 */
106*015b70a6SArmin Wolf 		string->length = cpu_to_le16((obj->string.length + 1) * 2);
107*015b70a6SArmin Wolf 		for (int i = 0; i < obj->string.length; i++)
108*015b70a6SArmin Wolf 			string->chars[i] = cpu_to_le16(obj->string.pointer[i]);
109*015b70a6SArmin Wolf 
110*015b70a6SArmin Wolf 		/*
111*015b70a6SArmin Wolf 		 * The Windows WMI-ACPI driver always emits a terminating nul character,
112*015b70a6SArmin Wolf 		 * so we emulate this behavior here as well.
113*015b70a6SArmin Wolf 		 */
114*015b70a6SArmin Wolf 		string->chars[obj->string.length] = '\0';
115*015b70a6SArmin Wolf 		break;
116*015b70a6SArmin Wolf 	case ACPI_TYPE_BUFFER:
117*015b70a6SArmin Wolf 		aligned = buffer;
118*015b70a6SArmin Wolf 		length = obj->buffer.length;
119*015b70a6SArmin Wolf 
120*015b70a6SArmin Wolf 		memcpy(aligned, obj->buffer.pointer, length);
121*015b70a6SArmin Wolf 		break;
122*015b70a6SArmin Wolf 	default:
123*015b70a6SArmin Wolf 		return -EPROTO;
124*015b70a6SArmin Wolf 	}
125*015b70a6SArmin Wolf 
126*015b70a6SArmin Wolf 	*consumed = (aligned - buffer) + length;
127*015b70a6SArmin Wolf 
128*015b70a6SArmin Wolf 	return 0;
129*015b70a6SArmin Wolf }
130*015b70a6SArmin Wolf 
131*015b70a6SArmin Wolf static int wmi_obj_transform(const union acpi_object *obj, u8 *buffer)
132*015b70a6SArmin Wolf {
133*015b70a6SArmin Wolf 	size_t consumed;
134*015b70a6SArmin Wolf 	int ret;
135*015b70a6SArmin Wolf 
136*015b70a6SArmin Wolf 	if (obj->type == ACPI_TYPE_PACKAGE) {
137*015b70a6SArmin Wolf 		for (int i = 0; i < obj->package.count; i++) {
138*015b70a6SArmin Wolf 			ret = wmi_obj_transform_simple(&obj->package.elements[i], buffer,
139*015b70a6SArmin Wolf 						       &consumed);
140*015b70a6SArmin Wolf 			if (ret < 0)
141*015b70a6SArmin Wolf 				return ret;
142*015b70a6SArmin Wolf 
143*015b70a6SArmin Wolf 			buffer += consumed;
144*015b70a6SArmin Wolf 		}
145*015b70a6SArmin Wolf 	} else {
146*015b70a6SArmin Wolf 		ret = wmi_obj_transform_simple(obj, buffer, &consumed);
147*015b70a6SArmin Wolf 		if (ret < 0)
148*015b70a6SArmin Wolf 			return ret;
149*015b70a6SArmin Wolf 	}
150*015b70a6SArmin Wolf 
151*015b70a6SArmin Wolf 	return 0;
152*015b70a6SArmin Wolf }
153*015b70a6SArmin Wolf 
154*015b70a6SArmin Wolf int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer)
155*015b70a6SArmin Wolf {
156*015b70a6SArmin Wolf 	size_t length, alloc_length;
157*015b70a6SArmin Wolf 	u8 *data;
158*015b70a6SArmin Wolf 	int ret;
159*015b70a6SArmin Wolf 
160*015b70a6SArmin Wolf 	ret = wmi_obj_get_buffer_length(obj, &length);
161*015b70a6SArmin Wolf 	if (ret < 0)
162*015b70a6SArmin Wolf 		return ret;
163*015b70a6SArmin Wolf 
164*015b70a6SArmin Wolf 	if (ARCH_KMALLOC_MINALIGN < 8) {
165*015b70a6SArmin Wolf 		/*
166*015b70a6SArmin Wolf 		 * kmalloc() guarantees that the alignment of the resulting memory allocation is at
167*015b70a6SArmin Wolf 		 * least the largest power-of-two divisor of the allocation size. The WMI buffer
168*015b70a6SArmin Wolf 		 * data needs to be aligned on a 8 byte boundary to properly support 64-bit WMI
169*015b70a6SArmin Wolf 		 * integers, so we have to round the allocation size to the next multiple of 8.
170*015b70a6SArmin Wolf 		 */
171*015b70a6SArmin Wolf 		alloc_length = round_up(length, 8);
172*015b70a6SArmin Wolf 	} else {
173*015b70a6SArmin Wolf 		alloc_length = length;
174*015b70a6SArmin Wolf 	}
175*015b70a6SArmin Wolf 
176*015b70a6SArmin Wolf 	data = kzalloc(alloc_length, GFP_KERNEL);
177*015b70a6SArmin Wolf 	if (!data)
178*015b70a6SArmin Wolf 		return -ENOMEM;
179*015b70a6SArmin Wolf 
180*015b70a6SArmin Wolf 	ret = wmi_obj_transform(obj, data);
181*015b70a6SArmin Wolf 	if (ret < 0) {
182*015b70a6SArmin Wolf 		kfree(data);
183*015b70a6SArmin Wolf 		return ret;
184*015b70a6SArmin Wolf 	}
185*015b70a6SArmin Wolf 
186*015b70a6SArmin Wolf 	buffer->length = length;
187*015b70a6SArmin Wolf 	buffer->data = data;
188*015b70a6SArmin Wolf 
189*015b70a6SArmin Wolf 	return 0;
190*015b70a6SArmin Wolf }
191*015b70a6SArmin Wolf EXPORT_SYMBOL_IF_KUNIT(wmi_unmarshal_acpi_object);
192*015b70a6SArmin Wolf 
193*015b70a6SArmin Wolf int wmi_marshal_string(const struct wmi_buffer *buffer, struct acpi_buffer *out)
194*015b70a6SArmin Wolf {
195*015b70a6SArmin Wolf 	const struct wmi_string *string;
196*015b70a6SArmin Wolf 	u16 length, value;
197*015b70a6SArmin Wolf 	size_t chars;
198*015b70a6SArmin Wolf 	char *str;
199*015b70a6SArmin Wolf 
200*015b70a6SArmin Wolf 	if (buffer->length < sizeof(*string))
201*015b70a6SArmin Wolf 		return -ENODATA;
202*015b70a6SArmin Wolf 
203*015b70a6SArmin Wolf 	string = buffer->data;
204*015b70a6SArmin Wolf 	length = get_unaligned_le16(&string->length);
205*015b70a6SArmin Wolf 	if (buffer->length < sizeof(*string) + length)
206*015b70a6SArmin Wolf 		return -ENODATA;
207*015b70a6SArmin Wolf 
208*015b70a6SArmin Wolf 	/* Each character needs to be 16 bits long */
209*015b70a6SArmin Wolf 	if (length % 2)
210*015b70a6SArmin Wolf 		return -EINVAL;
211*015b70a6SArmin Wolf 
212*015b70a6SArmin Wolf 	chars = length / 2;
213*015b70a6SArmin Wolf 	str = kmalloc(chars + 1, GFP_KERNEL);
214*015b70a6SArmin Wolf 	if (!str)
215*015b70a6SArmin Wolf 		return -ENOMEM;
216*015b70a6SArmin Wolf 
217*015b70a6SArmin Wolf 	for (int i = 0; i < chars; i++) {
218*015b70a6SArmin Wolf 		value = get_unaligned_le16(&string->chars[i]);
219*015b70a6SArmin Wolf 
220*015b70a6SArmin Wolf 		/* ACPI only accepts ASCII strings */
221*015b70a6SArmin Wolf 		if (value > 0x7F) {
222*015b70a6SArmin Wolf 			kfree(str);
223*015b70a6SArmin Wolf 			return -EINVAL;
224*015b70a6SArmin Wolf 		}
225*015b70a6SArmin Wolf 
226*015b70a6SArmin Wolf 		str[i] = value & 0xFF;
227*015b70a6SArmin Wolf 
228*015b70a6SArmin Wolf 		/*
229*015b70a6SArmin Wolf 		 * ACPI strings should only contain a single nul character at the end.
230*015b70a6SArmin Wolf 		 * Because of this we must not copy any padding from the WMI string.
231*015b70a6SArmin Wolf 		 */
232*015b70a6SArmin Wolf 		if (!value) {
233*015b70a6SArmin Wolf 			/* ACPICA wants the length of the string without the nul character */
234*015b70a6SArmin Wolf 			out->length = i;
235*015b70a6SArmin Wolf 			out->pointer = str;
236*015b70a6SArmin Wolf 			return 0;
237*015b70a6SArmin Wolf 		}
238*015b70a6SArmin Wolf 	}
239*015b70a6SArmin Wolf 
240*015b70a6SArmin Wolf 	str[chars] = '\0';
241*015b70a6SArmin Wolf 
242*015b70a6SArmin Wolf 	out->length = chars;
243*015b70a6SArmin Wolf 	out->pointer = str;
244*015b70a6SArmin Wolf 
245*015b70a6SArmin Wolf 	return 0;
246*015b70a6SArmin Wolf }
247*015b70a6SArmin Wolf EXPORT_SYMBOL_IF_KUNIT(wmi_marshal_string);
248