All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation
@ 2015-05-27  0:29 Paulo Alcantara
  2015-05-27  0:29 ` [Qemu-devel] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
                   ` (8 more replies)
  0 siblings, 9 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-05-27  0:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara

This interface provides some registers within a 32-byte range and can be
acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).

It's commonly used as a watchdog timer to detect system lockups through
SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
NO_REBOOT bit is not set in GCS (General Control and Status register),
the system will be resetted upon second timeout if TCO_RLD register
wasn't previously written to prevent timeout.

This patch adds support to TCO watchdog logic and few other features
like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
etc. are not implemented yet.

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 hw/acpi/Makefile.objs  |   1 +
 hw/acpi/ich9.c         |  36 ++++++++++
 hw/acpi/tco.c          | 188 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/isa/lpc_ich9.c      |  10 +++
 include/hw/acpi/ich9.h |   4 ++
 include/hw/acpi/tco.h  | 139 ++++++++++++++++++++++++++++++++++++
 include/hw/i386/ich9.h |   8 +++
 7 files changed, 386 insertions(+)
 create mode 100644 hw/acpi/tco.c
 create mode 100644 include/hw/acpi/tco.h

diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index b9fefa7..a32b7f8 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,4 +1,5 @@
 common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o cpu_hotplug.o
+common-obj-$(CONFIG_ACPI) += tco.o
 common-obj-$(CONFIG_ACPI) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI) += acpi_interface.o
 common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 84e5bb8..8c4364b 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -30,6 +30,7 @@
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "hw/acpi/acpi.h"
+#include "hw/acpi/tco.h"
 #include "sysemu/kvm.h"
 #include "exec/address-spaces.h"
 
@@ -92,8 +93,15 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
                             unsigned width)
 {
     ICH9LPCPMRegs *pm = opaque;
+    TCOIORegs *tr = &pm->tco_regs;
+
     switch (addr) {
     case 0:
+        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
+        if (tr->tco.cnt1 & TCO_LOCK) {
+            val &= ~ICH9_PMIO_SMI_EN_TCO_EN;
+            val |= pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
+        }
         pm->smi_en = val;
         break;
     }
@@ -107,6 +115,29 @@ static const MemoryRegionOps ich9_smi_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
+static uint64_t ich9_tco_readw(void *opaque, hwaddr addr, unsigned width)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    return acpi_pm_tco_ioport_readw(&pm->tco_regs, addr);
+}
+
+static void ich9_tco_writew(void *opaque, hwaddr addr, uint64_t val,
+                            unsigned width)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    acpi_pm_tco_ioport_writew(&pm->tco_regs, addr, val);
+}
+
+static const MemoryRegionOps ich9_tco_ops = {
+    .read = ich9_tco_readw,
+    .write = ich9_tco_writew,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
 void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
 {
     ICH9_DEBUG("to 0x%x\n", pm_io_base);
@@ -230,6 +261,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
                           "acpi-smi", 8);
     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
 
+    acpi_pm_tco_init(&pm->tco_regs);
+    memory_region_init_io(&pm->io_tco, OBJECT(lpc_pci), &ich9_tco_ops, pm,
+			  "sm-tco", ICH9_PMIO_TCO_LEN);
+    memory_region_add_subregion(&pm->io, ICH9_PMIO_TCO_RLD, &pm->io_tco);
+
     pm->irq = sci_irq;
     qemu_register_reset(pm_reset, pm);
     pm->powerdown_notifier.notify = pm_powerdown_req;
diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
new file mode 100644
index 0000000..3a44a95
--- /dev/null
+++ b/hw/acpi/tco.c
@@ -0,0 +1,188 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "sysemu/watchdog.h"
+#include "hw/i386/ich9.h"
+
+#include "hw/acpi/tco.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define TCO_DEBUG(fmt, ...)                                     \
+    do {                                                        \
+        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
+    } while (0)
+#else
+#define TCO_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+static QEMUTimer *tco_timer;
+static unsigned int timeouts_no;
+
+static inline void tco_timer_reload(void)
+{
+    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+    timer_del(tco_timer);
+    timer_mod(tco_timer, now + tco_ticks_per_sec());
+}
+
+static inline void tco_timer_stop(void)
+{
+    timer_del(tco_timer);
+}
+
+static void tco_timer_expired(void *opaque)
+{
+    TCOIORegs *tr = opaque;
+    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
+    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
+    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_LPC_RCBA_GCS);
+
+    tr->tco.rld--;
+    if (tr->tco.rld & TCO_RLD_MASK) {
+        goto out;
+    }
+
+    tr->tco.sts1 |= TCO_TIMEOUT;
+    if (++timeouts_no == 2) {
+        tr->tco.sts1 |= TCO_SECOND_TO_STS;
+        tr->tco.sts1 |= TCO_BOOT_STS;
+        timeouts_no = 0;
+
+        if (!(gcs & ICH9_LPC_RCBA_GCS_NO_REBOOT)) {
+            watchdog_perform_action();
+            tco_timer_stop();
+            return;
+        }
+    }
+    tr->tco.rld = tr->tco.tmr;
+
+    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
+        ich9_generate_smi();
+    } else {
+        ich9_generate_nmi();
+    }
+
+out:
+    tco_timer_reload();
+}
+
+void acpi_pm_tco_init(TCOIORegs *tr)
+{
+    *tr = TCO_IO_REGS_DEFAULTS_INIT();
+    tco_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr);
+}
+
+uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
+{
+    switch (addr) {
+    case TCO_RLD:
+        return tr->tco.rld;
+    case TCO_DAT_IN:
+        return tr->tco.din;
+    case TCO_DAT_OUT:
+        return tr->tco.dout;
+    case TCO1_STS:
+        return tr->tco.sts1;
+    case TCO2_STS:
+        return tr->tco.sts2;
+    case TCO1_CNT:
+        return tr->tco.cnt1;
+    case TCO2_CNT:
+        return tr->tco.cnt2;
+    case TCO_MESSAGE1:
+        return tr->tco.msg1;
+    case TCO_MESSAGE2:
+        return tr->tco.msg2;
+    case TCO_WDCNT:
+        return tr->tco.wdcnt;
+    case TCO_TMR:
+        return tr->tco.tmr;
+    case SW_IRQ_GEN:
+        return tr->sw_irq_gen;
+    }
+    return 0;
+}
+
+void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
+{
+    switch (addr) {
+    case TCO_RLD:
+        timeouts_no = 0;
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload();
+        } else {
+            tr->tco.rld = val;
+        }
+        break;
+    case TCO_DAT_IN:
+        tr->tco.din = val;
+        tr->tco.sts1 |= SW_TCO_SMI;
+        ich9_generate_smi();
+        break;
+    case TCO_DAT_OUT:
+        tr->tco.dout = val;
+        tr->tco.sts1 |= TCO_INT_STS;
+        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
+        break;
+    case TCO1_STS:
+        tr->tco.sts1 = val & TCO1_STS_MASK;
+        break;
+    case TCO2_STS:
+        tr->tco.sts2 = val & TCO2_STS_MASK;
+        break;
+    case TCO1_CNT:
+        val &= TCO1_CNT_MASK;
+        /* TCO_LOCK bit cannot be changed once set */
+        tr->tco.cnt1 = (val & ~TCO_LOCK) | (tr->tco.cnt1 & TCO_LOCK);
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload();
+        } else {
+            tco_timer_stop();
+        }
+        break;
+    case TCO2_CNT:
+        tr->tco.cnt2 = val;
+        break;
+    case TCO_MESSAGE1:
+        tr->tco.msg1 = val;
+        break;
+    case TCO_MESSAGE2:
+        tr->tco.msg2 = val;
+        break;
+    case TCO_WDCNT:
+        tr->tco.wdcnt = val;
+        break;
+    case TCO_TMR:
+        tr->tco.tmr = val;
+        break;
+    case SW_IRQ_GEN:
+        tr->sw_irq_gen = val;
+        break;
+    }
+}
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index dba7585..7bfb683 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -313,6 +313,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
     return route;
 }
 
+void ich9_generate_smi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+}
+
+void ich9_generate_nmi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
+}
+
 static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
 {
     switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
index c2d3dba..31c74af 100644
--- a/include/hw/acpi/ich9.h
+++ b/include/hw/acpi/ich9.h
@@ -25,6 +25,7 @@
 #include "hw/acpi/cpu_hotplug.h"
 #include "hw/acpi/memory_hotplug.h"
 #include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/tco.h"
 
 typedef struct ICH9LPCPMRegs {
     /*
@@ -37,6 +38,7 @@ typedef struct ICH9LPCPMRegs {
     MemoryRegion io;
     MemoryRegion io_gpe;
     MemoryRegion io_smi;
+    MemoryRegion io_tco;
 
     uint32_t smi_en;
     uint32_t smi_sts;
@@ -53,6 +55,8 @@ typedef struct ICH9LPCPMRegs {
     uint8_t disable_s3;
     uint8_t disable_s4;
     uint8_t s4_val;
+
+    TCOIORegs tco_regs;
 } ICH9LPCPMRegs;
 
 void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
new file mode 100644
index 0000000..700532c
--- /dev/null
+++ b/include/hw/acpi/tco.h
@@ -0,0 +1,139 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef HW_ACPI_TCO_H
+#define HW_ACPI_TCO_H
+
+#include "qemu/typedefs.h"
+#include "qemu-common.h"
+
+/* TCO I/O register offsets */
+enum {
+    TCO_RLD           = 0x00,
+    TCO_DAT_IN        = 0x02,
+    TCO_DAT_OUT       = 0x03,
+    TCO1_STS          = 0x04,
+    TCO2_STS          = 0x06,
+    TCO1_CNT          = 0x08,
+    TCO2_CNT          = 0x0a,
+    TCO_MESSAGE1      = 0x0c,
+    TCO_MESSAGE2      = 0x0d,
+    TCO_WDCNT         = 0x0e,
+    SW_IRQ_GEN        = 0x10,
+    TCO_TMR           = 0x12,
+};
+
+/* TCO I/O register defaults */
+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,
+};
+
+/* TCO I/O register control/status bits */
+enum {
+    SW_TCO_SMI           = (1 << 1),
+    TCO_INT_STS          = (1 << 2),
+    TCO_LOCK             = (1 << 12),
+    TCO_TMR_HLT          = (1 << 11),
+    TCO_TIMEOUT          = (1 << 3),
+    TCO_SECOND_TO_STS    = (1 << 1),
+    TCO_BOOT_STS         = (1 << 2),
+};
+
+/* TCO I/O registers mask bits */
+enum {
+    TCO_RLD_MASK     = 0x3ff,
+    TCO1_STS_MASK    = 0xe870,
+    TCO2_STS_MASK    = 0xfff8,
+    TCO1_CNT_MASK    = 0xfeff,
+    TCO_TMR_MASK     = 0x3ff,
+};
+
+typedef struct TCOIORegs {
+    struct {
+        uint16_t rld;
+        uint8_t din;
+        uint8_t dout;
+        uint16_t sts1;
+        uint16_t sts2;
+        uint16_t cnt1;
+        uint16_t cnt2;
+        uint8_t msg1;
+        uint8_t msg2;
+        uint8_t wdcnt;
+        uint16_t tmr;
+    } tco;
+    uint8_t sw_irq_gen;
+} TCOIORegs;
+
+#define TCO_IO_REGS_DEFAULTS_INIT()             \
+    (TCOIORegs) {                               \
+        .tco = {                                \
+            .rld      = TCO_RLD_DEFAULT,        \
+            .din      = TCO_DAT_IN_DEFAULT,     \
+            .dout     = TCO_DAT_OUT_DEFAULT,    \
+            .sts1     = TCO1_STS_DEFAULT,       \
+            .sts2     = TCO2_STS_DEFAULT,       \
+            .cnt1     = TCO1_CNT_DEFAULT,       \
+            .cnt2     = TCO2_CNT_DEFAULT,       \
+            .msg1     = TCO_MESSAGE1_DEFAULT,   \
+            .msg2     = TCO_MESSAGE2_DEFAULT,   \
+            .wdcnt    = TCO_WDCNT_DEFAULT,      \
+            .tmr      = TCO_TMR_DEFAULT,        \
+        },                                      \
+        .sw_irq_gen = SW_IRQ_GEN_DEFAULT        \
+    }
+
+/* tco.c */
+void acpi_pm_tco_init(TCOIORegs *tr);
+uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr);
+void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val);
+
+/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
+static inline int64_t tco_ticks_per_sec(void)
+{
+    return 600000000LL;
+}
+
+static inline int is_valid_tco_time(uint32_t val)
+{
+    /* values of 0 or 1 will be ignored by ICH */
+    return val > 1;
+}
+
+static inline int can_start_tco_timer(TCOIORegs *tr)
+{
+    return !(tr->tco.cnt1 & TCO_TMR_HLT) && is_valid_tco_time(tr->tco.tmr);
+}
+
+#endif /* HW_ACPI_TCO_H */
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index f4e522c..f41cca6 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -20,6 +20,9 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
 void ich9_lpc_pm_init(PCIDevice *pci_lpc);
 I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
 
+void ich9_generate_smi(void);
+void ich9_generate_nmi(void);
+
 #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
 
 #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
@@ -156,6 +159,8 @@ Object *ich9_lpc_find(void);
 #define ICH9_LPC_RCBA_BA_MASK                   Q35_MASK(32, 31, 14)
 #define ICH9_LPC_RCBA_EN                        0x1
 #define ICH9_LPC_RCBA_DEFAULT                   0x0
+#define ICH9_LPC_RCBA_GCS                       0x3410
+#define ICH9_LPC_RCBA_GCS_NO_REBOOT             (1 << 5)
 
 #define ICH9_LPC_PIC_NUM_PINS                   16
 #define ICH9_LPC_IOAPIC_NUM_PINS                24
@@ -180,7 +185,10 @@ Object *ich9_lpc_find(void);
 #define ICH9_PMIO_GPE0_LEN                      16
 #define ICH9_PMIO_SMI_EN                        0x30
 #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
+#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
 #define ICH9_PMIO_SMI_STS                       0x34
+#define ICH9_PMIO_TCO_RLD                       0x60
+#define ICH9_PMIO_TCO_LEN                       32
 
 /* FADT ACPI_ENABLE/ACPI_DISABLE */
 #define ICH9_APM_ACPI_ENABLE                    0x2
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-05-27  0:29 [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paulo Alcantara
@ 2015-05-27  0:29 ` Paulo Alcantara
  2015-05-27 12:03   ` Paolo Bonzini
  2015-05-28  7:13   ` [Qemu-devel] [SeaBIOS] " Gerd Hoffmann
  2015-05-27  0:29 ` [Qemu-devel] [PATCH 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-05-27  0:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 hw/i386/acpi-dsdt-pdrc.dsl    |  46 ++++++++++++++++++++++++++++++++++++++++++
 hw/i386/q35-acpi-dsdt.dsl     |   1 +
 tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7795 bytes
 3 files changed, 47 insertions(+)
 create mode 100644 hw/i386/acpi-dsdt-pdrc.dsl

diff --git a/hw/i386/acpi-dsdt-pdrc.dsl b/hw/i386/acpi-dsdt-pdrc.dsl
new file mode 100644
index 0000000..badb410
--- /dev/null
+++ b/hw/i386/acpi-dsdt-pdrc.dsl
@@ -0,0 +1,46 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/****************************************************************
+ * PCI Device Resource Comsumption
+ ****************************************************************/
+
+Scope(\_SB.PCI0) {
+    Device (PDRC) {
+        Name (_HID, EISAID("PNP0C02"))
+        Name (_UID, 1)
+
+        Name (PDRS, ResourceTemplate() {
+	    Memory32Fixed(ReadWrite, 0xfed1c000, 0x00004000)
+        })
+
+        Method (_CRS, 0, Serialized) {
+            Return(PDRS)
+        }
+    }
+}
+
+Scope(\_SB) {
+    OperationRegion (RCRB, SystemMemory, 0xfed1c000, 0x4000)
+    Field (RCRB, DWordAcc, Lock, Preserve) {
+        Offset(0x3000),
+	TCTL, 8,
+	, 24,
+	Offset(0x3400),
+	RTCC, 32,
+	HPTC, 32,
+	GCSR, 32,
+    }
+}
diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
index 16eaca3..32b680e 100644
--- a/hw/i386/q35-acpi-dsdt.dsl
+++ b/hw/i386/q35-acpi-dsdt.dsl
@@ -114,6 +114,7 @@ DefinitionBlock (
         }
     }
 
+#include "acpi-dsdt-pdrc.dsl"
 #include "acpi-dsdt-hpet.dsl"
 
 
diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..beea54c234954c54c3b008b52e3cd167701253c7 100644
GIT binary patch
delta 154
zcmexl{n>`gCD<jTSdM{#annYwc*c5H7QOgjr}zM8PlM<tivX7(XO4IePZu7?3p`95
z@u8kBj2uA0U_n7HzBWz<Mur0y|1mf)FjO*#aK}3b1#>I`$qHD3%!qCXat?B0-~p-O
xW^jmZQ~~jY85$TQLYzZ<I2a@t8vZc`g*ZDacm#wvE4Vuc2Qeya&S3140RYvzDYXCq

delta 24
gcmext^U0daCD<k8lPm)R<DrdQ@r;{aF?PxT0CYGA#sB~S

-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH 3/3] tests: add testcase for TCO watchdog emulation
  2015-05-27  0:29 [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paulo Alcantara
  2015-05-27  0:29 ` [Qemu-devel] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
@ 2015-05-27  0:29 ` Paulo Alcantara
  2015-05-27 11:58 ` [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paolo Bonzini
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-05-27  0:29 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 tests/Makefile   |   2 +
 tests/tco-test.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 349 insertions(+)
 create mode 100644 tests/tco-test.c

diff --git a/tests/Makefile b/tests/Makefile
index 729b969..43950d0 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -150,6 +150,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
 check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
+check-qtest-i386-y += tests/tco-test$(EXESUF)
 gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
 check-qtest-i386-y += $(check-qtest-pci-y)
 gcov-files-i386-y += $(gcov-files-pci-y)
@@ -363,6 +364,7 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
 tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
 tests/ne2000-test$(EXESUF): tests/ne2000-test.o
 tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
+tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
 tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
 tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
 tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
diff --git a/tests/tco-test.c b/tests/tco-test.c
new file mode 100644
index 0000000..ed5a685
--- /dev/null
+++ b/tests/tco-test.c
@@ -0,0 +1,347 @@
+/*
+ * QEMU ICH9 TCO emulation tests
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/i386/ich9.h"
+#include "hw/acpi/ich9.h"
+#include "hw/acpi/tco.h"
+
+#define PM_IO_BASE_ADDR    0xb000
+#define RCBA_BASE_ADDR     0xfed1c000
+
+#define TCO_SECS_TO_TICKS(secs) ((secs) * 10 / 16)
+
+typedef struct {
+    const char *args;
+    QPCIDevice *dev;
+    void *lpc_base;
+    void *tco_io_base;
+} TestData;
+
+static void test_init(TestData *d)
+{
+    QPCIBus *bus;
+    QTestState *qs;
+    char *s;
+
+    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
+    qs = qtest_start(s);
+    qtest_irq_intercept_in(qs, "ioapic");
+    g_free(s);
+
+    bus = qpci_init_pc();
+    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
+    g_assert(d->dev != NULL);
+
+    /* map PCI-to-LPC bridge interface BAR */
+    d->lpc_base = qpci_iomap(d->dev, 0, NULL);
+
+    qpci_device_enable(d->dev);
+
+    g_assert(d->lpc_base != NULL);
+
+    /* set ACPI PM I/O space base address */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_PMBASE,
+                       PM_IO_BASE_ADDR | 0x1);
+    /* enable ACPI I/O */
+    qpci_config_writeb(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_ACPI_CTRL,
+                       0x80);
+    /* set Root Complex BAR */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_RCBA,
+                       RCBA_BASE_ADDR | 0x1);
+
+    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
+}
+
+static void stop_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val |= TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void start_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val &= ~TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void load_tco(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
+}
+
+static void set_tco_timeout(const TestData *d, uint16_t secs)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR,
+                   TCO_SECS_TO_TICKS(secs));
+}
+
+static void clear_tco_status(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
+}
+
+static void reset_on_second_timeout(bool enable)
+{
+    uint32_t val;
+
+    val = readl(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS);
+    if (enable) {
+        val &= ~ICH9_LPC_RCBA_GCS_NO_REBOOT;
+    } else {
+        val |= ICH9_LPC_RCBA_GCS_NO_REBOOT;
+    }
+    writel(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS, val);
+}
+
+static void test_tco_defaults(void)
+{
+    TestData d;
+
+    d.args = NULL;
+    test_init(&d);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+                    TCO_RLD_DEFAULT);
+    /* TCO_DAT_IN & TCO_DAT_OUT */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
+                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
+    /* TCO1_STS & TCO2_STS */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
+                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
+    /* TCO1_CNT & TCO2_CNT */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
+    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
+                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
+                    TCO_WDCNT_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
+                    SW_IRQ_GEN_DEFAULT);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
+                    TCO_TMR_DEFAULT);
+    qtest_end();
+}
+
+static void test_tco_timeout(void)
+{
+    TestData d;
+    uint32_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, 4);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(4 * 1000000000LL);
+
+    /* test first timeout */
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    /* test clearing timeout bit */
+    val |= TCO_TIMEOUT;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+
+    /* test second timeout */
+    clock_step(4 * 1000000000LL);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static QDict *get_watchdog_action(void)
+{
+    QDict *ev = qmp("");
+    QDict *data;
+    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
+
+    data = qdict_get_qdict(ev, "data");
+    QINCREF(data);
+    QDECREF(ev);
+    return data;
+}
+
+static void test_tco_second_timeout_pause(void)
+{
+    TestData td;
+    QDict *ad;
+
+    td.args = "-watchdog-action pause";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, 16);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(16 * 1000000000LL);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_second_timeout_reset(void)
+{
+    TestData td;
+    QDict *ad;
+
+    td.args = "-watchdog-action reset";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, 16);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(16 * 1000000000LL);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_second_timeout_shutdown(void)
+{
+    TestData td;
+    QDict *ad;
+
+    td.args = "-watchdog-action shutdown";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, 16);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(16 * 1000000000LL);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_second_timeout_none(void)
+{
+    TestData td;
+    QDict *ad;
+
+    td.args = "-watchdog-action none";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, 16);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(16 * 1000000000LL);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_ticks_counter(void)
+{
+    TestData d;
+    int secs = 8;
+    unsigned int ticks;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    set_tco_timeout(&d, secs);
+    load_tco(&d);
+    start_tco(&d);
+
+    ticks = TCO_SECS_TO_TICKS(secs);
+    do {
+        g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+                        ticks);
+        clock_step(600000000LL);
+        ticks--;
+    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+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/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);
+    return g_test_run();
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation
  2015-05-27  0:29 [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paulo Alcantara
  2015-05-27  0:29 ` [Qemu-devel] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
  2015-05-27  0:29 ` [Qemu-devel] [PATCH 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
@ 2015-05-27 11:58 ` Paolo Bonzini
  2015-05-27 18:23   ` Paulo Alcantara
  2015-05-30 22:04 ` [Qemu-devel] [PATCH v2 " Paulo Alcantara
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 83+ messages in thread
From: Paolo Bonzini @ 2015-05-27 11:58 UTC (permalink / raw)
  To: Paulo Alcantara, qemu-devel; +Cc: seabios



On 27/05/2015 02:29, Paulo Alcantara wrote:
> This interface provides some registers within a 32-byte range and can be
> acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).
> 
> It's commonly used as a watchdog timer to detect system lockups through
> SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
> NO_REBOOT bit is not set in GCS (General Control and Status register),
> the system will be resetted upon second timeout if TCO_RLD register
> wasn't previously written to prevent timeout.
> 
> This patch adds support to TCO watchdog logic and few other features
> like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
> etc. are not implemented yet.
> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>

Hi,

the main issue here is lack of migration support.  You need to add a new
subsection to vmstate_ich9_pm that is migrated if the registers are
different from the default values.

Other comments inline.

> diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> new file mode 100644
> index 0000000..3a44a95
> --- /dev/null
> +++ b/hw/acpi/tco.c
> @@ -0,0 +1,188 @@
> +/*
> + * QEMU ICH9 TCO emulation
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include "qemu-common.h"
> +#include "sysemu/watchdog.h"
> +#include "hw/i386/ich9.h"
> +
> +#include "hw/acpi/tco.h"
> +
> +//#define DEBUG
> +
> +#ifdef DEBUG
> +#define TCO_DEBUG(fmt, ...)                                     \
> +    do {                                                        \
> +        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
> +    } while (0)
> +#else
> +#define TCO_DEBUG(fmt, ...) do { } while (0)
> +#endif
> +
> +static QEMUTimer *tco_timer;
> +static unsigned int timeouts_no;

These must not be globals.  Instead, add them to TCOIORegs.

> +static inline void tco_timer_reload(void)
> +{
> +    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +
> +    timer_del(tco_timer);
> +    timer_mod(tco_timer, now + tco_ticks_per_sec());

No need for del+mod.  Also, these are not tco_ticks_per_sec() but rather
TCO_TICK_NSEC.  It can be a #define instead of a function.

For a little extra challenge, you could the base value when the timer
was started inside TCOIORegs.  The timer can be made to expire directly
after 0.6*TCO_TMR seconds; you can use the base value and the current
time to compute the value of TCO_RLD.  Something like:

    case TCO_RLD:
        rld = tr->tco.rld;
        /* base_clock is set to -1 in tco_timer_stop, reloaded in
         * tco_timer_reload.
         */
        if (base_clock != -1) {
            elapsed = (qemu_get_clock(QEMU_CLOCK_VIRTUAL) - base_clock)
                        / TCO_TICK_NSEC;
            rld -= MIN(elapsed, rld);
        }

This makes it possible for QEMU to sleep most of the time when the guest
is idle, instead of waking up every 0.6 seconds.  But feel free to work
on this afterwards.

> +void acpi_pm_tco_init(TCOIORegs *tr)
> +{
> +    *tr = TCO_IO_REGS_DEFAULTS_INIT();

Just inline the macro definition here.

> +void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
> +{
> +    switch (addr) {
> +    case TCO_RLD:
> +        timeouts_no = 0;
> +        if (can_start_tco_timer(tr)) {
> +            tr->tco.rld = tr->tco.tmr;
> +            tco_timer_reload();
> +        } else {
> +            tr->tco.rld = val;
> +        }
> +        break;
> +    case TCO_DAT_IN:
> +        tr->tco.din = val;
> +        tr->tco.sts1 |= SW_TCO_SMI;
> +        ich9_generate_smi();
> +        break;
> +    case TCO_DAT_OUT:
> +        tr->tco.dout = val;
> +        tr->tco.sts1 |= TCO_INT_STS;
> +        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
> +        break;
> +    case TCO1_STS:
> +        tr->tco.sts1 = val & TCO1_STS_MASK;
> +        break;
> +    case TCO2_STS:
> +        tr->tco.sts2 = val & TCO2_STS_MASK;
> +        break;
> +    case TCO1_CNT:
> +        val &= TCO1_CNT_MASK;
> +        /* TCO_LOCK bit cannot be changed once set */
> +        tr->tco.cnt1 = (val & ~TCO_LOCK) | (tr->tco.cnt1 & TCO_LOCK);

This means that TCO_LOCK is never set in tr->tco.cnt1, I think?  If I'm
correct, it should be covered by tests.

> +        if (can_start_tco_timer(tr)) {
> +            tr->tco.rld = tr->tco.tmr;
> +            tco_timer_reload();
> +        } else {
> +            tco_timer_stop();
> +        }
> +        break;

> diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
> new file mode 100644
> index 0000000..700532c
> --- /dev/null
> +++ b/include/hw/acpi/tco.h
> @@ -0,0 +1,139 @@
> +/*
> + * QEMU ICH9 TCO emulation
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#ifndef HW_ACPI_TCO_H
> +#define HW_ACPI_TCO_H
> +
> +#include "qemu/typedefs.h"
> +#include "qemu-common.h"
> +
> +/* TCO I/O register offsets */
> +enum {
> +    TCO_RLD           = 0x00,
> +    TCO_DAT_IN        = 0x02,
> +    TCO_DAT_OUT       = 0x03,
> +    TCO1_STS          = 0x04,
> +    TCO2_STS          = 0x06,
> +    TCO1_CNT          = 0x08,
> +    TCO2_CNT          = 0x0a,
> +    TCO_MESSAGE1      = 0x0c,
> +    TCO_MESSAGE2      = 0x0d,
> +    TCO_WDCNT         = 0x0e,
> +    SW_IRQ_GEN        = 0x10,
> +    TCO_TMR           = 0x12,
> +};
> +
> +/* TCO I/O register defaults */
> +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,
> +};
> +
> +/* TCO I/O register control/status bits */
> +enum {
> +    SW_TCO_SMI           = (1 << 1),
> +    TCO_INT_STS          = (1 << 2),
> +    TCO_LOCK             = (1 << 12),
> +    TCO_TMR_HLT          = (1 << 11),
> +    TCO_TIMEOUT          = (1 << 3),
> +    TCO_SECOND_TO_STS    = (1 << 1),
> +    TCO_BOOT_STS         = (1 << 2),
> +};
> +
> +/* TCO I/O registers mask bits */
> +enum {
> +    TCO_RLD_MASK     = 0x3ff,
> +    TCO1_STS_MASK    = 0xe870,
> +    TCO2_STS_MASK    = 0xfff8,
> +    TCO1_CNT_MASK    = 0xfeff,
> +    TCO_TMR_MASK     = 0x3ff,
> +};
> +
> +typedef struct TCOIORegs {
> +    struct {
> +        uint16_t rld;
> +        uint8_t din;
> +        uint8_t dout;
> +        uint16_t sts1;
> +        uint16_t sts2;
> +        uint16_t cnt1;
> +        uint16_t cnt2;
> +        uint8_t msg1;
> +        uint8_t msg2;
> +        uint8_t wdcnt;
> +        uint16_t tmr;
> +    } tco;
> +    uint8_t sw_irq_gen;
> +} TCOIORegs;
> +
> +#define TCO_IO_REGS_DEFAULTS_INIT()             \
> +    (TCOIORegs) {                               \
> +        .tco = {                                \
> +            .rld      = TCO_RLD_DEFAULT,        \
> +            .din      = TCO_DAT_IN_DEFAULT,     \
> +            .dout     = TCO_DAT_OUT_DEFAULT,    \
> +            .sts1     = TCO1_STS_DEFAULT,       \
> +            .sts2     = TCO2_STS_DEFAULT,       \
> +            .cnt1     = TCO1_CNT_DEFAULT,       \
> +            .cnt2     = TCO2_CNT_DEFAULT,       \
> +            .msg1     = TCO_MESSAGE1_DEFAULT,   \
> +            .msg2     = TCO_MESSAGE2_DEFAULT,   \
> +            .wdcnt    = TCO_WDCNT_DEFAULT,      \
> +            .tmr      = TCO_TMR_DEFAULT,        \
> +        },                                      \
> +        .sw_irq_gen = SW_IRQ_GEN_DEFAULT        \
> +    }

No need for this definition.  This and the *_DEFAULT definitions as well
should be in the .c file.

> +/* tco.c */
> +void acpi_pm_tco_init(TCOIORegs *tr);
> +uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr);
> +void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val);
> +
> +/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
> +static inline int64_t tco_ticks_per_sec(void)
> +{
> +    return 600000000LL;
> +}
> +
> +static inline int is_valid_tco_time(uint32_t val)
> +{
> +    /* values of 0 or 1 will be ignored by ICH */
> +    return val > 1;
> +}
> +
> +static inline int can_start_tco_timer(TCOIORegs *tr)
> +{
> +    return !(tr->tco.cnt1 & TCO_TMR_HLT) && is_valid_tco_time(tr->tco.tmr);
> +}

These three inlines should be in the .c file.

Don't be discouraged by the comments.  It's good work!

Paolo

> +#endif /* HW_ACPI_TCO_H */
> diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
> index f4e522c..f41cca6 100644
> --- a/include/hw/i386/ich9.h
> +++ b/include/hw/i386/ich9.h
> @@ -20,6 +20,9 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
>  void ich9_lpc_pm_init(PCIDevice *pci_lpc);
>  I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
>  
> +void ich9_generate_smi(void);
> +void ich9_generate_nmi(void);
> +
>  #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
>  
>  #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
> @@ -156,6 +159,8 @@ Object *ich9_lpc_find(void);
>  #define ICH9_LPC_RCBA_BA_MASK                   Q35_MASK(32, 31, 14)
>  #define ICH9_LPC_RCBA_EN                        0x1
>  #define ICH9_LPC_RCBA_DEFAULT                   0x0
> +#define ICH9_LPC_RCBA_GCS                       0x3410
> +#define ICH9_LPC_RCBA_GCS_NO_REBOOT             (1 << 5)
>  
>  #define ICH9_LPC_PIC_NUM_PINS                   16
>  #define ICH9_LPC_IOAPIC_NUM_PINS                24
> @@ -180,7 +185,10 @@ Object *ich9_lpc_find(void);
>  #define ICH9_PMIO_GPE0_LEN                      16
>  #define ICH9_PMIO_SMI_EN                        0x30
>  #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
> +#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
>  #define ICH9_PMIO_SMI_STS                       0x34
> +#define ICH9_PMIO_TCO_RLD                       0x60
> +#define ICH9_PMIO_TCO_LEN                       32
>  
>  /* FADT ACPI_ENABLE/ACPI_DISABLE */
>  #define ICH9_APM_ACPI_ENABLE                    0x2
> 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-05-27  0:29 ` [Qemu-devel] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
@ 2015-05-27 12:03   ` Paolo Bonzini
  2015-05-27 17:51     ` Paulo Alcantara
  2015-05-28  7:13   ` [Qemu-devel] [SeaBIOS] " Gerd Hoffmann
  1 sibling, 1 reply; 83+ messages in thread
From: Paolo Bonzini @ 2015-05-27 12:03 UTC (permalink / raw)
  To: Paulo Alcantara, qemu-devel; +Cc: seabios



On 27/05/2015 02:29, Paulo Alcantara wrote:
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
>  hw/i386/acpi-dsdt-pdrc.dsl    |  46 ++++++++++++++++++++++++++++++++++++++++++

Why pdrc and not e.g. ccr (chipset configuration registers)?  I cannot
find PDRC / PCI device resource consumption in the ICH9 spec.

>  hw/i386/q35-acpi-dsdt.dsl     |   1 +
>  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7795 bytes
>  3 files changed, 47 insertions(+)
>  create mode 100644 hw/i386/acpi-dsdt-pdrc.dsl
> 
> diff --git a/hw/i386/acpi-dsdt-pdrc.dsl b/hw/i386/acpi-dsdt-pdrc.dsl
> new file mode 100644
> index 0000000..badb410
> --- /dev/null
> +++ b/hw/i386/acpi-dsdt-pdrc.dsl
> @@ -0,0 +1,46 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> +
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> +
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +/****************************************************************
> + * PCI Device Resource Comsumption

"Chipset configuration registers"

> + ****************************************************************/
> +
> +Scope(\_SB.PCI0) {
> +    Device (PDRC) {

Device (CCR)

> +        Name (_HID, EISAID("PNP0C02"))
> +        Name (_UID, 1)
> +
> +        Name (PDRS, ResourceTemplate() {

Just use Name(_CRS, ResourceTemplate() { ... })

> +	    Memory32Fixed(ReadWrite, 0xfed1c000, 0x00004000)
> +        })
> +
> +        Method (_CRS, 0, Serialized) {
> +            Return(PDRS)
> +        }
> +    }
> +}
> +
> +Scope(\_SB) {
> +    OperationRegion (RCRB, SystemMemory, 0xfed1c000, 0x4000)
> +    Field (RCRB, DWordAcc, Lock, Preserve) {
> +        Offset(0x3000),
> +	TCTL, 8,
> +	, 24,
> +	Offset(0x3400),
> +	RTCC, 32,
> +	HPTC, 32,
> +	GCSR, 32,
> +    }

Why do you need the RCRB OperationRegion if you never access it?

> +}
> diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> index 16eaca3..32b680e 100644
> --- a/hw/i386/q35-acpi-dsdt.dsl
> +++ b/hw/i386/q35-acpi-dsdt.dsl
> @@ -114,6 +114,7 @@ DefinitionBlock (
>          }
>      }
>  
> +#include "acpi-dsdt-pdrc.dsl"

Just include it in this file since it's not shared between i440FX and Q35.

Thanks,

Paolo

>  #include "acpi-dsdt-hpet.dsl"
>  
>  
> diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
> index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..beea54c234954c54c3b008b52e3cd167701253c7 100644
> GIT binary patch
> delta 154
> zcmexl{n>`gCD<jTSdM{#annYwc*c5H7QOgjr}zM8PlM<tivX7(XO4IePZu7?3p`95
> z@u8kBj2uA0U_n7HzBWz<Mur0y|1mf)FjO*#aK}3b1#>I`$qHD3%!qCXat?B0-~p-O
> xW^jmZQ~~jY85$TQLYzZ<I2a@t8vZc`g*ZDacm#wvE4Vuc2Qeya&S3140RYvzDYXCq
> 
> delta 24
> gcmext^U0daCD<k8lPm)R<DrdQ@r;{aF?PxT0CYGA#sB~S
> 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-05-27 12:03   ` Paolo Bonzini
@ 2015-05-27 17:51     ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-05-27 17:51 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: seabios, Paulo Alcantara, qemu-devel

On Wed, 27 May 2015 14:03:59 +0200
Paolo Bonzini <pbonzini@redhat.com> wrote:

> 
> 
> On 27/05/2015 02:29, Paulo Alcantara wrote:
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > ---
> >  hw/i386/acpi-dsdt-pdrc.dsl    |  46
> > ++++++++++++++++++++++++++++++++++++++++++
> 
> Why pdrc and not e.g. ccr (chipset configuration registers)?  I cannot
> find PDRC / PCI device resource consumption in the ICH9 spec.

I've found the "PDRC" name being used in ACPI DSDT table definitions on
the internet for the MS legacy PNP ID "PNP0C02". However, the ICH9 does
mention "Chipset Configuration registers" so it's really more
meaningful to name it as "CCR" and then matching spec.

> 
> >  hw/i386/q35-acpi-dsdt.dsl     |   1 +
> >  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7795 bytes
> >  3 files changed, 47 insertions(+)
> >  create mode 100644 hw/i386/acpi-dsdt-pdrc.dsl
> > 
> > diff --git a/hw/i386/acpi-dsdt-pdrc.dsl b/hw/i386/acpi-dsdt-pdrc.dsl
> > new file mode 100644
> > index 0000000..badb410
> > --- /dev/null
> > +++ b/hw/i386/acpi-dsdt-pdrc.dsl
> > @@ -0,0 +1,46 @@
> > +/*
> > + * This program is free software; you can redistribute it and/or
> > modify
> > + * it under the terms of the GNU General Public License as
> > published by
> > + * the Free Software Foundation; either version 2 of the License,
> > or
> > + * (at your option) any later version.
> > +
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > +
> > + * You should have received a copy of the GNU General Public
> > License along
> > + * with this program; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +/****************************************************************
> > + * PCI Device Resource Comsumption
> 
> "Chipset configuration registers"

OK.

> 
> > + ****************************************************************/
> > +
> > +Scope(\_SB.PCI0) {
> > +    Device (PDRC) {
> 
> Device (CCR)

OK.

> 
> > +        Name (_HID, EISAID("PNP0C02"))
> > +        Name (_UID, 1)
> > +
> > +        Name (PDRS, ResourceTemplate() {
> 
> Just use Name(_CRS, ResourceTemplate() { ... })

OK.

> 
> > +	    Memory32Fixed(ReadWrite, 0xfed1c000, 0x00004000)
> > +        })
> > +
> > +        Method (_CRS, 0, Serialized) {
> > +            Return(PDRS)
> > +        }
> > +    }
> > +}
> > +
> > +Scope(\_SB) {
> > +    OperationRegion (RCRB, SystemMemory, 0xfed1c000, 0x4000)
> > +    Field (RCRB, DWordAcc, Lock, Preserve) {
> > +        Offset(0x3000),
> > +	TCTL, 8,
> > +	, 24,
> > +	Offset(0x3400),
> > +	RTCC, 32,
> > +	HPTC, 32,
> > +	GCSR, 32,
> > +    }
> 
> Why do you need the RCRB OperationRegion if you never access it?

Indeed. It's not being referenced in any AML code so I'll remove it.

> 
> > +}
> > diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> > index 16eaca3..32b680e 100644
> > --- a/hw/i386/q35-acpi-dsdt.dsl
> > +++ b/hw/i386/q35-acpi-dsdt.dsl
> > @@ -114,6 +114,7 @@ DefinitionBlock (
> >          }
> >      }
> >  
> > +#include "acpi-dsdt-pdrc.dsl"
> 
> Just include it in this file since it's not shared between i440FX and
> Q35.

OK.

I'll send a v2 with all proposed changes.

Thanks,

Paulo 

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation
  2015-05-27 11:58 ` [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paolo Bonzini
@ 2015-05-27 18:23   ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-05-27 18:23 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: seabios, Paulo Alcantara, qemu-devel

On Wed, 27 May 2015 13:58:57 +0200
Paolo Bonzini <pbonzini@redhat.com> wrote:

> the main issue here is lack of migration support.  You need to add a
> new subsection to vmstate_ich9_pm that is migrated if the registers
> are different from the default values.

Hrm - OK. I didn't even take that into account. I'll do it.

> > +static QEMUTimer *tco_timer;
> > +static unsigned int timeouts_no;
> 
> These must not be globals.  Instead, add them to TCOIORegs.

OK.

> 
> > +static inline void tco_timer_reload(void)
> > +{
> > +    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > +
> > +    timer_del(tco_timer);
> > +    timer_mod(tco_timer, now + tco_ticks_per_sec());
> 
> No need for del+mod.  Also, these are not tco_ticks_per_sec() but
> rather TCO_TICK_NSEC.  It can be a #define instead of a function.

OK.

> For a little extra challenge, you could the base value when the timer
> was started inside TCOIORegs.  The timer can be made to expire
> directly after 0.6*TCO_TMR seconds; you can use the base value and
> the current time to compute the value of TCO_RLD.  Something like:
> 
>     case TCO_RLD:
>         rld = tr->tco.rld;
>         /* base_clock is set to -1 in tco_timer_stop, reloaded in
>          * tco_timer_reload.
>          */
>         if (base_clock != -1) {
>             elapsed = (qemu_get_clock(QEMU_CLOCK_VIRTUAL) -
> base_clock) / TCO_TICK_NSEC;
>             rld -= MIN(elapsed, rld);
>         }
> 
> This makes it possible for QEMU to sleep most of the time when the
> guest is idle, instead of waking up every 0.6 seconds.  But feel free
> to work on this afterwards.

Nice. I liked your approach and that will definitely avoid overheard
waking up on every 0.6s. No, I will work on this approach right now so
I can sleep well.

> 
> > +void acpi_pm_tco_init(TCOIORegs *tr)
> > +{
> > +    *tr = TCO_IO_REGS_DEFAULTS_INIT();
> 
> Just inline the macro definition here.

OK.

> > +    case TCO1_CNT:
> > +        val &= TCO1_CNT_MASK;
> > +        /* TCO_LOCK bit cannot be changed once set */
> > +        tr->tco.cnt1 = (val & ~TCO_LOCK) | (tr->tco.cnt1 &
> > TCO_LOCK);
> 
> This means that TCO_LOCK is never set in tr->tco.cnt1, I think?  If
> I'm correct, it should be covered by tests.

No. The TCO_LOCK bit is set in tr->tco.cnt1, but cannot be unset
afterwards -- on reset, of course, it will. Maybe a test that covers
setting it and then unsetting it might be interesting to have in
tests/tco-test.c.

> > +#define TCO_IO_REGS_DEFAULTS_INIT()             \
> > +    (TCOIORegs) {                               \
> > +        .tco = {                                \
> > +            .rld      = TCO_RLD_DEFAULT,        \
> > +            .din      = TCO_DAT_IN_DEFAULT,     \
> > +            .dout     = TCO_DAT_OUT_DEFAULT,    \
> > +            .sts1     = TCO1_STS_DEFAULT,       \
> > +            .sts2     = TCO2_STS_DEFAULT,       \
> > +            .cnt1     = TCO1_CNT_DEFAULT,       \
> > +            .cnt2     = TCO2_CNT_DEFAULT,       \
> > +            .msg1     = TCO_MESSAGE1_DEFAULT,   \
> > +            .msg2     = TCO_MESSAGE2_DEFAULT,   \
> > +            .wdcnt    = TCO_WDCNT_DEFAULT,      \
> > +            .tmr      = TCO_TMR_DEFAULT,        \
> > +        },                                      \
> > +        .sw_irq_gen = SW_IRQ_GEN_DEFAULT        \
> > +    }
> 
> No need for this definition.  This and the *_DEFAULT definitions as
> well should be in the .c file.

OK.

> 
> > +/* tco.c */
> > +void acpi_pm_tco_init(TCOIORegs *tr);
> > +uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr);
> > +void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr,
> > uint32_t val); +
> > +/* As per ICH9 spec, the internal timer has an error of ~0.6s on
> > every tick */ +static inline int64_t tco_ticks_per_sec(void)
> > +{
> > +    return 600000000LL;
> > +}
> > +
> > +static inline int is_valid_tco_time(uint32_t val)
> > +{
> > +    /* values of 0 or 1 will be ignored by ICH */
> > +    return val > 1;
> > +}
> > +
> > +static inline int can_start_tco_timer(TCOIORegs *tr)
> > +{
> > +    return !(tr->tco.cnt1 & TCO_TMR_HLT) &&
> > is_valid_tco_time(tr->tco.tmr); +}
> 
> These three inlines should be in the .c file.

OK.

> 
> Don't be discouraged by the comments.  It's good work!

Alright. Thank you for the comments! I'll send a v2 soon.

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-05-27  0:29 ` [Qemu-devel] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
  2015-05-27 12:03   ` Paolo Bonzini
@ 2015-05-28  7:13   ` Gerd Hoffmann
  2015-05-30 10:57     ` Paulo Alcantara
  1 sibling, 1 reply; 83+ messages in thread
From: Gerd Hoffmann @ 2015-05-28  7:13 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, qemu-devel

  Hi,

> +Scope(\_SB) {
> +    OperationRegion (RCRB, SystemMemory, 0xfed1c000, 0x4000)

Where does this address come from?
Is this a standard location suggested by intel specs?
Or is the firmware free to choose it?

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-05-28  7:13   ` [Qemu-devel] [SeaBIOS] " Gerd Hoffmann
@ 2015-05-30 10:57     ` Paulo Alcantara
  2015-06-01  7:16       ` Gerd Hoffmann
  0 siblings, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-05-30 10:57 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

Hi Gerd,

On Thu, 28 May 2015 09:13:35 +0200
Gerd Hoffmann <kraxel@redhat.com> wrote:

> > +Scope(\_SB) {
> > +    OperationRegion (RCRB, SystemMemory, 0xfed1c000, 0x4000)
> 
> Where does this address come from?

This address is reserved in an ACPI DSDT table for Intel Haswell in
Coreboot project, Vlv2DeviceRefCodePkg package in EDK II as well as my
Haswell laptop on which I can see it through `dmesg` :-)

> Is this a standard location suggested by intel specs?

I haven't found any Intel spec or any other document that suggests such
address, but from "9.4 Memory Map" section in ICH9 spec, it seems safe
to use that MMIO region for the 16KiB of chipset configuration
registers.

> Or is the firmware free to choose it?

Given that, I would say so.

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v2 1/3] ich9: add TCO interface emulation
  2015-05-27  0:29 [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paulo Alcantara
                   ` (2 preceding siblings ...)
  2015-05-27 11:58 ` [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paolo Bonzini
@ 2015-05-30 22:04 ` Paulo Alcantara
  2015-05-30 22:04   ` [Qemu-devel] [PATCH v2 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
                     ` (3 more replies)
  2015-06-22  0:37 ` [Qemu-devel] [PATCH v4 " Paulo Alcantara
                   ` (4 subsequent siblings)
  8 siblings, 4 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-05-30 22:04 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara

This interface provides some registers within a 32-byte range and can be
acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).

It's commonly used as a watchdog timer to detect system lockups through
SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
NO_REBOOT bit is not set in GCS (General Control and Status register),
the system will be resetted upon second timeout if TCO_RLD register
wasn't previously written to prevent timeout.

This patch adds support to TCO watchdog logic and few other features
like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
etc. are not implemented yet.

v1 -> v2:
  * add migration support for TCO I/O device state
  * wake up only when total time expired instead of every 0.6s
  * some cleanup suggested by Paolo Bonzini

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 hw/acpi/Makefile.objs  |   2 +-
 hw/acpi/ich9.c         |  59 ++++++++++++
 hw/acpi/tco.c          | 254 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/isa/lpc_ich9.c      |  10 ++
 include/hw/acpi/ich9.h |   4 +
 include/hw/acpi/tco.h  |  98 +++++++++++++++++++
 include/hw/i386/ich9.h |   8 ++
 7 files changed, 434 insertions(+), 1 deletion(-)
 create mode 100644 hw/acpi/tco.c
 create mode 100644 include/hw/acpi/tco.h

diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 29d46d8..3db1f07 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
+common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o tco.o
 common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
 common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI) += acpi_interface.o
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 84e5bb8..10959fa 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -30,6 +30,7 @@
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "hw/acpi/acpi.h"
+#include "hw/acpi/tco.h"
 #include "sysemu/kvm.h"
 #include "exec/address-spaces.h"
 
@@ -92,8 +93,15 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
                             unsigned width)
 {
     ICH9LPCPMRegs *pm = opaque;
+    TCOIORegs *tr = &pm->tco_regs;
+
     switch (addr) {
     case 0:
+        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
+        if (tr->tco.cnt1 & TCO_LOCK) {
+            val &= ~ICH9_PMIO_SMI_EN_TCO_EN;
+            val |= pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
+        }
         pm->smi_en = val;
         break;
     }
@@ -107,6 +115,29 @@ static const MemoryRegionOps ich9_smi_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
+static uint64_t ich9_tco_readw(void *opaque, hwaddr addr, unsigned width)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    return acpi_pm_tco_ioport_readw(&pm->tco_regs, addr);
+}
+
+static void ich9_tco_writew(void *opaque, hwaddr addr, uint64_t val,
+                            unsigned width)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    acpi_pm_tco_ioport_writew(&pm->tco_regs, addr, val);
+}
+
+static const MemoryRegionOps ich9_tco_ops = {
+    .read = ich9_tco_readw,
+    .write = ich9_tco_writew,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
 void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
 {
     ICH9_DEBUG("to 0x%x\n", pm_io_base);
@@ -157,6 +188,24 @@ static const VMStateDescription vmstate_memhp_state = {
     }
 };
 
+static bool vmstate_test_use_tco(void *opaque)
+{
+    ICH9LPCPMRegs *s = opaque;
+    return s->tco_regs.use_tco;
+}
+
+static const VMStateDescription vmstate_tco_io_state = {
+    .name = "ich9_pm/tco",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
+                       TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 const VMStateDescription vmstate_ich9_pm = {
     .name = "ich9_pm",
     .version_id = 1,
@@ -179,6 +228,10 @@ const VMStateDescription vmstate_ich9_pm = {
             .vmsd = &vmstate_memhp_state,
             .needed = vmstate_test_use_memhp,
         },
+        {
+            .vmsd = &vmstate_tco_io_state,
+            .needed = vmstate_test_use_tco,
+        },
         VMSTATE_END_OF_LIST()
     }
 };
@@ -230,6 +283,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
                           "acpi-smi", 8);
     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
 
+    acpi_pm_tco_init(&pm->tco_regs);
+    memory_region_init_io(&pm->io_tco, OBJECT(lpc_pci), &ich9_tco_ops, pm,
+			  "sm-tco", ICH9_PMIO_TCO_LEN);
+    memory_region_add_subregion(&pm->io, ICH9_PMIO_TCO_RLD, &pm->io_tco);
+
     pm->irq = sci_irq;
     qemu_register_reset(pm_reset, pm);
     pm->powerdown_notifier.notify = pm_powerdown_req;
@@ -357,6 +415,7 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
     pm->disable_s3 = 0;
     pm->disable_s4 = 0;
     pm->s4_val = 2;
+    pm->tco_regs.use_tco = true;
 
     object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE,
                                    &pm->pm_io_base, errp);
diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
new file mode 100644
index 0000000..5dd6e11
--- /dev/null
+++ b/hw/acpi/tco.c
@@ -0,0 +1,254 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "sysemu/watchdog.h"
+#include "hw/i386/ich9.h"
+
+#include "hw/acpi/tco.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define TCO_DEBUG(fmt, ...)                                     \
+    do {                                                        \
+        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
+    } while (0)
+#else
+#define TCO_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+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,
+};
+
+static inline void tco_timer_reload(TCOIORegs *tr)
+{
+    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
+    timer_mod(tr->tco_timer, tr->expire_time);
+}
+
+static inline void tco_timer_stop(TCOIORegs *tr)
+{
+    tr->expire_time = -1;
+}
+
+static void tco_timer_expired(void *opaque)
+{
+    TCOIORegs *tr = opaque;
+    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
+    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
+    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_LPC_RCBA_GCS);
+
+    tr->tco.rld = 0;
+    tr->tco.sts1 |= TCO_TIMEOUT;
+    if (++tr->timeouts_no == 2) {
+        tr->tco.sts1 |= TCO_SECOND_TO_STS;
+        tr->tco.sts1 |= TCO_BOOT_STS;
+        tr->timeouts_no = 0;
+
+        if (!(gcs & ICH9_LPC_RCBA_GCS_NO_REBOOT)) {
+            watchdog_perform_action();
+            tco_timer_stop(tr);
+            return;
+        }
+    }
+
+    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
+        ich9_generate_smi();
+    } else {
+        ich9_generate_nmi();
+    }
+    tr->tco.rld = tr->tco.tmr;
+    tco_timer_reload(tr);
+}
+
+void acpi_pm_tco_init(TCOIORegs *tr)
+{
+    *tr = (TCOIORegs) {
+        .tco = {
+            .rld      = TCO_RLD_DEFAULT,
+            .din      = TCO_DAT_IN_DEFAULT,
+            .dout     = TCO_DAT_OUT_DEFAULT,
+            .sts1     = TCO1_STS_DEFAULT,
+            .sts2     = TCO2_STS_DEFAULT,
+            .cnt1     = TCO1_CNT_DEFAULT,
+            .cnt2     = TCO2_CNT_DEFAULT,
+            .msg1     = TCO_MESSAGE1_DEFAULT,
+            .msg2     = TCO_MESSAGE2_DEFAULT,
+            .wdcnt    = TCO_WDCNT_DEFAULT,
+            .tmr      = TCO_TMR_DEFAULT,
+        },
+        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
+        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
+        .expire_time   = -1,
+        .timeouts_no   = 0,
+    };
+}
+
+/* NOTE: values of 0 or 1 will be ignored by ICH */
+static inline int can_start_tco_timer(TCOIORegs *tr)
+{
+    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
+}
+
+uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
+{
+    uint16_t rld;
+
+    switch (addr) {
+    case TCO_RLD:
+        if (tr->expire_time != -1) {
+            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
+            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
+        } else {
+            rld = tr->tco.rld;
+        }
+        return rld;
+    case TCO_DAT_IN:
+        return tr->tco.din;
+    case TCO_DAT_OUT:
+        return tr->tco.dout;
+    case TCO1_STS:
+        return tr->tco.sts1;
+    case TCO2_STS:
+        return tr->tco.sts2;
+    case TCO1_CNT:
+        return tr->tco.cnt1;
+    case TCO2_CNT:
+        return tr->tco.cnt2;
+    case TCO_MESSAGE1:
+        return tr->tco.msg1;
+    case TCO_MESSAGE2:
+        return tr->tco.msg2;
+    case TCO_WDCNT:
+        return tr->tco.wdcnt;
+    case TCO_TMR:
+        return tr->tco.tmr;
+    case SW_IRQ_GEN:
+        return tr->sw_irq_gen;
+    }
+    return 0;
+}
+
+void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
+{
+    switch (addr) {
+    case TCO_RLD:
+        tr->timeouts_no = 0;
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tr->tco.rld = val;
+        }
+        break;
+    case TCO_DAT_IN:
+        tr->tco.din = val;
+        tr->tco.sts1 |= SW_TCO_SMI;
+        ich9_generate_smi();
+        break;
+    case TCO_DAT_OUT:
+        tr->tco.dout = val;
+        tr->tco.sts1 |= TCO_INT_STS;
+        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
+        break;
+    case TCO1_STS:
+        tr->tco.sts1 = val & TCO1_STS_MASK;
+        break;
+    case TCO2_STS:
+        tr->tco.sts2 = val & TCO2_STS_MASK;
+        break;
+    case TCO1_CNT:
+        val &= TCO1_CNT_MASK;
+        /*
+         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
+         * is required to change this bit from 1 to 0 -- it defaults to 0.
+         */
+        tr->tco.cnt1 = tr->tco.cnt1 & TCO_LOCK ? val | TCO_LOCK : val;
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tco_timer_stop(tr);
+        }
+        break;
+    case TCO2_CNT:
+        tr->tco.cnt2 = val;
+        break;
+    case TCO_MESSAGE1:
+        tr->tco.msg1 = val;
+        break;
+    case TCO_MESSAGE2:
+        tr->tco.msg2 = val;
+        break;
+    case TCO_WDCNT:
+        tr->tco.wdcnt = val;
+        break;
+    case TCO_TMR:
+        tr->tco.tmr = val;
+        break;
+    case SW_IRQ_GEN:
+        tr->sw_irq_gen = val;
+        break;
+    }
+}
+
+const VMStateDescription vmstate_tco_io_sts = {
+    .name = "tco io device status",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(use_tco, TCOIORegs),
+        VMSTATE_UINT16(tco.rld, TCOIORegs),
+        VMSTATE_UINT8(tco.din, TCOIORegs),
+        VMSTATE_UINT8(tco.dout, TCOIORegs),
+        VMSTATE_UINT16(tco.sts1, TCOIORegs),
+        VMSTATE_UINT16(tco.sts2, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
+        VMSTATE_UINT8(tco.msg1, TCOIORegs),
+        VMSTATE_UINT8(tco.msg2, TCOIORegs),
+        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
+        VMSTATE_UINT16(tco.tmr, TCOIORegs),
+        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
+        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
+        VMSTATE_INT64(expire_time, TCOIORegs),
+        VMSTATE_UINT8(timeouts_no, TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index dba7585..7bfb683 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -313,6 +313,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
     return route;
 }
 
+void ich9_generate_smi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+}
+
+void ich9_generate_nmi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
+}
+
 static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
 {
     switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
index c2d3dba..31c74af 100644
--- a/include/hw/acpi/ich9.h
+++ b/include/hw/acpi/ich9.h
@@ -25,6 +25,7 @@
 #include "hw/acpi/cpu_hotplug.h"
 #include "hw/acpi/memory_hotplug.h"
 #include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/tco.h"
 
 typedef struct ICH9LPCPMRegs {
     /*
@@ -37,6 +38,7 @@ typedef struct ICH9LPCPMRegs {
     MemoryRegion io;
     MemoryRegion io_gpe;
     MemoryRegion io_smi;
+    MemoryRegion io_tco;
 
     uint32_t smi_en;
     uint32_t smi_sts;
@@ -53,6 +55,8 @@ typedef struct ICH9LPCPMRegs {
     uint8_t disable_s3;
     uint8_t disable_s4;
     uint8_t s4_val;
+
+    TCOIORegs tco_regs;
 } ICH9LPCPMRegs;
 
 void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
new file mode 100644
index 0000000..7240522
--- /dev/null
+++ b/include/hw/acpi/tco.h
@@ -0,0 +1,98 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef HW_ACPI_TCO_H
+#define HW_ACPI_TCO_H
+
+#include "qemu/typedefs.h"
+#include "qemu-common.h"
+
+/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
+#define TCO_TICK_NSEC 600000000LL
+
+/* TCO I/O register offsets */
+enum {
+    TCO_RLD           = 0x00,
+    TCO_DAT_IN        = 0x02,
+    TCO_DAT_OUT       = 0x03,
+    TCO1_STS          = 0x04,
+    TCO2_STS          = 0x06,
+    TCO1_CNT          = 0x08,
+    TCO2_CNT          = 0x0a,
+    TCO_MESSAGE1      = 0x0c,
+    TCO_MESSAGE2      = 0x0d,
+    TCO_WDCNT         = 0x0e,
+    SW_IRQ_GEN        = 0x10,
+    TCO_TMR           = 0x12,
+};
+
+/* TCO I/O register control/status bits */
+enum {
+    SW_TCO_SMI           = (1 << 1),
+    TCO_INT_STS          = (1 << 2),
+    TCO_LOCK             = (1 << 12),
+    TCO_TMR_HLT          = (1 << 11),
+    TCO_TIMEOUT          = (1 << 3),
+    TCO_SECOND_TO_STS    = (1 << 1),
+    TCO_BOOT_STS         = (1 << 2),
+};
+
+/* TCO I/O registers mask bits */
+enum {
+    TCO_RLD_MASK     = 0x3ff,
+    TCO1_STS_MASK    = 0xe870,
+    TCO2_STS_MASK    = 0xfff8,
+    TCO1_CNT_MASK    = 0xfeff,
+    TCO_TMR_MASK     = 0x3ff,
+};
+
+typedef struct TCOIORegs {
+    bool use_tco;
+    struct {
+        uint16_t rld;
+        uint8_t din;
+        uint8_t dout;
+        uint16_t sts1;
+        uint16_t sts2;
+        uint16_t cnt1;
+        uint16_t cnt2;
+        uint8_t msg1;
+        uint8_t msg2;
+        uint8_t wdcnt;
+        uint16_t tmr;
+    } tco;
+    uint8_t sw_irq_gen;
+
+    QEMUTimer *tco_timer;
+    int64_t expire_time;
+    uint8_t timeouts_no;
+} TCOIORegs;
+
+/* tco.c */
+void acpi_pm_tco_init(TCOIORegs *tr);
+uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr);
+void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val);
+
+extern const VMStateDescription vmstate_tco_io_sts;
+
+#endif /* HW_ACPI_TCO_H */
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index f4e522c..f41cca6 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -20,6 +20,9 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
 void ich9_lpc_pm_init(PCIDevice *pci_lpc);
 I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
 
+void ich9_generate_smi(void);
+void ich9_generate_nmi(void);
+
 #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
 
 #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
@@ -156,6 +159,8 @@ Object *ich9_lpc_find(void);
 #define ICH9_LPC_RCBA_BA_MASK                   Q35_MASK(32, 31, 14)
 #define ICH9_LPC_RCBA_EN                        0x1
 #define ICH9_LPC_RCBA_DEFAULT                   0x0
+#define ICH9_LPC_RCBA_GCS                       0x3410
+#define ICH9_LPC_RCBA_GCS_NO_REBOOT             (1 << 5)
 
 #define ICH9_LPC_PIC_NUM_PINS                   16
 #define ICH9_LPC_IOAPIC_NUM_PINS                24
@@ -180,7 +185,10 @@ Object *ich9_lpc_find(void);
 #define ICH9_PMIO_GPE0_LEN                      16
 #define ICH9_PMIO_SMI_EN                        0x30
 #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
+#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
 #define ICH9_PMIO_SMI_STS                       0x34
+#define ICH9_PMIO_TCO_RLD                       0x60
+#define ICH9_PMIO_TCO_LEN                       32
 
 /* FADT ACPI_ENABLE/ACPI_DISABLE */
 #define ICH9_APM_ACPI_ENABLE                    0x2
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v2 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-05-30 22:04 ` [Qemu-devel] [PATCH v2 " Paulo Alcantara
@ 2015-05-30 22:04   ` Paulo Alcantara
  2015-05-30 22:04   ` [Qemu-devel] [PATCH v2 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-05-30 22:04 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara

v1 -> v2:
  * s/PDRC/CCR/ for clarity and match ICH9 spec
  * remove unnecessary OperationRegion for RCRB

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 hw/i386/q35-acpi-dsdt.dsl     |  14 ++++++++++++++
 tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
 2 files changed, 14 insertions(+)

diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
index 16eaca3..92675c8 100644
--- a/hw/i386/q35-acpi-dsdt.dsl
+++ b/hw/i386/q35-acpi-dsdt.dsl
@@ -114,6 +114,20 @@ DefinitionBlock (
         }
     }
 
+/****************************************************************
+ * Chipset Configuration Registers
+ ****************************************************************/
+Scope(\_SB.PCI0) {
+    Device (CCR) {
+        Name (_HID, EISAID("PNP0C02"))
+        Name (_UID, 1)
+
+        Name (_CRS, ResourceTemplate() {
+	    Memory32Fixed(ReadWrite, 0xfed1c000, 0x00004000) // RCBA
+        })
+    }
+}
+
 #include "acpi-dsdt-hpet.dsl"
 
 
diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49 100644
GIT binary patch
delta 81
zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w

delta 24
gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs

-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v2 3/3] tests: add testcase for TCO watchdog emulation
  2015-05-30 22:04 ` [Qemu-devel] [PATCH v2 " Paulo Alcantara
  2015-05-30 22:04   ` [Qemu-devel] [PATCH v2 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
@ 2015-05-30 22:04   ` Paulo Alcantara
  2015-06-01  9:07     ` Paolo Bonzini
  2015-06-01  9:05   ` [Qemu-devel] [PATCH v2 1/3] ich9: add TCO interface emulation Paolo Bonzini
  2015-06-01 23:48   ` [Qemu-devel] [PATCH v3 " Paulo Alcantara
  3 siblings, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-05-30 22:04 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara

v1 -> v2:
  * some cleanup
  * added test for TCO_LOCK bit

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 tests/Makefile   |   2 +
 tests/tco-test.c | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 421 insertions(+)
 create mode 100644 tests/tco-test.c

diff --git a/tests/Makefile b/tests/Makefile
index 729b969..43950d0 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -150,6 +150,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
 check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
+check-qtest-i386-y += tests/tco-test$(EXESUF)
 gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
 check-qtest-i386-y += $(check-qtest-pci-y)
 gcov-files-i386-y += $(gcov-files-pci-y)
@@ -363,6 +364,7 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
 tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
 tests/ne2000-test$(EXESUF): tests/ne2000-test.o
 tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
+tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
 tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
 tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
 tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
diff --git a/tests/tco-test.c b/tests/tco-test.c
new file mode 100644
index 0000000..47c15c9
--- /dev/null
+++ b/tests/tco-test.c
@@ -0,0 +1,419 @@
+/*
+ * QEMU ICH9 TCO emulation tests
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/i386/ich9.h"
+#include "hw/acpi/ich9.h"
+#include "hw/acpi/tco.h"
+
+#define PM_IO_BASE_ADDR    0xb000
+#define RCBA_BASE_ADDR     0xfed1c000
+
+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;
+    QPCIDevice *dev;
+    void *lpc_base;
+    void *tco_io_base;
+} TestData;
+
+static void test_init(TestData *d)
+{
+    QPCIBus *bus;
+    QTestState *qs;
+    char *s;
+
+    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
+    qs = qtest_start(s);
+    qtest_irq_intercept_in(qs, "ioapic");
+    g_free(s);
+
+    bus = qpci_init_pc();
+    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
+    g_assert(d->dev != NULL);
+
+    /* map PCI-to-LPC bridge interface BAR */
+    d->lpc_base = qpci_iomap(d->dev, 0, NULL);
+
+    qpci_device_enable(d->dev);
+
+    g_assert(d->lpc_base != NULL);
+
+    /* set ACPI PM I/O space base address */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_PMBASE,
+                       PM_IO_BASE_ADDR | 0x1);
+    /* enable ACPI I/O */
+    qpci_config_writeb(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_ACPI_CTRL,
+                       0x80);
+    /* set Root Complex BAR */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_RCBA,
+                       RCBA_BASE_ADDR | 0x1);
+
+    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
+}
+
+static void stop_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val |= TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void start_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val &= ~TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void load_tco(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
+}
+
+static void set_tco_timeout(const TestData *d, uint16_t ticks)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
+}
+
+static void clear_tco_status(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
+}
+
+static void reset_on_second_timeout(bool enable)
+{
+    uint32_t val;
+
+    val = readl(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS);
+    if (enable) {
+        val &= ~ICH9_LPC_RCBA_GCS_NO_REBOOT;
+    } else {
+        val |= ICH9_LPC_RCBA_GCS_NO_REBOOT;
+    }
+    writel(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS, val);
+}
+
+static void test_tco_defaults(void)
+{
+    TestData d;
+
+    d.args = NULL;
+    test_init(&d);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+                    TCO_RLD_DEFAULT);
+    /* TCO_DAT_IN & TCO_DAT_OUT */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
+                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
+    /* TCO1_STS & TCO2_STS */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
+                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
+    /* TCO1_CNT & TCO2_CNT */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
+    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
+                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
+                    TCO_WDCNT_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
+                    SW_IRQ_GEN_DEFAULT);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
+                    TCO_TMR_DEFAULT);
+    qtest_end();
+}
+
+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;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    /* test first timeout */
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    /* test clearing timeout bit */
+    val |= TCO_TIMEOUT;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+
+    /* test second timeout */
+    clock_step(ticks * TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco_max_timeout(void)
+{
+    TestData d;
+    const uint16_t ticks = 0xffff;
+    uint32_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
+    g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+    clock_step(TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static QDict *get_watchdog_action(void)
+{
+    QDict *ev = qmp("");
+    QDict *data;
+    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
+
+    data = qdict_get_qdict(ev, "data");
+    QINCREF(data);
+    QDECREF(ev);
+    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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_ticks_counter(void)
+{
+    TestData d;
+    uint16_t ticks = TCO_SECS_TO_TICKS(8);
+    uint16_t rld;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+
+    do {
+        rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
+        g_assert_cmpint(rld, ==, ticks);
+        clock_step(TCO_TICK_NSEC);
+        ticks--;
+    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco_lock_bit(void)
+{
+    TestData d;
+    uint16_t val;
+
+    d.args = NULL;
+    test_init(&d);
+
+    val = TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    val &= ~TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    TCO_LOCK);
+    qtest_end();
+}
+
+int main(int argc, char **argv)
+{
+    (void)test_tco_ticks_counter;
+    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/lock_bit", test_tco_lock_bit);
+    return g_test_run();
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-05-30 10:57     ` Paulo Alcantara
@ 2015-06-01  7:16       ` Gerd Hoffmann
  2015-06-01 11:59         ` Paulo Alcantara
  0 siblings, 1 reply; 83+ messages in thread
From: Gerd Hoffmann @ 2015-06-01  7:16 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Sa, 2015-05-30 at 07:57 -0300, Paulo Alcantara wrote:
> Hi Gerd,
> 
> On Thu, 28 May 2015 09:13:35 +0200
> Gerd Hoffmann <kraxel@redhat.com> wrote:
> 
> > > +Scope(\_SB) {
> > > +    OperationRegion (RCRB, SystemMemory, 0xfed1c000, 0x4000)
> > 
> > Where does this address come from?
> 
> This address is reserved in an ACPI DSDT table for Intel Haswell in
> Coreboot project, Vlv2DeviceRefCodePkg package in EDK II as well as my
> Haswell laptop on which I can see it through `dmesg` :-)

So it seems to be kind of standard.
Same address on my laptop btw.

> > Is this a standard location suggested by intel specs?
> 
> I haven't found any Intel spec or any other document that suggests such
> address, but from "9.4 Memory Map" section in ICH9 spec, it seems safe
> to use that MMIO region for the 16KiB of chipset configuration
> registers.
> 
> > Or is the firmware free to choose it?
> 
> Given that, I would say so.

There are a few more cases where addresses programmed by the firmware
and addresses in the acpi tables must match: acpi registers, pci
mmconfig space for example.  They are handles this way:

  (1) firmware programs the hardware registers as it pleases.
  (2) when the firmware fetches the acpi tables from qemu (via fw_cfg)
      qemu will update the tables according to the hardware programming.

So the question is whenever we better do that here too, or whenever it
is fine to simply hardcore this to 0xfed1c000 everywhere ...

I tend to think hardcoding is fine in that case.  coreboot, edk2 &
seabios all place it at the same location, and it is highly unlikely we
ever want move this to another place some day for some reason.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v2 1/3] ich9: add TCO interface emulation
  2015-05-30 22:04 ` [Qemu-devel] [PATCH v2 " Paulo Alcantara
  2015-05-30 22:04   ` [Qemu-devel] [PATCH v2 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
  2015-05-30 22:04   ` [Qemu-devel] [PATCH v2 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
@ 2015-06-01  9:05   ` Paolo Bonzini
  2015-06-01 13:38     ` Paulo Alcantara
  2015-06-01 23:48   ` [Qemu-devel] [PATCH v3 " Paulo Alcantara
  3 siblings, 1 reply; 83+ messages in thread
From: Paolo Bonzini @ 2015-06-01  9:05 UTC (permalink / raw)
  To: Paulo Alcantara, qemu-devel; +Cc: seabios, Michael S. Tsirkin



On 31/05/2015 00:04, Paulo Alcantara wrote:
> +    case TCO_RLD:
> +        tr->timeouts_no = 0;
> +        if (can_start_tco_timer(tr)) {
> +            tr->tco.rld = tr->tco.tmr;
> +            tco_timer_reload(tr);
> +        } else {
> +            tr->tco.rld = val;

Please mask out bits outside TCO_RLD_MASK here, same as you do for
TCO1_STS_MASK and friends.

> +        }
> +        break;

[...]

> +        tr->tco.cnt1 = tr->tco.cnt1 & TCO_LOCK ? val | TCO_LOCK : val;

Since you have to respin, you can do:

	tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);

Otherwise looks good to me.

CCing the maintainer.

Paolo

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v2 3/3] tests: add testcase for TCO watchdog emulation
  2015-05-30 22:04   ` [Qemu-devel] [PATCH v2 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
@ 2015-06-01  9:07     ` Paolo Bonzini
  0 siblings, 0 replies; 83+ messages in thread
From: Paolo Bonzini @ 2015-06-01  9:07 UTC (permalink / raw)
  To: Paulo Alcantara, qemu-devel; +Cc: seabios



On 31/05/2015 00:04, Paulo Alcantara wrote:
> v1 -> v2:
>   * some cleanup
>   * added test for TCO_LOCK bit
> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
>  tests/Makefile   |   2 +
>  tests/tco-test.c | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 421 insertions(+)
>  create mode 100644 tests/tco-test.c
> 
> diff --git a/tests/Makefile b/tests/Makefile
> index 729b969..43950d0 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -150,6 +150,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
>  check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
>  check-qtest-i386-y += tests/drive_del-test$(EXESUF)
>  check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
> +check-qtest-i386-y += tests/tco-test$(EXESUF)
>  gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
>  check-qtest-i386-y += $(check-qtest-pci-y)
>  gcov-files-i386-y += $(gcov-files-pci-y)
> @@ -363,6 +364,7 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
>  tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
>  tests/ne2000-test$(EXESUF): tests/ne2000-test.o
>  tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
> +tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
>  tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
>  tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
>  tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
> diff --git a/tests/tco-test.c b/tests/tco-test.c
> new file mode 100644
> index 0000000..47c15c9
> --- /dev/null
> +++ b/tests/tco-test.c
> @@ -0,0 +1,419 @@
> +/*
> + * QEMU ICH9 TCO emulation tests
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include <glib.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +
> +#include "libqtest.h"
> +#include "libqos/pci.h"
> +#include "libqos/pci-pc.h"
> +#include "hw/pci/pci_regs.h"
> +#include "hw/i386/ich9.h"
> +#include "hw/acpi/ich9.h"
> +#include "hw/acpi/tco.h"
> +
> +#define PM_IO_BASE_ADDR    0xb000
> +#define RCBA_BASE_ADDR     0xfed1c000
> +
> +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;
> +    QPCIDevice *dev;
> +    void *lpc_base;
> +    void *tco_io_base;
> +} TestData;
> +
> +static void test_init(TestData *d)
> +{
> +    QPCIBus *bus;
> +    QTestState *qs;
> +    char *s;
> +
> +    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
> +    qs = qtest_start(s);
> +    qtest_irq_intercept_in(qs, "ioapic");
> +    g_free(s);
> +
> +    bus = qpci_init_pc();
> +    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
> +    g_assert(d->dev != NULL);
> +
> +    /* map PCI-to-LPC bridge interface BAR */
> +    d->lpc_base = qpci_iomap(d->dev, 0, NULL);
> +
> +    qpci_device_enable(d->dev);
> +
> +    g_assert(d->lpc_base != NULL);
> +
> +    /* set ACPI PM I/O space base address */
> +    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_PMBASE,
> +                       PM_IO_BASE_ADDR | 0x1);
> +    /* enable ACPI I/O */
> +    qpci_config_writeb(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_ACPI_CTRL,
> +                       0x80);
> +    /* set Root Complex BAR */
> +    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_RCBA,
> +                       RCBA_BASE_ADDR | 0x1);
> +
> +    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
> +}
> +
> +static void stop_tco(const TestData *d)
> +{
> +    uint32_t val;
> +
> +    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
> +    val |= TCO_TMR_HLT;
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
> +}
> +
> +static void start_tco(const TestData *d)
> +{
> +    uint32_t val;
> +
> +    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
> +    val &= ~TCO_TMR_HLT;
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
> +}
> +
> +static void load_tco(const TestData *d)
> +{
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
> +}
> +
> +static void set_tco_timeout(const TestData *d, uint16_t ticks)
> +{
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
> +}
> +
> +static void clear_tco_status(const TestData *d)
> +{
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
> +}
> +
> +static void reset_on_second_timeout(bool enable)
> +{
> +    uint32_t val;
> +
> +    val = readl(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS);
> +    if (enable) {
> +        val &= ~ICH9_LPC_RCBA_GCS_NO_REBOOT;
> +    } else {
> +        val |= ICH9_LPC_RCBA_GCS_NO_REBOOT;
> +    }
> +    writel(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS, val);
> +}
> +
> +static void test_tco_defaults(void)
> +{
> +    TestData d;
> +
> +    d.args = NULL;
> +    test_init(&d);
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
> +                    TCO_RLD_DEFAULT);
> +    /* TCO_DAT_IN & TCO_DAT_OUT */
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
> +                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
> +    /* TCO1_STS & TCO2_STS */
> +    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
> +                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
> +    /* TCO1_CNT & TCO2_CNT */
> +    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
> +                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
> +    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
> +                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
> +    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
> +                    TCO_WDCNT_DEFAULT);
> +    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
> +                    SW_IRQ_GEN_DEFAULT);
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
> +                    TCO_TMR_DEFAULT);
> +    qtest_end();
> +}
> +
> +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;
> +    test_init(&d);
> +
> +    stop_tco(&d);
> +    clear_tco_status(&d);
> +    reset_on_second_timeout(false);
> +    set_tco_timeout(&d, ticks);
> +    load_tco(&d);
> +    start_tco(&d);
> +    clock_step(ticks * TCO_TICK_NSEC);
> +
> +    /* test first timeout */
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_TIMEOUT ? 1 : 0;
> +    g_assert(ret == 1);
> +
> +    /* test clearing timeout bit */
> +    val |= TCO_TIMEOUT;
> +    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_TIMEOUT ? 1 : 0;
> +    g_assert(ret == 0);
> +
> +    /* test second timeout */
> +    clock_step(ticks * TCO_TICK_NSEC);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_TIMEOUT ? 1 : 0;
> +    g_assert(ret == 1);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
> +    g_assert(ret == 1);
> +
> +    stop_tco(&d);
> +    qtest_end();
> +}
> +
> +static void test_tco_max_timeout(void)
> +{
> +    TestData d;
> +    const uint16_t ticks = 0xffff;
> +    uint32_t val;
> +    int ret;
> +
> +    d.args = NULL;
> +    test_init(&d);
> +
> +    stop_tco(&d);
> +    clear_tco_status(&d);
> +    reset_on_second_timeout(false);
> +    set_tco_timeout(&d, ticks);
> +    load_tco(&d);
> +    start_tco(&d);
> +    clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
> +
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
> +    g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_TIMEOUT ? 1 : 0;
> +    g_assert(ret == 0);
> +    clock_step(TCO_TICK_NSEC);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_TIMEOUT ? 1 : 0;
> +    g_assert(ret == 1);
> +
> +    stop_tco(&d);
> +    qtest_end();
> +}
> +
> +static QDict *get_watchdog_action(void)
> +{
> +    QDict *ev = qmp("");
> +    QDict *data;
> +    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
> +
> +    data = qdict_get_qdict(ev, "data");
> +    QINCREF(data);
> +    QDECREF(ev);
> +    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";
> +    test_init(&td);
> +
> +    stop_tco(&td);
> +    clear_tco_status(&td);
> +    reset_on_second_timeout(true);
> +    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
> +    load_tco(&td);
> +    start_tco(&td);
> +    clock_step(ticks * TCO_TICK_NSEC * 2);
> +    ad = get_watchdog_action();
> +    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
> +    QDECREF(ad);
> +
> +    stop_tco(&td);
> +    qtest_end();
> +}
> +
> +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";
> +    test_init(&td);
> +
> +    stop_tco(&td);
> +    clear_tco_status(&td);
> +    reset_on_second_timeout(true);
> +    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
> +    load_tco(&td);
> +    start_tco(&td);
> +    clock_step(ticks * TCO_TICK_NSEC * 2);
> +    ad = get_watchdog_action();
> +    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
> +    QDECREF(ad);
> +
> +    stop_tco(&td);
> +    qtest_end();
> +}
> +
> +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";
> +    test_init(&td);
> +
> +    stop_tco(&td);
> +    clear_tco_status(&td);
> +    reset_on_second_timeout(true);
> +    set_tco_timeout(&td, ticks);
> +    load_tco(&td);
> +    start_tco(&td);
> +    clock_step(ticks * TCO_TICK_NSEC * 2);
> +    ad = get_watchdog_action();
> +    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
> +    QDECREF(ad);
> +
> +    stop_tco(&td);
> +    qtest_end();
> +}
> +
> +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";
> +    test_init(&td);
> +
> +    stop_tco(&td);
> +    clear_tco_status(&td);
> +    reset_on_second_timeout(true);
> +    set_tco_timeout(&td, ticks);
> +    load_tco(&td);
> +    start_tco(&td);
> +    clock_step(ticks * TCO_TICK_NSEC * 2);
> +    ad = get_watchdog_action();
> +    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
> +    QDECREF(ad);
> +
> +    stop_tco(&td);
> +    qtest_end();
> +}
> +
> +static void test_tco_ticks_counter(void)
> +{
> +    TestData d;
> +    uint16_t ticks = TCO_SECS_TO_TICKS(8);
> +    uint16_t rld;
> +
> +    d.args = NULL;
> +    test_init(&d);
> +
> +    stop_tco(&d);
> +    clear_tco_status(&d);
> +    reset_on_second_timeout(false);
> +    set_tco_timeout(&d, ticks);
> +    load_tco(&d);
> +    start_tco(&d);
> +
> +    do {
> +        rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
> +        g_assert_cmpint(rld, ==, ticks);
> +        clock_step(TCO_TICK_NSEC);
> +        ticks--;
> +    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
> +
> +    stop_tco(&d);
> +    qtest_end();
> +}
> +
> +static void test_tco_lock_bit(void)
> +{
> +    TestData d;
> +    uint16_t val;
> +
> +    d.args = NULL;
> +    test_init(&d);
> +
> +    val = TCO_LOCK;
> +    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
> +    val &= ~TCO_LOCK;
> +    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
> +                    TCO_LOCK);
> +    qtest_end();
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    (void)test_tco_ticks_counter;
> +    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/lock_bit", test_tco_lock_bit);
> +    return g_test_run();
> +}
> 

Looks good.

If you want, you can add a test for the reserved bits of the various
registers.

Paolo

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-01  7:16       ` Gerd Hoffmann
@ 2015-06-01 11:59         ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-01 11:59 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Paulo Alcantara, seabios, Paulo Alcantara, qemu-devel, pbonzini


On Mon, June 1, 2015 4:16 am, Gerd Hoffmann wrote:
> On Sa, 2015-05-30 at 07:57 -0300, Paulo Alcantara wrote:
>> Hi Gerd,
>>
>> On Thu, 28 May 2015 09:13:35 +0200
>> Gerd Hoffmann <kraxel@redhat.com> wrote:
>>
>> > > +Scope(\_SB) {
>> > > +    OperationRegion (RCRB, SystemMemory, 0xfed1c000, 0x4000)
>> >
>> > Where does this address come from?
>>
>> This address is reserved in an ACPI DSDT table for Intel Haswell in
>> Coreboot project, Vlv2DeviceRefCodePkg package in EDK II as well as my
>> Haswell laptop on which I can see it through `dmesg` :-)
>
> So it seems to be kind of standard.
> Same address on my laptop btw.
>
>> > Is this a standard location suggested by intel specs?
>>
>> I haven't found any Intel spec or any other document that suggests such
>> address, but from "9.4 Memory Map" section in ICH9 spec, it seems safe
>> to use that MMIO region for the 16KiB of chipset configuration
>> registers.
>>
>> > Or is the firmware free to choose it?
>>
>> Given that, I would say so.
>
> There are a few more cases where addresses programmed by the firmware
> and addresses in the acpi tables must match: acpi registers, pci
> mmconfig space for example.  They are handles this way:
>
>   (1) firmware programs the hardware registers as it pleases.
>   (2) when the firmware fetches the acpi tables from qemu (via fw_cfg)
>       qemu will update the tables according to the hardware programming.
>
> So the question is whenever we better do that here too, or whenever it
> is fine to simply hardcore this to 0xfed1c000 everywhere ...
>
> I tend to think hardcoding is fine in that case.  coreboot, edk2 &
> seabios all place it at the same location, and it is highly unlikely we
> ever want move this to another place some day for some reason.

Thanks for the explanation. So, handling it the same way as we do for acpi
regs and pci mmconfig space seems to be the proper solution, however I
think it's OK to hard-code that address for now since it's being used in
many places already.

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v2 1/3] ich9: add TCO interface emulation
  2015-06-01  9:05   ` [Qemu-devel] [PATCH v2 1/3] ich9: add TCO interface emulation Paolo Bonzini
@ 2015-06-01 13:38     ` Paulo Alcantara
  2015-06-01 21:37       ` Paulo Alcantara
  0 siblings, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-01 13:38 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: seabios, Paulo Alcantara, qemu-devel, Michael S. Tsirkin

On Mon, June 1, 2015 6:05 am, Paolo Bonzini wrote:
>
>
> On 31/05/2015 00:04, Paulo Alcantara wrote:
>> +    case TCO_RLD:
>> +        tr->timeouts_no = 0;
>> +        if (can_start_tco_timer(tr)) {
>> +            tr->tco.rld = tr->tco.tmr;
>> +            tco_timer_reload(tr);
>> +        } else {
>> +            tr->tco.rld = val;
>
> Please mask out bits outside TCO_RLD_MASK here, same as you do for
> TCO1_STS_MASK and friends.

OK.

>> +        tr->tco.cnt1 = tr->tco.cnt1 & TCO_LOCK ? val | TCO_LOCK : val;
>
> Since you have to respin, you can do:
>
> 	tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);

OK.

>
> Otherwise looks good to me.
>
> CCing the maintainer.

OK. Sorry for not CCing him earlier.

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v2 1/3] ich9: add TCO interface emulation
  2015-06-01 13:38     ` Paulo Alcantara
@ 2015-06-01 21:37       ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-01 21:37 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: seabios, Paulo Alcantara, qemu-devel, Michael S. Tsirkin

On Mon, 1 Jun 2015 10:38:32 -0300
"Paulo Alcantara" <pcacjr@zytor.com> wrote:

> On Mon, June 1, 2015 6:05 am, Paolo Bonzini wrote:
> >
> >
> > On 31/05/2015 00:04, Paulo Alcantara wrote:
> >> +    case TCO_RLD:
> >> +        tr->timeouts_no = 0;
> >> +        if (can_start_tco_timer(tr)) {
> >> +            tr->tco.rld = tr->tco.tmr;
> >> +            tco_timer_reload(tr);
> >> +        } else {
> >> +            tr->tco.rld = val;
> >
> > Please mask out bits outside TCO_RLD_MASK here, same as you do for
> > TCO1_STS_MASK and friends.
> 
> OK.

Hrm - actually, I can't do that. Unlike TCO1_STS, TCO2_STS and TCO1_CNT
registers that have some bits which are disabled by writing 1 to them
-- that's why I mask them out before updating -- the TCO_RLD and
TCO_TMR registers only have bits 15:10 marked as _reserved_ and I
should not zero them out. Does that seem correct to you?

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v3 1/3] ich9: add TCO interface emulation
  2015-05-30 22:04 ` [Qemu-devel] [PATCH v2 " Paulo Alcantara
                     ` (2 preceding siblings ...)
  2015-06-01  9:05   ` [Qemu-devel] [PATCH v2 1/3] ich9: add TCO interface emulation Paolo Bonzini
@ 2015-06-01 23:48   ` Paulo Alcantara
  2015-06-01 23:48     ` [Qemu-devel] [PATCH v3 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
                       ` (3 more replies)
  3 siblings, 4 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-01 23:48 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, mst

This interface provides some registers within a 32-byte range and can be
acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).

It's commonly used as a watchdog timer to detect system lockups through
SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
NO_REBOOT bit is not set in GCS (General Control and Status register),
the system will be resetted upon second timeout if TCO_RLD register
wasn't previously written to prevent timeout.

This patch adds support to TCO watchdog logic and few other features
like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
etc. are not implemented yet.

v1 -> v2:
  * add migration support for TCO I/O device state
  * wake up only when total time expired instead of every 0.6s
  * some cleanup suggested by Paolo Bonzini
v2 -> v3:
  * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
  * improve handling of TCO_LOCK bit in TCO1_CNT register

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 hw/acpi/Makefile.objs  |   2 +-
 hw/acpi/ich9.c         |  59 ++++++++++++
 hw/acpi/tco.c          | 254 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/isa/lpc_ich9.c      |  10 ++
 include/hw/acpi/ich9.h |   4 +
 include/hw/acpi/tco.h  |  98 +++++++++++++++++++
 include/hw/i386/ich9.h |   8 ++
 7 files changed, 434 insertions(+), 1 deletion(-)
 create mode 100644 hw/acpi/tco.c
 create mode 100644 include/hw/acpi/tco.h

diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 29d46d8..3db1f07 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
+common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o tco.o
 common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
 common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI) += acpi_interface.o
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 84e5bb8..10959fa 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -30,6 +30,7 @@
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "hw/acpi/acpi.h"
+#include "hw/acpi/tco.h"
 #include "sysemu/kvm.h"
 #include "exec/address-spaces.h"
 
@@ -92,8 +93,15 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
                             unsigned width)
 {
     ICH9LPCPMRegs *pm = opaque;
+    TCOIORegs *tr = &pm->tco_regs;
+
     switch (addr) {
     case 0:
+        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
+        if (tr->tco.cnt1 & TCO_LOCK) {
+            val &= ~ICH9_PMIO_SMI_EN_TCO_EN;
+            val |= pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
+        }
         pm->smi_en = val;
         break;
     }
@@ -107,6 +115,29 @@ static const MemoryRegionOps ich9_smi_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
+static uint64_t ich9_tco_readw(void *opaque, hwaddr addr, unsigned width)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    return acpi_pm_tco_ioport_readw(&pm->tco_regs, addr);
+}
+
+static void ich9_tco_writew(void *opaque, hwaddr addr, uint64_t val,
+                            unsigned width)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    acpi_pm_tco_ioport_writew(&pm->tco_regs, addr, val);
+}
+
+static const MemoryRegionOps ich9_tco_ops = {
+    .read = ich9_tco_readw,
+    .write = ich9_tco_writew,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
 void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
 {
     ICH9_DEBUG("to 0x%x\n", pm_io_base);
@@ -157,6 +188,24 @@ static const VMStateDescription vmstate_memhp_state = {
     }
 };
 
+static bool vmstate_test_use_tco(void *opaque)
+{
+    ICH9LPCPMRegs *s = opaque;
+    return s->tco_regs.use_tco;
+}
+
+static const VMStateDescription vmstate_tco_io_state = {
+    .name = "ich9_pm/tco",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
+                       TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 const VMStateDescription vmstate_ich9_pm = {
     .name = "ich9_pm",
     .version_id = 1,
@@ -179,6 +228,10 @@ const VMStateDescription vmstate_ich9_pm = {
             .vmsd = &vmstate_memhp_state,
             .needed = vmstate_test_use_memhp,
         },
+        {
+            .vmsd = &vmstate_tco_io_state,
+            .needed = vmstate_test_use_tco,
+        },
         VMSTATE_END_OF_LIST()
     }
 };
@@ -230,6 +283,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
                           "acpi-smi", 8);
     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
 
+    acpi_pm_tco_init(&pm->tco_regs);
+    memory_region_init_io(&pm->io_tco, OBJECT(lpc_pci), &ich9_tco_ops, pm,
+			  "sm-tco", ICH9_PMIO_TCO_LEN);
+    memory_region_add_subregion(&pm->io, ICH9_PMIO_TCO_RLD, &pm->io_tco);
+
     pm->irq = sci_irq;
     qemu_register_reset(pm_reset, pm);
     pm->powerdown_notifier.notify = pm_powerdown_req;
@@ -357,6 +415,7 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
     pm->disable_s3 = 0;
     pm->disable_s4 = 0;
     pm->s4_val = 2;
+    pm->tco_regs.use_tco = true;
 
     object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE,
                                    &pm->pm_io_base, errp);
diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
new file mode 100644
index 0000000..c159281
--- /dev/null
+++ b/hw/acpi/tco.c
@@ -0,0 +1,254 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "sysemu/watchdog.h"
+#include "hw/i386/ich9.h"
+
+#include "hw/acpi/tco.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define TCO_DEBUG(fmt, ...)                                     \
+    do {                                                        \
+        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
+    } while (0)
+#else
+#define TCO_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+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,
+};
+
+static inline void tco_timer_reload(TCOIORegs *tr)
+{
+    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
+    timer_mod(tr->tco_timer, tr->expire_time);
+}
+
+static inline void tco_timer_stop(TCOIORegs *tr)
+{
+    tr->expire_time = -1;
+}
+
+static void tco_timer_expired(void *opaque)
+{
+    TCOIORegs *tr = opaque;
+    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
+    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
+    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_LPC_RCBA_GCS);
+
+    tr->tco.rld = 0;
+    tr->tco.sts1 |= TCO_TIMEOUT;
+    if (++tr->timeouts_no == 2) {
+        tr->tco.sts2 |= TCO_SECOND_TO_STS;
+        tr->tco.sts2 |= TCO_BOOT_STS;
+        tr->timeouts_no = 0;
+
+        if (!(gcs & ICH9_LPC_RCBA_GCS_NO_REBOOT)) {
+            watchdog_perform_action();
+            tco_timer_stop(tr);
+            return;
+        }
+    }
+
+    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
+        ich9_generate_smi();
+    } else {
+        ich9_generate_nmi();
+    }
+    tr->tco.rld = tr->tco.tmr;
+    tco_timer_reload(tr);
+}
+
+void acpi_pm_tco_init(TCOIORegs *tr)
+{
+    *tr = (TCOIORegs) {
+        .tco = {
+            .rld      = TCO_RLD_DEFAULT,
+            .din      = TCO_DAT_IN_DEFAULT,
+            .dout     = TCO_DAT_OUT_DEFAULT,
+            .sts1     = TCO1_STS_DEFAULT,
+            .sts2     = TCO2_STS_DEFAULT,
+            .cnt1     = TCO1_CNT_DEFAULT,
+            .cnt2     = TCO2_CNT_DEFAULT,
+            .msg1     = TCO_MESSAGE1_DEFAULT,
+            .msg2     = TCO_MESSAGE2_DEFAULT,
+            .wdcnt    = TCO_WDCNT_DEFAULT,
+            .tmr      = TCO_TMR_DEFAULT,
+        },
+        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
+        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
+        .expire_time   = -1,
+        .timeouts_no   = 0,
+    };
+}
+
+/* NOTE: values of 0 or 1 will be ignored by ICH */
+static inline int can_start_tco_timer(TCOIORegs *tr)
+{
+    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
+}
+
+uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
+{
+    uint16_t rld;
+
+    switch (addr) {
+    case TCO_RLD:
+        if (tr->expire_time != -1) {
+            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
+            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
+        } else {
+            rld = tr->tco.rld;
+        }
+        return rld;
+    case TCO_DAT_IN:
+        return tr->tco.din;
+    case TCO_DAT_OUT:
+        return tr->tco.dout;
+    case TCO1_STS:
+        return tr->tco.sts1;
+    case TCO2_STS:
+        return tr->tco.sts2;
+    case TCO1_CNT:
+        return tr->tco.cnt1;
+    case TCO2_CNT:
+        return tr->tco.cnt2;
+    case TCO_MESSAGE1:
+        return tr->tco.msg1;
+    case TCO_MESSAGE2:
+        return tr->tco.msg2;
+    case TCO_WDCNT:
+        return tr->tco.wdcnt;
+    case TCO_TMR:
+        return tr->tco.tmr;
+    case SW_IRQ_GEN:
+        return tr->sw_irq_gen;
+    }
+    return 0;
+}
+
+void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
+{
+    switch (addr) {
+    case TCO_RLD:
+        tr->timeouts_no = 0;
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tr->tco.rld = val;
+        }
+        break;
+    case TCO_DAT_IN:
+        tr->tco.din = val;
+        tr->tco.sts1 |= SW_TCO_SMI;
+        ich9_generate_smi();
+        break;
+    case TCO_DAT_OUT:
+        tr->tco.dout = val;
+        tr->tco.sts1 |= TCO_INT_STS;
+        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
+        break;
+    case TCO1_STS:
+        tr->tco.sts1 = val & TCO1_STS_MASK;
+        break;
+    case TCO2_STS:
+        tr->tco.sts2 = val & TCO2_STS_MASK;
+        break;
+    case TCO1_CNT:
+        val &= TCO1_CNT_MASK;
+        /*
+         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
+         * is required to change this bit from 1 to 0 -- it defaults to 0.
+         */
+        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tco_timer_stop(tr);
+        }
+        break;
+    case TCO2_CNT:
+        tr->tco.cnt2 = val;
+        break;
+    case TCO_MESSAGE1:
+        tr->tco.msg1 = val;
+        break;
+    case TCO_MESSAGE2:
+        tr->tco.msg2 = val;
+        break;
+    case TCO_WDCNT:
+        tr->tco.wdcnt = val;
+        break;
+    case TCO_TMR:
+        tr->tco.tmr = val;
+        break;
+    case SW_IRQ_GEN:
+        tr->sw_irq_gen = val;
+        break;
+    }
+}
+
+const VMStateDescription vmstate_tco_io_sts = {
+    .name = "tco io device status",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(use_tco, TCOIORegs),
+        VMSTATE_UINT16(tco.rld, TCOIORegs),
+        VMSTATE_UINT8(tco.din, TCOIORegs),
+        VMSTATE_UINT8(tco.dout, TCOIORegs),
+        VMSTATE_UINT16(tco.sts1, TCOIORegs),
+        VMSTATE_UINT16(tco.sts2, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
+        VMSTATE_UINT8(tco.msg1, TCOIORegs),
+        VMSTATE_UINT8(tco.msg2, TCOIORegs),
+        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
+        VMSTATE_UINT16(tco.tmr, TCOIORegs),
+        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
+        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
+        VMSTATE_INT64(expire_time, TCOIORegs),
+        VMSTATE_UINT8(timeouts_no, TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index dba7585..7bfb683 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -313,6 +313,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
     return route;
 }
 
+void ich9_generate_smi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+}
+
+void ich9_generate_nmi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
+}
+
 static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
 {
     switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
index c2d3dba..31c74af 100644
--- a/include/hw/acpi/ich9.h
+++ b/include/hw/acpi/ich9.h
@@ -25,6 +25,7 @@
 #include "hw/acpi/cpu_hotplug.h"
 #include "hw/acpi/memory_hotplug.h"
 #include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/tco.h"
 
 typedef struct ICH9LPCPMRegs {
     /*
@@ -37,6 +38,7 @@ typedef struct ICH9LPCPMRegs {
     MemoryRegion io;
     MemoryRegion io_gpe;
     MemoryRegion io_smi;
+    MemoryRegion io_tco;
 
     uint32_t smi_en;
     uint32_t smi_sts;
@@ -53,6 +55,8 @@ typedef struct ICH9LPCPMRegs {
     uint8_t disable_s3;
     uint8_t disable_s4;
     uint8_t s4_val;
+
+    TCOIORegs tco_regs;
 } ICH9LPCPMRegs;
 
 void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
new file mode 100644
index 0000000..7240522
--- /dev/null
+++ b/include/hw/acpi/tco.h
@@ -0,0 +1,98 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef HW_ACPI_TCO_H
+#define HW_ACPI_TCO_H
+
+#include "qemu/typedefs.h"
+#include "qemu-common.h"
+
+/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
+#define TCO_TICK_NSEC 600000000LL
+
+/* TCO I/O register offsets */
+enum {
+    TCO_RLD           = 0x00,
+    TCO_DAT_IN        = 0x02,
+    TCO_DAT_OUT       = 0x03,
+    TCO1_STS          = 0x04,
+    TCO2_STS          = 0x06,
+    TCO1_CNT          = 0x08,
+    TCO2_CNT          = 0x0a,
+    TCO_MESSAGE1      = 0x0c,
+    TCO_MESSAGE2      = 0x0d,
+    TCO_WDCNT         = 0x0e,
+    SW_IRQ_GEN        = 0x10,
+    TCO_TMR           = 0x12,
+};
+
+/* TCO I/O register control/status bits */
+enum {
+    SW_TCO_SMI           = (1 << 1),
+    TCO_INT_STS          = (1 << 2),
+    TCO_LOCK             = (1 << 12),
+    TCO_TMR_HLT          = (1 << 11),
+    TCO_TIMEOUT          = (1 << 3),
+    TCO_SECOND_TO_STS    = (1 << 1),
+    TCO_BOOT_STS         = (1 << 2),
+};
+
+/* TCO I/O registers mask bits */
+enum {
+    TCO_RLD_MASK     = 0x3ff,
+    TCO1_STS_MASK    = 0xe870,
+    TCO2_STS_MASK    = 0xfff8,
+    TCO1_CNT_MASK    = 0xfeff,
+    TCO_TMR_MASK     = 0x3ff,
+};
+
+typedef struct TCOIORegs {
+    bool use_tco;
+    struct {
+        uint16_t rld;
+        uint8_t din;
+        uint8_t dout;
+        uint16_t sts1;
+        uint16_t sts2;
+        uint16_t cnt1;
+        uint16_t cnt2;
+        uint8_t msg1;
+        uint8_t msg2;
+        uint8_t wdcnt;
+        uint16_t tmr;
+    } tco;
+    uint8_t sw_irq_gen;
+
+    QEMUTimer *tco_timer;
+    int64_t expire_time;
+    uint8_t timeouts_no;
+} TCOIORegs;
+
+/* tco.c */
+void acpi_pm_tco_init(TCOIORegs *tr);
+uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr);
+void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val);
+
+extern const VMStateDescription vmstate_tco_io_sts;
+
+#endif /* HW_ACPI_TCO_H */
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index f4e522c..f41cca6 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -20,6 +20,9 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
 void ich9_lpc_pm_init(PCIDevice *pci_lpc);
 I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
 
+void ich9_generate_smi(void);
+void ich9_generate_nmi(void);
+
 #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
 
 #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
@@ -156,6 +159,8 @@ Object *ich9_lpc_find(void);
 #define ICH9_LPC_RCBA_BA_MASK                   Q35_MASK(32, 31, 14)
 #define ICH9_LPC_RCBA_EN                        0x1
 #define ICH9_LPC_RCBA_DEFAULT                   0x0
+#define ICH9_LPC_RCBA_GCS                       0x3410
+#define ICH9_LPC_RCBA_GCS_NO_REBOOT             (1 << 5)
 
 #define ICH9_LPC_PIC_NUM_PINS                   16
 #define ICH9_LPC_IOAPIC_NUM_PINS                24
@@ -180,7 +185,10 @@ Object *ich9_lpc_find(void);
 #define ICH9_PMIO_GPE0_LEN                      16
 #define ICH9_PMIO_SMI_EN                        0x30
 #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
+#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
 #define ICH9_PMIO_SMI_STS                       0x34
+#define ICH9_PMIO_TCO_RLD                       0x60
+#define ICH9_PMIO_TCO_LEN                       32
 
 /* FADT ACPI_ENABLE/ACPI_DISABLE */
 #define ICH9_APM_ACPI_ENABLE                    0x2
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v3 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-01 23:48   ` [Qemu-devel] [PATCH v3 " Paulo Alcantara
@ 2015-06-01 23:48     ` Paulo Alcantara
  2015-06-17 13:33       ` Michael S. Tsirkin
  2015-06-01 23:48     ` [Qemu-devel] [PATCH v3 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
                       ` (2 subsequent siblings)
  3 siblings, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-01 23:48 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, mst

v1 -> v2:
  * s/PDRC/CCR/ for clarity and match ICH9 spec
  * remove unnecessary OperationRegion for RCRB

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 hw/i386/q35-acpi-dsdt.dsl     |  14 ++++++++++++++
 tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
 2 files changed, 14 insertions(+)

diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
index 16eaca3..92675c8 100644
--- a/hw/i386/q35-acpi-dsdt.dsl
+++ b/hw/i386/q35-acpi-dsdt.dsl
@@ -114,6 +114,20 @@ DefinitionBlock (
         }
     }
 
+/****************************************************************
+ * Chipset Configuration Registers
+ ****************************************************************/
+Scope(\_SB.PCI0) {
+    Device (CCR) {
+        Name (_HID, EISAID("PNP0C02"))
+        Name (_UID, 1)
+
+        Name (_CRS, ResourceTemplate() {
+	    Memory32Fixed(ReadWrite, 0xfed1c000, 0x00004000) // RCBA
+        })
+    }
+}
+
 #include "acpi-dsdt-hpet.dsl"
 
 
diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49 100644
GIT binary patch
delta 81
zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w

delta 24
gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs

-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v3 3/3] tests: add testcase for TCO watchdog emulation
  2015-06-01 23:48   ` [Qemu-devel] [PATCH v3 " Paulo Alcantara
  2015-06-01 23:48     ` [Qemu-devel] [PATCH v3 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
@ 2015-06-01 23:48     ` Paulo Alcantara
  2015-06-17 13:37       ` Michael S. Tsirkin
  2015-06-10 13:17     ` [Qemu-devel] [PATCH v3 1/3] ich9: add TCO interface emulation Paulo Alcantara
  2015-06-17 13:27     ` Michael S. Tsirkin
  3 siblings, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-01 23:48 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, mst

v1 -> v2:
  * some cleanup
  * add test for TCO_LOCK bit
v2 -> v3:
  * add tests for TCO control & status bits
  * fix check of SECOND_TO_STS bit (it's set in TCO2_STS reg)

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 tests/Makefile   |   2 +
 tests/tco-test.c | 475 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 477 insertions(+)
 create mode 100644 tests/tco-test.c

diff --git a/tests/Makefile b/tests/Makefile
index 729b969..43950d0 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -150,6 +150,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
 check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
+check-qtest-i386-y += tests/tco-test$(EXESUF)
 gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
 check-qtest-i386-y += $(check-qtest-pci-y)
 gcov-files-i386-y += $(gcov-files-pci-y)
@@ -363,6 +364,7 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
 tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
 tests/ne2000-test$(EXESUF): tests/ne2000-test.o
 tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
+tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
 tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
 tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
 tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
diff --git a/tests/tco-test.c b/tests/tco-test.c
new file mode 100644
index 0000000..b9acf43
--- /dev/null
+++ b/tests/tco-test.c
@@ -0,0 +1,475 @@
+/*
+ * QEMU ICH9 TCO emulation tests
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/i386/ich9.h"
+#include "hw/acpi/ich9.h"
+#include "hw/acpi/tco.h"
+
+#define PM_IO_BASE_ADDR    0xb000
+#define RCBA_BASE_ADDR     0xfed1c000
+
+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;
+    QPCIDevice *dev;
+    void *lpc_base;
+    void *tco_io_base;
+} TestData;
+
+static void test_init(TestData *d)
+{
+    QPCIBus *bus;
+    QTestState *qs;
+    char *s;
+
+    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
+    qs = qtest_start(s);
+    qtest_irq_intercept_in(qs, "ioapic");
+    g_free(s);
+
+    bus = qpci_init_pc();
+    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
+    g_assert(d->dev != NULL);
+
+    /* map PCI-to-LPC bridge interface BAR */
+    d->lpc_base = qpci_iomap(d->dev, 0, NULL);
+
+    qpci_device_enable(d->dev);
+
+    g_assert(d->lpc_base != NULL);
+
+    /* set ACPI PM I/O space base address */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_PMBASE,
+                       PM_IO_BASE_ADDR | 0x1);
+    /* enable ACPI I/O */
+    qpci_config_writeb(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_ACPI_CTRL,
+                       0x80);
+    /* set Root Complex BAR */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_RCBA,
+                       RCBA_BASE_ADDR | 0x1);
+
+    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
+}
+
+static void stop_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val |= TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void start_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val &= ~TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void load_tco(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
+}
+
+static void set_tco_timeout(const TestData *d, uint16_t ticks)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
+}
+
+static void clear_tco_status(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
+}
+
+static void reset_on_second_timeout(bool enable)
+{
+    uint32_t val;
+
+    val = readl(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS);
+    if (enable) {
+        val &= ~ICH9_LPC_RCBA_GCS_NO_REBOOT;
+    } else {
+        val |= ICH9_LPC_RCBA_GCS_NO_REBOOT;
+    }
+    writel(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS, val);
+}
+
+static void test_tco_defaults(void)
+{
+    TestData d;
+
+    d.args = NULL;
+    test_init(&d);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+                    TCO_RLD_DEFAULT);
+    /* TCO_DAT_IN & TCO_DAT_OUT */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
+                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
+    /* TCO1_STS & TCO2_STS */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
+                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
+    /* TCO1_CNT & TCO2_CNT */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
+    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
+                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
+                    TCO_WDCNT_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
+                    SW_IRQ_GEN_DEFAULT);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
+                    TCO_TMR_DEFAULT);
+    qtest_end();
+}
+
+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;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    /* test first timeout */
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    /* test clearing timeout bit */
+    val |= TCO_TIMEOUT;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+
+    /* test second timeout */
+    clock_step(ticks * TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco_max_timeout(void)
+{
+    TestData d;
+    const uint16_t ticks = 0xffff;
+    uint32_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
+    g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+    clock_step(TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static QDict *get_watchdog_action(void)
+{
+    QDict *ev = qmp("");
+    QDict *data;
+    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
+
+    data = qdict_get_qdict(ev, "data");
+    QINCREF(data);
+    QDECREF(ev);
+    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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_ticks_counter(void)
+{
+    TestData d;
+    uint16_t ticks = TCO_SECS_TO_TICKS(8);
+    uint16_t rld;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+
+    do {
+        rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
+        g_assert_cmpint(rld, ==, ticks);
+        clock_step(TCO_TICK_NSEC);
+        ticks--;
+    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco1_control_bits(void)
+{
+    TestData d;
+    uint16_t val;
+
+    d.args = NULL;
+    test_init(&d);
+
+    val = TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    val &= ~TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    TCO_LOCK);
+    qtest_end();
+}
+
+static void test_tco1_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0);
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0);
+    qtest_end();
+}
+
+static void test_tco2_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = "-watchdog-action none";
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0);
+    qtest_end();
+}
+
+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();
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v3 1/3] ich9: add TCO interface emulation
  2015-06-01 23:48   ` [Qemu-devel] [PATCH v3 " Paulo Alcantara
  2015-06-01 23:48     ` [Qemu-devel] [PATCH v3 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
  2015-06-01 23:48     ` [Qemu-devel] [PATCH v3 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
@ 2015-06-10 13:17     ` Paulo Alcantara
  2015-06-17 13:27     ` Michael S. Tsirkin
  3 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-10 13:17 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, mst

On Mon, June 1, 2015 8:48 pm, Paulo Alcantara wrote:
> This interface provides some registers within a 32-byte range and can be
> acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).
>
> It's commonly used as a watchdog timer to detect system lockups through
> SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
> NO_REBOOT bit is not set in GCS (General Control and Status register),
> the system will be resetted upon second timeout if TCO_RLD register
> wasn't previously written to prevent timeout.
>
> This patch adds support to TCO watchdog logic and few other features
> like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
> etc. are not implemented yet.
>
> v1 -> v2:
>   * add migration support for TCO I/O device state
>   * wake up only when total time expired instead of every 0.6s
>   * some cleanup suggested by Paolo Bonzini
> v2 -> v3:
>   * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
>   * improve handling of TCO_LOCK bit in TCO1_CNT register

Michael, ping? :-)

(looks like there's a lot of ICH9 changes since this patchset was sent, so
you want me to rebase it against master and resend, please let me know)

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v3 1/3] ich9: add TCO interface emulation
  2015-06-01 23:48   ` [Qemu-devel] [PATCH v3 " Paulo Alcantara
                       ` (2 preceding siblings ...)
  2015-06-10 13:17     ` [Qemu-devel] [PATCH v3 1/3] ich9: add TCO interface emulation Paulo Alcantara
@ 2015-06-17 13:27     ` Michael S. Tsirkin
  2015-06-18  2:10       ` Paulo Alcantara
  3 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-17 13:27 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Mon, Jun 01, 2015 at 08:48:39PM -0300, Paulo Alcantara wrote:
> This interface provides some registers within a 32-byte range and can be
> acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).
> 
> It's commonly used as a watchdog timer to detect system lockups through
> SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
> NO_REBOOT bit is not set in GCS (General Control and Status register),
> the system will be resetted upon second timeout if TCO_RLD register
> wasn't previously written to prevent timeout.
> 
> This patch adds support to TCO watchdog logic and few other features
> like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
> etc. are not implemented yet.
> 
> v1 -> v2:
>   * add migration support for TCO I/O device state
>   * wake up only when total time expired instead of every 0.6s
>   * some cleanup suggested by Paolo Bonzini
> v2 -> v3:
>   * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
>   * improve handling of TCO_LOCK bit in TCO1_CNT register

changelog must come after --- so that git am ignores it.

> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>

for some reason v3 was sent as reply to v2.
Don't do that please.


> ---
>  hw/acpi/Makefile.objs  |   2 +-
>  hw/acpi/ich9.c         |  59 ++++++++++++
>  hw/acpi/tco.c          | 254 +++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/isa/lpc_ich9.c      |  10 ++
>  include/hw/acpi/ich9.h |   4 +
>  include/hw/acpi/tco.h  |  98 +++++++++++++++++++
>  include/hw/i386/ich9.h |   8 ++
>  7 files changed, 434 insertions(+), 1 deletion(-)
>  create mode 100644 hw/acpi/tco.c
>  create mode 100644 include/hw/acpi/tco.h
> 
> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> index 29d46d8..3db1f07 100644
> --- a/hw/acpi/Makefile.objs
> +++ b/hw/acpi/Makefile.objs
> @@ -1,4 +1,4 @@
> -common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
> +common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o tco.o
>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
>  common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
>  common-obj-$(CONFIG_ACPI) += acpi_interface.o
> diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
> index 84e5bb8..10959fa 100644
> --- a/hw/acpi/ich9.c
> +++ b/hw/acpi/ich9.c
> @@ -30,6 +30,7 @@
>  #include "qemu/timer.h"
>  #include "sysemu/sysemu.h"
>  #include "hw/acpi/acpi.h"
> +#include "hw/acpi/tco.h"
>  #include "sysemu/kvm.h"
>  #include "exec/address-spaces.h"
>  
> @@ -92,8 +93,15 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
>                              unsigned width)
>  {
>      ICH9LPCPMRegs *pm = opaque;
> +    TCOIORegs *tr = &pm->tco_regs;
> +
>      switch (addr) {
>      case 0:
> +        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
> +        if (tr->tco.cnt1 & TCO_LOCK) {
> +            val &= ~ICH9_PMIO_SMI_EN_TCO_EN;
> +            val |= pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
> +        }
>          pm->smi_en = val;
>          break;
>      }
> @@ -107,6 +115,29 @@ static const MemoryRegionOps ich9_smi_ops = {
>      .endianness = DEVICE_LITTLE_ENDIAN,
>  };
>  
> +static uint64_t ich9_tco_readw(void *opaque, hwaddr addr, unsigned width)
> +{
> +    ICH9LPCPMRegs *pm = opaque;
> +    return acpi_pm_tco_ioport_readw(&pm->tco_regs, addr);
> +}
> +
> +static void ich9_tco_writew(void *opaque, hwaddr addr, uint64_t val,
> +                            unsigned width)
> +{
> +    ICH9LPCPMRegs *pm = opaque;
> +    acpi_pm_tco_ioport_writew(&pm->tco_regs, addr, val);
> +}
> +
> +static const MemoryRegionOps ich9_tco_ops = {
> +    .read = ich9_tco_readw,
> +    .write = ich9_tco_writew,
> +    .valid.min_access_size = 1,
> +    .valid.max_access_size = 4,
> +    .impl.min_access_size = 1,
> +    .impl.max_access_size = 2,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
>  void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
>  {
>      ICH9_DEBUG("to 0x%x\n", pm_io_base);
> @@ -157,6 +188,24 @@ static const VMStateDescription vmstate_memhp_state = {
>      }
>  };
>  
> +static bool vmstate_test_use_tco(void *opaque)
> +{
> +    ICH9LPCPMRegs *s = opaque;
> +    return s->tco_regs.use_tco;
> +}
> +
> +static const VMStateDescription vmstate_tco_io_state = {
> +    .name = "ich9_pm/tco",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields      = (VMStateField[]) {
> +        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
> +                       TCOIORegs),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
>  const VMStateDescription vmstate_ich9_pm = {
>      .name = "ich9_pm",
>      .version_id = 1,
> @@ -179,6 +228,10 @@ const VMStateDescription vmstate_ich9_pm = {
>              .vmsd = &vmstate_memhp_state,
>              .needed = vmstate_test_use_memhp,
>          },
> +        {
> +            .vmsd = &vmstate_tco_io_state,
> +            .needed = vmstate_test_use_tco,
> +        },
>          VMSTATE_END_OF_LIST()
>      }
>  };
> @@ -230,6 +283,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
>                            "acpi-smi", 8);
>      memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
>  
> +    acpi_pm_tco_init(&pm->tco_regs);
> +    memory_region_init_io(&pm->io_tco, OBJECT(lpc_pci), &ich9_tco_ops, pm,
> +			  "sm-tco", ICH9_PMIO_TCO_LEN);
> +    memory_region_add_subregion(&pm->io, ICH9_PMIO_TCO_RLD, &pm->io_tco);
> +
>      pm->irq = sci_irq;
>      qemu_register_reset(pm_reset, pm);
>      pm->powerdown_notifier.notify = pm_powerdown_req;
> @@ -357,6 +415,7 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
>      pm->disable_s3 = 0;
>      pm->disable_s4 = 0;
>      pm->s4_val = 2;
> +    pm->tco_regs.use_tco = true;

Would be safer to add a property, and not to enable this for old machine types.

>  
>      object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE,
>                                     &pm->pm_io_base, errp);
> diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> new file mode 100644
> index 0000000..c159281
> --- /dev/null
> +++ b/hw/acpi/tco.c
> @@ -0,0 +1,254 @@
> +/*
> + * QEMU ICH9 TCO emulation
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include "qemu-common.h"
> +#include "sysemu/watchdog.h"
> +#include "hw/i386/ich9.h"
> +
> +#include "hw/acpi/tco.h"
> +
> +//#define DEBUG
> +
> +#ifdef DEBUG
> +#define TCO_DEBUG(fmt, ...)                                     \
> +    do {                                                        \
> +        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
> +    } while (0)
> +#else
> +#define TCO_DEBUG(fmt, ...) do { } while (0)
> +#endif
> +
> +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,
> +};
> +
> +static inline void tco_timer_reload(TCOIORegs *tr)
> +{
> +    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> +        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
> +    timer_mod(tr->tco_timer, tr->expire_time);
> +}
> +
> +static inline void tco_timer_stop(TCOIORegs *tr)
> +{
> +    tr->expire_time = -1;
> +}
> +
> +static void tco_timer_expired(void *opaque)
> +{
> +    TCOIORegs *tr = opaque;
> +    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
> +    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
> +    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_LPC_RCBA_GCS);
> +
> +    tr->tco.rld = 0;
> +    tr->tco.sts1 |= TCO_TIMEOUT;
> +    if (++tr->timeouts_no == 2) {
> +        tr->tco.sts2 |= TCO_SECOND_TO_STS;
> +        tr->tco.sts2 |= TCO_BOOT_STS;
> +        tr->timeouts_no = 0;
> +
> +        if (!(gcs & ICH9_LPC_RCBA_GCS_NO_REBOOT)) {
> +            watchdog_perform_action();
> +            tco_timer_stop(tr);
> +            return;

So this is a virtual clock - not running when VM is not running.
Doesn't this mean if you stop VM for a while, it's almost sure to
fire when you resume?

Maybe it's a useful feature, but maybe it's best to keep it off by
default?

> +        }
> +    }
> +
> +    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
> +        ich9_generate_smi();
> +    } else {
> +        ich9_generate_nmi();
> +    }
> +    tr->tco.rld = tr->tco.tmr;
> +    tco_timer_reload(tr);
> +}
> +
> +void acpi_pm_tco_init(TCOIORegs *tr)
> +{
> +    *tr = (TCOIORegs) {
> +        .tco = {
> +            .rld      = TCO_RLD_DEFAULT,
> +            .din      = TCO_DAT_IN_DEFAULT,
> +            .dout     = TCO_DAT_OUT_DEFAULT,
> +            .sts1     = TCO1_STS_DEFAULT,
> +            .sts2     = TCO2_STS_DEFAULT,
> +            .cnt1     = TCO1_CNT_DEFAULT,
> +            .cnt2     = TCO2_CNT_DEFAULT,
> +            .msg1     = TCO_MESSAGE1_DEFAULT,
> +            .msg2     = TCO_MESSAGE2_DEFAULT,
> +            .wdcnt    = TCO_WDCNT_DEFAULT,
> +            .tmr      = TCO_TMR_DEFAULT,
> +        },
> +        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
> +        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
> +        .expire_time   = -1,
> +        .timeouts_no   = 0,
> +    };
> +}
> +
> +/* NOTE: values of 0 or 1 will be ignored by ICH */
> +static inline int can_start_tco_timer(TCOIORegs *tr)
> +{
> +    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
> +}
> +
> +uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
> +{
> +    uint16_t rld;
> +
> +    switch (addr) {
> +    case TCO_RLD:
> +        if (tr->expire_time != -1) {
> +            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
> +            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
> +        } else {
> +            rld = tr->tco.rld;
> +        }
> +        return rld;
> +    case TCO_DAT_IN:
> +        return tr->tco.din;
> +    case TCO_DAT_OUT:
> +        return tr->tco.dout;
> +    case TCO1_STS:
> +        return tr->tco.sts1;
> +    case TCO2_STS:
> +        return tr->tco.sts2;
> +    case TCO1_CNT:
> +        return tr->tco.cnt1;
> +    case TCO2_CNT:
> +        return tr->tco.cnt2;
> +    case TCO_MESSAGE1:
> +        return tr->tco.msg1;
> +    case TCO_MESSAGE2:
> +        return tr->tco.msg2;
> +    case TCO_WDCNT:
> +        return tr->tco.wdcnt;
> +    case TCO_TMR:
> +        return tr->tco.tmr;
> +    case SW_IRQ_GEN:
> +        return tr->sw_irq_gen;
> +    }
> +    return 0;
> +}
> +
> +void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
> +{
> +    switch (addr) {
> +    case TCO_RLD:
> +        tr->timeouts_no = 0;
> +        if (can_start_tco_timer(tr)) {
> +            tr->tco.rld = tr->tco.tmr;
> +            tco_timer_reload(tr);
> +        } else {
> +            tr->tco.rld = val;
> +        }
> +        break;
> +    case TCO_DAT_IN:
> +        tr->tco.din = val;
> +        tr->tco.sts1 |= SW_TCO_SMI;
> +        ich9_generate_smi();
> +        break;
> +    case TCO_DAT_OUT:
> +        tr->tco.dout = val;
> +        tr->tco.sts1 |= TCO_INT_STS;
> +        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
> +        break;
> +    case TCO1_STS:
> +        tr->tco.sts1 = val & TCO1_STS_MASK;
> +        break;
> +    case TCO2_STS:
> +        tr->tco.sts2 = val & TCO2_STS_MASK;
> +        break;
> +    case TCO1_CNT:
> +        val &= TCO1_CNT_MASK;
> +        /*
> +         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
> +         * is required to change this bit from 1 to 0 -- it defaults to 0.
> +         */
> +        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
> +        if (can_start_tco_timer(tr)) {
> +            tr->tco.rld = tr->tco.tmr;
> +            tco_timer_reload(tr);
> +        } else {
> +            tco_timer_stop(tr);
> +        }
> +        break;
> +    case TCO2_CNT:
> +        tr->tco.cnt2 = val;
> +        break;
> +    case TCO_MESSAGE1:
> +        tr->tco.msg1 = val;
> +        break;
> +    case TCO_MESSAGE2:
> +        tr->tco.msg2 = val;
> +        break;
> +    case TCO_WDCNT:
> +        tr->tco.wdcnt = val;
> +        break;
> +    case TCO_TMR:
> +        tr->tco.tmr = val;
> +        break;
> +    case SW_IRQ_GEN:
> +        tr->sw_irq_gen = val;
> +        break;
> +    }
> +}
> +
> +const VMStateDescription vmstate_tco_io_sts = {
> +    .name = "tco io device status",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields      = (VMStateField[]) {
> +        VMSTATE_BOOL(use_tco, TCOIORegs),

So this field is only migrated if it is true.
Doesn't seem to make sense.

> +        VMSTATE_UINT16(tco.rld, TCOIORegs),
> +        VMSTATE_UINT8(tco.din, TCOIORegs),
> +        VMSTATE_UINT8(tco.dout, TCOIORegs),
> +        VMSTATE_UINT16(tco.sts1, TCOIORegs),
> +        VMSTATE_UINT16(tco.sts2, TCOIORegs),
> +        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
> +        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
> +        VMSTATE_UINT8(tco.msg1, TCOIORegs),
> +        VMSTATE_UINT8(tco.msg2, TCOIORegs),
> +        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
> +        VMSTATE_UINT16(tco.tmr, TCOIORegs),
> +        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
> +        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
> +        VMSTATE_INT64(expire_time, TCOIORegs),
> +        VMSTATE_UINT8(timeouts_no, TCOIORegs),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> index dba7585..7bfb683 100644
> --- a/hw/isa/lpc_ich9.c
> +++ b/hw/isa/lpc_ich9.c
> @@ -313,6 +313,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
>      return route;
>  }
>  
> +void ich9_generate_smi(void)
> +{
> +    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
> +}
> +
> +void ich9_generate_nmi(void)
> +{
> +    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
> +}
> +
>  static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
>  {
>      switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
> diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
> index c2d3dba..31c74af 100644
> --- a/include/hw/acpi/ich9.h
> +++ b/include/hw/acpi/ich9.h
> @@ -25,6 +25,7 @@
>  #include "hw/acpi/cpu_hotplug.h"
>  #include "hw/acpi/memory_hotplug.h"
>  #include "hw/acpi/acpi_dev_interface.h"
> +#include "hw/acpi/tco.h"
>  
>  typedef struct ICH9LPCPMRegs {
>      /*
> @@ -37,6 +38,7 @@ typedef struct ICH9LPCPMRegs {
>      MemoryRegion io;
>      MemoryRegion io_gpe;
>      MemoryRegion io_smi;
> +    MemoryRegion io_tco;
>  
>      uint32_t smi_en;
>      uint32_t smi_sts;
> @@ -53,6 +55,8 @@ typedef struct ICH9LPCPMRegs {
>      uint8_t disable_s3;
>      uint8_t disable_s4;
>      uint8_t s4_val;
> +
> +    TCOIORegs tco_regs;
>  } ICH9LPCPMRegs;
>  
>  void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
> diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
> new file mode 100644
> index 0000000..7240522
> --- /dev/null
> +++ b/include/hw/acpi/tco.h
> @@ -0,0 +1,98 @@
> +/*
> + * QEMU ICH9 TCO emulation
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#ifndef HW_ACPI_TCO_H
> +#define HW_ACPI_TCO_H
> +
> +#include "qemu/typedefs.h"
> +#include "qemu-common.h"
> +
> +/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
> +#define TCO_TICK_NSEC 600000000LL
> +
> +/* TCO I/O register offsets */
> +enum {
> +    TCO_RLD           = 0x00,
> +    TCO_DAT_IN        = 0x02,
> +    TCO_DAT_OUT       = 0x03,
> +    TCO1_STS          = 0x04,
> +    TCO2_STS          = 0x06,
> +    TCO1_CNT          = 0x08,
> +    TCO2_CNT          = 0x0a,
> +    TCO_MESSAGE1      = 0x0c,
> +    TCO_MESSAGE2      = 0x0d,
> +    TCO_WDCNT         = 0x0e,
> +    SW_IRQ_GEN        = 0x10,
> +    TCO_TMR           = 0x12,
> +};
> +
> +/* TCO I/O register control/status bits */
> +enum {
> +    SW_TCO_SMI           = (1 << 1),
> +    TCO_INT_STS          = (1 << 2),
> +    TCO_LOCK             = (1 << 12),
> +    TCO_TMR_HLT          = (1 << 11),
> +    TCO_TIMEOUT          = (1 << 3),
> +    TCO_SECOND_TO_STS    = (1 << 1),
> +    TCO_BOOT_STS         = (1 << 2),
> +};
> +
> +/* TCO I/O registers mask bits */
> +enum {
> +    TCO_RLD_MASK     = 0x3ff,
> +    TCO1_STS_MASK    = 0xe870,
> +    TCO2_STS_MASK    = 0xfff8,
> +    TCO1_CNT_MASK    = 0xfeff,
> +    TCO_TMR_MASK     = 0x3ff,
> +};
> +
> +typedef struct TCOIORegs {
> +    bool use_tco;
> +    struct {
> +        uint16_t rld;
> +        uint8_t din;
> +        uint8_t dout;
> +        uint16_t sts1;
> +        uint16_t sts2;
> +        uint16_t cnt1;
> +        uint16_t cnt2;
> +        uint8_t msg1;
> +        uint8_t msg2;
> +        uint8_t wdcnt;
> +        uint16_t tmr;
> +    } tco;
> +    uint8_t sw_irq_gen;
> +
> +    QEMUTimer *tco_timer;
> +    int64_t expire_time;
> +    uint8_t timeouts_no;
> +} TCOIORegs;
> +
> +/* tco.c */
> +void acpi_pm_tco_init(TCOIORegs *tr);
> +uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr);
> +void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val);
> +
> +extern const VMStateDescription vmstate_tco_io_sts;
> +
> +#endif /* HW_ACPI_TCO_H */
> diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
> index f4e522c..f41cca6 100644
> --- a/include/hw/i386/ich9.h
> +++ b/include/hw/i386/ich9.h
> @@ -20,6 +20,9 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
>  void ich9_lpc_pm_init(PCIDevice *pci_lpc);
>  I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
>  
> +void ich9_generate_smi(void);
> +void ich9_generate_nmi(void);
> +
>  #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
>  
>  #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
> @@ -156,6 +159,8 @@ Object *ich9_lpc_find(void);
>  #define ICH9_LPC_RCBA_BA_MASK                   Q35_MASK(32, 31, 14)
>  #define ICH9_LPC_RCBA_EN                        0x1
>  #define ICH9_LPC_RCBA_DEFAULT                   0x0
> +#define ICH9_LPC_RCBA_GCS                       0x3410
> +#define ICH9_LPC_RCBA_GCS_NO_REBOOT             (1 << 5)
>  
>  #define ICH9_LPC_PIC_NUM_PINS                   16
>  #define ICH9_LPC_IOAPIC_NUM_PINS                24
> @@ -180,7 +185,10 @@ Object *ich9_lpc_find(void);
>  #define ICH9_PMIO_GPE0_LEN                      16
>  #define ICH9_PMIO_SMI_EN                        0x30
>  #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
> +#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
>  #define ICH9_PMIO_SMI_STS                       0x34
> +#define ICH9_PMIO_TCO_RLD                       0x60
> +#define ICH9_PMIO_TCO_LEN                       32
>  
>  /* FADT ACPI_ENABLE/ACPI_DISABLE */
>  #define ICH9_APM_ACPI_ENABLE                    0x2
> -- 
> 2.1.0

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v3 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-01 23:48     ` [Qemu-devel] [PATCH v3 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
@ 2015-06-17 13:33       ` Michael S. Tsirkin
  2015-06-18  2:14         ` Paulo Alcantara
  0 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-17 13:33 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Mon, Jun 01, 2015 at 08:48:40PM -0300, Paulo Alcantara wrote:
> v1 -> v2:
>   * s/PDRC/CCR/ for clarity and match ICH9 spec
>   * remove unnecessary OperationRegion for RCRB

changelog should come after ---.
An empty commit log is too terse I think.
Could you quote spec in the changelog?

> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
>  hw/i386/q35-acpi-dsdt.dsl     |  14 ++++++++++++++
>  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
>  2 files changed, 14 insertions(+)
> 
> diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> index 16eaca3..92675c8 100644
> --- a/hw/i386/q35-acpi-dsdt.dsl
> +++ b/hw/i386/q35-acpi-dsdt.dsl
> @@ -114,6 +114,20 @@ DefinitionBlock (
>          }
>      }
>  
> +/****************************************************************
> + * Chipset Configuration Registers
> + ****************************************************************/
> +Scope(\_SB.PCI0) {
> +    Device (CCR) {
> +        Name (_HID, EISAID("PNP0C02"))
> +        Name (_UID, 1)
> +
> +        Name (_CRS, ResourceTemplate() {
> +	    Memory32Fixed(ReadWrite, 0xfed1c000, 0x00004000) // RCBA

Indent a bit more please.

> +        })
> +    }
> +}
> +
>  #include "acpi-dsdt-hpet.dsl"
>  
>  
> diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
> index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49 100644
> GIT binary patch
> delta 81
> zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> 
> delta 24
> gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> 
> -- 
> 2.1.0

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v3 3/3] tests: add testcase for TCO watchdog emulation
  2015-06-01 23:48     ` [Qemu-devel] [PATCH v3 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
@ 2015-06-17 13:37       ` Michael S. Tsirkin
  2015-06-18  2:23         ` Paulo Alcantara
  0 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-17 13:37 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Mon, Jun 01, 2015 at 08:48:41PM -0300, Paulo Alcantara wrote:
> v1 -> v2:
>   * some cleanup
>   * add test for TCO_LOCK bit
> v2 -> v3:
>   * add tests for TCO control & status bits
>   * fix check of SECOND_TO_STS bit (it's set in TCO2_STS reg)

Changelog after -- please.
Pls add a bit of description here.

> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
>  tests/Makefile   |   2 +
>  tests/tco-test.c | 475 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 477 insertions(+)
>  create mode 100644 tests/tco-test.c
> 
> diff --git a/tests/Makefile b/tests/Makefile
> index 729b969..43950d0 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -150,6 +150,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
>  check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
>  check-qtest-i386-y += tests/drive_del-test$(EXESUF)
>  check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
> +check-qtest-i386-y += tests/tco-test$(EXESUF)
>  gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
>  check-qtest-i386-y += $(check-qtest-pci-y)
>  gcov-files-i386-y += $(gcov-files-pci-y)
> @@ -363,6 +364,7 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
>  tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
>  tests/ne2000-test$(EXESUF): tests/ne2000-test.o
>  tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
> +tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
>  tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
>  tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
>  tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
> diff --git a/tests/tco-test.c b/tests/tco-test.c
> new file mode 100644
> index 0000000..b9acf43
> --- /dev/null
> +++ b/tests/tco-test.c
> @@ -0,0 +1,475 @@
> +/*
> + * QEMU ICH9 TCO emulation tests
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include <glib.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +
> +#include "libqtest.h"
> +#include "libqos/pci.h"
> +#include "libqos/pci-pc.h"
> +#include "hw/pci/pci_regs.h"
> +#include "hw/i386/ich9.h"
> +#include "hw/acpi/ich9.h"
> +#include "hw/acpi/tco.h"
> +
> +#define PM_IO_BASE_ADDR    0xb000
> +#define RCBA_BASE_ADDR     0xfed1c000
> +

This is duplicated in DSL. Add a macro?

> +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;
> +    QPCIDevice *dev;
> +    void *lpc_base;
> +    void *tco_io_base;
> +} TestData;
> +
> +static void test_init(TestData *d)
> +{
> +    QPCIBus *bus;
> +    QTestState *qs;
> +    char *s;
> +
> +    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
> +    qs = qtest_start(s);
> +    qtest_irq_intercept_in(qs, "ioapic");
> +    g_free(s);
> +
> +    bus = qpci_init_pc();
> +    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
> +    g_assert(d->dev != NULL);
> +
> +    /* map PCI-to-LPC bridge interface BAR */
> +    d->lpc_base = qpci_iomap(d->dev, 0, NULL);
> +
> +    qpci_device_enable(d->dev);
> +
> +    g_assert(d->lpc_base != NULL);
> +
> +    /* set ACPI PM I/O space base address */
> +    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_PMBASE,
> +                       PM_IO_BASE_ADDR | 0x1);
> +    /* enable ACPI I/O */
> +    qpci_config_writeb(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_ACPI_CTRL,
> +                       0x80);
> +    /* set Root Complex BAR */
> +    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_RCBA,
> +                       RCBA_BASE_ADDR | 0x1);
> +
> +    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
> +}
> +
> +static void stop_tco(const TestData *d)
> +{
> +    uint32_t val;
> +
> +    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
> +    val |= TCO_TMR_HLT;
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
> +}
> +
> +static void start_tco(const TestData *d)
> +{
> +    uint32_t val;
> +
> +    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
> +    val &= ~TCO_TMR_HLT;
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
> +}
> +
> +static void load_tco(const TestData *d)
> +{
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
> +}
> +
> +static void set_tco_timeout(const TestData *d, uint16_t ticks)
> +{
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
> +}
> +
> +static void clear_tco_status(const TestData *d)
> +{
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
> +    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
> +}
> +
> +static void reset_on_second_timeout(bool enable)
> +{
> +    uint32_t val;
> +
> +    val = readl(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS);
> +    if (enable) {
> +        val &= ~ICH9_LPC_RCBA_GCS_NO_REBOOT;
> +    } else {
> +        val |= ICH9_LPC_RCBA_GCS_NO_REBOOT;
> +    }
> +    writel(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS, val);
> +}
> +
> +static void test_tco_defaults(void)
> +{
> +    TestData d;
> +
> +    d.args = NULL;
> +    test_init(&d);
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
> +                    TCO_RLD_DEFAULT);
> +    /* TCO_DAT_IN & TCO_DAT_OUT */
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
> +                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
> +    /* TCO1_STS & TCO2_STS */
> +    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
> +                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
> +    /* TCO1_CNT & TCO2_CNT */
> +    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
> +                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
> +    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
> +                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
> +    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
> +                    TCO_WDCNT_DEFAULT);
> +    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
> +                    SW_IRQ_GEN_DEFAULT);
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
> +                    TCO_TMR_DEFAULT);
> +    qtest_end();
> +}
> +
> +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;
> +    test_init(&d);
> +
> +    stop_tco(&d);
> +    clear_tco_status(&d);
> +    reset_on_second_timeout(false);
> +    set_tco_timeout(&d, ticks);
> +    load_tco(&d);
> +    start_tco(&d);
> +    clock_step(ticks * TCO_TICK_NSEC);
> +
> +    /* test first timeout */
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_TIMEOUT ? 1 : 0;
> +    g_assert(ret == 1);
> +
> +    /* test clearing timeout bit */
> +    val |= TCO_TIMEOUT;
> +    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_TIMEOUT ? 1 : 0;
> +    g_assert(ret == 0);
> +
> +    /* test second timeout */
> +    clock_step(ticks * TCO_TICK_NSEC);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_TIMEOUT ? 1 : 0;
> +    g_assert(ret == 1);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
> +    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
> +    g_assert(ret == 1);
> +
> +    stop_tco(&d);
> +    qtest_end();
> +}
> +
> +static void test_tco_max_timeout(void)
> +{
> +    TestData d;
> +    const uint16_t ticks = 0xffff;
> +    uint32_t val;
> +    int ret;
> +
> +    d.args = NULL;
> +    test_init(&d);
> +
> +    stop_tco(&d);
> +    clear_tco_status(&d);
> +    reset_on_second_timeout(false);
> +    set_tco_timeout(&d, ticks);
> +    load_tco(&d);
> +    start_tco(&d);
> +    clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
> +
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
> +    g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_TIMEOUT ? 1 : 0;
> +    g_assert(ret == 0);
> +    clock_step(TCO_TICK_NSEC);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & TCO_TIMEOUT ? 1 : 0;
> +    g_assert(ret == 1);
> +
> +    stop_tco(&d);
> +    qtest_end();
> +}
> +
> +static QDict *get_watchdog_action(void)
> +{
> +    QDict *ev = qmp("");
> +    QDict *data;
> +    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
> +
> +    data = qdict_get_qdict(ev, "data");
> +    QINCREF(data);
> +    QDECREF(ev);
> +    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";
> +    test_init(&td);
> +
> +    stop_tco(&td);
> +    clear_tco_status(&td);
> +    reset_on_second_timeout(true);
> +    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
> +    load_tco(&td);
> +    start_tco(&td);
> +    clock_step(ticks * TCO_TICK_NSEC * 2);
> +    ad = get_watchdog_action();
> +    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
> +    QDECREF(ad);
> +
> +    stop_tco(&td);
> +    qtest_end();
> +}
> +
> +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";
> +    test_init(&td);
> +
> +    stop_tco(&td);
> +    clear_tco_status(&td);
> +    reset_on_second_timeout(true);
> +    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
> +    load_tco(&td);
> +    start_tco(&td);
> +    clock_step(ticks * TCO_TICK_NSEC * 2);
> +    ad = get_watchdog_action();
> +    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
> +    QDECREF(ad);
> +
> +    stop_tco(&td);
> +    qtest_end();
> +}
> +
> +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";
> +    test_init(&td);
> +
> +    stop_tco(&td);
> +    clear_tco_status(&td);
> +    reset_on_second_timeout(true);
> +    set_tco_timeout(&td, ticks);
> +    load_tco(&td);
> +    start_tco(&td);
> +    clock_step(ticks * TCO_TICK_NSEC * 2);
> +    ad = get_watchdog_action();
> +    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
> +    QDECREF(ad);
> +
> +    stop_tco(&td);
> +    qtest_end();
> +}
> +
> +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";
> +    test_init(&td);
> +
> +    stop_tco(&td);
> +    clear_tco_status(&td);
> +    reset_on_second_timeout(true);
> +    set_tco_timeout(&td, ticks);
> +    load_tco(&td);
> +    start_tco(&td);
> +    clock_step(ticks * TCO_TICK_NSEC * 2);
> +    ad = get_watchdog_action();
> +    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
> +    QDECREF(ad);
> +
> +    stop_tco(&td);
> +    qtest_end();
> +}
> +
> +static void test_tco_ticks_counter(void)
> +{
> +    TestData d;
> +    uint16_t ticks = TCO_SECS_TO_TICKS(8);
> +    uint16_t rld;
> +
> +    d.args = NULL;
> +    test_init(&d);
> +
> +    stop_tco(&d);
> +    clear_tco_status(&d);
> +    reset_on_second_timeout(false);
> +    set_tco_timeout(&d, ticks);
> +    load_tco(&d);
> +    start_tco(&d);
> +
> +    do {
> +        rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
> +        g_assert_cmpint(rld, ==, ticks);
> +        clock_step(TCO_TICK_NSEC);
> +        ticks--;
> +    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
> +
> +    stop_tco(&d);
> +    qtest_end();
> +}
> +
> +static void test_tco1_control_bits(void)
> +{
> +    TestData d;
> +    uint16_t val;
> +
> +    d.args = NULL;
> +    test_init(&d);
> +
> +    val = TCO_LOCK;
> +    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
> +    val &= ~TCO_LOCK;
> +    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
> +                    TCO_LOCK);
> +    qtest_end();
> +}
> +
> +static void test_tco1_status_bits(void)
> +{
> +    TestData d;
> +    uint16_t ticks = 8;
> +    uint16_t val;
> +    int ret;
> +
> +    d.args = NULL;
> +    test_init(&d);
> +
> +    stop_tco(&d);
> +    clear_tco_status(&d);
> +    reset_on_second_timeout(false);
> +    set_tco_timeout(&d, ticks);
> +    load_tco(&d);
> +    start_tco(&d);
> +    clock_step(ticks * TCO_TICK_NSEC);
> +
> +    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0);
> +    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0);
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
> +    ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
> +    g_assert(ret == 1);
> +    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0);
> +    qtest_end();
> +}
> +
> +static void test_tco2_status_bits(void)
> +{
> +    TestData d;
> +    uint16_t ticks = 8;
> +    uint16_t val;
> +    int ret;
> +
> +    d.args = "-watchdog-action none";
> +    test_init(&d);
> +
> +    stop_tco(&d);
> +    clear_tco_status(&d);
> +    reset_on_second_timeout(true);
> +    set_tco_timeout(&d, ticks);
> +    load_tco(&d);
> +    start_tco(&d);
> +    clock_step(ticks * TCO_TICK_NSEC * 2);
> +
> +    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
> +    ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
> +    g_assert(ret == 1);
> +    qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val);
> +    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0);
> +    qtest_end();
> +}
> +
> +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();
> +}
> -- 
> 2.1.0

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v3 1/3] ich9: add TCO interface emulation
  2015-06-17 13:27     ` Michael S. Tsirkin
@ 2015-06-18  2:10       ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-18  2:10 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Wed, 17 Jun 2015 15:27:53 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Mon, Jun 01, 2015 at 08:48:39PM -0300, Paulo Alcantara wrote:
> > This interface provides some registers within a 32-byte range and
> > can be acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).
> > 
> > It's commonly used as a watchdog timer to detect system lockups
> > through SMIs that are generated -- if TCO_EN bit is set -- on every
> > timeout. If NO_REBOOT bit is not set in GCS (General Control and
> > Status register), the system will be resetted upon second timeout
> > if TCO_RLD register wasn't previously written to prevent timeout.
> > 
> > This patch adds support to TCO watchdog logic and few other features
> > like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder
> > detection, etc. are not implemented yet.
> > 
> > v1 -> v2:
> >   * add migration support for TCO I/O device state
> >   * wake up only when total time expired instead of every 0.6s
> >   * some cleanup suggested by Paolo Bonzini
> > v2 -> v3:
> >   * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
> >   * improve handling of TCO_LOCK bit in TCO1_CNT register
> 
> changelog must come after --- so that git am ignores it.

Ok.

> 
> > 
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> 
> for some reason v3 was sent as reply to v2.
> Don't do that please.

Indeed. Sorry about that.

> 
> 
> > ---
> >  hw/acpi/Makefile.objs  |   2 +-
> >  hw/acpi/ich9.c         |  59 ++++++++++++
> >  hw/acpi/tco.c          | 254
> > +++++++++++++++++++++++++++++++++++++++++++++++++
> > hw/isa/lpc_ich9.c      |  10 ++ include/hw/acpi/ich9.h |   4 +
> >  include/hw/acpi/tco.h  |  98 +++++++++++++++++++
> >  include/hw/i386/ich9.h |   8 ++
> >  7 files changed, 434 insertions(+), 1 deletion(-)
> >  create mode 100644 hw/acpi/tco.c
> >  create mode 100644 include/hw/acpi/tco.h
> > 
> > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> > index 29d46d8..3db1f07 100644
> > --- a/hw/acpi/Makefile.objs
> > +++ b/hw/acpi/Makefile.objs
> > @@ -1,4 +1,4 @@
> > -common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
> > +common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
> > tco.o common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
> >  common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
> >  common-obj-$(CONFIG_ACPI) += acpi_interface.o
> > diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
> > index 84e5bb8..10959fa 100644
> > --- a/hw/acpi/ich9.c
> > +++ b/hw/acpi/ich9.c
> > @@ -30,6 +30,7 @@
> >  #include "qemu/timer.h"
> >  #include "sysemu/sysemu.h"
> >  #include "hw/acpi/acpi.h"
> > +#include "hw/acpi/tco.h"
> >  #include "sysemu/kvm.h"
> >  #include "exec/address-spaces.h"
> >  
> > @@ -92,8 +93,15 @@ static void ich9_smi_writel(void *opaque, hwaddr
> > addr, uint64_t val, unsigned width)
> >  {
> >      ICH9LPCPMRegs *pm = opaque;
> > +    TCOIORegs *tr = &pm->tco_regs;
> > +
> >      switch (addr) {
> >      case 0:
> > +        /* once TCO_LOCK bit is set, TCO_EN bit cannot be
> > overwritten */
> > +        if (tr->tco.cnt1 & TCO_LOCK) {
> > +            val &= ~ICH9_PMIO_SMI_EN_TCO_EN;
> > +            val |= pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
> > +        }
> >          pm->smi_en = val;
> >          break;
> >      }
> > @@ -107,6 +115,29 @@ static const MemoryRegionOps ich9_smi_ops = {
> >      .endianness = DEVICE_LITTLE_ENDIAN,
> >  };
> >  
> > +static uint64_t ich9_tco_readw(void *opaque, hwaddr addr, unsigned
> > width) +{
> > +    ICH9LPCPMRegs *pm = opaque;
> > +    return acpi_pm_tco_ioport_readw(&pm->tco_regs, addr);
> > +}
> > +
> > +static void ich9_tco_writew(void *opaque, hwaddr addr, uint64_t
> > val,
> > +                            unsigned width)
> > +{
> > +    ICH9LPCPMRegs *pm = opaque;
> > +    acpi_pm_tco_ioport_writew(&pm->tco_regs, addr, val);
> > +}
> > +
> > +static const MemoryRegionOps ich9_tco_ops = {
> > +    .read = ich9_tco_readw,
> > +    .write = ich9_tco_writew,
> > +    .valid.min_access_size = 1,
> > +    .valid.max_access_size = 4,
> > +    .impl.min_access_size = 1,
> > +    .impl.max_access_size = 2,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +};
> > +
> >  void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
> >  {
> >      ICH9_DEBUG("to 0x%x\n", pm_io_base);
> > @@ -157,6 +188,24 @@ static const VMStateDescription
> > vmstate_memhp_state = { }
> >  };
> >  
> > +static bool vmstate_test_use_tco(void *opaque)
> > +{
> > +    ICH9LPCPMRegs *s = opaque;
> > +    return s->tco_regs.use_tco;
> > +}
> > +
> > +static const VMStateDescription vmstate_tco_io_state = {
> > +    .name = "ich9_pm/tco",
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> > +    .minimum_version_id_old = 1,
> > +    .fields      = (VMStateField[]) {
> > +        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1,
> > vmstate_tco_io_sts,
> > +                       TCOIORegs),
> > +        VMSTATE_END_OF_LIST()
> > +    }
> > +};
> > +
> >  const VMStateDescription vmstate_ich9_pm = {
> >      .name = "ich9_pm",
> >      .version_id = 1,
> > @@ -179,6 +228,10 @@ const VMStateDescription vmstate_ich9_pm = {
> >              .vmsd = &vmstate_memhp_state,
> >              .needed = vmstate_test_use_memhp,
> >          },
> > +        {
> > +            .vmsd = &vmstate_tco_io_state,
> > +            .needed = vmstate_test_use_tco,
> > +        },
> >          VMSTATE_END_OF_LIST()
> >      }
> >  };
> > @@ -230,6 +283,11 @@ void ich9_pm_init(PCIDevice *lpc_pci,
> > ICH9LPCPMRegs *pm, "acpi-smi", 8);
> >      memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN,
> > &pm->io_smi); 
> > +    acpi_pm_tco_init(&pm->tco_regs);
> > +    memory_region_init_io(&pm->io_tco, OBJECT(lpc_pci),
> > &ich9_tco_ops, pm,
> > +			  "sm-tco", ICH9_PMIO_TCO_LEN);
> > +    memory_region_add_subregion(&pm->io, ICH9_PMIO_TCO_RLD,
> > &pm->io_tco); +
> >      pm->irq = sci_irq;
> >      qemu_register_reset(pm_reset, pm);
> >      pm->powerdown_notifier.notify = pm_powerdown_req;
> > @@ -357,6 +415,7 @@ void ich9_pm_add_properties(Object *obj,
> > ICH9LPCPMRegs *pm, Error **errp) pm->disable_s3 = 0;
> >      pm->disable_s4 = 0;
> >      pm->s4_val = 2;
> > +    pm->tco_regs.use_tco = true;
> 
> Would be safer to add a property, and not to enable this for old
> machine types.

This use_tco field is *really* confusing. My initial idea of using this
field was to determine whether migrate TCO registers or not, but now
that doesn't make sense at all. On Q35 machine type all TCO registers
should be migrated and initialised.

Since this is ICH9-specific code, doesn't this mean other machine types
(e.g. i440FX) wouldn't be supported? would we need to add a property to
it yet? 	

> 
> >  
> >      object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE,
> >                                     &pm->pm_io_base, errp);
> > diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> > new file mode 100644
> > index 0000000..c159281
> > --- /dev/null
> > +++ b/hw/acpi/tco.c
> > @@ -0,0 +1,254 @@
> > +/*
> > + * QEMU ICH9 TCO emulation
> > + *
> > + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> > obtaining a copy
> > + * of this software and associated documentation files (the
> > "Software"), to deal
> > + * in the Software without restriction, including without
> > limitation the rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense,
> > and/or sell
> > + * copies of the Software, and to permit persons to whom the
> > Software is
> > + * furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice shall be
> > included in
> > + * all copies or substantial portions of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> > EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
> > EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
> > DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> > ARISING FROM,
> > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > DEALINGS IN
> > + * THE SOFTWARE.
> > + */
> > +#include "qemu-common.h"
> > +#include "sysemu/watchdog.h"
> > +#include "hw/i386/ich9.h"
> > +
> > +#include "hw/acpi/tco.h"
> > +
> > +//#define DEBUG
> > +
> > +#ifdef DEBUG
> > +#define TCO_DEBUG(fmt, ...)                                     \
> > +    do {                                                        \
> > +        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
> > +    } while (0)
> > +#else
> > +#define TCO_DEBUG(fmt, ...) do { } while (0)
> > +#endif
> > +
> > +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,
> > +};
> > +
> > +static inline void tco_timer_reload(TCOIORegs *tr)
> > +{
> > +    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> > +        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
> > +    timer_mod(tr->tco_timer, tr->expire_time);
> > +}
> > +
> > +static inline void tco_timer_stop(TCOIORegs *tr)
> > +{
> > +    tr->expire_time = -1;
> > +}
> > +
> > +static void tco_timer_expired(void *opaque)
> > +{
> > +    TCOIORegs *tr = opaque;
> > +    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
> > +    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
> > +    uint32_t gcs = pci_get_long(lpc->chip_config +
> > ICH9_LPC_RCBA_GCS); +
> > +    tr->tco.rld = 0;
> > +    tr->tco.sts1 |= TCO_TIMEOUT;
> > +    if (++tr->timeouts_no == 2) {
> > +        tr->tco.sts2 |= TCO_SECOND_TO_STS;
> > +        tr->tco.sts2 |= TCO_BOOT_STS;
> > +        tr->timeouts_no = 0;
> > +
> > +        if (!(gcs & ICH9_LPC_RCBA_GCS_NO_REBOOT)) {
> > +            watchdog_perform_action();
> > +            tco_timer_stop(tr);
> > +            return;
> 
> So this is a virtual clock - not running when VM is not running.
> Doesn't this mean if you stop VM for a while, it's almost sure to
> fire when you resume?

Yes, it will.

> 
> Maybe it's a useful feature, but maybe it's best to keep it off by
> default?

The timer is already halted by default. So you might ask me why it's
halted if TCO_TMR_HLT bit is 0 by default (e.g. timer running)? It will
only start counting down if someone reloads it through TCO_RLD register.

I would just keep it running when resuming VM because ICH9 spec says
that it will _only_ count down in the S0 state. Although, I think it's
worth mentioning that the firmware should be able to stop it (or
restore its default values) while in the S3 resume path.

> 
> > +        }
> > +    }
> > +
> > +    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
> > +        ich9_generate_smi();
> > +    } else {
> > +        ich9_generate_nmi();
> > +    }
> > +    tr->tco.rld = tr->tco.tmr;
> > +    tco_timer_reload(tr);
> > +}
> > +
> > +void acpi_pm_tco_init(TCOIORegs *tr)
> > +{
> > +    *tr = (TCOIORegs) {
> > +        .tco = {
> > +            .rld      = TCO_RLD_DEFAULT,
> > +            .din      = TCO_DAT_IN_DEFAULT,
> > +            .dout     = TCO_DAT_OUT_DEFAULT,
> > +            .sts1     = TCO1_STS_DEFAULT,
> > +            .sts2     = TCO2_STS_DEFAULT,
> > +            .cnt1     = TCO1_CNT_DEFAULT,
> > +            .cnt2     = TCO2_CNT_DEFAULT,
> > +            .msg1     = TCO_MESSAGE1_DEFAULT,
> > +            .msg2     = TCO_MESSAGE2_DEFAULT,
> > +            .wdcnt    = TCO_WDCNT_DEFAULT,
> > +            .tmr      = TCO_TMR_DEFAULT,
> > +        },
> > +        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
> > +        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL,
> > tco_timer_expired, tr),
> > +        .expire_time   = -1,
> > +        .timeouts_no   = 0,
> > +    };
> > +}
> > +
> > +/* NOTE: values of 0 or 1 will be ignored by ICH */
> > +static inline int can_start_tco_timer(TCOIORegs *tr)
> > +{
> > +    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
> > +}
> > +
> > +uint32_t acpi_pm_tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
> > +{
> > +    uint16_t rld;
> > +
> > +    switch (addr) {
> > +    case TCO_RLD:
> > +        if (tr->expire_time != -1) {
> > +            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > +            int64_t elapsed = (tr->expire_time - now) /
> > TCO_TICK_NSEC;
> > +            rld = (uint16_t)elapsed | (tr->tco.rld &
> > ~TCO_RLD_MASK);
> > +        } else {
> > +            rld = tr->tco.rld;
> > +        }
> > +        return rld;
> > +    case TCO_DAT_IN:
> > +        return tr->tco.din;
> > +    case TCO_DAT_OUT:
> > +        return tr->tco.dout;
> > +    case TCO1_STS:
> > +        return tr->tco.sts1;
> > +    case TCO2_STS:
> > +        return tr->tco.sts2;
> > +    case TCO1_CNT:
> > +        return tr->tco.cnt1;
> > +    case TCO2_CNT:
> > +        return tr->tco.cnt2;
> > +    case TCO_MESSAGE1:
> > +        return tr->tco.msg1;
> > +    case TCO_MESSAGE2:
> > +        return tr->tco.msg2;
> > +    case TCO_WDCNT:
> > +        return tr->tco.wdcnt;
> > +    case TCO_TMR:
> > +        return tr->tco.tmr;
> > +    case SW_IRQ_GEN:
> > +        return tr->sw_irq_gen;
> > +    }
> > +    return 0;
> > +}
> > +
> > +void acpi_pm_tco_ioport_writew(TCOIORegs *tr, uint32_t addr,
> > uint32_t val) +{
> > +    switch (addr) {
> > +    case TCO_RLD:
> > +        tr->timeouts_no = 0;
> > +        if (can_start_tco_timer(tr)) {
> > +            tr->tco.rld = tr->tco.tmr;
> > +            tco_timer_reload(tr);
> > +        } else {
> > +            tr->tco.rld = val;
> > +        }
> > +        break;
> > +    case TCO_DAT_IN:
> > +        tr->tco.din = val;
> > +        tr->tco.sts1 |= SW_TCO_SMI;
> > +        ich9_generate_smi();
> > +        break;
> > +    case TCO_DAT_OUT:
> > +        tr->tco.dout = val;
> > +        tr->tco.sts1 |= TCO_INT_STS;
> > +        /* TODO: cause an interrupt, as selected by the
> > TCO_INT_SEL bits */
> > +        break;
> > +    case TCO1_STS:
> > +        tr->tco.sts1 = val & TCO1_STS_MASK;
> > +        break;
> > +    case TCO2_STS:
> > +        tr->tco.sts2 = val & TCO2_STS_MASK;
> > +        break;
> > +    case TCO1_CNT:
> > +        val &= TCO1_CNT_MASK;
> > +        /*
> > +         * once TCO_LOCK bit is set, it can not be cleared by
> > software. a reset
> > +         * is required to change this bit from 1 to 0 -- it
> > defaults to 0.
> > +         */
> > +        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
> > +        if (can_start_tco_timer(tr)) {
> > +            tr->tco.rld = tr->tco.tmr;
> > +            tco_timer_reload(tr);
> > +        } else {
> > +            tco_timer_stop(tr);
> > +        }
> > +        break;
> > +    case TCO2_CNT:
> > +        tr->tco.cnt2 = val;
> > +        break;
> > +    case TCO_MESSAGE1:
> > +        tr->tco.msg1 = val;
> > +        break;
> > +    case TCO_MESSAGE2:
> > +        tr->tco.msg2 = val;
> > +        break;
> > +    case TCO_WDCNT:
> > +        tr->tco.wdcnt = val;
> > +        break;
> > +    case TCO_TMR:
> > +        tr->tco.tmr = val;
> > +        break;
> > +    case SW_IRQ_GEN:
> > +        tr->sw_irq_gen = val;
> > +        break;
> > +    }
> > +}
> > +
> > +const VMStateDescription vmstate_tco_io_sts = {
> > +    .name = "tco io device status",
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> > +    .minimum_version_id_old = 1,
> > +    .fields      = (VMStateField[]) {
> > +        VMSTATE_BOOL(use_tco, TCOIORegs),
> 
> So this field is only migrated if it is true.
> Doesn't seem to make sense.

Yeah, indeed. I should remove this field and always migrate TCO
registers on Q35 machine type.

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v3 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-17 13:33       ` Michael S. Tsirkin
@ 2015-06-18  2:14         ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-18  2:14 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Wed, 17 Jun 2015 15:33:07 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Mon, Jun 01, 2015 at 08:48:40PM -0300, Paulo Alcantara wrote:
> > v1 -> v2:
> >   * s/PDRC/CCR/ for clarity and match ICH9 spec
> >   * remove unnecessary OperationRegion for RCRB
> 
> changelog should come after ---.

Ok.

> An empty commit log is too terse I think.
> Could you quote spec in the changelog?

Yes, sure.

> 
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > ---
> >  hw/i386/q35-acpi-dsdt.dsl     |  14 ++++++++++++++
> >  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
> >  2 files changed, 14 insertions(+)
> > 
> > diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> > index 16eaca3..92675c8 100644
> > --- a/hw/i386/q35-acpi-dsdt.dsl
> > +++ b/hw/i386/q35-acpi-dsdt.dsl
> > @@ -114,6 +114,20 @@ DefinitionBlock (
> >          }
> >      }
> >  
> > +/****************************************************************
> > + * Chipset Configuration Registers
> > + ****************************************************************/
> > +Scope(\_SB.PCI0) {
> > +    Device (CCR) {
> > +        Name (_HID, EISAID("PNP0C02"))
> > +        Name (_UID, 1)
> > +
> > +        Name (_CRS, ResourceTemplate() {
> > +	    Memory32Fixed(ReadWrite, 0xfed1c000, 0x00004000) //
> > RCBA
> 
> Indent a bit more please.

Ok.

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v3 3/3] tests: add testcase for TCO watchdog emulation
  2015-06-17 13:37       ` Michael S. Tsirkin
@ 2015-06-18  2:23         ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-18  2:23 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Wed, 17 Jun 2015 15:37:49 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Mon, Jun 01, 2015 at 08:48:41PM -0300, Paulo Alcantara wrote:
> > v1 -> v2:
> >   * some cleanup
> >   * add test for TCO_LOCK bit
> > v2 -> v3:
> >   * add tests for TCO control & status bits
> >   * fix check of SECOND_TO_STS bit (it's set in TCO2_STS reg)
> 
> Changelog after -- please.
> Pls add a bit of description here.

Ok.

> 
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > ---
> >  tests/Makefile   |   2 +
> >  tests/tco-test.c | 475
> > +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files
> > changed, 477 insertions(+) create mode 100644 tests/tco-test.c
> > 
> > diff --git a/tests/Makefile b/tests/Makefile
> > index 729b969..43950d0 100644
> > --- a/tests/Makefile
> > +++ b/tests/Makefile
> > @@ -150,6 +150,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
> >  check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
> >  check-qtest-i386-y += tests/drive_del-test$(EXESUF)
> >  check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
> > +check-qtest-i386-y += tests/tco-test$(EXESUF)
> >  gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
> >  check-qtest-i386-y += $(check-qtest-pci-y)
> >  gcov-files-i386-y += $(gcov-files-pci-y)
> > @@ -363,6 +364,7 @@ tests/eepro100-test$(EXESUF):
> > tests/eepro100-test.o tests/vmxnet3-test$(EXESUF):
> > tests/vmxnet3-test.o tests/ne2000-test$(EXESUF): tests/ne2000-test.o
> >  tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
> > +tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
> >  tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
> >  tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o
> > $(libqos-virtio-obj-y) tests/virtio-net-test$(EXESUF):
> > tests/virtio-net-test.o $(libqos-pc-obj-y) diff --git
> > a/tests/tco-test.c b/tests/tco-test.c new file mode 100644
> > index 0000000..b9acf43
> > --- /dev/null
> > +++ b/tests/tco-test.c
> > @@ -0,0 +1,475 @@
> > +/*
> > + * QEMU ICH9 TCO emulation tests
> > + *
> > + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> > obtaining a copy
> > + * of this software and associated documentation files (the
> > "Software"), to deal
> > + * in the Software without restriction, including without
> > limitation the rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense,
> > and/or sell
> > + * copies of the Software, and to permit persons to whom the
> > Software is
> > + * furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice shall be
> > included in
> > + * all copies or substantial portions of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> > EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
> > EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
> > DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> > ARISING FROM,
> > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > DEALINGS IN
> > + * THE SOFTWARE.
> > + */
> > +#include <glib.h>
> > +#include <string.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +
> > +#include "libqtest.h"
> > +#include "libqos/pci.h"
> > +#include "libqos/pci-pc.h"
> > +#include "hw/pci/pci_regs.h"
> > +#include "hw/i386/ich9.h"
> > +#include "hw/acpi/ich9.h"
> > +#include "hw/acpi/tco.h"
> > +
> > +#define PM_IO_BASE_ADDR    0xb000
> > +#define RCBA_BASE_ADDR     0xfed1c000
> > +
> 
> This is duplicated in DSL. Add a macro?

Ok. I will add a macro for RCBA address.

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-05-27  0:29 [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paulo Alcantara
                   ` (3 preceding siblings ...)
  2015-05-30 22:04 ` [Qemu-devel] [PATCH v2 " Paulo Alcantara
@ 2015-06-22  0:37 ` Paulo Alcantara
  2015-06-22  0:37   ` [Qemu-devel] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
                     ` (4 more replies)
  2015-06-22 23:10 ` [Qemu-devel] [PATCH v5 " Paulo Alcantara
                   ` (3 subsequent siblings)
  8 siblings, 5 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22  0:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, mst

This interface provides some registers within a 32-byte range and can be
acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).

It's commonly used as a watchdog timer to detect system lockups through
SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
NO_REBOOT bit is not set in GCS (General Control and Status register),
the system will be resetted upon second timeout if TCO_RLD register
wasn't previously written to prevent timeout.

This patch adds support to TCO watchdog logic and few other features
like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
etc. are not implemented yet.

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * add migration support for TCO I/O device state
  * wake up only when total time expired instead of every 0.6s
  * some cleanup suggested by Paolo Bonzini

v2 -> v3:
  * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
  * improve handling of TCO_LOCK bit in TCO1_CNT register

v3 -> v4:
  * fix some conflicts in hw/acpi/ich9.c after rebasing against master
  * remove meaningless "use_tco" field from TCOIORegs structure
  * add a object property named "enable_tco" and only enable TCO support
    on pc-q35-2.4 and later
---
 hw/acpi/Makefile.objs  |   2 +-
 hw/acpi/ich9.c         |  55 +++++++++-
 hw/acpi/tco.c          | 279 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/i386/pc_q35.c       |   4 +-
 hw/isa/lpc_ich9.c      |  15 ++-
 include/hw/acpi/ich9.h |   7 +-
 include/hw/acpi/tco.h  |  98 +++++++++++++++++
 include/hw/boards.h    |   3 +-
 include/hw/i386/ich9.h |  10 +-
 include/hw/i386/pc.h   |   1 +
 10 files changed, 466 insertions(+), 8 deletions(-)
 create mode 100644 hw/acpi/tco.c
 create mode 100644 include/hw/acpi/tco.h

diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 29d46d8..3db1f07 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
+common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o tco.o
 common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
 common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI) += acpi_interface.o
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 8a64ffb..d3d9953 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -30,6 +30,7 @@
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "hw/acpi/acpi.h"
+#include "hw/acpi/tco.h"
 #include "sysemu/kvm.h"
 #include "exec/address-spaces.h"
 
@@ -92,8 +93,16 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
                             unsigned width)
 {
     ICH9LPCPMRegs *pm = opaque;
+    TCOIORegs *tr = &pm->tco_regs;
+    uint64_t tco_en;
+
     switch (addr) {
     case 0:
+        tco_en = pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
+        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
+        if (tr->tco.cnt1 & TCO_LOCK) {
+            val = (val & ~ICH9_PMIO_SMI_EN_TCO_EN) | tco_en;
+        }
         pm->smi_en &= ~pm->smi_en_wmask;
         pm->smi_en |= (val & pm->smi_en_wmask);
         break;
@@ -159,6 +168,25 @@ static const VMStateDescription vmstate_memhp_state = {
     }
 };
 
+static bool vmstate_test_use_tco(void *opaque)
+{
+    ICH9LPCPMRegs *s = opaque;
+    return s->enable_tco;
+}
+
+static const VMStateDescription vmstate_tco_io_state = {
+    .name = "ich9_pm/tco",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .needed = vmstate_test_use_tco,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
+                       TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 const VMStateDescription vmstate_ich9_pm = {
     .name = "ich9_pm",
     .version_id = 1,
@@ -179,6 +207,10 @@ const VMStateDescription vmstate_ich9_pm = {
     .subsections = (const VMStateDescription*[]) {
         &vmstate_memhp_state,
         NULL
+    },
+    .subsections = (const VMStateDescription*[]) {
+        &vmstate_tco_io_state,
+        NULL
     }
 };
 
@@ -209,7 +241,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
     acpi_pm1_evt_power_down(&pm->acpi_regs);
 }
 
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
                   qemu_irq sci_irq)
 {
     memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
@@ -231,6 +263,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
                           "acpi-smi", 8);
     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
 
+    pm->enable_tco = enable_tco;
+    if (pm->enable_tco) {
+        acpi_pm_tco_init(&pm->tco_regs, &pm->io);
+    }
+
     pm->irq = sci_irq;
     qemu_register_reset(pm_reset, pm);
     pm->powerdown_notifier.notify = pm_powerdown_req;
@@ -351,6 +388,18 @@ out:
     error_propagate(errp, local_err);
 }
 
+static bool ich9_pm_get_enable_tco(Object *obj, Error **errp)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+    return s->pm.enable_tco;
+}
+
+static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+    s->pm.enable_tco = value;
+}
+
 void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
 {
     static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
@@ -382,6 +431,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
                         ich9_pm_get_s4_val,
                         ich9_pm_set_s4_val,
                         NULL, pm, NULL);
+    object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
+                             ich9_pm_get_enable_tco,
+                             ich9_pm_set_enable_tco,
+                             NULL);
 }
 
 void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp)
diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
new file mode 100644
index 0000000..b6af1d9
--- /dev/null
+++ b/hw/acpi/tco.c
@@ -0,0 +1,279 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "sysemu/watchdog.h"
+#include "hw/i386/ich9.h"
+
+#include "hw/acpi/tco.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define TCO_DEBUG(fmt, ...)                                     \
+    do {                                                        \
+        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
+    } while (0)
+#else
+#define TCO_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+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,
+};
+
+static inline void tco_timer_reload(TCOIORegs *tr)
+{
+    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
+    timer_mod(tr->tco_timer, tr->expire_time);
+}
+
+static inline void tco_timer_stop(TCOIORegs *tr)
+{
+    tr->expire_time = -1;
+}
+
+static void tco_timer_expired(void *opaque)
+{
+    TCOIORegs *tr = opaque;
+    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
+    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
+    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_LPC_RCBA_GCS);
+
+    tr->tco.rld = 0;
+    tr->tco.sts1 |= TCO_TIMEOUT;
+    if (++tr->timeouts_no == 2) {
+        tr->tco.sts2 |= TCO_SECOND_TO_STS;
+        tr->tco.sts2 |= TCO_BOOT_STS;
+        tr->timeouts_no = 0;
+
+        if (!(gcs & ICH9_LPC_RCBA_GCS_NO_REBOOT)) {
+            watchdog_perform_action();
+            tco_timer_stop(tr);
+            return;
+        }
+    }
+
+    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
+        ich9_generate_smi();
+    } else {
+        ich9_generate_nmi();
+    }
+    tr->tco.rld = tr->tco.tmr;
+    tco_timer_reload(tr);
+}
+
+/* NOTE: values of 0 or 1 will be ignored by ICH */
+static inline int can_start_tco_timer(TCOIORegs *tr)
+{
+    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
+}
+
+static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
+{
+    uint16_t rld;
+
+    switch (addr) {
+    case TCO_RLD:
+        if (tr->expire_time != -1) {
+            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
+            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
+        } else {
+            rld = tr->tco.rld;
+        }
+        return rld;
+    case TCO_DAT_IN:
+        return tr->tco.din;
+    case TCO_DAT_OUT:
+        return tr->tco.dout;
+    case TCO1_STS:
+        return tr->tco.sts1;
+    case TCO2_STS:
+        return tr->tco.sts2;
+    case TCO1_CNT:
+        return tr->tco.cnt1;
+    case TCO2_CNT:
+        return tr->tco.cnt2;
+    case TCO_MESSAGE1:
+        return tr->tco.msg1;
+    case TCO_MESSAGE2:
+        return tr->tco.msg2;
+    case TCO_WDCNT:
+        return tr->tco.wdcnt;
+    case TCO_TMR:
+        return tr->tco.tmr;
+    case SW_IRQ_GEN:
+        return tr->sw_irq_gen;
+    }
+    return 0;
+}
+
+static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
+{
+    switch (addr) {
+    case TCO_RLD:
+        tr->timeouts_no = 0;
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tr->tco.rld = val;
+        }
+        break;
+    case TCO_DAT_IN:
+        tr->tco.din = val;
+        tr->tco.sts1 |= SW_TCO_SMI;
+        ich9_generate_smi();
+        break;
+    case TCO_DAT_OUT:
+        tr->tco.dout = val;
+        tr->tco.sts1 |= TCO_INT_STS;
+        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
+        break;
+    case TCO1_STS:
+        tr->tco.sts1 = val & TCO1_STS_MASK;
+        break;
+    case TCO2_STS:
+        tr->tco.sts2 = val & TCO2_STS_MASK;
+        break;
+    case TCO1_CNT:
+        val &= TCO1_CNT_MASK;
+        /*
+         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
+         * is required to change this bit from 1 to 0 -- it defaults to 0.
+         */
+        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tco_timer_stop(tr);
+        }
+        break;
+    case TCO2_CNT:
+        tr->tco.cnt2 = val;
+        break;
+    case TCO_MESSAGE1:
+        tr->tco.msg1 = val;
+        break;
+    case TCO_MESSAGE2:
+        tr->tco.msg2 = val;
+        break;
+    case TCO_WDCNT:
+        tr->tco.wdcnt = val;
+        break;
+    case TCO_TMR:
+        tr->tco.tmr = val;
+        break;
+    case SW_IRQ_GEN:
+        tr->sw_irq_gen = val;
+        break;
+    }
+}
+
+static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width)
+{
+    TCOIORegs *tr = opaque;
+    return tco_ioport_readw(tr, addr);
+}
+
+static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val,
+                          unsigned width)
+{
+    TCOIORegs *tr = opaque;
+    tco_ioport_writew(tr, addr, val);
+}
+
+static const MemoryRegionOps tco_io_ops = {
+    .read = tco_io_readw,
+    .write = tco_io_writew,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent)
+{
+    *tr = (TCOIORegs) {
+        .tco = {
+            .rld      = TCO_RLD_DEFAULT,
+            .din      = TCO_DAT_IN_DEFAULT,
+            .dout     = TCO_DAT_OUT_DEFAULT,
+            .sts1     = TCO1_STS_DEFAULT,
+            .sts2     = TCO2_STS_DEFAULT,
+            .cnt1     = TCO1_CNT_DEFAULT,
+            .cnt2     = TCO2_CNT_DEFAULT,
+            .msg1     = TCO_MESSAGE1_DEFAULT,
+            .msg2     = TCO_MESSAGE2_DEFAULT,
+            .wdcnt    = TCO_WDCNT_DEFAULT,
+            .tmr      = TCO_TMR_DEFAULT,
+        },
+        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
+        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
+        .expire_time   = -1,
+        .timeouts_no   = 0,
+    };
+    memory_region_init_io(&tr->io, memory_region_owner(parent),
+                          &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN);
+    memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io);
+}
+
+const VMStateDescription vmstate_tco_io_sts = {
+    .name = "tco io device status",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT16(tco.rld, TCOIORegs),
+        VMSTATE_UINT8(tco.din, TCOIORegs),
+        VMSTATE_UINT8(tco.dout, TCOIORegs),
+        VMSTATE_UINT16(tco.sts1, TCOIORegs),
+        VMSTATE_UINT16(tco.sts2, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
+        VMSTATE_UINT8(tco.msg1, TCOIORegs),
+        VMSTATE_UINT8(tco.msg2, TCOIORegs),
+        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
+        VMSTATE_UINT16(tco.tmr, TCOIORegs),
+        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
+        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
+        VMSTATE_INT64(expire_time, TCOIORegs),
+        VMSTATE_UINT8(timeouts_no, TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 082cd93..7e35a36 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -253,7 +253,7 @@ static void pc_q35_init(MachineState *machine)
                          (pc_machine->vmport != ON_OFF_AUTO_ON), 0xff0104);
 
     /* connect pm stuff to lpc */
-    ich9_lpc_pm_init(lpc);
+    ich9_lpc_pm_init(lpc, !mc->no_tco);
 
     /* ahci and SATA device, for q35 1 ahci controller is built-in */
     ahci = pci_create_simple_multifunction(host_bus,
@@ -393,6 +393,7 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
     m->default_machine_opts = "firmware=bios-256k.bin";
     m->default_display = "std";
     m->no_floppy = 1;
+    m->no_tco = 0;
     m->alias = "q35";
 }
 
@@ -404,6 +405,7 @@ static void pc_q35_2_3_machine_options(MachineClass *m)
 {
     pc_q35_2_4_machine_options(m);
     m->no_floppy = 0;
+    m->no_tco = 1;
     m->alias = NULL;
     SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
 }
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index b3e0b1f..acf262c 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -313,6 +313,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
     return route;
 }
 
+void ich9_generate_smi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+}
+
+void ich9_generate_nmi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
+}
+
 static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
 {
     switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
@@ -357,11 +367,12 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
     }
 }
 
-void ich9_lpc_pm_init(PCIDevice *lpc_pci)
+void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool enable_tco)
 {
     ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
 
-    ich9_pm_init(lpc_pci, &lpc->pm, qemu_allocate_irq(ich9_set_sci, lpc, 0));
+    ich9_pm_init(lpc_pci, &lpc->pm, enable_tco,
+                 qemu_allocate_irq(ich9_set_sci, lpc, 0));
     ich9_lpc_reset(&lpc->d.qdev);
 }
 
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
index 77cc65c..a7eb421 100644
--- a/include/hw/acpi/ich9.h
+++ b/include/hw/acpi/ich9.h
@@ -25,6 +25,7 @@
 #include "hw/acpi/cpu_hotplug.h"
 #include "hw/acpi/memory_hotplug.h"
 #include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/tco.h"
 
 typedef struct ICH9LPCPMRegs {
     /*
@@ -37,6 +38,7 @@ typedef struct ICH9LPCPMRegs {
     MemoryRegion io;
     MemoryRegion io_gpe;
     MemoryRegion io_smi;
+    MemoryRegion io_tco;
 
     uint32_t smi_en;
     uint32_t smi_en_wmask;
@@ -54,9 +56,12 @@ typedef struct ICH9LPCPMRegs {
     uint8_t disable_s3;
     uint8_t disable_s4;
     uint8_t s4_val;
+    bool enable_tco;
+
+    TCOIORegs tco_regs;
 } ICH9LPCPMRegs;
 
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
                   qemu_irq sci_irq);
 void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base);
 extern const VMStateDescription vmstate_ich9_pm;
diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
new file mode 100644
index 0000000..477a32c
--- /dev/null
+++ b/include/hw/acpi/tco.h
@@ -0,0 +1,98 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef HW_ACPI_TCO_H
+#define HW_ACPI_TCO_H
+
+#include "qemu/typedefs.h"
+#include "qemu-common.h"
+
+/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
+#define TCO_TICK_NSEC 600000000LL
+
+/* TCO I/O register offsets */
+enum {
+    TCO_RLD           = 0x00,
+    TCO_DAT_IN        = 0x02,
+    TCO_DAT_OUT       = 0x03,
+    TCO1_STS          = 0x04,
+    TCO2_STS          = 0x06,
+    TCO1_CNT          = 0x08,
+    TCO2_CNT          = 0x0a,
+    TCO_MESSAGE1      = 0x0c,
+    TCO_MESSAGE2      = 0x0d,
+    TCO_WDCNT         = 0x0e,
+    SW_IRQ_GEN        = 0x10,
+    TCO_TMR           = 0x12,
+};
+
+/* TCO I/O register control/status bits */
+enum {
+    SW_TCO_SMI           = 1 << 1,
+    TCO_INT_STS          = 1 << 2,
+    TCO_LOCK             = 1 << 12,
+    TCO_TMR_HLT          = 1 << 11,
+    TCO_TIMEOUT          = 1 << 3,
+    TCO_SECOND_TO_STS    = 1 << 1,
+    TCO_BOOT_STS         = 1 << 2,
+};
+
+/* TCO I/O registers mask bits */
+enum {
+    TCO_RLD_MASK     = 0x3ff,
+    TCO1_STS_MASK    = 0xe870,
+    TCO2_STS_MASK    = 0xfff8,
+    TCO1_CNT_MASK    = 0xfeff,
+    TCO_TMR_MASK     = 0x3ff,
+};
+
+typedef struct TCOIORegs {
+    bool use_tco;
+    struct {
+        uint16_t rld;
+        uint8_t din;
+        uint8_t dout;
+        uint16_t sts1;
+        uint16_t sts2;
+        uint16_t cnt1;
+        uint16_t cnt2;
+        uint8_t msg1;
+        uint8_t msg2;
+        uint8_t wdcnt;
+        uint16_t tmr;
+    } tco;
+    uint8_t sw_irq_gen;
+
+    QEMUTimer *tco_timer;
+    int64_t expire_time;
+    uint8_t timeouts_no;
+
+    MemoryRegion io;
+} TCOIORegs;
+
+/* tco.c */
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent);
+
+extern const VMStateDescription vmstate_tco_io_sts;
+
+#endif /* HW_ACPI_TCO_H */
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 6379901..2aec9cb 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -99,7 +99,8 @@ struct MachineClass {
         no_floppy:1,
         no_cdrom:1,
         no_sdcard:1,
-        has_dynamic_sysbus:1;
+        has_dynamic_sysbus:1,
+        no_tco:1;
     int is_default;
     const char *default_machine_opts;
     const char *default_boot_order;
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index a2cc15c..80a5653 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -17,9 +17,12 @@
 void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
 int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx);
 PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
-void ich9_lpc_pm_init(PCIDevice *pci_lpc);
+void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool enable_tco);
 I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
 
+void ich9_generate_smi(void);
+void ich9_generate_nmi(void);
+
 #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
 
 #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
@@ -162,6 +165,8 @@ Object *ich9_lpc_find(void);
 #define ICH9_LPC_RCBA_BA_MASK                   Q35_MASK(32, 31, 14)
 #define ICH9_LPC_RCBA_EN                        0x1
 #define ICH9_LPC_RCBA_DEFAULT                   0x0
+#define ICH9_LPC_RCBA_GCS                       0x3410
+#define ICH9_LPC_RCBA_GCS_NO_REBOOT             (1 << 5)
 
 #define ICH9_LPC_PIC_NUM_PINS                   16
 #define ICH9_LPC_IOAPIC_NUM_PINS                24
@@ -186,7 +191,10 @@ Object *ich9_lpc_find(void);
 #define ICH9_PMIO_GPE0_LEN                      16
 #define ICH9_PMIO_SMI_EN                        0x30
 #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
+#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
 #define ICH9_PMIO_SMI_STS                       0x34
+#define ICH9_PMIO_TCO_RLD                       0x60
+#define ICH9_PMIO_TCO_LEN                       32
 
 /* FADT ACPI_ENABLE/ACPI_DISABLE */
 #define ICH9_APM_ACPI_ENABLE                    0x2
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 86c5651..c1afdc0 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -89,6 +89,7 @@ typedef struct PcPciInfo {
 #define ACPI_PM_PROP_PM_IO_BASE "pm_io_base"
 #define ACPI_PM_PROP_GPE0_BLK "gpe0_blk"
 #define ACPI_PM_PROP_GPE0_BLK_LEN "gpe0_blk_len"
+#define ACPI_PM_PROP_TCO_ENABLED "enable_tco"
 
 struct PcGuestInfo {
     bool isapc_ram_fw;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-22  0:37 ` [Qemu-devel] [PATCH v4 " Paulo Alcantara
@ 2015-06-22  0:37   ` Paulo Alcantara
  2015-06-22  8:40     ` Michael S. Tsirkin
  2015-06-23 10:38     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
  2015-06-22  0:37   ` [Qemu-devel] [PATCH v4 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
                     ` (3 subsequent siblings)
  4 siblings, 2 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22  0:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, mst

This block is mapped into memory space, using the Root Complex Base
Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this space
must be limited to 32-(DW) bit quantities. Burst accesses are not
allowed.

All Chipset Configuration Registers are located in this 16KiB space.

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * s/PDRC/CCR/ for clarity and match ICH9 spec
  * remove unnecessary OperationRegion for RCRB

v2 -> v3: (no changes)

v3 -> v4:
  * quote RCRB description from ICH9 spec to commit log
  * fix indentation issue in _CRS() method declaration
  * create hw/i386/ich9-cc.h for chipset configuration register values
    and use them in ASL
---
 hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
 include/hw/i386/ich9-cc.h     |  31 +++++++++++++++++++++++++++++++
 tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
 3 files changed, 47 insertions(+)
 create mode 100644 include/hw/i386/ich9-cc.h

diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
index 16eaca3..512c220 100644
--- a/hw/i386/q35-acpi-dsdt.dsl
+++ b/hw/i386/q35-acpi-dsdt.dsl
@@ -114,6 +114,22 @@ DefinitionBlock (
         }
     }
 
+#include "hw/i386/ich9-cc.h"
+
+/****************************************************************
+ * Chipset Configuration Registers
+ ****************************************************************/
+Scope(\_SB.PCI0) {
+    Device (CCR) {
+        Name (_HID, EISAID("PNP0C02"))
+        Name (_UID, 1)
+
+        Name (_CRS, ResourceTemplate() {
+            Memory32Fixed(ReadWrite, RCBA_BASE_ADDR, RCRB_SIZE)
+        })
+    }
+}
+
 #include "acpi-dsdt-hpet.dsl"
 
 
diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
new file mode 100644
index 0000000..675fb7f
--- /dev/null
+++ b/include/hw/i386/ich9-cc.h
@@ -0,0 +1,31 @@
+/*
+ * QEMU ICH9 Chipset Configuration Registers
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_ICH9_CC_H
+#define HW_ICH9_CC_H
+
+#define RCBA_BASE_ADDR    0xfed1c000
+#define RCRB_SIZE         0x00004000
+
+#endif /* HW_ICH9_CC_H */
diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49 100644
GIT binary patch
delta 81
zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w

delta 24
gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs

-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v4 3/3] tests: add testcase for TCO watchdog emulation
  2015-06-22  0:37 ` [Qemu-devel] [PATCH v4 " Paulo Alcantara
  2015-06-22  0:37   ` [Qemu-devel] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
@ 2015-06-22  0:37   ` Paulo Alcantara
  2015-06-22  8:39   ` [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation Michael S. Tsirkin
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22  0:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, mst

This patch adds a testcase that covers the following:
  1) TCO default values
  2) first and second TCO timeout
  3) watch and validate ticks counter through TCO_RLD register
  4) maximum supported TCO timeout (0x3ff)
  5) watchdog actions (pause/reset/shutdown/none) upon second TCO
     timeout
  6) set and get of TCO control and status bits

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * some cleanup
  * add test for TCO_LOCK bit

v2 -> v3:
  * add tests for TCO control & status bits
  * fix check of SECOND_TO_STS bit (it's set in TCO2_STS reg)

v3 -> v4:
  * add more description to commit log
  * use RCBA_BASE_ADDR macro defintion from hw/i386/ich9-cc.h instead
---
 tests/Makefile   |   2 +
 tests/tco-test.c | 475 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 477 insertions(+)
 create mode 100644 tests/tco-test.c

diff --git a/tests/Makefile b/tests/Makefile
index 4de40de..2b26ae7 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -150,6 +150,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
 check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
+check-qtest-i386-y += tests/tco-test$(EXESUF)
 gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
 check-qtest-i386-y += $(check-qtest-pci-y)
 gcov-files-i386-y += $(gcov-files-pci-y)
@@ -367,6 +368,7 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
 tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
 tests/ne2000-test$(EXESUF): tests/ne2000-test.o
 tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
+tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
 tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
 tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
 tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
diff --git a/tests/tco-test.c b/tests/tco-test.c
new file mode 100644
index 0000000..e48dfe2
--- /dev/null
+++ b/tests/tco-test.c
@@ -0,0 +1,475 @@
+/*
+ * QEMU ICH9 TCO emulation tests
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/i386/ich9.h"
+#include "hw/i386/ich9-cc.h"
+#include "hw/acpi/ich9.h"
+#include "hw/acpi/tco.h"
+
+#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;
+    QPCIDevice *dev;
+    void *lpc_base;
+    void *tco_io_base;
+} TestData;
+
+static void test_init(TestData *d)
+{
+    QPCIBus *bus;
+    QTestState *qs;
+    char *s;
+
+    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
+    qs = qtest_start(s);
+    qtest_irq_intercept_in(qs, "ioapic");
+    g_free(s);
+
+    bus = qpci_init_pc();
+    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
+    g_assert(d->dev != NULL);
+
+    /* map PCI-to-LPC bridge interface BAR */
+    d->lpc_base = qpci_iomap(d->dev, 0, NULL);
+
+    qpci_device_enable(d->dev);
+
+    g_assert(d->lpc_base != NULL);
+
+    /* set ACPI PM I/O space base address */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_PMBASE,
+                       PM_IO_BASE_ADDR | 0x1);
+    /* enable ACPI I/O */
+    qpci_config_writeb(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_ACPI_CTRL,
+                       0x80);
+    /* set Root Complex BAR */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_RCBA,
+                       RCBA_BASE_ADDR | 0x1);
+
+    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
+}
+
+static void stop_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val |= TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void start_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val &= ~TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void load_tco(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
+}
+
+static void set_tco_timeout(const TestData *d, uint16_t ticks)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
+}
+
+static void clear_tco_status(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
+}
+
+static void reset_on_second_timeout(bool enable)
+{
+    uint32_t val;
+
+    val = readl(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS);
+    if (enable) {
+        val &= ~ICH9_LPC_RCBA_GCS_NO_REBOOT;
+    } else {
+        val |= ICH9_LPC_RCBA_GCS_NO_REBOOT;
+    }
+    writel(RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS, val);
+}
+
+static void test_tco_defaults(void)
+{
+    TestData d;
+
+    d.args = NULL;
+    test_init(&d);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+                    TCO_RLD_DEFAULT);
+    /* TCO_DAT_IN & TCO_DAT_OUT */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
+                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
+    /* TCO1_STS & TCO2_STS */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
+                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
+    /* TCO1_CNT & TCO2_CNT */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
+    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
+                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
+                    TCO_WDCNT_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
+                    SW_IRQ_GEN_DEFAULT);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
+                    TCO_TMR_DEFAULT);
+    qtest_end();
+}
+
+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;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    /* test first timeout */
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    /* test clearing timeout bit */
+    val |= TCO_TIMEOUT;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+
+    /* test second timeout */
+    clock_step(ticks * TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco_max_timeout(void)
+{
+    TestData d;
+    const uint16_t ticks = 0xffff;
+    uint32_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
+    g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+    clock_step(TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static QDict *get_watchdog_action(void)
+{
+    QDict *ev = qmp("");
+    QDict *data;
+    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
+
+    data = qdict_get_qdict(ev, "data");
+    QINCREF(data);
+    QDECREF(ev);
+    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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_ticks_counter(void)
+{
+    TestData d;
+    uint16_t ticks = TCO_SECS_TO_TICKS(8);
+    uint16_t rld;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+
+    do {
+        rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
+        g_assert_cmpint(rld, ==, ticks);
+        clock_step(TCO_TICK_NSEC);
+        ticks--;
+    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco1_control_bits(void)
+{
+    TestData d;
+    uint16_t val;
+
+    d.args = NULL;
+    test_init(&d);
+
+    val = TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    val &= ~TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    TCO_LOCK);
+    qtest_end();
+}
+
+static void test_tco1_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0);
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0);
+    qtest_end();
+}
+
+static void test_tco2_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = "-watchdog-action none";
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0);
+    qtest_end();
+}
+
+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();
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22  0:37 ` [Qemu-devel] [PATCH v4 " Paulo Alcantara
  2015-06-22  0:37   ` [Qemu-devel] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
  2015-06-22  0:37   ` [Qemu-devel] [PATCH v4 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
@ 2015-06-22  8:39   ` Michael S. Tsirkin
  2015-06-22 12:30     ` Paulo Alcantara
  2015-06-22  8:43   ` Michael S. Tsirkin
  2015-06-22 18:29   ` Paulo Alcantara
  4 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-22  8:39 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Sun, Jun 21, 2015 at 09:37:01PM -0300, Paulo Alcantara wrote:
> diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> new file mode 100644
> index 0000000..b6af1d9
> --- /dev/null
> +++ b/hw/acpi/tco.c
> @@ -0,0 +1,279 @@
> +/*
> + * QEMU ICH9 TCO emulation
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.

Please make new original code GPLv2+. If you have copied from another
file, then you should follow that file's licensing, but in that case
you should also acknowledge the original copyright.


> + */

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-22  0:37   ` [Qemu-devel] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
@ 2015-06-22  8:40     ` Michael S. Tsirkin
  2015-06-22 12:53       ` Paulo Alcantara
  2015-06-23 10:38     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
  1 sibling, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-22  8:40 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Sun, Jun 21, 2015 at 09:37:02PM -0300, Paulo Alcantara wrote:
> This block is mapped into memory space, using the Root Complex Base
> Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this space
> must be limited to 32-(DW) bit quantities. Burst accesses are not
> allowed.
> 
> All Chipset Configuration Registers are located in this 16KiB space.
> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>

Just to make sure, the device is in fact only functional
if it's enabled using the property, correct?
In that case please add it conditionally to the SSDT instead.

> ---
> v1 -> v2:
>   * s/PDRC/CCR/ for clarity and match ICH9 spec
>   * remove unnecessary OperationRegion for RCRB
> 
> v2 -> v3: (no changes)
> 
> v3 -> v4:
>   * quote RCRB description from ICH9 spec to commit log
>   * fix indentation issue in _CRS() method declaration
>   * create hw/i386/ich9-cc.h for chipset configuration register values
>     and use them in ASL
> ---
>  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
>  include/hw/i386/ich9-cc.h     |  31 +++++++++++++++++++++++++++++++
>  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
>  3 files changed, 47 insertions(+)
>  create mode 100644 include/hw/i386/ich9-cc.h
> 
> diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> index 16eaca3..512c220 100644
> --- a/hw/i386/q35-acpi-dsdt.dsl
> +++ b/hw/i386/q35-acpi-dsdt.dsl
> @@ -114,6 +114,22 @@ DefinitionBlock (
>          }
>      }
>  
> +#include "hw/i386/ich9-cc.h"
> +
> +/****************************************************************
> + * Chipset Configuration Registers
> + ****************************************************************/
> +Scope(\_SB.PCI0) {
> +    Device (CCR) {
> +        Name (_HID, EISAID("PNP0C02"))
> +        Name (_UID, 1)
> +
> +        Name (_CRS, ResourceTemplate() {
> +            Memory32Fixed(ReadWrite, RCBA_BASE_ADDR, RCRB_SIZE)
> +        })
> +    }
> +}
> +
>  #include "acpi-dsdt-hpet.dsl"
>  
>  
> diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> new file mode 100644
> index 0000000..675fb7f
> --- /dev/null
> +++ b/include/hw/i386/ich9-cc.h
> @@ -0,0 +1,31 @@
> +/*
> + * QEMU ICH9 Chipset Configuration Registers
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#ifndef HW_ICH9_CC_H
> +#define HW_ICH9_CC_H
> +
> +#define RCBA_BASE_ADDR    0xfed1c000
> +#define RCRB_SIZE         0x00004000
> +
> +#endif /* HW_ICH9_CC_H */
> diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
> index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49 100644
> GIT binary patch
> delta 81
> zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> 
> delta 24
> gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> 
> -- 
> 2.1.0

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22  0:37 ` [Qemu-devel] [PATCH v4 " Paulo Alcantara
                     ` (2 preceding siblings ...)
  2015-06-22  8:39   ` [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation Michael S. Tsirkin
@ 2015-06-22  8:43   ` Michael S. Tsirkin
  2015-06-22  9:45     ` Paolo Bonzini
  2015-06-22 18:29   ` Paulo Alcantara
  4 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-22  8:43 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Sun, Jun 21, 2015 at 09:37:01PM -0300, Paulo Alcantara wrote:
> This interface provides some registers within a 32-byte range and can be
> acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).
> 
> It's commonly used as a watchdog timer to detect system lockups through
> SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
> NO_REBOOT bit is not set in GCS (General Control and Status register),
> the system will be resetted upon second timeout if TCO_RLD register
> wasn't previously written to prevent timeout.
> 
> This patch adds support to TCO watchdog logic and few other features
> like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
> etc. are not implemented yet.
> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>

Given that support is known to be partial, would it make sense
to keep it disabled by default for 2.4?
This way in 2.5 we won't need to add more flags to stay bug compatible.

> ---
> v1 -> v2:
>   * add migration support for TCO I/O device state
>   * wake up only when total time expired instead of every 0.6s
>   * some cleanup suggested by Paolo Bonzini
> 
> v2 -> v3:
>   * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
>   * improve handling of TCO_LOCK bit in TCO1_CNT register
> 
> v3 -> v4:
>   * fix some conflicts in hw/acpi/ich9.c after rebasing against master
>   * remove meaningless "use_tco" field from TCOIORegs structure
>   * add a object property named "enable_tco" and only enable TCO support
>     on pc-q35-2.4 and later
> ---
>  hw/acpi/Makefile.objs  |   2 +-
>  hw/acpi/ich9.c         |  55 +++++++++-
>  hw/acpi/tco.c          | 279 +++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/i386/pc_q35.c       |   4 +-
>  hw/isa/lpc_ich9.c      |  15 ++-
>  include/hw/acpi/ich9.h |   7 +-
>  include/hw/acpi/tco.h  |  98 +++++++++++++++++
>  include/hw/boards.h    |   3 +-
>  include/hw/i386/ich9.h |  10 +-
>  include/hw/i386/pc.h   |   1 +
>  10 files changed, 466 insertions(+), 8 deletions(-)
>  create mode 100644 hw/acpi/tco.c
>  create mode 100644 include/hw/acpi/tco.h
> 
> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> index 29d46d8..3db1f07 100644
> --- a/hw/acpi/Makefile.objs
> +++ b/hw/acpi/Makefile.objs
> @@ -1,4 +1,4 @@
> -common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
> +common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o tco.o
>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
>  common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
>  common-obj-$(CONFIG_ACPI) += acpi_interface.o
> diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
> index 8a64ffb..d3d9953 100644
> --- a/hw/acpi/ich9.c
> +++ b/hw/acpi/ich9.c
> @@ -30,6 +30,7 @@
>  #include "qemu/timer.h"
>  #include "sysemu/sysemu.h"
>  #include "hw/acpi/acpi.h"
> +#include "hw/acpi/tco.h"
>  #include "sysemu/kvm.h"
>  #include "exec/address-spaces.h"
>  
> @@ -92,8 +93,16 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
>                              unsigned width)
>  {
>      ICH9LPCPMRegs *pm = opaque;
> +    TCOIORegs *tr = &pm->tco_regs;
> +    uint64_t tco_en;
> +
>      switch (addr) {
>      case 0:
> +        tco_en = pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
> +        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
> +        if (tr->tco.cnt1 & TCO_LOCK) {
> +            val = (val & ~ICH9_PMIO_SMI_EN_TCO_EN) | tco_en;
> +        }
>          pm->smi_en &= ~pm->smi_en_wmask;
>          pm->smi_en |= (val & pm->smi_en_wmask);
>          break;
> @@ -159,6 +168,25 @@ static const VMStateDescription vmstate_memhp_state = {
>      }
>  };
>  
> +static bool vmstate_test_use_tco(void *opaque)
> +{
> +    ICH9LPCPMRegs *s = opaque;
> +    return s->enable_tco;
> +}
> +
> +static const VMStateDescription vmstate_tco_io_state = {
> +    .name = "ich9_pm/tco",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .needed = vmstate_test_use_tco,
> +    .fields      = (VMStateField[]) {
> +        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
> +                       TCOIORegs),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
>  const VMStateDescription vmstate_ich9_pm = {
>      .name = "ich9_pm",
>      .version_id = 1,
> @@ -179,6 +207,10 @@ const VMStateDescription vmstate_ich9_pm = {
>      .subsections = (const VMStateDescription*[]) {
>          &vmstate_memhp_state,
>          NULL
> +    },
> +    .subsections = (const VMStateDescription*[]) {
> +        &vmstate_tco_io_state,
> +        NULL
>      }
>  };
>  
> @@ -209,7 +241,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
>      acpi_pm1_evt_power_down(&pm->acpi_regs);
>  }
>  
> -void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
> +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
>                    qemu_irq sci_irq)
>  {
>      memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
> @@ -231,6 +263,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
>                            "acpi-smi", 8);
>      memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
>  
> +    pm->enable_tco = enable_tco;
> +    if (pm->enable_tco) {
> +        acpi_pm_tco_init(&pm->tco_regs, &pm->io);
> +    }
> +
>      pm->irq = sci_irq;
>      qemu_register_reset(pm_reset, pm);
>      pm->powerdown_notifier.notify = pm_powerdown_req;
> @@ -351,6 +388,18 @@ out:
>      error_propagate(errp, local_err);
>  }
>  
> +static bool ich9_pm_get_enable_tco(Object *obj, Error **errp)
> +{
> +    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
> +    return s->pm.enable_tco;
> +}
> +
> +static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp)
> +{
> +    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
> +    s->pm.enable_tco = value;
> +}
> +
>  void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
>  {
>      static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
> @@ -382,6 +431,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
>                          ich9_pm_get_s4_val,
>                          ich9_pm_set_s4_val,
>                          NULL, pm, NULL);
> +    object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
> +                             ich9_pm_get_enable_tco,
> +                             ich9_pm_set_enable_tco,
> +                             NULL);
>  }
>  
>  void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp)
> diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> new file mode 100644
> index 0000000..b6af1d9
> --- /dev/null
> +++ b/hw/acpi/tco.c
> @@ -0,0 +1,279 @@
> +/*
> + * QEMU ICH9 TCO emulation
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include "qemu-common.h"
> +#include "sysemu/watchdog.h"
> +#include "hw/i386/ich9.h"
> +
> +#include "hw/acpi/tco.h"
> +
> +//#define DEBUG
> +
> +#ifdef DEBUG
> +#define TCO_DEBUG(fmt, ...)                                     \
> +    do {                                                        \
> +        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
> +    } while (0)
> +#else
> +#define TCO_DEBUG(fmt, ...) do { } while (0)
> +#endif
> +
> +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,
> +};
> +
> +static inline void tco_timer_reload(TCOIORegs *tr)
> +{
> +    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> +        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
> +    timer_mod(tr->tco_timer, tr->expire_time);
> +}
> +
> +static inline void tco_timer_stop(TCOIORegs *tr)
> +{
> +    tr->expire_time = -1;
> +}
> +
> +static void tco_timer_expired(void *opaque)
> +{
> +    TCOIORegs *tr = opaque;
> +    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
> +    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
> +    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_LPC_RCBA_GCS);
> +
> +    tr->tco.rld = 0;
> +    tr->tco.sts1 |= TCO_TIMEOUT;
> +    if (++tr->timeouts_no == 2) {
> +        tr->tco.sts2 |= TCO_SECOND_TO_STS;
> +        tr->tco.sts2 |= TCO_BOOT_STS;
> +        tr->timeouts_no = 0;
> +
> +        if (!(gcs & ICH9_LPC_RCBA_GCS_NO_REBOOT)) {
> +            watchdog_perform_action();
> +            tco_timer_stop(tr);
> +            return;
> +        }
> +    }
> +
> +    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
> +        ich9_generate_smi();
> +    } else {
> +        ich9_generate_nmi();
> +    }
> +    tr->tco.rld = tr->tco.tmr;
> +    tco_timer_reload(tr);
> +}
> +
> +/* NOTE: values of 0 or 1 will be ignored by ICH */
> +static inline int can_start_tco_timer(TCOIORegs *tr)
> +{
> +    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
> +}
> +
> +static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
> +{
> +    uint16_t rld;
> +
> +    switch (addr) {
> +    case TCO_RLD:
> +        if (tr->expire_time != -1) {
> +            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
> +            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
> +        } else {
> +            rld = tr->tco.rld;
> +        }
> +        return rld;
> +    case TCO_DAT_IN:
> +        return tr->tco.din;
> +    case TCO_DAT_OUT:
> +        return tr->tco.dout;
> +    case TCO1_STS:
> +        return tr->tco.sts1;
> +    case TCO2_STS:
> +        return tr->tco.sts2;
> +    case TCO1_CNT:
> +        return tr->tco.cnt1;
> +    case TCO2_CNT:
> +        return tr->tco.cnt2;
> +    case TCO_MESSAGE1:
> +        return tr->tco.msg1;
> +    case TCO_MESSAGE2:
> +        return tr->tco.msg2;
> +    case TCO_WDCNT:
> +        return tr->tco.wdcnt;
> +    case TCO_TMR:
> +        return tr->tco.tmr;
> +    case SW_IRQ_GEN:
> +        return tr->sw_irq_gen;
> +    }
> +    return 0;
> +}
> +
> +static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
> +{
> +    switch (addr) {
> +    case TCO_RLD:
> +        tr->timeouts_no = 0;
> +        if (can_start_tco_timer(tr)) {
> +            tr->tco.rld = tr->tco.tmr;
> +            tco_timer_reload(tr);
> +        } else {
> +            tr->tco.rld = val;
> +        }
> +        break;
> +    case TCO_DAT_IN:
> +        tr->tco.din = val;
> +        tr->tco.sts1 |= SW_TCO_SMI;
> +        ich9_generate_smi();
> +        break;
> +    case TCO_DAT_OUT:
> +        tr->tco.dout = val;
> +        tr->tco.sts1 |= TCO_INT_STS;
> +        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
> +        break;
> +    case TCO1_STS:
> +        tr->tco.sts1 = val & TCO1_STS_MASK;
> +        break;
> +    case TCO2_STS:
> +        tr->tco.sts2 = val & TCO2_STS_MASK;
> +        break;
> +    case TCO1_CNT:
> +        val &= TCO1_CNT_MASK;
> +        /*
> +         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
> +         * is required to change this bit from 1 to 0 -- it defaults to 0.
> +         */
> +        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
> +        if (can_start_tco_timer(tr)) {
> +            tr->tco.rld = tr->tco.tmr;
> +            tco_timer_reload(tr);
> +        } else {
> +            tco_timer_stop(tr);
> +        }
> +        break;
> +    case TCO2_CNT:
> +        tr->tco.cnt2 = val;
> +        break;
> +    case TCO_MESSAGE1:
> +        tr->tco.msg1 = val;
> +        break;
> +    case TCO_MESSAGE2:
> +        tr->tco.msg2 = val;
> +        break;
> +    case TCO_WDCNT:
> +        tr->tco.wdcnt = val;
> +        break;
> +    case TCO_TMR:
> +        tr->tco.tmr = val;
> +        break;
> +    case SW_IRQ_GEN:
> +        tr->sw_irq_gen = val;
> +        break;
> +    }
> +}
> +
> +static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width)
> +{
> +    TCOIORegs *tr = opaque;
> +    return tco_ioport_readw(tr, addr);
> +}
> +
> +static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val,
> +                          unsigned width)
> +{
> +    TCOIORegs *tr = opaque;
> +    tco_ioport_writew(tr, addr, val);
> +}
> +
> +static const MemoryRegionOps tco_io_ops = {
> +    .read = tco_io_readw,
> +    .write = tco_io_writew,
> +    .valid.min_access_size = 1,
> +    .valid.max_access_size = 4,
> +    .impl.min_access_size = 1,
> +    .impl.max_access_size = 2,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent)
> +{
> +    *tr = (TCOIORegs) {
> +        .tco = {
> +            .rld      = TCO_RLD_DEFAULT,
> +            .din      = TCO_DAT_IN_DEFAULT,
> +            .dout     = TCO_DAT_OUT_DEFAULT,
> +            .sts1     = TCO1_STS_DEFAULT,
> +            .sts2     = TCO2_STS_DEFAULT,
> +            .cnt1     = TCO1_CNT_DEFAULT,
> +            .cnt2     = TCO2_CNT_DEFAULT,
> +            .msg1     = TCO_MESSAGE1_DEFAULT,
> +            .msg2     = TCO_MESSAGE2_DEFAULT,
> +            .wdcnt    = TCO_WDCNT_DEFAULT,
> +            .tmr      = TCO_TMR_DEFAULT,
> +        },
> +        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
> +        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
> +        .expire_time   = -1,
> +        .timeouts_no   = 0,
> +    };
> +    memory_region_init_io(&tr->io, memory_region_owner(parent),
> +                          &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN);
> +    memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io);
> +}
> +
> +const VMStateDescription vmstate_tco_io_sts = {
> +    .name = "tco io device status",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields      = (VMStateField[]) {
> +        VMSTATE_UINT16(tco.rld, TCOIORegs),
> +        VMSTATE_UINT8(tco.din, TCOIORegs),
> +        VMSTATE_UINT8(tco.dout, TCOIORegs),
> +        VMSTATE_UINT16(tco.sts1, TCOIORegs),
> +        VMSTATE_UINT16(tco.sts2, TCOIORegs),
> +        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
> +        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
> +        VMSTATE_UINT8(tco.msg1, TCOIORegs),
> +        VMSTATE_UINT8(tco.msg2, TCOIORegs),
> +        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
> +        VMSTATE_UINT16(tco.tmr, TCOIORegs),
> +        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
> +        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
> +        VMSTATE_INT64(expire_time, TCOIORegs),
> +        VMSTATE_UINT8(timeouts_no, TCOIORegs),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
> index 082cd93..7e35a36 100644
> --- a/hw/i386/pc_q35.c
> +++ b/hw/i386/pc_q35.c
> @@ -253,7 +253,7 @@ static void pc_q35_init(MachineState *machine)
>                           (pc_machine->vmport != ON_OFF_AUTO_ON), 0xff0104);
>  
>      /* connect pm stuff to lpc */
> -    ich9_lpc_pm_init(lpc);
> +    ich9_lpc_pm_init(lpc, !mc->no_tco);
>  
>      /* ahci and SATA device, for q35 1 ahci controller is built-in */
>      ahci = pci_create_simple_multifunction(host_bus,
> @@ -393,6 +393,7 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
>      m->default_machine_opts = "firmware=bios-256k.bin";
>      m->default_display = "std";
>      m->no_floppy = 1;
> +    m->no_tco = 0;
>      m->alias = "q35";
>  }
>  
> @@ -404,6 +405,7 @@ static void pc_q35_2_3_machine_options(MachineClass *m)
>  {
>      pc_q35_2_4_machine_options(m);
>      m->no_floppy = 0;
> +    m->no_tco = 1;
>      m->alias = NULL;
>      SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
>  }
> diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> index b3e0b1f..acf262c 100644
> --- a/hw/isa/lpc_ich9.c
> +++ b/hw/isa/lpc_ich9.c
> @@ -313,6 +313,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
>      return route;
>  }
>  
> +void ich9_generate_smi(void)
> +{
> +    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
> +}
> +
> +void ich9_generate_nmi(void)
> +{
> +    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
> +}
> +
>  static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
>  {
>      switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
> @@ -357,11 +367,12 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
>      }
>  }
>  
> -void ich9_lpc_pm_init(PCIDevice *lpc_pci)
> +void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool enable_tco)
>  {
>      ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
>  
> -    ich9_pm_init(lpc_pci, &lpc->pm, qemu_allocate_irq(ich9_set_sci, lpc, 0));
> +    ich9_pm_init(lpc_pci, &lpc->pm, enable_tco,
> +                 qemu_allocate_irq(ich9_set_sci, lpc, 0));
>      ich9_lpc_reset(&lpc->d.qdev);
>  }
>  
> diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
> index 77cc65c..a7eb421 100644
> --- a/include/hw/acpi/ich9.h
> +++ b/include/hw/acpi/ich9.h
> @@ -25,6 +25,7 @@
>  #include "hw/acpi/cpu_hotplug.h"
>  #include "hw/acpi/memory_hotplug.h"
>  #include "hw/acpi/acpi_dev_interface.h"
> +#include "hw/acpi/tco.h"
>  
>  typedef struct ICH9LPCPMRegs {
>      /*
> @@ -37,6 +38,7 @@ typedef struct ICH9LPCPMRegs {
>      MemoryRegion io;
>      MemoryRegion io_gpe;
>      MemoryRegion io_smi;
> +    MemoryRegion io_tco;
>  
>      uint32_t smi_en;
>      uint32_t smi_en_wmask;
> @@ -54,9 +56,12 @@ typedef struct ICH9LPCPMRegs {
>      uint8_t disable_s3;
>      uint8_t disable_s4;
>      uint8_t s4_val;
> +    bool enable_tco;
> +
> +    TCOIORegs tco_regs;
>  } ICH9LPCPMRegs;
>  
> -void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
> +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
>                    qemu_irq sci_irq);
>  void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base);
>  extern const VMStateDescription vmstate_ich9_pm;
> diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
> new file mode 100644
> index 0000000..477a32c
> --- /dev/null
> +++ b/include/hw/acpi/tco.h
> @@ -0,0 +1,98 @@
> +/*
> + * QEMU ICH9 TCO emulation
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#ifndef HW_ACPI_TCO_H
> +#define HW_ACPI_TCO_H
> +
> +#include "qemu/typedefs.h"
> +#include "qemu-common.h"
> +
> +/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
> +#define TCO_TICK_NSEC 600000000LL
> +
> +/* TCO I/O register offsets */
> +enum {
> +    TCO_RLD           = 0x00,
> +    TCO_DAT_IN        = 0x02,
> +    TCO_DAT_OUT       = 0x03,
> +    TCO1_STS          = 0x04,
> +    TCO2_STS          = 0x06,
> +    TCO1_CNT          = 0x08,
> +    TCO2_CNT          = 0x0a,
> +    TCO_MESSAGE1      = 0x0c,
> +    TCO_MESSAGE2      = 0x0d,
> +    TCO_WDCNT         = 0x0e,
> +    SW_IRQ_GEN        = 0x10,
> +    TCO_TMR           = 0x12,
> +};
> +
> +/* TCO I/O register control/status bits */
> +enum {
> +    SW_TCO_SMI           = 1 << 1,
> +    TCO_INT_STS          = 1 << 2,
> +    TCO_LOCK             = 1 << 12,
> +    TCO_TMR_HLT          = 1 << 11,
> +    TCO_TIMEOUT          = 1 << 3,
> +    TCO_SECOND_TO_STS    = 1 << 1,
> +    TCO_BOOT_STS         = 1 << 2,
> +};
> +
> +/* TCO I/O registers mask bits */
> +enum {
> +    TCO_RLD_MASK     = 0x3ff,
> +    TCO1_STS_MASK    = 0xe870,
> +    TCO2_STS_MASK    = 0xfff8,
> +    TCO1_CNT_MASK    = 0xfeff,
> +    TCO_TMR_MASK     = 0x3ff,
> +};
> +
> +typedef struct TCOIORegs {
> +    bool use_tco;
> +    struct {
> +        uint16_t rld;
> +        uint8_t din;
> +        uint8_t dout;
> +        uint16_t sts1;
> +        uint16_t sts2;
> +        uint16_t cnt1;
> +        uint16_t cnt2;
> +        uint8_t msg1;
> +        uint8_t msg2;
> +        uint8_t wdcnt;
> +        uint16_t tmr;
> +    } tco;
> +    uint8_t sw_irq_gen;
> +
> +    QEMUTimer *tco_timer;
> +    int64_t expire_time;
> +    uint8_t timeouts_no;
> +
> +    MemoryRegion io;
> +} TCOIORegs;
> +
> +/* tco.c */
> +void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent);
> +
> +extern const VMStateDescription vmstate_tco_io_sts;
> +
> +#endif /* HW_ACPI_TCO_H */
> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index 6379901..2aec9cb 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -99,7 +99,8 @@ struct MachineClass {
>          no_floppy:1,
>          no_cdrom:1,
>          no_sdcard:1,
> -        has_dynamic_sysbus:1;
> +        has_dynamic_sysbus:1,
> +        no_tco:1;
>      int is_default;
>      const char *default_machine_opts;
>      const char *default_boot_order;
> diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
> index a2cc15c..80a5653 100644
> --- a/include/hw/i386/ich9.h
> +++ b/include/hw/i386/ich9.h
> @@ -17,9 +17,12 @@
>  void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
>  int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx);
>  PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
> -void ich9_lpc_pm_init(PCIDevice *pci_lpc);
> +void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool enable_tco);
>  I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
>  
> +void ich9_generate_smi(void);
> +void ich9_generate_nmi(void);
> +
>  #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
>  
>  #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
> @@ -162,6 +165,8 @@ Object *ich9_lpc_find(void);
>  #define ICH9_LPC_RCBA_BA_MASK                   Q35_MASK(32, 31, 14)
>  #define ICH9_LPC_RCBA_EN                        0x1
>  #define ICH9_LPC_RCBA_DEFAULT                   0x0
> +#define ICH9_LPC_RCBA_GCS                       0x3410
> +#define ICH9_LPC_RCBA_GCS_NO_REBOOT             (1 << 5)
>  
>  #define ICH9_LPC_PIC_NUM_PINS                   16
>  #define ICH9_LPC_IOAPIC_NUM_PINS                24
> @@ -186,7 +191,10 @@ Object *ich9_lpc_find(void);
>  #define ICH9_PMIO_GPE0_LEN                      16
>  #define ICH9_PMIO_SMI_EN                        0x30
>  #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
> +#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
>  #define ICH9_PMIO_SMI_STS                       0x34
> +#define ICH9_PMIO_TCO_RLD                       0x60
> +#define ICH9_PMIO_TCO_LEN                       32
>  
>  /* FADT ACPI_ENABLE/ACPI_DISABLE */
>  #define ICH9_APM_ACPI_ENABLE                    0x2
> diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> index 86c5651..c1afdc0 100644
> --- a/include/hw/i386/pc.h
> +++ b/include/hw/i386/pc.h
> @@ -89,6 +89,7 @@ typedef struct PcPciInfo {
>  #define ACPI_PM_PROP_PM_IO_BASE "pm_io_base"
>  #define ACPI_PM_PROP_GPE0_BLK "gpe0_blk"
>  #define ACPI_PM_PROP_GPE0_BLK_LEN "gpe0_blk_len"
> +#define ACPI_PM_PROP_TCO_ENABLED "enable_tco"
>  
>  struct PcGuestInfo {
>      bool isapc_ram_fw;
> -- 
> 2.1.0

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22  8:43   ` Michael S. Tsirkin
@ 2015-06-22  9:45     ` Paolo Bonzini
  2015-06-22 12:11       ` Michael S. Tsirkin
  0 siblings, 1 reply; 83+ messages in thread
From: Paolo Bonzini @ 2015-06-22  9:45 UTC (permalink / raw)
  To: Michael S. Tsirkin, Paulo Alcantara; +Cc: seabios, Paulo Alcantara, qemu-devel



On 22/06/2015 10:43, Michael S. Tsirkin wrote:
> Given that support is known to be partial, would it make sense
> to keep it disabled by default for 2.4?

What is partial about it?  In fact, considering that q35 behavior is
still experimental it makes no sense to even make it conditional.  We
discussed this on IRC and I was hoping to hear you reply "sorry, I was
wrong".  Instead, I get this.

Michael, I'm seriously getting annoyed by this behavior.  Stop scaring
away contributors.

Paolo

> This way in 2.5 we won't need to add more flags to stay bug compatible.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22  9:45     ` Paolo Bonzini
@ 2015-06-22 12:11       ` Michael S. Tsirkin
  2015-06-22 12:36         ` Paulo Alcantara
  0 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-22 12:11 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: Paulo Alcantara, seabios, Paulo Alcantara, qemu-devel

On Mon, Jun 22, 2015 at 11:45:31AM +0200, Paolo Bonzini wrote:
> 
> 
> On 22/06/2015 10:43, Michael S. Tsirkin wrote:
> > Given that support is known to be partial, would it make sense
> > to keep it disabled by default for 2.4?
> 
> What is partial about it?

Ow, looks like I didn't send out the response to the patch itself.
Will do.

>  In fact, considering that q35 behavior is
> still experimental it makes no sense to even make it conditional.

I agree to this, though an option to disable seems useful for debugging,
so I'm glad that Paulo implemented it.  It's probably not strictly
required to disable for old machine types, but why not.

> We
> discussed this on IRC and I was hoping to hear you reply "sorry, I was
> wrong".  Instead, I get this.
> 
> Michael, I'm seriously getting annoyed by this behavior.  Stop scaring
> away contributors.
> 
> Paolo

Doing my best here, but I do think we need to be careful about merging
things at this stage to avoid delaying the release.

> > This way in 2.5 we won't need to add more flags to stay bug compatible.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22  8:39   ` [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation Michael S. Tsirkin
@ 2015-06-22 12:30     ` Paulo Alcantara
  2015-06-22 12:32       ` Paolo Bonzini
  0 siblings, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22 12:30 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Paulo Alcantara, pbonzini, seabios, Paulo Alcantara, qemu-devel


On Mon, June 22, 2015 5:39 am, Michael S. Tsirkin wrote:
> On Sun, Jun 21, 2015 at 09:37:01PM -0300, Paulo Alcantara wrote:
>> diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
>> new file mode 100644
>> index 0000000..b6af1d9
>> --- /dev/null
>> +++ b/hw/acpi/tco.c
>> @@ -0,0 +1,279 @@
>> +/*
>> + * QEMU ICH9 TCO emulation
>> + *
>> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
>> + *
>> + * Permission is hereby granted, free of charge, to any person
>> obtaining a copy
>> + * of this software and associated documentation files (the
>> "Software"), to deal
>> + * in the Software without restriction, including without limitation
>> the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
>> sell
>> + * copies of the Software, and to permit persons to whom the Software
>> is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be
>> included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>> EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>> MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>> SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
>> OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>> ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>> DEALINGS IN
>> + * THE SOFTWARE.
>
> Please make new original code GPLv2+. If you have copied from another
> file, then you should follow that file's licensing, but in that case
> you should also acknowledge the original copyright.

OK.

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22 12:30     ` Paulo Alcantara
@ 2015-06-22 12:32       ` Paolo Bonzini
  2015-06-22 12:47         ` Michael S. Tsirkin
  0 siblings, 1 reply; 83+ messages in thread
From: Paolo Bonzini @ 2015-06-22 12:32 UTC (permalink / raw)
  To: Paulo Alcantara, Michael S. Tsirkin; +Cc: seabios, Paulo Alcantara, qemu-devel



On 22/06/2015 14:30, Paulo Alcantara wrote:
>>> >> +/*
>>> >> + * QEMU ICH9 TCO emulation
>>> >> + *
>>> >> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
>>> >> + *
>>> >> + * Permission is hereby granted, free of charge, to any person
>>> >> obtaining a copy
>>> >> + * of this software and associated documentation files (the
>>> >> "Software"), to deal
>>> >> + * in the Software without restriction, including without limitation
>>> >> the rights
>>> >> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
>>> >> sell
>>> >> + * copies of the Software, and to permit persons to whom the Software
>>> >> is
>>> >> + * furnished to do so, subject to the following conditions:
>>> >> + *
>>> >> + * The above copyright notice and this permission notice shall be
>>> >> included in
>>> >> + * all copies or substantial portions of the Software.
>>> >> + *
>>> >> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>> >> EXPRESS OR
>>> >> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>> >> MERCHANTABILITY,
>>> >> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>>> >> SHALL
>>> >> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
>>> >> OTHER
>>> >> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>>> >> ARISING FROM,
>>> >> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>>> >> DEALINGS IN
>>> >> + * THE SOFTWARE.
>> >
>> > Please make new original code GPLv2+. If you have copied from another
>> > file, then you should follow that file's licensing, but in that case
>> > you should also acknowledge the original copyright.
> OK.

Why?  The only "forbidden" license for new code is GPLv2.

If you want to make things more permissive, that's accepted.

Paolo

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22 12:11       ` Michael S. Tsirkin
@ 2015-06-22 12:36         ` Paulo Alcantara
  2015-06-22 12:44           ` Michael S. Tsirkin
  0 siblings, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22 12:36 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Paolo Bonzini, seabios, Paulo Alcantara, Paulo Alcantara, qemu-devel


On Mon, June 22, 2015 9:11 am, Michael S. Tsirkin wrote:
> On Mon, Jun 22, 2015 at 11:45:31AM +0200, Paolo Bonzini wrote:
>>
>>
>> On 22/06/2015 10:43, Michael S. Tsirkin wrote:
>> > Given that support is known to be partial, would it make sense
>> > to keep it disabled by default for 2.4?
>>
>> What is partial about it?
>
> Ow, looks like I didn't send out the response to the patch itself.
> Will do.
>
>>  In fact, considering that q35 behavior is
>> still experimental it makes no sense to even make it conditional.
>
> I agree to this, though an option to disable seems useful for debugging,
> so I'm glad that Paulo implemented it.  It's probably not strictly
> required to disable for old machine types, but why not.
>
>> We
>> discussed this on IRC and I was hoping to hear you reply "sorry, I was
>> wrong".  Instead, I get this.
>>
>> Michael, I'm seriously getting annoyed by this behavior.  Stop scaring
>> away contributors.
>>
>> Paolo
>
> Doing my best here, but I do think we need to be careful about merging
> things at this stage to avoid delaying the release.
>
>> > This way in 2.5 we won't need to add more flags to stay bug
>> compatible.

Hi Michael,

I have seen no use other than watchdog functionality of TCO. The reason I
wrote it was because I was working on an internal project that needed TCO
to generate SMI so that my registered SW SMI handler in firmware would get
executed. If, at that time, I had it supported on QEMU that would
certainly have saved a lot of time instead testing it on bare hardware :-)

Given that, I think it's OK for me to enable it by default on pc-q35-2.4
and later.

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22 12:36         ` Paulo Alcantara
@ 2015-06-22 12:44           ` Michael S. Tsirkin
  2015-06-22 12:59             ` Paolo Bonzini
  0 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-22 12:44 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: Paolo Bonzini, seabios, Paulo Alcantara, qemu-devel

On Mon, Jun 22, 2015 at 09:36:57AM -0300, Paulo Alcantara wrote:
> 
> On Mon, June 22, 2015 9:11 am, Michael S. Tsirkin wrote:
> > On Mon, Jun 22, 2015 at 11:45:31AM +0200, Paolo Bonzini wrote:
> >>
> >>
> >> On 22/06/2015 10:43, Michael S. Tsirkin wrote:
> >> > Given that support is known to be partial, would it make sense
> >> > to keep it disabled by default for 2.4?
> >>
> >> What is partial about it?
> >
> > Ow, looks like I didn't send out the response to the patch itself.
> > Will do.
> >
> >>  In fact, considering that q35 behavior is
> >> still experimental it makes no sense to even make it conditional.
> >
> > I agree to this, though an option to disable seems useful for debugging,
> > so I'm glad that Paulo implemented it.  It's probably not strictly
> > required to disable for old machine types, but why not.
> >
> >> We
> >> discussed this on IRC and I was hoping to hear you reply "sorry, I was
> >> wrong".  Instead, I get this.
> >>
> >> Michael, I'm seriously getting annoyed by this behavior.  Stop scaring
> >> away contributors.
> >>
> >> Paolo
> >
> > Doing my best here, but I do think we need to be careful about merging
> > things at this stage to avoid delaying the release.
> >
> >> > This way in 2.5 we won't need to add more flags to stay bug
> >> compatible.
> 
> Hi Michael,
> 
> I have seen no use other than watchdog functionality of TCO. The reason I
> wrote it was because I was working on an internal project that needed TCO
> to generate SMI so that my registered SW SMI handler in firmware would get
> executed. If, at that time, I had it supported on QEMU that would
> certainly have saved a lot of time instead testing it on bare hardware :-)
> 
> Given that, I think it's OK for me to enable it by default on pc-q35-2.4
> and later.
> 
> Thanks,
> 
> Paulo

OK. Do you agree to move the ACPI bits to the SSDT, making
it conditional on device being enabled?


> -- 
> Paulo Alcantara, C.E.S.A.R
> Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22 12:32       ` Paolo Bonzini
@ 2015-06-22 12:47         ` Michael S. Tsirkin
  2015-06-22 13:04           ` Paolo Bonzini
  2015-06-22 13:10           ` Markus Armbruster
  0 siblings, 2 replies; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-22 12:47 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: Paulo Alcantara, seabios, Paulo Alcantara, qemu-devel

On Mon, Jun 22, 2015 at 02:32:17PM +0200, Paolo Bonzini wrote:
> 
> 
> On 22/06/2015 14:30, Paulo Alcantara wrote:
> >>> >> +/*
> >>> >> + * QEMU ICH9 TCO emulation
> >>> >> + *
> >>> >> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> >>> >> + *
> >>> >> + * Permission is hereby granted, free of charge, to any person
> >>> >> obtaining a copy
> >>> >> + * of this software and associated documentation files (the
> >>> >> "Software"), to deal
> >>> >> + * in the Software without restriction, including without limitation
> >>> >> the rights
> >>> >> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
> >>> >> sell
> >>> >> + * copies of the Software, and to permit persons to whom the Software
> >>> >> is
> >>> >> + * furnished to do so, subject to the following conditions:
> >>> >> + *
> >>> >> + * The above copyright notice and this permission notice shall be
> >>> >> included in
> >>> >> + * all copies or substantial portions of the Software.
> >>> >> + *
> >>> >> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> >>> >> EXPRESS OR
> >>> >> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> >>> >> MERCHANTABILITY,
> >>> >> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
> >>> >> SHALL
> >>> >> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> >>> >> OTHER
> >>> >> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> >>> >> ARISING FROM,
> >>> >> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> >>> >> DEALINGS IN
> >>> >> + * THE SOFTWARE.
> >> >
> >> > Please make new original code GPLv2+. If you have copied from another
> >> > file, then you should follow that file's licensing, but in that case
> >> > you should also acknowledge the original copyright.
> > OK.
> 
> Why?  The only "forbidden" license for new code is GPLv2.
> 
> If you want to make things more permissive, that's accepted.
> 
> Paolo

Because it's a pain if I need to move code between files with different
licenses.  MIT is GPL compatible but mixing licenses at random is still
not a good idea.

-- 
MST

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-22  8:40     ` Michael S. Tsirkin
@ 2015-06-22 12:53       ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22 12:53 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Paulo Alcantara, pbonzini, seabios, Paulo Alcantara, qemu-devel


On Mon, June 22, 2015 5:40 am, Michael S. Tsirkin wrote:
> On Sun, Jun 21, 2015 at 09:37:02PM -0300, Paulo Alcantara wrote:
>> This block is mapped into memory space, using the Root Complex Base
>> Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this space
>> must be limited to 32-(DW) bit quantities. Burst accesses are not
>> allowed.
>>
>> All Chipset Configuration Registers are located in this 16KiB space.
>>
>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>
> Just to make sure, the device is in fact only functional
> if it's enabled using the property, correct?
> In that case please add it conditionally to the SSDT instead.

No need to add it conditionally in SSDT. In fact, what this patch does is
just reserving the MMIO region but the firmware and OS is free to set RCBA
to whatever address it wants. For instance, the OVMF is currently setting
RCBA to that address already so, if running on pc-q35-2.3 (or older), the
TCO will not work either since it was not initialised (enable_tco ==
false).

In order to test Linux's iTCO_wdt kernel module I had to patch SeaBIOS
(patch still pending, BTW) and OVMF to set RCBA BAR -- neither affected by
this DSDT's change -- because the module requires RCRB to access
GCS_NO_REBOOT bit flag to decide whether or not reboot the system on
second TCO timeout. Otherwise the module won't be loaded up and no
"/dev/watchdog" interface.

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22 12:44           ` Michael S. Tsirkin
@ 2015-06-22 12:59             ` Paolo Bonzini
  0 siblings, 0 replies; 83+ messages in thread
From: Paolo Bonzini @ 2015-06-22 12:59 UTC (permalink / raw)
  To: Michael S. Tsirkin, Paulo Alcantara; +Cc: seabios, Paulo Alcantara, qemu-devel



On 22/06/2015 14:44, Michael S. Tsirkin wrote:
> OK. Do you agree to move the ACPI bits to the SSDT, making
> it conditional on device being enabled?

Again, no.  The chipset configuration registers range is always defined
by the chipset, even if the TCO is not in use.  It would have to be
conditional on whether the RCBA is programmed by the firmware, not on
whether the TCO watchdog is enabled.  That's a very different patch, and
it's independent of DSDT vs. SSDT.

You have to draw a line between required work and nice-to-haves that the
maintainer can do himself.  Otherwise, the possibilities are:

1) the contributor is scared away, and we lose a valuable feature

2) the contributor does it, but then keeps all remaining contributions
in house because the first one was such a hell to go through.

Paolo

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22 12:47         ` Michael S. Tsirkin
@ 2015-06-22 13:04           ` Paolo Bonzini
  2015-06-22 13:07             ` Michael S. Tsirkin
  2015-06-22 13:10           ` Markus Armbruster
  1 sibling, 1 reply; 83+ messages in thread
From: Paolo Bonzini @ 2015-06-22 13:04 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: seabios, qemu-devel



On 22/06/2015 14:47, Michael S. Tsirkin wrote:
> Because it's a pain if I need to move code between files with different
> licenses.  MIT is GPL compatible but mixing licenses at random is still
> not a good idea.

This is a non-problem.  How often does it happen that code is moved
between files (as opposed to extracted to a new file)?

Besides, the rule you want is not enforceable because Fabrice Bellard
explicitly wanted TCG-related files to be BSD.

Paolo

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22 13:04           ` Paolo Bonzini
@ 2015-06-22 13:07             ` Michael S. Tsirkin
  2015-06-22 13:19               ` Paulo Alcantara
  0 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-22 13:07 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: seabios, qemu-devel

On Mon, Jun 22, 2015 at 03:04:13PM +0200, Paolo Bonzini wrote:
> 
> 
> On 22/06/2015 14:47, Michael S. Tsirkin wrote:
> > Because it's a pain if I need to move code between files with different
> > licenses.  MIT is GPL compatible but mixing licenses at random is still
> > not a good idea.
> 
> This is a non-problem.  How often does it happen that code is moved
> between files (as opposed to extracted to a new file)?
> 
> Besides, the rule you want is not enforceable because Fabrice Bellard
> explicitly wanted TCG-related files to be BSD.
> 
> Paolo

It's not a big deal, but it's preferable if possible.
Paulo do you have a problem switching to GPLv2+?

-- 
MST

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22 12:47         ` Michael S. Tsirkin
  2015-06-22 13:04           ` Paolo Bonzini
@ 2015-06-22 13:10           ` Markus Armbruster
  1 sibling, 0 replies; 83+ messages in thread
From: Markus Armbruster @ 2015-06-22 13:10 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Paulo Alcantara, Paolo Bonzini, seabios, Paulo Alcantara, qemu-devel

"Michael S. Tsirkin" <mst@redhat.com> writes:

> On Mon, Jun 22, 2015 at 02:32:17PM +0200, Paolo Bonzini wrote:
>> 
>> 
>> On 22/06/2015 14:30, Paulo Alcantara wrote:
>> >>> >> +/*
>> >>> >> + * QEMU ICH9 TCO emulation
>> >>> >> + *
>> >>> >> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
>> >>> >> + *
>> >>> >> + * Permission is hereby granted, free of charge, to any person
>> >>> >> obtaining a copy
>> >>> >> + * of this software and associated documentation files (the
>> >>> >> "Software"), to deal
>> >>> >> + * in the Software without restriction, including without limitation
>> >>> >> the rights
>> >>> >> + * to use, copy, modify, merge, publish, distribute,
>> >>> >> sublicense, and/or
>> >>> >> sell
>> >>> >> + * copies of the Software, and to permit persons to whom the Software
>> >>> >> is
>> >>> >> + * furnished to do so, subject to the following conditions:
>> >>> >> + *
>> >>> >> + * The above copyright notice and this permission notice shall be
>> >>> >> included in
>> >>> >> + * all copies or substantial portions of the Software.
>> >>> >> + *
>> >>> >> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>> >>> >> EXPRESS OR
>> >>> >> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>> >>> >> MERCHANTABILITY,
>> >>> >> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
>> >>> >> SHALL
>> >>> >> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
>> >>> >> DAMAGES OR
>> >>> >> OTHER
>> >>> >> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>> >>> >> ARISING FROM,
>> >>> >> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>> >>> >> DEALINGS IN
>> >>> >> + * THE SOFTWARE.
>> >> >
>> >> > Please make new original code GPLv2+. If you have copied from another
>> >> > file, then you should follow that file's licensing, but in that case
>> >> > you should also acknowledge the original copyright.
>> > OK.
>> 
>> Why?  The only "forbidden" license for new code is GPLv2.
>> 
>> If you want to make things more permissive, that's accepted.
>> 
>> Paolo
>
> Because it's a pain if I need to move code between files with different
> licenses.  MIT is GPL compatible but mixing licenses at random is still
> not a good idea.

Seconded.  New code should be GPLv2+ unless you have a really good
reason for something else.

Keeping the original license in a derivative work is a really good
reason (assuming it's compatible to GPLv2; if it's not, we can't use the
derivative work anyway).

LGPLv2+ license for code meant to be linked into differently licensed
other projects may be a good reason.

Other reasons exist.

Whatever your reason is, you need to explain it.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22 13:07             ` Michael S. Tsirkin
@ 2015-06-22 13:19               ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22 13:19 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Paolo Bonzini, seabios, qemu-devel


On Mon, June 22, 2015 10:07 am, Michael S. Tsirkin wrote:
> On Mon, Jun 22, 2015 at 03:04:13PM +0200, Paolo Bonzini wrote:
>>
>>
>> On 22/06/2015 14:47, Michael S. Tsirkin wrote:
>> > Because it's a pain if I need to move code between files with
>> different
>> > licenses.  MIT is GPL compatible but mixing licenses at random is
>> still
>> > not a good idea.
>>
>> This is a non-problem.  How often does it happen that code is moved
>> between files (as opposed to extracted to a new file)?
>>
>> Besides, the rule you want is not enforceable because Fabrice Bellard
>> explicitly wanted TCG-related files to be BSD.
>>
>> Paolo
>
> It's not a big deal, but it's preferable if possible.
> Paulo do you have a problem switching to GPLv2+?

Hi Michael,

No, I really don't. If it's really preferable and brings advantage to the
project, then I see no problem at all.

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
  2015-06-22  0:37 ` [Qemu-devel] [PATCH v4 " Paulo Alcantara
                     ` (3 preceding siblings ...)
  2015-06-22  8:43   ` Michael S. Tsirkin
@ 2015-06-22 18:29   ` Paulo Alcantara
  4 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22 18:29 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, mst


On Sun, June 21, 2015 9:37 pm, Paulo Alcantara wrote:
> This interface provides some registers within a 32-byte range and can be
> acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).
>
> It's commonly used as a watchdog timer to detect system lockups through
> SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
> NO_REBOOT bit is not set in GCS (General Control and Status register),
> the system will be resetted upon second timeout if TCO_RLD register
> wasn't previously written to prevent timeout.
>
> This patch adds support to TCO watchdog logic and few other features
> like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
> etc. are not implemented yet.
>
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
> v1 -> v2:
>   * add migration support for TCO I/O device state
>   * wake up only when total time expired instead of every 0.6s
>   * some cleanup suggested by Paolo Bonzini
>
> v2 -> v3:
>   * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
>   * improve handling of TCO_LOCK bit in TCO1_CNT register
>
> v3 -> v4:
>   * fix some conflicts in hw/acpi/ich9.c after rebasing against master
>   * remove meaningless "use_tco" field from TCOIORegs structure
>   * add a object property named "enable_tco" and only enable TCO support
>     on pc-q35-2.4 and later
> ---
>  hw/acpi/Makefile.objs  |   2 +-
>  hw/acpi/ich9.c         |  55 +++++++++-
>  hw/acpi/tco.c          | 279
> +++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/i386/pc_q35.c       |   4 +-
>  hw/isa/lpc_ich9.c      |  15 ++-
>  include/hw/acpi/ich9.h |   7 +-
>  include/hw/acpi/tco.h  |  98 +++++++++++++++++
>  include/hw/boards.h    |   3 +-
>  include/hw/i386/ich9.h |  10 +-
>  include/hw/i386/pc.h   |   1 +
>  10 files changed, 466 insertions(+), 8 deletions(-)
>  create mode 100644 hw/acpi/tco.c
>  create mode 100644 include/hw/acpi/tco.h
>
> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> index 29d46d8..3db1f07 100644
> --- a/hw/acpi/Makefile.objs
> +++ b/hw/acpi/Makefile.objs
> @@ -1,4 +1,4 @@
> -common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
> +common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o tco.o
>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
>  common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
>  common-obj-$(CONFIG_ACPI) += acpi_interface.o
> diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
> index 8a64ffb..d3d9953 100644
> --- a/hw/acpi/ich9.c
> +++ b/hw/acpi/ich9.c
> @@ -30,6 +30,7 @@
>  #include "qemu/timer.h"
>  #include "sysemu/sysemu.h"
>  #include "hw/acpi/acpi.h"
> +#include "hw/acpi/tco.h"
>  #include "sysemu/kvm.h"
>  #include "exec/address-spaces.h"
>
> @@ -92,8 +93,16 @@ static void ich9_smi_writel(void *opaque, hwaddr addr,
> uint64_t val,
>                              unsigned width)
>  {
>      ICH9LPCPMRegs *pm = opaque;
> +    TCOIORegs *tr = &pm->tco_regs;
> +    uint64_t tco_en;
> +
>      switch (addr) {
>      case 0:
> +        tco_en = pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
> +        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
> +        if (tr->tco.cnt1 & TCO_LOCK) {
> +            val = (val & ~ICH9_PMIO_SMI_EN_TCO_EN) | tco_en;
> +        }
>          pm->smi_en &= ~pm->smi_en_wmask;
>          pm->smi_en |= (val & pm->smi_en_wmask);
>          break;
> @@ -159,6 +168,25 @@ static const VMStateDescription vmstate_memhp_state =
> {
>      }
>  };
>
> +static bool vmstate_test_use_tco(void *opaque)
> +{
> +    ICH9LPCPMRegs *s = opaque;
> +    return s->enable_tco;
> +}
> +
> +static const VMStateDescription vmstate_tco_io_state = {
> +    .name = "ich9_pm/tco",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .needed = vmstate_test_use_tco,
> +    .fields      = (VMStateField[]) {
> +        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
> +                       TCOIORegs),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
>  const VMStateDescription vmstate_ich9_pm = {
>      .name = "ich9_pm",
>      .version_id = 1,
> @@ -179,6 +207,10 @@ const VMStateDescription vmstate_ich9_pm = {
>      .subsections = (const VMStateDescription*[]) {
>          &vmstate_memhp_state,
>          NULL
> +    },
> +    .subsections = (const VMStateDescription*[]) {
> +        &vmstate_tco_io_state,
> +        NULL
>      }
>  };
>
> @@ -209,7 +241,7 @@ static void pm_powerdown_req(Notifier *n, void
> *opaque)
>      acpi_pm1_evt_power_down(&pm->acpi_regs);
>  }
>
> -void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
> +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
>                    qemu_irq sci_irq)
>  {
>      memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm",
> ICH9_PMIO_SIZE);
> @@ -231,6 +263,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs
> *pm,
>                            "acpi-smi", 8);
>      memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
>
> +    pm->enable_tco = enable_tco;
> +    if (pm->enable_tco) {
> +        acpi_pm_tco_init(&pm->tco_regs, &pm->io);
> +    }
> +
>      pm->irq = sci_irq;
>      qemu_register_reset(pm_reset, pm);
>      pm->powerdown_notifier.notify = pm_powerdown_req;
> @@ -351,6 +388,18 @@ out:
>      error_propagate(errp, local_err);
>  }
>
> +static bool ich9_pm_get_enable_tco(Object *obj, Error **errp)
> +{
> +    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
> +    return s->pm.enable_tco;
> +}
> +
> +static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp)
> +{
> +    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
> +    s->pm.enable_tco = value;
> +}
> +
>  void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
>  {
>      static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
> @@ -382,6 +431,10 @@ void ich9_pm_add_properties(Object *obj,
> ICH9LPCPMRegs *pm, Error **errp)
>                          ich9_pm_get_s4_val,
>                          ich9_pm_set_s4_val,
>                          NULL, pm, NULL);
> +    object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
> +                             ich9_pm_get_enable_tco,
> +                             ich9_pm_set_enable_tco,
> +                             NULL);
>  }
>
>  void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error
> **errp)
> diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> new file mode 100644
> index 0000000..b6af1d9
> --- /dev/null
> +++ b/hw/acpi/tco.c
> @@ -0,0 +1,279 @@
> +/*
> + * QEMU ICH9 TCO emulation
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> a copy
> + * of this software and associated documentation files (the "Software"),
> to deal
> + * in the Software without restriction, including without limitation the
> rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
> sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be
> included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
> SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> IN
> + * THE SOFTWARE.
> + */
> +#include "qemu-common.h"
> +#include "sysemu/watchdog.h"
> +#include "hw/i386/ich9.h"
> +
> +#include "hw/acpi/tco.h"
> +
> +//#define DEBUG
> +
> +#ifdef DEBUG
> +#define TCO_DEBUG(fmt, ...)                                     \
> +    do {                                                        \
> +        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
> +    } while (0)
> +#else
> +#define TCO_DEBUG(fmt, ...) do { } while (0)
> +#endif
> +
> +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,
> +};
> +
> +static inline void tco_timer_reload(TCOIORegs *tr)
> +{
> +    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> +        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
> +    timer_mod(tr->tco_timer, tr->expire_time);
> +}
> +
> +static inline void tco_timer_stop(TCOIORegs *tr)
> +{
> +    tr->expire_time = -1;
> +}
> +
> +static void tco_timer_expired(void *opaque)
> +{
> +    TCOIORegs *tr = opaque;
> +    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
> +    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
> +    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_LPC_RCBA_GCS);
> +
> +    tr->tco.rld = 0;
> +    tr->tco.sts1 |= TCO_TIMEOUT;
> +    if (++tr->timeouts_no == 2) {
> +        tr->tco.sts2 |= TCO_SECOND_TO_STS;
> +        tr->tco.sts2 |= TCO_BOOT_STS;
> +        tr->timeouts_no = 0;
> +
> +        if (!(gcs & ICH9_LPC_RCBA_GCS_NO_REBOOT)) {
> +            watchdog_perform_action();
> +            tco_timer_stop(tr);
> +            return;
> +        }
> +    }
> +
> +    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
> +        ich9_generate_smi();
> +    } else {
> +        ich9_generate_nmi();
> +    }
> +    tr->tco.rld = tr->tco.tmr;
> +    tco_timer_reload(tr);
> +}
> +
> +/* NOTE: values of 0 or 1 will be ignored by ICH */
> +static inline int can_start_tco_timer(TCOIORegs *tr)
> +{
> +    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
> +}
> +
> +static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
> +{
> +    uint16_t rld;
> +
> +    switch (addr) {
> +    case TCO_RLD:
> +        if (tr->expire_time != -1) {
> +            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
> +            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
> +        } else {
> +            rld = tr->tco.rld;
> +        }
> +        return rld;
> +    case TCO_DAT_IN:
> +        return tr->tco.din;
> +    case TCO_DAT_OUT:
> +        return tr->tco.dout;
> +    case TCO1_STS:
> +        return tr->tco.sts1;
> +    case TCO2_STS:
> +        return tr->tco.sts2;
> +    case TCO1_CNT:
> +        return tr->tco.cnt1;
> +    case TCO2_CNT:
> +        return tr->tco.cnt2;
> +    case TCO_MESSAGE1:
> +        return tr->tco.msg1;
> +    case TCO_MESSAGE2:
> +        return tr->tco.msg2;
> +    case TCO_WDCNT:
> +        return tr->tco.wdcnt;
> +    case TCO_TMR:
> +        return tr->tco.tmr;
> +    case SW_IRQ_GEN:
> +        return tr->sw_irq_gen;
> +    }
> +    return 0;
> +}
> +
> +static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
> +{
> +    switch (addr) {
> +    case TCO_RLD:
> +        tr->timeouts_no = 0;
> +        if (can_start_tco_timer(tr)) {
> +            tr->tco.rld = tr->tco.tmr;
> +            tco_timer_reload(tr);
> +        } else {
> +            tr->tco.rld = val;
> +        }
> +        break;
> +    case TCO_DAT_IN:
> +        tr->tco.din = val;
> +        tr->tco.sts1 |= SW_TCO_SMI;
> +        ich9_generate_smi();
> +        break;
> +    case TCO_DAT_OUT:
> +        tr->tco.dout = val;
> +        tr->tco.sts1 |= TCO_INT_STS;
> +        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits
> */
> +        break;
> +    case TCO1_STS:
> +        tr->tco.sts1 = val & TCO1_STS_MASK;
> +        break;
> +    case TCO2_STS:
> +        tr->tco.sts2 = val & TCO2_STS_MASK;
> +        break;
> +    case TCO1_CNT:
> +        val &= TCO1_CNT_MASK;
> +        /*
> +         * once TCO_LOCK bit is set, it can not be cleared by software. a
> reset
> +         * is required to change this bit from 1 to 0 -- it defaults to
> 0.
> +         */
> +        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
> +        if (can_start_tco_timer(tr)) {
> +            tr->tco.rld = tr->tco.tmr;
> +            tco_timer_reload(tr);
> +        } else {
> +            tco_timer_stop(tr);
> +        }
> +        break;
> +    case TCO2_CNT:
> +        tr->tco.cnt2 = val;
> +        break;
> +    case TCO_MESSAGE1:
> +        tr->tco.msg1 = val;
> +        break;
> +    case TCO_MESSAGE2:
> +        tr->tco.msg2 = val;
> +        break;
> +    case TCO_WDCNT:
> +        tr->tco.wdcnt = val;
> +        break;
> +    case TCO_TMR:
> +        tr->tco.tmr = val;
> +        break;
> +    case SW_IRQ_GEN:
> +        tr->sw_irq_gen = val;
> +        break;
> +    }
> +}
> +
> +static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width)
> +{
> +    TCOIORegs *tr = opaque;
> +    return tco_ioport_readw(tr, addr);
> +}
> +
> +static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val,
> +                          unsigned width)
> +{
> +    TCOIORegs *tr = opaque;
> +    tco_ioport_writew(tr, addr, val);
> +}
> +
> +static const MemoryRegionOps tco_io_ops = {
> +    .read = tco_io_readw,
> +    .write = tco_io_writew,
> +    .valid.min_access_size = 1,
> +    .valid.max_access_size = 4,
> +    .impl.min_access_size = 1,
> +    .impl.max_access_size = 2,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent)
> +{
> +    *tr = (TCOIORegs) {
> +        .tco = {
> +            .rld      = TCO_RLD_DEFAULT,
> +            .din      = TCO_DAT_IN_DEFAULT,
> +            .dout     = TCO_DAT_OUT_DEFAULT,
> +            .sts1     = TCO1_STS_DEFAULT,
> +            .sts2     = TCO2_STS_DEFAULT,
> +            .cnt1     = TCO1_CNT_DEFAULT,
> +            .cnt2     = TCO2_CNT_DEFAULT,
> +            .msg1     = TCO_MESSAGE1_DEFAULT,
> +            .msg2     = TCO_MESSAGE2_DEFAULT,
> +            .wdcnt    = TCO_WDCNT_DEFAULT,
> +            .tmr      = TCO_TMR_DEFAULT,
> +        },
> +        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
> +        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL,
> tco_timer_expired, tr),
> +        .expire_time   = -1,
> +        .timeouts_no   = 0,
> +    };
> +    memory_region_init_io(&tr->io, memory_region_owner(parent),
> +                          &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN);
> +    memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io);
> +}
> +
> +const VMStateDescription vmstate_tco_io_sts = {
> +    .name = "tco io device status",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields      = (VMStateField[]) {
> +        VMSTATE_UINT16(tco.rld, TCOIORegs),
> +        VMSTATE_UINT8(tco.din, TCOIORegs),
> +        VMSTATE_UINT8(tco.dout, TCOIORegs),
> +        VMSTATE_UINT16(tco.sts1, TCOIORegs),
> +        VMSTATE_UINT16(tco.sts2, TCOIORegs),
> +        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
> +        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
> +        VMSTATE_UINT8(tco.msg1, TCOIORegs),
> +        VMSTATE_UINT8(tco.msg2, TCOIORegs),
> +        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
> +        VMSTATE_UINT16(tco.tmr, TCOIORegs),
> +        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
> +        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
> +        VMSTATE_INT64(expire_time, TCOIORegs),
> +        VMSTATE_UINT8(timeouts_no, TCOIORegs),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
> index 082cd93..7e35a36 100644
> --- a/hw/i386/pc_q35.c
> +++ b/hw/i386/pc_q35.c
> @@ -253,7 +253,7 @@ static void pc_q35_init(MachineState *machine)
>                           (pc_machine->vmport != ON_OFF_AUTO_ON),
> 0xff0104);
>
>      /* connect pm stuff to lpc */
> -    ich9_lpc_pm_init(lpc);
> +    ich9_lpc_pm_init(lpc, !mc->no_tco);
>
>      /* ahci and SATA device, for q35 1 ahci controller is built-in */
>      ahci = pci_create_simple_multifunction(host_bus,
> @@ -393,6 +393,7 @@ static void pc_q35_2_4_machine_options(MachineClass
> *m)
>      m->default_machine_opts = "firmware=bios-256k.bin";
>      m->default_display = "std";
>      m->no_floppy = 1;
> +    m->no_tco = 0;
>      m->alias = "q35";
>  }
>
> @@ -404,6 +405,7 @@ static void pc_q35_2_3_machine_options(MachineClass
> *m)
>  {
>      pc_q35_2_4_machine_options(m);
>      m->no_floppy = 0;
> +    m->no_tco = 1;
>      m->alias = NULL;
>      SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
>  }
> diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> index b3e0b1f..acf262c 100644
> --- a/hw/isa/lpc_ich9.c
> +++ b/hw/isa/lpc_ich9.c
> @@ -313,6 +313,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque,
> int pirq_pin)
>      return route;
>  }
>
> +void ich9_generate_smi(void)
> +{
> +    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
> +}
> +
> +void ich9_generate_nmi(void)
> +{
> +    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
> +}
> +
>  static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
>  {
>      switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
> @@ -357,11 +367,12 @@ static void ich9_set_sci(void *opaque, int irq_num,
> int level)
>      }
>  }
>
> -void ich9_lpc_pm_init(PCIDevice *lpc_pci)
> +void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool enable_tco)
>  {
>      ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
>
> -    ich9_pm_init(lpc_pci, &lpc->pm, qemu_allocate_irq(ich9_set_sci, lpc,
> 0));
> +    ich9_pm_init(lpc_pci, &lpc->pm, enable_tco,
> +                 qemu_allocate_irq(ich9_set_sci, lpc, 0));
>      ich9_lpc_reset(&lpc->d.qdev);
>  }
>
> diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
> index 77cc65c..a7eb421 100644
> --- a/include/hw/acpi/ich9.h
> +++ b/include/hw/acpi/ich9.h
> @@ -25,6 +25,7 @@
>  #include "hw/acpi/cpu_hotplug.h"
>  #include "hw/acpi/memory_hotplug.h"
>  #include "hw/acpi/acpi_dev_interface.h"
> +#include "hw/acpi/tco.h"
>
>  typedef struct ICH9LPCPMRegs {
>      /*
> @@ -37,6 +38,7 @@ typedef struct ICH9LPCPMRegs {
>      MemoryRegion io;
>      MemoryRegion io_gpe;
>      MemoryRegion io_smi;
> +    MemoryRegion io_tco;
>
>      uint32_t smi_en;
>      uint32_t smi_en_wmask;
> @@ -54,9 +56,12 @@ typedef struct ICH9LPCPMRegs {
>      uint8_t disable_s3;
>      uint8_t disable_s4;
>      uint8_t s4_val;
> +    bool enable_tco;
> +
> +    TCOIORegs tco_regs;
>  } ICH9LPCPMRegs;
>
> -void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
> +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
>                    qemu_irq sci_irq);
>  void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base);
>  extern const VMStateDescription vmstate_ich9_pm;
> diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
> new file mode 100644
> index 0000000..477a32c
> --- /dev/null
> +++ b/include/hw/acpi/tco.h
> @@ -0,0 +1,98 @@
> +/*
> + * QEMU ICH9 TCO emulation
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> a copy
> + * of this software and associated documentation files (the "Software"),
> to deal
> + * in the Software without restriction, including without limitation the
> rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
> sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be
> included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
> SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> IN
> + * THE SOFTWARE.
> + */
> +#ifndef HW_ACPI_TCO_H
> +#define HW_ACPI_TCO_H
> +
> +#include "qemu/typedefs.h"
> +#include "qemu-common.h"
> +
> +/* As per ICH9 spec, the internal timer has an error of ~0.6s on every
> tick */
> +#define TCO_TICK_NSEC 600000000LL
> +
> +/* TCO I/O register offsets */
> +enum {
> +    TCO_RLD           = 0x00,
> +    TCO_DAT_IN        = 0x02,
> +    TCO_DAT_OUT       = 0x03,
> +    TCO1_STS          = 0x04,
> +    TCO2_STS          = 0x06,
> +    TCO1_CNT          = 0x08,
> +    TCO2_CNT          = 0x0a,
> +    TCO_MESSAGE1      = 0x0c,
> +    TCO_MESSAGE2      = 0x0d,
> +    TCO_WDCNT         = 0x0e,
> +    SW_IRQ_GEN        = 0x10,
> +    TCO_TMR           = 0x12,
> +};
> +
> +/* TCO I/O register control/status bits */
> +enum {
> +    SW_TCO_SMI           = 1 << 1,
> +    TCO_INT_STS          = 1 << 2,
> +    TCO_LOCK             = 1 << 12,
> +    TCO_TMR_HLT          = 1 << 11,
> +    TCO_TIMEOUT          = 1 << 3,
> +    TCO_SECOND_TO_STS    = 1 << 1,
> +    TCO_BOOT_STS         = 1 << 2,
> +};
> +
> +/* TCO I/O registers mask bits */
> +enum {
> +    TCO_RLD_MASK     = 0x3ff,
> +    TCO1_STS_MASK    = 0xe870,
> +    TCO2_STS_MASK    = 0xfff8,
> +    TCO1_CNT_MASK    = 0xfeff,
> +    TCO_TMR_MASK     = 0x3ff,
> +};
> +
> +typedef struct TCOIORegs {
> +    bool use_tco;

This field is no longer used and should be removed. When I get home I'll
send a v5 with this fix and the license stuff. Thank you all for reviewing
it. That's really appreciable.

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v5 1/3] ich9: add TCO interface emulation
  2015-05-27  0:29 [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paulo Alcantara
                   ` (4 preceding siblings ...)
  2015-06-22  0:37 ` [Qemu-devel] [PATCH v4 " Paulo Alcantara
@ 2015-06-22 23:10 ` Paulo Alcantara
  2015-06-22 23:10   ` [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
                     ` (2 more replies)
  2015-06-24 18:03 ` [Qemu-devel] [PATCH v6 1/2] " Paulo Alcantara
                   ` (2 subsequent siblings)
  8 siblings, 3 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22 23:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

This interface provides some registers within a 32-byte range and can be
acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).

It's commonly used as a watchdog timer to detect system lockups through
SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
NO_REBOOT bit is not set in GCS (General Control and Status register),
the system will be resetted upon second timeout if TCO_RLD register
wasn't previously written to prevent timeout.

This patch adds support to TCO watchdog logic and few other features
like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
etc. are not implemented yet.

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * add migration support for TCO I/O device state
  * wake up only when total time expired instead of every 0.6s
  * some cleanup suggested by Paolo Bonzini

v2 -> v3:
  * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
  * improve handling of TCO_LOCK bit in TCO1_CNT register

v3 -> v4:
  * fix some conflicts in hw/acpi/ich9.c after rebasing against master
  * remove meaningless "use_tco" field from TCOIORegs structure
  * add a object property named "enable_tco" and only enable TCO support
    on pc-q35-2.4 and later

v4 -> v5:
  * remove unused field (use_tco) in TCOIORegs structure
  * move license to GPLv2+
---
 hw/acpi/Makefile.objs  |   2 +-
 hw/acpi/ich9.c         |  55 ++++++++++-
 hw/acpi/tco.c          | 264 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/i386/pc_q35.c       |   4 +-
 hw/isa/lpc_ich9.c      |  15 ++-
 include/hw/acpi/ich9.h |   7 +-
 include/hw/acpi/tco.h  |  82 +++++++++++++++
 include/hw/boards.h    |   3 +-
 include/hw/i386/ich9.h |  10 +-
 include/hw/i386/pc.h   |   1 +
 10 files changed, 435 insertions(+), 8 deletions(-)
 create mode 100644 hw/acpi/tco.c
 create mode 100644 include/hw/acpi/tco.h

diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 29d46d8..3db1f07 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
+common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o tco.o
 common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
 common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI) += acpi_interface.o
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 8a64ffb..d3d9953 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -30,6 +30,7 @@
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "hw/acpi/acpi.h"
+#include "hw/acpi/tco.h"
 #include "sysemu/kvm.h"
 #include "exec/address-spaces.h"
 
@@ -92,8 +93,16 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
                             unsigned width)
 {
     ICH9LPCPMRegs *pm = opaque;
+    TCOIORegs *tr = &pm->tco_regs;
+    uint64_t tco_en;
+
     switch (addr) {
     case 0:
+        tco_en = pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
+        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
+        if (tr->tco.cnt1 & TCO_LOCK) {
+            val = (val & ~ICH9_PMIO_SMI_EN_TCO_EN) | tco_en;
+        }
         pm->smi_en &= ~pm->smi_en_wmask;
         pm->smi_en |= (val & pm->smi_en_wmask);
         break;
@@ -159,6 +168,25 @@ static const VMStateDescription vmstate_memhp_state = {
     }
 };
 
+static bool vmstate_test_use_tco(void *opaque)
+{
+    ICH9LPCPMRegs *s = opaque;
+    return s->enable_tco;
+}
+
+static const VMStateDescription vmstate_tco_io_state = {
+    .name = "ich9_pm/tco",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .needed = vmstate_test_use_tco,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
+                       TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 const VMStateDescription vmstate_ich9_pm = {
     .name = "ich9_pm",
     .version_id = 1,
@@ -179,6 +207,10 @@ const VMStateDescription vmstate_ich9_pm = {
     .subsections = (const VMStateDescription*[]) {
         &vmstate_memhp_state,
         NULL
+    },
+    .subsections = (const VMStateDescription*[]) {
+        &vmstate_tco_io_state,
+        NULL
     }
 };
 
@@ -209,7 +241,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
     acpi_pm1_evt_power_down(&pm->acpi_regs);
 }
 
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
                   qemu_irq sci_irq)
 {
     memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
@@ -231,6 +263,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
                           "acpi-smi", 8);
     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
 
+    pm->enable_tco = enable_tco;
+    if (pm->enable_tco) {
+        acpi_pm_tco_init(&pm->tco_regs, &pm->io);
+    }
+
     pm->irq = sci_irq;
     qemu_register_reset(pm_reset, pm);
     pm->powerdown_notifier.notify = pm_powerdown_req;
@@ -351,6 +388,18 @@ out:
     error_propagate(errp, local_err);
 }
 
+static bool ich9_pm_get_enable_tco(Object *obj, Error **errp)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+    return s->pm.enable_tco;
+}
+
+static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+    s->pm.enable_tco = value;
+}
+
 void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
 {
     static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
@@ -382,6 +431,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
                         ich9_pm_get_s4_val,
                         ich9_pm_set_s4_val,
                         NULL, pm, NULL);
+    object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
+                             ich9_pm_get_enable_tco,
+                             ich9_pm_set_enable_tco,
+                             NULL);
 }
 
 void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp)
diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
new file mode 100644
index 0000000..b01aeda
--- /dev/null
+++ b/hw/acpi/tco.c
@@ -0,0 +1,264 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * 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.
+ */
+#include "qemu-common.h"
+#include "sysemu/watchdog.h"
+#include "hw/i386/ich9.h"
+
+#include "hw/acpi/tco.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define TCO_DEBUG(fmt, ...)                                     \
+    do {                                                        \
+        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
+    } while (0)
+#else
+#define TCO_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+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,
+};
+
+static inline void tco_timer_reload(TCOIORegs *tr)
+{
+    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
+    timer_mod(tr->tco_timer, tr->expire_time);
+}
+
+static inline void tco_timer_stop(TCOIORegs *tr)
+{
+    tr->expire_time = -1;
+}
+
+static void tco_timer_expired(void *opaque)
+{
+    TCOIORegs *tr = opaque;
+    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
+    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
+    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_LPC_RCBA_GCS);
+
+    tr->tco.rld = 0;
+    tr->tco.sts1 |= TCO_TIMEOUT;
+    if (++tr->timeouts_no == 2) {
+        tr->tco.sts2 |= TCO_SECOND_TO_STS;
+        tr->tco.sts2 |= TCO_BOOT_STS;
+        tr->timeouts_no = 0;
+
+        if (!(gcs & ICH9_LPC_RCBA_GCS_NO_REBOOT)) {
+            watchdog_perform_action();
+            tco_timer_stop(tr);
+            return;
+        }
+    }
+
+    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
+        ich9_generate_smi();
+    } else {
+        ich9_generate_nmi();
+    }
+    tr->tco.rld = tr->tco.tmr;
+    tco_timer_reload(tr);
+}
+
+/* NOTE: values of 0 or 1 will be ignored by ICH */
+static inline int can_start_tco_timer(TCOIORegs *tr)
+{
+    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
+}
+
+static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
+{
+    uint16_t rld;
+
+    switch (addr) {
+    case TCO_RLD:
+        if (tr->expire_time != -1) {
+            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
+            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
+        } else {
+            rld = tr->tco.rld;
+        }
+        return rld;
+    case TCO_DAT_IN:
+        return tr->tco.din;
+    case TCO_DAT_OUT:
+        return tr->tco.dout;
+    case TCO1_STS:
+        return tr->tco.sts1;
+    case TCO2_STS:
+        return tr->tco.sts2;
+    case TCO1_CNT:
+        return tr->tco.cnt1;
+    case TCO2_CNT:
+        return tr->tco.cnt2;
+    case TCO_MESSAGE1:
+        return tr->tco.msg1;
+    case TCO_MESSAGE2:
+        return tr->tco.msg2;
+    case TCO_WDCNT:
+        return tr->tco.wdcnt;
+    case TCO_TMR:
+        return tr->tco.tmr;
+    case SW_IRQ_GEN:
+        return tr->sw_irq_gen;
+    }
+    return 0;
+}
+
+static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
+{
+    switch (addr) {
+    case TCO_RLD:
+        tr->timeouts_no = 0;
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tr->tco.rld = val;
+        }
+        break;
+    case TCO_DAT_IN:
+        tr->tco.din = val;
+        tr->tco.sts1 |= SW_TCO_SMI;
+        ich9_generate_smi();
+        break;
+    case TCO_DAT_OUT:
+        tr->tco.dout = val;
+        tr->tco.sts1 |= TCO_INT_STS;
+        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
+        break;
+    case TCO1_STS:
+        tr->tco.sts1 = val & TCO1_STS_MASK;
+        break;
+    case TCO2_STS:
+        tr->tco.sts2 = val & TCO2_STS_MASK;
+        break;
+    case TCO1_CNT:
+        val &= TCO1_CNT_MASK;
+        /*
+         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
+         * is required to change this bit from 1 to 0 -- it defaults to 0.
+         */
+        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tco_timer_stop(tr);
+        }
+        break;
+    case TCO2_CNT:
+        tr->tco.cnt2 = val;
+        break;
+    case TCO_MESSAGE1:
+        tr->tco.msg1 = val;
+        break;
+    case TCO_MESSAGE2:
+        tr->tco.msg2 = val;
+        break;
+    case TCO_WDCNT:
+        tr->tco.wdcnt = val;
+        break;
+    case TCO_TMR:
+        tr->tco.tmr = val;
+        break;
+    case SW_IRQ_GEN:
+        tr->sw_irq_gen = val;
+        break;
+    }
+}
+
+static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width)
+{
+    TCOIORegs *tr = opaque;
+    return tco_ioport_readw(tr, addr);
+}
+
+static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val,
+                          unsigned width)
+{
+    TCOIORegs *tr = opaque;
+    tco_ioport_writew(tr, addr, val);
+}
+
+static const MemoryRegionOps tco_io_ops = {
+    .read = tco_io_readw,
+    .write = tco_io_writew,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent)
+{
+    *tr = (TCOIORegs) {
+        .tco = {
+            .rld      = TCO_RLD_DEFAULT,
+            .din      = TCO_DAT_IN_DEFAULT,
+            .dout     = TCO_DAT_OUT_DEFAULT,
+            .sts1     = TCO1_STS_DEFAULT,
+            .sts2     = TCO2_STS_DEFAULT,
+            .cnt1     = TCO1_CNT_DEFAULT,
+            .cnt2     = TCO2_CNT_DEFAULT,
+            .msg1     = TCO_MESSAGE1_DEFAULT,
+            .msg2     = TCO_MESSAGE2_DEFAULT,
+            .wdcnt    = TCO_WDCNT_DEFAULT,
+            .tmr      = TCO_TMR_DEFAULT,
+        },
+        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
+        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
+        .expire_time   = -1,
+        .timeouts_no   = 0,
+    };
+    memory_region_init_io(&tr->io, memory_region_owner(parent),
+                          &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN);
+    memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io);
+}
+
+const VMStateDescription vmstate_tco_io_sts = {
+    .name = "tco io device status",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT16(tco.rld, TCOIORegs),
+        VMSTATE_UINT8(tco.din, TCOIORegs),
+        VMSTATE_UINT8(tco.dout, TCOIORegs),
+        VMSTATE_UINT16(tco.sts1, TCOIORegs),
+        VMSTATE_UINT16(tco.sts2, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
+        VMSTATE_UINT8(tco.msg1, TCOIORegs),
+        VMSTATE_UINT8(tco.msg2, TCOIORegs),
+        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
+        VMSTATE_UINT16(tco.tmr, TCOIORegs),
+        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
+        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
+        VMSTATE_INT64(expire_time, TCOIORegs),
+        VMSTATE_UINT8(timeouts_no, TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 082cd93..7e35a36 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -253,7 +253,7 @@ static void pc_q35_init(MachineState *machine)
                          (pc_machine->vmport != ON_OFF_AUTO_ON), 0xff0104);
 
     /* connect pm stuff to lpc */
-    ich9_lpc_pm_init(lpc);
+    ich9_lpc_pm_init(lpc, !mc->no_tco);
 
     /* ahci and SATA device, for q35 1 ahci controller is built-in */
     ahci = pci_create_simple_multifunction(host_bus,
@@ -393,6 +393,7 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
     m->default_machine_opts = "firmware=bios-256k.bin";
     m->default_display = "std";
     m->no_floppy = 1;
+    m->no_tco = 0;
     m->alias = "q35";
 }
 
@@ -404,6 +405,7 @@ static void pc_q35_2_3_machine_options(MachineClass *m)
 {
     pc_q35_2_4_machine_options(m);
     m->no_floppy = 0;
+    m->no_tco = 1;
     m->alias = NULL;
     SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
 }
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index b3e0b1f..acf262c 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -313,6 +313,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
     return route;
 }
 
+void ich9_generate_smi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+}
+
+void ich9_generate_nmi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
+}
+
 static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
 {
     switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
@@ -357,11 +367,12 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
     }
 }
 
-void ich9_lpc_pm_init(PCIDevice *lpc_pci)
+void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool enable_tco)
 {
     ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
 
-    ich9_pm_init(lpc_pci, &lpc->pm, qemu_allocate_irq(ich9_set_sci, lpc, 0));
+    ich9_pm_init(lpc_pci, &lpc->pm, enable_tco,
+                 qemu_allocate_irq(ich9_set_sci, lpc, 0));
     ich9_lpc_reset(&lpc->d.qdev);
 }
 
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
index 77cc65c..a7eb421 100644
--- a/include/hw/acpi/ich9.h
+++ b/include/hw/acpi/ich9.h
@@ -25,6 +25,7 @@
 #include "hw/acpi/cpu_hotplug.h"
 #include "hw/acpi/memory_hotplug.h"
 #include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/tco.h"
 
 typedef struct ICH9LPCPMRegs {
     /*
@@ -37,6 +38,7 @@ typedef struct ICH9LPCPMRegs {
     MemoryRegion io;
     MemoryRegion io_gpe;
     MemoryRegion io_smi;
+    MemoryRegion io_tco;
 
     uint32_t smi_en;
     uint32_t smi_en_wmask;
@@ -54,9 +56,12 @@ typedef struct ICH9LPCPMRegs {
     uint8_t disable_s3;
     uint8_t disable_s4;
     uint8_t s4_val;
+    bool enable_tco;
+
+    TCOIORegs tco_regs;
 } ICH9LPCPMRegs;
 
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
                   qemu_irq sci_irq);
 void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base);
 extern const VMStateDescription vmstate_ich9_pm;
diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
new file mode 100644
index 0000000..c63afc8
--- /dev/null
+++ b/include/hw/acpi/tco.h
@@ -0,0 +1,82 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * 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.
+ */
+#ifndef HW_ACPI_TCO_H
+#define HW_ACPI_TCO_H
+
+#include "qemu/typedefs.h"
+#include "qemu-common.h"
+
+/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
+#define TCO_TICK_NSEC 600000000LL
+
+/* TCO I/O register offsets */
+enum {
+    TCO_RLD           = 0x00,
+    TCO_DAT_IN        = 0x02,
+    TCO_DAT_OUT       = 0x03,
+    TCO1_STS          = 0x04,
+    TCO2_STS          = 0x06,
+    TCO1_CNT          = 0x08,
+    TCO2_CNT          = 0x0a,
+    TCO_MESSAGE1      = 0x0c,
+    TCO_MESSAGE2      = 0x0d,
+    TCO_WDCNT         = 0x0e,
+    SW_IRQ_GEN        = 0x10,
+    TCO_TMR           = 0x12,
+};
+
+/* TCO I/O register control/status bits */
+enum {
+    SW_TCO_SMI           = 1 << 1,
+    TCO_INT_STS          = 1 << 2,
+    TCO_LOCK             = 1 << 12,
+    TCO_TMR_HLT          = 1 << 11,
+    TCO_TIMEOUT          = 1 << 3,
+    TCO_SECOND_TO_STS    = 1 << 1,
+    TCO_BOOT_STS         = 1 << 2,
+};
+
+/* TCO I/O registers mask bits */
+enum {
+    TCO_RLD_MASK     = 0x3ff,
+    TCO1_STS_MASK    = 0xe870,
+    TCO2_STS_MASK    = 0xfff8,
+    TCO1_CNT_MASK    = 0xfeff,
+    TCO_TMR_MASK     = 0x3ff,
+};
+
+typedef struct TCOIORegs {
+    struct {
+        uint16_t rld;
+        uint8_t din;
+        uint8_t dout;
+        uint16_t sts1;
+        uint16_t sts2;
+        uint16_t cnt1;
+        uint16_t cnt2;
+        uint8_t msg1;
+        uint8_t msg2;
+        uint8_t wdcnt;
+        uint16_t tmr;
+    } tco;
+    uint8_t sw_irq_gen;
+
+    QEMUTimer *tco_timer;
+    int64_t expire_time;
+    uint8_t timeouts_no;
+
+    MemoryRegion io;
+} TCOIORegs;
+
+/* tco.c */
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent);
+
+extern const VMStateDescription vmstate_tco_io_sts;
+
+#endif /* HW_ACPI_TCO_H */
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 6379901..2aec9cb 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -99,7 +99,8 @@ struct MachineClass {
         no_floppy:1,
         no_cdrom:1,
         no_sdcard:1,
-        has_dynamic_sysbus:1;
+        has_dynamic_sysbus:1,
+        no_tco:1;
     int is_default;
     const char *default_machine_opts;
     const char *default_boot_order;
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index a2cc15c..80a5653 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -17,9 +17,12 @@
 void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
 int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx);
 PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
-void ich9_lpc_pm_init(PCIDevice *pci_lpc);
+void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool enable_tco);
 I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
 
+void ich9_generate_smi(void);
+void ich9_generate_nmi(void);
+
 #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
 
 #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
@@ -162,6 +165,8 @@ Object *ich9_lpc_find(void);
 #define ICH9_LPC_RCBA_BA_MASK                   Q35_MASK(32, 31, 14)
 #define ICH9_LPC_RCBA_EN                        0x1
 #define ICH9_LPC_RCBA_DEFAULT                   0x0
+#define ICH9_LPC_RCBA_GCS                       0x3410
+#define ICH9_LPC_RCBA_GCS_NO_REBOOT             (1 << 5)
 
 #define ICH9_LPC_PIC_NUM_PINS                   16
 #define ICH9_LPC_IOAPIC_NUM_PINS                24
@@ -186,7 +191,10 @@ Object *ich9_lpc_find(void);
 #define ICH9_PMIO_GPE0_LEN                      16
 #define ICH9_PMIO_SMI_EN                        0x30
 #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
+#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
 #define ICH9_PMIO_SMI_STS                       0x34
+#define ICH9_PMIO_TCO_RLD                       0x60
+#define ICH9_PMIO_TCO_LEN                       32
 
 /* FADT ACPI_ENABLE/ACPI_DISABLE */
 #define ICH9_APM_ACPI_ENABLE                    0x2
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 86c5651..c1afdc0 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -89,6 +89,7 @@ typedef struct PcPciInfo {
 #define ACPI_PM_PROP_PM_IO_BASE "pm_io_base"
 #define ACPI_PM_PROP_GPE0_BLK "gpe0_blk"
 #define ACPI_PM_PROP_GPE0_BLK_LEN "gpe0_blk_len"
+#define ACPI_PM_PROP_TCO_ENABLED "enable_tco"
 
 struct PcGuestInfo {
     bool isapc_ram_fw;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-22 23:10 ` [Qemu-devel] [PATCH v5 " Paulo Alcantara
@ 2015-06-22 23:10   ` Paulo Alcantara
  2015-06-23 14:29     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
                       ` (2 more replies)
  2015-06-22 23:10   ` [Qemu-devel] [PATCH v5 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
  2015-06-23  6:39   ` [Qemu-devel] [PATCH v5 1/3] ich9: add TCO interface emulation Michael S. Tsirkin
  2 siblings, 3 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22 23:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

This block is mapped into memory space, using the Root Complex Base
Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this space
must be limited to 32-(DW) bit quantities. Burst accesses are not
allowed.

All Chipset Configuration Registers are located in this 16KiB space.

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * s/PDRC/CCR/ for clarity and match ICH9 spec
  * remove unnecessary OperationRegion for RCRB

v2 -> v3: (no changes)

v3 -> v4:
  * quote RCRB description from ICH9 spec to commit log
  * fix indentation issue in _CRS() method declaration
  * create hw/i386/ich9-cc.h for chipset configuration register values
    and use them in ASL

v4 -> v5:
  * prefix macros in ich9-cc.h with "ICH9_" for better readability and
    make use of them in CCR device definition
---
 hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
 include/hw/i386/ich9-cc.h     |  15 +++++++++++++++
 tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
 3 files changed, 31 insertions(+)
 create mode 100644 include/hw/i386/ich9-cc.h

diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
index 16eaca3..8f4bb6a 100644
--- a/hw/i386/q35-acpi-dsdt.dsl
+++ b/hw/i386/q35-acpi-dsdt.dsl
@@ -114,6 +114,22 @@ DefinitionBlock (
         }
     }
 
+#include "hw/i386/ich9-cc.h"
+
+/****************************************************************
+ * Chipset Configuration Registers
+ ****************************************************************/
+Scope(\_SB.PCI0) {
+    Device (CCR) {
+        Name (_HID, EISAID("PNP0C02"))
+        Name (_UID, 1)
+
+        Name (_CRS, ResourceTemplate() {
+            Memory32Fixed(ReadWrite, ICH9_RCBA_BASE_ADDR, ICH9_RCRB_SIZE)
+        })
+    }
+}
+
 #include "acpi-dsdt-hpet.dsl"
 
 
diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
new file mode 100644
index 0000000..d4918ff
--- /dev/null
+++ b/include/hw/i386/ich9-cc.h
@@ -0,0 +1,15 @@
+/*
+ * QEMU ICH9 Chipset Configuration Registers
+ *
+ * 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.
+ */
+#ifndef HW_ICH9_CC_H
+#define HW_ICH9_CC_H
+
+#define ICH9_RCBA_BASE_ADDR    0xfed1c000
+#define ICH9_RCRB_SIZE         0x00004000
+
+#endif /* HW_ICH9_CC_H */
diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49 100644
GIT binary patch
delta 81
zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w

delta 24
gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs

-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v5 3/3] tests: add testcase for TCO watchdog emulation
  2015-06-22 23:10 ` [Qemu-devel] [PATCH v5 " Paulo Alcantara
  2015-06-22 23:10   ` [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
@ 2015-06-22 23:10   ` Paulo Alcantara
  2015-06-23  6:39   ` [Qemu-devel] [PATCH v5 1/3] ich9: add TCO interface emulation Michael S. Tsirkin
  2 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-22 23:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

This patch adds a testcase that covers the following:
  1) TCO default values
  2) first and second TCO timeout
  3) watch and validate ticks counter through TCO_RLD register
  4) maximum supported TCO timeout (0x3ff)
  5) watchdog actions (pause/reset/shutdown/none) upon second TCO
     timeout
  6) set and get of TCO control and status bits

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * some cleanup
  * add test for TCO_LOCK bit

v2 -> v3:
  * add tests for TCO control & status bits
  * fix check of SECOND_TO_STS bit (it's set in TCO2_STS reg)

v3 -> v4:
  * add more description to commit log
  * use RCBA_BASE_ADDR macro defintion from hw/i386/ich9-cc.h instead

v4 -> v5:
  * use modified macros (now prefixed with ICH9_) from ich9-cc.h
  * move license to GPLv2+
---
 tests/Makefile   |   2 +
 tests/tco-test.c | 460 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 462 insertions(+)
 create mode 100644 tests/tco-test.c

diff --git a/tests/Makefile b/tests/Makefile
index eff5e11..ef1e981 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -152,6 +152,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
 check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
+check-qtest-i386-y += tests/tco-test$(EXESUF)
 gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
 check-qtest-i386-y += $(check-qtest-pci-y)
 gcov-files-i386-y += $(gcov-files-pci-y)
@@ -370,6 +371,7 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
 tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
 tests/ne2000-test$(EXESUF): tests/ne2000-test.o
 tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
+tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
 tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
 tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
 tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
diff --git a/tests/tco-test.c b/tests/tco-test.c
new file mode 100644
index 0000000..79a673e
--- /dev/null
+++ b/tests/tco-test.c
@@ -0,0 +1,460 @@
+/*
+ * 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.
+ */
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/i386/ich9.h"
+#include "hw/i386/ich9-cc.h"
+#include "hw/acpi/ich9.h"
+#include "hw/acpi/tco.h"
+
+#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;
+    QPCIDevice *dev;
+    void *lpc_base;
+    void *tco_io_base;
+} TestData;
+
+static void test_init(TestData *d)
+{
+    QPCIBus *bus;
+    QTestState *qs;
+    char *s;
+
+    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
+    qs = qtest_start(s);
+    qtest_irq_intercept_in(qs, "ioapic");
+    g_free(s);
+
+    bus = qpci_init_pc();
+    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
+    g_assert(d->dev != NULL);
+
+    /* map PCI-to-LPC bridge interface BAR */
+    d->lpc_base = qpci_iomap(d->dev, 0, NULL);
+
+    qpci_device_enable(d->dev);
+
+    g_assert(d->lpc_base != NULL);
+
+    /* set ACPI PM I/O space base address */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_PMBASE,
+                       PM_IO_BASE_ADDR | 0x1);
+    /* enable ACPI I/O */
+    qpci_config_writeb(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_ACPI_CTRL,
+                       0x80);
+    /* set Root Complex BAR */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_RCBA,
+                       ICH9_RCBA_BASE_ADDR | 0x1);
+
+    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
+}
+
+static void stop_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val |= TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void start_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val &= ~TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void load_tco(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
+}
+
+static void set_tco_timeout(const TestData *d, uint16_t ticks)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
+}
+
+static void clear_tco_status(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
+}
+
+static void reset_on_second_timeout(bool enable)
+{
+    uint32_t val;
+
+    val = readl(ICH9_RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS);
+    if (enable) {
+        val &= ~ICH9_LPC_RCBA_GCS_NO_REBOOT;
+    } else {
+        val |= ICH9_LPC_RCBA_GCS_NO_REBOOT;
+    }
+    writel(ICH9_RCBA_BASE_ADDR + ICH9_LPC_RCBA_GCS, val);
+}
+
+static void test_tco_defaults(void)
+{
+    TestData d;
+
+    d.args = NULL;
+    test_init(&d);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+                    TCO_RLD_DEFAULT);
+    /* TCO_DAT_IN & TCO_DAT_OUT */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
+                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
+    /* TCO1_STS & TCO2_STS */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
+                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
+    /* TCO1_CNT & TCO2_CNT */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
+    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
+                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
+                    TCO_WDCNT_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
+                    SW_IRQ_GEN_DEFAULT);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
+                    TCO_TMR_DEFAULT);
+    qtest_end();
+}
+
+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;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    /* test first timeout */
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    /* test clearing timeout bit */
+    val |= TCO_TIMEOUT;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+
+    /* test second timeout */
+    clock_step(ticks * TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco_max_timeout(void)
+{
+    TestData d;
+    const uint16_t ticks = 0xffff;
+    uint32_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
+    g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+    clock_step(TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static QDict *get_watchdog_action(void)
+{
+    QDict *ev = qmp("");
+    QDict *data;
+    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
+
+    data = qdict_get_qdict(ev, "data");
+    QINCREF(data);
+    QDECREF(ev);
+    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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_ticks_counter(void)
+{
+    TestData d;
+    uint16_t ticks = TCO_SECS_TO_TICKS(8);
+    uint16_t rld;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+
+    do {
+        rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
+        g_assert_cmpint(rld, ==, ticks);
+        clock_step(TCO_TICK_NSEC);
+        ticks--;
+    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco1_control_bits(void)
+{
+    TestData d;
+    uint16_t val;
+
+    d.args = NULL;
+    test_init(&d);
+
+    val = TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    val &= ~TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    TCO_LOCK);
+    qtest_end();
+}
+
+static void test_tco1_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0);
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0);
+    qtest_end();
+}
+
+static void test_tco2_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = "-watchdog-action none";
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0);
+    qtest_end();
+}
+
+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();
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v5 1/3] ich9: add TCO interface emulation
  2015-06-22 23:10 ` [Qemu-devel] [PATCH v5 " Paulo Alcantara
  2015-06-22 23:10   ` [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
  2015-06-22 23:10   ` [Qemu-devel] [PATCH v5 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
@ 2015-06-23  6:39   ` Michael S. Tsirkin
  2 siblings, 0 replies; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-23  6:39 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Mon, Jun 22, 2015 at 08:10:27PM -0300, Paulo Alcantara wrote:
> This interface provides some registers within a 32-byte range and can be
> acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).
> 
> It's commonly used as a watchdog timer to detect system lockups through
> SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
> NO_REBOOT bit is not set in GCS (General Control and Status register),
> the system will be resetted upon second timeout if TCO_RLD register
> wasn't previously written to prevent timeout.
> 
> This patch adds support to TCO watchdog logic and few other features
> like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
> etc. are not implemented yet.
> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>

One useful feature to implement could be ability to set the NO_REBOOT
strapping pin to 1. And maybe it's even a better default - will have
to experiment with this feature some more.
Does not have to block this patchset, can be done by a patch on top.

-- 
MST

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-22  0:37   ` [Qemu-devel] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
  2015-06-22  8:40     ` Michael S. Tsirkin
@ 2015-06-23 10:38     ` Igor Mammedov
  2015-06-23 10:58       ` Michael S. Tsirkin
                         ` (3 more replies)
  1 sibling, 4 replies; 83+ messages in thread
From: Igor Mammedov @ 2015-06-23 10:38 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, qemu-devel, mst

On Sun, 21 Jun 2015 21:37:02 -0300
Paulo Alcantara <pcacjr@gmail.com> wrote:

> This block is mapped into memory space, using the Root Complex Base
> Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this space
> must be limited to 32-(DW) bit quantities. Burst accesses are not
> allowed.
> 
> All Chipset Configuration Registers are located in this 16KiB space.
> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
> v1 -> v2:
>   * s/PDRC/CCR/ for clarity and match ICH9 spec
>   * remove unnecessary OperationRegion for RCRB
> 
> v2 -> v3: (no changes)
> 
> v3 -> v4:
>   * quote RCRB description from ICH9 spec to commit log
>   * fix indentation issue in _CRS() method declaration
>   * create hw/i386/ich9-cc.h for chipset configuration register values
>     and use them in ASL
> ---
>  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
>  include/hw/i386/ich9-cc.h     |  31 +++++++++++++++++++++++++++++++
>  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
>  3 files changed, 47 insertions(+)
>  create mode 100644 include/hw/i386/ich9-cc.h
> 
> diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> index 16eaca3..512c220 100644
> --- a/hw/i386/q35-acpi-dsdt.dsl
> +++ b/hw/i386/q35-acpi-dsdt.dsl
> @@ -114,6 +114,22 @@ DefinitionBlock (
>          }
>      }
>  
> +#include "hw/i386/ich9-cc.h"
> +
> +/****************************************************************
> + * Chipset Configuration Registers
> + ****************************************************************/
> +Scope(\_SB.PCI0) {
> +    Device (CCR) {
I'd name it something like: TCOW

> +        Name (_HID, EISAID("PNP0C02"))
PNP0C02 not in ACPI spec, pls use PNP0A06 like we do in other places
for reserving IO space.

> +        Name (_UID, 1)
s/1/"TCO watchdog resources"/
> +
> +        Name (_CRS, ResourceTemplate() {
> +            Memory32Fixed(ReadWrite, RCBA_BASE_ADDR, RCRB_SIZE)
since this values are dynamically programmed by BIOS
it's incorrect to put them here statically.
Take RCBA_BASE_ADDR from respective ICH9 register,
which should be programmed by BIOS before ACPI tables are read by it.

Also like Michael suggested create this device dynamically in build_ssdt(),
for example look at "if (misc->applesmc_io_base) {" block in
hw/i386/acpi-build.c and add this device only if it's present
(i.e. for version from which it's supported)

> +        })
> +    }
> +}
> +
>  #include "acpi-dsdt-hpet.dsl"
>  
>  
> diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> new file mode 100644
> index 0000000..675fb7f
> --- /dev/null
> +++ b/include/hw/i386/ich9-cc.h
> @@ -0,0 +1,31 @@
> +/*
> + * QEMU ICH9 Chipset Configuration Registers
> + *
> + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#ifndef HW_ICH9_CC_H
> +#define HW_ICH9_CC_H
> +
> +#define RCBA_BASE_ADDR    0xfed1c000
where does this value come from?

> +#define RCRB_SIZE         0x00004000
> +
> +#endif /* HW_ICH9_CC_H */
> diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
> index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49 100644
> GIT binary patch
> delta 81
> zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> 
> delta 24
> gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-23 10:38     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
@ 2015-06-23 10:58       ` Michael S. Tsirkin
  2015-06-23 12:29         ` Igor Mammedov
  2015-06-23 11:15       ` Paolo Bonzini
                         ` (2 subsequent siblings)
  3 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-23 10:58 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Tue, Jun 23, 2015 at 12:38:59PM +0200, Igor Mammedov wrote:
> On Sun, 21 Jun 2015 21:37:02 -0300
> Paulo Alcantara <pcacjr@gmail.com> wrote:
> 
> > This block is mapped into memory space, using the Root Complex Base
> > Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this space
> > must be limited to 32-(DW) bit quantities. Burst accesses are not
> > allowed.
> > 
> > All Chipset Configuration Registers are located in this 16KiB space.
> > 
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > ---
> > v1 -> v2:
> >   * s/PDRC/CCR/ for clarity and match ICH9 spec
> >   * remove unnecessary OperationRegion for RCRB
> > 
> > v2 -> v3: (no changes)
> > 
> > v3 -> v4:
> >   * quote RCRB description from ICH9 spec to commit log
> >   * fix indentation issue in _CRS() method declaration
> >   * create hw/i386/ich9-cc.h for chipset configuration register values
> >     and use them in ASL
> > ---
> >  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
> >  include/hw/i386/ich9-cc.h     |  31 +++++++++++++++++++++++++++++++
> >  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
> >  3 files changed, 47 insertions(+)
> >  create mode 100644 include/hw/i386/ich9-cc.h
> > 
> > diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> > index 16eaca3..512c220 100644
> > --- a/hw/i386/q35-acpi-dsdt.dsl
> > +++ b/hw/i386/q35-acpi-dsdt.dsl
> > @@ -114,6 +114,22 @@ DefinitionBlock (
> >          }
> >      }
> >  
> > +#include "hw/i386/ich9-cc.h"
> > +
> > +/****************************************************************
> > + * Chipset Configuration Registers
> > + ****************************************************************/
> > +Scope(\_SB.PCI0) {
> > +    Device (CCR) {
> I'd name it something like: TCOW
> 
> > +        Name (_HID, EISAID("PNP0C02"))
> PNP0C02 not in ACPI spec, pls use PNP0A06 like we do in other places
> for reserving IO space.
> 
> > +        Name (_UID, 1)
> s/1/"TCO watchdog resources"/

I think we switched all UID values to integers, you can
look it up in git history.  Let's keep this consistent?

> > +
> > +        Name (_CRS, ResourceTemplate() {
> > +            Memory32Fixed(ReadWrite, RCBA_BASE_ADDR, RCRB_SIZE)
> since this values are dynamically programmed by BIOS
> it's incorrect to put them here statically.
> Take RCBA_BASE_ADDR from respective ICH9 register,
> which should be programmed by BIOS before ACPI tables are read by it.
> 
> Also like Michael suggested create this device dynamically in build_ssdt(),
> for example look at "if (misc->applesmc_io_base) {" block in
> hw/i386/acpi-build.c and add this device only if it's present
> (i.e. for version from which it's supported)
> 
> > +        })
> > +    }
> > +}
> > +
> >  #include "acpi-dsdt-hpet.dsl"
> >  
> >  
> > diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> > new file mode 100644
> > index 0000000..675fb7f
> > --- /dev/null
> > +++ b/include/hw/i386/ich9-cc.h
> > @@ -0,0 +1,31 @@
> > +/*
> > + * QEMU ICH9 Chipset Configuration Registers
> > + *
> > + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a copy
> > + * of this software and associated documentation files (the "Software"), to deal
> > + * in the Software without restriction, including without limitation the rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> > + * copies of the Software, and to permit persons to whom the Software is
> > + * furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice shall be included in
> > + * all copies or substantial portions of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> > + * THE SOFTWARE.
> > + */
> > +
> > +#ifndef HW_ICH9_CC_H
> > +#define HW_ICH9_CC_H
> > +
> > +#define RCBA_BASE_ADDR    0xfed1c000
> where does this value come from?
> 
> > +#define RCRB_SIZE         0x00004000
> > +
> > +#endif /* HW_ICH9_CC_H */
> > diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
> > index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49 100644
> > GIT binary patch
> > delta 81
> > zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> > lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> > 
> > delta 24
> > gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> > 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-23 10:38     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
  2015-06-23 10:58       ` Michael S. Tsirkin
@ 2015-06-23 11:15       ` Paolo Bonzini
  2015-06-23 11:18       ` Paolo Bonzini
  2015-06-23 12:22       ` Michael S. Tsirkin
  3 siblings, 0 replies; 83+ messages in thread
From: Paolo Bonzini @ 2015-06-23 11:15 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: seabios, Paulo Alcantara, qemu-devel, mst



----- Original Message -----
> From: "Igor Mammedov" <imammedo@redhat.com>
> To: "Paulo Alcantara" <pcacjr@gmail.com>
> Cc: qemu-devel@nongnu.org, pbonzini@redhat.com, seabios@seabios.org, mst@redhat.com
> Sent: Tuesday, June 23, 2015 12:38:59 PM
> Subject: Re: [SeaBIOS] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
> 
> On Sun, 21 Jun 2015 21:37:02 -0300
> Paulo Alcantara <pcacjr@gmail.com> wrote:
> 
> > This block is mapped into memory space, using the Root Complex Base
> > Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this space
> > must be limited to 32-(DW) bit quantities. Burst accesses are not
> > allowed.
> > 
> > All Chipset Configuration Registers are located in this 16KiB space.
> > 
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > ---
> > v1 -> v2:
> >   * s/PDRC/CCR/ for clarity and match ICH9 spec
> >   * remove unnecessary OperationRegion for RCRB
> > 
> > v2 -> v3: (no changes)
> > 
> > v3 -> v4:
> >   * quote RCRB description from ICH9 spec to commit log
> >   * fix indentation issue in _CRS() method declaration
> >   * create hw/i386/ich9-cc.h for chipset configuration register values
> >     and use them in ASL
> > ---
> >  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
> >  include/hw/i386/ich9-cc.h     |  31 +++++++++++++++++++++++++++++++
> >  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
> >  3 files changed, 47 insertions(+)
> >  create mode 100644 include/hw/i386/ich9-cc.h
> > 
> > diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> > index 16eaca3..512c220 100644
> > --- a/hw/i386/q35-acpi-dsdt.dsl
> > +++ b/hw/i386/q35-acpi-dsdt.dsl
> > @@ -114,6 +114,22 @@ DefinitionBlock (
> >          }
> >      }
> >  
> > +#include "hw/i386/ich9-cc.h"
> > +
> > +/****************************************************************
> > + * Chipset Configuration Registers
> > + ****************************************************************/
> > +Scope(\_SB.PCI0) {
> > +    Device (CCR) {
> I'd name it something like: TCOW

Guys, can you *please* read the reviews to v1/2/3 and even to v4
itself?!?!?

Paolo

> > +        Name (_HID, EISAID("PNP0C02"))
> PNP0C02 not in ACPI spec, pls use PNP0A06 like we do in other places
> for reserving IO space.
> 
> > +        Name (_UID, 1)
> s/1/"TCO watchdog resources"/
> > +
> > +        Name (_CRS, ResourceTemplate() {
> > +            Memory32Fixed(ReadWrite, RCBA_BASE_ADDR, RCRB_SIZE)
> since this values are dynamically programmed by BIOS
> it's incorrect to put them here statically.
> Take RCBA_BASE_ADDR from respective ICH9 register,
> which should be programmed by BIOS before ACPI tables are read by it.
> 
> Also like Michael suggested create this device dynamically in build_ssdt(),
> for example look at "if (misc->applesmc_io_base) {" block in
> hw/i386/acpi-build.c and add this device only if it's present
> (i.e. for version from which it's supported)
> 
> > +        })
> > +    }
> > +}
> > +
> >  #include "acpi-dsdt-hpet.dsl"
> >  
> >  
> > diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> > new file mode 100644
> > index 0000000..675fb7f
> > --- /dev/null
> > +++ b/include/hw/i386/ich9-cc.h
> > @@ -0,0 +1,31 @@
> > +/*
> > + * QEMU ICH9 Chipset Configuration Registers
> > + *
> > + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > copy
> > + * of this software and associated documentation files (the "Software"),
> > to deal
> > + * in the Software without restriction, including without limitation the
> > rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
> > sell
> > + * copies of the Software, and to permit persons to whom the Software is
> > + * furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice shall be included
> > in
> > + * all copies or substantial portions of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> > OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> > OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > FROM,
> > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > IN
> > + * THE SOFTWARE.
> > + */
> > +
> > +#ifndef HW_ICH9_CC_H
> > +#define HW_ICH9_CC_H
> > +
> > +#define RCBA_BASE_ADDR    0xfed1c000
> where does this value come from?
> 
> > +#define RCRB_SIZE         0x00004000
> > +
> > +#endif /* HW_ICH9_CC_H */
> > diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
> > index
> > 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49
> > 100644
> > GIT binary patch
> > delta 81
> > zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> > lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> > 
> > delta 24
> > gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> > 
> 
> 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-23 10:38     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
  2015-06-23 10:58       ` Michael S. Tsirkin
  2015-06-23 11:15       ` Paolo Bonzini
@ 2015-06-23 11:18       ` Paolo Bonzini
  2015-06-23 12:22       ` Michael S. Tsirkin
  3 siblings, 0 replies; 83+ messages in thread
From: Paolo Bonzini @ 2015-06-23 11:18 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: seabios, Paulo Alcantara, qemu-devel, mst



----- Original Message -----
> From: "Igor Mammedov" <imammedo@redhat.com>
> To: "Paulo Alcantara" <pcacjr@gmail.com>
> Cc: qemu-devel@nongnu.org, pbonzini@redhat.com, seabios@seabios.org, mst@redhat.com
> Sent: Tuesday, June 23, 2015 12:38:59 PM
> Subject: Re: [SeaBIOS] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
> 
> On Sun, 21 Jun 2015 21:37:02 -0300
> Paulo Alcantara <pcacjr@gmail.com> wrote:
> 
> > This block is mapped into memory space, using the Root Complex Base
> > Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this space
> > must be limited to 32-(DW) bit quantities. Burst accesses are not
> > allowed.
> > 
> > All Chipset Configuration Registers are located in this 16KiB space.
> > 
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > ---
> > v1 -> v2:
> >   * s/PDRC/CCR/ for clarity and match ICH9 spec
> >   * remove unnecessary OperationRegion for RCRB
> > 
> > v2 -> v3: (no changes)
> > 
> > v3 -> v4:
> >   * quote RCRB description from ICH9 spec to commit log
> >   * fix indentation issue in _CRS() method declaration
> >   * create hw/i386/ich9-cc.h for chipset configuration register values
> >     and use them in ASL
> > ---
> >  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
> >  include/hw/i386/ich9-cc.h     |  31 +++++++++++++++++++++++++++++++
> >  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
> >  3 files changed, 47 insertions(+)
> >  create mode 100644 include/hw/i386/ich9-cc.h
> > 
> > diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> > index 16eaca3..512c220 100644
> > --- a/hw/i386/q35-acpi-dsdt.dsl
> > +++ b/hw/i386/q35-acpi-dsdt.dsl
> > @@ -114,6 +114,22 @@ DefinitionBlock (
> >          }
> >      }
> >  
> > +#include "hw/i386/ich9-cc.h"
> > +
> > +/****************************************************************
> > + * Chipset Configuration Registers
> > + ****************************************************************/
> > +Scope(\_SB.PCI0) {
> > +    Device (CCR) {
> I'd name it something like: TCOW
> 
> > +        Name (_HID, EISAID("PNP0C02"))
> PNP0C02 not in ACPI spec, pls use PNP0A06 like we do in other places
> for reserving IO space.

The ACPI spec says "Note: Plug and Play IDs that are not defined by the ACPI
specification are defined and described in the following document [URL]"
where PNP0C02 is the "General ID for reserving resources required by Plug
and Play system board registers (not specific to a particular device)".

PNP0A06 is the "ACPI bus", whatever that means.

Paolo

> > +        Name (_UID, 1)
> s/1/"TCO watchdog resources"/
> > +
> > +        Name (_CRS, ResourceTemplate() {
> > +            Memory32Fixed(ReadWrite, RCBA_BASE_ADDR, RCRB_SIZE)
> since this values are dynamically programmed by BIOS
> it's incorrect to put them here statically.
> Take RCBA_BASE_ADDR from respective ICH9 register,
> which should be programmed by BIOS before ACPI tables are read by it.
> 
> Also like Michael suggested create this device dynamically in build_ssdt(),
> for example look at "if (misc->applesmc_io_base) {" block in
> hw/i386/acpi-build.c and add this device only if it's present
> (i.e. for version from which it's supported)
> 
> > +        })
> > +    }
> > +}
> > +
> >  #include "acpi-dsdt-hpet.dsl"
> >  
> >  
> > diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> > new file mode 100644
> > index 0000000..675fb7f
> > --- /dev/null
> > +++ b/include/hw/i386/ich9-cc.h
> > @@ -0,0 +1,31 @@
> > +/*
> > + * QEMU ICH9 Chipset Configuration Registers
> > + *
> > + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > copy
> > + * of this software and associated documentation files (the "Software"),
> > to deal
> > + * in the Software without restriction, including without limitation the
> > rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or
> > sell
> > + * copies of the Software, and to permit persons to whom the Software is
> > + * furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice shall be included
> > in
> > + * all copies or substantial portions of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> > OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
> > OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > FROM,
> > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > IN
> > + * THE SOFTWARE.
> > + */
> > +
> > +#ifndef HW_ICH9_CC_H
> > +#define HW_ICH9_CC_H
> > +
> > +#define RCBA_BASE_ADDR    0xfed1c000
> where does this value come from?
> 
> > +#define RCRB_SIZE         0x00004000
> > +
> > +#endif /* HW_ICH9_CC_H */
> > diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
> > index
> > 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49
> > 100644
> > GIT binary patch
> > delta 81
> > zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> > lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> > 
> > delta 24
> > gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> > 
> 
> 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-23 10:38     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
                         ` (2 preceding siblings ...)
  2015-06-23 11:18       ` Paolo Bonzini
@ 2015-06-23 12:22       ` Michael S. Tsirkin
  3 siblings, 0 replies; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-23 12:22 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Tue, Jun 23, 2015 at 12:38:59PM +0200, Igor Mammedov wrote:
 
> > +        Name (_UID, 1)
> s/1/"TCO watchdog resources"/
> > +
> > +        Name (_CRS, ResourceTemplate() {
> > +            Memory32Fixed(ReadWrite, RCBA_BASE_ADDR, RCRB_SIZE)
> since this values are dynamically programmed by BIOS
> it's incorrect to put them here statically.
> Take RCBA_BASE_ADDR from respective ICH9 register,
> which should be programmed by BIOS before ACPI tables are read by it.


Hmm this is true, isn't it?

static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rbca_old)
{
    uint32_t rbca = pci_get_long(lpc->d.config + ICH9_LPC_RCBA);

    if (rbca_old & ICH9_LPC_RCBA_EN) {
            memory_region_del_subregion(get_system_memory(), &lpc->rbca_mem);
    }
    if (rbca & ICH9_LPC_RCBA_EN) {
            memory_region_add_subregion_overlap(get_system_memory(),
                                                rbca & ICH9_LPC_RCBA_BA_MASK,
                                                &lpc->rbca_mem, 1);
    }
}

So the RBCA base must come from device config,
and in particular, this means it must be in the
SSDT since the DSDT is static.

We already have aml_memory32_fixed, e.g.

                if (misc->tpm_version != TPM_VERSION_UNSPEC) {
                    dev = aml_device("ISA.TPM");
                    aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31")));
                    aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
                    crs = aml_resource_template();
                    aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE,
                               TPM_TIS_ADDR_SIZE, AML_READ_WRITE));
                    aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ));
                    aml_append(dev, aml_name_decl("_CRS", crs));
                    aml_append(scope, dev);
                }

so it won't be hard to do.

-- 
MST

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-23 10:58       ` Michael S. Tsirkin
@ 2015-06-23 12:29         ` Igor Mammedov
  2015-06-23 12:37           ` Michael S. Tsirkin
  0 siblings, 1 reply; 83+ messages in thread
From: Igor Mammedov @ 2015-06-23 12:29 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel

On Tue, 23 Jun 2015 12:58:13 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Tue, Jun 23, 2015 at 12:38:59PM +0200, Igor Mammedov wrote:
> > On Sun, 21 Jun 2015 21:37:02 -0300
> > Paulo Alcantara <pcacjr@gmail.com> wrote:
> > 
> > > This block is mapped into memory space, using the Root Complex
> > > Base Address (RCBA) register of the PCI-to-LPC bridge. Accesses
> > > in this space must be limited to 32-(DW) bit quantities. Burst
> > > accesses are not allowed.
> > > 
> > > All Chipset Configuration Registers are located in this 16KiB
> > > space.
> > > 
> > > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > > ---
> > > v1 -> v2:
> > >   * s/PDRC/CCR/ for clarity and match ICH9 spec
> > >   * remove unnecessary OperationRegion for RCRB
> > > 
> > > v2 -> v3: (no changes)
> > > 
> > > v3 -> v4:
> > >   * quote RCRB description from ICH9 spec to commit log
> > >   * fix indentation issue in _CRS() method declaration
> > >   * create hw/i386/ich9-cc.h for chipset configuration register
> > > values and use them in ASL
> > > ---
> > >  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
> > >  include/hw/i386/ich9-cc.h     |  31
> > > +++++++++++++++++++++++++++++++ tests/acpi-test-data/q35/DSDT |
> > > Bin 7666 -> 7723 bytes 3 files changed, 47 insertions(+)
> > >  create mode 100644 include/hw/i386/ich9-cc.h
> > > 
> > > diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> > > index 16eaca3..512c220 100644
> > > --- a/hw/i386/q35-acpi-dsdt.dsl
> > > +++ b/hw/i386/q35-acpi-dsdt.dsl
> > > @@ -114,6 +114,22 @@ DefinitionBlock (
> > >          }
> > >      }
> > >  
> > > +#include "hw/i386/ich9-cc.h"
> > > +
> > > +/****************************************************************
> > > + * Chipset Configuration Registers
> > > +
> > > ****************************************************************/
> > > +Scope(\_SB.PCI0) {
> > > +    Device (CCR) {
> > I'd name it something like: TCOW
> > 
> > > +        Name (_HID, EISAID("PNP0C02"))
> > PNP0C02 not in ACPI spec, pls use PNP0A06 like we do in other places
> > for reserving IO space.
> > 
> > > +        Name (_UID, 1)
> > s/1/"TCO watchdog resources"/
> 
> I think we switched all UID values to integers, you can
> look it up in git history.  Let's keep this consistent?
in ssdt for container devices we use strings which says
what container is for.
so using description string is consistent with what we are doing now.


With numbers it's easy to create UID collision, which makes some Windows
versions BSOD.

as side effect:
It makes container device self describing if one looks in
device-manager.
> 
> > > +
> > > +        Name (_CRS, ResourceTemplate() {
> > > +            Memory32Fixed(ReadWrite, RCBA_BASE_ADDR, RCRB_SIZE)
> > since this values are dynamically programmed by BIOS
> > it's incorrect to put them here statically.
> > Take RCBA_BASE_ADDR from respective ICH9 register,
> > which should be programmed by BIOS before ACPI tables are read by
> > it.
> > 
> > Also like Michael suggested create this device dynamically in
> > build_ssdt(), for example look at "if (misc->applesmc_io_base) {"
> > block in hw/i386/acpi-build.c and add this device only if it's
> > present (i.e. for version from which it's supported)
> > 
> > > +        })
> > > +    }
> > > +}
> > > +
> > >  #include "acpi-dsdt-hpet.dsl"
> > >  
> > >  
> > > diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> > > new file mode 100644
> > > index 0000000..675fb7f
> > > --- /dev/null
> > > +++ b/include/hw/i386/ich9-cc.h
> > > @@ -0,0 +1,31 @@
> > > +/*
> > > + * QEMU ICH9 Chipset Configuration Registers
> > > + *
> > > + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> > > + *
> > > + * Permission is hereby granted, free of charge, to any person
> > > obtaining a copy
> > > + * of this software and associated documentation files (the
> > > "Software"), to deal
> > > + * in the Software without restriction, including without
> > > limitation the rights
> > > + * to use, copy, modify, merge, publish, distribute, sublicense,
> > > and/or sell
> > > + * copies of the Software, and to permit persons to whom the
> > > Software is
> > > + * furnished to do so, subject to the following conditions:
> > > + *
> > > + * The above copyright notice and this permission notice shall
> > > be included in
> > > + * all copies or substantial portions of the Software.
> > > + *
> > > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
> > > KIND, EXPRESS OR
> > > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > > MERCHANTABILITY,
> > > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
> > > EVENT SHALL
> > > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
> > > DAMAGES OR OTHER
> > > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
> > > OTHERWISE, ARISING FROM,
> > > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > > DEALINGS IN
> > > + * THE SOFTWARE.
> > > + */
> > > +
> > > +#ifndef HW_ICH9_CC_H
> > > +#define HW_ICH9_CC_H
> > > +
> > > +#define RCBA_BASE_ADDR    0xfed1c000
> > where does this value come from?
> > 
> > > +#define RCRB_SIZE         0x00004000
> > > +
> > > +#endif /* HW_ICH9_CC_H */
> > > diff --git a/tests/acpi-test-data/q35/DSDT
> > > b/tests/acpi-test-data/q35/DSDT index
> > > 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49
> > > 100644 GIT binary patch delta 81
> > > zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> > > lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> > > 
> > > delta 24
> > > gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> > > 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-23 12:29         ` Igor Mammedov
@ 2015-06-23 12:37           ` Michael S. Tsirkin
  0 siblings, 0 replies; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-23 12:37 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, lersek

On Tue, Jun 23, 2015 at 02:29:37PM +0200, Igor Mammedov wrote:
> On Tue, 23 Jun 2015 12:58:13 +0200
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> > On Tue, Jun 23, 2015 at 12:38:59PM +0200, Igor Mammedov wrote:
> > > On Sun, 21 Jun 2015 21:37:02 -0300
> > > Paulo Alcantara <pcacjr@gmail.com> wrote:
> > > 
> > > > This block is mapped into memory space, using the Root Complex
> > > > Base Address (RCBA) register of the PCI-to-LPC bridge. Accesses
> > > > in this space must be limited to 32-(DW) bit quantities. Burst
> > > > accesses are not allowed.
> > > > 
> > > > All Chipset Configuration Registers are located in this 16KiB
> > > > space.
> > > > 
> > > > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > > > ---
> > > > v1 -> v2:
> > > >   * s/PDRC/CCR/ for clarity and match ICH9 spec
> > > >   * remove unnecessary OperationRegion for RCRB
> > > > 
> > > > v2 -> v3: (no changes)
> > > > 
> > > > v3 -> v4:
> > > >   * quote RCRB description from ICH9 spec to commit log
> > > >   * fix indentation issue in _CRS() method declaration
> > > >   * create hw/i386/ich9-cc.h for chipset configuration register
> > > > values and use them in ASL
> > > > ---
> > > >  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
> > > >  include/hw/i386/ich9-cc.h     |  31
> > > > +++++++++++++++++++++++++++++++ tests/acpi-test-data/q35/DSDT |
> > > > Bin 7666 -> 7723 bytes 3 files changed, 47 insertions(+)
> > > >  create mode 100644 include/hw/i386/ich9-cc.h
> > > > 
> > > > diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> > > > index 16eaca3..512c220 100644
> > > > --- a/hw/i386/q35-acpi-dsdt.dsl
> > > > +++ b/hw/i386/q35-acpi-dsdt.dsl
> > > > @@ -114,6 +114,22 @@ DefinitionBlock (
> > > >          }
> > > >      }
> > > >  
> > > > +#include "hw/i386/ich9-cc.h"
> > > > +
> > > > +/****************************************************************
> > > > + * Chipset Configuration Registers
> > > > +
> > > > ****************************************************************/
> > > > +Scope(\_SB.PCI0) {
> > > > +    Device (CCR) {
> > > I'd name it something like: TCOW
> > > 
> > > > +        Name (_HID, EISAID("PNP0C02"))
> > > PNP0C02 not in ACPI spec, pls use PNP0A06 like we do in other places
> > > for reserving IO space.
> > > 
> > > > +        Name (_UID, 1)
> > > s/1/"TCO watchdog resources"/
> > 
> > I think we switched all UID values to integers, you can
> > look it up in git history.  Let's keep this consistent?
> in ssdt for container devices we use strings which says
> what container is for.
> so using description string is consistent with what we are doing now.
>
> 
> With numbers it's easy to create UID collision, which makes some Windows
> versions BSOD.
> 
> as side effect:
> It makes container device self describing if one looks in
> device-manager.


But see
commit c96d9286a6d70452e5fa4f1e3f840715e325be95
    i386/acpi-build: more traditional _UID and _HID for PXB root buses

ARM also seems to have #s as UIDs for CPUs.

I don't think I'll block this patch on this issue, we
can tweak the UID later easily.


> > 
> > > > +
> > > > +        Name (_CRS, ResourceTemplate() {
> > > > +            Memory32Fixed(ReadWrite, RCBA_BASE_ADDR, RCRB_SIZE)
> > > since this values are dynamically programmed by BIOS
> > > it's incorrect to put them here statically.
> > > Take RCBA_BASE_ADDR from respective ICH9 register,
> > > which should be programmed by BIOS before ACPI tables are read by
> > > it.
> > > 
> > > Also like Michael suggested create this device dynamically in
> > > build_ssdt(), for example look at "if (misc->applesmc_io_base) {"
> > > block in hw/i386/acpi-build.c and add this device only if it's
> > > present (i.e. for version from which it's supported)
> > > 
> > > > +        })
> > > > +    }
> > > > +}
> > > > +
> > > >  #include "acpi-dsdt-hpet.dsl"
> > > >  
> > > >  
> > > > diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> > > > new file mode 100644
> > > > index 0000000..675fb7f
> > > > --- /dev/null
> > > > +++ b/include/hw/i386/ich9-cc.h
> > > > @@ -0,0 +1,31 @@
> > > > +/*
> > > > + * QEMU ICH9 Chipset Configuration Registers
> > > > + *
> > > > + * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
> > > > + *
> > > > + * Permission is hereby granted, free of charge, to any person
> > > > obtaining a copy
> > > > + * of this software and associated documentation files (the
> > > > "Software"), to deal
> > > > + * in the Software without restriction, including without
> > > > limitation the rights
> > > > + * to use, copy, modify, merge, publish, distribute, sublicense,
> > > > and/or sell
> > > > + * copies of the Software, and to permit persons to whom the
> > > > Software is
> > > > + * furnished to do so, subject to the following conditions:
> > > > + *
> > > > + * The above copyright notice and this permission notice shall
> > > > be included in
> > > > + * all copies or substantial portions of the Software.
> > > > + *
> > > > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
> > > > KIND, EXPRESS OR
> > > > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > > > MERCHANTABILITY,
> > > > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
> > > > EVENT SHALL
> > > > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
> > > > DAMAGES OR OTHER
> > > > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
> > > > OTHERWISE, ARISING FROM,
> > > > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > > > DEALINGS IN
> > > > + * THE SOFTWARE.
> > > > + */
> > > > +
> > > > +#ifndef HW_ICH9_CC_H
> > > > +#define HW_ICH9_CC_H
> > > > +
> > > > +#define RCBA_BASE_ADDR    0xfed1c000
> > > where does this value come from?
> > > 
> > > > +#define RCRB_SIZE         0x00004000
> > > > +
> > > > +#endif /* HW_ICH9_CC_H */
> > > > diff --git a/tests/acpi-test-data/q35/DSDT
> > > > b/tests/acpi-test-data/q35/DSDT index
> > > > 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49
> > > > 100644 GIT binary patch delta 81
> > > > zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> > > > lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> > > > 
> > > > delta 24
> > > > gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> > > > 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-22 23:10   ` [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
@ 2015-06-23 14:29     ` Igor Mammedov
  2015-06-23 15:33       ` Michael S. Tsirkin
  2015-06-23 14:39     ` Igor Mammedov
  2015-06-24 15:11     ` [Qemu-devel] " Michael S. Tsirkin
  2 siblings, 1 reply; 83+ messages in thread
From: Igor Mammedov @ 2015-06-23 14:29 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, mst, qemu-devel, kraxel

On Mon, 22 Jun 2015 20:10:28 -0300
Paulo Alcantara <pcacjr@gmail.com> wrote:

> This block is mapped into memory space, using the Root Complex Base
> Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this
> space must be limited to 32-(DW) bit quantities. Burst accesses are
> not allowed.
> 
> All Chipset Configuration Registers are located in this 16KiB space.
Do we really need to describe this block in ASL?
PCI0 bus _CRS doesn't include 0xfed1c000 address and stops at
0xfec00000 (w32.end - end of 32-bit PCI window), so adding a
device that consumes not provided by parent bus resources
probably is not correct.

Taking in account that address is beyond of dynamically used PCI
memory resources, I'd just drop patch.

If the goal of it to make sure that it provides resource conflict
check then Windows wasn't doing a good job with PNP0C02 used,
that's why we ended up with PNP0A06 (between PNP0C02, PNP0A06, ACPI0004)
for this task in cases of absence of a defined device HID.
And it should be moved out of PCI0 bus scope anyway.

> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
> v1 -> v2:
>   * s/PDRC/CCR/ for clarity and match ICH9 spec
>   * remove unnecessary OperationRegion for RCRB
> 
> v2 -> v3: (no changes)
> 
> v3 -> v4:
>   * quote RCRB description from ICH9 spec to commit log
>   * fix indentation issue in _CRS() method declaration
>   * create hw/i386/ich9-cc.h for chipset configuration register values
>     and use them in ASL
> 
> v4 -> v5:
>   * prefix macros in ich9-cc.h with "ICH9_" for better readability and
>     make use of them in CCR device definition
> ---
>  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
>  include/hw/i386/ich9-cc.h     |  15 +++++++++++++++
>  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
>  3 files changed, 31 insertions(+)
>  create mode 100644 include/hw/i386/ich9-cc.h
> 
> diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> index 16eaca3..8f4bb6a 100644
> --- a/hw/i386/q35-acpi-dsdt.dsl
> +++ b/hw/i386/q35-acpi-dsdt.dsl
> @@ -114,6 +114,22 @@ DefinitionBlock (
>          }
>      }
>  
> +#include "hw/i386/ich9-cc.h"
> +
> +/****************************************************************
> + * Chipset Configuration Registers
> + ****************************************************************/
> +Scope(\_SB.PCI0) {
> +    Device (CCR) {
> +        Name (_HID, EISAID("PNP0C02"))
> +        Name (_UID, 1)
> +
> +        Name (_CRS, ResourceTemplate() {
> +            Memory32Fixed(ReadWrite, ICH9_RCBA_BASE_ADDR,
> ICH9_RCRB_SIZE)
> +        })
> +    }
> +}
> +
>  #include "acpi-dsdt-hpet.dsl"
>  
>  
> diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> new file mode 100644
> index 0000000..d4918ff
> --- /dev/null
> +++ b/include/hw/i386/ich9-cc.h
> @@ -0,0 +1,15 @@
> +/*
> + * QEMU ICH9 Chipset Configuration Registers
> + *
> + * 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.
> + */
> +#ifndef HW_ICH9_CC_H
> +#define HW_ICH9_CC_H
> +
> +#define ICH9_RCBA_BASE_ADDR    0xfed1c000
> +#define ICH9_RCRB_SIZE         0x00004000
> +
> +#endif /* HW_ICH9_CC_H */
> diff --git a/tests/acpi-test-data/q35/DSDT
> b/tests/acpi-test-data/q35/DSDT index
> 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49
> 100644 GIT binary patch delta 81
> zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> 
> delta 24
> gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-22 23:10   ` [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
  2015-06-23 14:29     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
@ 2015-06-23 14:39     ` Igor Mammedov
  2015-06-23 15:06       ` Michael S. Tsirkin
  2015-06-24 15:11     ` [Qemu-devel] " Michael S. Tsirkin
  2 siblings, 1 reply; 83+ messages in thread
From: Igor Mammedov @ 2015-06-23 14:39 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, mst, qemu-devel, kraxel

On Mon, 22 Jun 2015 20:10:28 -0300
Paulo Alcantara <pcacjr@gmail.com> wrote:

Also I've stumbled upon 
http://download.intel.com/design/chipsets/applnots/29227301.pdf
don't we need to implement WDDT ACPI table as well?

> This block is mapped into memory space, using the Root Complex Base
> Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this
> space must be limited to 32-(DW) bit quantities. Burst accesses are
> not allowed.
> 
> All Chipset Configuration Registers are located in this 16KiB space.
> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
> v1 -> v2:
>   * s/PDRC/CCR/ for clarity and match ICH9 spec
>   * remove unnecessary OperationRegion for RCRB
> 
> v2 -> v3: (no changes)
> 
> v3 -> v4:
>   * quote RCRB description from ICH9 spec to commit log
>   * fix indentation issue in _CRS() method declaration
>   * create hw/i386/ich9-cc.h for chipset configuration register values
>     and use them in ASL
> 
> v4 -> v5:
>   * prefix macros in ich9-cc.h with "ICH9_" for better readability and
>     make use of them in CCR device definition
> ---
>  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
>  include/hw/i386/ich9-cc.h     |  15 +++++++++++++++
>  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
>  3 files changed, 31 insertions(+)
>  create mode 100644 include/hw/i386/ich9-cc.h
> 
> diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> index 16eaca3..8f4bb6a 100644
> --- a/hw/i386/q35-acpi-dsdt.dsl
> +++ b/hw/i386/q35-acpi-dsdt.dsl
> @@ -114,6 +114,22 @@ DefinitionBlock (
>          }
>      }
>  
> +#include "hw/i386/ich9-cc.h"
> +
> +/****************************************************************
> + * Chipset Configuration Registers
> + ****************************************************************/
> +Scope(\_SB.PCI0) {
> +    Device (CCR) {
> +        Name (_HID, EISAID("PNP0C02"))
> +        Name (_UID, 1)
> +
> +        Name (_CRS, ResourceTemplate() {
> +            Memory32Fixed(ReadWrite, ICH9_RCBA_BASE_ADDR,
> ICH9_RCRB_SIZE)
> +        })
> +    }
> +}
> +
>  #include "acpi-dsdt-hpet.dsl"
>  
>  
> diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> new file mode 100644
> index 0000000..d4918ff
> --- /dev/null
> +++ b/include/hw/i386/ich9-cc.h
> @@ -0,0 +1,15 @@
> +/*
> + * QEMU ICH9 Chipset Configuration Registers
> + *
> + * 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.
> + */
> +#ifndef HW_ICH9_CC_H
> +#define HW_ICH9_CC_H
> +
> +#define ICH9_RCBA_BASE_ADDR    0xfed1c000
> +#define ICH9_RCRB_SIZE         0x00004000
> +
> +#endif /* HW_ICH9_CC_H */
> diff --git a/tests/acpi-test-data/q35/DSDT
> b/tests/acpi-test-data/q35/DSDT index
> 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49
> 100644 GIT binary patch delta 81
> zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> 
> delta 24
> gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-23 14:39     ` Igor Mammedov
@ 2015-06-23 15:06       ` Michael S. Tsirkin
  2015-06-23 15:12         ` Igor Mammedov
  0 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-23 15:06 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Tue, Jun 23, 2015 at 04:39:31PM +0200, Igor Mammedov wrote:
> On Mon, 22 Jun 2015 20:10:28 -0300
> Paulo Alcantara <pcacjr@gmail.com> wrote:
> 
> Also I've stumbled upon 
> http://download.intel.com/design/chipsets/applnots/29227301.pdf
> don't we need to implement WDDT ACPI table as well?

Maybe but linux does not seem to care.

-- 
MST

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-23 15:06       ` Michael S. Tsirkin
@ 2015-06-23 15:12         ` Igor Mammedov
  2015-06-23 15:29           ` Michael S. Tsirkin
  0 siblings, 1 reply; 83+ messages in thread
From: Igor Mammedov @ 2015-06-23 15:12 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Tue, 23 Jun 2015 17:06:46 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Tue, Jun 23, 2015 at 04:39:31PM +0200, Igor Mammedov wrote:
> > On Mon, 22 Jun 2015 20:10:28 -0300
> > Paulo Alcantara <pcacjr@gmail.com> wrote:
> > 
> > Also I've stumbled upon 
> > http://download.intel.com/design/chipsets/applnots/29227301.pdf
> > don't we need to implement WDDT ACPI table as well?
> 
> Maybe but linux does not seem to care.
What about Windows?

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-23 15:12         ` Igor Mammedov
@ 2015-06-23 15:29           ` Michael S. Tsirkin
  0 siblings, 0 replies; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-23 15:29 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Tue, Jun 23, 2015 at 05:12:07PM +0200, Igor Mammedov wrote:
> On Tue, 23 Jun 2015 17:06:46 +0200
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> > On Tue, Jun 23, 2015 at 04:39:31PM +0200, Igor Mammedov wrote:
> > > On Mon, 22 Jun 2015 20:10:28 -0300
> > > Paulo Alcantara <pcacjr@gmail.com> wrote:
> > > 
> > > Also I've stumbled upon 
> > > http://download.intel.com/design/chipsets/applnots/29227301.pdf
> > > don't we need to implement WDDT ACPI table as well?
> > 
> > Maybe but linux does not seem to care.
> What about Windows?
> 

Needs testing - I tried existing guests and they do not touch TCO but
maybe a new install will. Possibly windows does not touch TCO since
there's no WDDT ACPI table, but it's not a regression so does not have
to block this patchset.

-- 
MST

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [SeaBIOS] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-23 14:29     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
@ 2015-06-23 15:33       ` Michael S. Tsirkin
  0 siblings, 0 replies; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-23 15:33 UTC (permalink / raw)
  To: Igor Mammedov; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Tue, Jun 23, 2015 at 04:29:40PM +0200, Igor Mammedov wrote:
> On Mon, 22 Jun 2015 20:10:28 -0300
> Paulo Alcantara <pcacjr@gmail.com> wrote:
> 
> > This block is mapped into memory space, using the Root Complex Base
> > Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this
> > space must be limited to 32-(DW) bit quantities. Burst accesses are
> > not allowed.
> > 
> > All Chipset Configuration Registers are located in this 16KiB space.
> Do we really need to describe this block in ASL?
> PCI0 bus _CRS doesn't include 0xfed1c000 address and stops at
> 0xfec00000 (w32.end - end of 32-bit PCI window), so adding a
> device that consumes not provided by parent bus resources
> probably is not correct.
> 
> Taking in account that address is beyond of dynamically used PCI
> memory resources, I'd just drop patch.
> 
> If the goal of it to make sure that it provides resource conflict
> check then Windows wasn't doing a good job with PNP0C02 used,
> that's why we ended up with PNP0A06 (between PNP0C02, PNP0A06, ACPI0004)
> for this task in cases of absence of a defined device HID.
> And it should be moved out of PCI0 bus scope anyway.

Paulo, does some guest need this patch (2/3)?

> > 
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > ---
> > v1 -> v2:
> >   * s/PDRC/CCR/ for clarity and match ICH9 spec
> >   * remove unnecessary OperationRegion for RCRB
> > 
> > v2 -> v3: (no changes)
> > 
> > v3 -> v4:
> >   * quote RCRB description from ICH9 spec to commit log
> >   * fix indentation issue in _CRS() method declaration
> >   * create hw/i386/ich9-cc.h for chipset configuration register values
> >     and use them in ASL
> > 
> > v4 -> v5:
> >   * prefix macros in ich9-cc.h with "ICH9_" for better readability and
> >     make use of them in CCR device definition
> > ---
> >  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
> >  include/hw/i386/ich9-cc.h     |  15 +++++++++++++++
> >  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
> >  3 files changed, 31 insertions(+)
> >  create mode 100644 include/hw/i386/ich9-cc.h
> > 
> > diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> > index 16eaca3..8f4bb6a 100644
> > --- a/hw/i386/q35-acpi-dsdt.dsl
> > +++ b/hw/i386/q35-acpi-dsdt.dsl
> > @@ -114,6 +114,22 @@ DefinitionBlock (
> >          }
> >      }
> >  
> > +#include "hw/i386/ich9-cc.h"
> > +
> > +/****************************************************************
> > + * Chipset Configuration Registers
> > + ****************************************************************/
> > +Scope(\_SB.PCI0) {
> > +    Device (CCR) {
> > +        Name (_HID, EISAID("PNP0C02"))
> > +        Name (_UID, 1)
> > +
> > +        Name (_CRS, ResourceTemplate() {
> > +            Memory32Fixed(ReadWrite, ICH9_RCBA_BASE_ADDR,
> > ICH9_RCRB_SIZE)
> > +        })
> > +    }
> > +}
> > +
> >  #include "acpi-dsdt-hpet.dsl"
> >  
> >  
> > diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> > new file mode 100644
> > index 0000000..d4918ff
> > --- /dev/null
> > +++ b/include/hw/i386/ich9-cc.h
> > @@ -0,0 +1,15 @@
> > +/*
> > + * QEMU ICH9 Chipset Configuration Registers
> > + *
> > + * 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.
> > + */
> > +#ifndef HW_ICH9_CC_H
> > +#define HW_ICH9_CC_H
> > +
> > +#define ICH9_RCBA_BASE_ADDR    0xfed1c000
> > +#define ICH9_RCRB_SIZE         0x00004000
> > +
> > +#endif /* HW_ICH9_CC_H */
> > diff --git a/tests/acpi-test-data/q35/DSDT
> > b/tests/acpi-test-data/q35/DSDT index
> > 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49
> > 100644 GIT binary patch delta 81
> > zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> > lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> > 
> > delta 24
> > gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> > 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-22 23:10   ` [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
  2015-06-23 14:29     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
  2015-06-23 14:39     ` Igor Mammedov
@ 2015-06-24 15:11     ` Michael S. Tsirkin
  2015-06-24 16:00       ` Paulo Alcantara
  2 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-24 15:11 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Mon, Jun 22, 2015 at 08:10:28PM -0300, Paulo Alcantara wrote:
> This block is mapped into memory space, using the Root Complex Base
> Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this space
> must be limited to 32-(DW) bit quantities. Burst accesses are not
> allowed.
> 
> All Chipset Configuration Registers are located in this 16KiB space.
> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>

Can you confirm things just work for you if you drop this patch from the
series?

> ---
> v1 -> v2:
>   * s/PDRC/CCR/ for clarity and match ICH9 spec
>   * remove unnecessary OperationRegion for RCRB
> 
> v2 -> v3: (no changes)
> 
> v3 -> v4:
>   * quote RCRB description from ICH9 spec to commit log
>   * fix indentation issue in _CRS() method declaration
>   * create hw/i386/ich9-cc.h for chipset configuration register values
>     and use them in ASL
> 
> v4 -> v5:
>   * prefix macros in ich9-cc.h with "ICH9_" for better readability and
>     make use of them in CCR device definition
> ---
>  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
>  include/hw/i386/ich9-cc.h     |  15 +++++++++++++++
>  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
>  3 files changed, 31 insertions(+)
>  create mode 100644 include/hw/i386/ich9-cc.h
> 
> diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> index 16eaca3..8f4bb6a 100644
> --- a/hw/i386/q35-acpi-dsdt.dsl
> +++ b/hw/i386/q35-acpi-dsdt.dsl
> @@ -114,6 +114,22 @@ DefinitionBlock (
>          }
>      }
>  
> +#include "hw/i386/ich9-cc.h"
> +
> +/****************************************************************
> + * Chipset Configuration Registers
> + ****************************************************************/
> +Scope(\_SB.PCI0) {
> +    Device (CCR) {
> +        Name (_HID, EISAID("PNP0C02"))
> +        Name (_UID, 1)
> +
> +        Name (_CRS, ResourceTemplate() {
> +            Memory32Fixed(ReadWrite, ICH9_RCBA_BASE_ADDR, ICH9_RCRB_SIZE)
> +        })
> +    }
> +}
> +
>  #include "acpi-dsdt-hpet.dsl"
>  
>  
> diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> new file mode 100644
> index 0000000..d4918ff
> --- /dev/null
> +++ b/include/hw/i386/ich9-cc.h
> @@ -0,0 +1,15 @@
> +/*
> + * QEMU ICH9 Chipset Configuration Registers
> + *
> + * 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.
> + */
> +#ifndef HW_ICH9_CC_H
> +#define HW_ICH9_CC_H
> +
> +#define ICH9_RCBA_BASE_ADDR    0xfed1c000
> +#define ICH9_RCRB_SIZE         0x00004000
> +
> +#endif /* HW_ICH9_CC_H */
> diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
> index 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49 100644
> GIT binary patch
> delta 81
> zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> 
> delta 24
> gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> 
> -- 
> 2.1.0

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-24 15:11     ` [Qemu-devel] " Michael S. Tsirkin
@ 2015-06-24 16:00       ` Paulo Alcantara
  2015-06-24 16:04         ` Michael S. Tsirkin
  0 siblings, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-24 16:00 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Wed, 24 Jun 2015 17:11:26 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Mon, Jun 22, 2015 at 08:10:28PM -0300, Paulo Alcantara wrote:
> > This block is mapped into memory space, using the Root Complex Base
> > Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this
> > space must be limited to 32-(DW) bit quantities. Burst accesses are
> > not allowed.
> > 
> > All Chipset Configuration Registers are located in this 16KiB space.
> > 
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> 
> Can you confirm things just work for you if you drop this patch from
> the series?

Hi Michael,

Yes, everything works. I still see no point of adding it to SSDT
because OVMF is already setting RCBA to that fixed address and so
SeaBIOS once my patch is applied. Therefore, there is no no such thing
like "dynamically programmed by BIOS" either. Although, my host and
probably yours define the same address in DSDT.

Given that UEFI and a BIOS implementations are using the same fixed
address, this patch was done in a hope of not letting the ACPI-aware OS
using the same address for another BAR or something else thus avoiding
conflicts.

Please, let me know if I'm missing something here. If you want, I can
send v6 with this patch removed completely.

(BTW, sorry for the delay. It's holiday here in Brazil and, you know,
real life happens sometimes :-) )

Thanks,

Paulo

> 
> > ---
> > v1 -> v2:
> >   * s/PDRC/CCR/ for clarity and match ICH9 spec
> >   * remove unnecessary OperationRegion for RCRB
> > 
> > v2 -> v3: (no changes)
> > 
> > v3 -> v4:
> >   * quote RCRB description from ICH9 spec to commit log
> >   * fix indentation issue in _CRS() method declaration
> >   * create hw/i386/ich9-cc.h for chipset configuration register
> > values and use them in ASL
> > 
> > v4 -> v5:
> >   * prefix macros in ich9-cc.h with "ICH9_" for better readability
> > and make use of them in CCR device definition
> > ---
> >  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
> >  include/hw/i386/ich9-cc.h     |  15 +++++++++++++++
> >  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
> >  3 files changed, 31 insertions(+)
> >  create mode 100644 include/hw/i386/ich9-cc.h
> > 
> > diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> > index 16eaca3..8f4bb6a 100644
> > --- a/hw/i386/q35-acpi-dsdt.dsl
> > +++ b/hw/i386/q35-acpi-dsdt.dsl
> > @@ -114,6 +114,22 @@ DefinitionBlock (
> >          }
> >      }
> >  
> > +#include "hw/i386/ich9-cc.h"
> > +
> > +/****************************************************************
> > + * Chipset Configuration Registers
> > + ****************************************************************/
> > +Scope(\_SB.PCI0) {
> > +    Device (CCR) {
> > +        Name (_HID, EISAID("PNP0C02"))
> > +        Name (_UID, 1)
> > +
> > +        Name (_CRS, ResourceTemplate() {
> > +            Memory32Fixed(ReadWrite, ICH9_RCBA_BASE_ADDR,
> > ICH9_RCRB_SIZE)
> > +        })
> > +    }
> > +}
> > +
> >  #include "acpi-dsdt-hpet.dsl"
> >  
> >  
> > diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> > new file mode 100644
> > index 0000000..d4918ff
> > --- /dev/null
> > +++ b/include/hw/i386/ich9-cc.h
> > @@ -0,0 +1,15 @@
> > +/*
> > + * QEMU ICH9 Chipset Configuration Registers
> > + *
> > + * 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.
> > + */
> > +#ifndef HW_ICH9_CC_H
> > +#define HW_ICH9_CC_H
> > +
> > +#define ICH9_RCBA_BASE_ADDR    0xfed1c000
> > +#define ICH9_RCRB_SIZE         0x00004000
> > +
> > +#endif /* HW_ICH9_CC_H */
> > diff --git a/tests/acpi-test-data/q35/DSDT
> > b/tests/acpi-test-data/q35/DSDT index
> > 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49
> > 100644 GIT binary patch delta 81
> > zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> > lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> > 
> > delta 24
> > gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> > 
> > -- 
> > 2.1.0
> 



-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table
  2015-06-24 16:00       ` Paulo Alcantara
@ 2015-06-24 16:04         ` Michael S. Tsirkin
  0 siblings, 0 replies; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-24 16:04 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Wed, Jun 24, 2015 at 01:00:49PM -0300, Paulo Alcantara wrote:
> On Wed, 24 Jun 2015 17:11:26 +0200
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> > On Mon, Jun 22, 2015 at 08:10:28PM -0300, Paulo Alcantara wrote:
> > > This block is mapped into memory space, using the Root Complex Base
> > > Address (RCBA) register of the PCI-to-LPC bridge. Accesses in this
> > > space must be limited to 32-(DW) bit quantities. Burst accesses are
> > > not allowed.
> > > 
> > > All Chipset Configuration Registers are located in this 16KiB space.
> > > 
> > > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > 
> > Can you confirm things just work for you if you drop this patch from
> > the series?
> 
> Hi Michael,
> 
> Yes, everything works. I still see no point of adding it to SSDT
> because OVMF is already setting RCBA to that fixed address and so
> SeaBIOS once my patch is applied. Therefore, there is no no such thing
> like "dynamically programmed by BIOS" either. Although, my host and
> probably yours define the same address in DSDT.
> 
> Given that UEFI and a BIOS implementations are using the same fixed
> address, this patch was done in a hope of not letting the ACPI-aware OS
> using the same address for another BAR or something else thus avoiding
> conflicts.

But does PCI root CRS cover this range? If not - an ACPI aware
OS can't assign PCI BARs in this range really.


> Please, let me know if I'm missing something here. If you want, I can
> send v6 with this patch removed completely.
> 
> (BTW, sorry for the delay. It's holiday here in Brazil and, you know,
> real life happens sometimes :-) )
> 
> Thanks,
> 
> Paulo

No rush, we have a couple of days until the hard freeze still


> > 
> > > ---
> > > v1 -> v2:
> > >   * s/PDRC/CCR/ for clarity and match ICH9 spec
> > >   * remove unnecessary OperationRegion for RCRB
> > > 
> > > v2 -> v3: (no changes)
> > > 
> > > v3 -> v4:
> > >   * quote RCRB description from ICH9 spec to commit log
> > >   * fix indentation issue in _CRS() method declaration
> > >   * create hw/i386/ich9-cc.h for chipset configuration register
> > > values and use them in ASL
> > > 
> > > v4 -> v5:
> > >   * prefix macros in ich9-cc.h with "ICH9_" for better readability
> > > and make use of them in CCR device definition
> > > ---
> > >  hw/i386/q35-acpi-dsdt.dsl     |  16 ++++++++++++++++
> > >  include/hw/i386/ich9-cc.h     |  15 +++++++++++++++
> > >  tests/acpi-test-data/q35/DSDT | Bin 7666 -> 7723 bytes
> > >  3 files changed, 31 insertions(+)
> > >  create mode 100644 include/hw/i386/ich9-cc.h
> > > 
> > > diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
> > > index 16eaca3..8f4bb6a 100644
> > > --- a/hw/i386/q35-acpi-dsdt.dsl
> > > +++ b/hw/i386/q35-acpi-dsdt.dsl
> > > @@ -114,6 +114,22 @@ DefinitionBlock (
> > >          }
> > >      }
> > >  
> > > +#include "hw/i386/ich9-cc.h"
> > > +
> > > +/****************************************************************
> > > + * Chipset Configuration Registers
> > > + ****************************************************************/
> > > +Scope(\_SB.PCI0) {
> > > +    Device (CCR) {
> > > +        Name (_HID, EISAID("PNP0C02"))
> > > +        Name (_UID, 1)
> > > +
> > > +        Name (_CRS, ResourceTemplate() {
> > > +            Memory32Fixed(ReadWrite, ICH9_RCBA_BASE_ADDR,
> > > ICH9_RCRB_SIZE)
> > > +        })
> > > +    }
> > > +}
> > > +
> > >  #include "acpi-dsdt-hpet.dsl"
> > >  
> > >  
> > > diff --git a/include/hw/i386/ich9-cc.h b/include/hw/i386/ich9-cc.h
> > > new file mode 100644
> > > index 0000000..d4918ff
> > > --- /dev/null
> > > +++ b/include/hw/i386/ich9-cc.h
> > > @@ -0,0 +1,15 @@
> > > +/*
> > > + * QEMU ICH9 Chipset Configuration Registers
> > > + *
> > > + * 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.
> > > + */
> > > +#ifndef HW_ICH9_CC_H
> > > +#define HW_ICH9_CC_H
> > > +
> > > +#define ICH9_RCBA_BASE_ADDR    0xfed1c000
> > > +#define ICH9_RCRB_SIZE         0x00004000
> > > +
> > > +#endif /* HW_ICH9_CC_H */
> > > diff --git a/tests/acpi-test-data/q35/DSDT
> > > b/tests/acpi-test-data/q35/DSDT index
> > > 4723e5954dccb00995ccaf521b7daf6bf15cf1d4..f3bda7b54ea6d669b1498d9380e7781207fb6e49
> > > 100644 GIT binary patch delta 81
> > > zcmexlz1oJ$CD<iITaJN&F>xbTJfnq$UVN}qe1Nm3L3ERjvvW{9N4$rp3y<Rk9wv_X
> > > lP)`>|j(F#wU_n7HzBWz<Mur0y|1mf)FjO*Z&S3140RVI`6(s-w
> > > 
> > > delta 24
> > > gcmZ2&^U0daCD<k8lPm)R<DrdQ@r;`nF?PxT0Bl$YHUIzs
> > > 
> > > -- 
> > > 2.1.0
> > 
> 
> 
> 
> -- 
> Paulo Alcantara, C.E.S.A.R
> Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v6 1/2] ich9: add TCO interface emulation
  2015-05-27  0:29 [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paulo Alcantara
                   ` (5 preceding siblings ...)
  2015-06-22 23:10 ` [Qemu-devel] [PATCH v5 " Paulo Alcantara
@ 2015-06-24 18:03 ` Paulo Alcantara
  2015-06-24 18:03   ` [Qemu-devel] [PATCH v6 2/2] tests: add testcase for TCO watchdog emulation Paulo Alcantara
  2015-06-27 17:56 ` [Qemu-devel] [PATCH v7 1/3] ich9: add TCO interface emulation Paulo Alcantara
  2015-06-28 17:58 ` [Qemu-devel] [PATCH v8 1/3] ich9: add TCO interface emulation Paulo Alcantara
  8 siblings, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-24 18:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

This interface provides some registers within a 32-byte range and can be
acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).

It's commonly used as a watchdog timer to detect system lockups through
SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
NO_REBOOT bit is not set in GCS (General Control and Status register),
the system will be resetted upon second timeout if TCO_RLD register
wasn't previously written to prevent timeout.

This patch adds support to TCO watchdog logic and few other features
like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
etc. are not implemented yet.

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * add migration support for TCO I/O device state
  * wake up only when total time expired instead of every 0.6s
  * some cleanup suggested by Paolo Bonzini

v2 -> v3:
  * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
  * improve handling of TCO_LOCK bit in TCO1_CNT register

v3 -> v4:
  * fix some conflicts in hw/acpi/ich9.c after rebasing against master
  * remove meaningless "use_tco" field from TCOIORegs structure
  * add a object property named "enable_tco" and only enable TCO support
    on pc-q35-2.4 and later

v4 -> v5:
  * remove unused field (use_tco) in TCOIORegs structure
  * move license to GPLv2+

v5 -> v6:
  * remove "io_tco" field from ICH9LPCPMRegs structure since it's no
    longer used
  * set ICH9_CC_GCS_NO_REBOOT bit by default in ICH9's LPC
    initialisation
---
 hw/acpi/Makefile.objs  |   2 +-
 hw/acpi/ich9.c         |  55 ++++++++++-
 hw/acpi/tco.c          | 264 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/i386/pc_q35.c       |   4 +-
 hw/isa/lpc_ich9.c      |  16 ++-
 include/hw/acpi/ich9.h |   6 +-
 include/hw/acpi/tco.h  |  82 +++++++++++++++
 include/hw/boards.h    |   3 +-
 include/hw/i386/ich9.h |  11 ++-
 include/hw/i386/pc.h   |   1 +
 10 files changed, 436 insertions(+), 8 deletions(-)
 create mode 100644 hw/acpi/tco.c
 create mode 100644 include/hw/acpi/tco.h

diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 29d46d8..3db1f07 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
+common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o tco.o
 common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
 common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI) += acpi_interface.o
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 8a64ffb..d3d9953 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -30,6 +30,7 @@
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "hw/acpi/acpi.h"
+#include "hw/acpi/tco.h"
 #include "sysemu/kvm.h"
 #include "exec/address-spaces.h"
 
@@ -92,8 +93,16 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
                             unsigned width)
 {
     ICH9LPCPMRegs *pm = opaque;
+    TCOIORegs *tr = &pm->tco_regs;
+    uint64_t tco_en;
+
     switch (addr) {
     case 0:
+        tco_en = pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
+        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
+        if (tr->tco.cnt1 & TCO_LOCK) {
+            val = (val & ~ICH9_PMIO_SMI_EN_TCO_EN) | tco_en;
+        }
         pm->smi_en &= ~pm->smi_en_wmask;
         pm->smi_en |= (val & pm->smi_en_wmask);
         break;
@@ -159,6 +168,25 @@ static const VMStateDescription vmstate_memhp_state = {
     }
 };
 
+static bool vmstate_test_use_tco(void *opaque)
+{
+    ICH9LPCPMRegs *s = opaque;
+    return s->enable_tco;
+}
+
+static const VMStateDescription vmstate_tco_io_state = {
+    .name = "ich9_pm/tco",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .needed = vmstate_test_use_tco,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
+                       TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 const VMStateDescription vmstate_ich9_pm = {
     .name = "ich9_pm",
     .version_id = 1,
@@ -179,6 +207,10 @@ const VMStateDescription vmstate_ich9_pm = {
     .subsections = (const VMStateDescription*[]) {
         &vmstate_memhp_state,
         NULL
+    },
+    .subsections = (const VMStateDescription*[]) {
+        &vmstate_tco_io_state,
+        NULL
     }
 };
 
@@ -209,7 +241,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
     acpi_pm1_evt_power_down(&pm->acpi_regs);
 }
 
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
                   qemu_irq sci_irq)
 {
     memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
@@ -231,6 +263,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
                           "acpi-smi", 8);
     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
 
+    pm->enable_tco = enable_tco;
+    if (pm->enable_tco) {
+        acpi_pm_tco_init(&pm->tco_regs, &pm->io);
+    }
+
     pm->irq = sci_irq;
     qemu_register_reset(pm_reset, pm);
     pm->powerdown_notifier.notify = pm_powerdown_req;
@@ -351,6 +388,18 @@ out:
     error_propagate(errp, local_err);
 }
 
+static bool ich9_pm_get_enable_tco(Object *obj, Error **errp)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+    return s->pm.enable_tco;
+}
+
+static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+    s->pm.enable_tco = value;
+}
+
 void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
 {
     static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
@@ -382,6 +431,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
                         ich9_pm_get_s4_val,
                         ich9_pm_set_s4_val,
                         NULL, pm, NULL);
+    object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
+                             ich9_pm_get_enable_tco,
+                             ich9_pm_set_enable_tco,
+                             NULL);
 }
 
 void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp)
diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
new file mode 100644
index 0000000..1794a54
--- /dev/null
+++ b/hw/acpi/tco.c
@@ -0,0 +1,264 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * 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.
+ */
+#include "qemu-common.h"
+#include "sysemu/watchdog.h"
+#include "hw/i386/ich9.h"
+
+#include "hw/acpi/tco.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define TCO_DEBUG(fmt, ...)                                     \
+    do {                                                        \
+        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
+    } while (0)
+#else
+#define TCO_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+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,
+};
+
+static inline void tco_timer_reload(TCOIORegs *tr)
+{
+    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
+    timer_mod(tr->tco_timer, tr->expire_time);
+}
+
+static inline void tco_timer_stop(TCOIORegs *tr)
+{
+    tr->expire_time = -1;
+}
+
+static void tco_timer_expired(void *opaque)
+{
+    TCOIORegs *tr = opaque;
+    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
+    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
+    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_CC_GCS);
+
+    tr->tco.rld = 0;
+    tr->tco.sts1 |= TCO_TIMEOUT;
+    if (++tr->timeouts_no == 2) {
+        tr->tco.sts2 |= TCO_SECOND_TO_STS;
+        tr->tco.sts2 |= TCO_BOOT_STS;
+        tr->timeouts_no = 0;
+
+        if (!(gcs & ICH9_CC_GCS_NO_REBOOT)) {
+            watchdog_perform_action();
+            tco_timer_stop(tr);
+            return;
+        }
+    }
+
+    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
+        ich9_generate_smi();
+    } else {
+        ich9_generate_nmi();
+    }
+    tr->tco.rld = tr->tco.tmr;
+    tco_timer_reload(tr);
+}
+
+/* NOTE: values of 0 or 1 will be ignored by ICH */
+static inline int can_start_tco_timer(TCOIORegs *tr)
+{
+    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
+}
+
+static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
+{
+    uint16_t rld;
+
+    switch (addr) {
+    case TCO_RLD:
+        if (tr->expire_time != -1) {
+            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
+            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
+        } else {
+            rld = tr->tco.rld;
+        }
+        return rld;
+    case TCO_DAT_IN:
+        return tr->tco.din;
+    case TCO_DAT_OUT:
+        return tr->tco.dout;
+    case TCO1_STS:
+        return tr->tco.sts1;
+    case TCO2_STS:
+        return tr->tco.sts2;
+    case TCO1_CNT:
+        return tr->tco.cnt1;
+    case TCO2_CNT:
+        return tr->tco.cnt2;
+    case TCO_MESSAGE1:
+        return tr->tco.msg1;
+    case TCO_MESSAGE2:
+        return tr->tco.msg2;
+    case TCO_WDCNT:
+        return tr->tco.wdcnt;
+    case TCO_TMR:
+        return tr->tco.tmr;
+    case SW_IRQ_GEN:
+        return tr->sw_irq_gen;
+    }
+    return 0;
+}
+
+static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
+{
+    switch (addr) {
+    case TCO_RLD:
+        tr->timeouts_no = 0;
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tr->tco.rld = val;
+        }
+        break;
+    case TCO_DAT_IN:
+        tr->tco.din = val;
+        tr->tco.sts1 |= SW_TCO_SMI;
+        ich9_generate_smi();
+        break;
+    case TCO_DAT_OUT:
+        tr->tco.dout = val;
+        tr->tco.sts1 |= TCO_INT_STS;
+        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
+        break;
+    case TCO1_STS:
+        tr->tco.sts1 = val & TCO1_STS_MASK;
+        break;
+    case TCO2_STS:
+        tr->tco.sts2 = val & TCO2_STS_MASK;
+        break;
+    case TCO1_CNT:
+        val &= TCO1_CNT_MASK;
+        /*
+         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
+         * is required to change this bit from 1 to 0 -- it defaults to 0.
+         */
+        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tco_timer_stop(tr);
+        }
+        break;
+    case TCO2_CNT:
+        tr->tco.cnt2 = val;
+        break;
+    case TCO_MESSAGE1:
+        tr->tco.msg1 = val;
+        break;
+    case TCO_MESSAGE2:
+        tr->tco.msg2 = val;
+        break;
+    case TCO_WDCNT:
+        tr->tco.wdcnt = val;
+        break;
+    case TCO_TMR:
+        tr->tco.tmr = val;
+        break;
+    case SW_IRQ_GEN:
+        tr->sw_irq_gen = val;
+        break;
+    }
+}
+
+static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width)
+{
+    TCOIORegs *tr = opaque;
+    return tco_ioport_readw(tr, addr);
+}
+
+static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val,
+                          unsigned width)
+{
+    TCOIORegs *tr = opaque;
+    tco_ioport_writew(tr, addr, val);
+}
+
+static const MemoryRegionOps tco_io_ops = {
+    .read = tco_io_readw,
+    .write = tco_io_writew,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent)
+{
+    *tr = (TCOIORegs) {
+        .tco = {
+            .rld      = TCO_RLD_DEFAULT,
+            .din      = TCO_DAT_IN_DEFAULT,
+            .dout     = TCO_DAT_OUT_DEFAULT,
+            .sts1     = TCO1_STS_DEFAULT,
+            .sts2     = TCO2_STS_DEFAULT,
+            .cnt1     = TCO1_CNT_DEFAULT,
+            .cnt2     = TCO2_CNT_DEFAULT,
+            .msg1     = TCO_MESSAGE1_DEFAULT,
+            .msg2     = TCO_MESSAGE2_DEFAULT,
+            .wdcnt    = TCO_WDCNT_DEFAULT,
+            .tmr      = TCO_TMR_DEFAULT,
+        },
+        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
+        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
+        .expire_time   = -1,
+        .timeouts_no   = 0,
+    };
+    memory_region_init_io(&tr->io, memory_region_owner(parent),
+                          &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN);
+    memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io);
+}
+
+const VMStateDescription vmstate_tco_io_sts = {
+    .name = "tco io device status",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT16(tco.rld, TCOIORegs),
+        VMSTATE_UINT8(tco.din, TCOIORegs),
+        VMSTATE_UINT8(tco.dout, TCOIORegs),
+        VMSTATE_UINT16(tco.sts1, TCOIORegs),
+        VMSTATE_UINT16(tco.sts2, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
+        VMSTATE_UINT8(tco.msg1, TCOIORegs),
+        VMSTATE_UINT8(tco.msg2, TCOIORegs),
+        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
+        VMSTATE_UINT16(tco.tmr, TCOIORegs),
+        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
+        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
+        VMSTATE_INT64(expire_time, TCOIORegs),
+        VMSTATE_UINT8(timeouts_no, TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 082cd93..7e35a36 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -253,7 +253,7 @@ static void pc_q35_init(MachineState *machine)
                          (pc_machine->vmport != ON_OFF_AUTO_ON), 0xff0104);
 
     /* connect pm stuff to lpc */
-    ich9_lpc_pm_init(lpc);
+    ich9_lpc_pm_init(lpc, !mc->no_tco);
 
     /* ahci and SATA device, for q35 1 ahci controller is built-in */
     ahci = pci_create_simple_multifunction(host_bus,
@@ -393,6 +393,7 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
     m->default_machine_opts = "firmware=bios-256k.bin";
     m->default_display = "std";
     m->no_floppy = 1;
+    m->no_tco = 0;
     m->alias = "q35";
 }
 
@@ -404,6 +405,7 @@ static void pc_q35_2_3_machine_options(MachineClass *m)
 {
     pc_q35_2_4_machine_options(m);
     m->no_floppy = 0;
+    m->no_tco = 1;
     m->alias = NULL;
     SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
 }
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index b3e0b1f..b547002 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -138,6 +138,7 @@ static void ich9_cc_reset(ICH9LPCState *lpc)
     pci_set_long(c + ICH9_CC_D27IR, ICH9_CC_DIR_DEFAULT);
     pci_set_long(c + ICH9_CC_D26IR, ICH9_CC_DIR_DEFAULT);
     pci_set_long(c + ICH9_CC_D25IR, ICH9_CC_DIR_DEFAULT);
+    pci_set_long(c + ICH9_CC_GCS, ICH9_CC_GCS_DEFAULT);
 
     ich9_cc_update(lpc);
 }
@@ -313,6 +314,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
     return route;
 }
 
+void ich9_generate_smi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+}
+
+void ich9_generate_nmi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
+}
+
 static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
 {
     switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
@@ -357,11 +368,12 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
     }
 }
 
-void ich9_lpc_pm_init(PCIDevice *lpc_pci)
+void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool enable_tco)
 {
     ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
 
-    ich9_pm_init(lpc_pci, &lpc->pm, qemu_allocate_irq(ich9_set_sci, lpc, 0));
+    ich9_pm_init(lpc_pci, &lpc->pm, enable_tco,
+                 qemu_allocate_irq(ich9_set_sci, lpc, 0));
     ich9_lpc_reset(&lpc->d.qdev);
 }
 
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
index 77cc65c..bc3daec 100644
--- a/include/hw/acpi/ich9.h
+++ b/include/hw/acpi/ich9.h
@@ -25,6 +25,7 @@
 #include "hw/acpi/cpu_hotplug.h"
 #include "hw/acpi/memory_hotplug.h"
 #include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/tco.h"
 
 typedef struct ICH9LPCPMRegs {
     /*
@@ -54,9 +55,12 @@ typedef struct ICH9LPCPMRegs {
     uint8_t disable_s3;
     uint8_t disable_s4;
     uint8_t s4_val;
+    bool enable_tco;
+
+    TCOIORegs tco_regs;
 } ICH9LPCPMRegs;
 
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
                   qemu_irq sci_irq);
 void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base);
 extern const VMStateDescription vmstate_ich9_pm;
diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
new file mode 100644
index 0000000..c63afc8
--- /dev/null
+++ b/include/hw/acpi/tco.h
@@ -0,0 +1,82 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * 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.
+ */
+#ifndef HW_ACPI_TCO_H
+#define HW_ACPI_TCO_H
+
+#include "qemu/typedefs.h"
+#include "qemu-common.h"
+
+/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
+#define TCO_TICK_NSEC 600000000LL
+
+/* TCO I/O register offsets */
+enum {
+    TCO_RLD           = 0x00,
+    TCO_DAT_IN        = 0x02,
+    TCO_DAT_OUT       = 0x03,
+    TCO1_STS          = 0x04,
+    TCO2_STS          = 0x06,
+    TCO1_CNT          = 0x08,
+    TCO2_CNT          = 0x0a,
+    TCO_MESSAGE1      = 0x0c,
+    TCO_MESSAGE2      = 0x0d,
+    TCO_WDCNT         = 0x0e,
+    SW_IRQ_GEN        = 0x10,
+    TCO_TMR           = 0x12,
+};
+
+/* TCO I/O register control/status bits */
+enum {
+    SW_TCO_SMI           = 1 << 1,
+    TCO_INT_STS          = 1 << 2,
+    TCO_LOCK             = 1 << 12,
+    TCO_TMR_HLT          = 1 << 11,
+    TCO_TIMEOUT          = 1 << 3,
+    TCO_SECOND_TO_STS    = 1 << 1,
+    TCO_BOOT_STS         = 1 << 2,
+};
+
+/* TCO I/O registers mask bits */
+enum {
+    TCO_RLD_MASK     = 0x3ff,
+    TCO1_STS_MASK    = 0xe870,
+    TCO2_STS_MASK    = 0xfff8,
+    TCO1_CNT_MASK    = 0xfeff,
+    TCO_TMR_MASK     = 0x3ff,
+};
+
+typedef struct TCOIORegs {
+    struct {
+        uint16_t rld;
+        uint8_t din;
+        uint8_t dout;
+        uint16_t sts1;
+        uint16_t sts2;
+        uint16_t cnt1;
+        uint16_t cnt2;
+        uint8_t msg1;
+        uint8_t msg2;
+        uint8_t wdcnt;
+        uint16_t tmr;
+    } tco;
+    uint8_t sw_irq_gen;
+
+    QEMUTimer *tco_timer;
+    int64_t expire_time;
+    uint8_t timeouts_no;
+
+    MemoryRegion io;
+} TCOIORegs;
+
+/* tco.c */
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent);
+
+extern const VMStateDescription vmstate_tco_io_sts;
+
+#endif /* HW_ACPI_TCO_H */
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 6379901..2aec9cb 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -99,7 +99,8 @@ struct MachineClass {
         no_floppy:1,
         no_cdrom:1,
         no_sdcard:1,
-        has_dynamic_sysbus:1;
+        has_dynamic_sysbus:1,
+        no_tco:1;
     int is_default;
     const char *default_machine_opts;
     const char *default_boot_order;
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index a2cc15c..f5681a3 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -17,9 +17,12 @@
 void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
 int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx);
 PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
-void ich9_lpc_pm_init(PCIDevice *pci_lpc);
+void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool enable_tco);
 I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
 
+void ich9_generate_smi(void);
+void ich9_generate_nmi(void);
+
 #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
 
 #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
@@ -90,6 +93,9 @@ Object *ich9_lpc_find(void);
 #define ICH9_CC_DIR_MASK                        0x7
 #define ICH9_CC_OIC                             0x31FF
 #define ICH9_CC_OIC_AEN                         0x1
+#define ICH9_CC_GCS                             0x3410
+#define ICH9_CC_GCS_DEFAULT                     0x00000020
+#define ICH9_CC_GCS_NO_REBOOT                   (1 << 5)
 
 /* D28:F[0-5] */
 #define ICH9_PCIE_DEV                           28
@@ -186,7 +192,10 @@ Object *ich9_lpc_find(void);
 #define ICH9_PMIO_GPE0_LEN                      16
 #define ICH9_PMIO_SMI_EN                        0x30
 #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
+#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
 #define ICH9_PMIO_SMI_STS                       0x34
+#define ICH9_PMIO_TCO_RLD                       0x60
+#define ICH9_PMIO_TCO_LEN                       32
 
 /* FADT ACPI_ENABLE/ACPI_DISABLE */
 #define ICH9_APM_ACPI_ENABLE                    0x2
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 86c5651..c1afdc0 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -89,6 +89,7 @@ typedef struct PcPciInfo {
 #define ACPI_PM_PROP_PM_IO_BASE "pm_io_base"
 #define ACPI_PM_PROP_GPE0_BLK "gpe0_blk"
 #define ACPI_PM_PROP_GPE0_BLK_LEN "gpe0_blk_len"
+#define ACPI_PM_PROP_TCO_ENABLED "enable_tco"
 
 struct PcGuestInfo {
     bool isapc_ram_fw;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v6 2/2] tests: add testcase for TCO watchdog emulation
  2015-06-24 18:03 ` [Qemu-devel] [PATCH v6 1/2] " Paulo Alcantara
@ 2015-06-24 18:03   ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-24 18:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

This patch adds a testcase that covers the following:
  1) TCO default values
  2) first and second TCO timeout
  3) watch and validate ticks counter through TCO_RLD register
  4) maximum supported TCO timeout (0x3ff)
  5) watchdog actions (pause/reset/shutdown/none) upon second TCO
     timeout
  6) set and get of TCO control and status bits

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * some cleanup
  * add test for TCO_LOCK bit

v2 -> v3:
  * add tests for TCO control & status bits
  * fix check of SECOND_TO_STS bit (it's set in TCO2_STS reg)

v3 -> v4:
  * add more description to commit log
  * use RCBA_BASE_ADDR macro defintion from hw/i386/ich9-cc.h instead

v4 -> v5:
  * use modified macros (now prefixed with ICH9_) from ich9-cc.h
  * move license to GPLv2+

v5 -> v6:
  * remove include of "hw/i386/ich9-cc.h" since it's no longer exist
---
 tests/Makefile   |   2 +
 tests/tco-test.c | 460 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 462 insertions(+)
 create mode 100644 tests/tco-test.c

diff --git a/tests/Makefile b/tests/Makefile
index eff5e11..ef1e981 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -152,6 +152,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
 check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
+check-qtest-i386-y += tests/tco-test$(EXESUF)
 gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
 check-qtest-i386-y += $(check-qtest-pci-y)
 gcov-files-i386-y += $(gcov-files-pci-y)
@@ -370,6 +371,7 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
 tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
 tests/ne2000-test$(EXESUF): tests/ne2000-test.o
 tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
+tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
 tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
 tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
 tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
diff --git a/tests/tco-test.c b/tests/tco-test.c
new file mode 100644
index 0000000..1a2fe3d
--- /dev/null
+++ b/tests/tco-test.c
@@ -0,0 +1,460 @@
+/*
+ * 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.
+ */
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/i386/ich9.h"
+#include "hw/acpi/ich9.h"
+#include "hw/acpi/tco.h"
+
+#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;
+    QPCIDevice *dev;
+    void *lpc_base;
+    void *tco_io_base;
+} TestData;
+
+static void test_init(TestData *d)
+{
+    QPCIBus *bus;
+    QTestState *qs;
+    char *s;
+
+    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
+    qs = qtest_start(s);
+    qtest_irq_intercept_in(qs, "ioapic");
+    g_free(s);
+
+    bus = qpci_init_pc();
+    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
+    g_assert(d->dev != NULL);
+
+    /* map PCI-to-LPC bridge interface BAR */
+    d->lpc_base = qpci_iomap(d->dev, 0, NULL);
+
+    qpci_device_enable(d->dev);
+
+    g_assert(d->lpc_base != NULL);
+
+    /* set ACPI PM I/O space base address */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_PMBASE,
+                       PM_IO_BASE_ADDR | 0x1);
+    /* enable ACPI I/O */
+    qpci_config_writeb(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_ACPI_CTRL,
+                       0x80);
+    /* set Root Complex BAR */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_RCBA,
+                       RCBA_BASE_ADDR | 0x1);
+
+    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
+}
+
+static void stop_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val |= TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void start_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val &= ~TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void load_tco(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
+}
+
+static void set_tco_timeout(const TestData *d, uint16_t ticks)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
+}
+
+static void clear_tco_status(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
+}
+
+static void reset_on_second_timeout(bool enable)
+{
+    uint32_t val;
+
+    val = readl(RCBA_BASE_ADDR + ICH9_CC_GCS);
+    if (enable) {
+        val &= ~ICH9_CC_GCS_NO_REBOOT;
+    } else {
+        val |= ICH9_CC_GCS_NO_REBOOT;
+    }
+    writel(RCBA_BASE_ADDR + ICH9_CC_GCS, val);
+}
+
+static void test_tco_defaults(void)
+{
+    TestData d;
+
+    d.args = NULL;
+    test_init(&d);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+                    TCO_RLD_DEFAULT);
+    /* TCO_DAT_IN & TCO_DAT_OUT */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
+                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
+    /* TCO1_STS & TCO2_STS */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
+                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
+    /* TCO1_CNT & TCO2_CNT */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
+    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
+                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
+                    TCO_WDCNT_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
+                    SW_IRQ_GEN_DEFAULT);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
+                    TCO_TMR_DEFAULT);
+    qtest_end();
+}
+
+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;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    /* test first timeout */
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    /* test clearing timeout bit */
+    val |= TCO_TIMEOUT;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+
+    /* test second timeout */
+    clock_step(ticks * TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco_max_timeout(void)
+{
+    TestData d;
+    const uint16_t ticks = 0xffff;
+    uint32_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
+    g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+    clock_step(TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static QDict *get_watchdog_action(void)
+{
+    QDict *ev = qmp("");
+    QDict *data;
+    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
+
+    data = qdict_get_qdict(ev, "data");
+    QINCREF(data);
+    QDECREF(ev);
+    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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_ticks_counter(void)
+{
+    TestData d;
+    uint16_t ticks = TCO_SECS_TO_TICKS(8);
+    uint16_t rld;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+
+    do {
+        rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
+        g_assert_cmpint(rld, ==, ticks);
+        clock_step(TCO_TICK_NSEC);
+        ticks--;
+    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco1_control_bits(void)
+{
+    TestData d;
+    uint16_t val;
+
+    d.args = NULL;
+    test_init(&d);
+
+    val = TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    val &= ~TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    TCO_LOCK);
+    qtest_end();
+}
+
+static void test_tco1_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0);
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0);
+    qtest_end();
+}
+
+static void test_tco2_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = "-watchdog-action none";
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0);
+    qtest_end();
+}
+
+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();
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v7 1/3] ich9: add TCO interface emulation
  2015-05-27  0:29 [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paulo Alcantara
                   ` (6 preceding siblings ...)
  2015-06-24 18:03 ` [Qemu-devel] [PATCH v6 1/2] " Paulo Alcantara
@ 2015-06-27 17:56 ` Paulo Alcantara
  2015-06-27 17:56   ` [Qemu-devel] [PATCH v7 2/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
  2015-06-27 17:56   ` [Qemu-devel] [PATCH v7 3/3] ich9: implement strap SPKR pin logic Paulo Alcantara
  2015-06-28 17:58 ` [Qemu-devel] [PATCH v8 1/3] ich9: add TCO interface emulation Paulo Alcantara
  8 siblings, 2 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-27 17:56 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

This interface provides some registers within a 32-byte range and can be
acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).

It's commonly used as a watchdog timer to detect system lockups through
SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
NO_REBOOT bit is not set in GCS (General Control and Status register),
the system will be resetted upon second timeout if TCO_RLD register
wasn't previously written to prevent timeout.

This patch adds support to TCO watchdog logic and few other features
like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
etc. are not implemented yet.

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * add migration support for TCO I/O device state
  * wake up only when total time expired instead of every 0.6s
  * some cleanup suggested by Paolo Bonzini

v2 -> v3:
  * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
  * improve handling of TCO_LOCK bit in TCO1_CNT register

v3 -> v4:
  * fix some conflicts in hw/acpi/ich9.c after rebasing against master
  * remove meaningless "use_tco" field from TCOIORegs structure
  * add a object property named "enable_tco" and only enable TCO support
    on pc-q35-2.4 and later

v4 -> v5:
  * remove unused field (use_tco) in TCOIORegs structure
  * move license to GPLv2+

v5 -> v6:
  * remove "io_tco" field from ICH9LPCPMRegs structure since it's no
    longer used
  * set ICH9_CC_GCS_NO_REBOOT bit by default in ICH9's LPC
    initialisation

v6 -> v7: (no changes)
---
 hw/acpi/Makefile.objs  |   2 +-
 hw/acpi/ich9.c         |  55 ++++++++++-
 hw/acpi/tco.c          | 264 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/i386/pc_q35.c       |   4 +-
 hw/isa/lpc_ich9.c      |  16 ++-
 include/hw/acpi/ich9.h |   6 +-
 include/hw/acpi/tco.h  |  82 +++++++++++++++
 include/hw/boards.h    |   3 +-
 include/hw/i386/ich9.h |  11 ++-
 include/hw/i386/pc.h   |   1 +
 10 files changed, 436 insertions(+), 8 deletions(-)
 create mode 100644 hw/acpi/tco.c
 create mode 100644 include/hw/acpi/tco.h

diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 29d46d8..3db1f07 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
+common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o tco.o
 common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
 common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI) += acpi_interface.o
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 8a64ffb..d3d9953 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -30,6 +30,7 @@
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "hw/acpi/acpi.h"
+#include "hw/acpi/tco.h"
 #include "sysemu/kvm.h"
 #include "exec/address-spaces.h"
 
@@ -92,8 +93,16 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
                             unsigned width)
 {
     ICH9LPCPMRegs *pm = opaque;
+    TCOIORegs *tr = &pm->tco_regs;
+    uint64_t tco_en;
+
     switch (addr) {
     case 0:
+        tco_en = pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
+        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
+        if (tr->tco.cnt1 & TCO_LOCK) {
+            val = (val & ~ICH9_PMIO_SMI_EN_TCO_EN) | tco_en;
+        }
         pm->smi_en &= ~pm->smi_en_wmask;
         pm->smi_en |= (val & pm->smi_en_wmask);
         break;
@@ -159,6 +168,25 @@ static const VMStateDescription vmstate_memhp_state = {
     }
 };
 
+static bool vmstate_test_use_tco(void *opaque)
+{
+    ICH9LPCPMRegs *s = opaque;
+    return s->enable_tco;
+}
+
+static const VMStateDescription vmstate_tco_io_state = {
+    .name = "ich9_pm/tco",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .needed = vmstate_test_use_tco,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
+                       TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 const VMStateDescription vmstate_ich9_pm = {
     .name = "ich9_pm",
     .version_id = 1,
@@ -179,6 +207,10 @@ const VMStateDescription vmstate_ich9_pm = {
     .subsections = (const VMStateDescription*[]) {
         &vmstate_memhp_state,
         NULL
+    },
+    .subsections = (const VMStateDescription*[]) {
+        &vmstate_tco_io_state,
+        NULL
     }
 };
 
@@ -209,7 +241,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
     acpi_pm1_evt_power_down(&pm->acpi_regs);
 }
 
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
                   qemu_irq sci_irq)
 {
     memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
@@ -231,6 +263,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
                           "acpi-smi", 8);
     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
 
+    pm->enable_tco = enable_tco;
+    if (pm->enable_tco) {
+        acpi_pm_tco_init(&pm->tco_regs, &pm->io);
+    }
+
     pm->irq = sci_irq;
     qemu_register_reset(pm_reset, pm);
     pm->powerdown_notifier.notify = pm_powerdown_req;
@@ -351,6 +388,18 @@ out:
     error_propagate(errp, local_err);
 }
 
+static bool ich9_pm_get_enable_tco(Object *obj, Error **errp)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+    return s->pm.enable_tco;
+}
+
+static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+    s->pm.enable_tco = value;
+}
+
 void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
 {
     static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
@@ -382,6 +431,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
                         ich9_pm_get_s4_val,
                         ich9_pm_set_s4_val,
                         NULL, pm, NULL);
+    object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
+                             ich9_pm_get_enable_tco,
+                             ich9_pm_set_enable_tco,
+                             NULL);
 }
 
 void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp)
diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
new file mode 100644
index 0000000..1794a54
--- /dev/null
+++ b/hw/acpi/tco.c
@@ -0,0 +1,264 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * 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.
+ */
+#include "qemu-common.h"
+#include "sysemu/watchdog.h"
+#include "hw/i386/ich9.h"
+
+#include "hw/acpi/tco.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define TCO_DEBUG(fmt, ...)                                     \
+    do {                                                        \
+        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
+    } while (0)
+#else
+#define TCO_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+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,
+};
+
+static inline void tco_timer_reload(TCOIORegs *tr)
+{
+    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
+    timer_mod(tr->tco_timer, tr->expire_time);
+}
+
+static inline void tco_timer_stop(TCOIORegs *tr)
+{
+    tr->expire_time = -1;
+}
+
+static void tco_timer_expired(void *opaque)
+{
+    TCOIORegs *tr = opaque;
+    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
+    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
+    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_CC_GCS);
+
+    tr->tco.rld = 0;
+    tr->tco.sts1 |= TCO_TIMEOUT;
+    if (++tr->timeouts_no == 2) {
+        tr->tco.sts2 |= TCO_SECOND_TO_STS;
+        tr->tco.sts2 |= TCO_BOOT_STS;
+        tr->timeouts_no = 0;
+
+        if (!(gcs & ICH9_CC_GCS_NO_REBOOT)) {
+            watchdog_perform_action();
+            tco_timer_stop(tr);
+            return;
+        }
+    }
+
+    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
+        ich9_generate_smi();
+    } else {
+        ich9_generate_nmi();
+    }
+    tr->tco.rld = tr->tco.tmr;
+    tco_timer_reload(tr);
+}
+
+/* NOTE: values of 0 or 1 will be ignored by ICH */
+static inline int can_start_tco_timer(TCOIORegs *tr)
+{
+    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
+}
+
+static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
+{
+    uint16_t rld;
+
+    switch (addr) {
+    case TCO_RLD:
+        if (tr->expire_time != -1) {
+            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
+            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
+        } else {
+            rld = tr->tco.rld;
+        }
+        return rld;
+    case TCO_DAT_IN:
+        return tr->tco.din;
+    case TCO_DAT_OUT:
+        return tr->tco.dout;
+    case TCO1_STS:
+        return tr->tco.sts1;
+    case TCO2_STS:
+        return tr->tco.sts2;
+    case TCO1_CNT:
+        return tr->tco.cnt1;
+    case TCO2_CNT:
+        return tr->tco.cnt2;
+    case TCO_MESSAGE1:
+        return tr->tco.msg1;
+    case TCO_MESSAGE2:
+        return tr->tco.msg2;
+    case TCO_WDCNT:
+        return tr->tco.wdcnt;
+    case TCO_TMR:
+        return tr->tco.tmr;
+    case SW_IRQ_GEN:
+        return tr->sw_irq_gen;
+    }
+    return 0;
+}
+
+static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
+{
+    switch (addr) {
+    case TCO_RLD:
+        tr->timeouts_no = 0;
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tr->tco.rld = val;
+        }
+        break;
+    case TCO_DAT_IN:
+        tr->tco.din = val;
+        tr->tco.sts1 |= SW_TCO_SMI;
+        ich9_generate_smi();
+        break;
+    case TCO_DAT_OUT:
+        tr->tco.dout = val;
+        tr->tco.sts1 |= TCO_INT_STS;
+        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
+        break;
+    case TCO1_STS:
+        tr->tco.sts1 = val & TCO1_STS_MASK;
+        break;
+    case TCO2_STS:
+        tr->tco.sts2 = val & TCO2_STS_MASK;
+        break;
+    case TCO1_CNT:
+        val &= TCO1_CNT_MASK;
+        /*
+         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
+         * is required to change this bit from 1 to 0 -- it defaults to 0.
+         */
+        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tco_timer_stop(tr);
+        }
+        break;
+    case TCO2_CNT:
+        tr->tco.cnt2 = val;
+        break;
+    case TCO_MESSAGE1:
+        tr->tco.msg1 = val;
+        break;
+    case TCO_MESSAGE2:
+        tr->tco.msg2 = val;
+        break;
+    case TCO_WDCNT:
+        tr->tco.wdcnt = val;
+        break;
+    case TCO_TMR:
+        tr->tco.tmr = val;
+        break;
+    case SW_IRQ_GEN:
+        tr->sw_irq_gen = val;
+        break;
+    }
+}
+
+static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width)
+{
+    TCOIORegs *tr = opaque;
+    return tco_ioport_readw(tr, addr);
+}
+
+static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val,
+                          unsigned width)
+{
+    TCOIORegs *tr = opaque;
+    tco_ioport_writew(tr, addr, val);
+}
+
+static const MemoryRegionOps tco_io_ops = {
+    .read = tco_io_readw,
+    .write = tco_io_writew,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent)
+{
+    *tr = (TCOIORegs) {
+        .tco = {
+            .rld      = TCO_RLD_DEFAULT,
+            .din      = TCO_DAT_IN_DEFAULT,
+            .dout     = TCO_DAT_OUT_DEFAULT,
+            .sts1     = TCO1_STS_DEFAULT,
+            .sts2     = TCO2_STS_DEFAULT,
+            .cnt1     = TCO1_CNT_DEFAULT,
+            .cnt2     = TCO2_CNT_DEFAULT,
+            .msg1     = TCO_MESSAGE1_DEFAULT,
+            .msg2     = TCO_MESSAGE2_DEFAULT,
+            .wdcnt    = TCO_WDCNT_DEFAULT,
+            .tmr      = TCO_TMR_DEFAULT,
+        },
+        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
+        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
+        .expire_time   = -1,
+        .timeouts_no   = 0,
+    };
+    memory_region_init_io(&tr->io, memory_region_owner(parent),
+                          &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN);
+    memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io);
+}
+
+const VMStateDescription vmstate_tco_io_sts = {
+    .name = "tco io device status",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT16(tco.rld, TCOIORegs),
+        VMSTATE_UINT8(tco.din, TCOIORegs),
+        VMSTATE_UINT8(tco.dout, TCOIORegs),
+        VMSTATE_UINT16(tco.sts1, TCOIORegs),
+        VMSTATE_UINT16(tco.sts2, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
+        VMSTATE_UINT8(tco.msg1, TCOIORegs),
+        VMSTATE_UINT8(tco.msg2, TCOIORegs),
+        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
+        VMSTATE_UINT16(tco.tmr, TCOIORegs),
+        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
+        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
+        VMSTATE_INT64(expire_time, TCOIORegs),
+        VMSTATE_UINT8(timeouts_no, TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 082cd93..7e35a36 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -253,7 +253,7 @@ static void pc_q35_init(MachineState *machine)
                          (pc_machine->vmport != ON_OFF_AUTO_ON), 0xff0104);
 
     /* connect pm stuff to lpc */
-    ich9_lpc_pm_init(lpc);
+    ich9_lpc_pm_init(lpc, !mc->no_tco);
 
     /* ahci and SATA device, for q35 1 ahci controller is built-in */
     ahci = pci_create_simple_multifunction(host_bus,
@@ -393,6 +393,7 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
     m->default_machine_opts = "firmware=bios-256k.bin";
     m->default_display = "std";
     m->no_floppy = 1;
+    m->no_tco = 0;
     m->alias = "q35";
 }
 
@@ -404,6 +405,7 @@ static void pc_q35_2_3_machine_options(MachineClass *m)
 {
     pc_q35_2_4_machine_options(m);
     m->no_floppy = 0;
+    m->no_tco = 1;
     m->alias = NULL;
     SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
 }
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index b3e0b1f..b547002 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -138,6 +138,7 @@ static void ich9_cc_reset(ICH9LPCState *lpc)
     pci_set_long(c + ICH9_CC_D27IR, ICH9_CC_DIR_DEFAULT);
     pci_set_long(c + ICH9_CC_D26IR, ICH9_CC_DIR_DEFAULT);
     pci_set_long(c + ICH9_CC_D25IR, ICH9_CC_DIR_DEFAULT);
+    pci_set_long(c + ICH9_CC_GCS, ICH9_CC_GCS_DEFAULT);
 
     ich9_cc_update(lpc);
 }
@@ -313,6 +314,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
     return route;
 }
 
+void ich9_generate_smi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+}
+
+void ich9_generate_nmi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
+}
+
 static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
 {
     switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
@@ -357,11 +368,12 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
     }
 }
 
-void ich9_lpc_pm_init(PCIDevice *lpc_pci)
+void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool enable_tco)
 {
     ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
 
-    ich9_pm_init(lpc_pci, &lpc->pm, qemu_allocate_irq(ich9_set_sci, lpc, 0));
+    ich9_pm_init(lpc_pci, &lpc->pm, enable_tco,
+                 qemu_allocate_irq(ich9_set_sci, lpc, 0));
     ich9_lpc_reset(&lpc->d.qdev);
 }
 
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
index 77cc65c..bc3daec 100644
--- a/include/hw/acpi/ich9.h
+++ b/include/hw/acpi/ich9.h
@@ -25,6 +25,7 @@
 #include "hw/acpi/cpu_hotplug.h"
 #include "hw/acpi/memory_hotplug.h"
 #include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/tco.h"
 
 typedef struct ICH9LPCPMRegs {
     /*
@@ -54,9 +55,12 @@ typedef struct ICH9LPCPMRegs {
     uint8_t disable_s3;
     uint8_t disable_s4;
     uint8_t s4_val;
+    bool enable_tco;
+
+    TCOIORegs tco_regs;
 } ICH9LPCPMRegs;
 
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
                   qemu_irq sci_irq);
 void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base);
 extern const VMStateDescription vmstate_ich9_pm;
diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
new file mode 100644
index 0000000..c63afc8
--- /dev/null
+++ b/include/hw/acpi/tco.h
@@ -0,0 +1,82 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * 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.
+ */
+#ifndef HW_ACPI_TCO_H
+#define HW_ACPI_TCO_H
+
+#include "qemu/typedefs.h"
+#include "qemu-common.h"
+
+/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
+#define TCO_TICK_NSEC 600000000LL
+
+/* TCO I/O register offsets */
+enum {
+    TCO_RLD           = 0x00,
+    TCO_DAT_IN        = 0x02,
+    TCO_DAT_OUT       = 0x03,
+    TCO1_STS          = 0x04,
+    TCO2_STS          = 0x06,
+    TCO1_CNT          = 0x08,
+    TCO2_CNT          = 0x0a,
+    TCO_MESSAGE1      = 0x0c,
+    TCO_MESSAGE2      = 0x0d,
+    TCO_WDCNT         = 0x0e,
+    SW_IRQ_GEN        = 0x10,
+    TCO_TMR           = 0x12,
+};
+
+/* TCO I/O register control/status bits */
+enum {
+    SW_TCO_SMI           = 1 << 1,
+    TCO_INT_STS          = 1 << 2,
+    TCO_LOCK             = 1 << 12,
+    TCO_TMR_HLT          = 1 << 11,
+    TCO_TIMEOUT          = 1 << 3,
+    TCO_SECOND_TO_STS    = 1 << 1,
+    TCO_BOOT_STS         = 1 << 2,
+};
+
+/* TCO I/O registers mask bits */
+enum {
+    TCO_RLD_MASK     = 0x3ff,
+    TCO1_STS_MASK    = 0xe870,
+    TCO2_STS_MASK    = 0xfff8,
+    TCO1_CNT_MASK    = 0xfeff,
+    TCO_TMR_MASK     = 0x3ff,
+};
+
+typedef struct TCOIORegs {
+    struct {
+        uint16_t rld;
+        uint8_t din;
+        uint8_t dout;
+        uint16_t sts1;
+        uint16_t sts2;
+        uint16_t cnt1;
+        uint16_t cnt2;
+        uint8_t msg1;
+        uint8_t msg2;
+        uint8_t wdcnt;
+        uint16_t tmr;
+    } tco;
+    uint8_t sw_irq_gen;
+
+    QEMUTimer *tco_timer;
+    int64_t expire_time;
+    uint8_t timeouts_no;
+
+    MemoryRegion io;
+} TCOIORegs;
+
+/* tco.c */
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent);
+
+extern const VMStateDescription vmstate_tco_io_sts;
+
+#endif /* HW_ACPI_TCO_H */
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 6379901..2aec9cb 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -99,7 +99,8 @@ struct MachineClass {
         no_floppy:1,
         no_cdrom:1,
         no_sdcard:1,
-        has_dynamic_sysbus:1;
+        has_dynamic_sysbus:1,
+        no_tco:1;
     int is_default;
     const char *default_machine_opts;
     const char *default_boot_order;
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index a2cc15c..f5681a3 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -17,9 +17,12 @@
 void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
 int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx);
 PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
-void ich9_lpc_pm_init(PCIDevice *pci_lpc);
+void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool enable_tco);
 I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
 
+void ich9_generate_smi(void);
+void ich9_generate_nmi(void);
+
 #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
 
 #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
@@ -90,6 +93,9 @@ Object *ich9_lpc_find(void);
 #define ICH9_CC_DIR_MASK                        0x7
 #define ICH9_CC_OIC                             0x31FF
 #define ICH9_CC_OIC_AEN                         0x1
+#define ICH9_CC_GCS                             0x3410
+#define ICH9_CC_GCS_DEFAULT                     0x00000020
+#define ICH9_CC_GCS_NO_REBOOT                   (1 << 5)
 
 /* D28:F[0-5] */
 #define ICH9_PCIE_DEV                           28
@@ -186,7 +192,10 @@ Object *ich9_lpc_find(void);
 #define ICH9_PMIO_GPE0_LEN                      16
 #define ICH9_PMIO_SMI_EN                        0x30
 #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
+#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
 #define ICH9_PMIO_SMI_STS                       0x34
+#define ICH9_PMIO_TCO_RLD                       0x60
+#define ICH9_PMIO_TCO_LEN                       32
 
 /* FADT ACPI_ENABLE/ACPI_DISABLE */
 #define ICH9_APM_ACPI_ENABLE                    0x2
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 86c5651..c1afdc0 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -89,6 +89,7 @@ typedef struct PcPciInfo {
 #define ACPI_PM_PROP_PM_IO_BASE "pm_io_base"
 #define ACPI_PM_PROP_GPE0_BLK "gpe0_blk"
 #define ACPI_PM_PROP_GPE0_BLK_LEN "gpe0_blk_len"
+#define ACPI_PM_PROP_TCO_ENABLED "enable_tco"
 
 struct PcGuestInfo {
     bool isapc_ram_fw;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v7 2/3] tests: add testcase for TCO watchdog emulation
  2015-06-27 17:56 ` [Qemu-devel] [PATCH v7 1/3] ich9: add TCO interface emulation Paulo Alcantara
@ 2015-06-27 17:56   ` Paulo Alcantara
  2015-06-27 17:56   ` [Qemu-devel] [PATCH v7 3/3] ich9: implement strap SPKR pin logic Paulo Alcantara
  1 sibling, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-27 17:56 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

This patch adds a testcase that covers the following:
  1) TCO default values
  2) first and second TCO timeout
  3) watch and validate ticks counter through TCO_RLD register
  4) maximum supported TCO timeout (0x3ff)
  5) watchdog actions (pause/reset/shutdown/none) upon second TCO
     timeout
  6) set and get of TCO control and status bits

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * some cleanup
  * add test for TCO_LOCK bit

v2 -> v3:
  * add tests for TCO control & status bits
  * fix check of SECOND_TO_STS bit (it's set in TCO2_STS reg)

v3 -> v4:
  * add more description to commit log
  * use RCBA_BASE_ADDR macro defintion from hw/i386/ich9-cc.h instead

v4 -> v5:
  * use modified macros (now prefixed with ICH9_) from ich9-cc.h
  * move license to GPLv2+

v5 -> v6:
  * remove include of "hw/i386/ich9-cc.h" since it's no longer exist

v6 -> v7: (no changes)
---
 tests/Makefile   |   2 +
 tests/tco-test.c | 460 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 462 insertions(+)
 create mode 100644 tests/tco-test.c

diff --git a/tests/Makefile b/tests/Makefile
index eff5e11..ef1e981 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -152,6 +152,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
 check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
+check-qtest-i386-y += tests/tco-test$(EXESUF)
 gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
 check-qtest-i386-y += $(check-qtest-pci-y)
 gcov-files-i386-y += $(gcov-files-pci-y)
@@ -370,6 +371,7 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
 tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
 tests/ne2000-test$(EXESUF): tests/ne2000-test.o
 tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
+tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
 tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
 tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
 tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
diff --git a/tests/tco-test.c b/tests/tco-test.c
new file mode 100644
index 0000000..1a2fe3d
--- /dev/null
+++ b/tests/tco-test.c
@@ -0,0 +1,460 @@
+/*
+ * 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.
+ */
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/i386/ich9.h"
+#include "hw/acpi/ich9.h"
+#include "hw/acpi/tco.h"
+
+#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;
+    QPCIDevice *dev;
+    void *lpc_base;
+    void *tco_io_base;
+} TestData;
+
+static void test_init(TestData *d)
+{
+    QPCIBus *bus;
+    QTestState *qs;
+    char *s;
+
+    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
+    qs = qtest_start(s);
+    qtest_irq_intercept_in(qs, "ioapic");
+    g_free(s);
+
+    bus = qpci_init_pc();
+    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
+    g_assert(d->dev != NULL);
+
+    /* map PCI-to-LPC bridge interface BAR */
+    d->lpc_base = qpci_iomap(d->dev, 0, NULL);
+
+    qpci_device_enable(d->dev);
+
+    g_assert(d->lpc_base != NULL);
+
+    /* set ACPI PM I/O space base address */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_PMBASE,
+                       PM_IO_BASE_ADDR | 0x1);
+    /* enable ACPI I/O */
+    qpci_config_writeb(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_ACPI_CTRL,
+                       0x80);
+    /* set Root Complex BAR */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_RCBA,
+                       RCBA_BASE_ADDR | 0x1);
+
+    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
+}
+
+static void stop_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val |= TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void start_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val &= ~TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void load_tco(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
+}
+
+static void set_tco_timeout(const TestData *d, uint16_t ticks)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
+}
+
+static void clear_tco_status(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
+}
+
+static void reset_on_second_timeout(bool enable)
+{
+    uint32_t val;
+
+    val = readl(RCBA_BASE_ADDR + ICH9_CC_GCS);
+    if (enable) {
+        val &= ~ICH9_CC_GCS_NO_REBOOT;
+    } else {
+        val |= ICH9_CC_GCS_NO_REBOOT;
+    }
+    writel(RCBA_BASE_ADDR + ICH9_CC_GCS, val);
+}
+
+static void test_tco_defaults(void)
+{
+    TestData d;
+
+    d.args = NULL;
+    test_init(&d);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+                    TCO_RLD_DEFAULT);
+    /* TCO_DAT_IN & TCO_DAT_OUT */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
+                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
+    /* TCO1_STS & TCO2_STS */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
+                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
+    /* TCO1_CNT & TCO2_CNT */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
+    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
+                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
+                    TCO_WDCNT_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
+                    SW_IRQ_GEN_DEFAULT);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
+                    TCO_TMR_DEFAULT);
+    qtest_end();
+}
+
+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;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    /* test first timeout */
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    /* test clearing timeout bit */
+    val |= TCO_TIMEOUT;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+
+    /* test second timeout */
+    clock_step(ticks * TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco_max_timeout(void)
+{
+    TestData d;
+    const uint16_t ticks = 0xffff;
+    uint32_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
+    g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+    clock_step(TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static QDict *get_watchdog_action(void)
+{
+    QDict *ev = qmp("");
+    QDict *data;
+    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
+
+    data = qdict_get_qdict(ev, "data");
+    QINCREF(data);
+    QDECREF(ev);
+    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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_ticks_counter(void)
+{
+    TestData d;
+    uint16_t ticks = TCO_SECS_TO_TICKS(8);
+    uint16_t rld;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+
+    do {
+        rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
+        g_assert_cmpint(rld, ==, ticks);
+        clock_step(TCO_TICK_NSEC);
+        ticks--;
+    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco1_control_bits(void)
+{
+    TestData d;
+    uint16_t val;
+
+    d.args = NULL;
+    test_init(&d);
+
+    val = TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    val &= ~TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    TCO_LOCK);
+    qtest_end();
+}
+
+static void test_tco1_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0);
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0);
+    qtest_end();
+}
+
+static void test_tco2_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = "-watchdog-action none";
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0);
+    qtest_end();
+}
+
+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();
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v7 3/3] ich9: implement strap SPKR pin logic
  2015-06-27 17:56 ` [Qemu-devel] [PATCH v7 1/3] ich9: add TCO interface emulation Paulo Alcantara
  2015-06-27 17:56   ` [Qemu-devel] [PATCH v7 2/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
@ 2015-06-27 17:56   ` Paulo Alcantara
  2015-06-28  8:37     ` Michael S. Tsirkin
  1 sibling, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-27 17:56 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

If the signal is sampled high, this indicates that the system is
strapped to the "No Reboot" mode (ICH9 will disable the TCO Timer system
reboot feature). The status of this strap is readable via the NO_REBOOT
bit (CC: offset 0x3410:bit 5).

The NO_REBOOT bit is set when SPKR pin on ICH9 is sampled high. This bit
may be set or cleared by software if the strap is sampled low but may
not override the strap when it indicates "No Reboot".

This patch implements the logic where hardware has ability to set SPKR
pin through a property named "pin-spkr" and it's sampled low by default.

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 hw/acpi/tco.c          |  3 ++-
 hw/isa/lpc_ich9.c      | 38 ++++++++++++++++++++++++++++++++++++++
 include/hw/i386/ich9.h | 11 +++++++++++
 3 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
index 1794a54..c1f5739 100644
--- a/hw/acpi/tco.c
+++ b/hw/acpi/tco.c
@@ -64,7 +64,8 @@ static void tco_timer_expired(void *opaque)
         tr->tco.sts2 |= TCO_BOOT_STS;
         tr->timeouts_no = 0;
 
-        if (!(gcs & ICH9_CC_GCS_NO_REBOOT)) {
+        if ((lpc->pin_strap.spkr & ICH9_PS_SPKR_PIN_LOW) &&
+            !(gcs & ICH9_CC_GCS_NO_REBOOT)) {
             watchdog_perform_action();
             tco_timer_stop(tr);
             return;
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index b547002..49d1f30 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -575,11 +575,49 @@ static void ich9_lpc_get_sci_int(Object *obj, Visitor *v,
     visit_type_uint32(v, &value, name, errp);
 }
 
+static void ich9_lpc_get_spkr_pin(Object *obj, Visitor *v,
+                                  void *opaque, const char *name,
+                                  Error **errp)
+{
+    ICH9LPCState *lpc = opaque;
+    uint8_t value = lpc->pin_strap.spkr;
+
+    visit_type_uint8(v, &value, name, errp);
+}
+
+static void ich9_lpc_set_spkr_pin(Object *obj, Visitor *v,
+                                  void *opaque, const char *name,
+                                  Error **errp)
+{
+    ICH9LPCState *lpc = opaque;
+    Error *local_err = NULL;
+    uint8_t value;
+    uint32_t *gcs;
+
+    visit_type_uint8(v, &value, name, &local_err);
+    if (local_err) {
+        goto out;
+    }
+    value &= ICH9_PS_SPKR_PIN_MASK;
+    if (value & ICH9_PS_SPKR_PIN_HIGH) {
+        gcs = (uint32_t *)&lpc->chip_config[ICH9_CC_GCS];
+        *gcs |= ICH9_CC_GCS_NO_REBOOT;
+    }
+    lpc->pin_strap.spkr = value;
+out:
+    error_propagate(errp, local_err);
+}
+
 static void ich9_lpc_add_properties(ICH9LPCState *lpc)
 {
     static const uint8_t acpi_enable_cmd = ICH9_APM_ACPI_ENABLE;
     static const uint8_t acpi_disable_cmd = ICH9_APM_ACPI_DISABLE;
+    lpc->pin_strap.spkr = ICH9_PS_SPKR_PIN_DEFAULT;
 
+    object_property_add(OBJECT(lpc), "pin-spkr", "uint8",
+                        ich9_lpc_get_spkr_pin,
+                        ich9_lpc_set_spkr_pin,
+                        NULL, lpc, NULL);
     object_property_add(OBJECT(lpc), ACPI_PM_PROP_SCI_INT, "uint32",
                         ich9_lpc_get_sci_int,
                         NULL, NULL, NULL, NULL);
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index f5681a3..aafc43f 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -46,6 +46,11 @@ typedef struct ICH9LPCState {
     ICH9LPCPMRegs pm;
     uint32_t sci_level; /* track sci level */
 
+    /* 2.24 Pin Straps */
+    struct {
+        uint8_t spkr;
+    } pin_strap;
+
     /* 10.1 Chipset Configuration registers(Memory Space)
      which is pointed by RCBA */
     uint8_t chip_config[ICH9_CC_SIZE];
@@ -72,6 +77,12 @@ Object *ich9_lpc_find(void);
 #define Q35_MASK(bit, ms_bit, ls_bit) \
 ((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1)))
 
+/* ICH9: Pin Straps */
+#define ICH9_PS_SPKR_PIN_LOW        0x01
+#define ICH9_PS_SPKR_PIN_HIGH       0x02
+#define ICH9_PS_SPKR_PIN_MASK       0x03
+#define ICH9_PS_SPKR_PIN_DEFAULT    ICH9_PS_SPKR_PIN_LOW
+
 /* ICH9: Chipset Configuration Registers */
 #define ICH9_CC_ADDR_MASK                       (ICH9_CC_SIZE - 1)
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v7 3/3] ich9: implement strap SPKR pin logic
  2015-06-27 17:56   ` [Qemu-devel] [PATCH v7 3/3] ich9: implement strap SPKR pin logic Paulo Alcantara
@ 2015-06-28  8:37     ` Michael S. Tsirkin
  2015-06-28 16:21       ` Paulo Alcantara
  0 siblings, 1 reply; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-06-28  8:37 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Sat, Jun 27, 2015 at 02:56:33PM -0300, Paulo Alcantara wrote:
> If the signal is sampled high, this indicates that the system is
> strapped to the "No Reboot" mode (ICH9 will disable the TCO Timer system
> reboot feature). The status of this strap is readable via the NO_REBOOT
> bit (CC: offset 0x3410:bit 5).
> 
> The NO_REBOOT bit is set when SPKR pin on ICH9 is sampled high. This bit
> may be set or cleared by software if the strap is sampled low but may
> not override the strap when it indicates "No Reboot".
> 
> This patch implements the logic where hardware has ability to set SPKR
> pin through a property named "pin-spkr"

I would call it "noreboot" and not pin-spkr
since that's what it does in the end.

> and it's sampled low by default.

I think sample high is a safer default.

> 
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
>  hw/acpi/tco.c          |  3 ++-
>  hw/isa/lpc_ich9.c      | 38 ++++++++++++++++++++++++++++++++++++++
>  include/hw/i386/ich9.h | 11 +++++++++++
>  3 files changed, 51 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> index 1794a54..c1f5739 100644
> --- a/hw/acpi/tco.c
> +++ b/hw/acpi/tco.c
> @@ -64,7 +64,8 @@ static void tco_timer_expired(void *opaque)
>          tr->tco.sts2 |= TCO_BOOT_STS;
>          tr->timeouts_no = 0;
>  
> -        if (!(gcs & ICH9_CC_GCS_NO_REBOOT)) {
> +        if ((lpc->pin_strap.spkr & ICH9_PS_SPKR_PIN_LOW) &&
> +            !(gcs & ICH9_CC_GCS_NO_REBOOT)) {
>              watchdog_perform_action();
>              tco_timer_stop(tr);
>              return;
> diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> index b547002..49d1f30 100644
> --- a/hw/isa/lpc_ich9.c
> +++ b/hw/isa/lpc_ich9.c
> @@ -575,11 +575,49 @@ static void ich9_lpc_get_sci_int(Object *obj, Visitor *v,
>      visit_type_uint32(v, &value, name, errp);
>  }
>  
> +static void ich9_lpc_get_spkr_pin(Object *obj, Visitor *v,
> +                                  void *opaque, const char *name,
> +                                  Error **errp)
> +{
> +    ICH9LPCState *lpc = opaque;
> +    uint8_t value = lpc->pin_strap.spkr;
> +
> +    visit_type_uint8(v, &value, name, errp);
> +}
> +
> +static void ich9_lpc_set_spkr_pin(Object *obj, Visitor *v,
> +                                  void *opaque, const char *name,
> +                                  Error **errp)
> +{
> +    ICH9LPCState *lpc = opaque;
> +    Error *local_err = NULL;
> +    uint8_t value;
> +    uint32_t *gcs;
> +
> +    visit_type_uint8(v, &value, name, &local_err);
> +    if (local_err) {
> +        goto out;
> +    }
> +    value &= ICH9_PS_SPKR_PIN_MASK;
> +    if (value & ICH9_PS_SPKR_PIN_HIGH) {
> +        gcs = (uint32_t *)&lpc->chip_config[ICH9_CC_GCS];
> +        *gcs |= ICH9_CC_GCS_NO_REBOOT;
> +    }
> +    lpc->pin_strap.spkr = value;
> +out:
> +    error_propagate(errp, local_err);
> +}
> +
>  static void ich9_lpc_add_properties(ICH9LPCState *lpc)
>  {
>      static const uint8_t acpi_enable_cmd = ICH9_APM_ACPI_ENABLE;
>      static const uint8_t acpi_disable_cmd = ICH9_APM_ACPI_DISABLE;
> +    lpc->pin_strap.spkr = ICH9_PS_SPKR_PIN_DEFAULT;
>  
> +    object_property_add(OBJECT(lpc), "pin-spkr", "uint8",
> +                        ich9_lpc_get_spkr_pin,
> +                        ich9_lpc_set_spkr_pin,
> +                        NULL, lpc, NULL);
>      object_property_add(OBJECT(lpc), ACPI_PM_PROP_SCI_INT, "uint32",
>                          ich9_lpc_get_sci_int,
>                          NULL, NULL, NULL, NULL);

BTW it's easier to add simple properties in dc->props
then you don't need all the error propagate code etc.


> diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
> index f5681a3..aafc43f 100644
> --- a/include/hw/i386/ich9.h
> +++ b/include/hw/i386/ich9.h
> @@ -46,6 +46,11 @@ typedef struct ICH9LPCState {
>      ICH9LPCPMRegs pm;
>      uint32_t sci_level; /* track sci level */
>  
> +    /* 2.24 Pin Straps */
> +    struct {
> +        uint8_t spkr;
> +    } pin_strap;
> +
>      /* 10.1 Chipset Configuration registers(Memory Space)
>       which is pointed by RCBA */
>      uint8_t chip_config[ICH9_CC_SIZE];
> @@ -72,6 +77,12 @@ Object *ich9_lpc_find(void);
>  #define Q35_MASK(bit, ms_bit, ls_bit) \
>  ((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1)))
>  
> +/* ICH9: Pin Straps */
> +#define ICH9_PS_SPKR_PIN_LOW        0x01
> +#define ICH9_PS_SPKR_PIN_HIGH       0x02
> +#define ICH9_PS_SPKR_PIN_MASK       0x03
> +#define ICH9_PS_SPKR_PIN_DEFAULT    ICH9_PS_SPKR_PIN_LOW
> +

The interface seems a bit inconvenient to me.
Why not make it a simple boolean property?


>  /* ICH9: Chipset Configuration Registers */
>  #define ICH9_CC_ADDR_MASK                       (ICH9_CC_SIZE - 1)
>  
> -- 
> 2.1.0

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v7 3/3] ich9: implement strap SPKR pin logic
  2015-06-28  8:37     ` Michael S. Tsirkin
@ 2015-06-28 16:21       ` Paulo Alcantara
  0 siblings, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-28 16:21 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: pbonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Sun, 28 Jun 2015 10:37:58 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Sat, Jun 27, 2015 at 02:56:33PM -0300, Paulo Alcantara wrote:
> > If the signal is sampled high, this indicates that the system is
> > strapped to the "No Reboot" mode (ICH9 will disable the TCO Timer
> > system reboot feature). The status of this strap is readable via
> > the NO_REBOOT bit (CC: offset 0x3410:bit 5).
> > 
> > The NO_REBOOT bit is set when SPKR pin on ICH9 is sampled high.
> > This bit may be set or cleared by software if the strap is sampled
> > low but may not override the strap when it indicates "No Reboot".
> > 
> > This patch implements the logic where hardware has ability to set
> > SPKR pin through a property named "pin-spkr"
> 
> I would call it "noreboot" and not pin-spkr
> since that's what it does in the end.

Right. That's also more user intuitive, indeed.

> 
> > and it's sampled low by default.
> 
> I think sample high is a safer default.

OK. I'll default it to high.

> 
> > 
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > ---
> >  hw/acpi/tco.c          |  3 ++-
> >  hw/isa/lpc_ich9.c      | 38 ++++++++++++++++++++++++++++++++++++++
> >  include/hw/i386/ich9.h | 11 +++++++++++
> >  3 files changed, 51 insertions(+), 1 deletion(-)
> > 
> > diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> > index 1794a54..c1f5739 100644
> > --- a/hw/acpi/tco.c
> > +++ b/hw/acpi/tco.c
> > @@ -64,7 +64,8 @@ static void tco_timer_expired(void *opaque)
> >          tr->tco.sts2 |= TCO_BOOT_STS;
> >          tr->timeouts_no = 0;
> >  
> > -        if (!(gcs & ICH9_CC_GCS_NO_REBOOT)) {
> > +        if ((lpc->pin_strap.spkr & ICH9_PS_SPKR_PIN_LOW) &&
> > +            !(gcs & ICH9_CC_GCS_NO_REBOOT)) {
> >              watchdog_perform_action();
> >              tco_timer_stop(tr);
> >              return;
> > diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> > index b547002..49d1f30 100644
> > --- a/hw/isa/lpc_ich9.c
> > +++ b/hw/isa/lpc_ich9.c
> > @@ -575,11 +575,49 @@ static void ich9_lpc_get_sci_int(Object *obj,
> > Visitor *v, visit_type_uint32(v, &value, name, errp);
> >  }
> >  
> > +static void ich9_lpc_get_spkr_pin(Object *obj, Visitor *v,
> > +                                  void *opaque, const char *name,
> > +                                  Error **errp)
> > +{
> > +    ICH9LPCState *lpc = opaque;
> > +    uint8_t value = lpc->pin_strap.spkr;
> > +
> > +    visit_type_uint8(v, &value, name, errp);
> > +}
> > +
> > +static void ich9_lpc_set_spkr_pin(Object *obj, Visitor *v,
> > +                                  void *opaque, const char *name,
> > +                                  Error **errp)
> > +{
> > +    ICH9LPCState *lpc = opaque;
> > +    Error *local_err = NULL;
> > +    uint8_t value;
> > +    uint32_t *gcs;
> > +
> > +    visit_type_uint8(v, &value, name, &local_err);
> > +    if (local_err) {
> > +        goto out;
> > +    }
> > +    value &= ICH9_PS_SPKR_PIN_MASK;
> > +    if (value & ICH9_PS_SPKR_PIN_HIGH) {
> > +        gcs = (uint32_t *)&lpc->chip_config[ICH9_CC_GCS];
> > +        *gcs |= ICH9_CC_GCS_NO_REBOOT;
> > +    }
> > +    lpc->pin_strap.spkr = value;
> > +out:
> > +    error_propagate(errp, local_err);
> > +}
> > +
> >  static void ich9_lpc_add_properties(ICH9LPCState *lpc)
> >  {
> >      static const uint8_t acpi_enable_cmd = ICH9_APM_ACPI_ENABLE;
> >      static const uint8_t acpi_disable_cmd = ICH9_APM_ACPI_DISABLE;
> > +    lpc->pin_strap.spkr = ICH9_PS_SPKR_PIN_DEFAULT;
> >  
> > +    object_property_add(OBJECT(lpc), "pin-spkr", "uint8",
> > +                        ich9_lpc_get_spkr_pin,
> > +                        ich9_lpc_set_spkr_pin,
> > +                        NULL, lpc, NULL);
> >      object_property_add(OBJECT(lpc), ACPI_PM_PROP_SCI_INT,
> > "uint32", ich9_lpc_get_sci_int,
> >                          NULL, NULL, NULL, NULL);
> 
> BTW it's easier to add simple properties in dc->props
> then you don't need all the error propagate code etc.

Hrm - good to know. I'll take a look at it. Thanks.

> 
> 
> > diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
> > index f5681a3..aafc43f 100644
> > --- a/include/hw/i386/ich9.h
> > +++ b/include/hw/i386/ich9.h
> > @@ -46,6 +46,11 @@ typedef struct ICH9LPCState {
> >      ICH9LPCPMRegs pm;
> >      uint32_t sci_level; /* track sci level */
> >  
> > +    /* 2.24 Pin Straps */
> > +    struct {
> > +        uint8_t spkr;
> > +    } pin_strap;
> > +
> >      /* 10.1 Chipset Configuration registers(Memory Space)
> >       which is pointed by RCBA */
> >      uint8_t chip_config[ICH9_CC_SIZE];
> > @@ -72,6 +77,12 @@ Object *ich9_lpc_find(void);
> >  #define Q35_MASK(bit, ms_bit, ls_bit) \
> >  ((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL <<
> > ls_bit) - 1))) 
> > +/* ICH9: Pin Straps */
> > +#define ICH9_PS_SPKR_PIN_LOW        0x01
> > +#define ICH9_PS_SPKR_PIN_HIGH       0x02
> > +#define ICH9_PS_SPKR_PIN_MASK       0x03
> > +#define ICH9_PS_SPKR_PIN_DEFAULT    ICH9_PS_SPKR_PIN_LOW
> > +
> 
> The interface seems a bit inconvenient to me.
> Why not make it a simple boolean property?

No real reason, actually. Since it has no more than 2 states (high and
low), a boolean property would be appropriate. I'll make it boolean.

Thanks,

Paulo

-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v8 1/3] ich9: add TCO interface emulation
  2015-05-27  0:29 [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paulo Alcantara
                   ` (7 preceding siblings ...)
  2015-06-27 17:56 ` [Qemu-devel] [PATCH v7 1/3] ich9: add TCO interface emulation Paulo Alcantara
@ 2015-06-28 17:58 ` Paulo Alcantara
  2015-06-28 17:58   ` [Qemu-devel] [PATCH v8 2/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
  2015-06-28 17:58   ` [Qemu-devel] [PATCH v8 3/3] ich9: implement strap SPKR pin logic Paulo Alcantara
  8 siblings, 2 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-28 17:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

This interface provides some registers within a 32-byte range and can be
acessed through PCI-to-LPC bridge interface (PMBASE + 0x60).

It's commonly used as a watchdog timer to detect system lockups through
SMIs that are generated -- if TCO_EN bit is set -- on every timeout. If
NO_REBOOT bit is not set in GCS (General Control and Status register),
the system will be resetted upon second timeout if TCO_RLD register
wasn't previously written to prevent timeout.

This patch adds support to TCO watchdog logic and few other features
like mapping NMIs to SMIs (NMI2SMI_EN bit), system intruder detection,
etc. are not implemented yet.

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * add migration support for TCO I/O device state
  * wake up only when total time expired instead of every 0.6s
  * some cleanup suggested by Paolo Bonzini

v2 -> v3:
  * set SECOND_TO_STS and BOOT_STS bits in TCO2_STS instead
  * improve handling of TCO_LOCK bit in TCO1_CNT register

v3 -> v4:
  * fix some conflicts in hw/acpi/ich9.c after rebasing against master
  * remove meaningless "use_tco" field from TCOIORegs structure
  * add a object property named "enable_tco" and only enable TCO support
    on pc-q35-2.4 and later

v4 -> v5:
  * remove unused field (use_tco) in TCOIORegs structure
  * move license to GPLv2+

v5 -> v6:
  * remove "io_tco" field from ICH9LPCPMRegs structure since it's no
    longer used
  * set ICH9_CC_GCS_NO_REBOOT bit by default in ICH9's LPC
    initialisation

v6 -> v7: (no changes)
---
 hw/acpi/Makefile.objs  |   2 +-
 hw/acpi/ich9.c         |  55 ++++++++++-
 hw/acpi/tco.c          | 264 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/i386/pc_q35.c       |   4 +-
 hw/isa/lpc_ich9.c      |  16 ++-
 include/hw/acpi/ich9.h |   6 +-
 include/hw/acpi/tco.h  |  82 +++++++++++++++
 include/hw/boards.h    |   3 +-
 include/hw/i386/ich9.h |  11 ++-
 include/hw/i386/pc.h   |   1 +
 10 files changed, 436 insertions(+), 8 deletions(-)
 create mode 100644 hw/acpi/tco.c
 create mode 100644 include/hw/acpi/tco.h

diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 29d46d8..3db1f07 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o
+common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o ich9.o pcihp.o tco.o
 common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
 common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI) += acpi_interface.o
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 8a64ffb..d3d9953 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -30,6 +30,7 @@
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
 #include "hw/acpi/acpi.h"
+#include "hw/acpi/tco.h"
 #include "sysemu/kvm.h"
 #include "exec/address-spaces.h"
 
@@ -92,8 +93,16 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
                             unsigned width)
 {
     ICH9LPCPMRegs *pm = opaque;
+    TCOIORegs *tr = &pm->tco_regs;
+    uint64_t tco_en;
+
     switch (addr) {
     case 0:
+        tco_en = pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
+        /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
+        if (tr->tco.cnt1 & TCO_LOCK) {
+            val = (val & ~ICH9_PMIO_SMI_EN_TCO_EN) | tco_en;
+        }
         pm->smi_en &= ~pm->smi_en_wmask;
         pm->smi_en |= (val & pm->smi_en_wmask);
         break;
@@ -159,6 +168,25 @@ static const VMStateDescription vmstate_memhp_state = {
     }
 };
 
+static bool vmstate_test_use_tco(void *opaque)
+{
+    ICH9LPCPMRegs *s = opaque;
+    return s->enable_tco;
+}
+
+static const VMStateDescription vmstate_tco_io_state = {
+    .name = "ich9_pm/tco",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .needed = vmstate_test_use_tco,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
+                       TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 const VMStateDescription vmstate_ich9_pm = {
     .name = "ich9_pm",
     .version_id = 1,
@@ -179,6 +207,10 @@ const VMStateDescription vmstate_ich9_pm = {
     .subsections = (const VMStateDescription*[]) {
         &vmstate_memhp_state,
         NULL
+    },
+    .subsections = (const VMStateDescription*[]) {
+        &vmstate_tco_io_state,
+        NULL
     }
 };
 
@@ -209,7 +241,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
     acpi_pm1_evt_power_down(&pm->acpi_regs);
 }
 
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
                   qemu_irq sci_irq)
 {
     memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
@@ -231,6 +263,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
                           "acpi-smi", 8);
     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
 
+    pm->enable_tco = enable_tco;
+    if (pm->enable_tco) {
+        acpi_pm_tco_init(&pm->tco_regs, &pm->io);
+    }
+
     pm->irq = sci_irq;
     qemu_register_reset(pm_reset, pm);
     pm->powerdown_notifier.notify = pm_powerdown_req;
@@ -351,6 +388,18 @@ out:
     error_propagate(errp, local_err);
 }
 
+static bool ich9_pm_get_enable_tco(Object *obj, Error **errp)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+    return s->pm.enable_tco;
+}
+
+static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp)
+{
+    ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+    s->pm.enable_tco = value;
+}
+
 void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
 {
     static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
@@ -382,6 +431,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
                         ich9_pm_get_s4_val,
                         ich9_pm_set_s4_val,
                         NULL, pm, NULL);
+    object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
+                             ich9_pm_get_enable_tco,
+                             ich9_pm_set_enable_tco,
+                             NULL);
 }
 
 void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp)
diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
new file mode 100644
index 0000000..1794a54
--- /dev/null
+++ b/hw/acpi/tco.c
@@ -0,0 +1,264 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * 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.
+ */
+#include "qemu-common.h"
+#include "sysemu/watchdog.h"
+#include "hw/i386/ich9.h"
+
+#include "hw/acpi/tco.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define TCO_DEBUG(fmt, ...)                                     \
+    do {                                                        \
+        fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__);    \
+    } while (0)
+#else
+#define TCO_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+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,
+};
+
+static inline void tco_timer_reload(TCOIORegs *tr)
+{
+    tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
+    timer_mod(tr->tco_timer, tr->expire_time);
+}
+
+static inline void tco_timer_stop(TCOIORegs *tr)
+{
+    tr->expire_time = -1;
+}
+
+static void tco_timer_expired(void *opaque)
+{
+    TCOIORegs *tr = opaque;
+    ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
+    ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
+    uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_CC_GCS);
+
+    tr->tco.rld = 0;
+    tr->tco.sts1 |= TCO_TIMEOUT;
+    if (++tr->timeouts_no == 2) {
+        tr->tco.sts2 |= TCO_SECOND_TO_STS;
+        tr->tco.sts2 |= TCO_BOOT_STS;
+        tr->timeouts_no = 0;
+
+        if (!(gcs & ICH9_CC_GCS_NO_REBOOT)) {
+            watchdog_perform_action();
+            tco_timer_stop(tr);
+            return;
+        }
+    }
+
+    if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
+        ich9_generate_smi();
+    } else {
+        ich9_generate_nmi();
+    }
+    tr->tco.rld = tr->tco.tmr;
+    tco_timer_reload(tr);
+}
+
+/* NOTE: values of 0 or 1 will be ignored by ICH */
+static inline int can_start_tco_timer(TCOIORegs *tr)
+{
+    return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
+}
+
+static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
+{
+    uint16_t rld;
+
+    switch (addr) {
+    case TCO_RLD:
+        if (tr->expire_time != -1) {
+            int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+            int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
+            rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
+        } else {
+            rld = tr->tco.rld;
+        }
+        return rld;
+    case TCO_DAT_IN:
+        return tr->tco.din;
+    case TCO_DAT_OUT:
+        return tr->tco.dout;
+    case TCO1_STS:
+        return tr->tco.sts1;
+    case TCO2_STS:
+        return tr->tco.sts2;
+    case TCO1_CNT:
+        return tr->tco.cnt1;
+    case TCO2_CNT:
+        return tr->tco.cnt2;
+    case TCO_MESSAGE1:
+        return tr->tco.msg1;
+    case TCO_MESSAGE2:
+        return tr->tco.msg2;
+    case TCO_WDCNT:
+        return tr->tco.wdcnt;
+    case TCO_TMR:
+        return tr->tco.tmr;
+    case SW_IRQ_GEN:
+        return tr->sw_irq_gen;
+    }
+    return 0;
+}
+
+static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
+{
+    switch (addr) {
+    case TCO_RLD:
+        tr->timeouts_no = 0;
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tr->tco.rld = val;
+        }
+        break;
+    case TCO_DAT_IN:
+        tr->tco.din = val;
+        tr->tco.sts1 |= SW_TCO_SMI;
+        ich9_generate_smi();
+        break;
+    case TCO_DAT_OUT:
+        tr->tco.dout = val;
+        tr->tco.sts1 |= TCO_INT_STS;
+        /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
+        break;
+    case TCO1_STS:
+        tr->tco.sts1 = val & TCO1_STS_MASK;
+        break;
+    case TCO2_STS:
+        tr->tco.sts2 = val & TCO2_STS_MASK;
+        break;
+    case TCO1_CNT:
+        val &= TCO1_CNT_MASK;
+        /*
+         * once TCO_LOCK bit is set, it can not be cleared by software. a reset
+         * is required to change this bit from 1 to 0 -- it defaults to 0.
+         */
+        tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
+        if (can_start_tco_timer(tr)) {
+            tr->tco.rld = tr->tco.tmr;
+            tco_timer_reload(tr);
+        } else {
+            tco_timer_stop(tr);
+        }
+        break;
+    case TCO2_CNT:
+        tr->tco.cnt2 = val;
+        break;
+    case TCO_MESSAGE1:
+        tr->tco.msg1 = val;
+        break;
+    case TCO_MESSAGE2:
+        tr->tco.msg2 = val;
+        break;
+    case TCO_WDCNT:
+        tr->tco.wdcnt = val;
+        break;
+    case TCO_TMR:
+        tr->tco.tmr = val;
+        break;
+    case SW_IRQ_GEN:
+        tr->sw_irq_gen = val;
+        break;
+    }
+}
+
+static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width)
+{
+    TCOIORegs *tr = opaque;
+    return tco_ioport_readw(tr, addr);
+}
+
+static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val,
+                          unsigned width)
+{
+    TCOIORegs *tr = opaque;
+    tco_ioport_writew(tr, addr, val);
+}
+
+static const MemoryRegionOps tco_io_ops = {
+    .read = tco_io_readw,
+    .write = tco_io_writew,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent)
+{
+    *tr = (TCOIORegs) {
+        .tco = {
+            .rld      = TCO_RLD_DEFAULT,
+            .din      = TCO_DAT_IN_DEFAULT,
+            .dout     = TCO_DAT_OUT_DEFAULT,
+            .sts1     = TCO1_STS_DEFAULT,
+            .sts2     = TCO2_STS_DEFAULT,
+            .cnt1     = TCO1_CNT_DEFAULT,
+            .cnt2     = TCO2_CNT_DEFAULT,
+            .msg1     = TCO_MESSAGE1_DEFAULT,
+            .msg2     = TCO_MESSAGE2_DEFAULT,
+            .wdcnt    = TCO_WDCNT_DEFAULT,
+            .tmr      = TCO_TMR_DEFAULT,
+        },
+        .sw_irq_gen    = SW_IRQ_GEN_DEFAULT,
+        .tco_timer     = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
+        .expire_time   = -1,
+        .timeouts_no   = 0,
+    };
+    memory_region_init_io(&tr->io, memory_region_owner(parent),
+                          &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN);
+    memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io);
+}
+
+const VMStateDescription vmstate_tco_io_sts = {
+    .name = "tco io device status",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT16(tco.rld, TCOIORegs),
+        VMSTATE_UINT8(tco.din, TCOIORegs),
+        VMSTATE_UINT8(tco.dout, TCOIORegs),
+        VMSTATE_UINT16(tco.sts1, TCOIORegs),
+        VMSTATE_UINT16(tco.sts2, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt1, TCOIORegs),
+        VMSTATE_UINT16(tco.cnt2, TCOIORegs),
+        VMSTATE_UINT8(tco.msg1, TCOIORegs),
+        VMSTATE_UINT8(tco.msg2, TCOIORegs),
+        VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
+        VMSTATE_UINT16(tco.tmr, TCOIORegs),
+        VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
+        VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
+        VMSTATE_INT64(expire_time, TCOIORegs),
+        VMSTATE_UINT8(timeouts_no, TCOIORegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 082cd93..7e35a36 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -253,7 +253,7 @@ static void pc_q35_init(MachineState *machine)
                          (pc_machine->vmport != ON_OFF_AUTO_ON), 0xff0104);
 
     /* connect pm stuff to lpc */
-    ich9_lpc_pm_init(lpc);
+    ich9_lpc_pm_init(lpc, !mc->no_tco);
 
     /* ahci and SATA device, for q35 1 ahci controller is built-in */
     ahci = pci_create_simple_multifunction(host_bus,
@@ -393,6 +393,7 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
     m->default_machine_opts = "firmware=bios-256k.bin";
     m->default_display = "std";
     m->no_floppy = 1;
+    m->no_tco = 0;
     m->alias = "q35";
 }
 
@@ -404,6 +405,7 @@ static void pc_q35_2_3_machine_options(MachineClass *m)
 {
     pc_q35_2_4_machine_options(m);
     m->no_floppy = 0;
+    m->no_tco = 1;
     m->alias = NULL;
     SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
 }
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index b3e0b1f..b547002 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -138,6 +138,7 @@ static void ich9_cc_reset(ICH9LPCState *lpc)
     pci_set_long(c + ICH9_CC_D27IR, ICH9_CC_DIR_DEFAULT);
     pci_set_long(c + ICH9_CC_D26IR, ICH9_CC_DIR_DEFAULT);
     pci_set_long(c + ICH9_CC_D25IR, ICH9_CC_DIR_DEFAULT);
+    pci_set_long(c + ICH9_CC_GCS, ICH9_CC_GCS_DEFAULT);
 
     ich9_cc_update(lpc);
 }
@@ -313,6 +314,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
     return route;
 }
 
+void ich9_generate_smi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+}
+
+void ich9_generate_nmi(void)
+{
+    cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
+}
+
 static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
 {
     switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
@@ -357,11 +368,12 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
     }
 }
 
-void ich9_lpc_pm_init(PCIDevice *lpc_pci)
+void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool enable_tco)
 {
     ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
 
-    ich9_pm_init(lpc_pci, &lpc->pm, qemu_allocate_irq(ich9_set_sci, lpc, 0));
+    ich9_pm_init(lpc_pci, &lpc->pm, enable_tco,
+                 qemu_allocate_irq(ich9_set_sci, lpc, 0));
     ich9_lpc_reset(&lpc->d.qdev);
 }
 
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
index 77cc65c..bc3daec 100644
--- a/include/hw/acpi/ich9.h
+++ b/include/hw/acpi/ich9.h
@@ -25,6 +25,7 @@
 #include "hw/acpi/cpu_hotplug.h"
 #include "hw/acpi/memory_hotplug.h"
 #include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/tco.h"
 
 typedef struct ICH9LPCPMRegs {
     /*
@@ -54,9 +55,12 @@ typedef struct ICH9LPCPMRegs {
     uint8_t disable_s3;
     uint8_t disable_s4;
     uint8_t s4_val;
+    bool enable_tco;
+
+    TCOIORegs tco_regs;
 } ICH9LPCPMRegs;
 
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, bool enable_tco,
                   qemu_irq sci_irq);
 void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base);
 extern const VMStateDescription vmstate_ich9_pm;
diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h
new file mode 100644
index 0000000..c63afc8
--- /dev/null
+++ b/include/hw/acpi/tco.h
@@ -0,0 +1,82 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * 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.
+ */
+#ifndef HW_ACPI_TCO_H
+#define HW_ACPI_TCO_H
+
+#include "qemu/typedefs.h"
+#include "qemu-common.h"
+
+/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */
+#define TCO_TICK_NSEC 600000000LL
+
+/* TCO I/O register offsets */
+enum {
+    TCO_RLD           = 0x00,
+    TCO_DAT_IN        = 0x02,
+    TCO_DAT_OUT       = 0x03,
+    TCO1_STS          = 0x04,
+    TCO2_STS          = 0x06,
+    TCO1_CNT          = 0x08,
+    TCO2_CNT          = 0x0a,
+    TCO_MESSAGE1      = 0x0c,
+    TCO_MESSAGE2      = 0x0d,
+    TCO_WDCNT         = 0x0e,
+    SW_IRQ_GEN        = 0x10,
+    TCO_TMR           = 0x12,
+};
+
+/* TCO I/O register control/status bits */
+enum {
+    SW_TCO_SMI           = 1 << 1,
+    TCO_INT_STS          = 1 << 2,
+    TCO_LOCK             = 1 << 12,
+    TCO_TMR_HLT          = 1 << 11,
+    TCO_TIMEOUT          = 1 << 3,
+    TCO_SECOND_TO_STS    = 1 << 1,
+    TCO_BOOT_STS         = 1 << 2,
+};
+
+/* TCO I/O registers mask bits */
+enum {
+    TCO_RLD_MASK     = 0x3ff,
+    TCO1_STS_MASK    = 0xe870,
+    TCO2_STS_MASK    = 0xfff8,
+    TCO1_CNT_MASK    = 0xfeff,
+    TCO_TMR_MASK     = 0x3ff,
+};
+
+typedef struct TCOIORegs {
+    struct {
+        uint16_t rld;
+        uint8_t din;
+        uint8_t dout;
+        uint16_t sts1;
+        uint16_t sts2;
+        uint16_t cnt1;
+        uint16_t cnt2;
+        uint8_t msg1;
+        uint8_t msg2;
+        uint8_t wdcnt;
+        uint16_t tmr;
+    } tco;
+    uint8_t sw_irq_gen;
+
+    QEMUTimer *tco_timer;
+    int64_t expire_time;
+    uint8_t timeouts_no;
+
+    MemoryRegion io;
+} TCOIORegs;
+
+/* tco.c */
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent);
+
+extern const VMStateDescription vmstate_tco_io_sts;
+
+#endif /* HW_ACPI_TCO_H */
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 6379901..2aec9cb 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -99,7 +99,8 @@ struct MachineClass {
         no_floppy:1,
         no_cdrom:1,
         no_sdcard:1,
-        has_dynamic_sysbus:1;
+        has_dynamic_sysbus:1,
+        no_tco:1;
     int is_default;
     const char *default_machine_opts;
     const char *default_boot_order;
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index a2cc15c..f5681a3 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -17,9 +17,12 @@
 void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
 int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx);
 PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin);
-void ich9_lpc_pm_init(PCIDevice *pci_lpc);
+void ich9_lpc_pm_init(PCIDevice *pci_lpc, bool enable_tco);
 I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
 
+void ich9_generate_smi(void);
+void ich9_generate_nmi(void);
+
 #define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
 
 #define TYPE_ICH9_LPC_DEVICE "ICH9-LPC"
@@ -90,6 +93,9 @@ Object *ich9_lpc_find(void);
 #define ICH9_CC_DIR_MASK                        0x7
 #define ICH9_CC_OIC                             0x31FF
 #define ICH9_CC_OIC_AEN                         0x1
+#define ICH9_CC_GCS                             0x3410
+#define ICH9_CC_GCS_DEFAULT                     0x00000020
+#define ICH9_CC_GCS_NO_REBOOT                   (1 << 5)
 
 /* D28:F[0-5] */
 #define ICH9_PCIE_DEV                           28
@@ -186,7 +192,10 @@ Object *ich9_lpc_find(void);
 #define ICH9_PMIO_GPE0_LEN                      16
 #define ICH9_PMIO_SMI_EN                        0x30
 #define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
+#define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
 #define ICH9_PMIO_SMI_STS                       0x34
+#define ICH9_PMIO_TCO_RLD                       0x60
+#define ICH9_PMIO_TCO_LEN                       32
 
 /* FADT ACPI_ENABLE/ACPI_DISABLE */
 #define ICH9_APM_ACPI_ENABLE                    0x2
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 86c5651..c1afdc0 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -89,6 +89,7 @@ typedef struct PcPciInfo {
 #define ACPI_PM_PROP_PM_IO_BASE "pm_io_base"
 #define ACPI_PM_PROP_GPE0_BLK "gpe0_blk"
 #define ACPI_PM_PROP_GPE0_BLK_LEN "gpe0_blk_len"
+#define ACPI_PM_PROP_TCO_ENABLED "enable_tco"
 
 struct PcGuestInfo {
     bool isapc_ram_fw;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v8 2/3] tests: add testcase for TCO watchdog emulation
  2015-06-28 17:58 ` [Qemu-devel] [PATCH v8 1/3] ich9: add TCO interface emulation Paulo Alcantara
@ 2015-06-28 17:58   ` Paulo Alcantara
  2015-06-28 17:58   ` [Qemu-devel] [PATCH v8 3/3] ich9: implement strap SPKR pin logic Paulo Alcantara
  1 sibling, 0 replies; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-28 17:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

This patch adds a testcase that covers the following:
  1) TCO default values
  2) first and second TCO timeout
  3) watch and validate ticks counter through TCO_RLD register
  4) maximum supported TCO timeout (0x3ff)
  5) watchdog actions (pause/reset/shutdown/none) upon second TCO
     timeout
  6) set and get of TCO control and status bits

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v1 -> v2:
  * some cleanup
  * add test for TCO_LOCK bit

v2 -> v3:
  * add tests for TCO control & status bits
  * fix check of SECOND_TO_STS bit (it's set in TCO2_STS reg)

v3 -> v4:
  * add more description to commit log
  * use RCBA_BASE_ADDR macro defintion from hw/i386/ich9-cc.h instead

v4 -> v5:
  * use modified macros (now prefixed with ICH9_) from ich9-cc.h
  * move license to GPLv2+

v5 -> v6:
  * remove include of "hw/i386/ich9-cc.h" since it's no longer exist

v6 -> v7: (no changes)
---
 tests/Makefile   |   2 +
 tests/tco-test.c | 460 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 462 insertions(+)
 create mode 100644 tests/tco-test.c

diff --git a/tests/Makefile b/tests/Makefile
index eff5e11..ef1e981 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -152,6 +152,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 check-qtest-i386-y += tests/drive_del-test$(EXESUF)
 check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
+check-qtest-i386-y += tests/tco-test$(EXESUF)
 gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
 check-qtest-i386-y += $(check-qtest-pci-y)
 gcov-files-i386-y += $(gcov-files-pci-y)
@@ -370,6 +371,7 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
 tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
 tests/ne2000-test$(EXESUF): tests/ne2000-test.o
 tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
+tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
 tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
 tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
 tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
diff --git a/tests/tco-test.c b/tests/tco-test.c
new file mode 100644
index 0000000..1a2fe3d
--- /dev/null
+++ b/tests/tco-test.c
@@ -0,0 +1,460 @@
+/*
+ * 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.
+ */
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/i386/ich9.h"
+#include "hw/acpi/ich9.h"
+#include "hw/acpi/tco.h"
+
+#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;
+    QPCIDevice *dev;
+    void *lpc_base;
+    void *tco_io_base;
+} TestData;
+
+static void test_init(TestData *d)
+{
+    QPCIBus *bus;
+    QTestState *qs;
+    char *s;
+
+    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
+    qs = qtest_start(s);
+    qtest_irq_intercept_in(qs, "ioapic");
+    g_free(s);
+
+    bus = qpci_init_pc();
+    d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
+    g_assert(d->dev != NULL);
+
+    /* map PCI-to-LPC bridge interface BAR */
+    d->lpc_base = qpci_iomap(d->dev, 0, NULL);
+
+    qpci_device_enable(d->dev);
+
+    g_assert(d->lpc_base != NULL);
+
+    /* set ACPI PM I/O space base address */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_PMBASE,
+                       PM_IO_BASE_ADDR | 0x1);
+    /* enable ACPI I/O */
+    qpci_config_writeb(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_ACPI_CTRL,
+                       0x80);
+    /* set Root Complex BAR */
+    qpci_config_writel(d->dev, (uintptr_t)d->lpc_base + ICH9_LPC_RCBA,
+                       RCBA_BASE_ADDR | 0x1);
+
+    d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
+}
+
+static void stop_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val |= TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void start_tco(const TestData *d)
+{
+    uint32_t val;
+
+    val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+    val &= ~TCO_TMR_HLT;
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void load_tco(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
+}
+
+static void set_tco_timeout(const TestData *d, uint16_t ticks)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
+}
+
+static void clear_tco_status(const TestData *d)
+{
+    qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
+    qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
+}
+
+static void reset_on_second_timeout(bool enable)
+{
+    uint32_t val;
+
+    val = readl(RCBA_BASE_ADDR + ICH9_CC_GCS);
+    if (enable) {
+        val &= ~ICH9_CC_GCS_NO_REBOOT;
+    } else {
+        val |= ICH9_CC_GCS_NO_REBOOT;
+    }
+    writel(RCBA_BASE_ADDR + ICH9_CC_GCS, val);
+}
+
+static void test_tco_defaults(void)
+{
+    TestData d;
+
+    d.args = NULL;
+    test_init(&d);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+                    TCO_RLD_DEFAULT);
+    /* TCO_DAT_IN & TCO_DAT_OUT */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
+                    (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
+    /* TCO1_STS & TCO2_STS */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
+                    (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
+    /* TCO1_CNT & TCO2_CNT */
+    g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
+    /* TCO_MESSAGE1 & TCO_MESSAGE2 */
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
+                    (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
+                    TCO_WDCNT_DEFAULT);
+    g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
+                    SW_IRQ_GEN_DEFAULT);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
+                    TCO_TMR_DEFAULT);
+    qtest_end();
+}
+
+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;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    /* test first timeout */
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    /* test clearing timeout bit */
+    val |= TCO_TIMEOUT;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+
+    /* test second timeout */
+    clock_step(ticks * TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & TCO_SECOND_TO_STS ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco_max_timeout(void)
+{
+    TestData d;
+    const uint16_t ticks = 0xffff;
+    uint32_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
+    g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 0);
+    clock_step(TCO_TICK_NSEC);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & TCO_TIMEOUT ? 1 : 0;
+    g_assert(ret == 1);
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static QDict *get_watchdog_action(void)
+{
+    QDict *ev = qmp("");
+    QDict *data;
+    g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
+
+    data = qdict_get_qdict(ev, "data");
+    QINCREF(data);
+    QDECREF(ev);
+    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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+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";
+    test_init(&td);
+
+    stop_tco(&td);
+    clear_tco_status(&td);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&td, ticks);
+    load_tco(&td);
+    start_tco(&td);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+    ad = get_watchdog_action();
+    g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
+    QDECREF(ad);
+
+    stop_tco(&td);
+    qtest_end();
+}
+
+static void test_tco_ticks_counter(void)
+{
+    TestData d;
+    uint16_t ticks = TCO_SECS_TO_TICKS(8);
+    uint16_t rld;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+
+    do {
+        rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
+        g_assert_cmpint(rld, ==, ticks);
+        clock_step(TCO_TICK_NSEC);
+        ticks--;
+    } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
+
+    stop_tco(&d);
+    qtest_end();
+}
+
+static void test_tco1_control_bits(void)
+{
+    TestData d;
+    uint16_t val;
+
+    d.args = NULL;
+    test_init(&d);
+
+    val = TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    val &= ~TCO_LOCK;
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
+                    TCO_LOCK);
+    qtest_end();
+}
+
+static void test_tco1_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = NULL;
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(false);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC);
+
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0);
+    qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0);
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+    ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0);
+    qtest_end();
+}
+
+static void test_tco2_status_bits(void)
+{
+    TestData d;
+    uint16_t ticks = 8;
+    uint16_t val;
+    int ret;
+
+    d.args = "-watchdog-action none";
+    test_init(&d);
+
+    stop_tco(&d);
+    clear_tco_status(&d);
+    reset_on_second_timeout(true);
+    set_tco_timeout(&d, ticks);
+    load_tco(&d);
+    start_tco(&d);
+    clock_step(ticks * TCO_TICK_NSEC * 2);
+
+    val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+    ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
+    g_assert(ret == 1);
+    qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val);
+    g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0);
+    qtest_end();
+}
+
+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();
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [Qemu-devel] [PATCH v8 3/3] ich9: implement strap SPKR pin logic
  2015-06-28 17:58 ` [Qemu-devel] [PATCH v8 1/3] ich9: add TCO interface emulation Paulo Alcantara
  2015-06-28 17:58   ` [Qemu-devel] [PATCH v8 2/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
@ 2015-06-28 17:58   ` Paulo Alcantara
  2015-07-01 13:18     ` Paolo Bonzini
  1 sibling, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-06-28 17:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, seabios, Paulo Alcantara, kraxel, mst

If the signal is sampled high, this indicates that the system is
strapped to the "No Reboot" mode (ICH9 will disable the TCO Timer system
reboot feature). The status of this strap is readable via the NO_REBOOT
bit (CC: offset 0x3410:bit 5).

The NO_REBOOT bit is set when SPKR pin on ICH9 is sampled high. This bit
may be set or cleared by software if the strap is sampled low but may
not override the strap when it indicates "No Reboot".

This patch implements the logic where hardware has ability to set SPKR
pin through a property named "noreboot" and it's sampled high by
default.

Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
v7 -> v8:
  * change property name to "noreboot"
  * default "noreboot" property to high
  * define property in dc->props
  * update tco tests to support and exercise "noreboot" property
---
 hw/acpi/tco.c          |  2 +-
 hw/isa/lpc_ich9.c      |  6 ++++++
 include/hw/i386/ich9.h |  5 +++++
 tests/tco-test.c       | 18 ++++++++++++++++--
 4 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
index 1794a54..7a026c2 100644
--- a/hw/acpi/tco.c
+++ b/hw/acpi/tco.c
@@ -64,7 +64,7 @@ static void tco_timer_expired(void *opaque)
         tr->tco.sts2 |= TCO_BOOT_STS;
         tr->timeouts_no = 0;
 
-        if (!(gcs & ICH9_CC_GCS_NO_REBOOT)) {
+        if (!lpc->pin_strap.spkr_hi && !(gcs & ICH9_CC_GCS_NO_REBOOT)) {
             watchdog_perform_action();
             tco_timer_stop(tr);
             return;
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index b547002..3b460d4 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -688,6 +688,11 @@ static const VMStateDescription vmstate_ich9_lpc = {
     }
 };
 
+static Property ich9_lpc_properties[] = {
+    DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void ich9_lpc_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -699,6 +704,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
     dc->reset = ich9_lpc_reset;
     k->init = ich9_lpc_init;
     dc->vmsd = &vmstate_ich9_lpc;
+    dc->props = ich9_lpc_properties;
     k->config_write = ich9_lpc_config_write;
     dc->desc = "ICH9 LPC bridge";
     k->vendor_id = PCI_VENDOR_ID_INTEL;
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index f5681a3..63c5cd8 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -46,6 +46,11 @@ typedef struct ICH9LPCState {
     ICH9LPCPMRegs pm;
     uint32_t sci_level; /* track sci level */
 
+    /* 2.24 Pin Straps */
+    struct {
+        bool spkr_hi;
+    } pin_strap;
+
     /* 10.1 Chipset Configuration registers(Memory Space)
      which is pointed by RCBA */
     uint8_t chip_config[ICH9_CC_SIZE];
diff --git a/tests/tco-test.c b/tests/tco-test.c
index 1a2fe3d..6a48188 100644
--- a/tests/tco-test.c
+++ b/tests/tco-test.c
@@ -42,6 +42,7 @@ enum {
 
 typedef struct {
     const char *args;
+    bool noreboot;
     QPCIDevice *dev;
     void *lpc_base;
     void *tco_io_base;
@@ -53,7 +54,9 @@ static void test_init(TestData *d)
     QTestState *qs;
     char *s;
 
-    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
+    s = g_strdup_printf("-machine q35 %s %s",
+                        d->noreboot ? "" : "-global ICH9-LPC.noreboot=false",
+                        !d->args ? "" : d->args);
     qs = qtest_start(s);
     qtest_irq_intercept_in(qs, "ioapic");
     g_free(s);
@@ -135,6 +138,7 @@ static void test_tco_defaults(void)
     TestData d;
 
     d.args = NULL;
+    d.noreboot = true;
     test_init(&d);
     g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
                     TCO_RLD_DEFAULT);
@@ -167,6 +171,7 @@ static void test_tco_timeout(void)
     int ret;
 
     d.args = NULL;
+    d.noreboot = true;
     test_init(&d);
 
     stop_tco(&d);
@@ -210,6 +215,7 @@ static void test_tco_max_timeout(void)
     int ret;
 
     d.args = NULL;
+    d.noreboot = true;
     test_init(&d);
 
     stop_tco(&d);
@@ -253,6 +259,7 @@ static void test_tco_second_timeout_pause(void)
     QDict *ad;
 
     td.args = "-watchdog-action pause";
+    td.noreboot = false;
     test_init(&td);
 
     stop_tco(&td);
@@ -277,6 +284,7 @@ static void test_tco_second_timeout_reset(void)
     QDict *ad;
 
     td.args = "-watchdog-action reset";
+    td.noreboot = false;
     test_init(&td);
 
     stop_tco(&td);
@@ -301,6 +309,7 @@ static void test_tco_second_timeout_shutdown(void)
     QDict *ad;
 
     td.args = "-watchdog-action shutdown";
+    td.noreboot = false;
     test_init(&td);
 
     stop_tco(&td);
@@ -325,6 +334,7 @@ static void test_tco_second_timeout_none(void)
     QDict *ad;
 
     td.args = "-watchdog-action none";
+    td.noreboot = false;
     test_init(&td);
 
     stop_tco(&td);
@@ -349,6 +359,7 @@ static void test_tco_ticks_counter(void)
     uint16_t rld;
 
     d.args = NULL;
+    d.noreboot = true;
     test_init(&d);
 
     stop_tco(&d);
@@ -375,6 +386,7 @@ static void test_tco1_control_bits(void)
     uint16_t val;
 
     d.args = NULL;
+    d.noreboot = true;
     test_init(&d);
 
     val = TCO_LOCK;
@@ -394,6 +406,7 @@ static void test_tco1_status_bits(void)
     int ret;
 
     d.args = NULL;
+    d.noreboot = true;
     test_init(&d);
 
     stop_tco(&d);
@@ -421,7 +434,8 @@ static void test_tco2_status_bits(void)
     uint16_t val;
     int ret;
 
-    d.args = "-watchdog-action none";
+    d.args = NULL;
+    d.noreboot = true;
     test_init(&d);
 
     stop_tco(&d);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v8 3/3] ich9: implement strap SPKR pin logic
  2015-06-28 17:58   ` [Qemu-devel] [PATCH v8 3/3] ich9: implement strap SPKR pin logic Paulo Alcantara
@ 2015-07-01 13:18     ` Paolo Bonzini
  2015-07-01 13:31       ` Michael S. Tsirkin
  0 siblings, 1 reply; 83+ messages in thread
From: Paolo Bonzini @ 2015-07-01 13:18 UTC (permalink / raw)
  To: Paulo Alcantara, qemu-devel; +Cc: seabios, Paulo Alcantara, kraxel, mst



On 28/06/2015 19:58, Paulo Alcantara wrote:
> If the signal is sampled high, this indicates that the system is
> strapped to the "No Reboot" mode (ICH9 will disable the TCO Timer system
> reboot feature). The status of this strap is readable via the NO_REBOOT
> bit (CC: offset 0x3410:bit 5).
> 
> The NO_REBOOT bit is set when SPKR pin on ICH9 is sampled high. This bit
> may be set or cleared by software if the strap is sampled low but may
> not override the strap when it indicates "No Reboot".
> 
> This patch implements the logic where hardware has ability to set SPKR
> pin through a property named "noreboot" and it's sampled high by
> default.

I know Michael suggested this, but I think default high is a worse
default.  It does not allow recovering from a hard lockup where you
cannot process an NMI, unlike all other watchdogs implemented by QEMU.
In fact, the Linux driver fails to start if the strap is high.

My theory is that hardware manufacturers should only set the strap high
if they want the firmware to have total control of the watchdog via SMIs
(TCO_EN).

If it is just a matter of being late in 2.4, just delay everything to
2.5.  It doesn't require any more work from Paulo, as you can just flip
the default yourself without adding a new machine type (in fact I'm
still not sure why machine types for Q35 are versioned, since migration
is not supported...).

Paolo

> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
> v7 -> v8:
>   * change property name to "noreboot"
>   * default "noreboot" property to high
>   * define property in dc->props
>   * update tco tests to support and exercise "noreboot" property
> ---
>  hw/acpi/tco.c          |  2 +-
>  hw/isa/lpc_ich9.c      |  6 ++++++
>  include/hw/i386/ich9.h |  5 +++++
>  tests/tco-test.c       | 18 ++++++++++++++++--
>  4 files changed, 28 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> index 1794a54..7a026c2 100644
> --- a/hw/acpi/tco.c
> +++ b/hw/acpi/tco.c
> @@ -64,7 +64,7 @@ static void tco_timer_expired(void *opaque)
>          tr->tco.sts2 |= TCO_BOOT_STS;
>          tr->timeouts_no = 0;
>  
> -        if (!(gcs & ICH9_CC_GCS_NO_REBOOT)) {
> +        if (!lpc->pin_strap.spkr_hi && !(gcs & ICH9_CC_GCS_NO_REBOOT)) {
>              watchdog_perform_action();
>              tco_timer_stop(tr);
>              return;
> diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> index b547002..3b460d4 100644
> --- a/hw/isa/lpc_ich9.c
> +++ b/hw/isa/lpc_ich9.c
> @@ -688,6 +688,11 @@ static const VMStateDescription vmstate_ich9_lpc = {
>      }
>  };
>  
> +static Property ich9_lpc_properties[] = {
> +    DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
>  static void ich9_lpc_class_init(ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(klass);
> @@ -699,6 +704,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
>      dc->reset = ich9_lpc_reset;
>      k->init = ich9_lpc_init;
>      dc->vmsd = &vmstate_ich9_lpc;
> +    dc->props = ich9_lpc_properties;
>      k->config_write = ich9_lpc_config_write;
>      dc->desc = "ICH9 LPC bridge";
>      k->vendor_id = PCI_VENDOR_ID_INTEL;
> diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
> index f5681a3..63c5cd8 100644
> --- a/include/hw/i386/ich9.h
> +++ b/include/hw/i386/ich9.h
> @@ -46,6 +46,11 @@ typedef struct ICH9LPCState {
>      ICH9LPCPMRegs pm;
>      uint32_t sci_level; /* track sci level */
>  
> +    /* 2.24 Pin Straps */
> +    struct {
> +        bool spkr_hi;
> +    } pin_strap;
> +
>      /* 10.1 Chipset Configuration registers(Memory Space)
>       which is pointed by RCBA */
>      uint8_t chip_config[ICH9_CC_SIZE];
> diff --git a/tests/tco-test.c b/tests/tco-test.c
> index 1a2fe3d..6a48188 100644
> --- a/tests/tco-test.c
> +++ b/tests/tco-test.c
> @@ -42,6 +42,7 @@ enum {
>  
>  typedef struct {
>      const char *args;
> +    bool noreboot;
>      QPCIDevice *dev;
>      void *lpc_base;
>      void *tco_io_base;
> @@ -53,7 +54,9 @@ static void test_init(TestData *d)
>      QTestState *qs;
>      char *s;
>  
> -    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
> +    s = g_strdup_printf("-machine q35 %s %s",
> +                        d->noreboot ? "" : "-global ICH9-LPC.noreboot=false",
> +                        !d->args ? "" : d->args);
>      qs = qtest_start(s);
>      qtest_irq_intercept_in(qs, "ioapic");
>      g_free(s);
> @@ -135,6 +138,7 @@ static void test_tco_defaults(void)
>      TestData d;
>  
>      d.args = NULL;
> +    d.noreboot = true;
>      test_init(&d);
>      g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
>                      TCO_RLD_DEFAULT);
> @@ -167,6 +171,7 @@ static void test_tco_timeout(void)
>      int ret;
>  
>      d.args = NULL;
> +    d.noreboot = true;
>      test_init(&d);
>  
>      stop_tco(&d);
> @@ -210,6 +215,7 @@ static void test_tco_max_timeout(void)
>      int ret;
>  
>      d.args = NULL;
> +    d.noreboot = true;
>      test_init(&d);
>  
>      stop_tco(&d);
> @@ -253,6 +259,7 @@ static void test_tco_second_timeout_pause(void)
>      QDict *ad;
>  
>      td.args = "-watchdog-action pause";
> +    td.noreboot = false;
>      test_init(&td);
>  
>      stop_tco(&td);
> @@ -277,6 +284,7 @@ static void test_tco_second_timeout_reset(void)
>      QDict *ad;
>  
>      td.args = "-watchdog-action reset";
> +    td.noreboot = false;
>      test_init(&td);
>  
>      stop_tco(&td);
> @@ -301,6 +309,7 @@ static void test_tco_second_timeout_shutdown(void)
>      QDict *ad;
>  
>      td.args = "-watchdog-action shutdown";
> +    td.noreboot = false;
>      test_init(&td);
>  
>      stop_tco(&td);
> @@ -325,6 +334,7 @@ static void test_tco_second_timeout_none(void)
>      QDict *ad;
>  
>      td.args = "-watchdog-action none";
> +    td.noreboot = false;
>      test_init(&td);
>  
>      stop_tco(&td);
> @@ -349,6 +359,7 @@ static void test_tco_ticks_counter(void)
>      uint16_t rld;
>  
>      d.args = NULL;
> +    d.noreboot = true;
>      test_init(&d);
>  
>      stop_tco(&d);
> @@ -375,6 +386,7 @@ static void test_tco1_control_bits(void)
>      uint16_t val;
>  
>      d.args = NULL;
> +    d.noreboot = true;
>      test_init(&d);
>  
>      val = TCO_LOCK;
> @@ -394,6 +406,7 @@ static void test_tco1_status_bits(void)
>      int ret;
>  
>      d.args = NULL;
> +    d.noreboot = true;
>      test_init(&d);
>  
>      stop_tco(&d);
> @@ -421,7 +434,8 @@ static void test_tco2_status_bits(void)
>      uint16_t val;
>      int ret;
>  
> -    d.args = "-watchdog-action none";
> +    d.args = NULL;
> +    d.noreboot = true;
>      test_init(&d);
>  
>      stop_tco(&d);
> 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v8 3/3] ich9: implement strap SPKR pin logic
  2015-07-01 13:18     ` Paolo Bonzini
@ 2015-07-01 13:31       ` Michael S. Tsirkin
  2015-07-01 13:34         ` Paolo Bonzini
  2015-07-02  1:30         ` Paulo Alcantara
  0 siblings, 2 replies; 83+ messages in thread
From: Michael S. Tsirkin @ 2015-07-01 13:31 UTC (permalink / raw)
  To: Paolo Bonzini
  Cc: Paulo Alcantara, seabios, Paulo Alcantara, qemu-devel, kraxel

On Wed, Jul 01, 2015 at 03:18:41PM +0200, Paolo Bonzini wrote:
> 
> 
> On 28/06/2015 19:58, Paulo Alcantara wrote:
> > If the signal is sampled high, this indicates that the system is
> > strapped to the "No Reboot" mode (ICH9 will disable the TCO Timer system
> > reboot feature). The status of this strap is readable via the NO_REBOOT
> > bit (CC: offset 0x3410:bit 5).
> > 
> > The NO_REBOOT bit is set when SPKR pin on ICH9 is sampled high. This bit
> > may be set or cleared by software if the strap is sampled low but may
> > not override the strap when it indicates "No Reboot".
> > 
> > This patch implements the logic where hardware has ability to set SPKR
> > pin through a property named "noreboot" and it's sampled high by
> > default.
> 
> I know Michael suggested this, but I think default high is a worse
> default.  It does not allow recovering from a hard lockup where you
> cannot process an NMI, unlike all other watchdogs implemented by QEMU.
> In fact, the Linux driver fails to start if the strap is high.
> 
> My theory is that hardware manufacturers should only set the strap high
> if they want the firmware to have total control of the watchdog via SMIs
> (TCO_EN).
> 
> If it is just a matter of being late in 2.4, just delay everything to
> 2.5.  It doesn't require any more work from Paulo, as you can just flip
> the default yourself without adding a new machine type (in fact I'm
> still not sure why machine types for Q35 are versioned, since migration
> is not supported...).
> 
> Paolo

I don't think we should defer the whole series because of the argument
about the default.  I've merged these patches, I you like, pls send a
one-line patch on top to flip the default with some info on how it was
tested, and we can discuss it separately.

Makes sense?

BTW 2.4 makes qemu versioned because ahci finally supports migration
so yes, we'll have to version from now on.


> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > ---
> > v7 -> v8:
> >   * change property name to "noreboot"
> >   * default "noreboot" property to high
> >   * define property in dc->props
> >   * update tco tests to support and exercise "noreboot" property
> > ---
> >  hw/acpi/tco.c          |  2 +-
> >  hw/isa/lpc_ich9.c      |  6 ++++++
> >  include/hw/i386/ich9.h |  5 +++++
> >  tests/tco-test.c       | 18 ++++++++++++++++--
> >  4 files changed, 28 insertions(+), 3 deletions(-)
> > 
> > diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> > index 1794a54..7a026c2 100644
> > --- a/hw/acpi/tco.c
> > +++ b/hw/acpi/tco.c
> > @@ -64,7 +64,7 @@ static void tco_timer_expired(void *opaque)
> >          tr->tco.sts2 |= TCO_BOOT_STS;
> >          tr->timeouts_no = 0;
> >  
> > -        if (!(gcs & ICH9_CC_GCS_NO_REBOOT)) {
> > +        if (!lpc->pin_strap.spkr_hi && !(gcs & ICH9_CC_GCS_NO_REBOOT)) {
> >              watchdog_perform_action();
> >              tco_timer_stop(tr);
> >              return;
> > diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> > index b547002..3b460d4 100644
> > --- a/hw/isa/lpc_ich9.c
> > +++ b/hw/isa/lpc_ich9.c
> > @@ -688,6 +688,11 @@ static const VMStateDescription vmstate_ich9_lpc = {
> >      }
> >  };
> >  
> > +static Property ich9_lpc_properties[] = {
> > +    DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> >  static void ich9_lpc_class_init(ObjectClass *klass, void *data)
> >  {
> >      DeviceClass *dc = DEVICE_CLASS(klass);
> > @@ -699,6 +704,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
> >      dc->reset = ich9_lpc_reset;
> >      k->init = ich9_lpc_init;
> >      dc->vmsd = &vmstate_ich9_lpc;
> > +    dc->props = ich9_lpc_properties;
> >      k->config_write = ich9_lpc_config_write;
> >      dc->desc = "ICH9 LPC bridge";
> >      k->vendor_id = PCI_VENDOR_ID_INTEL;
> > diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
> > index f5681a3..63c5cd8 100644
> > --- a/include/hw/i386/ich9.h
> > +++ b/include/hw/i386/ich9.h
> > @@ -46,6 +46,11 @@ typedef struct ICH9LPCState {
> >      ICH9LPCPMRegs pm;
> >      uint32_t sci_level; /* track sci level */
> >  
> > +    /* 2.24 Pin Straps */
> > +    struct {
> > +        bool spkr_hi;
> > +    } pin_strap;
> > +
> >      /* 10.1 Chipset Configuration registers(Memory Space)
> >       which is pointed by RCBA */
> >      uint8_t chip_config[ICH9_CC_SIZE];
> > diff --git a/tests/tco-test.c b/tests/tco-test.c
> > index 1a2fe3d..6a48188 100644
> > --- a/tests/tco-test.c
> > +++ b/tests/tco-test.c
> > @@ -42,6 +42,7 @@ enum {
> >  
> >  typedef struct {
> >      const char *args;
> > +    bool noreboot;
> >      QPCIDevice *dev;
> >      void *lpc_base;
> >      void *tco_io_base;
> > @@ -53,7 +54,9 @@ static void test_init(TestData *d)
> >      QTestState *qs;
> >      char *s;
> >  
> > -    s = g_strdup_printf("-machine q35 %s", !d->args ? "" : d->args);
> > +    s = g_strdup_printf("-machine q35 %s %s",
> > +                        d->noreboot ? "" : "-global ICH9-LPC.noreboot=false",
> > +                        !d->args ? "" : d->args);
> >      qs = qtest_start(s);
> >      qtest_irq_intercept_in(qs, "ioapic");
> >      g_free(s);
> > @@ -135,6 +138,7 @@ static void test_tco_defaults(void)
> >      TestData d;
> >  
> >      d.args = NULL;
> > +    d.noreboot = true;
> >      test_init(&d);
> >      g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
> >                      TCO_RLD_DEFAULT);
> > @@ -167,6 +171,7 @@ static void test_tco_timeout(void)
> >      int ret;
> >  
> >      d.args = NULL;
> > +    d.noreboot = true;
> >      test_init(&d);
> >  
> >      stop_tco(&d);
> > @@ -210,6 +215,7 @@ static void test_tco_max_timeout(void)
> >      int ret;
> >  
> >      d.args = NULL;
> > +    d.noreboot = true;
> >      test_init(&d);
> >  
> >      stop_tco(&d);
> > @@ -253,6 +259,7 @@ static void test_tco_second_timeout_pause(void)
> >      QDict *ad;
> >  
> >      td.args = "-watchdog-action pause";
> > +    td.noreboot = false;
> >      test_init(&td);
> >  
> >      stop_tco(&td);
> > @@ -277,6 +284,7 @@ static void test_tco_second_timeout_reset(void)
> >      QDict *ad;
> >  
> >      td.args = "-watchdog-action reset";
> > +    td.noreboot = false;
> >      test_init(&td);
> >  
> >      stop_tco(&td);
> > @@ -301,6 +309,7 @@ static void test_tco_second_timeout_shutdown(void)
> >      QDict *ad;
> >  
> >      td.args = "-watchdog-action shutdown";
> > +    td.noreboot = false;
> >      test_init(&td);
> >  
> >      stop_tco(&td);
> > @@ -325,6 +334,7 @@ static void test_tco_second_timeout_none(void)
> >      QDict *ad;
> >  
> >      td.args = "-watchdog-action none";
> > +    td.noreboot = false;
> >      test_init(&td);
> >  
> >      stop_tco(&td);
> > @@ -349,6 +359,7 @@ static void test_tco_ticks_counter(void)
> >      uint16_t rld;
> >  
> >      d.args = NULL;
> > +    d.noreboot = true;
> >      test_init(&d);
> >  
> >      stop_tco(&d);
> > @@ -375,6 +386,7 @@ static void test_tco1_control_bits(void)
> >      uint16_t val;
> >  
> >      d.args = NULL;
> > +    d.noreboot = true;
> >      test_init(&d);
> >  
> >      val = TCO_LOCK;
> > @@ -394,6 +406,7 @@ static void test_tco1_status_bits(void)
> >      int ret;
> >  
> >      d.args = NULL;
> > +    d.noreboot = true;
> >      test_init(&d);
> >  
> >      stop_tco(&d);
> > @@ -421,7 +434,8 @@ static void test_tco2_status_bits(void)
> >      uint16_t val;
> >      int ret;
> >  
> > -    d.args = "-watchdog-action none";
> > +    d.args = NULL;
> > +    d.noreboot = true;
> >      test_init(&d);
> >  
> >      stop_tco(&d);
> > 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v8 3/3] ich9: implement strap SPKR pin logic
  2015-07-01 13:31       ` Michael S. Tsirkin
@ 2015-07-01 13:34         ` Paolo Bonzini
  2015-07-02  1:30         ` Paulo Alcantara
  1 sibling, 0 replies; 83+ messages in thread
From: Paolo Bonzini @ 2015-07-01 13:34 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Paulo Alcantara, seabios, Paulo Alcantara, qemu-devel, kraxel



On 01/07/2015 15:31, Michael S. Tsirkin wrote:
> I don't think we should defer the whole series because of the argument
> about the default.  I've merged these patches, I you like, pls send a
> one-line patch on top to flip the default with some info on how it was
> tested, and we can discuss it separately.
> 
> Makes sense?

Perfect.

Paolo

> BTW 2.4 makes qemu versioned because ahci finally supports migration
> so yes, we'll have to version from now on.
> 
> 

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v8 3/3] ich9: implement strap SPKR pin logic
  2015-07-01 13:31       ` Michael S. Tsirkin
  2015-07-01 13:34         ` Paolo Bonzini
@ 2015-07-02  1:30         ` Paulo Alcantara
  2015-07-02  6:55           ` Paolo Bonzini
  1 sibling, 1 reply; 83+ messages in thread
From: Paulo Alcantara @ 2015-07-02  1:30 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Paolo Bonzini, seabios, Paulo Alcantara, qemu-devel, kraxel

On Wed, 1 Jul 2015 15:31:31 +0200
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Wed, Jul 01, 2015 at 03:18:41PM +0200, Paolo Bonzini wrote:
> > 
> > 
> > On 28/06/2015 19:58, Paulo Alcantara wrote:
> > > If the signal is sampled high, this indicates that the system is
> > > strapped to the "No Reboot" mode (ICH9 will disable the TCO Timer
> > > system reboot feature). The status of this strap is readable via
> > > the NO_REBOOT bit (CC: offset 0x3410:bit 5).
> > > 
> > > The NO_REBOOT bit is set when SPKR pin on ICH9 is sampled high.
> > > This bit may be set or cleared by software if the strap is
> > > sampled low but may not override the strap when it indicates "No
> > > Reboot".
> > > 
> > > This patch implements the logic where hardware has ability to set
> > > SPKR pin through a property named "noreboot" and it's sampled
> > > high by default.
> > 
> > I know Michael suggested this, but I think default high is a worse
> > default.  It does not allow recovering from a hard lockup where you
> > cannot process an NMI, unlike all other watchdogs implemented by
> > QEMU. In fact, the Linux driver fails to start if the strap is high.

No, the iTCO_wdt driver won't fail to start. It actually depends on
whether RCBA BAR is set. However, ICH9 will just ignore whether
NO_REBOOT is set or unset when SPKR pin is high.

I think you forgot to apply my SeaBIOS patch that sets RCBA BAR so it
failed to start. We should also make sure it's applied once TCO patches
get upstream.

Thanks,

Paulo

> > 
> > My theory is that hardware manufacturers should only set the strap
> > high if they want the firmware to have total control of the
> > watchdog via SMIs (TCO_EN).
> > 
> > If it is just a matter of being late in 2.4, just delay everything
> > to 2.5.  It doesn't require any more work from Paulo, as you can
> > just flip the default yourself without adding a new machine type
> > (in fact I'm still not sure why machine types for Q35 are
> > versioned, since migration is not supported...).
> > 
> > Paolo
> 
> I don't think we should defer the whole series because of the argument
> about the default.  I've merged these patches, I you like, pls send a
> one-line patch on top to flip the default with some info on how it was
> tested, and we can discuss it separately.
> 
> Makes sense?
> 
> BTW 2.4 makes qemu versioned because ahci finally supports migration
> so yes, we'll have to version from now on.
> 
> 
> > > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > > ---
> > > v7 -> v8:
> > >   * change property name to "noreboot"
> > >   * default "noreboot" property to high
> > >   * define property in dc->props
> > >   * update tco tests to support and exercise "noreboot" property
> > > ---
> > >  hw/acpi/tco.c          |  2 +-
> > >  hw/isa/lpc_ich9.c      |  6 ++++++
> > >  include/hw/i386/ich9.h |  5 +++++
> > >  tests/tco-test.c       | 18 ++++++++++++++++--
> > >  4 files changed, 28 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
> > > index 1794a54..7a026c2 100644
> > > --- a/hw/acpi/tco.c
> > > +++ b/hw/acpi/tco.c
> > > @@ -64,7 +64,7 @@ static void tco_timer_expired(void *opaque)
> > >          tr->tco.sts2 |= TCO_BOOT_STS;
> > >          tr->timeouts_no = 0;
> > >  
> > > -        if (!(gcs & ICH9_CC_GCS_NO_REBOOT)) {
> > > +        if (!lpc->pin_strap.spkr_hi && !(gcs &
> > > ICH9_CC_GCS_NO_REBOOT)) { watchdog_perform_action();
> > >              tco_timer_stop(tr);
> > >              return;
> > > diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> > > index b547002..3b460d4 100644
> > > --- a/hw/isa/lpc_ich9.c
> > > +++ b/hw/isa/lpc_ich9.c
> > > @@ -688,6 +688,11 @@ static const VMStateDescription
> > > vmstate_ich9_lpc = { }
> > >  };
> > >  
> > > +static Property ich9_lpc_properties[] = {
> > > +    DEFINE_PROP_BOOL("noreboot", ICH9LPCState,
> > > pin_strap.spkr_hi, true),
> > > +    DEFINE_PROP_END_OF_LIST(),
> > > +};
> > > +
> > >  static void ich9_lpc_class_init(ObjectClass *klass, void *data)
> > >  {
> > >      DeviceClass *dc = DEVICE_CLASS(klass);
> > > @@ -699,6 +704,7 @@ static void ich9_lpc_class_init(ObjectClass
> > > *klass, void *data) dc->reset = ich9_lpc_reset;
> > >      k->init = ich9_lpc_init;
> > >      dc->vmsd = &vmstate_ich9_lpc;
> > > +    dc->props = ich9_lpc_properties;
> > >      k->config_write = ich9_lpc_config_write;
> > >      dc->desc = "ICH9 LPC bridge";
> > >      k->vendor_id = PCI_VENDOR_ID_INTEL;
> > > diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
> > > index f5681a3..63c5cd8 100644
> > > --- a/include/hw/i386/ich9.h
> > > +++ b/include/hw/i386/ich9.h
> > > @@ -46,6 +46,11 @@ typedef struct ICH9LPCState {
> > >      ICH9LPCPMRegs pm;
> > >      uint32_t sci_level; /* track sci level */
> > >  
> > > +    /* 2.24 Pin Straps */
> > > +    struct {
> > > +        bool spkr_hi;
> > > +    } pin_strap;
> > > +
> > >      /* 10.1 Chipset Configuration registers(Memory Space)
> > >       which is pointed by RCBA */
> > >      uint8_t chip_config[ICH9_CC_SIZE];
> > > diff --git a/tests/tco-test.c b/tests/tco-test.c
> > > index 1a2fe3d..6a48188 100644
> > > --- a/tests/tco-test.c
> > > +++ b/tests/tco-test.c
> > > @@ -42,6 +42,7 @@ enum {
> > >  
> > >  typedef struct {
> > >      const char *args;
> > > +    bool noreboot;
> > >      QPCIDevice *dev;
> > >      void *lpc_base;
> > >      void *tco_io_base;
> > > @@ -53,7 +54,9 @@ static void test_init(TestData *d)
> > >      QTestState *qs;
> > >      char *s;
> > >  
> > > -    s = g_strdup_printf("-machine q35 %s", !d->args ? "" :
> > > d->args);
> > > +    s = g_strdup_printf("-machine q35 %s %s",
> > > +                        d->noreboot ? "" : "-global
> > > ICH9-LPC.noreboot=false",
> > > +                        !d->args ? "" : d->args);
> > >      qs = qtest_start(s);
> > >      qtest_irq_intercept_in(qs, "ioapic");
> > >      g_free(s);
> > > @@ -135,6 +138,7 @@ static void test_tco_defaults(void)
> > >      TestData d;
> > >  
> > >      d.args = NULL;
> > > +    d.noreboot = true;
> > >      test_init(&d);
> > >      g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base +
> > > TCO_RLD), ==, TCO_RLD_DEFAULT);
> > > @@ -167,6 +171,7 @@ static void test_tco_timeout(void)
> > >      int ret;
> > >  
> > >      d.args = NULL;
> > > +    d.noreboot = true;
> > >      test_init(&d);
> > >  
> > >      stop_tco(&d);
> > > @@ -210,6 +215,7 @@ static void test_tco_max_timeout(void)
> > >      int ret;
> > >  
> > >      d.args = NULL;
> > > +    d.noreboot = true;
> > >      test_init(&d);
> > >  
> > >      stop_tco(&d);
> > > @@ -253,6 +259,7 @@ static void
> > > test_tco_second_timeout_pause(void) QDict *ad;
> > >  
> > >      td.args = "-watchdog-action pause";
> > > +    td.noreboot = false;
> > >      test_init(&td);
> > >  
> > >      stop_tco(&td);
> > > @@ -277,6 +284,7 @@ static void
> > > test_tco_second_timeout_reset(void) QDict *ad;
> > >  
> > >      td.args = "-watchdog-action reset";
> > > +    td.noreboot = false;
> > >      test_init(&td);
> > >  
> > >      stop_tco(&td);
> > > @@ -301,6 +309,7 @@ static void
> > > test_tco_second_timeout_shutdown(void) QDict *ad;
> > >  
> > >      td.args = "-watchdog-action shutdown";
> > > +    td.noreboot = false;
> > >      test_init(&td);
> > >  
> > >      stop_tco(&td);
> > > @@ -325,6 +334,7 @@ static void test_tco_second_timeout_none(void)
> > >      QDict *ad;
> > >  
> > >      td.args = "-watchdog-action none";
> > > +    td.noreboot = false;
> > >      test_init(&td);
> > >  
> > >      stop_tco(&td);
> > > @@ -349,6 +359,7 @@ static void test_tco_ticks_counter(void)
> > >      uint16_t rld;
> > >  
> > >      d.args = NULL;
> > > +    d.noreboot = true;
> > >      test_init(&d);
> > >  
> > >      stop_tco(&d);
> > > @@ -375,6 +386,7 @@ static void test_tco1_control_bits(void)
> > >      uint16_t val;
> > >  
> > >      d.args = NULL;
> > > +    d.noreboot = true;
> > >      test_init(&d);
> > >  
> > >      val = TCO_LOCK;
> > > @@ -394,6 +406,7 @@ static void test_tco1_status_bits(void)
> > >      int ret;
> > >  
> > >      d.args = NULL;
> > > +    d.noreboot = true;
> > >      test_init(&d);
> > >  
> > >      stop_tco(&d);
> > > @@ -421,7 +434,8 @@ static void test_tco2_status_bits(void)
> > >      uint16_t val;
> > >      int ret;
> > >  
> > > -    d.args = "-watchdog-action none";
> > > +    d.args = NULL;
> > > +    d.noreboot = true;
> > >      test_init(&d);
> > >  
> > >      stop_tco(&d);
> > > 
> 



-- 
Paulo Alcantara, C.E.S.A.R
Speaking for myself only.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [Qemu-devel] [PATCH v8 3/3] ich9: implement strap SPKR pin logic
  2015-07-02  1:30         ` Paulo Alcantara
@ 2015-07-02  6:55           ` Paolo Bonzini
  0 siblings, 0 replies; 83+ messages in thread
From: Paolo Bonzini @ 2015-07-02  6:55 UTC (permalink / raw)
  To: Paulo Alcantara, Michael S. Tsirkin
  Cc: seabios, Paulo Alcantara, qemu-devel, kraxel



On 02/07/2015 03:30, Paulo Alcantara wrote:
> No, the iTCO_wdt driver won't fail to start. It actually depends on
> whether RCBA BAR is set. However, ICH9 will just ignore whether
> NO_REBOOT is set or unset when SPKR pin is high.

This is the code I was referring to, in iTCO_wdt_start:

        /* disable chipset's NO_REBOOT bit */
        if (iTCO_wdt_unset_NO_REBOOT_bit()) {
                spin_unlock(&iTCO_wdt_private.io_lock);
                pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n");
                return -EIO;
        }

Paolo

^ permalink raw reply	[flat|nested] 83+ messages in thread

end of thread, other threads:[~2015-07-02  6:55 UTC | newest]

Thread overview: 83+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-05-27  0:29 [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paulo Alcantara
2015-05-27  0:29 ` [Qemu-devel] [PATCH 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
2015-05-27 12:03   ` Paolo Bonzini
2015-05-27 17:51     ` Paulo Alcantara
2015-05-28  7:13   ` [Qemu-devel] [SeaBIOS] " Gerd Hoffmann
2015-05-30 10:57     ` Paulo Alcantara
2015-06-01  7:16       ` Gerd Hoffmann
2015-06-01 11:59         ` Paulo Alcantara
2015-05-27  0:29 ` [Qemu-devel] [PATCH 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
2015-05-27 11:58 ` [Qemu-devel] [PATCH 1/3] ich9: add TCO interface emulation Paolo Bonzini
2015-05-27 18:23   ` Paulo Alcantara
2015-05-30 22:04 ` [Qemu-devel] [PATCH v2 " Paulo Alcantara
2015-05-30 22:04   ` [Qemu-devel] [PATCH v2 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
2015-05-30 22:04   ` [Qemu-devel] [PATCH v2 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
2015-06-01  9:07     ` Paolo Bonzini
2015-06-01  9:05   ` [Qemu-devel] [PATCH v2 1/3] ich9: add TCO interface emulation Paolo Bonzini
2015-06-01 13:38     ` Paulo Alcantara
2015-06-01 21:37       ` Paulo Alcantara
2015-06-01 23:48   ` [Qemu-devel] [PATCH v3 " Paulo Alcantara
2015-06-01 23:48     ` [Qemu-devel] [PATCH v3 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
2015-06-17 13:33       ` Michael S. Tsirkin
2015-06-18  2:14         ` Paulo Alcantara
2015-06-01 23:48     ` [Qemu-devel] [PATCH v3 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
2015-06-17 13:37       ` Michael S. Tsirkin
2015-06-18  2:23         ` Paulo Alcantara
2015-06-10 13:17     ` [Qemu-devel] [PATCH v3 1/3] ich9: add TCO interface emulation Paulo Alcantara
2015-06-17 13:27     ` Michael S. Tsirkin
2015-06-18  2:10       ` Paulo Alcantara
2015-06-22  0:37 ` [Qemu-devel] [PATCH v4 " Paulo Alcantara
2015-06-22  0:37   ` [Qemu-devel] [PATCH v4 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
2015-06-22  8:40     ` Michael S. Tsirkin
2015-06-22 12:53       ` Paulo Alcantara
2015-06-23 10:38     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
2015-06-23 10:58       ` Michael S. Tsirkin
2015-06-23 12:29         ` Igor Mammedov
2015-06-23 12:37           ` Michael S. Tsirkin
2015-06-23 11:15       ` Paolo Bonzini
2015-06-23 11:18       ` Paolo Bonzini
2015-06-23 12:22       ` Michael S. Tsirkin
2015-06-22  0:37   ` [Qemu-devel] [PATCH v4 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
2015-06-22  8:39   ` [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation Michael S. Tsirkin
2015-06-22 12:30     ` Paulo Alcantara
2015-06-22 12:32       ` Paolo Bonzini
2015-06-22 12:47         ` Michael S. Tsirkin
2015-06-22 13:04           ` Paolo Bonzini
2015-06-22 13:07             ` Michael S. Tsirkin
2015-06-22 13:19               ` Paulo Alcantara
2015-06-22 13:10           ` Markus Armbruster
2015-06-22  8:43   ` Michael S. Tsirkin
2015-06-22  9:45     ` Paolo Bonzini
2015-06-22 12:11       ` Michael S. Tsirkin
2015-06-22 12:36         ` Paulo Alcantara
2015-06-22 12:44           ` Michael S. Tsirkin
2015-06-22 12:59             ` Paolo Bonzini
2015-06-22 18:29   ` Paulo Alcantara
2015-06-22 23:10 ` [Qemu-devel] [PATCH v5 " Paulo Alcantara
2015-06-22 23:10   ` [Qemu-devel] [PATCH v5 2/3] target-i386: reserve RCRB mmio space in ACPI DSDT table Paulo Alcantara
2015-06-23 14:29     ` [Qemu-devel] [SeaBIOS] " Igor Mammedov
2015-06-23 15:33       ` Michael S. Tsirkin
2015-06-23 14:39     ` Igor Mammedov
2015-06-23 15:06       ` Michael S. Tsirkin
2015-06-23 15:12         ` Igor Mammedov
2015-06-23 15:29           ` Michael S. Tsirkin
2015-06-24 15:11     ` [Qemu-devel] " Michael S. Tsirkin
2015-06-24 16:00       ` Paulo Alcantara
2015-06-24 16:04         ` Michael S. Tsirkin
2015-06-22 23:10   ` [Qemu-devel] [PATCH v5 3/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
2015-06-23  6:39   ` [Qemu-devel] [PATCH v5 1/3] ich9: add TCO interface emulation Michael S. Tsirkin
2015-06-24 18:03 ` [Qemu-devel] [PATCH v6 1/2] " Paulo Alcantara
2015-06-24 18:03   ` [Qemu-devel] [PATCH v6 2/2] tests: add testcase for TCO watchdog emulation Paulo Alcantara
2015-06-27 17:56 ` [Qemu-devel] [PATCH v7 1/3] ich9: add TCO interface emulation Paulo Alcantara
2015-06-27 17:56   ` [Qemu-devel] [PATCH v7 2/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
2015-06-27 17:56   ` [Qemu-devel] [PATCH v7 3/3] ich9: implement strap SPKR pin logic Paulo Alcantara
2015-06-28  8:37     ` Michael S. Tsirkin
2015-06-28 16:21       ` Paulo Alcantara
2015-06-28 17:58 ` [Qemu-devel] [PATCH v8 1/3] ich9: add TCO interface emulation Paulo Alcantara
2015-06-28 17:58   ` [Qemu-devel] [PATCH v8 2/3] tests: add testcase for TCO watchdog emulation Paulo Alcantara
2015-06-28 17:58   ` [Qemu-devel] [PATCH v8 3/3] ich9: implement strap SPKR pin logic Paulo Alcantara
2015-07-01 13:18     ` Paolo Bonzini
2015-07-01 13:31       ` Michael S. Tsirkin
2015-07-01 13:34         ` Paolo Bonzini
2015-07-02  1:30         ` Paulo Alcantara
2015-07-02  6:55           ` Paolo Bonzini

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.