From: Christian Storm <[email protected]>

Refactoring of the iTCO watchdog support module to make
integration of further supported iTCOs easier.

Support for the following iTCO watchdogs is added/improved:
* Apollo Lake for IPC127E
* ICH9 for QEmu's q35 machine

Signed-off-by: Christian Storm <[email protected]>
---
 drivers/watchdog/itco.c | 281 +++++++++++++++++++++++++++++-----------
 1 file changed, 202 insertions(+), 79 deletions(-)

diff --git a/drivers/watchdog/itco.c b/drivers/watchdog/itco.c
index 8346f36..dc1733e 100644
--- a/drivers/watchdog/itco.c
+++ b/drivers/watchdog/itco.c
@@ -1,10 +1,11 @@
 /*
  * EFI Boot Guard, iTCO support (Version 3 and later)
  *
- * Copyright (c) Siemens AG, 2017
+ * Copyright (c) Siemens AG, 2019
  *
  * Authors:
  *  Jan Kiszka <[email protected]>
+ *  Christian Storm <[email protected]>
  *
  * This work is licensed under the terms of the GNU GPL, version 2.  See
  * the COPYING file in the top-level directory.
@@ -16,136 +17,258 @@
 #include <efilib.h>
 #include <pci/header.h>
 
-#define PCI_DEVICE_ID_INTEL_BAYTRAIL   0x0f1c
-#define PCI_DEVICE_ID_INTEL_WPT_LP     0x9cc3
-#define PCI_DEVICE_ID_INTEL_APL        0x5ae8
+#define PMBASE_ADDRMASK 0x0000ff80
+#define PMCBASE_ADDRMASK 0xfffffe00
 
-#define PMBASE_REG                     0x40
-# define PMBASE_ADDRMASK               0xff00
-#define PMCBASE_REG                    0x44
-# define PMCBASE_ADDRMASK              0xfffffe00
+#define SMI_TCO_MASK (1 << 13)
 
-#define SMI_EN_REG                     0x30
-# define TCO_EN                                (1 << 13)
+#define TCO_RLD_REG 0x00
+#define TCO1_CNT_REG 0x08
+#define TCO_TMR_HLT_MASK (1 << 11)
+#define TCO_TMR_REG 0x12
 
-#define TCO_RLD_REG                    0x00
-#define TCO1_CNT_REG                   0x08
-# define TCO_TMR_HLT                   (1 << 11)
-#define TCO_TMR_REG                    0x12
+#define PMC_NO_REBOOT_MASK (1 << 4)
 
-#define PMC_REG                                0x08
-# define PMC_NO_REBOOT                 (1 << 4)
+enum iTCO_chipsets {
+       ITCO_INTEL_APL = 0,
+       ITCO_INTEL_BAYTRAIL,
+       ITCO_INTEL_WPT_LP,
+       ITCO_INTEL_ICH9,
+};
 
-static EFI_STATUS __attribute__((constructor))
-init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 pci_device_id,
-     UINTN timeout)
+typedef struct {
+       CHAR16 name[16];
+       UINT32 pci_id;
+       UINT32 pmbase;
+       UINT32 pmcbasereg;
+       UINT32 pmcreg;
+       UINT32 smireg;
+} iTCO_info;
+
+static iTCO_info iTCO_chipset_info[] = {
+    [ITCO_INTEL_APL] =
+       {
+           .name = L"Apollo Lake",
+           .pci_id = 0x5ae8,
+           .pmcbasereg = 0x10,
+           .pmcreg = 0x1008,
+           .smireg = 0x40,
+           .pmbase = 0x400,
+       },
+    [ITCO_INTEL_BAYTRAIL] =
+       {
+           .name = L"Baytrail",
+           .pci_id = 0x0f1c,
+           .pmcbasereg = 0x44,
+           .pmcreg = 0x08,
+           .smireg = 0x30,
+       },
+    [ITCO_INTEL_WPT_LP] =
+       {
+           .name = L"Wildcat",
+           .pci_id = 0x9cc3,
+           .pmcbasereg = 0x44,
+           .pmcreg = 0x08,
+           .smireg = 0x30,
+       },
+    [ITCO_INTEL_ICH9] =
+       {
+           .name = L"ICH9", /* QEmu machine q35 */
+           .pci_id = 0x2918,
+           .pmcbasereg = 0x44,
+           .pmcreg = 0x08,
+           .smireg = 0x30,
+       },
+};
+
+static BOOLEAN itco_supported(UINT16 pci_device_id, UINT8 *index)
 {
-       UINT32 pmbase, tcobase, pmcbase, value;
+       for (UINT8 i = 0;
+            i < (sizeof(iTCO_chipset_info) / sizeof(iTCO_chipset_info[0]));
+            i++) {
+               if (pci_device_id == iTCO_chipset_info[i].pci_id) {
+                       *index = i;
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+static UINT32 get_pmbase(EFI_PCI_IO *pci_io, iTCO_info *itco)
+{
+       UINT32 pmbase;
        EFI_STATUS status;
 
-       if (!pci_io || pci_vendor_id != PCI_VENDOR_ID_INTEL ||
-           (pci_device_id != PCI_DEVICE_ID_INTEL_BAYTRAIL &&
-            pci_device_id != PCI_DEVICE_ID_INTEL_WPT_LP &&
-            pci_device_id != PCI_DEVICE_ID_INTEL_APL)) {
-               return EFI_UNSUPPORTED;
+       if (itco->pmbase) {
+               return itco->pmbase & PMBASE_ADDRMASK;
        }
 
-       Print(L"Detected Intel TCO watchdog\n");
-
-       /* Get PMBASE and TCOBASE */
        status = uefi_call_wrapper(pci_io->Pci.Read, 5, pci_io,
-                                  EfiPciIoWidthUint32, PMBASE_REG,
-                                  1, &pmbase);
+                                  EfiPciIoWidthUint32, 0x40, 1, &pmbase);
        if (EFI_ERROR(status)) {
-               return status;
+               Print(L"Error reading PMBASE: %r\n", status);
+               return 0;
        }
-       pmbase &= PMBASE_ADDRMASK;
-       tcobase = (pmbase & PMBASE_ADDRMASK) + 0x60;
+       return pmbase & PMBASE_ADDRMASK;
+}
 
-       /* Get PMCBASE address */
-       status = uefi_call_wrapper(pci_io->Pci.Read, 5, pci_io,
-                                  EfiPciIoWidthUint32, PMCBASE_REG,
-                                  1, &pmcbase);
+static EFI_STATUS update_no_reboot_flag(EFI_PCI_IO *pci_io, iTCO_info *itco)
+{
+       EFI_STATUS status;
+       UINT32 pmcbase, value;
+
+       status =
+           uefi_call_wrapper(pci_io->Pci.Read, 5, pci_io, EfiPciIoWidthUint32,
+                             itco->pmcbasereg, 1, &pmcbase);
        if (EFI_ERROR(status)) {
                return status;
        }
        pmcbase &= PMCBASE_ADDRMASK;
 
+       status = uefi_call_wrapper(
+           pci_io->Mem.Read, 6, pci_io, EfiPciIoWidthUint32,
+           EFI_PCI_IO_PASS_THROUGH_BAR, pmcbase + itco->pmcreg, 1, &value);
+       if (EFI_ERROR(status)) {
+               return status;
+       }
+       value &= ~PMC_NO_REBOOT_MASK;
+       status = uefi_call_wrapper(
+           pci_io->Mem.Write, 6, pci_io, EfiPciIoWidthUint32,
+           EFI_PCI_IO_PASS_THROUGH_BAR, pmcbase + itco->pmcreg, 1, &value);
+       if (EFI_ERROR(status)) {
+               return status;
+       }
+       return status;
+}
+
+static EFI_STATUS update_no_reboot_flag_apl(__attribute__((unused))
+                                           EFI_PCI_IO *pci_io,
+                                           iTCO_info *itco)
+{
+#define MM_PCI_OFFSET(Bus, Device, Function)                                   
\
+       ((UINTN)(Bus << 20) + (UINTN)(Device << 15) + (UINTN)(Function << 12))
+
+#define PCIE_MMCFG_BASE 0xE0000000
+
+#define MmPciBase(Bus, Device, Function, Offset)                               
\
+       ((UINTN)(PCIE_MMCFG_BASE) + MM_PCI_OFFSET(Bus, Device, Function) +     \
+        Offset)
+
+#define MmPciBarAddr(Bus, Device, Function, Bar, Type)                         
\
+       ((UINTN)(*(                                                            \
+           volatile Type *)(((UINTN)MmPciBase(Bus, Device, Function,          \
+                                              0x10 + (Bar) * sizeof(Type))) & \
+                            ~0x0f)))
+
+       /* Unhide the P2SB device if it's hidden. */
+       BOOLEAN p2sbvisible =
+           *(volatile UINT16 *)MmPciBase(0, 13, 0, 0) != 0xFFFF;
+       if (!p2sbvisible) {
+               *(volatile UINT8 *)MmPciBase(0, 13, 0, 0xE1) = 0;
+       }
+
+       /* Get PMC_BASE from PMC Controller Register. */
+       volatile UINT8 *reg =
+           (volatile UINT8 *)(MmPciBase(0, 13, 1, (UINTN)itco->pmcreg));
+       UINT8 value = *reg;
+       value &= ~PMC_NO_REBOOT_MASK;
+       *reg = value;
+
+       if (p2sbvisible) {
+               *(volatile UINT8 *)MmPciBase(0, 13, 0, 0xE1) = 1;
+       }
+
+       return EFI_SUCCESS;
+}
+
+static EFI_STATUS __attribute__((constructor))
+init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 pci_device_id,
+     UINTN timeout)
+{
+       UINT8 itco_chip;
+       iTCO_info *itco;
+       UINT32 pmbase, tcobase, value;
+       EFI_STATUS status;
+
+       if (!pci_io || pci_vendor_id != PCI_VENDOR_ID_INTEL ||
+           !itco_supported(pci_device_id, &itco_chip)) {
+               return EFI_UNSUPPORTED;
+       }
+       itco = &iTCO_chipset_info[itco_chip];
+
+       Print(L"Detected Intel TCO %s watchdog\n", itco->name);
+
+       /* Get PMBASE and TCOBASE */
+       if ((pmbase = get_pmbase(pci_io, itco)) == 0) {
+               return EFI_UNSUPPORTED;
+       }
+       tcobase = pmbase + 0x60;
+
        /* Enable TCO SMIs */
-       status = uefi_call_wrapper(pci_io->Io.Read, 6, pci_io,
-                                  EfiPciIoWidthUint32,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  pmbase + SMI_EN_REG, 1, &value);
+       status = uefi_call_wrapper(
+           pci_io->Io.Read, 6, pci_io, EfiPciIoWidthUint32,
+           EFI_PCI_IO_PASS_THROUGH_BAR, pmbase + itco->smireg, 1, &value);
        if (EFI_ERROR(status)) {
                return status;
        }
-       value |= TCO_EN;
-       status = uefi_call_wrapper(pci_io->Io.Write, 6, pci_io,
-                                  EfiPciIoWidthUint32,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  pmbase + SMI_EN_REG, 1, &value);
+       value |= SMI_TCO_MASK;
+       status = uefi_call_wrapper(
+           pci_io->Io.Write, 6, pci_io, EfiPciIoWidthUint32,
+           EFI_PCI_IO_PASS_THROUGH_BAR, pmbase + itco->smireg, 1, &value);
        if (EFI_ERROR(status)) {
                return status;
        }
 
        /* Set timer value */
-       status = uefi_call_wrapper(pci_io->Io.Read, 6, pci_io,
-                                  EfiPciIoWidthUint16,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  tcobase + TCO_TMR_REG, 1, &value);
+       status = uefi_call_wrapper(
+           pci_io->Io.Read, 6, pci_io, EfiPciIoWidthUint16,
+           EFI_PCI_IO_PASS_THROUGH_BAR, tcobase + TCO_TMR_REG, 1, &value);
        if (EFI_ERROR(status)) {
                return status;
        }
        value &= 0xfc00;
-       value |= timeout & 0x3ff;
-       status = uefi_call_wrapper(pci_io->Io.Write, 6, pci_io,
-                                  EfiPciIoWidthUint16,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  tcobase + TCO_TMR_REG, 1, &value);
+       value |= ((timeout * 10) / 6) & 0x3ff;
+       status = uefi_call_wrapper(
+           pci_io->Io.Write, 6, pci_io, EfiPciIoWidthUint16,
+           EFI_PCI_IO_PASS_THROUGH_BAR, tcobase + TCO_TMR_REG, 1, &value);
        if (EFI_ERROR(status)) {
                return status;
        }
 
        /* Force reloading of timer value */
        value = 1;
-       status = uefi_call_wrapper(pci_io->Io.Write, 6, pci_io,
-                                  EfiPciIoWidthUint16,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  tcobase + TCO_RLD_REG, 1, &value);
+       status = uefi_call_wrapper(
+           pci_io->Io.Write, 6, pci_io, EfiPciIoWidthUint16,
+           EFI_PCI_IO_PASS_THROUGH_BAR, tcobase + TCO_RLD_REG, 1, &value);
        if (EFI_ERROR(status)) {
                return status;
        }
 
        /* Clear NO_REBOOT flag */
-       status = uefi_call_wrapper(pci_io->Mem.Read, 6, pci_io,
-                                  EfiPciIoWidthUint32,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  pmcbase + PMC_REG, 1, &value);
-       if (EFI_ERROR(status)) {
-               return status;
+       switch (itco_chip) {
+       case ITCO_INTEL_APL:
+               status = update_no_reboot_flag_apl(pci_io, itco);
+               break;
+       case ITCO_INTEL_BAYTRAIL:
+       case ITCO_INTEL_WPT_LP:
+               status = update_no_reboot_flag(pci_io, itco);
+               break;
        }
-       value &= ~PMC_NO_REBOOT;
-       status = uefi_call_wrapper(pci_io->Mem.Write, 6, pci_io,
-                                  EfiPciIoWidthUint32,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  pmcbase + PMC_REG, 1, &value);
        if (EFI_ERROR(status)) {
                return status;
        }
 
        /* Clear HLT flag to start timer */
-       status = uefi_call_wrapper(pci_io->Io.Read, 6, pci_io,
-                                  EfiPciIoWidthUint16,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  tcobase + TCO1_CNT_REG, 1, &value);
+       status = uefi_call_wrapper(
+           pci_io->Io.Read, 6, pci_io, EfiPciIoWidthUint16,
+           EFI_PCI_IO_PASS_THROUGH_BAR, tcobase + TCO1_CNT_REG, 1, &value);
        if (EFI_ERROR(status)) {
                return status;
        }
-       value &= ~TCO_TMR_HLT;
-       status = uefi_call_wrapper(pci_io->Io.Write, 6, pci_io,
-                                  EfiPciIoWidthUint16,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  tcobase + TCO1_CNT_REG, 1, &value);
+       value &= ~TCO_TMR_HLT_MASK;
+       status = uefi_call_wrapper(
+           pci_io->Io.Write, 6, pci_io, EfiPciIoWidthUint16,
+           EFI_PCI_IO_PASS_THROUGH_BAR, tcobase + TCO1_CNT_REG, 1, &value);
 
        return status;
 }
-- 
2.23.0

-- 
You received this message because you are subscribed to the Google Groups "EFI 
Boot Guard" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/efibootguard-dev/20191009152443.23093-1-christian.storm%40siemens.com.

Reply via email to