From 5ac11dd7fd757898ff4c401ec996a92bc3c233a2 Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Sun, 11 Jun 2023 23:25:02 -0700 Subject: [PATCH] kgpe: add patch for USE_WATCHDOG_ON_BOOT and enable it This commit adds a coreboot patch which enables CONFIG_USE_WATCHDOG_ON_BOOT for kgpe-d16; in upstream coreboot this feature is found on only a few intel cpu platforms. When enabled, this feature starts the hardware watchdog very early in the boot process -- before PNP enumeration or DRAM initialization. This ensures that any hangs or freezes due to transient conditions (flakiness, temperature, electrical noise) won't prevent the machine from eventually booting. This is very useful for unattended servers. On kgpe-d16 the watchdog is cancelled immediately before jumping to the payload (i.e. Linux kernel). I found that if I left the watchdog enabled, any attempt to use it (for example, to cancel it or extend it) from Linux resulted in the machine resetting. Perhaps this can be fixed, but for now I am content to simply re-enable the watchdog from Linux rather than leaving it running. --- src/coreboot/default.nix | 1 + ...-w83667hg-a-watchdog-during-romstage.patch | 254 ++++++++++++++++++ src/platform/kgpe/default.nix | 8 + 3 files changed, 263 insertions(+) create mode 100644 src/coreboot/patches/0026-kgpe-d16-start-w83667hg-a-watchdog-during-romstage.patch diff --git a/src/coreboot/default.nix b/src/coreboot/default.nix index c837b51..eb417d7 100644 --- a/src/coreboot/default.nix +++ b/src/coreboot/default.nix @@ -109,6 +109,7 @@ stdenv.mkDerivation { ./patches/0004-superio-winbond-w83667hg-a-superio.c-do-not-use-get_.patch ./patches/0001-romstage-print-out-dimm-voltages.patch ./patches/0002-kgpe-d16-do-not-enable-hw-monitor-until-kernel-boots.patch + ./patches/0026-kgpe-d16-start-w83667hg-a-watchdog-during-romstage.patch # am1i patches ./patches/0021-am1i-omit-amdfw.rom-completely-it-has-broken-address.patch diff --git a/src/coreboot/patches/0026-kgpe-d16-start-w83667hg-a-watchdog-during-romstage.patch b/src/coreboot/patches/0026-kgpe-d16-start-w83667hg-a-watchdog-during-romstage.patch new file mode 100644 index 0000000..b2ae2af --- /dev/null +++ b/src/coreboot/patches/0026-kgpe-d16-start-w83667hg-a-watchdog-during-romstage.patch @@ -0,0 +1,254 @@ +From d320221a74a641712d97066d616b81df5a1fb3c1 Mon Sep 17 00:00:00 2001 +From: Your Name +Date: Sun, 11 Jun 2023 22:18:37 -0700 +Subject: [PATCH] kgpe-d16: start w83667hg-a watchdog during romstage + +--- + src/mainboard/asus/kgpe-d16/Kconfig | 1 + + src/mainboard/asus/kgpe-d16/devicetree.cb | 2 +- + src/mainboard/asus/kgpe-d16/mainboard.c | 12 +++++ + src/mainboard/asus/kgpe-d16/romstage.c | 12 +++++ + src/superio/winbond/Makefile.inc | 1 + + src/superio/winbond/common/early_init.c | 53 ++++++++++++++++++++++- + src/superio/winbond/common/winbond.h | 1 + + src/superio/winbond/w83667hg-a/superio.c | 27 ++++++++++++ + 8 files changed, 106 insertions(+), 3 deletions(-) + +diff --git a/src/mainboard/asus/kgpe-d16/Kconfig b/src/mainboard/asus/kgpe-d16/Kconfig +index c6b2de3ab92..3660b7c644b 100644 +--- a/src/mainboard/asus/kgpe-d16/Kconfig ++++ b/src/mainboard/asus/kgpe-d16/Kconfig +@@ -32,6 +32,7 @@ config BOARD_SPECIFIC_OPTIONS + select DRIVERS_ASPEED_AST2050 + select MAINBOARD_FORCE_NATIVE_VGA_INIT + select MAINBOARD_HAS_NATIVE_VGA_INIT ++ select USE_WATCHDOG_ON_BOOT + + config MAINBOARD_DIR + string +diff --git a/src/mainboard/asus/kgpe-d16/devicetree.cb b/src/mainboard/asus/kgpe-d16/devicetree.cb +index ff2023ddd05..3be328d1725 100644 +--- a/src/mainboard/asus/kgpe-d16/devicetree.cb ++++ b/src/mainboard/asus/kgpe-d16/devicetree.cb +@@ -199,7 +199,7 @@ chip northbridge/amd/amdfam10/root_complex # Root complex + device pnp 2e.207 off end # GIPO7 + device pnp 2e.307 off end # GIPO8 + device pnp 2e.407 off end # GIPO9 +- device pnp 2e.8 off end # WDT ++ device pnp 2e.8 on end # WDT + device pnp 2e.108 off end # GPIO 1 + device pnp 2e.9 off end # GPIO2 + device pnp 2e.109 off end # GPIO3 +diff --git a/src/mainboard/asus/kgpe-d16/mainboard.c b/src/mainboard/asus/kgpe-d16/mainboard.c +index 14a4a697620..8d377cafa95 100644 +--- a/src/mainboard/asus/kgpe-d16/mainboard.c ++++ b/src/mainboard/asus/kgpe-d16/mainboard.c +@@ -21,8 +21,12 @@ + #include + #include + #include ++#include + #include + #include ++#include ++#include ++#include + + void set_pcie_reset(void) + { +@@ -110,3 +114,11 @@ void sb7xx_51xx_setup_sata_port_indication(void *sata_bar5) + struct chip_operations mainboard_ops = { + .enable_dev = mainboard_enable, + }; ++ ++#define PNP_DEV(PORT, FUNC) (((PORT) << 8) | (FUNC)) ++#define WDT_DEV PNP_DEV(0x2e, W83667HG_A_WDT1) ++#define ACPI_DEV PNP_DEV(0x2e, W83667HG_A_ACPI) ++void watchdog_off(void); ++void watchdog_off(void) { ++ winbond_enable_watchdog(WDT_DEV, ACPI_DEV, 0); ++} +diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c +index 88fa60d2d1f..81a5bf5d67d 100644 +--- a/src/mainboard/asus/kgpe-d16/romstage.c ++++ b/src/mainboard/asus/kgpe-d16/romstage.c +@@ -53,6 +53,8 @@ + + #define SERIAL_0_DEV PNP_DEV(0x2e, W83667HG_A_SP1) + #define SERIAL_1_DEV PNP_DEV(0x2e, W83667HG_A_SP2) ++#define WDT_DEV PNP_DEV(0x2e, W83667HG_A_WDT1) ++#define ACPI_DEV PNP_DEV(0x2e, W83667HG_A_ACPI) + + void activate_spd_rom(const struct mem_controller *ctrl); + int spd_read_byte(unsigned int device, unsigned int address); +@@ -530,6 +532,8 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + byte = pci_read_config8(PCI_DEV(0, 0x14, 3), 0x78); + byte &= ~(1 << 0); + pci_write_config8(PCI_DEV(0, 0x14, 3), 0x78, byte); ++ ++ winbond_enable_watchdog(WDT_DEV, ACPI_DEV, 40); + } + + printk(BIOS_SPEW, "Initial stack pointer: %08x\n", esp); +@@ -681,6 +685,10 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x40); + ++ if (!cpu_init_detectedx && boot_cpu()) { ++ winbond_enable_watchdog(WDT_DEV, ACPI_DEV, 60); ++ } ++ printk(BIOS_ERR, "initializing dram...\n"); + timestamp_add_now(TS_BEFORE_INITRAM); + printk(BIOS_DEBUG, "raminit_amdmct()\n"); + raminit_amdmct(sysinfo); +@@ -712,6 +720,10 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + pci_write_config16(PCI_DEV(0, 0x14, 0), 0x54, 0x0707); + pci_write_config16(PCI_DEV(0, 0x14, 0), 0x56, 0x0bb0); + pci_write_config16(PCI_DEV(0, 0x14, 0), 0x5a, 0x0ff0); ++ ++ if (!cpu_init_detectedx && boot_cpu()) { ++ winbond_enable_watchdog(WDT_DEV, ACPI_DEV, 60); ++ } + } + + /** +diff --git a/src/superio/winbond/Makefile.inc b/src/superio/winbond/Makefile.inc +index b0b7b8c5202..0502441a2f0 100644 +--- a/src/superio/winbond/Makefile.inc ++++ b/src/superio/winbond/Makefile.inc +@@ -15,6 +15,7 @@ + + ## include generic winbond pre-ram stage driver + romstage-$(CONFIG_SUPERIO_WINBOND_COMMON_ROMSTAGE) += common/early_init.c ++ramstage-$(CONFIG_SUPERIO_WINBOND_COMMON_ROMSTAGE) += common/early_init.c + + subdirs-y += w83627dhg + subdirs-y += w83627ehg +diff --git a/src/superio/winbond/common/early_init.c b/src/superio/winbond/common/early_init.c +index 7c3ce2b3a56..ba3cbb2a34f 100644 +--- a/src/superio/winbond/common/early_init.c ++++ b/src/superio/winbond/common/early_init.c +@@ -33,9 +33,13 @@ + * + */ + +-#include +-#include ++#define __SIMPLE_DEVICE__ ++#include ++#include + #include ++#include ++#include ++#include + #include "winbond.h" + + #define WINBOND_ENTRY_KEY 0x87 +@@ -56,6 +60,51 @@ void pnp_exit_conf_state(pnp_devfn_t dev) + outb(WINBOND_EXIT_KEY, port); + } + ++void winbond_enable_watchdog(pnp_devfn_t dev, pnp_devfn_t dev_acpi, u8 seconds) ++{ ++#if IS_ENABLED(CONFIG_USE_WATCHDOG_ON_BOOT) ++ u8 byte; ++ ++ // ACPI device: set EN_WDT2PWROK //////////////////// ++ ++ pnp_enter_conf_state(dev_acpi); ++ pnp_set_logical_device(dev_acpi); ++ pnp_set_enable(dev_acpi, 1); ++ byte = pnp_read_config(dev_acpi, 0xf2); ++ byte |= (1<<7); // EN_WDT2PWROK = Enable PWROK0,1 or 2 pin drive pulse by WDTO ++ pnp_write_config(dev_acpi, 0xf2, byte); ++ pnp_exit_conf_state(dev_acpi); ++ ++ // WDT device: enable timer //////////////////// ++ ++ pnp_enter_conf_state(dev); ++ pnp_set_logical_device(dev); ++ pnp_set_enable(dev, 1); ++ ++ byte = pnp_read_config(dev, 0xf5); ++ byte &= ~0x4e; ++ //byte |= 0x1 << 4; // count 1000 times faster than usual (i.e. milliseconds/milliminutes) ++ //byte |= 0x1 << 3; // count in minutes rather than seconds ++ byte |= 0x1 << 1; // Enable the WDTO# output low pulse to the KBRST# pin (PIN28) ++ pnp_write_config(dev, 0xf5, byte); ++ ++ byte = pnp_read_config(dev, 0xf7); ++ byte &= ~0xc0; // keyboard/mouse interrupts do not reset WDT ++ pnp_write_config(dev, 0xf7, byte); ++ ++ // watchdog timer value in seconds; countdown starts ++ // immediately.; write 0 to disable counter. ++ pnp_write_config(dev, 0xf6, seconds); ++ ++ pnp_exit_conf_state(dev); ++ if (seconds == 0) { ++ printk(BIOS_ERR, "stopped W83667HG-A watchdog.\n"); ++ } else { ++ printk(BIOS_ERR, "started W83667HG-A watchdog; you have %d seconds to disable it with: modprobe w83627hf_wdt; echo -n V > /dev/watchdog\n", seconds); ++ } ++#endif ++} ++ + /* Bring up early serial debugging output before the RAM is initialized. */ + void winbond_enable_serial(pnp_devfn_t dev, u16 iobase) + { +diff --git a/src/superio/winbond/common/winbond.h b/src/superio/winbond/common/winbond.h +index e472018bf19..87aa32ae46d 100644 +--- a/src/superio/winbond/common/winbond.h ++++ b/src/superio/winbond/common/winbond.h +@@ -21,6 +21,7 @@ + #include + + void winbond_enable_serial(pnp_devfn_t dev, uint16_t iobase); ++void winbond_enable_watchdog(pnp_devfn_t dev, pnp_devfn_t dev_acpi, u8 seconds); + void winbond_set_pinmux(pnp_devfn_t dev, uint8_t offset, uint8_t mask, uint8_t state); + void winbond_set_clksel_48(pnp_devfn_t dev); + +diff --git a/src/superio/winbond/w83667hg-a/superio.c b/src/superio/winbond/w83667hg-a/superio.c +index ec1de699d0e..70794635293 100644 +--- a/src/superio/winbond/w83667hg-a/superio.c ++++ b/src/superio/winbond/w83667hg-a/superio.c +@@ -81,9 +81,36 @@ static void w83667hg_a_init(struct device *dev) + else if (power_status == 2) + byte |= (0x2 << 5); /* Use last power state */ + pnp_write_config(dev, 0xe4, byte); ++ ++ byte = pnp_read_config(dev, 0xf2); ++ byte |= (1<<7); // EN_WDT2PWROK = Enable PWROK0,1 or 2 pin drive pulse by WDTO ++ pnp_write_config(dev, 0xf2, byte); ++ + pnp_exit_conf_mode_aa(dev); + printk(BIOS_INFO, "set power %s after power fail\n", power_status ? "on" : "off"); + break; ++ case W83667HG_A_WDT1: ++ pnp_enter_conf_mode_8787(dev); ++ pnp_set_logical_device(dev); ++ ++ byte = pnp_read_config(dev, 0xf5); ++ byte &= ~0x4e; ++ //byte |= 0x1 << 1; // count 1000 times faster than usual (i.e. milliseconds/milliminutes) ++ //byte |= 0x1 << 1; // count in minutes rather than seconds ++ byte |= 0x1 << 1; // Enable the WDTO# output low pulse to the KBRST# pin (PIN28) ++ pnp_write_config(dev, 0xf5, byte); ++ ++ byte = pnp_read_config(dev, 0xf7); ++ byte &= ~0xc0; // keyboard/mouse interrupts do not reset WDT ++ pnp_write_config(dev, 0xf7, byte); ++ ++ // watchdog timer value in seconds; countdown starts ++ // immediately. write 0 to disable counter. ++ //pnp_write_config(dev, 0xf6, 0); ++ //printk(BIOS_INFO, "started watchdog timer!"); ++ ++ pnp_exit_conf_mode_aa(dev); ++ break; + } + } + +-- +2.39.1 + diff --git a/src/platform/kgpe/default.nix b/src/platform/kgpe/default.nix index 365964c..c26b098 100644 --- a/src/platform/kgpe/default.nix +++ b/src/platform/kgpe/default.nix @@ -97,6 +97,14 @@ CONSOLE_CBMEM = lib.mkForce no; ONBOARD_VGA_IS_PRIMARY = lib.mkForce no; + + # Coreboot on some versions of the kgpe-d16 will hang during + # PNP enumeration approximately ~10% of the time during PNP + # enumeration (before DRAM init); starting the watchdog from + # the romstage ensures that the machine will always finish + # booting, although it may take two (or in rare cases three) + # attempts. You absolutely want this for unattended servers. + USE_WATCHDOG_ON_BOOT = lib.mkForce yes; }; coreboot-toolchain = with final.coreboot-toolchain; [ x64 i386 ]; uart-for-console =