[PATCH 0/9] iTCO fixes and refactorings

14 views
Skip to first unread message

Jan Kiszka

unread,
May 31, 2021, 4:01:14 AM5/31/21
to efibootg...@googlegroups.com, Christian Storm
This fixes a number of issues of the iTCO driver, specifically around
version 2 devices. Tested on an old NM10-based board and in QEMU.

Jan

Jan Kiszka (9):
watchdog: iTCO: Add support for NM10 chipset
watchdog: iTCO: Resolve iTCO_regs on demand
watchdog: iTCO: Make iTCO_chipset_info const
watchdog: iTCO: Access TCO directly
watchdog: iTCO: Access PMC directly
watchdog: iTCO: Refactor version-specific handling
watchdog: iTCO: Factor out PMBASE retrieval
watchdog: iTCO: Account for second timeout case if SMIs are not
triggered
watchdog: iTCO: Fix Wildcat Point and ICH9 version

drivers/watchdog/itco.c | 196 ++++++++++++++++++----------------------
1 file changed, 88 insertions(+), 108 deletions(-)

--
2.26.2

Jan Kiszka

unread,
May 31, 2021, 4:01:16 AM5/31/21
to efibootg...@googlegroups.com, Christian Storm
From: Jan Kiszka <jan.k...@siemens.com>

This will be used for SMI enable checking later on. We only support this
for version 2 and 3 for now. Higher versions are more complicated to
implement.

Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
drivers/watchdog/itco.c | 39 +++++++++++++++++++++++++++++----------
1 file changed, 29 insertions(+), 10 deletions(-)

diff --git a/drivers/watchdog/itco.c b/drivers/watchdog/itco.c
index 0e87494..835e081 100644
--- a/drivers/watchdog/itco.c
+++ b/drivers/watchdog/itco.c
@@ -46,6 +46,7 @@ enum iTCO_versions {

typedef struct {
UINT32 tco_base;
+ UINT32 pm_base_reg;
UINT32 pm_base_addr_mask;
UINT32 pmc_base_reg;
UINT32 pmc_reg;
@@ -70,6 +71,7 @@ static const iTCO_regs iTCO_version_regs[] = {
.pmc_reg = 0x3410, /* GCS_REG */
.pmc_no_reboot_mask = (1 << 5), /* GCS_NO_REBOOT */
.pmc_base_addr_mask = 0xffffc000, /* RCBABASE_ADDRMASK */
+ .pm_base_reg = 0x40,
.pm_base_addr_mask = 0x0000ff80,
},
[ITCO_V3] =
@@ -78,6 +80,7 @@ static const iTCO_regs iTCO_version_regs[] = {
.pmc_reg = 0x08,
.pmc_no_reboot_mask = (1 << 4),
.pmc_base_addr_mask = 0xfffffe00,
+ .pm_base_reg = 0x40,
.pm_base_addr_mask = 0x0000ff80,
},
[ITCO_V4] =
@@ -162,27 +165,42 @@ static BOOLEAN itco_supported(UINT16 pci_device_id, UINT8 *index)
return FALSE;
}

-static UINTN get_timeout_value(UINT32 iTCO_version, UINTN seconds){
+static UINTN get_timeout_value(UINT32 iTCO_version, UINTN seconds)
+{
return iTCO_version == ITCO_V3 ? seconds : ((seconds * 10 ) / 6);
}

-static UINT32 get_tco_base(EFI_PCI_IO *pci_io, const iTCO_info *itco)
+static UINT32 get_pm_base(EFI_PCI_IO *pci_io, const iTCO_info *itco)
{
const iTCO_regs* regs = &iTCO_version_regs[itco->itco_version];
- UINT32 pm_base;
EFI_STATUS status;
+ UINT32 pm_base;

- if (regs->tco_base) {
- return regs->tco_base;
+ if (!regs->pm_base_reg) {
+ return 0;
}

status = uefi_call_wrapper(pci_io->Pci.Read, 5, pci_io,
- EfiPciIoWidthUint32, 0x40, 1, &pm_base);
+ EfiPciIoWidthUint32,
+ regs->pm_base_reg, 1, &pm_base);
if (EFI_ERROR(status)) {
Print(L"Error reading PM_BASE: %r\n", status);
return 0;
}
- return (pm_base & regs->pm_base_addr_mask) + 0x60;
+ return pm_base & regs->pm_base_addr_mask;
+}
+
+static UINT32 get_tco_base(UINT32 pm_base, const iTCO_info *itco)
+{
+ const iTCO_regs* regs = &iTCO_version_regs[itco->itco_version];
+
+ if (regs->tco_base) {
+ return regs->tco_base;
+ } else if (pm_base) {
+ return pm_base + 0x60;
+ } else {
+ return 0;
+ }
}

static void update_no_reboot_flag_cnt(UINT32 tco_base)
@@ -252,9 +270,9 @@ static EFI_STATUS __attribute__((constructor))
init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 pci_device_id,
UINTN timeout)
{
+ UINT32 pm_base, tco_base, value;
UINT8 itco_chip;
const iTCO_info *itco;
- UINT32 tco_base, value;
EFI_STATUS status;

if (!pci_io || pci_vendor_id != PCI_VENDOR_ID_INTEL ||
@@ -265,8 +283,9 @@ init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 pci_device_id,

Print(L"Detected Intel TCO %s watchdog\n", itco->name);

- /* Get TCOBASE */
- tco_base = get_tco_base(pci_io, itco);
+ pm_base = get_pm_base(pci_io, itco);
+
+ tco_base = get_tco_base(pm_base, itco);
if (!tco_base) {
return EFI_UNSUPPORTED;
}
--
2.26.2

Jan Kiszka

unread,
May 31, 2021, 4:04:53 AM5/31/21
to efibootg...@googlegroups.com, Christian Storm
From: Jan Kiszka <jan.k...@siemens.com>

This register does not belong to the LPC PCI device. Thus, accessing it
directly is not only appropriate: Some BIOSes refuse the access via
the PCI protocol and EFI_PCI_IO_PASS_THROUGH_BAR, causing the no reboot
flag update to fail. See with QEMU and TianoCore e.g.

Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---
drivers/watchdog/itco.c | 21 +++++----------------
1 file changed, 5 insertions(+), 16 deletions(-)

diff --git a/drivers/watchdog/itco.c b/drivers/watchdog/itco.c
index 7114978..007da60 100644
--- a/drivers/watchdog/itco.c
+++ b/drivers/watchdog/itco.c
@@ -199,7 +199,8 @@ static EFI_STATUS update_no_reboot_flag_mem(EFI_PCI_IO *pci_io,
{
const iTCO_regs* regs = &iTCO_version_regs[itco->itco_version];
EFI_STATUS status;
- UINT32 pmc_base, value;
+ UINT32 pmc_base;
+ UINTN pmc_reg;

status =
uefi_call_wrapper(pci_io->Pci.Read, 5, pci_io, EfiPciIoWidthUint32,
@@ -209,21 +210,9 @@ static EFI_STATUS update_no_reboot_flag_mem(EFI_PCI_IO *pci_io,
}
pmc_base &= regs->pmc_base_addr_mask;

- status = uefi_call_wrapper(pci_io->Mem.Read, 6, pci_io,
- EfiPciIoWidthUint32,
- EFI_PCI_IO_PASS_THROUGH_BAR,
- pmc_base + regs->pmc_reg, 1, &value);
- if (EFI_ERROR(status)) {
- return status;
- }
- value &= ~regs->pmc_no_reboot_mask;
- status = uefi_call_wrapper(pci_io->Mem.Write, 6, pci_io,
- EfiPciIoWidthUint32,
- EFI_PCI_IO_PASS_THROUGH_BAR,
- pmc_base + regs->pmc_reg, 1, &value);
- if (EFI_ERROR(status)) {
- return status;
- }
+ pmc_reg = pmc_base + regs->pmc_reg;
+ *(volatile UINT32 *)pmc_reg &= ~regs->pmc_no_reboot_mask;
+
return status;
}

--
2.26.2

Reply all
Reply to author
Forward
0 new messages