From f2e7fd5a61404ff60652fe6dfc9a4e0d1cdad5a9 Mon Sep 17 00:00:00 2001 From: Rob Barnes Date: Tue, 15 Nov 2022 01:46:49 +0000 Subject: system: Implement system safe mode Basic implementation of system safe mode recovery. System safe mode is a recovery mode that may be started after a fault/panic. It allows the AP to collect info about the fault and system state before the system resets This CL only includes support for legacy CrOS EC BUG=b:249128225 BRANCH=None TEST=Manually tested on octopus Orig-Change-Id: I15139bb082011485b54e4ca7813839940bf5401a Orig-Signed-off-by: Rob Barnes Orig-Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4029604 Orig-Reviewed-by: Daisuke Nojiri Change-Id: I8abd563b82b611dafbc9fe1fda05ac6ade2b7c91 Signed-off-by: Rob Barnes Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4320966 Reviewed-by: Boris Mittelberg --- common/build.mk | 1 + common/host_command.c | 6 +++ common/system_safe_mode.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 common/system_safe_mode.c (limited to 'common') diff --git a/common/build.mk b/common/build.mk index 32e61123cb..f91d020f54 100644 --- a/common/build.mk +++ b/common/build.mk @@ -151,6 +151,7 @@ common-$(HAS_TASK_KEYSCAN)+=keyboard_scan.o common-$(HAS_TASK_LIGHTBAR)+=lb_common.o lightbar.o common-$(HAS_TASK_MOTIONSENSE)+=motion_sense.o common-$(HAS_TASK_TPM)+=tpm_registers.o +common-$(CONFIG_SYSTEM_SAFE_MODE)+=system_safe_mode.o ifneq ($(CONFIG_COMMON_RUNTIME),) common-$(CONFIG_MALLOC)+=shmalloc.o diff --git a/common/host_command.c b/common/host_command.c index cad9fd94a9..38113f021e 100644 --- a/common/host_command.c +++ b/common/host_command.c @@ -14,6 +14,7 @@ #include "lpc.h" #include "shared_mem.h" #include "system.h" +#include "system_safe_mode.h" #include "task.h" #include "timer.h" #include "util.h" @@ -403,6 +404,11 @@ static const struct host_command *find_host_command(int command) #else const struct host_command *cmd; + if (IS_ENABLED(CONFIG_SYSTEM_SAFE_MODE) && system_is_in_safe_mode()) { + if (!command_is_allowed_in_safe_mode(command)) + return NULL; + } + for (cmd = __hcmds; cmd < __hcmds_end; cmd++) { if (command == cmd->command) return cmd; diff --git a/common/system_safe_mode.c b/common/system_safe_mode.c new file mode 100644 index 0000000000..dbad287463 --- /dev/null +++ b/common/system_safe_mode.c @@ -0,0 +1,127 @@ +/* Copyright 2022 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "common.h" +#include "console.h" +#include "cpu.h" +#include "ec_commands.h" +#include "hooks.h" +#include "panic.h" +#include "stdbool.h" +#include "stddef.h" +#include "system.h" +#include "system_safe_mode.h" +#include "task.h" +#include "timer.h" +#include "watchdog.h" + +static bool in_safe_mode; + +static const int safe_mode_allowed_hostcmds[] = { + EC_CMD_GET_PROTOCOL_INFO, + EC_CMD_GET_VERSION, EC_CMD_CONSOLE_SNAPSHOT, + EC_CMD_CONSOLE_READ, EC_CMD_GET_NEXT_EVENT, + EC_CMD_GET_UPTIME_INFO +}; + +#ifndef CONFIG_ZEPHYR + +/* TODO: This function can be generalized for zephyr and legacy EC by + * improving ec_tasks support in zephyr. + */ +static bool task_is_safe_mode_critical(task_id_t task_id) +{ + const task_id_t safe_mode_critical_tasks[] = { + TASK_ID_HOOKS, + TASK_ID_IDLE, + TASK_ID_HOSTCMD, + }; + for (int i = 0; i < ARRAY_SIZE(safe_mode_critical_tasks); i++) + if (safe_mode_critical_tasks[i] == task_id) + return true; + return false; +} + +bool current_task_is_safe_mode_critical(void) +{ + return task_is_safe_mode_critical(task_get_current()); +} + +int disable_non_safe_mode_critical_tasks(void) +{ + for (task_id_t task_id = 0; task_id < TASK_ID_COUNT; task_id++) { + if (!task_is_safe_mode_critical(task_id)) { + task_disable_task(task_id); + } + } + return EC_SUCCESS; +} + +#endif /* CONFIG_ZEPHYR */ + +void handle_system_safe_mode_timeout(void) +{ + panic_printf("Safe mode timeout after %d msec\n", + CONFIG_SYSTEM_SAFE_MODE_TIMEOUT_MSEC); + panic_reboot(); +} +DECLARE_DEFERRED(handle_system_safe_mode_timeout); + +__overridable int schedule_system_safe_mode_timeout(void) +{ + hook_call_deferred(&handle_system_safe_mode_timeout_data, + CONFIG_SYSTEM_SAFE_MODE_TIMEOUT_MSEC * MSEC); + return EC_SUCCESS; +} + +bool system_is_in_safe_mode(void) +{ + return !!in_safe_mode; +} + +bool command_is_allowed_in_safe_mode(int command) +{ + for (int i = 0; i < ARRAY_SIZE(safe_mode_allowed_hostcmds); i++) + if (command == safe_mode_allowed_hostcmds[i]) + return true; + return false; +} + +int start_system_safe_mode(void) +{ + if (!system_is_in_rw()) { + panic_printf("Can only enter safe mode from RW image\n"); + return EC_ERROR_INVAL; + } + + if (system_is_in_safe_mode()) { + panic_printf("Already in system safe mode"); + return EC_ERROR_INVAL; + } + + if (current_task_is_safe_mode_critical()) { + /* TODO: Restart critical tasks */ + panic_printf( + "Fault in critical task, cannot enter system safe mode\n"); + return EC_ERROR_INVAL; + } + + disable_non_safe_mode_critical_tasks(); + + schedule_system_safe_mode_timeout(); + + in_safe_mode = true; + + panic_printf("\nStarting system safe mode\n"); + + return EC_SUCCESS; +} + +#ifdef TEST_BUILD +void set_system_safe_mode(bool mode) +{ + in_safe_mode = mode; +} +#endif -- cgit v1.2.1