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