qemu-devel
[Top][All Lists]
Advanced

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

Re: [PATCH v2] hw/acpi/ich9: Add periodic and swsmi timer


From: Michael S. Tsirkin
Subject: Re: [PATCH v2] hw/acpi/ich9: Add periodic and swsmi timer
Date: Tue, 10 Sep 2024 11:20:51 -0400

On Wed, Jul 31, 2024 at 10:28:34AM +0200, Dominic Prinz wrote:
> This patch implements the periodic and the swsmi ICH9 chipset timer. They are
> especially useful when prototyping UEFI firmware (e.g. with EDK2's OVMF)
> using QEMU.
> 
> For backwards compatibility, the compat properties "x-smi-swsmi-timer",
> and "x-smi-periodic-timer" are introduced.
> 
> Additionally, writes to the SMI_STS register are enabled for the
> corresponding two bits.
> 
> Signed-off-by: Dominic Prinz <git@dprinz.de>
> ---
> Changes since previous version:
>   - Ensured backwards compatablity by introducing two compat properties
>   - Introduced write mask for SMI_STS register to make future work easier
> 
>  hw/acpi/ich9.c                | 23 +++++++++
>  hw/acpi/ich9_timer.c          | 93 +++++++++++++++++++++++++++++++++++
>  hw/acpi/meson.build           |  2 +-
>  hw/i386/pc.c                  |  2 +
>  hw/isa/lpc_ich9.c             | 14 ++++++
>  include/hw/acpi/ich9.h        |  6 +++
>  include/hw/acpi/ich9_timer.h  | 23 +++++++++
>  include/hw/southbridge/ich9.h |  4 ++
>  8 files changed, 166 insertions(+), 1 deletion(-)
>  create mode 100644 hw/acpi/ich9_timer.c
>  create mode 100644 include/hw/acpi/ich9_timer.h
> 
> diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
> index 02d8546bd3..c15e5b8281 100644
> --- a/hw/acpi/ich9.c
> +++ b/hw/acpi/ich9.c
> @@ -35,6 +35,7 @@
>  #include "sysemu/runstate.h"
>  #include "hw/acpi/acpi.h"
>  #include "hw/acpi/ich9_tco.h"
> +#include "hw/acpi/ich9_timer.h"
>  
>  #include "hw/southbridge/ich9.h"
>  #include "hw/mem/pc-dimm.h"
> @@ -108,6 +109,18 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, 
> uint64_t val,
>          }
>          pm->smi_en &= ~pm->smi_en_wmask;
>          pm->smi_en |= (val & pm->smi_en_wmask);
> +        if (pm->swsmi_timer_enabled) {
> +            ich9_pm_update_swsmi_timer(pm, pm->smi_en &
> +                                               ICH9_PMIO_SMI_EN_SWSMI_EN);
> +        }
> +        if (pm->periodic_timer_enabled) {
> +            ich9_pm_update_periodic_timer(pm, pm->smi_en &
> +                                                  
> ICH9_PMIO_SMI_EN_PERIODIC_EN);
> +        }
> +        break;
> +    case 4:
> +        pm->smi_sts &= ~pm->smi_sts_wmask;
> +        pm->smi_sts |= (val & pm->smi_sts_wmask);
>          break;
>      }
>  }
> @@ -286,6 +299,8 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
>  
>  void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq)
>  {
> +    pm->smi_sts_wmask = 0;
> +
>      memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
>      memory_region_set_enabled(&pm->io, false);
>      memory_region_add_subregion(pci_address_space_io(lpc_pci),
> @@ -305,6 +320,14 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, 
> qemu_irq sci_irq)
>                            "acpi-smi", 8);
>      memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
>  
> +    if (pm->swsmi_timer_enabled) {
> +        ich9_pm_swsmi_timer_init(pm);
> +    }
> +
> +    if (pm->periodic_timer_enabled) {
> +        ich9_pm_periodic_timer_init(pm);
> +    }
> +
>      if (pm->enable_tco) {
>          acpi_pm_tco_init(&pm->tco_regs, &pm->io);
>      }
> diff --git a/hw/acpi/ich9_timer.c b/hw/acpi/ich9_timer.c
> new file mode 100644
> index 0000000000..5b1c910156
> --- /dev/null
> +++ b/hw/acpi/ich9_timer.c
> @@ -0,0 +1,93 @@
> +/*
> + * QEMU ICH9 Timer emulation
> + *
> + * Copyright (c) 2024 Dominic Prinz <git@dprinz.de>
> + *
> + * 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/osdep.h"
> +#include "hw/core/cpu.h"
> +#include "hw/pci/pci.h"
> +#include "hw/southbridge/ich9.h"
> +#include "qemu/timer.h"
> +
> +#include "hw/acpi/ich9_timer.h"
> +
> +void ich9_pm_update_swsmi_timer(ICH9LPCPMRegs *pm, bool enable)
> +{
> +    uint16_t swsmi_rate_sel;
> +    int64_t expire_time;
> +    ICH9LPCState *lpc;
> +
> +    if (enable) {
> +        lpc = container_of(pm, ICH9LPCState, pm);
> +        swsmi_rate_sel =
> +            (pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_3) & 0xc0) >> 6;
> +
> +        if (swsmi_rate_sel == 0) {
> +            expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 1500000LL;
> +        } else {
> +            expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> +                          8 * (1 << swsmi_rate_sel) * 1000000LL;
> +        }
> +
> +        timer_mod(pm->swsmi_timer, expire_time);
> +    } else {
> +        timer_del(pm->swsmi_timer);
> +    }
> +}
> +
> +static void ich9_pm_swsmi_timer_expired(void *opaque)
> +{
> +    ICH9LPCPMRegs *pm = opaque;
> +
> +    pm->smi_sts |= ICH9_PMIO_SMI_STS_SWSMI_STS;
> +    ich9_generate_smi();
> +
> +    ich9_pm_update_swsmi_timer(pm, pm->smi_en & ICH9_PMIO_SMI_EN_SWSMI_EN);
> +}
> +
> +void ich9_pm_swsmi_timer_init(ICH9LPCPMRegs *pm)
> +{
> +    pm->smi_sts_wmask |= ICH9_PMIO_SMI_STS_SWSMI_STS;
> +    pm->swsmi_timer =
> +        timer_new_ns(QEMU_CLOCK_VIRTUAL, ich9_pm_swsmi_timer_expired, pm);
> +}
> +
> +void ich9_pm_update_periodic_timer(ICH9LPCPMRegs *pm, bool enable)
> +{
> +    uint16_t per_smi_sel;
> +    int64_t expire_time;
> +    ICH9LPCState *lpc;
> +
> +    if (enable) {
> +        lpc = container_of(pm, ICH9LPCState, pm);
> +        per_smi_sel = pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_1) & 3;
> +        expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> +                      8 * (1 << (3 - per_smi_sel)) * NANOSECONDS_PER_SECOND;
> +
> +        timer_mod(pm->periodic_timer, expire_time);
> +    } else {
> +        timer_del(pm->periodic_timer);
> +    }
> +}
> +
> +static void ich9_pm_periodic_timer_expired(void *opaque)
> +{
> +    ICH9LPCPMRegs *pm = opaque;
> +
> +    pm->smi_sts = ICH9_PMIO_SMI_STS_PERIODIC_STS;
> +    ich9_generate_smi();
> +
> +    ich9_pm_update_periodic_timer(pm,
> +                                  pm->smi_en & ICH9_PMIO_SMI_EN_PERIODIC_EN);
> +}
> +
> +void ich9_pm_periodic_timer_init(ICH9LPCPMRegs *pm)
> +{
> +    pm->smi_sts_wmask |= ICH9_PMIO_SMI_STS_PERIODIC_STS;
> +    pm->periodic_timer =
> +        timer_new_ns(QEMU_CLOCK_VIRTUAL, ich9_pm_periodic_timer_expired, pm);
> +}
> diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build
> index fa5c07db90..7f8ccc9b7a 100644
> --- a/hw/acpi/meson.build
> +++ b/hw/acpi/meson.build
> @@ -24,7 +24,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_PCI_BRIDGE', if_true: 
> files('pci-bridge.c'))
>  acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_true: files('pcihp.c'))
>  acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_false: 
> files('acpi-pci-hotplug-stub.c'))
>  acpi_ss.add(when: 'CONFIG_ACPI_VIOT', if_true: files('viot.c'))
> -acpi_ss.add(when: 'CONFIG_ACPI_ICH9', if_true: files('ich9.c', 'ich9_tco.c'))
> +acpi_ss.add(when: 'CONFIG_ACPI_ICH9', if_true: files('ich9.c', 'ich9_tco.c', 
> 'ich9_timer.c'))
>  acpi_ss.add(when: 'CONFIG_ACPI_ERST', if_true: files('erst.c'))
>  acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: 
> files('ipmi-stub.c'))
>  acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c'))
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index c74931d577..a97b1b1f57 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -85,6 +85,8 @@ GlobalProperty pc_compat_9_0[] = {
>      { TYPE_X86_CPU, "guest-phys-bits", "0" },
>      { "sev-guest", "legacy-vm-type", "on" },
>      { TYPE_X86_CPU, "legacy-multi-node", "on" },
> +    { "ICH9-LPC", "x-smi-swsmi-timer", "off" },
> +    { "ICH9-LPC", "x-smi-periodic-timer", "off" },
>  };
>  const size_t pc_compat_9_0_len = G_N_ELEMENTS(pc_compat_9_0);
>

this needs to be 9.1 now, right?
  
> diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> index bd727b2320..ab17b76f54 100644
> --- a/hw/isa/lpc_ich9.c
> +++ b/hw/isa/lpc_ich9.c
> @@ -43,6 +43,7 @@
>  #include "hw/southbridge/ich9.h"
>  #include "hw/acpi/acpi.h"
>  #include "hw/acpi/ich9.h"
> +#include "hw/acpi/ich9_timer.h"
>  #include "hw/pci/pci_bus.h"
>  #include "hw/qdev-properties.h"
>  #include "sysemu/runstate.h"
> @@ -531,6 +532,15 @@ ich9_lpc_pmcon_update(ICH9LPCState *lpc)
>      uint16_t gen_pmcon_1 = pci_get_word(lpc->d.config + 
> ICH9_LPC_GEN_PMCON_1);
>      uint16_t wmask;
>  
> +    if (lpc->pm.swsmi_timer_enabled) {
> +        ich9_pm_update_swsmi_timer(
> +            &lpc->pm, lpc->pm.smi_en & ICH9_PMIO_SMI_EN_SWSMI_EN);
> +    }
> +    if (lpc->pm.periodic_timer_enabled) {
> +        ich9_pm_update_periodic_timer(
> +            &lpc->pm, lpc->pm.smi_en & ICH9_PMIO_SMI_EN_PERIODIC_EN);
> +    }
> +
>      if (gen_pmcon_1 & ICH9_LPC_GEN_PMCON_1_SMI_LOCK) {
>          wmask = pci_get_word(lpc->d.wmask + ICH9_LPC_GEN_PMCON_1);
>          wmask &= ~ICH9_LPC_GEN_PMCON_1_SMI_LOCK;
> @@ -826,6 +836,10 @@ static Property ich9_lpc_properties[] = {
>                        ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT, true),
>      DEFINE_PROP_BIT64("x-smi-cpu-hotunplug", ICH9LPCState, smi_host_features,
>                        ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT, true),
> +    DEFINE_PROP_BOOL("x-smi-swsmi-timer", ICH9LPCState,
> +                     pm.swsmi_timer_enabled, true),
> +    DEFINE_PROP_BOOL("x-smi-periodic-timer", ICH9LPCState,
> +                     pm.periodic_timer_enabled, true),
>      DEFINE_PROP_END_OF_LIST(),
>  };
>  
> diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
> index 2faf7f0cae..245fe08dc2 100644
> --- a/include/hw/acpi/ich9.h
> +++ b/include/hw/acpi/ich9.h
> @@ -46,6 +46,7 @@ typedef struct ICH9LPCPMRegs {
>      uint32_t smi_en;
>      uint32_t smi_en_wmask;
>      uint32_t smi_sts;
> +    uint32_t smi_sts_wmask;
>  
>      qemu_irq irq;      /* SCI */
>  
> @@ -68,6 +69,11 @@ typedef struct ICH9LPCPMRegs {
>      bool smm_compat;
>      bool enable_tco;
>      TCOIORegs tco_regs;
> +
> +    bool swsmi_timer_enabled;
> +    bool periodic_timer_enabled;
> +    QEMUTimer *swsmi_timer;
> +    QEMUTimer *periodic_timer;
>  } ICH9LPCPMRegs;
>  
>  #define ACPI_PM_PROP_TCO_ENABLED "enable_tco"
> diff --git a/include/hw/acpi/ich9_timer.h b/include/hw/acpi/ich9_timer.h
> new file mode 100644
> index 0000000000..5112df4385
> --- /dev/null
> +++ b/include/hw/acpi/ich9_timer.h
> @@ -0,0 +1,23 @@
> +/*
> + * QEMU ICH9 Timer emulation
> + *
> + * Copyright (c) 2024 Dominic Prinz <git@dprinz.de>
> + *
> + * 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_ICH9_TIMER_H
> +#define HW_ACPI_ICH9_TIMER_H
> +
> +#include "hw/acpi/ich9.h"
> +
> +void ich9_pm_update_swsmi_timer(ICH9LPCPMRegs *pm, bool enable);
> +
> +void ich9_pm_swsmi_timer_init(ICH9LPCPMRegs *pm);
> +
> +void ich9_pm_update_periodic_timer(ICH9LPCPMRegs *pm, bool enable);
> +
> +void ich9_pm_periodic_timer_init(ICH9LPCPMRegs *pm);
> +
> +#endif
> diff --git a/include/hw/southbridge/ich9.h b/include/hw/southbridge/ich9.h
> index fd01649d04..6c60017024 100644
> --- a/include/hw/southbridge/ich9.h
> +++ b/include/hw/southbridge/ich9.h
> @@ -196,8 +196,12 @@ struct ICH9LPCState {
>  #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_SWSMI_EN               (1 << 6)
>  #define ICH9_PMIO_SMI_EN_TCO_EN                 (1 << 13)
> +#define ICH9_PMIO_SMI_EN_PERIODIC_EN            (1 << 14)
>  #define ICH9_PMIO_SMI_STS                       0x34
> +#define ICH9_PMIO_SMI_STS_SWSMI_STS             (1 << 6)
> +#define ICH9_PMIO_SMI_STS_PERIODIC_STS          (1 << 14)
>  #define ICH9_PMIO_TCO_RLD                       0x60
>  #define ICH9_PMIO_TCO_LEN                       32
>  
> -- 
> 2.34.1




reply via email to

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