starlabs: add ACPI SMI bridge for EFI options
Add a Device NVS (DNVS) protocol and SMM handler to let ACPI read and write a restricted set of coreboot options stored in the UEFI variable store. ACPI fills DNVS and triggers an SMI via APM_CNT (0xB2). SMM performs the requested operation and updates DNVS with status and, for reads, the returned value. Signed-off-by: Sean Rhodes <sean@starlabs.systems> Change-Id: Ice0ac36f6d0e1de88daf7010cb1771453547619e Reviewed-on: https://review.coreboot.org/c/coreboot/+/91303 Reviewed-by: Matt DeVillier <matt.devillier@gmail.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
484e39c068
commit
3fa3818e41
5 changed files with 249 additions and 2 deletions
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
11
src/mainboard/starlabs/common/gnvs.c
Normal file
11
src/mainboard/starlabs/common/gnvs.c
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <acpi/acpi_gnvs.h>
|
||||
#include <starlabs/efi_option_smi.h>
|
||||
|
||||
size_t size_of_dnvs(void)
|
||||
{
|
||||
return sizeof(struct starlabs_dnvs_efiopt);
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef STARLABS_EFI_OPTION_SMI_H
|
||||
#define STARLABS_EFI_OPTION_SMI_H
|
||||
|
||||
#include <types.h>
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
188
src/mainboard/starlabs/common/smihandler.c
Normal file
188
src/mainboard/starlabs/common/smihandler.c
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <acpi/acpi_gnvs.h>
|
||||
#include <commonlib/helpers.h>
|
||||
#include <cpu/x86/smm.h>
|
||||
#include <ec/starlabs/merlin/ec.h>
|
||||
#if CONFIG(SOC_INTEL_COMMON_BLOCK_FAST_SPI)
|
||||
#include <cpu/intel/msr.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
#include <device/mmio.h>
|
||||
#include <intelblocks/fast_spi.h>
|
||||
#endif
|
||||
#include <option.h>
|
||||
#include <soc/nvs.h>
|
||||
#include <starlabs/efi_option_smi.h>
|
||||
#include <types.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue