2015-06-28 20:58:57 +03:00
|
|
|
/*
|
|
|
|
* QEMU ICH9 TCO emulation tests
|
|
|
|
*
|
|
|
|
* Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
|
|
* See the COPYING file in the top-level directory.
|
|
|
|
*/
|
2018-02-01 14:18:39 +03:00
|
|
|
|
2016-02-08 21:08:51 +03:00
|
|
|
#include "qemu/osdep.h"
|
2015-06-28 20:58:57 +03:00
|
|
|
|
2022-03-30 12:39:05 +03:00
|
|
|
#include "libqtest.h"
|
2015-06-28 20:58:57 +03:00
|
|
|
#include "libqos/pci.h"
|
|
|
|
#include "libqos/pci-pc.h"
|
2018-02-01 14:18:39 +03:00
|
|
|
#include "qapi/qmp/qdict.h"
|
2015-06-28 20:58:57 +03:00
|
|
|
#include "hw/pci/pci_regs.h"
|
2023-02-13 20:30:33 +03:00
|
|
|
#include "hw/southbridge/ich9.h"
|
2015-06-28 20:58:57 +03:00
|
|
|
#include "hw/acpi/ich9.h"
|
2022-12-12 13:51:15 +03:00
|
|
|
#include "hw/acpi/ich9_tco.h"
|
2015-06-28 20:58:57 +03:00
|
|
|
|
|
|
|
#define RCBA_BASE_ADDR 0xfed1c000
|
|
|
|
#define PM_IO_BASE_ADDR 0xb000
|
|
|
|
|
|
|
|
enum {
|
|
|
|
TCO_RLD_DEFAULT = 0x0000,
|
|
|
|
TCO_DAT_IN_DEFAULT = 0x00,
|
|
|
|
TCO_DAT_OUT_DEFAULT = 0x00,
|
|
|
|
TCO1_STS_DEFAULT = 0x0000,
|
|
|
|
TCO2_STS_DEFAULT = 0x0000,
|
|
|
|
TCO1_CNT_DEFAULT = 0x0000,
|
|
|
|
TCO2_CNT_DEFAULT = 0x0008,
|
|
|
|
TCO_MESSAGE1_DEFAULT = 0x00,
|
|
|
|
TCO_MESSAGE2_DEFAULT = 0x00,
|
|
|
|
TCO_WDCNT_DEFAULT = 0x00,
|
|
|
|
TCO_TMR_DEFAULT = 0x0004,
|
|
|
|
SW_IRQ_GEN_DEFAULT = 0x03,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define TCO_SECS_TO_TICKS(secs) (((secs) * 10) / 6)
|
|
|
|
#define TCO_TICKS_TO_SECS(ticks) (((ticks) * 6) / 10)
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
const char *args;
|
2015-06-28 20:58:58 +03:00
|
|
|
bool noreboot;
|
2015-06-28 20:58:57 +03:00
|
|
|
QPCIDevice *dev;
|
2016-10-24 07:52:06 +03:00
|
|
|
QPCIBar tco_io_bar;
|
2017-02-05 23:47:24 +03:00
|
|
|
QPCIBus *bus;
|
2019-04-09 11:52:41 +03:00
|
|
|
QTestState *qts;
|
2015-06-28 20:58:57 +03:00
|
|
|
} TestData;
|
|
|
|
|
2017-02-05 23:47:24 +03:00
|
|
|
static void test_end(TestData *d)
|
|
|
|
{
|
|
|
|
g_free(d->dev);
|
|
|
|
qpci_free_pc(d->bus);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_quit(d->qts);
|
2017-02-05 23:47:24 +03:00
|
|
|
}
|
|
|
|
|
2015-06-28 20:58:57 +03:00
|
|
|
static void test_init(TestData *d)
|
|
|
|
{
|
|
|
|
QTestState *qs;
|
|
|
|
|
2018-08-06 09:53:43 +03:00
|
|
|
qs = qtest_initf("-machine q35 %s %s",
|
hw/isa: enable TCO watchdog reboot pin strap by default
The TCO watchdog implementation default behaviour from POV of the
guest OS relies on the initial values for two I/O ports:
* TCO1_CNT == 0x0
Since bit 11 (TCO Timer Halt) is clear, the watchdog state
is considered to be initially running
* GCS == 0x20
Since bit 5 (No Reboot) is set, the watchdog will not trigger
when the timer expires
This is a safe default, because the No Reboot bit will prevent the
watchdog from triggering if the guest OS is unaware of its existance,
or is slow in configuring it. When a Linux guest initializes the TCO
watchdog, it will attempt to clear the "No Reboot" flag, and read the
value back. If the clear was honoured, the driver will treat this as
an indicator that the watchdog is functional and create the guest
watchdog device.
QEMU implements a second "no reboot" flag, however, via pin straps
which overrides the behaviour of the guest controlled "no reboot"
flag:
commit 5add35bec1e249bb5345a47008c8f298d4760be4
Author: Paulo Alcantara <pcacjr@gmail.com>
Date: Sun Jun 28 14:58:58 2015 -0300
ich9: implement strap SPKR pin logic
This second 'noreboot' pin was defaulted to high, which also inhibits
triggering of the requested watchdog actions, unless QEMU is launched
with the magic flag "-global ICH9-LPC.noreboot=false".
This is a bad default as we are exposing a watchdog to every guest OS
using the q35 machine type, but preventing it from actually doing what
it is designed to do. What is worse is that the guest OS and its apps
have no way to know that the watchdog is never going to fire, due to
this second 'noreboot' pin.
If a guest OS had no watchdog device at all, then apps whose operation
and/or data integrity relies on a watchdog can refuse to launch, and
alert the administrator of the problematic deployment. With Q35 machines
unconditionally exposing a watchdog though, apps will think their
deployment is correct but in fact have no protection at all.
This patch flips the default of the second 'no reboot' flag, so that
configured watchdog actions will be honoured out of the box for the
7.2 Q35 machine type onwards, if the guest enables use of the watchdog.
See also related bug reports
https://bugzilla.redhat.com/show_bug.cgi?id=2080207
https://bugzilla.redhat.com/show_bug.cgi?id=2136889
https://bugzilla.redhat.com/show_bug.cgi?id=2137346
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Message-Id: <20221216125749.596075-5-berrange@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2022-12-16 15:57:48 +03:00
|
|
|
d->noreboot ? "-global ICH9-LPC.noreboot=true" : "",
|
2018-08-06 09:53:43 +03:00
|
|
|
!d->args ? "" : d->args);
|
2015-06-28 20:58:57 +03:00
|
|
|
qtest_irq_intercept_in(qs, "ioapic");
|
|
|
|
|
2018-07-19 14:50:27 +03:00
|
|
|
d->bus = qpci_new_pc(qs, NULL);
|
2017-02-05 23:47:24 +03:00
|
|
|
d->dev = qpci_device_find(d->bus, QPCI_DEVFN(0x1f, 0x00));
|
2015-06-28 20:58:57 +03:00
|
|
|
g_assert(d->dev != NULL);
|
|
|
|
|
|
|
|
qpci_device_enable(d->dev);
|
|
|
|
|
|
|
|
/* set ACPI PM I/O space base address */
|
2015-07-08 10:06:15 +03:00
|
|
|
qpci_config_writel(d->dev, ICH9_LPC_PMBASE, PM_IO_BASE_ADDR | 0x1);
|
2015-06-28 20:58:57 +03:00
|
|
|
/* enable ACPI I/O */
|
2015-07-08 10:06:15 +03:00
|
|
|
qpci_config_writeb(d->dev, ICH9_LPC_ACPI_CTRL, 0x80);
|
2015-06-28 20:58:57 +03:00
|
|
|
/* set Root Complex BAR */
|
2015-07-08 10:06:15 +03:00
|
|
|
qpci_config_writel(d->dev, ICH9_LPC_RCBA, RCBA_BASE_ADDR | 0x1);
|
2015-06-28 20:58:57 +03:00
|
|
|
|
2016-10-24 07:52:06 +03:00
|
|
|
d->tco_io_bar = qpci_legacy_iomap(d->dev, PM_IO_BASE_ADDR + 0x60);
|
2019-04-09 11:52:41 +03:00
|
|
|
d->qts = qs;
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void stop_tco(const TestData *d)
|
|
|
|
{
|
|
|
|
uint32_t val;
|
|
|
|
|
2016-10-24 07:52:06 +03:00
|
|
|
val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT);
|
2015-06-28 20:58:57 +03:00
|
|
|
val |= TCO_TMR_HLT;
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void start_tco(const TestData *d)
|
|
|
|
{
|
|
|
|
uint32_t val;
|
|
|
|
|
2016-10-24 07:52:06 +03:00
|
|
|
val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT);
|
2015-06-28 20:58:57 +03:00
|
|
|
val &= ~TCO_TMR_HLT;
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void load_tco(const TestData *d)
|
|
|
|
{
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writew(d->dev, d->tco_io_bar, TCO_RLD, 4);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void set_tco_timeout(const TestData *d, uint16_t ticks)
|
|
|
|
{
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writew(d->dev, d->tco_io_bar, TCO_TMR, ticks);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void clear_tco_status(const TestData *d)
|
|
|
|
{
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writew(d->dev, d->tco_io_bar, TCO1_STS, 0x0008);
|
|
|
|
qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0002);
|
|
|
|
qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0004);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
2019-04-09 11:52:41 +03:00
|
|
|
static void reset_on_second_timeout(const TestData *td, bool enable)
|
2015-06-28 20:58:57 +03:00
|
|
|
{
|
|
|
|
uint32_t val;
|
|
|
|
|
2019-04-09 11:52:41 +03:00
|
|
|
val = qtest_readl(td->qts, RCBA_BASE_ADDR + ICH9_CC_GCS);
|
2015-06-28 20:58:57 +03:00
|
|
|
if (enable) {
|
|
|
|
val &= ~ICH9_CC_GCS_NO_REBOOT;
|
|
|
|
} else {
|
|
|
|
val |= ICH9_CC_GCS_NO_REBOOT;
|
|
|
|
}
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_writel(td->qts, RCBA_BASE_ADDR + ICH9_CC_GCS, val);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco_defaults(void)
|
|
|
|
{
|
|
|
|
TestData d;
|
|
|
|
|
|
|
|
d.args = NULL;
|
2015-06-28 20:58:58 +03:00
|
|
|
d.noreboot = true;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&d);
|
2016-10-24 07:52:06 +03:00
|
|
|
g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD), ==,
|
2015-06-28 20:58:57 +03:00
|
|
|
TCO_RLD_DEFAULT);
|
|
|
|
/* TCO_DAT_IN & TCO_DAT_OUT */
|
2016-10-24 07:52:06 +03:00
|
|
|
g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_DAT_IN), ==,
|
2015-06-28 20:58:57 +03:00
|
|
|
(TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
|
|
|
|
/* TCO1_STS & TCO2_STS */
|
2016-10-24 07:52:06 +03:00
|
|
|
g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_STS), ==,
|
2015-06-28 20:58:57 +03:00
|
|
|
(TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
|
|
|
|
/* TCO1_CNT & TCO2_CNT */
|
2016-10-24 07:52:06 +03:00
|
|
|
g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_CNT), ==,
|
2015-06-28 20:58:57 +03:00
|
|
|
(TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
|
|
|
|
/* TCO_MESSAGE1 & TCO_MESSAGE2 */
|
2016-10-24 07:52:06 +03:00
|
|
|
g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_MESSAGE1), ==,
|
2015-06-28 20:58:57 +03:00
|
|
|
(TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
|
2016-10-24 07:52:06 +03:00
|
|
|
g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, TCO_WDCNT), ==,
|
2015-06-28 20:58:57 +03:00
|
|
|
TCO_WDCNT_DEFAULT);
|
2016-10-24 07:52:06 +03:00
|
|
|
g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, SW_IRQ_GEN), ==,
|
2015-06-28 20:58:57 +03:00
|
|
|
SW_IRQ_GEN_DEFAULT);
|
2016-10-24 07:52:06 +03:00
|
|
|
g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_TMR), ==,
|
2015-06-28 20:58:57 +03:00
|
|
|
TCO_TMR_DEFAULT);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&d);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco_timeout(void)
|
|
|
|
{
|
|
|
|
TestData d;
|
|
|
|
const uint16_t ticks = TCO_SECS_TO_TICKS(4);
|
|
|
|
uint32_t val;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
d.args = NULL;
|
2015-06-28 20:58:58 +03:00
|
|
|
d.noreboot = true;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&d);
|
|
|
|
|
|
|
|
stop_tco(&d);
|
|
|
|
clear_tco_status(&d);
|
2019-04-09 11:52:41 +03:00
|
|
|
reset_on_second_timeout(&d, false);
|
2015-06-28 20:58:57 +03:00
|
|
|
set_tco_timeout(&d, ticks);
|
|
|
|
load_tco(&d);
|
|
|
|
start_tco(&d);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC);
|
2015-06-28 20:58:57 +03:00
|
|
|
|
|
|
|
/* test first timeout */
|
2016-10-24 07:52:06 +03:00
|
|
|
val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
|
2015-06-28 20:58:57 +03:00
|
|
|
ret = val & TCO_TIMEOUT ? 1 : 0;
|
|
|
|
g_assert(ret == 1);
|
|
|
|
|
|
|
|
/* test clearing timeout bit */
|
|
|
|
val |= TCO_TIMEOUT;
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val);
|
|
|
|
val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
|
2015-06-28 20:58:57 +03:00
|
|
|
ret = val & TCO_TIMEOUT ? 1 : 0;
|
|
|
|
g_assert(ret == 0);
|
|
|
|
|
|
|
|
/* test second timeout */
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC);
|
2016-10-24 07:52:06 +03:00
|
|
|
val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
|
2015-06-28 20:58:57 +03:00
|
|
|
ret = val & TCO_TIMEOUT ? 1 : 0;
|
|
|
|
g_assert(ret == 1);
|
2016-10-24 07:52:06 +03:00
|
|
|
val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS);
|
2015-06-28 20:58:57 +03:00
|
|
|
ret = val & TCO_SECOND_TO_STS ? 1 : 0;
|
|
|
|
g_assert(ret == 1);
|
|
|
|
|
|
|
|
stop_tco(&d);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&d);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco_max_timeout(void)
|
|
|
|
{
|
|
|
|
TestData d;
|
|
|
|
const uint16_t ticks = 0xffff;
|
|
|
|
uint32_t val;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
d.args = NULL;
|
2015-06-28 20:58:58 +03:00
|
|
|
d.noreboot = true;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&d);
|
|
|
|
|
|
|
|
stop_tco(&d);
|
|
|
|
clear_tco_status(&d);
|
2019-04-09 11:52:41 +03:00
|
|
|
reset_on_second_timeout(&d, false);
|
2015-06-28 20:58:57 +03:00
|
|
|
set_tco_timeout(&d, ticks);
|
|
|
|
load_tco(&d);
|
|
|
|
start_tco(&d);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(d.qts, ((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
|
2015-06-28 20:58:57 +03:00
|
|
|
|
2016-10-24 07:52:06 +03:00
|
|
|
val = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD);
|
2015-06-28 20:58:57 +03:00
|
|
|
g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
|
2016-10-24 07:52:06 +03:00
|
|
|
val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
|
2015-06-28 20:58:57 +03:00
|
|
|
ret = val & TCO_TIMEOUT ? 1 : 0;
|
|
|
|
g_assert(ret == 0);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(d.qts, TCO_TICK_NSEC);
|
2016-10-24 07:52:06 +03:00
|
|
|
val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
|
2015-06-28 20:58:57 +03:00
|
|
|
ret = val & TCO_TIMEOUT ? 1 : 0;
|
|
|
|
g_assert(ret == 1);
|
|
|
|
|
|
|
|
stop_tco(&d);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&d);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
2019-04-09 11:52:41 +03:00
|
|
|
static QDict *get_watchdog_action(const TestData *td)
|
2015-06-28 20:58:57 +03:00
|
|
|
{
|
2019-04-09 11:52:41 +03:00
|
|
|
QDict *ev = qtest_qmp_eventwait_ref(td->qts, "WATCHDOG");
|
2015-06-28 20:58:57 +03:00
|
|
|
QDict *data;
|
|
|
|
|
|
|
|
data = qdict_get_qdict(ev, "data");
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_ref(data);
|
|
|
|
qobject_unref(ev);
|
2015-06-28 20:58:57 +03:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco_second_timeout_pause(void)
|
|
|
|
{
|
|
|
|
TestData td;
|
|
|
|
const uint16_t ticks = TCO_SECS_TO_TICKS(32);
|
|
|
|
QDict *ad;
|
|
|
|
|
|
|
|
td.args = "-watchdog-action pause";
|
2015-06-28 20:58:58 +03:00
|
|
|
td.noreboot = false;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&td);
|
|
|
|
|
|
|
|
stop_tco(&td);
|
|
|
|
clear_tco_status(&td);
|
2019-04-09 11:52:41 +03:00
|
|
|
reset_on_second_timeout(&td, true);
|
2015-06-28 20:58:57 +03:00
|
|
|
set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
|
|
|
|
load_tco(&td);
|
|
|
|
start_tco(&td);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2);
|
|
|
|
ad = get_watchdog_action(&td);
|
2015-06-28 20:58:57 +03:00
|
|
|
g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(ad);
|
2015-06-28 20:58:57 +03:00
|
|
|
|
|
|
|
stop_tco(&td);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&td);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco_second_timeout_reset(void)
|
|
|
|
{
|
|
|
|
TestData td;
|
|
|
|
const uint16_t ticks = TCO_SECS_TO_TICKS(16);
|
|
|
|
QDict *ad;
|
|
|
|
|
|
|
|
td.args = "-watchdog-action reset";
|
2015-06-28 20:58:58 +03:00
|
|
|
td.noreboot = false;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&td);
|
|
|
|
|
|
|
|
stop_tco(&td);
|
|
|
|
clear_tco_status(&td);
|
2019-04-09 11:52:41 +03:00
|
|
|
reset_on_second_timeout(&td, true);
|
2015-06-28 20:58:57 +03:00
|
|
|
set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
|
|
|
|
load_tco(&td);
|
|
|
|
start_tco(&td);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2);
|
|
|
|
ad = get_watchdog_action(&td);
|
2015-06-28 20:58:57 +03:00
|
|
|
g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(ad);
|
2015-06-28 20:58:57 +03:00
|
|
|
|
|
|
|
stop_tco(&td);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&td);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco_second_timeout_shutdown(void)
|
|
|
|
{
|
|
|
|
TestData td;
|
|
|
|
const uint16_t ticks = TCO_SECS_TO_TICKS(128);
|
|
|
|
QDict *ad;
|
|
|
|
|
|
|
|
td.args = "-watchdog-action shutdown";
|
2015-06-28 20:58:58 +03:00
|
|
|
td.noreboot = false;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&td);
|
|
|
|
|
|
|
|
stop_tco(&td);
|
|
|
|
clear_tco_status(&td);
|
2019-04-09 11:52:41 +03:00
|
|
|
reset_on_second_timeout(&td, true);
|
2015-06-28 20:58:57 +03:00
|
|
|
set_tco_timeout(&td, ticks);
|
|
|
|
load_tco(&td);
|
|
|
|
start_tco(&td);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2);
|
|
|
|
ad = get_watchdog_action(&td);
|
2015-06-28 20:58:57 +03:00
|
|
|
g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(ad);
|
2015-06-28 20:58:57 +03:00
|
|
|
|
|
|
|
stop_tco(&td);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&td);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco_second_timeout_none(void)
|
|
|
|
{
|
|
|
|
TestData td;
|
|
|
|
const uint16_t ticks = TCO_SECS_TO_TICKS(256);
|
|
|
|
QDict *ad;
|
|
|
|
|
|
|
|
td.args = "-watchdog-action none";
|
2015-06-28 20:58:58 +03:00
|
|
|
td.noreboot = false;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&td);
|
|
|
|
|
|
|
|
stop_tco(&td);
|
|
|
|
clear_tco_status(&td);
|
2019-04-09 11:52:41 +03:00
|
|
|
reset_on_second_timeout(&td, true);
|
2015-06-28 20:58:57 +03:00
|
|
|
set_tco_timeout(&td, ticks);
|
|
|
|
load_tco(&td);
|
|
|
|
start_tco(&td);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(td.qts, ticks * TCO_TICK_NSEC * 2);
|
|
|
|
ad = get_watchdog_action(&td);
|
2015-06-28 20:58:57 +03:00
|
|
|
g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(ad);
|
2015-06-28 20:58:57 +03:00
|
|
|
|
|
|
|
stop_tco(&td);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&td);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco_ticks_counter(void)
|
|
|
|
{
|
|
|
|
TestData d;
|
|
|
|
uint16_t ticks = TCO_SECS_TO_TICKS(8);
|
|
|
|
uint16_t rld;
|
|
|
|
|
|
|
|
d.args = NULL;
|
2015-06-28 20:58:58 +03:00
|
|
|
d.noreboot = true;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&d);
|
|
|
|
|
|
|
|
stop_tco(&d);
|
|
|
|
clear_tco_status(&d);
|
2019-04-09 11:52:41 +03:00
|
|
|
reset_on_second_timeout(&d, false);
|
2015-06-28 20:58:57 +03:00
|
|
|
set_tco_timeout(&d, ticks);
|
|
|
|
load_tco(&d);
|
|
|
|
start_tco(&d);
|
|
|
|
|
|
|
|
do {
|
2016-10-24 07:52:06 +03:00
|
|
|
rld = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD) & TCO_RLD_MASK;
|
2015-06-28 20:58:57 +03:00
|
|
|
g_assert_cmpint(rld, ==, ticks);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(d.qts, TCO_TICK_NSEC);
|
2015-06-28 20:58:57 +03:00
|
|
|
ticks--;
|
2016-10-24 07:52:06 +03:00
|
|
|
} while (!(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS) & TCO_TIMEOUT));
|
2015-06-28 20:58:57 +03:00
|
|
|
|
|
|
|
stop_tco(&d);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&d);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco1_control_bits(void)
|
|
|
|
{
|
|
|
|
TestData d;
|
|
|
|
uint16_t val;
|
|
|
|
|
|
|
|
d.args = NULL;
|
2015-06-28 20:58:58 +03:00
|
|
|
d.noreboot = true;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&d);
|
|
|
|
|
|
|
|
val = TCO_LOCK;
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val);
|
2015-06-28 20:58:57 +03:00
|
|
|
val &= ~TCO_LOCK;
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val);
|
|
|
|
g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_CNT), ==,
|
2015-06-28 20:58:57 +03:00
|
|
|
TCO_LOCK);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&d);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco1_status_bits(void)
|
|
|
|
{
|
|
|
|
TestData d;
|
|
|
|
uint16_t ticks = 8;
|
|
|
|
uint16_t val;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
d.args = NULL;
|
2015-06-28 20:58:58 +03:00
|
|
|
d.noreboot = true;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&d);
|
|
|
|
|
|
|
|
stop_tco(&d);
|
|
|
|
clear_tco_status(&d);
|
2019-04-09 11:52:41 +03:00
|
|
|
reset_on_second_timeout(&d, false);
|
2015-06-28 20:58:57 +03:00
|
|
|
set_tco_timeout(&d, ticks);
|
|
|
|
load_tco(&d);
|
|
|
|
start_tco(&d);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC);
|
2015-06-28 20:58:57 +03:00
|
|
|
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_IN, 0);
|
|
|
|
qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_OUT, 0);
|
|
|
|
val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
|
2015-06-28 20:58:57 +03:00
|
|
|
ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
|
|
|
|
g_assert(ret == 1);
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val);
|
|
|
|
g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS), ==, 0);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&d);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_tco2_status_bits(void)
|
|
|
|
{
|
|
|
|
TestData d;
|
|
|
|
uint16_t ticks = 8;
|
|
|
|
uint16_t val;
|
|
|
|
int ret;
|
|
|
|
|
2015-06-28 20:58:58 +03:00
|
|
|
d.args = NULL;
|
|
|
|
d.noreboot = true;
|
2015-06-28 20:58:57 +03:00
|
|
|
test_init(&d);
|
|
|
|
|
|
|
|
stop_tco(&d);
|
|
|
|
clear_tco_status(&d);
|
2019-04-09 11:52:41 +03:00
|
|
|
reset_on_second_timeout(&d, true);
|
2015-06-28 20:58:57 +03:00
|
|
|
set_tco_timeout(&d, ticks);
|
|
|
|
load_tco(&d);
|
|
|
|
start_tco(&d);
|
2019-04-09 11:52:41 +03:00
|
|
|
qtest_clock_step(d.qts, ticks * TCO_TICK_NSEC * 2);
|
2015-06-28 20:58:57 +03:00
|
|
|
|
2016-10-24 07:52:06 +03:00
|
|
|
val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS);
|
2015-06-28 20:58:57 +03:00
|
|
|
ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
|
|
|
|
g_assert(ret == 1);
|
2016-10-24 07:52:06 +03:00
|
|
|
qpci_io_writew(d.dev, d.tco_io_bar, TCO2_STS, val);
|
|
|
|
g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS), ==, 0);
|
2017-02-05 23:47:24 +03:00
|
|
|
test_end(&d);
|
2015-06-28 20:58:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
|
|
|
|
qtest_add_func("tco/defaults", test_tco_defaults);
|
|
|
|
qtest_add_func("tco/timeout/no_action", test_tco_timeout);
|
|
|
|
qtest_add_func("tco/timeout/no_action/max", test_tco_max_timeout);
|
|
|
|
qtest_add_func("tco/second_timeout/pause", test_tco_second_timeout_pause);
|
|
|
|
qtest_add_func("tco/second_timeout/reset", test_tco_second_timeout_reset);
|
|
|
|
qtest_add_func("tco/second_timeout/shutdown",
|
|
|
|
test_tco_second_timeout_shutdown);
|
|
|
|
qtest_add_func("tco/second_timeout/none", test_tco_second_timeout_none);
|
|
|
|
qtest_add_func("tco/counter", test_tco_ticks_counter);
|
|
|
|
qtest_add_func("tco/tco1_control/bits", test_tco1_control_bits);
|
|
|
|
qtest_add_func("tco/tco1_status/bits", test_tco1_status_bits);
|
|
|
|
qtest_add_func("tco/tco2_status/bits", test_tco2_status_bits);
|
|
|
|
return g_test_run();
|
|
|
|
}
|