1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Functions corresponding to methods under BIOS interface GUID 4 * for use with hp-bioscfg driver. 5 * 6 * Copyright (c) 2022 Hewlett-Packard Inc. 7 */ 8 9 #include <linux/wmi.h> 10 #include "bioscfg.h" 11 12 /* 13 * struct bios_args buffer is dynamically allocated. New WMI command types 14 * were introduced that exceeds 128-byte data size. Changes to handle 15 * the data size allocation scheme were kept in hp_wmi_perform_query function. 16 */ 17 struct bios_args { 18 u32 signature; 19 u32 command; 20 u32 commandtype; 21 u32 datasize; 22 u8 data[] __counted_by(datasize); 23 }; 24 25 /** 26 * hp_set_attribute 27 * 28 * @a_name: The attribute name 29 * @a_value: The attribute value 30 * 31 * Sets an attribute to new value 32 * 33 * Returns zero on success 34 * -ENODEV if device is not found 35 * -EINVAL if the instance of 'Setup Admin' password is not found. 36 * -ENOMEM unable to allocate memory 37 */ 38 int hp_set_attribute(const char *a_name, const char *a_value) 39 { 40 int security_area_size; 41 int a_name_size, a_value_size; 42 u16 *buffer = NULL; 43 u16 *start; 44 int buffer_size, instance, ret; 45 char *auth_token_choice; 46 47 mutex_lock(&bioscfg_drv.mutex); 48 49 instance = hp_get_password_instance_for_type(SETUP_PASSWD); 50 if (instance < 0) { 51 ret = -EINVAL; 52 goto out_set_attribute; 53 } 54 55 /* Select which auth token to use; password or [auth token] */ 56 if (bioscfg_drv.spm_data.auth_token) 57 auth_token_choice = bioscfg_drv.spm_data.auth_token; 58 else 59 auth_token_choice = bioscfg_drv.password_data[instance].current_password; 60 61 a_name_size = hp_calculate_string_buffer(a_name); 62 a_value_size = hp_calculate_string_buffer(a_value); 63 security_area_size = hp_calculate_security_buffer(auth_token_choice); 64 buffer_size = a_name_size + a_value_size + security_area_size; 65 66 buffer = kmalloc(buffer_size + 1, GFP_KERNEL); 67 if (!buffer) { 68 ret = -ENOMEM; 69 goto out_set_attribute; 70 } 71 72 /* build variables to set */ 73 start = buffer; 74 start = hp_ascii_to_utf16_unicode(start, a_name); 75 if (!start) { 76 ret = -EINVAL; 77 goto out_set_attribute; 78 } 79 80 start = hp_ascii_to_utf16_unicode(start, a_value); 81 if (!start) { 82 ret = -EINVAL; 83 goto out_set_attribute; 84 } 85 86 ret = hp_populate_security_buffer(start, auth_token_choice); 87 if (ret < 0) 88 goto out_set_attribute; 89 90 ret = hp_wmi_set_bios_setting(buffer, buffer_size); 91 92 out_set_attribute: 93 kfree(buffer); 94 mutex_unlock(&bioscfg_drv.mutex); 95 return ret; 96 } 97 98 /** 99 * hp_wmi_perform_query 100 * 101 * @query: The commandtype (enum hp_wmi_commandtype) 102 * @command: The command (enum hp_wmi_command) 103 * @buffer: Buffer used as input and/or output 104 * @insize: Size of input buffer 105 * @outsize: Size of output buffer 106 * 107 * returns zero on success 108 * an HP WMI query specific error code (which is positive) 109 * -EINVAL if the query was not successful at all 110 * -EINVAL if the output buffer size exceeds buffersize 111 * 112 * Note: The buffersize must at least be the maximum of the input and output 113 * size. E.g. Battery info query is defined to have 1 byte input 114 * and 128 byte output. The caller would do: 115 * buffer = kzalloc(128, GFP_KERNEL); 116 * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, 117 * buffer, 1, 128) 118 */ 119 int hp_wmi_perform_query(int query, enum hp_wmi_command command, void *buffer, 120 u32 insize, u32 outsize) 121 { 122 struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL }; 123 struct bios_return *bios_return; 124 union acpi_object *obj = NULL; 125 struct bios_args *args = NULL; 126 int mid, actual_outsize, ret; 127 size_t bios_args_size; 128 129 mid = hp_encode_outsize_for_pvsz(outsize); 130 if (WARN_ON(mid < 0)) 131 return mid; 132 133 bios_args_size = struct_size(args, data, insize); 134 args = kmalloc(bios_args_size, GFP_KERNEL); 135 if (!args) 136 return -ENOMEM; 137 138 input.length = bios_args_size; 139 input.pointer = args; 140 141 /* BIOS expects 'SECU' in hex as the signature value*/ 142 args->signature = 0x55434553; 143 args->command = command; 144 args->commandtype = query; 145 args->datasize = insize; 146 memcpy(args->data, buffer, flex_array_size(args, data, insize)); 147 148 ret = wmi_evaluate_method(HP_WMI_BIOS_GUID, 0, mid, &input, &output); 149 if (ret) 150 goto out_free; 151 152 obj = output.pointer; 153 if (!obj) { 154 ret = -EINVAL; 155 goto out_free; 156 } 157 158 if (obj->type != ACPI_TYPE_BUFFER || 159 obj->buffer.length < sizeof(*bios_return)) { 160 pr_warn("query 0x%x returned wrong type or too small buffer\n", query); 161 ret = -EINVAL; 162 goto out_free; 163 } 164 165 bios_return = (struct bios_return *)obj->buffer.pointer; 166 ret = bios_return->return_code; 167 if (ret) { 168 if (ret != INVALID_CMD_VALUE && ret != INVALID_CMD_TYPE) 169 pr_warn("query 0x%x returned error 0x%x\n", query, ret); 170 goto out_free; 171 } 172 173 /* Ignore output data of zero size */ 174 if (!outsize) 175 goto out_free; 176 177 actual_outsize = min_t(u32, outsize, obj->buffer.length - sizeof(*bios_return)); 178 memcpy_and_pad(buffer, outsize, obj->buffer.pointer + sizeof(*bios_return), 179 actual_outsize, 0); 180 181 out_free: 182 ret = hp_wmi_error_and_message(ret); 183 184 kfree(obj); 185 kfree(args); 186 return ret; 187 } 188 189 static void *utf16_empty_string(u16 *p) 190 { 191 *p++ = 2; 192 *p++ = 0x00; 193 return p; 194 } 195 196 /** 197 * hp_ascii_to_utf16_unicode - Convert ascii string to UTF-16 unicode 198 * 199 * BIOS supports UTF-16 characters that are 2 bytes long. No variable 200 * multi-byte language supported. 201 * 202 * @p: Unicode buffer address 203 * @str: string to convert to unicode 204 * 205 * Returns a void pointer to the buffer string 206 */ 207 void *hp_ascii_to_utf16_unicode(u16 *p, const u8 *str) 208 { 209 int len = strlen(str); 210 int ret; 211 212 /* 213 * Add null character when reading an empty string 214 * "02 00 00 00" 215 */ 216 if (len == 0) 217 return utf16_empty_string(p); 218 219 /* Move pointer len * 2 number of bytes */ 220 *p++ = len * 2; 221 ret = utf8s_to_utf16s(str, strlen(str), UTF16_HOST_ENDIAN, p, len); 222 if (ret < 0) { 223 dev_err(bioscfg_drv.class_dev, "UTF16 conversion failed\n"); 224 return NULL; 225 } 226 227 if (ret * sizeof(u16) > U16_MAX) { 228 dev_err(bioscfg_drv.class_dev, "Error string too long\n"); 229 return NULL; 230 } 231 232 p += len; 233 return p; 234 } 235 236 /** 237 * hp_wmi_set_bios_setting - Set setting's value in BIOS 238 * 239 * @input_buffer: Input buffer address 240 * @input_size: Input buffer size 241 * 242 * Returns: Count of unicode characters written to BIOS if successful, otherwise 243 * -ENOMEM unable to allocate memory 244 * -EINVAL buffer not allocated or too small 245 */ 246 int hp_wmi_set_bios_setting(u16 *input_buffer, u32 input_size) 247 { 248 union acpi_object *obj; 249 struct acpi_buffer input = {input_size, input_buffer}; 250 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 251 int ret; 252 253 ret = wmi_evaluate_method(HP_WMI_SET_BIOS_SETTING_GUID, 0, 1, &input, &output); 254 255 obj = output.pointer; 256 if (!obj) 257 return -EINVAL; 258 259 if (obj->type != ACPI_TYPE_INTEGER) { 260 ret = -EINVAL; 261 goto out_free; 262 } 263 264 ret = obj->integer.value; 265 if (ret) { 266 ret = hp_wmi_error_and_message(ret); 267 goto out_free; 268 } 269 270 out_free: 271 kfree(obj); 272 return ret; 273 } 274 275 static int hp_attr_set_interface_probe(struct wmi_device *wdev, const void *context) 276 { 277 mutex_lock(&bioscfg_drv.mutex); 278 mutex_unlock(&bioscfg_drv.mutex); 279 return 0; 280 } 281 282 static void hp_attr_set_interface_remove(struct wmi_device *wdev) 283 { 284 mutex_lock(&bioscfg_drv.mutex); 285 mutex_unlock(&bioscfg_drv.mutex); 286 } 287 288 static const struct wmi_device_id hp_attr_set_interface_id_table[] = { 289 { .guid_string = HP_WMI_BIOS_GUID}, 290 { } 291 }; 292 293 static struct wmi_driver hp_attr_set_interface_driver = { 294 .driver = { 295 .name = DRIVER_NAME, 296 }, 297 .probe = hp_attr_set_interface_probe, 298 .remove = hp_attr_set_interface_remove, 299 .id_table = hp_attr_set_interface_id_table, 300 }; 301 302 int hp_init_attr_set_interface(void) 303 { 304 return wmi_driver_register(&hp_attr_set_interface_driver); 305 } 306 307 void hp_exit_attr_set_interface(void) 308 { 309 wmi_driver_unregister(&hp_attr_set_interface_driver); 310 } 311 312 MODULE_DEVICE_TABLE(wmi, hp_attr_set_interface_id_table); 313