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