qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation


From: Michael S. Tsirkin
Subject: Re: [Qemu-devel] [PATCH v4 1/3] ich9: add TCO interface emulation
Date: Mon, 22 Jun 2015 10:43:38 +0200

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 <address@hidden>

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 <address@hidden>
> + *
> + * 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 <address@hidden>
> + *
> + * 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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]