diff --git a/src/mainboard/starlabs/common/Kconfig b/src/mainboard/starlabs/common/Kconfig index adeda59bdf..8a670142c0 100644 --- a/src/mainboard/starlabs/common/Kconfig +++ b/src/mainboard/starlabs/common/Kconfig @@ -17,8 +17,15 @@ config MB_COMMON_DIR string default "starlabs/common" -config SOC_INTEL_COMMON_BLOCK_SMM_TCO_INTRUDER_SMI_ENABLE - default n +config STARLABS_ACPI_EFI_OPTION_SMI + bool "Expose coreboot options to ACPI via SMI (UEFI var store)" + depends on USE_UEFI_VARIABLE_STORE + depends on HAVE_ACPI_TABLES + default y + help + Provide ACPI methods that proxy a restricted set of coreboot options + (e.g. keyboard backlight and trackpad state) to the UEFI variable + store via an SMM APMC SMI handler. source "src/mainboard/starlabs/common/hda/Kconfig" diff --git a/src/mainboard/starlabs/common/Makefile.mk b/src/mainboard/starlabs/common/Makefile.mk index 90c0d94d5a..b12b7610d3 100644 --- a/src/mainboard/starlabs/common/Makefile.mk +++ b/src/mainboard/starlabs/common/Makefile.mk @@ -7,3 +7,6 @@ subdirs-$(CONFIG_VENDOR_STARLABS) += pin_mux subdirs-$(CONFIG_VENDOR_STARLABS) += smbios CPPFLAGS_common += -I$(src)/mainboard/starlabs/common/include + +ramstage-$(CONFIG_STARLABS_ACPI_EFI_OPTION_SMI) += gnvs.c +smm-$(CONFIG_STARLABS_ACPI_EFI_OPTION_SMI) += smihandler.c diff --git a/src/mainboard/starlabs/common/gnvs.c b/src/mainboard/starlabs/common/gnvs.c new file mode 100644 index 0000000000..7e119470ab --- /dev/null +++ b/src/mainboard/starlabs/common/gnvs.c @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +#include +#include + +size_t size_of_dnvs(void) +{ + return sizeof(struct starlabs_dnvs_efiopt); +} diff --git a/src/mainboard/starlabs/common/include/starlabs/efi_option_smi.h b/src/mainboard/starlabs/common/include/starlabs/efi_option_smi.h new file mode 100644 index 0000000000..cdd6bf4a89 --- /dev/null +++ b/src/mainboard/starlabs/common/include/starlabs/efi_option_smi.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef STARLABS_EFI_OPTION_SMI_H +#define STARLABS_EFI_OPTION_SMI_H + +#include + +/* + * ACPI <-> SMM protocol for reading/writing a restricted set of coreboot + * options in the UEFI variable store. + * + * ACPI fills DNVS, then triggers an APMC SMI by writing STARLABS_APMC_CMD + * to the APM_CNT port (0xB2). SMM reads DNVS, performs the operation, and + * updates DNVS with status and (for reads) the returned value. + */ + +#define STARLABS_APMC_CMD_EFI_OPTION 0xE2 + +enum starlabs_efiopt_cmd { + STARLABS_EFIOPT_CMD_GET = 1, + STARLABS_EFIOPT_CMD_SET = 2, +}; + +enum starlabs_efiopt_id { + STARLABS_EFIOPT_ID_FN_LOCK_STATE = 1, + STARLABS_EFIOPT_ID_TRACKPAD_STATE = 2, + STARLABS_EFIOPT_ID_KBL_BRIGHTNESS = 3, + STARLABS_EFIOPT_ID_KBL_STATE = 4, +}; + +struct starlabs_dnvs_efiopt { + uint32_t cmd; + uint32_t id; + uint32_t value; + uint32_t status; +} __packed; + +#endif /* STARLABS_EFI_OPTION_SMI_H */ diff --git a/src/mainboard/starlabs/common/smihandler.c b/src/mainboard/starlabs/common/smihandler.c new file mode 100644 index 0000000000..d74cce1008 --- /dev/null +++ b/src/mainboard/starlabs/common/smihandler.c @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#if CONFIG(SOC_INTEL_COMMON_BLOCK_FAST_SPI) +#include +#include +#include +#include +#endif +#include +#include +#include +#include + +#if CONFIG(SOC_INTEL_COMMON_BLOCK_FAST_SPI) +static void set_insmm_sts(const bool enable_writes) +{ + msr_t msr = { + .lo = read32p(0xfed30880), + .hi = 0, + }; + + if (enable_writes) + msr.lo |= 1; + else + msr.lo &= ~1; + + wrmsr(MSR_SPCL_CHIPSET_USAGE, msr); +} +#endif + +static bool chipset_disable_wp(void) +{ +#if CONFIG(SOC_INTEL_COMMON_BLOCK_FAST_SPI) + const bool wp_enabled = !fast_spi_wpd_status(); + + if (wp_enabled) { + set_insmm_sts(true); + /* + * As per BWG, clearing "SPI_BIOS_CONTROL_SYNC_SS" + * bit is a must prior setting SPI_BIOS_CONTROL_WPD" bit + * to avoid 3-strike error. + */ + fast_spi_clear_sync_smi_status(); + fast_spi_disable_wp(); + } + + return wp_enabled; +#endif + + return false; +} + +static void chipset_enable_wp(void) +{ +#if CONFIG(SOC_INTEL_COMMON_BLOCK_FAST_SPI) + fast_spi_enable_wp(); + set_insmm_sts(false); +#endif +} + +static struct starlabs_dnvs_efiopt *get_starlabs_dnvs_efiopt(void) +{ + if (!gnvs) + return NULL; + + const size_t gnvs_size = ALIGN_UP(sizeof(struct global_nvs), sizeof(uint64_t)); + + uint8_t *base = (uint8_t *)gnvs; + base += gnvs_size; + return (struct starlabs_dnvs_efiopt *)base; +} + +struct starlabs_efiopt_entry { + const char *name; + enum starlabs_efiopt_id id; + uint32_t fallback; +}; + +static const struct starlabs_efiopt_entry *find_efiopt(enum starlabs_efiopt_id id) +{ + static const struct starlabs_efiopt_entry opts[] = { + { + .name = "fn_lock_state", + .id = STARLABS_EFIOPT_ID_FN_LOCK_STATE, + .fallback = LOCKED, + }, + { + .name = "trackpad_state", + .id = STARLABS_EFIOPT_ID_TRACKPAD_STATE, + .fallback = TRACKPAD_ENABLED, + }, + { + .name = "kbl_brightness", + .id = STARLABS_EFIOPT_ID_KBL_BRIGHTNESS, + .fallback = CONFIG(EC_STARLABS_KBL_LEVELS) ? KBL_LOW : KBL_ON, + }, + { + .name = "kbl_state", + .id = STARLABS_EFIOPT_ID_KBL_STATE, + .fallback = KBL_ENABLED, + }, + }; + + for (size_t i = 0; i < ARRAY_SIZE(opts); i++) { + if (opts[i].id == id) + return &opts[i]; + } + + return NULL; +} + +static enum cb_err normalize_value(enum starlabs_efiopt_id id, uint32_t *value) +{ + if (!value) + return CB_ERR_ARG; + + switch (id) { + case STARLABS_EFIOPT_ID_FN_LOCK_STATE: + if (*value == UNLOCKED || *value == LOCKED) + return CB_SUCCESS; + return CB_ERR_ARG; + case STARLABS_EFIOPT_ID_TRACKPAD_STATE: + /* Normalize "re-enabled" to "enabled". */ + if (*value == 0x11) + *value = TRACKPAD_ENABLED; + if (*value == TRACKPAD_ENABLED || *value == TRACKPAD_DISABLED) + return CB_SUCCESS; + return CB_ERR_ARG; + case STARLABS_EFIOPT_ID_KBL_BRIGHTNESS: + if (*value == KBL_ON || *value == KBL_OFF || *value == KBL_LOW || + *value == KBL_HIGH) + return CB_SUCCESS; + return CB_ERR_ARG; + case STARLABS_EFIOPT_ID_KBL_STATE: + if (*value == KBL_DISABLED || *value == KBL_ENABLED) + return CB_SUCCESS; + return CB_ERR_ARG; + default: + return CB_ERR_ARG; + } +} + +int mainboard_smi_apmc(u8 data) +{ + if (data != STARLABS_APMC_CMD_EFI_OPTION) + return 0; + + struct starlabs_dnvs_efiopt *dnvs = get_starlabs_dnvs_efiopt(); + if (!dnvs) + return 0; + + const enum starlabs_efiopt_cmd cmd = dnvs->cmd; + const enum starlabs_efiopt_id id = dnvs->id; + const struct starlabs_efiopt_entry *opt = find_efiopt(id); + + if (!opt) { + dnvs->status = CB_ERR_ARG; + return 1; + } + + switch (cmd) { + case STARLABS_EFIOPT_CMD_GET: + dnvs->value = get_uint_option(opt->name, opt->fallback); + dnvs->status = CB_SUCCESS; + break; + case STARLABS_EFIOPT_CMD_SET: { + uint32_t value = dnvs->value; + dnvs->status = normalize_value(id, &value); + if (dnvs->status != CB_SUCCESS) + break; + + const bool wp_enabled = chipset_disable_wp(); + dnvs->status = set_uint_option(opt->name, value); + if (wp_enabled) + chipset_enable_wp(); + break; + } + default: + dnvs->status = CB_ERR_ARG; + break; + } + + return 1; +}