[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] hw/acpi/ich9: Add periodic and swsmi timer
From: |
Michael S. Tsirkin |
Subject: |
Re: [PATCH] hw/acpi/ich9: Add periodic and swsmi timer |
Date: |
Tue, 30 Jul 2024 14:53:19 -0400 |
On Fri, Jul 19, 2024 at 12:20:53PM +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)
> with QEMU.
>
> This includes that writes to the SMI_STS register are enabled for the
> corresponding two bits.
>
> Signed-off-by: Dominic Prinz <git@dprinz.de>
Without getting too deep into it, generally any guest visible
behaviour change is best gated on a compat property,
to prevent changing existing machine types.
> ---
> hw/acpi/ich9.c | 15 +++++++
> hw/acpi/ich9_timer.c | 79 +++++++++++++++++++++++++++++++++++
> hw/acpi/meson.build | 2 +-
> hw/isa/lpc_ich9.c | 6 +++
> include/hw/acpi/ich9.h | 3 ++
> include/hw/acpi/ich9_timer.h | 23 ++++++++++
> include/hw/southbridge/ich9.h | 4 ++
> 7 files changed, 131 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..97d645b750 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,16 @@ 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);
> + ich9_pm_update_swsmi_timer(pm, pm->smi_en &
> + ICH9_PMIO_SMI_EN_SWSMI_EN);
> + ich9_pm_update_periodic_timer(pm, pm->smi_en &
> + ICH9_PMIO_SMI_EN_PERIODIC_EN);
> + break;
> + case 4:
> + pm->smi_sts &= ~(ICH9_PMIO_SMI_STS_SWSMI_STS |
> + ICH9_PMIO_SMI_STS_PERIODIC_STS);
> + pm->smi_sts |= val & (ICH9_PMIO_SMI_STS_SWSMI_STS |
> + ICH9_PMIO_SMI_STS_PERIODIC_STS);
> break;
> }
> }
> @@ -305,6 +316,10 @@ 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);
>
> + pm->swsmi_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ich9_swsmi_timer, pm);
> +
> + pm->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
> ich9_periodic_timer, 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..2e6b68f0fb
> --- /dev/null
> +++ b/hw/acpi/ich9_timer.c
> @@ -0,0 +1,79 @@
> +/*
> + * 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);
> + }
> +}
> +
> +void ich9_swsmi_timer(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_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);
> + }
> +}
> +
> +void ich9_periodic_timer(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);
> +}
> 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/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
> index bd727b2320..56daeeedbf 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,11 @@ 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;
>
> + ich9_pm_update_swsmi_timer(&lpc->pm, lpc->pm.smi_en &
> + ICH9_PMIO_SMI_EN_SWSMI_EN);
> + 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;
> diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
> index 2faf7f0cae..940a3d795f 100644
> --- a/include/hw/acpi/ich9.h
> +++ b/include/hw/acpi/ich9.h
> @@ -68,6 +68,9 @@ typedef struct ICH9LPCPMRegs {
> bool smm_compat;
> bool enable_tco;
> TCOIORegs tco_regs;
> +
> + 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..e48f9918d8
> --- /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_swsmi_timer(void *opaque);
> +
> +void ich9_pm_update_periodic_timer(ICH9LPCPMRegs *pm, bool enable);
> +
> +void ich9_periodic_timer(void *opaque);
> +
> +#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