Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI / sysfs: Extend ACPI sysfs to provide access to boot error region

The ACPI sysfs interface provides a way to read each ACPI table from
userspace via entries in /sys/firmware/acpi/tables/

The BERT table simply provides the size and address of the error
record in BIOS reserved memory and users may want access to this
record.

In an earlier age we might have used /dev/mem to retrieve this error
record, but many systems disable /dev/mem for security reasons.

Extend this driver to provide read-only access to the data via a
file in a new directory /sys/firmware/acpi/tables/data/BERT

Acked-by: Punit Agrawal <punit.agrawal@arm.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>

v4: fix typo reported by Punit
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Tony Luck and committed by
Rafael J. Wysocki
7dae6326 14ccee78

+79
+79
drivers/acpi/sysfs.c
··· 306 306 /* 307 307 * ACPI table sysfs I/F: 308 308 * /sys/firmware/acpi/tables/ 309 + * /sys/firmware/acpi/tables/data/ 309 310 * /sys/firmware/acpi/tables/dynamic/ 310 311 */ 311 312 312 313 static LIST_HEAD(acpi_table_attr_list); 313 314 static struct kobject *tables_kobj; 315 + static struct kobject *tables_data_kobj; 314 316 static struct kobject *dynamic_tables_kobj; 315 317 static struct kobject *hotplug_kobj; 316 318 ··· 325 323 int instance; 326 324 char filename[ACPI_NAME_SIZE+ACPI_INST_SIZE]; 327 325 struct list_head node; 326 + }; 327 + 328 + struct acpi_data_attr { 329 + struct bin_attribute attr; 330 + u64 addr; 328 331 }; 329 332 330 333 static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj, ··· 427 420 return AE_OK; 428 421 } 429 422 423 + static ssize_t acpi_data_show(struct file *filp, struct kobject *kobj, 424 + struct bin_attribute *bin_attr, char *buf, 425 + loff_t offset, size_t count) 426 + { 427 + struct acpi_data_attr *data_attr; 428 + void __iomem *base; 429 + ssize_t rc; 430 + 431 + data_attr = container_of(bin_attr, struct acpi_data_attr, attr); 432 + 433 + base = acpi_os_map_memory(data_attr->addr, data_attr->attr.size); 434 + if (!base) 435 + return -ENOMEM; 436 + rc = memory_read_from_buffer(buf, count, &offset, base, 437 + data_attr->attr.size); 438 + acpi_os_unmap_memory(base, data_attr->attr.size); 439 + 440 + return rc; 441 + } 442 + 443 + static int acpi_bert_data_init(void *th, struct acpi_data_attr *data_attr) 444 + { 445 + struct acpi_table_bert *bert = th; 446 + 447 + if (bert->header.length < sizeof(struct acpi_table_bert) || 448 + bert->region_length < sizeof(struct acpi_hest_generic_status)) { 449 + kfree(data_attr); 450 + return -EINVAL; 451 + } 452 + data_attr->addr = bert->address; 453 + data_attr->attr.size = bert->region_length; 454 + data_attr->attr.attr.name = "BERT"; 455 + 456 + return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr); 457 + } 458 + 459 + static struct acpi_data_obj { 460 + char *name; 461 + int (*fn)(void *, struct acpi_data_attr *); 462 + } acpi_data_objs[] = { 463 + { ACPI_SIG_BERT, acpi_bert_data_init }, 464 + }; 465 + 466 + #define NUM_ACPI_DATA_OBJS ARRAY_SIZE(acpi_data_objs) 467 + 468 + static int acpi_table_data_init(struct acpi_table_header *th) 469 + { 470 + struct acpi_data_attr *data_attr; 471 + int i; 472 + 473 + for (i = 0; i < NUM_ACPI_DATA_OBJS; i++) { 474 + if (ACPI_COMPARE_NAME(th->signature, acpi_data_objs[i].name)) { 475 + data_attr = kzalloc(sizeof(*data_attr), GFP_KERNEL); 476 + if (!data_attr) 477 + return -ENOMEM; 478 + sysfs_attr_init(&data_attr->attr.attr); 479 + data_attr->attr.read = acpi_data_show; 480 + data_attr->attr.attr.mode = 0400; 481 + return acpi_data_objs[i].fn(th, data_attr); 482 + } 483 + } 484 + return 0; 485 + } 486 + 430 487 static int acpi_tables_sysfs_init(void) 431 488 { 432 489 struct acpi_table_attr *table_attr; ··· 502 431 tables_kobj = kobject_create_and_add("tables", acpi_kobj); 503 432 if (!tables_kobj) 504 433 goto err; 434 + 435 + tables_data_kobj = kobject_create_and_add("data", tables_kobj); 436 + if (!tables_data_kobj) 437 + goto err_tables_data; 505 438 506 439 dynamic_tables_kobj = kobject_create_and_add("dynamic", tables_kobj); 507 440 if (!dynamic_tables_kobj) ··· 531 456 return ret; 532 457 } 533 458 list_add_tail(&table_attr->node, &acpi_table_attr_list); 459 + acpi_table_data_init(table_header); 534 460 } 535 461 536 462 kobject_uevent(tables_kobj, KOBJ_ADD); 463 + kobject_uevent(tables_data_kobj, KOBJ_ADD); 537 464 kobject_uevent(dynamic_tables_kobj, KOBJ_ADD); 538 465 539 466 return 0; 540 467 err_dynamic_tables: 468 + kobject_put(tables_data_kobj); 469 + err_tables_data: 541 470 kobject_put(tables_kobj); 542 471 err: 543 472 return -ENOMEM;