acpi: Add support for WDAT table

This commit lays the groundwork for implementing the ACPI WDAT (Watchdog
Action Table) table specification. The WDAT is a special ACPI table
introduced by Microsoft that describes the watchdog for the OS.

Platforms that need to implement the WDAT table must describe the
hardware watchdog management operations as described in the
specification. See “Links to ACPI-Related Documents”
(http://uefi.org/acpi) under the heading “Watchdog Action Table”.

BUG=b:314260167
TEST=Mock the acpi_soc_fill_wdat function for a specific platform/soc
and enable ACPI_WDAT_WDT in the kconfig. Check if the build passes
successfully.

Change-Id: Ieb82d1f69b2b7fffacfd2928bc71f8ff10498074
Signed-off-by: Marek Maslanka <mmaslanka@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/79380
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Jakub Czapiga <czapiga@google.com>
diff --git a/src/acpi/Kconfig b/src/acpi/Kconfig
index 83d18bf..417ff60 100644
--- a/src/acpi/Kconfig
+++ b/src/acpi/Kconfig
@@ -111,3 +111,11 @@
 	help
 	  This variable sets the maximum number of distinct caches per
 	  topology level. Increasing this option also increases stack usage.
+
+config ACPI_WDAT_WDT
+	bool
+	default n
+	depends on HAVE_ACPI_TABLES
+	help
+	  Selected by platforms that support and fill ACPI Watchdog Action Table
+	  (WDAT).
diff --git a/src/acpi/acpi.c b/src/acpi/acpi.c
index beba5fd..5e76916 100644
--- a/src/acpi/acpi.c
+++ b/src/acpi/acpi.c
@@ -1218,6 +1218,25 @@
 	header->length = current - (unsigned long)iort;
 }
 
+static void acpi_create_wdat(acpi_header_t *header, void *unused)
+{
+	if (!CONFIG(ACPI_WDAT_WDT))
+		return;
+
+	acpi_wdat_t *wdat = (acpi_wdat_t *)header;
+	unsigned long current = (unsigned long)wdat + sizeof(acpi_wdat_t);
+
+	memset((void *)wdat, 0, sizeof(acpi_wdat_t));
+
+	if (acpi_fill_header(header, "WDAT", WDAT, sizeof(acpi_wdat_t)) != CB_SUCCESS)
+		return;
+
+	current = acpi_soc_fill_wdat(wdat, current);
+
+	/* (Re)calculate length. */
+	header->length = current - (unsigned long)wdat;
+}
+
 unsigned long acpi_create_lpi_desc_ncst(acpi_lpi_desc_ncst_t *lpi_desc, uint16_t uid)
 {
 	memset(lpi_desc, 0, sizeof(acpi_lpi_desc_ncst_t));
@@ -1434,6 +1453,7 @@
 		{ acpi_create_gtdt, NULL, sizeof(acpi_gtdt_t) },
 		{ acpi_create_pptt, NULL, sizeof(acpi_pptt_t) },
 		{ acpi_create_iort, NULL, sizeof(acpi_iort_t) },
+		{ acpi_create_wdat, NULL, sizeof(acpi_wdat_t) },
 	};
 
 	current = start;
@@ -1787,6 +1807,8 @@
 		return 3;
 	case IORT: /* IO Remapping Table E.e */
 		return 6;
+	case WDAT:
+		return 1;
 	default:
 		return -1;
 	}
diff --git a/src/include/acpi/acpi.h b/src/include/acpi/acpi.h
index 01504ce..d5f4008 100644
--- a/src/include/acpi/acpi.h
+++ b/src/include/acpi/acpi.h
@@ -102,6 +102,7 @@
 	SSDT,   /* Secondary System Description Table */
 	TCPA,   /* Trusted Computing Platform Alliance Table */
 	TPM2,   /* Trusted Platform Module 2.0 Table */
+	WDAT,   /* Watchdog Action Table */
 	XSDT,   /* Extended System Description Table */
 	/* Additional proprietary tables used by coreboot */
 	CRAT,   /* Component Resource Attribute Table */
@@ -1657,6 +1658,70 @@
 #define ACPI_GTDT_WATCHDOG_IRQ_POLARITY     (1<<1)
 #define ACPI_GTDT_WATCHDOG_SECURE           (1<<2)
 
+enum acpi_wdat_actions {
+	ACPI_WDAT_RESET = 1,
+	ACPI_WDAT_GET_CURRENT_COUNTDOWN = 4,
+	ACPI_WDAT_GET_COUNTDOWN = 5,
+	ACPI_WDAT_SET_COUNTDOWN = 6,
+	ACPI_WDAT_GET_RUNNING_STATE = 8,
+	ACPI_WDAT_SET_RUNNING_STATE = 9,
+	ACPI_WDAT_GET_STOPPED_STATE = 10,
+	ACPI_WDAT_SET_STOPPED_STATE = 11,
+	ACPI_WDAT_GET_REBOOT = 16,
+	ACPI_WDAT_SET_REBOOT = 17,
+	ACPI_WDAT_GET_SHUTDOWN = 18,
+	ACPI_WDAT_SET_SHUTDOWN = 19,
+	ACPI_WDAT_GET_STATUS = 32,
+	ACPI_WDAT_SET_STATUS = 33,
+	ACPI_WDAT_ACTION_RESERVED = 34	/* 34 and greater are reserved */
+};
+
+enum acpi_wdat_instructions {
+	ACPI_WDAT_READ_VALUE = 0,
+	ACPI_WDAT_READ_COUNTDOWN = 1,
+	ACPI_WDAT_WRITE_VALUE = 2,
+	ACPI_WDAT_WRITE_COUNTDOWN = 3,
+	ACPI_WDAT_INSTRUCTION_RESERVED = 4,	/* 4 and greater are reserved */
+	ACPI_WDAT_PRESERVE_REGISTER = 0x80	/* Except for this value */
+};
+
+enum acpi_wdat_flags {
+	ACPI_WDAT_FLAG_DISABLED = 0,
+	ACPI_WDAT_FLAG_ENABLED = 1
+};
+
+enum acpi_wdat_access_size {
+	ACPI_WDAT_ACCESS_SIZE_BYTE = 1,
+	ACPI_WDAT_ACCESS_SIZE_WORD = 2,
+	ACPI_WDAT_ACCESS_SIZE_DWORD = 3
+};
+
+/* ACPI WDAT */
+typedef struct acpi_wdat_entry {
+	u8 action;
+	u8 instruction;
+	u16 reserved;
+	struct acpi_gen_regaddr register_region;
+	u32 value;
+	u32 mask;
+} __packed acpi_wdat_entry_t;
+
+typedef struct acpi_table_wdat {
+	acpi_header_t header;	/* Common ACPI table header */
+	u32 header_length;
+	u16 pci_segment;
+	u8 pci_bus;
+	u8 pci_device;
+	u8 pci_function;
+	u8 reserved[3];
+	u32 timer_period;
+	u32 max_count;
+	u32 min_count;
+	u8 flags;
+	u8 reserved2[3];
+	u32 entries;
+} __packed acpi_wdat_t;
+
 uintptr_t get_coreboot_rsdp(void);
 void acpi_create_einj(acpi_einj_t *einj, uintptr_t addr, u8 actions);
 
@@ -1819,6 +1884,35 @@
 unsigned long acpi_gtdt_add_watchdog(unsigned long current, uint64_t refresh_frame,
 				     uint64_t control_frame, uint32_t gsiv, uint32_t flags);
 
+/*
+ * Populate primary acpi_wdat_t struct to provide basic information about watchdog and
+ * associated acpi_wdat_entry_t structures, which correspond to watchdog-related
+ * actions such as start/stop watchdog, set timeout, ping watchdog, get remaining time,
+ * etc. Each acpi_wdat_entry_t entry indicates what needs to be written to a specific
+ * address to perform a specific action or at which address the watchdog-related
+ * information is stored.
+ *
+ * The acpi_wdat_entry_t structures follow the acpi_wdat_t, so the table layout is as
+ * follows:
+ *  +---------------------+
+ *  | acpi_wdat_t {       |
+ *  |     ...             |
+ *  | }                   |
+ *  | acpi_wdat_entry_t { |
+ *  |     ...             |
+ *  | }                   |
+ *  | acpi_wdat_entry_t { |
+ *  |     ...             |
+ *  | }                   |
+ *  +---------------------+
+ *
+ * @param wdat Pointer to populate acpi_wdat_t struct
+ * @param current Position in memory after the acpi_wdat_t struct which also indicates
+ *                the position where the first acpi_wdat_entry_t must be placed.
+ * @return Position after last acpi_wdat_entry_t struct
+ */
+unsigned long acpi_soc_fill_wdat(acpi_wdat_t *wdat, unsigned long current);
+
 /* For ACPI S3 support. */
 void __noreturn acpi_resume(void *wake_vec);
 void mainboard_suspend_resume(void);