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

Paulo Alcantara pcacjr at zytor.com
Mon Jun 22 20:29:08 CEST 2015


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 at 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 at 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 at 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.



More information about the SeaBIOS mailing list