efivars.c (16195B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Originally from efivars.c, 4 * 5 * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com> 6 * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com> 7 * 8 * This code takes all variables accessible from EFI runtime and 9 * exports them via sysfs 10 */ 11 12#include <linux/efi.h> 13#include <linux/module.h> 14#include <linux/slab.h> 15#include <linux/ucs2_string.h> 16#include <linux/compat.h> 17 18#define EFIVARS_VERSION "0.08" 19#define EFIVARS_DATE "2004-May-17" 20 21MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>"); 22MODULE_DESCRIPTION("sysfs interface to EFI Variables"); 23MODULE_LICENSE("GPL"); 24MODULE_VERSION(EFIVARS_VERSION); 25 26static LIST_HEAD(efivar_sysfs_list); 27 28static struct kset *efivars_kset; 29 30static struct bin_attribute *efivars_new_var; 31static struct bin_attribute *efivars_del_var; 32 33struct compat_efi_variable { 34 efi_char16_t VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)]; 35 efi_guid_t VendorGuid; 36 __u32 DataSize; 37 __u8 Data[1024]; 38 __u32 Status; 39 __u32 Attributes; 40} __packed; 41 42struct efivar_attribute { 43 struct attribute attr; 44 ssize_t (*show) (struct efivar_entry *entry, char *buf); 45 ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count); 46}; 47 48#define EFIVAR_ATTR(_name, _mode, _show, _store) \ 49struct efivar_attribute efivar_attr_##_name = { \ 50 .attr = {.name = __stringify(_name), .mode = _mode}, \ 51 .show = _show, \ 52 .store = _store, \ 53}; 54 55#define to_efivar_attr(_attr) container_of(_attr, struct efivar_attribute, attr) 56#define to_efivar_entry(obj) container_of(obj, struct efivar_entry, kobj) 57 58/* 59 * Prototype for sysfs creation function 60 */ 61static int 62efivar_create_sysfs_entry(struct efivar_entry *new_var); 63 64static ssize_t 65efivar_guid_read(struct efivar_entry *entry, char *buf) 66{ 67 struct efi_variable *var = &entry->var; 68 char *str = buf; 69 70 if (!entry || !buf) 71 return 0; 72 73 efi_guid_to_str(&var->VendorGuid, str); 74 str += strlen(str); 75 str += sprintf(str, "\n"); 76 77 return str - buf; 78} 79 80static ssize_t 81efivar_attr_read(struct efivar_entry *entry, char *buf) 82{ 83 struct efi_variable *var = &entry->var; 84 unsigned long size = sizeof(var->Data); 85 char *str = buf; 86 int ret; 87 88 if (!entry || !buf) 89 return -EINVAL; 90 91 ret = efivar_entry_get(entry, &var->Attributes, &size, var->Data); 92 var->DataSize = size; 93 if (ret) 94 return -EIO; 95 96 if (var->Attributes & EFI_VARIABLE_NON_VOLATILE) 97 str += sprintf(str, "EFI_VARIABLE_NON_VOLATILE\n"); 98 if (var->Attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS) 99 str += sprintf(str, "EFI_VARIABLE_BOOTSERVICE_ACCESS\n"); 100 if (var->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) 101 str += sprintf(str, "EFI_VARIABLE_RUNTIME_ACCESS\n"); 102 if (var->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) 103 str += sprintf(str, "EFI_VARIABLE_HARDWARE_ERROR_RECORD\n"); 104 if (var->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) 105 str += sprintf(str, 106 "EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS\n"); 107 if (var->Attributes & 108 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) 109 str += sprintf(str, 110 "EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS\n"); 111 if (var->Attributes & EFI_VARIABLE_APPEND_WRITE) 112 str += sprintf(str, "EFI_VARIABLE_APPEND_WRITE\n"); 113 return str - buf; 114} 115 116static ssize_t 117efivar_size_read(struct efivar_entry *entry, char *buf) 118{ 119 struct efi_variable *var = &entry->var; 120 unsigned long size = sizeof(var->Data); 121 char *str = buf; 122 int ret; 123 124 if (!entry || !buf) 125 return -EINVAL; 126 127 ret = efivar_entry_get(entry, &var->Attributes, &size, var->Data); 128 var->DataSize = size; 129 if (ret) 130 return -EIO; 131 132 str += sprintf(str, "0x%lx\n", var->DataSize); 133 return str - buf; 134} 135 136static ssize_t 137efivar_data_read(struct efivar_entry *entry, char *buf) 138{ 139 struct efi_variable *var = &entry->var; 140 unsigned long size = sizeof(var->Data); 141 int ret; 142 143 if (!entry || !buf) 144 return -EINVAL; 145 146 ret = efivar_entry_get(entry, &var->Attributes, &size, var->Data); 147 var->DataSize = size; 148 if (ret) 149 return -EIO; 150 151 memcpy(buf, var->Data, var->DataSize); 152 return var->DataSize; 153} 154 155static inline int 156sanity_check(struct efi_variable *var, efi_char16_t *name, efi_guid_t vendor, 157 unsigned long size, u32 attributes, u8 *data) 158{ 159 /* 160 * If only updating the variable data, then the name 161 * and guid should remain the same 162 */ 163 if (memcmp(name, var->VariableName, sizeof(var->VariableName)) || 164 efi_guidcmp(vendor, var->VendorGuid)) { 165 printk(KERN_ERR "efivars: Cannot edit the wrong variable!\n"); 166 return -EINVAL; 167 } 168 169 if ((size <= 0) || (attributes == 0)){ 170 printk(KERN_ERR "efivars: DataSize & Attributes must be valid!\n"); 171 return -EINVAL; 172 } 173 174 if ((attributes & ~EFI_VARIABLE_MASK) != 0 || 175 efivar_validate(vendor, name, data, size) == false) { 176 printk(KERN_ERR "efivars: Malformed variable content\n"); 177 return -EINVAL; 178 } 179 180 return 0; 181} 182 183static void 184copy_out_compat(struct efi_variable *dst, struct compat_efi_variable *src) 185{ 186 memcpy(dst->VariableName, src->VariableName, EFI_VAR_NAME_LEN); 187 memcpy(dst->Data, src->Data, sizeof(src->Data)); 188 189 dst->VendorGuid = src->VendorGuid; 190 dst->DataSize = src->DataSize; 191 dst->Attributes = src->Attributes; 192} 193 194/* 195 * We allow each variable to be edited via rewriting the 196 * entire efi variable structure. 197 */ 198static ssize_t 199efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) 200{ 201 struct efi_variable *new_var, *var = &entry->var; 202 efi_char16_t *name; 203 unsigned long size; 204 efi_guid_t vendor; 205 u32 attributes; 206 u8 *data; 207 int err; 208 209 if (!entry || !buf) 210 return -EINVAL; 211 212 if (in_compat_syscall()) { 213 struct compat_efi_variable *compat; 214 215 if (count != sizeof(*compat)) 216 return -EINVAL; 217 218 compat = (struct compat_efi_variable *)buf; 219 attributes = compat->Attributes; 220 vendor = compat->VendorGuid; 221 name = compat->VariableName; 222 size = compat->DataSize; 223 data = compat->Data; 224 225 err = sanity_check(var, name, vendor, size, attributes, data); 226 if (err) 227 return err; 228 229 copy_out_compat(&entry->var, compat); 230 } else { 231 if (count != sizeof(struct efi_variable)) 232 return -EINVAL; 233 234 new_var = (struct efi_variable *)buf; 235 236 attributes = new_var->Attributes; 237 vendor = new_var->VendorGuid; 238 name = new_var->VariableName; 239 size = new_var->DataSize; 240 data = new_var->Data; 241 242 err = sanity_check(var, name, vendor, size, attributes, data); 243 if (err) 244 return err; 245 246 memcpy(&entry->var, new_var, count); 247 } 248 249 err = efivar_entry_set(entry, attributes, size, data, NULL); 250 if (err) { 251 printk(KERN_WARNING "efivars: set_variable() failed: status=%d\n", err); 252 return -EIO; 253 } 254 255 return count; 256} 257 258static ssize_t 259efivar_show_raw(struct efivar_entry *entry, char *buf) 260{ 261 struct efi_variable *var = &entry->var; 262 struct compat_efi_variable *compat; 263 unsigned long datasize = sizeof(var->Data); 264 size_t size; 265 int ret; 266 267 if (!entry || !buf) 268 return 0; 269 270 ret = efivar_entry_get(entry, &var->Attributes, &datasize, var->Data); 271 var->DataSize = datasize; 272 if (ret) 273 return -EIO; 274 275 if (in_compat_syscall()) { 276 compat = (struct compat_efi_variable *)buf; 277 278 size = sizeof(*compat); 279 memcpy(compat->VariableName, var->VariableName, 280 EFI_VAR_NAME_LEN); 281 memcpy(compat->Data, var->Data, sizeof(compat->Data)); 282 283 compat->VendorGuid = var->VendorGuid; 284 compat->DataSize = var->DataSize; 285 compat->Attributes = var->Attributes; 286 } else { 287 size = sizeof(*var); 288 memcpy(buf, var, size); 289 } 290 291 return size; 292} 293 294/* 295 * Generic read/write functions that call the specific functions of 296 * the attributes... 297 */ 298static ssize_t efivar_attr_show(struct kobject *kobj, struct attribute *attr, 299 char *buf) 300{ 301 struct efivar_entry *var = to_efivar_entry(kobj); 302 struct efivar_attribute *efivar_attr = to_efivar_attr(attr); 303 ssize_t ret = -EIO; 304 305 if (!capable(CAP_SYS_ADMIN)) 306 return -EACCES; 307 308 if (efivar_attr->show) { 309 ret = efivar_attr->show(var, buf); 310 } 311 return ret; 312} 313 314static ssize_t efivar_attr_store(struct kobject *kobj, struct attribute *attr, 315 const char *buf, size_t count) 316{ 317 struct efivar_entry *var = to_efivar_entry(kobj); 318 struct efivar_attribute *efivar_attr = to_efivar_attr(attr); 319 ssize_t ret = -EIO; 320 321 if (!capable(CAP_SYS_ADMIN)) 322 return -EACCES; 323 324 if (efivar_attr->store) 325 ret = efivar_attr->store(var, buf, count); 326 327 return ret; 328} 329 330static const struct sysfs_ops efivar_attr_ops = { 331 .show = efivar_attr_show, 332 .store = efivar_attr_store, 333}; 334 335static void efivar_release(struct kobject *kobj) 336{ 337 struct efivar_entry *var = to_efivar_entry(kobj); 338 kfree(var); 339} 340 341static EFIVAR_ATTR(guid, 0400, efivar_guid_read, NULL); 342static EFIVAR_ATTR(attributes, 0400, efivar_attr_read, NULL); 343static EFIVAR_ATTR(size, 0400, efivar_size_read, NULL); 344static EFIVAR_ATTR(data, 0400, efivar_data_read, NULL); 345static EFIVAR_ATTR(raw_var, 0600, efivar_show_raw, efivar_store_raw); 346 347static struct attribute *def_attrs[] = { 348 &efivar_attr_guid.attr, 349 &efivar_attr_size.attr, 350 &efivar_attr_attributes.attr, 351 &efivar_attr_data.attr, 352 &efivar_attr_raw_var.attr, 353 NULL, 354}; 355ATTRIBUTE_GROUPS(def); 356 357static struct kobj_type efivar_ktype = { 358 .release = efivar_release, 359 .sysfs_ops = &efivar_attr_ops, 360 .default_groups = def_groups, 361}; 362 363static ssize_t efivar_create(struct file *filp, struct kobject *kobj, 364 struct bin_attribute *bin_attr, 365 char *buf, loff_t pos, size_t count) 366{ 367 struct compat_efi_variable *compat = (struct compat_efi_variable *)buf; 368 struct efi_variable *new_var = (struct efi_variable *)buf; 369 struct efivar_entry *new_entry; 370 bool need_compat = in_compat_syscall(); 371 efi_char16_t *name; 372 unsigned long size; 373 u32 attributes; 374 u8 *data; 375 int err; 376 377 if (!capable(CAP_SYS_ADMIN)) 378 return -EACCES; 379 380 if (need_compat) { 381 if (count != sizeof(*compat)) 382 return -EINVAL; 383 384 attributes = compat->Attributes; 385 name = compat->VariableName; 386 size = compat->DataSize; 387 data = compat->Data; 388 } else { 389 if (count != sizeof(*new_var)) 390 return -EINVAL; 391 392 attributes = new_var->Attributes; 393 name = new_var->VariableName; 394 size = new_var->DataSize; 395 data = new_var->Data; 396 } 397 398 if ((attributes & ~EFI_VARIABLE_MASK) != 0 || 399 efivar_validate(new_var->VendorGuid, name, data, 400 size) == false) { 401 printk(KERN_ERR "efivars: Malformed variable content\n"); 402 return -EINVAL; 403 } 404 405 new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL); 406 if (!new_entry) 407 return -ENOMEM; 408 409 if (need_compat) 410 copy_out_compat(&new_entry->var, compat); 411 else 412 memcpy(&new_entry->var, new_var, sizeof(*new_var)); 413 414 err = efivar_entry_set(new_entry, attributes, size, 415 data, &efivar_sysfs_list); 416 if (err) { 417 if (err == -EEXIST) 418 err = -EINVAL; 419 goto out; 420 } 421 422 if (efivar_create_sysfs_entry(new_entry)) { 423 printk(KERN_WARNING "efivars: failed to create sysfs entry.\n"); 424 kfree(new_entry); 425 } 426 return count; 427 428out: 429 kfree(new_entry); 430 return err; 431} 432 433static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, 434 struct bin_attribute *bin_attr, 435 char *buf, loff_t pos, size_t count) 436{ 437 struct efi_variable *del_var = (struct efi_variable *)buf; 438 struct compat_efi_variable *compat; 439 struct efivar_entry *entry; 440 efi_char16_t *name; 441 efi_guid_t vendor; 442 int err = 0; 443 444 if (!capable(CAP_SYS_ADMIN)) 445 return -EACCES; 446 447 if (in_compat_syscall()) { 448 if (count != sizeof(*compat)) 449 return -EINVAL; 450 451 compat = (struct compat_efi_variable *)buf; 452 name = compat->VariableName; 453 vendor = compat->VendorGuid; 454 } else { 455 if (count != sizeof(*del_var)) 456 return -EINVAL; 457 458 name = del_var->VariableName; 459 vendor = del_var->VendorGuid; 460 } 461 462 if (efivar_entry_iter_begin()) 463 return -EINTR; 464 entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true); 465 if (!entry) 466 err = -EINVAL; 467 else if (__efivar_entry_delete(entry)) 468 err = -EIO; 469 470 if (err) { 471 efivar_entry_iter_end(); 472 return err; 473 } 474 475 if (!entry->scanning) { 476 efivar_entry_iter_end(); 477 efivar_unregister(entry); 478 } else 479 efivar_entry_iter_end(); 480 481 /* It's dead Jim.... */ 482 return count; 483} 484 485/** 486 * efivar_create_sysfs_entry - create a new entry in sysfs 487 * @new_var: efivar entry to create 488 * 489 * Returns 0 on success, negative error code on failure 490 */ 491static int 492efivar_create_sysfs_entry(struct efivar_entry *new_var) 493{ 494 int short_name_size; 495 char *short_name; 496 unsigned long utf8_name_size; 497 efi_char16_t *variable_name = new_var->var.VariableName; 498 int ret; 499 500 /* 501 * Length of the variable bytes in UTF8, plus the '-' separator, 502 * plus the GUID, plus trailing NUL 503 */ 504 utf8_name_size = ucs2_utf8size(variable_name); 505 short_name_size = utf8_name_size + 1 + EFI_VARIABLE_GUID_LEN + 1; 506 507 short_name = kmalloc(short_name_size, GFP_KERNEL); 508 if (!short_name) 509 return -ENOMEM; 510 511 ucs2_as_utf8(short_name, variable_name, short_name_size); 512 513 /* This is ugly, but necessary to separate one vendor's 514 private variables from another's. */ 515 short_name[utf8_name_size] = '-'; 516 efi_guid_to_str(&new_var->var.VendorGuid, 517 short_name + utf8_name_size + 1); 518 519 new_var->kobj.kset = efivars_kset; 520 521 ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype, 522 NULL, "%s", short_name); 523 kfree(short_name); 524 if (ret) { 525 kobject_put(&new_var->kobj); 526 return ret; 527 } 528 529 kobject_uevent(&new_var->kobj, KOBJ_ADD); 530 if (efivar_entry_add(new_var, &efivar_sysfs_list)) { 531 efivar_unregister(new_var); 532 return -EINTR; 533 } 534 535 return 0; 536} 537 538static int 539create_efivars_bin_attributes(void) 540{ 541 struct bin_attribute *attr; 542 int error; 543 544 /* new_var */ 545 attr = kzalloc(sizeof(*attr), GFP_KERNEL); 546 if (!attr) 547 return -ENOMEM; 548 549 attr->attr.name = "new_var"; 550 attr->attr.mode = 0200; 551 attr->write = efivar_create; 552 efivars_new_var = attr; 553 554 /* del_var */ 555 attr = kzalloc(sizeof(*attr), GFP_KERNEL); 556 if (!attr) { 557 error = -ENOMEM; 558 goto out_free; 559 } 560 attr->attr.name = "del_var"; 561 attr->attr.mode = 0200; 562 attr->write = efivar_delete; 563 efivars_del_var = attr; 564 565 sysfs_bin_attr_init(efivars_new_var); 566 sysfs_bin_attr_init(efivars_del_var); 567 568 /* Register */ 569 error = sysfs_create_bin_file(&efivars_kset->kobj, efivars_new_var); 570 if (error) { 571 printk(KERN_ERR "efivars: unable to create new_var sysfs file" 572 " due to error %d\n", error); 573 goto out_free; 574 } 575 576 error = sysfs_create_bin_file(&efivars_kset->kobj, efivars_del_var); 577 if (error) { 578 printk(KERN_ERR "efivars: unable to create del_var sysfs file" 579 " due to error %d\n", error); 580 sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var); 581 goto out_free; 582 } 583 584 return 0; 585out_free: 586 kfree(efivars_del_var); 587 efivars_del_var = NULL; 588 kfree(efivars_new_var); 589 efivars_new_var = NULL; 590 return error; 591} 592 593static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor, 594 unsigned long name_size, void *data) 595{ 596 struct efivar_entry *entry; 597 598 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 599 if (!entry) 600 return -ENOMEM; 601 602 memcpy(entry->var.VariableName, name, name_size); 603 memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); 604 605 efivar_create_sysfs_entry(entry); 606 607 return 0; 608} 609 610static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data) 611{ 612 int err = efivar_entry_remove(entry); 613 614 if (err) 615 return err; 616 efivar_unregister(entry); 617 return 0; 618} 619 620static void efivars_sysfs_exit(void) 621{ 622 /* Remove all entries and destroy */ 623 int err; 624 625 err = __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list, 626 NULL, NULL); 627 if (err) { 628 pr_err("efivars: Failed to destroy sysfs entries\n"); 629 return; 630 } 631 632 if (efivars_new_var) 633 sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var); 634 if (efivars_del_var) 635 sysfs_remove_bin_file(&efivars_kset->kobj, efivars_del_var); 636 kfree(efivars_new_var); 637 kfree(efivars_del_var); 638 kset_unregister(efivars_kset); 639} 640 641static int efivars_sysfs_init(void) 642{ 643 struct kobject *parent_kobj = efivars_kobject(); 644 int error = 0; 645 646 /* No efivars has been registered yet */ 647 if (!parent_kobj || !efivar_supports_writes()) 648 return 0; 649 650 printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION, 651 EFIVARS_DATE); 652 653 efivars_kset = kset_create_and_add("vars", NULL, parent_kobj); 654 if (!efivars_kset) { 655 printk(KERN_ERR "efivars: Subsystem registration failed.\n"); 656 return -ENOMEM; 657 } 658 659 efivar_init(efivars_sysfs_callback, NULL, true, &efivar_sysfs_list); 660 661 error = create_efivars_bin_attributes(); 662 if (error) { 663 efivars_sysfs_exit(); 664 return error; 665 } 666 667 return 0; 668} 669 670module_init(efivars_sysfs_init); 671module_exit(efivars_sysfs_exit);