From patchwork Tue Jan 13 04:21:15 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alistair Popple X-Patchwork-Id: 428253 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id A2B9A1401AF for ; Tue, 13 Jan 2015 15:21:25 +1100 (AEDT) Received: from ozlabs.org (ozlabs.org [103.22.144.67]) by lists.ozlabs.org (Postfix) with ESMTP id 8F3891A0C1B for ; Tue, 13 Jan 2015 15:21:25 +1100 (AEDT) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Received: from e23smtp02.au.ibm.com (e23smtp02.au.ibm.com [202.81.31.144]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 8B4C11A0749 for ; Tue, 13 Jan 2015 15:21:20 +1100 (AEDT) Received: from /spool/local by e23smtp02.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Tue, 13 Jan 2015 14:21:18 +1000 Received: from d23dlp01.au.ibm.com (202.81.31.203) by e23smtp02.au.ibm.com (202.81.31.208) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Tue, 13 Jan 2015 14:21:16 +1000 Received: from d23relay09.au.ibm.com (d23relay09.au.ibm.com [9.185.63.181]) by d23dlp01.au.ibm.com (Postfix) with ESMTP id 88EF62CE8047 for ; Tue, 13 Jan 2015 15:21:16 +1100 (EST) Received: from d23av02.au.ibm.com (d23av02.au.ibm.com [9.190.235.138]) by d23relay09.au.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id t0D4LGmG23789610 for ; Tue, 13 Jan 2015 15:21:16 +1100 Received: from d23av02.au.ibm.com (localhost [127.0.0.1]) by d23av02.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id t0D4LGZe010735 for ; Tue, 13 Jan 2015 15:21:16 +1100 Received: from ozlabs.au.ibm.com (ozlabs.au.ibm.com [9.192.253.14]) by d23av02.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id t0D4LFjN010732 for ; Tue, 13 Jan 2015 15:21:15 +1100 Received: from localhost (haven.au.ibm.com [9.192.253.15]) (using TLSv1.2 with cipher DHE-RSA-AES128-SHA (128/128 bits)) (No client certificate requested) by ozlabs.au.ibm.com (Postfix) with ESMTPSA id C956CA01A5 for ; Tue, 13 Jan 2015 15:21:15 +1100 (AEDT) From: Alistair Popple To: skiboot@lists.ozlabs.org Date: Tue, 13 Jan 2015 15:21:15 +1100 Message-Id: <1421122875-2963-3-git-send-email-alistair@popple.id.au> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1421122875-2963-1-git-send-email-alistair@popple.id.au> References: <1421122875-2963-1-git-send-email-alistair@popple.id.au> X-TM-AS-MML: disable X-Content-Scanned: Fidelis XPS MAILER x-cbid: 15011304-0005-0000-0000-0000012FA6AD Subject: [Skiboot] [PATCH 3/3] ipmi/wdt: Add ipmi watchdog timer support X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" Add support for an ipmi watchdog timer. This patch will cause the system to be reset if opal_run_pollers() isn't called for more than about 60 seconds. The timer is reset just prior to running the payload. It is the responsibility of the payload to ensure either opal_run_pollers() is called frequently enough or to disable the watchdog timer by sending appropriate ipmi commands. Signed-off-by: Alistair Popple --- core/init.c | 7 ++- hw/ipmi/Makefile.inc | 1 + hw/ipmi/ipmi-watchdog.c | 142 ++++++++++++++++++++++++++++++++++++++++++++ include/ipmi.h | 13 ++++ platforms/astbmc/common.c | 1 + platforms/astbmc/palmetto.c | 1 + 6 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 hw/ipmi/ipmi-watchdog.c diff --git a/core/init.c b/core/init.c index cffa638..ffd4621 100644 --- a/core/init.c +++ b/core/init.c @@ -44,6 +44,8 @@ #include #include +#include + /* * Boot semaphore, incremented by each CPU calling in * @@ -367,7 +369,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. */ @@ -672,6 +674,8 @@ void __noreturn main_cpu_entry(const void *fdt, u32 master_cpu) /* ... and add remaining reservations to the DT */ mem_region_add_dt_reserved(); + ipmi_wdt_reset(); + load_and_boot_kernel(false); } @@ -707,4 +711,3 @@ void __noreturn secondary_cpu_entry(void) __secondary_cpu_entry(); } - 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..20607ec --- /dev/null +++ b/hw/ipmi/ipmi-watchdog.c @@ -0,0 +1,142 @@ + +/* 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 +#include +#include +#include +#include +#include +#include + +#define TIMER_USE_DONT_LOG 0x80 +#define TIMER_USE_DONT_STOP 0x40 +#define TIMER_USE_POST 0x02 + +/* WDT expiration actions */ +#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 lock ipmi_msg_lock = LOCK_UNLOCKED; +static bool ipmi_msg_busy = true; +static struct ipmi_msg *ipmi_msg; +static struct timer wdt_timer; + +static void ipmi_wdt_complete(struct ipmi_msg *msg) +{ + ipmi_msg_busy = false; + + if (msg->cmd == IPMI_CMD(IPMI_RESET_WDT) && !msg->user_data) + schedule_timer(&wdt_timer, msecs_to_tb((WDT_TIMEOUT - WDT_MARGIN)*100)); +} + +/* As the WDT message is used continually we reduce stress on the + * memory allocator by using a single message. This function waits for + * the message to become available by running the pollers to flush the + * queue. For the WDT case this makes sense as a backlog of IPMI + * messages could cause a reset if we don't prioritise the processing + * of the IPMI message queue. */ +static void get_ipmi_msg(void) +{ +retry: + while (ipmi_msg_busy) + time_wait_ms(100); + + lock(&ipmi_msg_lock); + if (ipmi_msg_busy) { + unlock(&ipmi_msg_lock); + goto retry; + } + + ipmi_msg_busy = true; + unlock(&ipmi_msg_lock); +} + +static void set_wdt(uint8_t action, uint16_t count) +{ + get_ipmi_msg(); + ipmi_init_msg(ipmi_msg, IPMI_DEFAULT_INTERFACE, IPMI_SET_WDT, + ipmi_wdt_complete, NULL, 6, 0); + ipmi_msg->data[0] = TIMER_USE_DONT_LOG + | TIMER_USE_POST; /* Timer Use */ + ipmi_msg->data[1] = action; /* Timer Actions */ + ipmi_msg->data[2] = 0; /* 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) +{ + get_ipmi_msg(); + ipmi_init_msg(ipmi_msg, IPMI_DEFAULT_INTERFACE, IPMI_RESET_WDT, + ipmi_wdt_complete, data, 0, 0); + ipmi_queue_msg(ipmi_msg); +} + +void ipmi_wdt_final_reset(void) +{ + cancel_timer(&wdt_timer); + reset_wdt(NULL, (void *) !NULL); +} + +void ipmi_wdt_reset(void) +{ + cancel_timer(&wdt_timer); + reset_wdt(NULL, NULL); +} + +void ipmi_wdt_init(void) +{ + ipmi_msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_SET_WDT, + ipmi_wdt_complete, NULL, NULL, 6, 0); + if (!ipmi_msg) { + prerror("Unable to allocate watchdog ipmi message\n"); + return; + } + + ipmi_msg_busy = false; + init_timer(&wdt_timer, reset_wdt, NULL); + set_wdt(WDT_POWER_CYCLE_ACTION, WDT_TIMEOUT); + + /* 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); + + /* Crank the state machines until we've processed the reset + * command to start the wdt. This ensures the wdt has started + * before we hand control over to the rest of the system.*/ + get_ipmi_msg(); + ipmi_msg_busy = false; + + return; +} diff --git a/include/ipmi.h b/include/ipmi.h index bbeae5a..40dee24 100644 --- a/include/ipmi.h +++ b/include/ipmi.h @@ -93,6 +93,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_PARTIAL_ADD_ESEL IPMI_CODE(IPMI_NETFN_OEM, 0xf0) @@ -202,4 +204,15 @@ void ipmi_fru_init(uint8_t fru_dev_id); struct errorlog; int ipmi_elog_commit(struct errorlog *elog_buf); +/* Starts the watchdog timer */ +void ipmi_wdt_init(void); + +/* Queue a watchdog timer reset. Schedules future resets to prevent + * timer expiration. */ +void ipmi_wdt_reset(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 993ac4c..336d275 100644 --- a/platforms/astbmc/common.c +++ b/platforms/astbmc/common.c @@ -51,6 +51,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 a0030e8..b9ef4a4 100644 --- a/platforms/astbmc/palmetto.c +++ b/platforms/astbmc/palmetto.c @@ -51,4 +51,5 @@ DECLARE_PLATFORM(palmetto) = { .cec_power_down = astbmc_ipmi_power_down, .cec_reboot = astbmc_ipmi_reboot, .elog_commit = ipmi_elog_commit, + .exit = ipmi_wdt_final_reset, };