acpi: Add initial support for CEDT

Add initial CEDT (CXL Early Discovery Table) support based on
CXL spec 2.0 section 9.14.1.

Add functions to create CEDT table (revision 1), and create CEDT
CXL Host Bridge Structure (CHBS) and CXL Fixed Memory Windows
Structure (CFMWS).

TESTED=Create CEDT table on Intel Archer City CRB, dumped the
CEDT table and examined the content.

Signed-off-by: Jonathan Zhang <jonzhang@fb.com>
Change-Id: I4fbce78efc86ad9f2468c37b4827a6dadbdc6802
Reviewed-on: https://review.coreboot.org/c/coreboot/+/64263
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-by: Marc Jones <marc@marcjonesconsulting.com>
diff --git a/src/acpi/acpi.c b/src/acpi/acpi.c
index e1d4088..8d81284 100644
--- a/src/acpi/acpi.c
+++ b/src/acpi/acpi.c
@@ -575,6 +575,90 @@
 	header->checksum = acpi_checksum((void *)srat, header->length);
 }
 
+int acpi_create_cedt_chbs(acpi_cedt_chbs_t *chbs, u32 uid, u32 cxl_ver, u64 base)
+{
+	memset((void *)chbs, 0, sizeof(acpi_cedt_chbs_t));
+
+	chbs->type = ACPI_CEDT_STRUCTURE_CHBS;
+	chbs->length = sizeof(acpi_cedt_chbs_t);
+	chbs->uid = uid;
+	chbs->cxl_ver = cxl_ver;
+	chbs->base = base;
+
+	/*
+	 * CXL spec 2.0 section 9.14.1.2 "CXL CHBS"
+	 * CXL 1.1 spec compliant host bridge: 8KB
+	 * CXL 2.0 spec compliant host bridge: 64KB
+	 */
+	if (cxl_ver == ACPI_CEDT_CHBS_CXL_VER_1_1)
+		chbs->len = 8 * KiB;
+	else if (cxl_ver == ACPI_CEDT_CHBS_CXL_VER_2_0)
+		chbs->len = 64 * KiB;
+	else
+		printk(BIOS_ERR, "ACPI(%s:%s): Incorrect CXL version:%d\n", __FILE__, __func__,
+		       cxl_ver);
+
+	return chbs->length;
+}
+
+int acpi_create_cedt_cfmws(acpi_cedt_cfmws_t *cfmws, u64 base_hpa, u64 window_size, u8 eniw,
+			   u32 hbig, u16 restriction, u16 qtg_id, const u32 *interleave_target)
+{
+	memset((void *)cfmws, 0, sizeof(acpi_cedt_cfmws_t));
+
+	cfmws->type = ACPI_CEDT_STRUCTURE_CFMWS;
+
+	u8 niw = 0;
+	if (eniw >= 8)
+		printk(BIOS_ERR, "ACPI(%s:%s): Incorrect eniw::%d\n", __FILE__, __func__, eniw);
+	else
+		/* NIW = 2 ** ENIW */
+		niw = 0x1 << eniw;
+	/* 36 + 4 * NIW */
+	cfmws->length = sizeof(acpi_cedt_cfmws_t) + 4 * niw;
+
+	cfmws->base_hpa = base_hpa;
+	cfmws->window_size = window_size;
+	cfmws->eniw = eniw;
+
+	// 0: Standard Modulo Arithmetic. Other values reserved.
+	cfmws->interleave_arithmetic = 0;
+
+	cfmws->hbig = hbig;
+	cfmws->restriction = restriction;
+	cfmws->qtg_id = qtg_id;
+	memcpy(&cfmws->interleave_target, interleave_target, 4 * niw);
+
+	return cfmws->length;
+}
+
+void acpi_create_cedt(acpi_cedt_t *cedt, unsigned long (*acpi_fill_cedt)(unsigned long current))
+{
+	acpi_header_t *header = &(cedt->header);
+	unsigned long current = (unsigned long)cedt + sizeof(acpi_cedt_t);
+
+	memset((void *)cedt, 0, sizeof(acpi_cedt_t));
+
+	if (!header)
+		return;
+
+	/* Fill out header fields. */
+	memcpy(header->signature, "CEDT", 4);
+	memcpy(header->oem_id, OEM_ID, 6);
+	memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
+	memcpy(header->asl_compiler_id, ASLC, 4);
+
+	header->asl_compiler_revision = asl_revision;
+	header->length = sizeof(acpi_cedt_t);
+	header->revision = get_acpi_table_revision(CEDT);
+
+	current = acpi_fill_cedt(current);
+
+	/* (Re)calculate length and checksum. */
+	header->length = current - (unsigned long)cedt;
+	header->checksum = acpi_checksum((void *)cedt, header->length);
+}
+
 int acpi_create_hmat_mpda(acpi_hmat_mpda_t *mpda, u32 initiator, u32 memory)
 {
 	memset((void *)mpda, 0, sizeof(acpi_hmat_mpda_t));
diff --git a/src/include/acpi/acpi.h b/src/include/acpi/acpi.h
index d7fc243..fa216aa 100644
--- a/src/include/acpi/acpi.h
+++ b/src/include/acpi/acpi.h
@@ -72,8 +72,8 @@
 
 enum acpi_tables {
 	/* Tables defined by ACPI and used by coreboot */
-	BERT, DBG2, DMAR, DSDT, EINJ, FACS, FADT, HEST, HMAT, HPET, IVRS, MADT,
-	MCFG, RSDP, RSDT, SLIT, SRAT, SSDT, TCPA, TPM2, XSDT, ECDT, LPIT,
+	BERT, CEDT, DBG2, DMAR, DSDT, EINJ, FACS, FADT, HEST, HMAT, HPET, IVRS,
+	MADT, MCFG, RSDP, RSDT, SLIT, SRAT, SSDT, TCPA, TPM2, XSDT, ECDT, LPIT,
 	/* Additional proprietary tables used by coreboot */
 	VFCT, NHLT, SPMI, CRAT
 };
@@ -222,6 +222,60 @@
 } __packed acpi_mcfg_mmconfig_t;
 
 /*
+ * CEDT (CXL Early Discovery Table)
+ * CXL spec 2.0 section 9.14.1
+ */
+typedef struct acpi_cedt {
+	acpi_header_t header;
+	/* Followed by CEDT structures[n] */
+} __packed acpi_cedt_t;
+
+#define ACPI_CEDT_STRUCTURE_CHBS 0
+#define ACPI_CEDT_STRUCTURE_CFMWS 1
+
+#define ACPI_CEDT_CHBS_CXL_VER_1_1 0x00
+#define ACPI_CEDT_CHBS_CXL_VER_2_0 0x01
+
+/* CHBS: CXL Host Bridge Structure */
+typedef struct acpi_cedt_chbs {
+	u8 type; /* Always 0, other values reserved */
+	u8 resv1;
+	u16 length; /* Length in bytes (32) */
+	u32 uid;    /* CXL Host Bridge Unique ID */
+	u32 cxl_ver;
+	u32 resv2;
+	/*
+	 * For CXL 1.1, the base is Downstream Port Root Complex Resource Block;
+	 * For CXL 2.0, the base is CXL Host Bridge Component Registers.
+	 */
+	u64 base;
+	u64 len;
+} __packed acpi_cedt_chbs_t;
+
+#define ACPI_CEDT_CFMWS_RESTRICTION_TYPE_2_MEM (1 << 0)
+#define ACPI_CEDT_CFMWS_RESTRICTION_TYPE_3_MEM (1 << 1)
+#define ACPI_CEDT_CFMWS_RESTRICTION_VOLATIL    (1 << 2)
+#define ACPI_CEDT_CFMWS_RESTRICTION_PERSISTENT (1 << 3)
+#define ACPI_CEDT_CFMWS_RESTRICTION_FIXED      (1 << 4)
+
+/* CFMWS: CXL Fixed Memory Window Structure */
+typedef struct acpi_cedt_cfmws {
+	u8 type; /* Type (0) */
+	u8 resv1;
+	u16 length; /* Length in bytes (32) */
+	u32 resv2;
+	u64 base_hpa;		  /* Base of the HPA range, 256MB aligned */
+	u64 window_size;	  /* Number of bytes this window represents */
+	u8 eniw;		  /* Encoded Number of Interleave Ways */
+	u8 interleave_arithmetic; /* Standard Modulo arithmetic (0) */
+	u16 resv3;
+	u32 hbig; /* Host Bridge Interleave Granularity */
+	u16 restriction;
+	u16 qtg_id;
+	u32 interleave_target[]; /* Interleave Target List */
+} __packed acpi_cedt_cfmws_t;
+
+/*
  * HMAT (Heterogeneous Memory Attribute Table)
  * ACPI spec 6.4 section 5.2.27
  */
@@ -1260,6 +1314,15 @@
 
 void acpi_add_table(acpi_rsdp_t *rsdp, void *table);
 
+/* Create CXL Early Discovery Table */
+void acpi_create_cedt(acpi_cedt_t *cedt,
+	unsigned long (*acpi_fill_cedt)(unsigned long current));
+/* Create a CXL Host Bridge Structure for CEDT */
+int acpi_create_cedt_chbs(acpi_cedt_chbs_t *chbs, u32 uid, u32 cxl_ver, u64 base);
+/* Create a CXL Fixed Memory Window Structure for CEDT */
+int acpi_create_cedt_cfmws(acpi_cedt_cfmws_t *cfmws, u64 base_hpa, u64 window_size,
+	u8 eniw, u32 hbig, u16 restriction, u16 qtg_id, const u32 *interleave_target);
+
 int acpi_create_madt_lapic(acpi_madt_lapic_t *lapic, u8 cpu, u8 apic);
 int acpi_create_madt_ioapic(acpi_madt_ioapic_t *ioapic, u8 id, u32 addr,
 			    u32 gsi_base);