arch/x86: Expose function do get DRHDs from DMAR ACPI table

This is part of Intel VT-D and how to discover capabilities, base
addresses and so on in order to start taking advantage from it.

There is a lot to get from there, but currently we are interested only
by getting the remapping hardware base address. And more specifically
for interrupt remapping usage.

There might be more than one of such hardware so the exposed function is
made to retrieve all of them.

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
diff --git a/arch/x86/core/acpi.c b/arch/x86/core/acpi.c
index 01e1848..6e553e7 100644
--- a/arch/x86/core/acpi.c
+++ b/arch/x86/core/acpi.c
@@ -7,6 +7,8 @@
 
 static struct acpi_rsdp *rsdp;
 bool is_rdsp_searched = false;
+static struct acpi_dmar *dmar;
+bool is_dmar_searched;
 
 static bool check_sum(struct acpi_sdt *t)
 {
@@ -159,3 +161,120 @@
 
 	return NULL;
 }
+
+static void find_dmar(void)
+{
+	if (is_dmar_searched) {
+		return;
+	}
+
+	dmar = z_acpi_find_table(ACPI_DMAR_SIGNATURE);
+	is_dmar_searched = true;
+}
+
+struct acpi_dmar *z_acpi_find_dmar(void)
+{
+	find_dmar();
+	return dmar;
+}
+
+struct acpi_drhd *z_acpi_find_drhds(int *n)
+{
+	struct acpi_drhd *drhds = NULL;
+	uintptr_t offset;
+	uintptr_t base;
+
+	find_dmar();
+
+	if (dmar == NULL) {
+		return NULL;
+	}
+
+	*n = 0;
+	base = POINTER_TO_UINT(dmar);
+
+	offset = POINTER_TO_UINT(dmar->remap_entries) - base;
+	while (offset < dmar->sdt.length) {
+		struct acpi_dmar_entry *entry;
+
+		entry = (struct acpi_dmar_entry *)(offset + base);
+		if (entry->type == ACPI_DMAR_TYPE_DRHD) {
+			if (*n == 0) {
+				drhds = (struct acpi_drhd *)entry;
+			}
+
+			(*n)++;
+		} else {
+			/* DMAR entries are found packed by type so
+			 * if type is not DRHD, we will not encounter one,
+			 * anymore.
+			 */
+			break;
+		}
+
+		offset += entry->length;
+	}
+
+	return drhds;
+}
+
+struct acpi_dmar_dev_scope *z_acpi_get_drhd_dev_scopes(struct acpi_drhd *drhd,
+						       int *n)
+{
+	uintptr_t offset;
+	uintptr_t base;
+
+	if (drhd->entry.length <= ACPI_DRHD_MIN_SIZE) {
+		return NULL;
+	}
+
+	*n = 0;
+	base = POINTER_TO_UINT(drhd);
+
+	offset = POINTER_TO_UINT(drhd->device_scope) - base;
+	while (offset < drhd->entry.length) {
+		struct acpi_dmar_dev_scope *dev_scope;
+
+		dev_scope = (struct acpi_dmar_dev_scope *)(offset + base);
+
+		(*n)++;
+
+		offset += dev_scope->length;
+	}
+
+	return (*n == 0) ? NULL : drhd->device_scope;
+}
+
+struct acpi_dmar_dev_path *
+z_acpi_get_dev_scope_paths(struct acpi_dmar_dev_scope *dev_scope, int *n)
+{
+	switch (dev_scope->type) {
+	case ACPI_DRHD_DEV_SCOPE_PCI_EPD:
+		/* Fall through */
+	case ACPI_DRHD_DEV_SCOPE_PCI_SUB_H:
+		/* Fall through */
+	case ACPI_DRHD_DEV_SCOPE_IOAPIC:
+		if (dev_scope->length < (ACPI_DMAR_DEV_SCOPE_MIN_SIZE +
+					 ACPI_DMAR_DEV_PATH_SIZE)) {
+			return NULL;
+		}
+
+		break;
+	case ACPI_DRHD_DEV_SCOPE_MSI_CAP_HPET:
+		/* Fall through */
+	case ACPI_DRHD_DEV_SCOPE_NAMESPACE_DEV:
+		if (dev_scope->length != (ACPI_DMAR_DEV_SCOPE_MIN_SIZE +
+					  ACPI_DMAR_DEV_PATH_SIZE)) {
+			return NULL;
+		}
+
+		break;
+	default:
+		return NULL;
+	}
+
+	*n = (dev_scope->length - ACPI_DMAR_DEV_SCOPE_MIN_SIZE) /
+		ACPI_DMAR_DEV_PATH_SIZE;
+
+	return dev_scope->path;
+}
diff --git a/include/arch/x86/acpi.h b/include/arch/x86/acpi.h
index eee69c5..559f5ef 100644
--- a/include/arch/x86/acpi.h
+++ b/include/arch/x86/acpi.h
@@ -92,16 +92,97 @@
 
 #define ACPI_CPU_FLAGS_ENABLED 0x01
 
+/* Generic DMA Remapping entry structure part */
+struct acpi_dmar_entry {
+	uint16_t type; /* See ACPI_DMAR_TYPE_* below */
+	uint16_t length;
+} __packed;
+
+#define ACPI_DMAR_TYPE_DRHD 0 /* DMA Remapping Hardware Unit Definition */
+#define ACPI_DMAR_TYPE_RMRR 1 /* Do not care atm (legacy usage) */
+#define ACPI_DMAR_TYPE_ATSR 2 /* Do not care atm (PCIE ATS support) */
+#define ACPI_DMAR_TYPE_RHSA 3 /* Do not care atm (NUMA specific ) */
+#define ACPI_DMAR_TYPE_ANDD 4 /* Do not care atm (ACPI DSDT related) */
+#define ACPI_DMAR_TYPE_SACT 5 /* Do not care atm */
+
+/* PCI Device/Function Pair (forming the BDF, with start_bus_num below) */
+struct acpi_dmar_dev_path {
+	uint8_t device;
+	uint8_t function;
+} __packed;
+
+#define ACPI_DMAR_DEV_PATH_SIZE			2
+
+/* DMA Remapping Device Scope */
+struct acpi_dmar_dev_scope {
+	uint8_t type; /* See ACPI_DRHD_DEV_SCOPE_* below */
+	uint8_t length; /* 6 + X where X is Path attribute size */
+	uint16_t _reserved;
+	uint8_t enumeration_id;
+	uint8_t start_bus_num; /* PCI bus, forming BDF with each Path below */
+	struct acpi_dmar_dev_path path[]; /* One is at least always found */
+} __packed;
+
+#define ACPI_DMAR_DEV_SCOPE_MIN_SIZE		6
+
+#define ACPI_DRHD_DEV_SCOPE_PCI_EPD		0x01
+#define ACPI_DRHD_DEV_SCOPE_PCI_SUB_H		0x02
+#define ACPI_DRHD_DEV_SCOPE_IOAPIC		0x03
+#define ACPI_DRHD_DEV_SCOPE_MSI_CAP_HPET	0x04
+#define ACPI_DRHD_DEV_SCOPE_NAMESPACE_DEV	0x05
+
+struct acpi_drhd {
+	struct acpi_dmar_entry entry;
+	uint8_t flags; /* See ACPI_DRHD_FLAG_* below */
+	uint8_t _reserved;
+	uint16_t segment_num; /* Associated PCI segment */
+	uint64_t base_address; /* Base address of the remapping hw */
+	struct acpi_dmar_dev_scope device_scope[];
+} __packed;
+
+#define ACPI_DRHD_MIN_SIZE			16
+
+#define ACPI_DRHD_FLAG_INCLUDE_PCI_ALL		BIT(0)
+
+#define ACPI_DMAR_SIGNATURE 0x52414D44  /* 'DMAR' */
+
+#define ACPI_DMAR_FLAG_INTR_REMAP		BIT(0)
+#define ACPI_DMAR_FLAG_X2APIC_OPT_OUT		BIT(1)
+#define ACPI_DMAR_FLAG_DMA_CTRL_PLATFORM_OPT_IN	BIT(2)
+
+/* DMA Remapping reporting structure */
+struct acpi_dmar {
+	struct acpi_sdt sdt;
+	uint8_t host_addr_width;
+	uint8_t flags;
+	uint8_t _reserved[10];
+	struct acpi_dmar_entry remap_entries[];
+} __packed;
+
 #if defined(CONFIG_ACPI)
 
 void *z_acpi_find_table(uint32_t signature);
 
 struct acpi_cpu *z_acpi_get_cpu(int n);
 
+struct acpi_dmar *z_acpi_find_dmar(void);
+
+struct acpi_drhd *z_acpi_find_drhds(int *n);
+
+struct acpi_dmar_dev_scope *z_acpi_get_drhd_dev_scopes(struct acpi_drhd *drhd,
+						       int *n);
+
+struct acpi_dmar_dev_path *
+z_acpi_get_dev_scope_paths(struct acpi_dmar_dev_scope *dev_scope, int *n);
+
 #else /* CONFIG_ACPI */
 
 #define z_acpi_find_table(...) NULL
 #define z_acpi_get_cpu(...) NULL
+#define z_acpi_find_dmar(...) NULL
+#define z_acpi_find_drhds(...) NULL
+#define z_acpi_get_drhd_dev_scopes(...) NULL
+#define z_acpi_get_dev_scope_paths(...) NULL
 
 #endif /* CONFIG_ACPI */