ec/system76: Support lockdown based on EC security state
Change-Id: I00701aa1397c24efe6f2d163822968b528f5b915
This commit is contained in:
@@ -18,6 +18,11 @@ config EC_SYSTEM76_EC_DGPU
|
|||||||
bool
|
bool
|
||||||
default n
|
default n
|
||||||
|
|
||||||
|
config EC_SYSTEM76_EC_LOCKDOWN
|
||||||
|
depends on EC_SYSTEM76_EC
|
||||||
|
bool
|
||||||
|
default n
|
||||||
|
|
||||||
config EC_SYSTEM76_EC_OLED
|
config EC_SYSTEM76_EC_OLED
|
||||||
depends on EC_SYSTEM76_EC
|
depends on EC_SYSTEM76_EC
|
||||||
bool
|
bool
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
ifeq ($(CONFIG_EC_SYSTEM76_EC),y)
|
ifeq ($(CONFIG_EC_SYSTEM76_EC),y)
|
||||||
|
|
||||||
all-y += system76_ec.c
|
all-y += system76_ec.c
|
||||||
|
ramstage-$(CONFIG_EC_SYSTEM76_EC_LOCKDOWN) += lockdown.c
|
||||||
smm-$(CONFIG_DEBUG_SMI) += system76_ec.c
|
smm-$(CONFIG_DEBUG_SMI) += system76_ec.c
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
61
src/ec/system76/ec/lockdown.c
Normal file
61
src/ec/system76/ec/lockdown.c
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
|
||||||
|
#include <bootstate.h>
|
||||||
|
#include <commonlib/region.h>
|
||||||
|
#include <fmap.h>
|
||||||
|
#include <spi_flash.h>
|
||||||
|
|
||||||
|
#include "system76_ec.h"
|
||||||
|
|
||||||
|
static int protect_region_by_name(const char *name)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
struct region region;
|
||||||
|
|
||||||
|
res = fmap_locate_area(name, ®ion);
|
||||||
|
if (res < 0) {
|
||||||
|
printk(BIOS_ERR, "fmap_locate_area '%s' failed: %d\n", name, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = spi_flash_ctrlr_protect_region(
|
||||||
|
boot_device_spi_flash(),
|
||||||
|
®ion,
|
||||||
|
WRITE_PROTECT
|
||||||
|
);
|
||||||
|
if (res < 0) {
|
||||||
|
printk(BIOS_ERR, "spi_flash_ctrlr_protect_region '%s' failed: %d\n", name, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(BIOS_INFO, "protected '%s'\n", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lock(void *unused)
|
||||||
|
{
|
||||||
|
uint8_t state = SYSTEM76_EC_SECURITY_STATE_UNLOCK;
|
||||||
|
if (system76_ec_security_get(&state) < 0) {
|
||||||
|
printk(BIOS_INFO, "failed to get security state, assuming unlocked\n");
|
||||||
|
state = SYSTEM76_EC_SECURITY_STATE_UNLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(BIOS_INFO, "security state: %d\n", state);
|
||||||
|
if (state != SYSTEM76_EC_SECURITY_STATE_UNLOCK) {
|
||||||
|
// Protect WP_RO region, which should contain FMAP and COREBOOT
|
||||||
|
protect_region_by_name("WP_RO");
|
||||||
|
// Protect RW_MRC_CACHE region, this must be done after it is written
|
||||||
|
protect_region_by_name("RW_MRC_CACHE");
|
||||||
|
//TODO: protect entire flash except when in SMM?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keep in sync with mrc_cache.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if CONFIG(MRC_WRITE_NV_LATE)
|
||||||
|
BOOT_STATE_INIT_ENTRY(BS_OS_RESUME_CHECK, BS_ON_EXIT, lock, NULL);
|
||||||
|
#else
|
||||||
|
BOOT_STATE_INIT_ENTRY(BS_DEV_RESOURCES, BS_ON_ENTRY, lock, NULL);
|
||||||
|
#endif
|
@@ -3,6 +3,7 @@
|
|||||||
#include <arch/io.h>
|
#include <arch/io.h>
|
||||||
#include <console/system76_ec.h>
|
#include <console/system76_ec.h>
|
||||||
#include <timer.h>
|
#include <timer.h>
|
||||||
|
#include "system76_ec.h"
|
||||||
|
|
||||||
// This is the command region for System76 EC firmware. It must be
|
// This is the command region for System76 EC firmware. It must be
|
||||||
// enabled for LPC in the mainboard.
|
// enabled for LPC in the mainboard.
|
||||||
@@ -11,15 +12,22 @@
|
|||||||
|
|
||||||
#define REG_CMD 0
|
#define REG_CMD 0
|
||||||
#define REG_RESULT 1
|
#define REG_RESULT 1
|
||||||
|
#define REG_DATA 2
|
||||||
|
|
||||||
// When command register is 0, command is complete
|
// When command register is 0, command is complete
|
||||||
#define CMD_FINISHED 0
|
#define CMD_FINISHED 0
|
||||||
|
|
||||||
// Print command. Registers are unique for each command
|
// Print command. Registers are unique for each command
|
||||||
#define CMD_PRINT 4
|
#define CMD_PRINT 4
|
||||||
#define CMD_PRINT_REG_FLAGS 2
|
#define CMD_PRINT_REG_FLAGS REG_DATA
|
||||||
#define CMD_PRINT_REG_LEN 3
|
#define CMD_PRINT_REG_LEN (REG_DATA + 1)
|
||||||
#define CMD_PRINT_REG_DATA 4
|
#define CMD_PRINT_REG_DATA (REG_DATA + 2)
|
||||||
|
|
||||||
|
// Get security state command
|
||||||
|
#define CMD_SECURITY_GET 20
|
||||||
|
|
||||||
|
// OK result, any other values are errors
|
||||||
|
#define RESULT_OK 0
|
||||||
|
|
||||||
static inline uint8_t system76_ec_read(uint8_t addr)
|
static inline uint8_t system76_ec_read(uint8_t addr)
|
||||||
{
|
{
|
||||||
@@ -59,3 +67,81 @@ void system76_ec_print(uint8_t byte)
|
|||||||
if (byte == '\n' || len >= (SYSTEM76_EC_SIZE - CMD_PRINT_REG_DATA))
|
if (byte == '\n' || len >= (SYSTEM76_EC_SIZE - CMD_PRINT_REG_DATA))
|
||||||
system76_ec_flush();
|
system76_ec_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue a command not checking if the console needs to be flushed
|
||||||
|
// Do not print from this command to avoid EC protocol issues
|
||||||
|
static int system76_ec_unsafe(uint8_t cmd, uint8_t * data, int length) {
|
||||||
|
// Error if length is too long
|
||||||
|
if (length > (SYSTEM76_EC_SIZE - REG_DATA)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error if command is in progress
|
||||||
|
if (system76_ec_read(REG_CMD) != CMD_FINISHED) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write command data
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
system76_ec_write(REG_DATA + i, data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start command
|
||||||
|
system76_ec_write(REG_CMD, cmd);
|
||||||
|
|
||||||
|
// Wait for command completion, for up to 10 milliseconds, with a
|
||||||
|
// test period of 1 microsecond
|
||||||
|
wait_us(10000, system76_ec_read(REG_CMD) == CMD_FINISHED);
|
||||||
|
|
||||||
|
// Error if command did not complete
|
||||||
|
if (system76_ec_read(REG_CMD) != CMD_FINISHED) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read command data
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
data[i] = system76_ec_read(REG_DATA + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check result
|
||||||
|
if (system76_ec_read(REG_RESULT) != RESULT_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper to allow issuing commands while console is being used
|
||||||
|
// Do not print from this command to avoid EC protocol issues
|
||||||
|
static int system76_ec_command(uint8_t cmd, uint8_t * data, int length) {
|
||||||
|
// Error if command is in progress
|
||||||
|
if (system76_ec_read(REG_CMD) != CMD_FINISHED) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush print buffer if it has data
|
||||||
|
// Checked for completion by system76_ec_unsafe
|
||||||
|
if (system76_ec_read(CMD_PRINT_REG_LEN) > 0) {
|
||||||
|
system76_ec_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run command now that print buffer is flushed
|
||||||
|
int res = system76_ec_unsafe(cmd, data, length);
|
||||||
|
if (res < 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear command data (for future prints)
|
||||||
|
// Length is checked by system76_ec_unsafe
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
system76_ec_write(REG_DATA + i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get security state
|
||||||
|
int system76_ec_security_get(uint8_t * state) {
|
||||||
|
*state = 0;
|
||||||
|
return system76_ec_command(CMD_SECURITY_GET, state, sizeof(uint8_t));
|
||||||
|
}
|
||||||
|
16
src/ec/system76/ec/system76_ec.h
Normal file
16
src/ec/system76/ec/system76_ec.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef EC_SYSTEM76_EC_H
|
||||||
|
#define EC_SYSTEM76_EC_H
|
||||||
|
|
||||||
|
// Default value, flashing is prevented, cannot be set with CMD_SECURITY_SET
|
||||||
|
#define SYSTEM76_EC_SECURITY_STATE_LOCK 0
|
||||||
|
// Flashing is allowed, cannot be set with CMD_SECURITY_SET
|
||||||
|
#define SYSTEM76_EC_SECURITY_STATE_UNLOCK 1
|
||||||
|
// Flashing will be prevented on the next reboot
|
||||||
|
#define SYSTEM76_EC_SECURITY_STATE_PREPARE_LOCK 2
|
||||||
|
// Flashing will be allowed on the next reboot
|
||||||
|
#define SYSTEM76_EC_SECURITY_STATE_PREPARE_UNLOCK 3
|
||||||
|
|
||||||
|
// Get security state
|
||||||
|
int system76_ec_security_get(uint8_t * state);
|
||||||
|
|
||||||
|
#endif /* EC_SYSTEM76_EC_H */
|
Reference in New Issue
Block a user