diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-20 15:02:13 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-20 15:02:13 -1000 |
| commit | 9e947d96915af63e28896caae85a81b74855621b (patch) | |
| tree | 514dc090453484e7680cb530e25943c7b43009fd | |
| parent | e5d379e9c3148aaef37909ce6d4c3ca18fad0312 (diff) | |
| parent | f2e05ce9763feb1dd7da15afd2cc61df227ee85a (diff) | |
| download | lwn-master.tar.gz lwn-master.zip | |
Merge tag 'chrome-platform-firmware-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linuxHEADmaster
Pull chrome platform firmware loader updates from Tzung-Bi Shih:
"New:
- Rename drivers/firmware/google/ -> drivers/firmware/coreboot/ and
corresponding Kconfig names
- Separate firmware_attributes_class from drivers/platform/x86/ to
drivers/firmware/ so that it is available for non-x86 platforms
- Add CFR (coreboot form representation) driver to expose the
attributes via sysfs
Improvements:
- Use named initializers for acpi_device_id"
* tag 'chrome-platform-firmware-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux:
firmware: coreboot: Add CFR firmware attributes driver
firmware: Move firmware attributes class helper
firmware: coreboot: Use named initializers for acpi_device_id
firmware: Rename google firmware directory to coreboot
| -rw-r--r-- | MAINTAINERS | 34 | ||||
| -rw-r--r-- | drivers/firmware/Kconfig | 5 | ||||
| -rw-r--r-- | drivers/firmware/Makefile | 3 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/Kconfig (renamed from drivers/firmware/google/Kconfig) | 89 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/Makefile | 15 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/cbmem.c (renamed from drivers/firmware/google/cbmem.c) | 0 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/coreboot-cfr.c | 1204 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/coreboot_table.c (renamed from drivers/firmware/google/coreboot_table.c) | 4 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/coreboot_table.h (renamed from drivers/firmware/google/coreboot_table.h) | 0 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/framebuffer-coreboot.c (renamed from drivers/firmware/google/framebuffer-coreboot.c) | 0 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/gsmi.c (renamed from drivers/firmware/google/gsmi.c) | 0 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/memconsole-coreboot.c (renamed from drivers/firmware/google/memconsole-coreboot.c) | 0 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/memconsole-x86-legacy.c (renamed from drivers/firmware/google/memconsole-x86-legacy.c) | 0 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/memconsole.c (renamed from drivers/firmware/google/memconsole.c) | 0 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/memconsole.h (renamed from drivers/firmware/google/memconsole.h) | 6 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/vpd.c (renamed from drivers/firmware/google/vpd.c) | 0 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/vpd_decode.c (renamed from drivers/firmware/google/vpd_decode.c) | 0 | ||||
| -rw-r--r-- | drivers/firmware/coreboot/vpd_decode.h (renamed from drivers/firmware/google/vpd_decode.h) | 0 | ||||
| -rw-r--r-- | drivers/firmware/firmware_attributes_class.c (renamed from drivers/platform/x86/firmware_attributes_class.c) | 2 | ||||
| -rw-r--r-- | drivers/firmware/google/Makefile | 14 | ||||
| -rw-r--r-- | drivers/platform/x86/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/platform/x86/Makefile | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/asus-armoury.c | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/dell/dell-wmi-sysman/sysman.c | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/hp/hp-bioscfg/bioscfg.c | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/lenovo/think-lmi.c | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/lenovo/wmi-other.c | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/samsung-galaxybook.c | 2 | ||||
| -rw-r--r-- | include/linux/firmware_attributes.h (renamed from drivers/platform/x86/firmware_attributes_class.h) | 6 |
29 files changed, 1335 insertions, 64 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 4f9bcbb88fd0..917f3f72e31b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6716,6 +6716,22 @@ F: tools/testing/selftests/cgroup/test_hugetlb_memcg.c F: tools/testing/selftests/cgroup/test_kmem.c F: tools/testing/selftests/cgroup/test_memcontrol.c +COREBOOT CFR FIRMWARE ATTRIBUTES DRIVER +M: Sean Rhodes <sean@starlabs.systems> +L: chrome-platform@lists.linux.dev +S: Maintained +F: drivers/firmware/coreboot/coreboot-cfr.c + +COREBOOT FIRMWARE DRIVERS +M: Tzung-Bi Shih <tzungbi@kernel.org> +R: Brian Norris <briannorris@chromium.org> +R: Julius Werner <jwerner@chromium.org> +L: chrome-platform@lists.linux.dev +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git +F: drivers/firmware/coreboot/ +F: include/linux/coreboot.h + CORETEMP HARDWARE MONITORING DRIVER L: linux-hwmon@vger.kernel.org S: Orphan @@ -10196,6 +10212,14 @@ F: include/linux/firewire.h F: include/uapi/linux/firewire*.h F: tools/firewire/ +FIRMWARE ATTRIBUTES CLASS +M: Hans de Goede <hansg@kernel.org> +M: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/firmware/firmware_attributes_class.c +F: include/linux/firmware_attributes.h + FIRMWARE FRAMEWORK FOR ARMV8-A M: Sudeep Holla <sudeep.holla@kernel.org> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) @@ -11150,16 +11174,6 @@ S: Maintained F: Documentation/networking/device_drivers/ethernet/google/gve.rst F: drivers/net/ethernet/google -GOOGLE FIRMWARE DRIVERS -M: Tzung-Bi Shih <tzungbi@kernel.org> -R: Brian Norris <briannorris@chromium.org> -R: Julius Werner <jwerner@chromium.org> -L: chrome-platform@lists.linux.dev -S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git -F: drivers/firmware/google/ -F: include/linux/coreboot.h - GOOGLE TENSOR SoC SUPPORT M: Peter Griffin <peter.griffin@linaro.org> R: André Draszik <andre.draszik@linaro.org> diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index f1a76d482429..43e2e2a64594 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -8,6 +8,9 @@ menu "Firmware Drivers" source "drivers/firmware/arm_scmi/Kconfig" +config FW_ATTR_CLASS + tristate + config ARM_SCPI_PROTOCOL tristate "ARM System Control and Power Interface (SCPI) Message Protocol" depends on ARM || ARM64 || COMPILE_TEST @@ -300,7 +303,7 @@ source "drivers/firmware/arm_ffa/Kconfig" source "drivers/firmware/arm_rmm/Kconfig" source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/cirrus/Kconfig" -source "drivers/firmware/google/Kconfig" +source "drivers/firmware/coreboot/Kconfig" source "drivers/firmware/efi/Kconfig" source "drivers/firmware/imx/Kconfig" source "drivers/firmware/meson/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 54755db0cf6e..50b338313152 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o +obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o obj-$(CONFIG_SYSFB) += sysfb.o obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o obj-$(CONFIG_TH1520_AON_PROTOCOL) += thead,th1520-aon.o @@ -31,7 +32,7 @@ obj-y += broadcom/ obj-y += cirrus/ obj-y += meson/ obj-y += microchip/ -obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ +obj-$(CONFIG_COREBOOT_FIRMWARE) += coreboot/ obj-y += efi/ obj-y += imx/ obj-y += psci/ diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/coreboot/Kconfig index b78c644fa253..acfc7d187228 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/coreboot/Kconfig @@ -1,13 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-only -menuconfig GOOGLE_FIRMWARE - bool "Google Firmware Drivers" +menuconfig COREBOOT_FIRMWARE + bool "coreboot firmware drivers" + default GOOGLE_FIRMWARE default n help - These firmware drivers are used by Google servers, - Chromebooks and other devices using coreboot firmware. + These firmware drivers are used by devices running coreboot + firmware, including Chromebooks and some Google servers. + They include shared support for coreboot table entries and + drivers for CBMEM, framebuffer, firmware console and VPD records. If in doubt, say "N". -if GOOGLE_FIRMWARE +if COREBOOT_FIRMWARE config GOOGLE_SMI tristate "SMI interface for Google platforms" @@ -19,9 +22,10 @@ config GOOGLE_SMI driver provides an interface for reading and writing NVRAM variables. -config GOOGLE_CBMEM +config COREBOOT_CBMEM tristate "CBMEM entries in sysfs" - depends on GOOGLE_COREBOOT_TABLE + depends on COREBOOT_TABLE + default GOOGLE_CBMEM help CBMEM is a downwards-growing memory region created by the Coreboot BIOS containing tagged data structures from the @@ -34,9 +38,10 @@ config GOOGLE_CBMEM memory for each entry in sysfs under /sys/bus/coreboot/devices/cbmem-<id>. -config GOOGLE_COREBOOT_TABLE +config COREBOOT_TABLE tristate "Coreboot Table Access" depends on HAS_IOMEM && (ACPI || OF) + default GOOGLE_COREBOOT_TABLE help This option enables the coreboot_table module, which provides other firmware modules access to the coreboot table. The coreboot table @@ -44,42 +49,90 @@ config GOOGLE_COREBOOT_TABLE device tree node /firmware/coreboot. If unsure say N. -config GOOGLE_MEMCONSOLE +config COREBOOT_CFR + tristate "coreboot CFR firmware attributes" + depends on EFI + depends on COREBOOT_TABLE + depends on HAS_IOPORT + select CRC32 + select FW_ATTR_CLASS + help + This driver parses the coreboot CFR table and exposes runtime EFI + variable-backed options through the firmware-attributes sysfs + interface. + + If you have a system running coreboot with CFR runtime + configuration support, say Y or M here. + +config COREBOOT_MEMCONSOLE_COMMON tristate - depends on GOOGLE_MEMCONSOLE_X86_LEGACY || GOOGLE_MEMCONSOLE_COREBOOT + depends on GOOGLE_MEMCONSOLE_X86_LEGACY || COREBOOT_MEMCONSOLE + default GOOGLE_MEMCONSOLE config GOOGLE_MEMCONSOLE_X86_LEGACY tristate "Firmware Memory Console - X86 Legacy support" depends on X86 && ACPI && DMI - select GOOGLE_MEMCONSOLE + select COREBOOT_MEMCONSOLE_COMMON help This option enables the kernel to search for a firmware log in the EBDA on Google servers. If found, this log is exported to userland in the file /sys/firmware/log. + This driver is limited to x86 systems that expose the legacy + Google EBDA console format. -config GOOGLE_FRAMEBUFFER_COREBOOT +config COREBOOT_FRAMEBUFFER tristate "Coreboot Framebuffer" - depends on GOOGLE_COREBOOT_TABLE + depends on COREBOOT_TABLE + default GOOGLE_FRAMEBUFFER_COREBOOT help This option enables the kernel to search for a framebuffer in the coreboot table. If found, it is registered with a platform device of type coreboot-framebuffer. Using the old device of type simple-framebuffer is deprecated. -config GOOGLE_MEMCONSOLE_COREBOOT +config COREBOOT_MEMCONSOLE tristate "Firmware Memory Console" - depends on GOOGLE_COREBOOT_TABLE - select GOOGLE_MEMCONSOLE + depends on COREBOOT_TABLE + select COREBOOT_MEMCONSOLE_COMMON + default GOOGLE_MEMCONSOLE_COREBOOT help This option enables the kernel to search for a firmware log in the coreboot table. If found, this log is exported to userland in the file /sys/firmware/log. + This is the coreboot-table backed variant of the firmware memory + console driver. config GOOGLE_VPD tristate "Vital Product Data" - depends on GOOGLE_COREBOOT_TABLE + depends on COREBOOT_TABLE help This option enables the kernel to expose the content of Google VPD under /sys/firmware/vpd. + VPD records carry firmware-provided key/value data used by + Google devices. + +endif # COREBOOT_FIRMWARE + +config GOOGLE_FIRMWARE + bool + transitional + +config GOOGLE_CBMEM + tristate + transitional + +config GOOGLE_COREBOOT_TABLE + tristate + transitional + +config GOOGLE_MEMCONSOLE + tristate + transitional + +config GOOGLE_FRAMEBUFFER_COREBOOT + tristate + transitional -endif # GOOGLE_FIRMWARE +config GOOGLE_MEMCONSOLE_COREBOOT + tristate + transitional diff --git a/drivers/firmware/coreboot/Makefile b/drivers/firmware/coreboot/Makefile new file mode 100644 index 000000000000..a08d167a95a8 --- /dev/null +++ b/drivers/firmware/coreboot/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_GOOGLE_SMI) += gsmi.o +obj-$(CONFIG_COREBOOT_TABLE) += coreboot_table.o +obj-$(CONFIG_COREBOOT_CFR) += coreboot-cfr.o +obj-$(CONFIG_COREBOOT_FRAMEBUFFER) += framebuffer-coreboot.o +obj-$(CONFIG_COREBOOT_MEMCONSOLE_COMMON) += memconsole.o +obj-$(CONFIG_COREBOOT_MEMCONSOLE) += memconsole-coreboot.o +obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o + +# Must come after coreboot_table.o, as this driver depends on that bus type. +obj-$(CONFIG_COREBOOT_CBMEM) += cbmem.o + +vpd-sysfs-y := vpd.o vpd_decode.o +obj-$(CONFIG_GOOGLE_VPD) += vpd-sysfs.o diff --git a/drivers/firmware/google/cbmem.c b/drivers/firmware/coreboot/cbmem.c index 4d20477ed9b2..4d20477ed9b2 100644 --- a/drivers/firmware/google/cbmem.c +++ b/drivers/firmware/coreboot/cbmem.c diff --git a/drivers/firmware/coreboot/coreboot-cfr.c b/drivers/firmware/coreboot/coreboot-cfr.c new file mode 100644 index 000000000000..2d9ea5d213d8 --- /dev/null +++ b/drivers/firmware/coreboot/coreboot-cfr.c @@ -0,0 +1,1204 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * coreboot CFR firmware attributes driver. + * + * Parses LB_TAG_CFR_ROOT records from the coreboot table and exposes + * runtime EFI variable-backed options through the firmware-attributes class. + */ + +#include <linux/array_size.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/compiler_attributes.h> +#include <linux/container_of.h> +#include <linux/crc32.h> +#include <linux/ctype.h> +#include <linux/device.h> +#include <linux/device-id/coreboot.h> +#include <linux/efi.h> +#include <linux/err.h> +#include <linux/firmware_attributes.h> +#include <linux/io.h> +#include <linux/kdev_t.h> +#include <linux/kobject.h> +#include <linux/kstrtox.h> +#include <linux/limits.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/types.h> + +#include "coreboot_table.h" + +#define DRIVER_NAME "coreboot-cfr" + +#define LB_TAG_CFR_ROOT 0x47 +#define CFR_VERSION 0 + +enum cfr_tags { + CFR_TAG_OPTION_FORM = 1, + CFR_TAG_ENUM_VALUE = 2, + CFR_TAG_OPTION_ENUM = 3, + CFR_TAG_OPTION_NUMBER = 4, + CFR_TAG_OPTION_BOOL = 5, + CFR_TAG_VARCHAR_OPT_NAME = 7, + CFR_TAG_VARCHAR_UI_NAME = 8, + CFR_TAG_RUNTIME_APPLY = 13, +}; + +enum cfr_option_flags { + CFR_OPTFLAG_READONLY = BIT(0), + CFR_OPTFLAG_INACTIVE = BIT(1), + CFR_OPTFLAG_SUPPRESS = BIT(2), + CFR_OPTFLAG_VOLATILE = BIT(3), + CFR_OPTFLAG_RUNTIME = BIT(4), +}; + +enum cfr_runtime_apply_method { + CFR_RUNTIME_APPLY_NONE = 0, + CFR_RUNTIME_APPLY_APM_CNT = 1, +}; + +struct lb_cfr { + u32 tag; + u32 size; + u32 version; + u32 checksum; +} __packed; + +struct lb_cfr_varbinary { + u32 tag; + u32 size; + u32 data_length; +} __packed; + +struct lb_cfr_enum_value { + u32 tag; + u32 size; + u32 value; +} __packed; + +struct lb_cfr_runtime_apply { + u32 tag; + u32 size; + u32 method; + u32 id; +} __packed; + +struct lb_cfr_numeric_option { + u32 tag; + u32 size; + cb_u64 object_id; + cb_u64 dependency_id; + u32 flags; + u32 default_value; + u32 min; + u32 max; + u32 step; + u32 display_flags; +} __packed; + +struct lb_cfr_option_form { + u32 tag; + u32 size; + cb_u64 object_id; + cb_u64 dependency_id; + u32 flags; +} __packed; + +#define COREBOOT_CFR_OPT_SKIP_FLAGS \ + (CFR_OPTFLAG_SUPPRESS | CFR_OPTFLAG_VOLATILE) + +#define COREBOOT_CFR_OPT_READ_ONLY_FLAGS \ + (CFR_OPTFLAG_READONLY | CFR_OPTFLAG_INACTIVE) + +#define COREBOOT_CFR_EFI_ATTRS \ + (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | \ + EFI_VARIABLE_RUNTIME_ACCESS) + +#define COREBOOT_CFR_APM_CNT_PORT 0xb2 +#define COREBOOT_CFR_APM_STS_PORT 0xb3 +#define COREBOOT_CFR_APM_APPLY_CMD 0xe3 +#define COREBOOT_CFR_MAX_FORM_DEPTH 16 + +static efi_guid_t coreboot_cfr_guid = EFI_GUID(0xceae4c1d, 0x335b, 0x4685, + 0xa4, 0xa0, 0xfc, 0x4a, + 0x94, 0xee, 0xa0, 0x85); + +enum coreboot_cfr_setting_type { + COREBOOT_CFR_SETTING_ENUM, + COREBOOT_CFR_SETTING_NUMBER, + COREBOOT_CFR_SETTING_BOOL, +}; + +struct coreboot_cfr_enum { + char *label; + u32 value; +}; + +struct coreboot_cfr_setting { + struct kobject kobj; + struct list_head node; + struct coreboot_cfr_drvdata *drvdata; + enum coreboot_cfr_setting_type type; + char *name; + char *display_name; + struct coreboot_cfr_enum *values; + unsigned int n_values; + u32 default_value; + u32 min; + u32 max; + u32 step; + u32 runtime_apply_method; + u32 runtime_apply_id; + bool read_only; +}; + +struct coreboot_cfr_drvdata { + struct device *class_dev; + struct kset *attrs_kset; + struct list_head settings; + /* Serializes EFI variable writes and the matching runtime apply hook. */ + struct mutex lock; + bool efi_writes_supported; + bool pending_reboot; +}; + +struct coreboot_cfr_iterator { + const u8 *cursor; + size_t remaining; +}; + +static struct coreboot_cfr_setting *to_coreboot_cfr_setting(struct kobject *kobj) +{ + return container_of(kobj, struct coreboot_cfr_setting, kobj); +} + +static bool coreboot_cfr_string_is_valid_name(const char *name) +{ + return name && name[0] && !strchr(name, '/') && + strcmp(name, "pending_reboot") != 0; +} + +static bool coreboot_cfr_string_is_valid_label(const char *label) +{ + return label && !strchr(label, ';') && !strchr(label, '\n'); +} + +static char *coreboot_cfr_string_dup(const struct lb_cfr_varbinary *str) +{ + const char *data = (const char *)(str + 1); + size_t len = str->data_length; + + if (len && !data[len - 1]) + len--; + + return kmemdup_nul(data, len, GFP_KERNEL); +} + +static const struct coreboot_table_entry * +coreboot_cfr_next_entry(struct coreboot_cfr_iterator *iterator) +{ + const struct coreboot_table_entry *entry; + + if (!iterator->remaining) + return NULL; + + if (iterator->remaining < sizeof(*entry)) + return ERR_PTR(-EINVAL); + + entry = (const struct coreboot_table_entry *)iterator->cursor; + if (entry->size < sizeof(*entry) || entry->size > iterator->remaining) + return ERR_PTR(-EINVAL); + + iterator->cursor += entry->size; + iterator->remaining -= entry->size; + + return entry; +} + +static const struct coreboot_table_entry * +coreboot_cfr_child_entry(const void *base, size_t len, u32 tag) +{ + struct coreboot_cfr_iterator iterator = { + .cursor = base, + .remaining = len, + }; + const struct coreboot_table_entry *entry; + + for (;;) { + entry = coreboot_cfr_next_entry(&iterator); + if (IS_ERR_OR_NULL(entry)) + return entry; + + if (entry->tag == tag) + return entry; + } +} + +static const struct lb_cfr_varbinary * +coreboot_cfr_child_string(const void *base, size_t len, u32 tag) +{ + const struct lb_cfr_varbinary *str; + const struct coreboot_table_entry *entry; + + entry = coreboot_cfr_child_entry(base, len, tag); + if (IS_ERR(entry)) + return ERR_CAST(entry); + if (!entry) + return NULL; + + if (entry->size < sizeof(*str)) + return ERR_PTR(-EINVAL); + + str = (const struct lb_cfr_varbinary *)entry; + if (str->data_length > entry->size - sizeof(*str)) + return ERR_PTR(-EINVAL); + + return str; +} + +static const struct lb_cfr_runtime_apply * +coreboot_cfr_child_runtime_apply(const void *base, size_t len) +{ + const struct lb_cfr_runtime_apply *runtime_apply; + const struct coreboot_table_entry *entry; + + entry = coreboot_cfr_child_entry(base, len, CFR_TAG_RUNTIME_APPLY); + if (IS_ERR(entry)) + return ERR_CAST(entry); + if (!entry) + return NULL; + + if (entry->size < sizeof(*runtime_apply)) + return ERR_PTR(-EINVAL); + + runtime_apply = (const struct lb_cfr_runtime_apply *)entry; + if (runtime_apply->method == CFR_RUNTIME_APPLY_APM_CNT && + runtime_apply->id > U8_MAX) + return ERR_PTR(-EINVAL); + + return runtime_apply; +} + +static efi_char16_t *coreboot_cfr_efi_name(const char *name) +{ + size_t len, i; + + len = strlen(name); + if (len >= EFI_VAR_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + efi_char16_t *efi_name __free(kfree) = + kcalloc(len + 1, sizeof(*efi_name), GFP_KERNEL); + if (!efi_name) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < len; i++) { + if (!isascii(name[i])) + return ERR_PTR(-EINVAL); + efi_name[i] = name[i]; + } + + return no_free_ptr(efi_name); +} + +/* Caller must hold the efivar lock. */ +static int coreboot_cfr_read_efi_value(efi_char16_t *efi_name, u32 *value, + u32 *attrs) +{ + unsigned long size = sizeof(__le32); + efi_status_t status; + __le32 data; + u32 attr; + + status = efivar_get_variable(efi_name, &coreboot_cfr_guid, &attr, + &size, &data); + if (status != EFI_SUCCESS) + return efi_status_to_err(status); + + if (size != sizeof(data)) + return -EINVAL; + + if (!(attr & EFI_VARIABLE_RUNTIME_ACCESS)) + return -EOPNOTSUPP; + + *value = le32_to_cpu(data); + if (attrs) + *attrs = attr; + + return 0; +} + +static int coreboot_cfr_read_value(const struct coreboot_cfr_setting *setting, + u32 *value, u32 *attrs) +{ + efi_char16_t *efi_name __free(kfree) = + coreboot_cfr_efi_name(setting->name); + int ret; + + if (IS_ERR(efi_name)) + return PTR_ERR(no_free_ptr(efi_name)); + + ret = efivar_lock(); + if (ret) + return ret; + + ret = coreboot_cfr_read_efi_value(efi_name, value, attrs); + efivar_unlock(); + + return ret; +} + +/* Caller must hold the efivar lock. */ +static int coreboot_cfr_write_efi_value(efi_char16_t *efi_name, u32 value, + u32 attrs) +{ + efi_status_t status; + __le32 data; + + if (!efivar_supports_writes()) + return -EROFS; + + data = cpu_to_le32(value); + status = efivar_set_variable_locked(efi_name, &coreboot_cfr_guid, attrs, + sizeof(data), &data, false); + if (status != EFI_SUCCESS) + return efi_status_to_err(status); + + return 0; +} + +static int coreboot_cfr_apply_runtime(struct coreboot_cfr_setting *setting) +{ + u8 status; + + if (setting->runtime_apply_method != CFR_RUNTIME_APPLY_APM_CNT) + return -EOPNOTSUPP; + + outb((u8)setting->runtime_apply_id, COREBOOT_CFR_APM_STS_PORT); + outb(COREBOOT_CFR_APM_APPLY_CMD, COREBOOT_CFR_APM_CNT_PORT); + status = inb(COREBOOT_CFR_APM_STS_PORT); + if (status) + return -EIO; + + return 0; +} + +static int coreboot_cfr_write_value(struct coreboot_cfr_setting *setting, + u32 value) +{ + efi_char16_t *efi_name; + u32 attrs; + u32 old; + int restore_ret; + int ret; + + if (setting->read_only) + return -EACCES; + + efi_name = coreboot_cfr_efi_name(setting->name); + if (IS_ERR(efi_name)) + return PTR_ERR(efi_name); + + mutex_lock(&setting->drvdata->lock); + + ret = efivar_lock(); + if (ret) + goto out_unlock_mutex; + + ret = coreboot_cfr_read_efi_value(efi_name, &old, &attrs); + if (ret) + goto out_unlock_efi; + + if ((attrs & COREBOOT_CFR_EFI_ATTRS) != COREBOOT_CFR_EFI_ATTRS) { + ret = -EOPNOTSUPP; + goto out_unlock_efi; + } + + if (old == value) + goto out_unlock_efi; + + ret = coreboot_cfr_write_efi_value(efi_name, value, attrs); + if (ret) + goto out_unlock_efi; + + ret = coreboot_cfr_apply_runtime(setting); + if (ret == -EOPNOTSUPP) { + /* EFI changed; firmware will consume it after reboot. */ + setting->drvdata->pending_reboot = true; + ret = 0; + } else if (ret) { + restore_ret = coreboot_cfr_write_efi_value(efi_name, old, attrs); + if (restore_ret) { + setting->drvdata->pending_reboot = true; + ret = restore_ret; + } else { + goto out_unlock_efi; + } + } + + efivar_unlock(); + kobject_uevent(&setting->drvdata->class_dev->kobj, KOBJ_CHANGE); + mutex_unlock(&setting->drvdata->lock); + kfree(efi_name); + return ret; + +out_unlock_efi: + efivar_unlock(); +out_unlock_mutex: + mutex_unlock(&setting->drvdata->lock); + kfree(efi_name); + return ret; +} + +static const char * +coreboot_cfr_label_from_value(const struct coreboot_cfr_setting *setting, + u32 value) +{ + unsigned int i; + + for (i = 0; i < setting->n_values; i++) { + if (setting->values[i].value == value) + return setting->values[i].label; + } + + return NULL; +} + +static int coreboot_cfr_parse_value(struct coreboot_cfr_setting *setting, + const char *label, u32 *value_out) +{ + unsigned int i; + + for (i = 0; i < setting->n_values; i++) { + if (!sysfs_streq(label, setting->values[i].label)) + continue; + + *value_out = setting->values[i].value; + return 0; + } + + return kstrtou32(label, 0, value_out); +} + +static bool coreboot_cfr_value_is_valid(struct coreboot_cfr_setting *setting, + u32 value) +{ + u32 delta; + + if (setting->type != COREBOOT_CFR_SETTING_NUMBER) { + /* Enum and bool values are valid only if they have a label. */ + return coreboot_cfr_label_from_value(setting, value); + } + + if (value < setting->min || value > setting->max) + return false; + + if (!setting->step) + return true; + + delta = value - setting->min; + return delta % setting->step == 0; +} + +static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj); + + if (setting->type == COREBOOT_CFR_SETTING_NUMBER) + return sysfs_emit(buf, "integer\n"); + + return sysfs_emit(buf, "enumeration\n"); +} + +static ssize_t display_name_language_code_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "en_US.UTF-8\n"); +} + +static ssize_t display_name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj); + + return sysfs_emit(buf, "%s\n", setting->display_name); +} + +static ssize_t possible_values_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj); + ssize_t len = 0; + unsigned int i; + + for (i = 0; i < setting->n_values; i++) { + len += sysfs_emit_at(buf, len, "%s%s", i ? ";" : "", + setting->values[i].label); + } + + len += sysfs_emit_at(buf, len, "\n"); + return len; +} + +static ssize_t min_value_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj); + + return sysfs_emit(buf, "%u\n", setting->min); +} + +static ssize_t max_value_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj); + + return sysfs_emit(buf, "%u\n", setting->max); +} + +static ssize_t scalar_increment_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj); + + return sysfs_emit(buf, "%u\n", setting->step); +} + +static ssize_t default_value_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj); + const char *label; + + if (setting->type == COREBOOT_CFR_SETTING_NUMBER) + return sysfs_emit(buf, "%u\n", setting->default_value); + + label = coreboot_cfr_label_from_value(setting, setting->default_value); + if (!label) + return sysfs_emit(buf, "%u\n", setting->default_value); + + return sysfs_emit(buf, "%s\n", label); +} + +static ssize_t current_value_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj); + const char *label; + u32 value; + int ret; + + ret = coreboot_cfr_read_value(setting, &value, NULL); + if (ret) + return ret; + + if (setting->type == COREBOOT_CFR_SETTING_NUMBER) + return sysfs_emit(buf, "%u\n", value); + + label = coreboot_cfr_label_from_value(setting, value); + if (!label) + return -EINVAL; + + return sysfs_emit(buf, "%s\n", label); +} + +static ssize_t current_value_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj); + u32 value; + int ret; + + ret = coreboot_cfr_parse_value(setting, buf, &value); + if (ret) + return ret; + + if (!coreboot_cfr_value_is_valid(setting, value)) + return -EINVAL; + + ret = coreboot_cfr_write_value(setting, value); + if (ret) + return ret; + + return count; +} + +static struct kobj_attribute type_attr = __ATTR_RO(type); +static struct kobj_attribute display_name_language_code_attr = + __ATTR_RO(display_name_language_code); +static struct kobj_attribute display_name_attr = __ATTR_RO(display_name); +static struct kobj_attribute possible_values_attr = __ATTR_RO(possible_values); +static struct kobj_attribute min_value_attr = __ATTR_RO(min_value); +static struct kobj_attribute max_value_attr = __ATTR_RO(max_value); +static struct kobj_attribute scalar_increment_attr = __ATTR_RO(scalar_increment); +static struct kobj_attribute default_value_attr = __ATTR_RO(default_value); +static struct kobj_attribute current_value_attr = __ATTR_RW(current_value); + +static struct attribute *coreboot_cfr_setting_attrs[] = { + &type_attr.attr, + &display_name_language_code_attr.attr, + &display_name_attr.attr, + &possible_values_attr.attr, + &min_value_attr.attr, + &max_value_attr.attr, + &scalar_increment_attr.attr, + &default_value_attr.attr, + ¤t_value_attr.attr, + NULL, +}; + +static umode_t coreboot_cfr_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct coreboot_cfr_setting *setting = to_coreboot_cfr_setting(kobj); + + if (setting->type == COREBOOT_CFR_SETTING_NUMBER && + attr == &possible_values_attr.attr) + return 0; + + if (setting->type != COREBOOT_CFR_SETTING_NUMBER && + (attr == &min_value_attr.attr || attr == &max_value_attr.attr || + attr == &scalar_increment_attr.attr)) + return 0; + + if (setting->read_only && attr == ¤t_value_attr.attr) + return 0444; + + return attr->mode; +} + +static const struct attribute_group coreboot_cfr_setting_group = { + .attrs = coreboot_cfr_setting_attrs, + .is_visible = coreboot_cfr_attr_is_visible, +}; + +static void coreboot_cfr_free_setting(struct coreboot_cfr_setting *setting) +{ + unsigned int i; + + for (i = 0; i < setting->n_values; i++) + kfree(setting->values[i].label); + + kfree(setting->values); + kfree(setting->display_name); + kfree(setting->name); + kfree(setting); +} + +static void coreboot_cfr_setting_release(struct kobject *kobj) +{ + coreboot_cfr_free_setting(to_coreboot_cfr_setting(kobj)); +} + +static const struct kobj_type coreboot_cfr_setting_ktype = { + .release = coreboot_cfr_setting_release, + .sysfs_ops = &kobj_sysfs_ops, +}; + +static ssize_t pending_reboot_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct device *dev = kobj_to_dev(kobj->parent); + struct coreboot_cfr_drvdata *data; + + data = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", data->pending_reboot); +} + +static struct kobj_attribute pending_reboot_attr = __ATTR_RO(pending_reboot); + +static int coreboot_cfr_copy_bool_values(struct coreboot_cfr_setting *setting) +{ + static const struct coreboot_cfr_enum bool_values[] = { + { .label = "Disabled", .value = 0 }, + { .label = "Enabled", .value = 1 }, + }; + unsigned int i; + + setting->values = kcalloc(ARRAY_SIZE(bool_values), sizeof(*setting->values), + GFP_KERNEL); + if (!setting->values) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(bool_values); i++) { + setting->values[i].label = kstrdup(bool_values[i].label, + GFP_KERNEL); + if (!setting->values[i].label) + return -ENOMEM; + setting->values[i].value = bool_values[i].value; + setting->n_values++; + } + + return 0; +} + +static int coreboot_cfr_count_enum_values(const void *base, size_t len) +{ + struct coreboot_cfr_iterator iterator = { + .cursor = base, + .remaining = len, + }; + const struct coreboot_table_entry *entry; + int count = 0; + + for (;;) { + entry = coreboot_cfr_next_entry(&iterator); + if (IS_ERR(entry)) + return PTR_ERR(entry); + if (!entry) + return count; + + if (entry->tag == CFR_TAG_ENUM_VALUE) + count++; + } +} + +static int coreboot_cfr_copy_enum_values(struct coreboot_cfr_setting *setting, + const void *base, size_t len) +{ + struct coreboot_cfr_iterator iterator = { + .cursor = base, + .remaining = len, + }; + const struct lb_cfr_enum_value *enum_value; + const struct lb_cfr_varbinary *label; + const struct coreboot_table_entry *entry; + struct coreboot_cfr_enum *value; + int count; + + count = coreboot_cfr_count_enum_values(base, len); + if (count <= 0) + return count ?: -EINVAL; + + setting->values = kcalloc(count, sizeof(*setting->values), GFP_KERNEL); + if (!setting->values) + return -ENOMEM; + + for (;;) { + entry = coreboot_cfr_next_entry(&iterator); + if (IS_ERR(entry)) + return PTR_ERR(entry); + if (!entry) + return 0; + + if (entry->tag != CFR_TAG_ENUM_VALUE) + continue; + + if (entry->size < sizeof(*enum_value)) + return -EINVAL; + + enum_value = (const struct lb_cfr_enum_value *)entry; + label = coreboot_cfr_child_string(enum_value + 1, + enum_value->size - sizeof(*enum_value), + CFR_TAG_VARCHAR_UI_NAME); + if (IS_ERR(label)) + return PTR_ERR(label); + if (!label) + return -EINVAL; + + value = &setting->values[setting->n_values]; + value->label = coreboot_cfr_string_dup(label); + if (!value->label) + return -ENOMEM; + if (!coreboot_cfr_string_is_valid_label(value->label)) { + kfree(value->label); + value->label = NULL; + return -EINVAL; + } + + value->value = enum_value->value; + setting->n_values++; + } +} + +static bool +coreboot_cfr_possible_values_fit(const struct coreboot_cfr_setting *setting) +{ + size_t len = 1; /* Trailing newline. */ + size_t label_len; + unsigned int i; + + for (i = 0; i < setting->n_values; i++) { + if (len >= PAGE_SIZE) + return false; + + if (i) + len++; + + label_len = strlen(setting->values[i].label); + if (label_len >= PAGE_SIZE - len) + return false; + + len += label_len; + } + + return true; +} + +static int coreboot_cfr_setting_is_usable(struct coreboot_cfr_setting *setting) +{ + u32 value; + int ret; + + ret = coreboot_cfr_read_value(setting, &value, NULL); + if (ret) + return ret; + + if (!coreboot_cfr_value_is_valid(setting, value)) + return -EINVAL; + + return 0; +} + +static int coreboot_cfr_register_setting(struct coreboot_cfr_drvdata *data, + struct coreboot_cfr_setting *setting) +{ + int ret; + + ret = kobject_init_and_add(&setting->kobj, &coreboot_cfr_setting_ktype, + &data->attrs_kset->kobj, "%s", setting->name); + if (ret) + goto err_put_kobj; + + ret = sysfs_create_group(&setting->kobj, &coreboot_cfr_setting_group); + if (ret) + goto err_put_kobj; + + list_add_tail(&setting->node, &data->settings); + return 0; + +err_put_kobj: + kobject_put(&setting->kobj); + return ret; +} + +static int coreboot_cfr_add_numeric_option(struct coreboot_cfr_drvdata *data, + const struct lb_cfr_numeric_option *option, + bool parent_read_only) +{ + const struct lb_cfr_varbinary *name; + const struct lb_cfr_varbinary *display_name; + const struct lb_cfr_runtime_apply *runtime_apply; + const void *child_base = option + 1; + struct coreboot_cfr_setting *setting; + size_t child_len = option->size - sizeof(*option); + int ret; + + if (!(option->flags & CFR_OPTFLAG_RUNTIME)) + return 0; + + if (option->flags & COREBOOT_CFR_OPT_SKIP_FLAGS) + return 0; + + if (option->dependency_id) + return 0; + + setting = kzalloc_obj(*setting, GFP_KERNEL); + if (!setting) + return -ENOMEM; + + INIT_LIST_HEAD(&setting->node); + setting->drvdata = data; + setting->default_value = option->default_value; + setting->min = option->min; + setting->max = option->max; + setting->step = option->step ?: 1; + setting->read_only = + (option->flags & COREBOOT_CFR_OPT_READ_ONLY_FLAGS) || + !data->efi_writes_supported || parent_read_only; + + runtime_apply = coreboot_cfr_child_runtime_apply(child_base, child_len); + if (IS_ERR(runtime_apply)) { + ret = PTR_ERR(runtime_apply); + goto err_put_setting; + } + + if (runtime_apply && runtime_apply->method == CFR_RUNTIME_APPLY_APM_CNT) { + setting->runtime_apply_method = runtime_apply->method; + setting->runtime_apply_id = runtime_apply->id; + } + + name = coreboot_cfr_child_string(child_base, child_len, + CFR_TAG_VARCHAR_OPT_NAME); + if (IS_ERR(name)) { + ret = PTR_ERR(name); + goto err_put_setting; + } + if (!name) { + ret = -EINVAL; + goto err_put_setting; + } + + setting->name = coreboot_cfr_string_dup(name); + if (!setting->name) { + ret = -ENOMEM; + goto err_put_setting; + } + + if (!coreboot_cfr_string_is_valid_name(setting->name)) { + ret = -EINVAL; + goto err_put_setting; + } + + display_name = coreboot_cfr_child_string(child_base, child_len, + CFR_TAG_VARCHAR_UI_NAME); + if (IS_ERR(display_name)) { + ret = PTR_ERR(display_name); + goto err_put_setting; + } + if (display_name) + setting->display_name = coreboot_cfr_string_dup(display_name); + else + setting->display_name = kstrdup(setting->name, GFP_KERNEL); + if (!setting->display_name) { + ret = -ENOMEM; + goto err_put_setting; + } + + switch (option->tag) { + case CFR_TAG_OPTION_BOOL: + setting->type = COREBOOT_CFR_SETTING_BOOL; + setting->min = 0; + setting->max = 1; + setting->step = 1; + ret = coreboot_cfr_copy_bool_values(setting); + break; + case CFR_TAG_OPTION_ENUM: + setting->type = COREBOOT_CFR_SETTING_ENUM; + ret = coreboot_cfr_copy_enum_values(setting, child_base, + child_len); + break; + case CFR_TAG_OPTION_NUMBER: + setting->type = COREBOOT_CFR_SETTING_NUMBER; + if (setting->max < setting->min) + ret = -EINVAL; + else + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + + if (ret) + goto err_put_setting; + /* possible_values must be returned completely in one sysfs read. */ + if (setting->type != COREBOOT_CFR_SETTING_NUMBER && + !coreboot_cfr_possible_values_fit(setting)) { + ret = 0; + goto err_put_setting; + } + + ret = coreboot_cfr_setting_is_usable(setting); + if (ret) { + /* + * CFR may describe options without a matching, readable runtime + * EFI variable. Skip those, but propagate transient EFI failures. + */ + if (ret == -ENOENT || ret == -EINVAL || ret == -EOPNOTSUPP || + ret == -ENAMETOOLONG) + ret = 0; + goto err_put_setting; + } + + /* The kobject release callback owns setting after this call. */ + return coreboot_cfr_register_setting(data, setting); + +err_put_setting: + coreboot_cfr_free_setting(setting); + return ret; +} + +static int coreboot_cfr_parse_records(struct coreboot_cfr_drvdata *data, + const void *base, size_t len, + unsigned int depth, + bool parent_read_only) +{ + struct coreboot_cfr_iterator iterator = { + .cursor = base, + .remaining = len, + }; + const struct lb_cfr_option_form *form; + const struct lb_cfr_numeric_option *option; + const struct coreboot_table_entry *entry; + const void *child_base; + size_t child_len; + int ret; + + for (;;) { + entry = coreboot_cfr_next_entry(&iterator); + if (IS_ERR(entry)) + return PTR_ERR(entry); + if (!entry) + return 0; + + switch (entry->tag) { + case CFR_TAG_OPTION_FORM: + if (entry->size < sizeof(struct lb_cfr_option_form)) + return -EINVAL; + + form = (const struct lb_cfr_option_form *)entry; + if (form->dependency_id || + form->flags & CFR_OPTFLAG_SUPPRESS) + break; + + if (depth >= COREBOOT_CFR_MAX_FORM_DEPTH) + return -E2BIG; + + child_base = form + 1; + child_len = entry->size - sizeof(struct lb_cfr_option_form); + ret = coreboot_cfr_parse_records(data, child_base, + child_len, depth + 1, + parent_read_only || + (form->flags & + COREBOOT_CFR_OPT_READ_ONLY_FLAGS)); + if (ret) + return ret; + break; + case CFR_TAG_OPTION_ENUM: + case CFR_TAG_OPTION_NUMBER: + case CFR_TAG_OPTION_BOOL: + option = (const struct lb_cfr_numeric_option *)entry; + if (entry->size < sizeof(*option)) + return -EINVAL; + ret = coreboot_cfr_add_numeric_option(data, option, + parent_read_only); + if (ret) + return ret; + break; + default: + /* Ignore unsupported, child-only and future record types. */ + break; + } + } +} + +static void coreboot_cfr_unregister_settings(struct coreboot_cfr_drvdata *data) +{ + struct coreboot_cfr_setting *setting, *tmp; + + list_for_each_entry_safe(setting, tmp, &data->settings, node) { + sysfs_remove_group(&setting->kobj, &coreboot_cfr_setting_group); + list_del(&setting->node); + kobject_put(&setting->kobj); + } +} + +static int coreboot_cfr_probe(struct coreboot_device *dev) +{ + const struct lb_cfr *root = (const struct lb_cfr *)dev->raw; + struct coreboot_cfr_drvdata *data; + size_t payload_len; + int ret; + + if (dev->entry.size < sizeof(*root)) + return -EINVAL; + + if (root->tag != LB_TAG_CFR_ROOT || root->version != CFR_VERSION) + return -EINVAL; + + if (root->size < sizeof(*root) || root->size > dev->entry.size) + return -EINVAL; + + payload_len = root->size - sizeof(*root); + if (crc32_be(0, root + 1, payload_len) != root->checksum) + return -EBADMSG; + + if (!efivar_is_available()) + return -EPROBE_DEFER; + + data = devm_kzalloc(&dev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = efivar_lock(); + if (ret) + return ret; + data->efi_writes_supported = efivar_supports_writes(); + efivar_unlock(); + + INIT_LIST_HEAD(&data->settings); + ret = devm_mutex_init(&dev->dev, &data->lock); + if (ret) + return ret; + + dev_set_drvdata(&dev->dev, data); + + data->class_dev = device_create(&firmware_attributes_class, NULL, + MKDEV(0, 0), NULL, DRIVER_NAME); + if (IS_ERR(data->class_dev)) + return PTR_ERR(data->class_dev); + dev_set_drvdata(data->class_dev, data); + + data->attrs_kset = kset_create_and_add("attributes", NULL, + &data->class_dev->kobj); + if (!data->attrs_kset) { + ret = -ENOMEM; + goto err_unregister_dev; + } + + ret = sysfs_create_file(&data->attrs_kset->kobj, + &pending_reboot_attr.attr); + if (ret) + goto err_unregister_attrs; + + ret = coreboot_cfr_parse_records(data, root + 1, payload_len, 0, false); + if (ret) + goto err_unregister_settings; + + if (list_empty(&data->settings)) { + ret = -ENODEV; + goto err_unregister_settings; + } + + return 0; + +err_unregister_settings: + coreboot_cfr_unregister_settings(data); + sysfs_remove_file(&data->attrs_kset->kobj, &pending_reboot_attr.attr); +err_unregister_attrs: + kset_unregister(data->attrs_kset); +err_unregister_dev: + device_unregister(data->class_dev); + return ret; +} + +static void coreboot_cfr_remove(struct coreboot_device *dev) +{ + struct coreboot_cfr_drvdata *data = dev_get_drvdata(&dev->dev); + + coreboot_cfr_unregister_settings(data); + sysfs_remove_file(&data->attrs_kset->kobj, &pending_reboot_attr.attr); + kset_unregister(data->attrs_kset); + device_unregister(data->class_dev); +} + +static const struct coreboot_device_id coreboot_cfr_ids[] = { + { .tag = LB_TAG_CFR_ROOT }, + { } +}; +MODULE_DEVICE_TABLE(coreboot, coreboot_cfr_ids); + +static struct coreboot_driver coreboot_cfr_driver = { + .probe = coreboot_cfr_probe, + .remove = coreboot_cfr_remove, + .drv = { + .name = DRIVER_NAME, + }, + .id_table = coreboot_cfr_ids, +}; +module_coreboot_driver(coreboot_cfr_driver); + +MODULE_AUTHOR("Sean Rhodes <sean@starlabs.systems>"); +MODULE_DESCRIPTION("coreboot CFR firmware attributes driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("EFIVAR"); diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/coreboot/coreboot_table.c index 25ee8cf53429..2c8c928fbb33 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/coreboot/coreboot_table.c @@ -250,8 +250,8 @@ static void coreboot_table_remove(struct platform_device *pdev) #ifdef CONFIG_ACPI static const struct acpi_device_id cros_coreboot_acpi_match[] = { - { "GOOGCB00", 0 }, - { "BOOT0000", 0 }, + { .id = "GOOGCB00" }, + { .id = "BOOT0000" }, { } }; MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match); diff --git a/drivers/firmware/google/coreboot_table.h b/drivers/firmware/coreboot/coreboot_table.h index 616ca3903e5c..616ca3903e5c 100644 --- a/drivers/firmware/google/coreboot_table.h +++ b/drivers/firmware/coreboot/coreboot_table.h diff --git a/drivers/firmware/google/framebuffer-coreboot.c b/drivers/firmware/coreboot/framebuffer-coreboot.c index 1a6d4ac6db31..1a6d4ac6db31 100644 --- a/drivers/firmware/google/framebuffer-coreboot.c +++ b/drivers/firmware/coreboot/framebuffer-coreboot.c diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/coreboot/gsmi.c index e21e7408cc6d..e21e7408cc6d 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/coreboot/gsmi.c diff --git a/drivers/firmware/google/memconsole-coreboot.c b/drivers/firmware/coreboot/memconsole-coreboot.c index 75e372732c67..75e372732c67 100644 --- a/drivers/firmware/google/memconsole-coreboot.c +++ b/drivers/firmware/coreboot/memconsole-coreboot.c diff --git a/drivers/firmware/google/memconsole-x86-legacy.c b/drivers/firmware/coreboot/memconsole-x86-legacy.c index a0974c376985..a0974c376985 100644 --- a/drivers/firmware/google/memconsole-x86-legacy.c +++ b/drivers/firmware/coreboot/memconsole-x86-legacy.c diff --git a/drivers/firmware/google/memconsole.c b/drivers/firmware/coreboot/memconsole.c index 6138a1653ec5..6138a1653ec5 100644 --- a/drivers/firmware/google/memconsole.c +++ b/drivers/firmware/coreboot/memconsole.c diff --git a/drivers/firmware/google/memconsole.h b/drivers/firmware/coreboot/memconsole.h index aaff2b72b606..58899c2abaaa 100644 --- a/drivers/firmware/google/memconsole.h +++ b/drivers/firmware/coreboot/memconsole.h @@ -7,8 +7,8 @@ * Copyright 2017 Google Inc. */ -#ifndef __FIRMWARE_GOOGLE_MEMCONSOLE_H -#define __FIRMWARE_GOOGLE_MEMCONSOLE_H +#ifndef __FIRMWARE_COREBOOT_MEMCONSOLE_H +#define __FIRMWARE_COREBOOT_MEMCONSOLE_H #include <linux/types.h> @@ -33,4 +33,4 @@ int memconsole_sysfs_init(void); */ void memconsole_exit(void); -#endif /* __FIRMWARE_GOOGLE_MEMCONSOLE_H */ +#endif /* __FIRMWARE_COREBOOT_MEMCONSOLE_H */ diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/coreboot/vpd.c index fbb5a8d7cd0d..fbb5a8d7cd0d 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/coreboot/vpd.c diff --git a/drivers/firmware/google/vpd_decode.c b/drivers/firmware/coreboot/vpd_decode.c index 5c6f2a74f104..5c6f2a74f104 100644 --- a/drivers/firmware/google/vpd_decode.c +++ b/drivers/firmware/coreboot/vpd_decode.c diff --git a/drivers/firmware/google/vpd_decode.h b/drivers/firmware/coreboot/vpd_decode.h index 8dbe41cac599..8dbe41cac599 100644 --- a/drivers/firmware/google/vpd_decode.h +++ b/drivers/firmware/coreboot/vpd_decode.h diff --git a/drivers/platform/x86/firmware_attributes_class.c b/drivers/firmware/firmware_attributes_class.c index 736e96c186d9..503ecc288086 100644 --- a/drivers/platform/x86/firmware_attributes_class.c +++ b/drivers/firmware/firmware_attributes_class.c @@ -3,7 +3,7 @@ /* Firmware attributes class helper module */ #include <linux/module.h> -#include "firmware_attributes_class.h" +#include <linux/firmware_attributes.h> const struct class firmware_attributes_class = { .name = "firmware-attributes", diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile deleted file mode 100644 index 8151e323cc43..000000000000 --- a/drivers/firmware/google/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-$(CONFIG_GOOGLE_SMI) += gsmi.o -obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o -obj-$(CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT) += framebuffer-coreboot.o -obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o -obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o -obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o - -# Must come after coreboot_table.o, as this driver depends on that bus type. -obj-$(CONFIG_GOOGLE_CBMEM) += cbmem.o - -vpd-sysfs-y := vpd.o vpd_decode.o -obj-$(CONFIG_GOOGLE_VPD) += vpd-sysfs.o diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 957034f39e4e..4851bf45157d 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -925,9 +925,6 @@ config DASHARO_ACPI source "drivers/platform/x86/x86-android-tablets/Kconfig" -config FW_ATTR_CLASS - tristate - config INTEL_IMR bool "Intel Isolated Memory Region support" depends on X86_INTEL_QUARK && IOSF_MBI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 872ac3842391..cab20a4145e3 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -63,8 +63,6 @@ obj-$(CONFIG_X86_PLATFORM_DRIVERS_HP) += hp/ # Hewlett Packard Enterprise obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o -obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o - # IBM Thinkpad (before 2005) obj-$(CONFIG_IBM_RTL) += ibm_rtl.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c index 93d9665717af..fb382de5b57b 100644 --- a/drivers/platform/x86/asus-armoury.c +++ b/drivers/platform/x86/asus-armoury.c @@ -21,6 +21,7 @@ #include <linux/dmi.h> #include <linux/err.h> #include <linux/errno.h> +#include <linux/firmware_attributes.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/kmod.h> @@ -35,7 +36,6 @@ #include <linux/sysfs.h> #include "asus-armoury.h" -#include "firmware_attributes_class.h" #define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index ab46a023cc34..ae89670f73b4 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -10,13 +10,13 @@ #include <linux/align.h> #include <linux/fs.h> #include <linux/dmi.h> +#include <linux/firmware_attributes.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/wmi.h> #include "dell-wmi-sysman.h" -#include "../../firmware_attributes_class.h" #define MAX_TYPES 4 #include <linux/nls.h> diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c index 309634c1cc20..d5a2d0963bbf 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c @@ -7,6 +7,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/firmware_attributes.h> #include <linux/fs.h> #include <linux/module.h> #include <linux/kernel.h> @@ -14,7 +15,6 @@ #include <linux/string.h> #include <linux/wmi.h> #include "bioscfg.h" -#include "../../firmware_attributes_class.h" #include <linux/nls.h> #include <linux/errno.h> diff --git a/drivers/platform/x86/lenovo/think-lmi.c b/drivers/platform/x86/lenovo/think-lmi.c index a0e3fa766e37..ebfc6182986e 100644 --- a/drivers/platform/x86/lenovo/think-lmi.c +++ b/drivers/platform/x86/lenovo/think-lmi.c @@ -14,13 +14,13 @@ #include <linux/acpi.h> #include <linux/array_size.h> #include <linux/errno.h> +#include <linux/firmware_attributes.h> #include <linux/fs.h> #include <linux/mutex.h> #include <linux/string_helpers.h> #include <linux/types.h> #include <linux/dmi.h> #include <linux/wmi.h> -#include "../firmware_attributes_class.h" #include "think-lmi.h" static bool debug_support; diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index fbb32bf404f2..b175971bb308 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -33,6 +33,7 @@ #include <linux/container_of.h> #include <linux/device.h> #include <linux/export.h> +#include <linux/firmware_attributes.h> #include <linux/gfp_types.h> #include <linux/hwmon.h> #include <linux/idr.h> @@ -50,7 +51,6 @@ #include "wmi-capdata.h" #include "wmi-events.h" #include "wmi-helpers.h" -#include "../firmware_attributes_class.h" #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B" diff --git a/drivers/platform/x86/samsung-galaxybook.c b/drivers/platform/x86/samsung-galaxybook.c index 99f72b204a51..88ef11004ad0 100644 --- a/drivers/platform/x86/samsung-galaxybook.c +++ b/drivers/platform/x86/samsung-galaxybook.c @@ -14,6 +14,7 @@ #include <linux/acpi.h> #include <linux/bits.h> #include <linux/err.h> +#include <linux/firmware_attributes.h> #include <linux/i8042.h> #include <linux/init.h> #include <linux/input.h> @@ -28,7 +29,6 @@ #include <linux/uuid.h> #include <linux/workqueue.h> #include <acpi/battery.h> -#include "firmware_attributes_class.h" #define DRIVER_NAME "samsung-galaxybook" diff --git a/drivers/platform/x86/firmware_attributes_class.h b/include/linux/firmware_attributes.h index d27abe54fcf9..d4026ba1fcc2 100644 --- a/drivers/platform/x86/firmware_attributes_class.h +++ b/include/linux/firmware_attributes.h @@ -2,11 +2,11 @@ /* Firmware attributes class helper module */ -#ifndef FW_ATTR_CLASS_H -#define FW_ATTR_CLASS_H +#ifndef _LINUX_FIRMWARE_ATTRIBUTES_H +#define _LINUX_FIRMWARE_ATTRIBUTES_H #include <linux/device/class.h> extern const struct class firmware_attributes_class; -#endif /* FW_ATTR_CLASS_H */ +#endif /* _LINUX_FIRMWARE_ATTRIBUTES_H */ |
