linux/drivers/firmware/efi/rci2-table.c
Ard Biesheuvel a470552ee8 efi: Don't attempt to map RCI2 config table if it doesn't exist
Commit:

  1c5fecb612 ("efi: Export Runtime Configuration Interface table to sysfs")

... added support for a Dell specific UEFI configuration table, but
failed to take into account that mapping the table should not be
attempted unless the table actually exists. If it doesn't exist,
the code usually fails silently unless pr_debug() prints are
enabled. However, on 32-bit PAE x86, the splat below is produced due
to the attempt to map the placeholder value EFI_INVALID_TABLE_ADDR
which we use for non-existing UEFI configuration tables, and which
equals ULONG_MAX.

   memremap attempted on mixed range 0x00000000ffffffff size: 0x1e
   WARNING: CPU: 1 PID: 1 at kernel/iomem.c:81 memremap+0x1a3/0x1c0
   Modules linked in:
   CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.4.2-smp-mine #1
   Hardware name: Hewlett-Packard HP Z400 Workstation/0B4Ch, BIOS 786G3 v03.61 03/05/2018
   EIP: memremap+0x1a3/0x1c0
  ...
   Call Trace:
    ? map_properties+0x473/0x473
    ? efi_rci2_sysfs_init+0x2c/0x154
    ? map_properties+0x473/0x473
    ? do_one_initcall+0x49/0x1d4
    ? parse_args+0x1e8/0x2a0
    ? do_early_param+0x7a/0x7a
    ? kernel_init_freeable+0x139/0x1c2
    ? rest_init+0x8e/0x8e
    ? kernel_init+0xd/0xf2
    ? ret_from_fork+0x2e/0x38

Fix this by checking whether the table exists before attempting to map it.

Reported-by: Richard Narron <comet.berkeley@gmail.com>
Tested-by: Richard Narron <comet.berkeley@gmail.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Cc: linux-efi@vger.kernel.org
Fixes: 1c5fecb612 ("efi: Export Runtime Configuration Interface table to sysfs")
Link: https://lkml.kernel.org/r/20191210090945.11501-2-ardb@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2019-12-10 12:13:02 +01:00

151 lines
3.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Export Runtime Configuration Interface Table Version 2 (RCI2)
* to sysfs
*
* Copyright (C) 2019 Dell Inc
* by Narendra K <Narendra.K@dell.com>
*
* System firmware advertises the address of the RCI2 Table via
* an EFI Configuration Table entry. This code retrieves the RCI2
* table from the address and exports it to sysfs as a binary
* attribute 'rci2' under /sys/firmware/efi/tables directory.
*/
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/efi.h>
#include <linux/types.h>
#include <linux/io.h>
#define RCI_SIGNATURE "_RC_"
struct rci2_table_global_hdr {
u16 type;
u16 resvd0;
u16 hdr_len;
u8 rci2_sig[4];
u16 resvd1;
u32 resvd2;
u32 resvd3;
u8 major_rev;
u8 minor_rev;
u16 num_of_structs;
u32 rci2_len;
u16 rci2_chksum;
} __packed;
static u8 *rci2_base;
static u32 rci2_table_len;
unsigned long rci2_table_phys __ro_after_init = EFI_INVALID_TABLE_ADDR;
static ssize_t raw_table_read(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t pos, size_t count)
{
memcpy(buf, attr->private + pos, count);
return count;
}
static BIN_ATTR(rci2, S_IRUSR, raw_table_read, NULL, 0);
static u16 checksum(void)
{
u8 len_is_odd = rci2_table_len % 2;
u32 chksum_len = rci2_table_len;
u16 *base = (u16 *)rci2_base;
u8 buf[2] = {0};
u32 offset = 0;
u16 chksum = 0;
if (len_is_odd)
chksum_len -= 1;
while (offset < chksum_len) {
chksum += *base;
offset += 2;
base++;
}
if (len_is_odd) {
buf[0] = *(u8 *)base;
chksum += *(u16 *)(buf);
}
return chksum;
}
static int __init efi_rci2_sysfs_init(void)
{
struct kobject *tables_kobj;
int ret = -ENOMEM;
if (rci2_table_phys == EFI_INVALID_TABLE_ADDR)
return 0;
rci2_base = memremap(rci2_table_phys,
sizeof(struct rci2_table_global_hdr),
MEMREMAP_WB);
if (!rci2_base) {
pr_debug("RCI2 table init failed - could not map RCI2 table\n");
goto err;
}
if (strncmp(rci2_base +
offsetof(struct rci2_table_global_hdr, rci2_sig),
RCI_SIGNATURE, 4)) {
pr_debug("RCI2 table init failed - incorrect signature\n");
ret = -ENODEV;
goto err_unmap;
}
rci2_table_len = *(u32 *)(rci2_base +
offsetof(struct rci2_table_global_hdr,
rci2_len));
memunmap(rci2_base);
if (!rci2_table_len) {
pr_debug("RCI2 table init failed - incorrect table length\n");
goto err;
}
rci2_base = memremap(rci2_table_phys, rci2_table_len, MEMREMAP_WB);
if (!rci2_base) {
pr_debug("RCI2 table - could not map RCI2 table\n");
goto err;
}
if (checksum() != 0) {
pr_debug("RCI2 table - incorrect checksum\n");
ret = -ENODEV;
goto err_unmap;
}
tables_kobj = kobject_create_and_add("tables", efi_kobj);
if (!tables_kobj) {
pr_debug("RCI2 table - tables_kobj creation failed\n");
goto err_unmap;
}
bin_attr_rci2.size = rci2_table_len;
bin_attr_rci2.private = rci2_base;
ret = sysfs_create_bin_file(tables_kobj, &bin_attr_rci2);
if (ret != 0) {
pr_debug("RCI2 table - rci2 sysfs bin file creation failed\n");
kobject_del(tables_kobj);
kobject_put(tables_kobj);
goto err_unmap;
}
return 0;
err_unmap:
memunmap(rci2_base);
err:
pr_debug("RCI2 table - sysfs initialization failed\n");
return ret;
}
late_initcall(efi_rci2_sysfs_init);