Tested on a Supermicro X11 series (Simatic IPC 1047E) and on some other random Supermicro X9 series machine. Should work for any board that has IPMI in any version.
Developed using the IPMI Spec 2.0 sections 9 and 27. Error handling and recovery tested to some degree. Best way to work turned out to be a user-space application with iopl(3) and setting "no action". Signed-off-by: Henning Schild <[email protected]> --- .gitignore | 1 + Makefile.am | 3 +- drivers/watchdog/.ipmi_wdt.c.swn | Bin 0 -> 12288 bytes drivers/watchdog/ipmi_wdt.c | 193 +++++++++++++++++++++++++++++++ 4 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 drivers/watchdog/.ipmi_wdt.c.swn create mode 100644 drivers/watchdog/ipmi_wdt.c diff --git a/.gitignore b/.gitignore index ae0d682be947..0dfbde702618 100644 --- a/.gitignore +++ b/.gitignore @@ -85,6 +85,7 @@ m4/lt* version.h libtool .libs +*.pc # Test binaries test_bgenv_init_retval diff --git a/Makefile.am b/Makefile.am index 48c560f72f0c..07f059a5b373 100644 --- a/Makefile.am +++ b/Makefile.am @@ -166,7 +166,8 @@ efi_sources_watchdogs = \ drivers/watchdog/atom-quark.c \ drivers/watchdog/ipc4x7e_wdt.c \ drivers/watchdog/itco.c \ - drivers/watchdog/hpwdt.c + drivers/watchdog/hpwdt.c \ + drivers/watchdog/ipmi_wdt.c else efi_sources_watchdogs = endif diff --git a/drivers/watchdog/.ipmi_wdt.c.swn b/drivers/watchdog/.ipmi_wdt.c.swn new file mode 100644 index 0000000000000000000000000000000000000000..d5e0f93dd94236c02d30c96b348641f981fd9e59 GIT binary patch literal 12288 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 F{{`k&sS^MI literal 0 HcmV?d00001 diff --git a/drivers/watchdog/ipmi_wdt.c b/drivers/watchdog/ipmi_wdt.c new file mode 100644 index 000000000000..fead8d9afd50 --- /dev/null +++ b/drivers/watchdog/ipmi_wdt.c @@ -0,0 +1,193 @@ +/* + * EFI Boot Guard + * + * Copyright (c) Siemens AG, 2023 + * + * Authors: + * Henning Schild <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <efi.h> +#include <pci/header.h> +#include <sys/io.h> +#include "utils.h" + +#define SMBIOS_TYPE_IPMI_KCS 38 +#define IPMI_KCS_DEFAULT_IOBASE 0xca2 + +#define IPMI_KCS_STS_OBF 0x1 +#define IPMI_KCS_STS_IBF 0x2 + +#define IPMI_KCS_CMD_ABORT 0x60 +#define IPMI_KCS_CMD_WRITE_START 0x61 +#define IPMI_KCS_CMD_WRITE_END 0x62 + +#define IPMI_KCS_NETFS_LUN_WDT 0x18 + +#define IPMI_WDT_CMD_RESET 0x22 +#define IPMI_WDT_CMD_SET 0x24 +#define IPMI_WDT_SET_USE_OSLOAD 0x3 +#define IPMI_WDT_SET_ACTION_HARD_RESET 0x1 + + +#define kcs_sts_is_error(sts) (((sts >> 6 ) & 0x3) == 0x3) + +static char +set_wdt_data[] = {IPMI_WDT_SET_USE_OSLOAD, IPMI_WDT_SET_ACTION_HARD_RESET, + 0x00, 0x00,0x00, 0x00}; + +static EFI_STATUS +kcs_wait_iobf(UINT16 io_base, char iobf) +{ + char sts; + + while (1) { + sts = inb(io_base + 1); + if (kcs_sts_is_error(sts)) + return EFI_DEVICE_ERROR; + if (iobf == IPMI_KCS_STS_IBF) { + // IBF we wait for clear + if (!(sts & IPMI_KCS_STS_IBF)) + break; + } else { + // OBF we wait for set + if (sts & IPMI_KCS_STS_OBF) + break; + } + } + + return EFI_SUCCESS; +} + +static EFI_STATUS +kcs_outb(UINT8 value, UINT16 io_base, UINT16 port) +{ + EFI_STATUS status; + + status = kcs_wait_iobf(io_base, IPMI_KCS_STS_IBF); + if (status) + return status; + + outb(value, io_base + port); + // dummy read + inb(io_base); + + return EFI_SUCCESS; +} + +static EFI_STATUS +_send_ipmi_cmd(UINT16 io_base, char cmd, char *data, int datalen) +{ + int i, err = 0; + char lastbyte = cmd; + + err += kcs_outb(IPMI_KCS_CMD_WRITE_START, io_base, 1); + err += kcs_outb(IPMI_KCS_NETFS_LUN_WDT, io_base, 0); + + if (datalen) { + lastbyte = data[datalen - 1]; + err += kcs_outb(cmd, io_base, 0); + for (i = 0; i < datalen - 1; i++) + err += kcs_outb(data[i], io_base, 0); + } + + err += kcs_outb(IPMI_KCS_CMD_WRITE_END, io_base, 1); + err += kcs_outb(lastbyte, io_base, 0); + + if (err) + return EFI_DEVICE_ERROR; + + return kcs_wait_iobf(io_base, IPMI_KCS_STS_OBF); +} + +static VOID +handle_ipmi_error(UINT16 io_base) +{ + WARNING(L"Handling Error Status 0x%x\n", inb(io_base + 1)); + + outb(IPMI_KCS_CMD_ABORT, io_base + 1); + + if (kcs_wait_iobf(io_base, IPMI_KCS_STS_IBF)) + // stuck in error state, hopefully does not happen + return; + + if (inb((io_base + 1) & IPMI_KCS_STS_OBF)) + inb(io_base); + outb(0x0, io_base); + + if (kcs_wait_iobf(io_base, IPMI_KCS_STS_IBF)) + // stuck in error state, hopefully does not happen + return; +} + +static EFI_STATUS +send_ipmi_cmd(UINT16 io_base, char cmd, char *data, int datalen) +{ + EFI_STATUS status; + int i; + + for (i = 0; i < 5; i++) { + status = _send_ipmi_cmd(io_base, cmd, data, datalen); + if (status == EFI_SUCCESS) + return status; + handle_ipmi_error(io_base); + } + + return status; +} + +static EFI_STATUS __attribute__((constructor)) +init(__attribute__((unused)) EFI_PCI_IO *pci_io, + __attribute__((unused)) UINT16 pci_vendor_id, + __attribute__((unused)) UINT16 pci_device_id, + UINTN timeout) +{ + SMBIOS_STRUCTURE_TABLE *smbios_table; + SMBIOS_STRUCTURE_POINTER smbios_struct; + EFI_STATUS status; + UINT64 io_base; + UINT16 *timeout_value; + + status = LibGetSystemConfigurationTable(&SMBIOSTableGuid, + (VOID **)&smbios_table); + + if (status != EFI_SUCCESS) + return EFI_UNSUPPORTED; + + smbios_struct = smbios_find_struct(smbios_table, SMBIOS_TYPE_IPMI_KCS); + + if (smbios_struct.Raw == NULL) + return EFI_UNSUPPORTED; + + io_base = *((UINT64 *)(smbios_struct.Raw + 8)); + if (io_base == 0) { + io_base = IPMI_KCS_DEFAULT_IOBASE; + } else { + if (!(io_base & 1)) + // MMIO not implemented + return EFI_UNSUPPORTED; + + io_base &= ~1; + } + + INFO(L"Detected IPMI watchdog at I/O 0x%x\n", io_base); + timeout_value = (UINT16 *)(set_wdt_data + 4); + *timeout_value = timeout * 10; + + status = send_ipmi_cmd(io_base, IPMI_WDT_CMD_SET, set_wdt_data, + sizeof(set_wdt_data)); + if (status == EFI_SUCCESS) + status = send_ipmi_cmd(io_base, IPMI_WDT_CMD_RESET, NULL, 0); + + if (status != EFI_SUCCESS) { + WARNING(L"Watchdog device repeatedly reported errors. " \ + "Failed to arm the watchdog, but still booting up.\n"); + } + + return EFI_SUCCESS; +} -- 2.39.3 -- You received this message because you are subscribed to the Google Groups "EFI Boot Guard" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/efibootguard-dev/20230515180851.24382-5-henning.schild%40siemens.com.
