diff options
author | Alistair Popple <alistair@popple.id.au> | 2015-02-04 16:07:46 +1100 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2015-02-09 14:24:58 +1100 |
commit | 739e6df1163301a200b604b5eefa00770f9fa44c (patch) | |
tree | d8f05f37ddcd0f5f28e0753f13da9d4c7804be40 | |
parent | e8ffc3caadf6e5ddbcd0910ae239eb1be0fdb381 (diff) | |
download | skiboot-739e6df1163301a200b604b5eefa00770f9fa44c.zip skiboot-739e6df1163301a200b604b5eefa00770f9fa44c.tar.gz skiboot-739e6df1163301a200b604b5eefa00770f9fa44c.tar.bz2 |
ipmi/wdt: Add ipmi watchdog timer support
Add support for an ipmi watchdog timer. During skiboot initialisation
this patch will cause the system to be reset if opal_run_pollers()
isn't called for more than 60 seconds.
Once the payload is started the watchdog timer will be reset and a
pre-timeout interrupt set. The payload should then receive the
interrupt and call into skiboot which will disable the watchdog timer.
Signed-off-by: Alistair Popple <alistair@popple.id.au>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
-rw-r--r-- | core/init.c | 5 | ||||
-rw-r--r-- | core/ipmi.c | 6 | ||||
-rw-r--r-- | hw/ipmi/Makefile.inc | 1 | ||||
-rw-r--r-- | hw/ipmi/ipmi-watchdog.c | 127 | ||||
-rw-r--r-- | include/ipmi.h | 12 | ||||
-rw-r--r-- | platforms/astbmc/common.c | 1 | ||||
-rw-r--r-- | platforms/astbmc/palmetto.c | 1 |
7 files changed, 151 insertions, 2 deletions
diff --git a/core/init.c b/core/init.c index a4a64d3..0b29e2b 100644 --- a/core/init.c +++ b/core/init.c @@ -45,6 +45,8 @@ #include <hostservices.h> #include <timer.h> +#include <ipmi.h> + /* * Boot semaphore, incremented by each CPU calling in * @@ -388,7 +390,7 @@ void __noreturn load_and_boot_kernel(bool is_reboot) } fsp_console_select_stdout(); - /* + /* * OCC takes few secs to boot. Call this as late as * as possible to avoid delay. */ @@ -728,4 +730,3 @@ void __noreturn secondary_cpu_entry(void) __secondary_cpu_entry(); } - diff --git a/core/ipmi.c b/core/ipmi.c index e4dfefd..39997f6 100644 --- a/core/ipmi.c +++ b/core/ipmi.c @@ -156,6 +156,12 @@ static void ipmi_get_message_flags_complete(struct ipmi_msg *msg) prlog(PR_DEBUG, "IPMI Get Message Flags: %02x\n", flags); + /* Once we see an interrupt we assume the payload has + * booted. We disable the wdt and let the OS setup its own + * wdt. */ + if (flags & IPMI_MESSAGE_FLAGS_WATCHDOG_PRE_TIMEOUT) + ipmi_wdt_stop(); + /* Message available in the event buffer? Queue a Read Event command * to retrieve it. The flag is cleared by performing a read */ if (flags & IPMI_MESSAGE_FLAGS_EVENT_BUFFER) { diff --git a/hw/ipmi/Makefile.inc b/hw/ipmi/Makefile.inc index 02670d7..1c358a9 100644 --- a/hw/ipmi/Makefile.inc +++ b/hw/ipmi/Makefile.inc @@ -1,5 +1,6 @@ SUBDIRS += hw/ipmi IPMI_OBJS = ipmi-rtc.o ipmi-power.o ipmi-opal.o ipmi-fru.o ipmi-sel.o +IPMI_OBJS += ipmi-watchdog.o IPMI = hw/ipmi/built-in.o $(IPMI): $(IPMI_OBJS:%=hw/ipmi/%) diff --git a/hw/ipmi/ipmi-watchdog.c b/hw/ipmi/ipmi-watchdog.c new file mode 100644 index 0000000..de766cf --- /dev/null +++ b/hw/ipmi/ipmi-watchdog.c @@ -0,0 +1,127 @@ + +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <ipmi.h> +#include <lock.h> +#include <opal.h> +#include <device.h> +#include <timer.h> +#include <timebase.h> +#include <pool.h> +#include <skiboot.h> + +#define TIMER_USE_DONT_LOG 0x80 +#define TIMER_USE_DONT_STOP 0x40 +#define TIMER_USE_POST 0x02 + +/* WDT expiration actions */ +#define WDT_PRETIMEOUT_SMI 0x10 +#define WDT_POWER_CYCLE_ACTION 0x01 +#define WDT_NO_ACTION 0x00 + +/* How long to set the overall watchdog timeout for. In units of + * 100ms. If the timer is not reset within this time the watchdog + * expiration action will occur. */ +#define WDT_TIMEOUT 600 + +/* How often to reset the timer using schedule_timer(). Too short and +we risk accidently resetting the system due to opal_run_pollers() not +being called in time, too short and we waste time resetting the wdt +more frequently than neccessary. */ +#define WDT_MARGIN 300 + +static struct timer wdt_timer; +static bool wdt_stopped = false; + +static void ipmi_wdt_complete(struct ipmi_msg *msg) +{ + if (msg->cmd == IPMI_CMD(IPMI_RESET_WDT) && !msg->user_data) + schedule_timer(&wdt_timer, msecs_to_tb( + (WDT_TIMEOUT - WDT_MARGIN)*100)); + + ipmi_free_msg(msg); +} + +static void set_wdt(uint8_t action, uint16_t count, uint8_t pretimeout) +{ + struct ipmi_msg *ipmi_msg; + + ipmi_msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_SET_WDT, + ipmi_wdt_complete, NULL, NULL, 6, 0); + if (!ipmi_msg) { + prerror("Unable to allocate set wdt message\n"); + return; + } + ipmi_msg->error = ipmi_wdt_complete; + ipmi_msg->data[0] = TIMER_USE_POST | + TIMER_USE_DONT_LOG; /* Timer Use */ + ipmi_msg->data[1] = action; /* Timer Actions */ + ipmi_msg->data[2] = pretimeout; /* Pre-timeout Interval */ + ipmi_msg->data[3] = 0; /* Timer Use Flags */ + ipmi_msg->data[4] = count & 0xff; /* Initial countdown (lsb) */ + ipmi_msg->data[5] = (count >> 8) & 0xff; /* Initial countdown (msb) */ + ipmi_queue_msg(ipmi_msg); +} + +static void reset_wdt(struct timer *t __unused, void *data) +{ + struct ipmi_msg *ipmi_msg; + + ipmi_msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_RESET_WDT, + ipmi_wdt_complete, data, NULL, 0, 0); + if (!ipmi_msg) { + prerror("Unable to allocate reset wdt message\n"); + return; + } + + if (!data) + ipmi_queue_msg_sync(ipmi_msg); + else + ipmi_queue_msg(ipmi_msg); +} + +void ipmi_wdt_stop(void) +{ + if (!wdt_stopped) { + wdt_stopped = true; + set_wdt(WDT_NO_ACTION, 100, 0); + } +} + +void ipmi_wdt_final_reset(void) +{ + set_wdt(WDT_POWER_CYCLE_ACTION | WDT_PRETIMEOUT_SMI, WDT_TIMEOUT, + WDT_MARGIN/10); + reset_wdt(NULL, (void *) 1); + cancel_timer(&wdt_timer); +} + +void ipmi_wdt_init(void) +{ + init_timer(&wdt_timer, reset_wdt, NULL); + set_wdt(WDT_POWER_CYCLE_ACTION, WDT_TIMEOUT, 0); + + /* Start the WDT */ + reset_wdt(NULL, NULL); + + /* For some reason we have to reset it twice to get it to + * actually start the first time. */ + reset_wdt(NULL, NULL); + + return; +} diff --git a/include/ipmi.h b/include/ipmi.h index df8cf99..9e52916 100644 --- a/include/ipmi.h +++ b/include/ipmi.h @@ -101,6 +101,8 @@ #define IPMI_CHASSIS_CONTROL IPMI_CODE(IPMI_NETFN_CHASSIS, 0x02) #define IPMI_SET_POWER_STATE IPMI_CODE(IPMI_NETFN_APP, 0x06) #define IPMI_GET_POWER_STATE IPMI_CODE(IPMI_NETFN_APP, 0x07) +#define IPMI_RESET_WDT IPMI_CODE(IPMI_NETFN_APP, 0x22) +#define IPMI_SET_WDT IPMI_CODE(IPMI_NETFN_APP, 0x24) #define IPMI_SET_ENABLES IPMI_CODE(IPMI_NETFN_APP, 0x2E) #define IPMI_GET_ENABLES IPMI_CODE(IPMI_NETFN_APP, 0x2F) #define IPMI_CLEAR_MESSAGE_FLAGS IPMI_CODE(IPMI_NETFN_APP, 0x30) @@ -226,4 +228,14 @@ int ipmi_elog_commit(struct errorlog *elog_buf); /* Callback to parse an OEM SEL message */ void ipmi_parse_sel(struct ipmi_msg *msg); +/* Starts the watchdog timer */ +void ipmi_wdt_init(void); + +/* Stop the wdt */ +void ipmi_wdt_stop(void); + +/* Reset the watchdog timer. Does not return until the timer has been + * reset and does not schedule future resets. */ +void ipmi_wdt_final_reset(void); + #endif diff --git a/platforms/astbmc/common.c b/platforms/astbmc/common.c index fc4c776..47c1764 100644 --- a/platforms/astbmc/common.c +++ b/platforms/astbmc/common.c @@ -96,6 +96,7 @@ void astbmc_init(void) /* Register the BT interface with the IPMI layer */ bt_init(); + ipmi_wdt_init(); ipmi_rtc_init(); ipmi_opal_init(); ipmi_fru_init(0x01); diff --git a/platforms/astbmc/palmetto.c b/platforms/astbmc/palmetto.c index cfa7236..210d10a 100644 --- a/platforms/astbmc/palmetto.c +++ b/platforms/astbmc/palmetto.c @@ -52,4 +52,5 @@ DECLARE_PLATFORM(palmetto) = { .cec_reboot = astbmc_ipmi_reboot, .elog_commit = ipmi_elog_commit, .load_resource = pnor_load_resource, + .exit = ipmi_wdt_final_reset, }; |