1#include <acpi/dmar.h>
2#include <asm.h>
3#include <console/panic.h>
4#include <console/printf.h>
5#include <drivers/iommu/vt_d.h>
6#include <drivers/mmio.h>
7#include <log.h>
8#include <mem/page.h>
9#include <mem/vmm.h>
10#include <uacpi/tables.h>
11
12#include "uacpi/acpi.h"
13#include "uacpi/status.h"
14
15LOG_HANDLE_DECLARE_DEFAULT(dmar);
16LOG_SITE_DECLARE_DEFAULT(dmar);
17
18static void print_device_scopes(const uint8_t *ptr, const uint8_t *end) {
19 while (ptr < end) {
20 const struct acpi_dmar_dss *sc = (const void *) ptr;
21 if (sc->length < 6 || ptr + sc->length > end)
22 break;
23
24 printf(format: "scope: type=%d start_bus=%02x", sc->type, sc->start_bus);
25
26 size_t path_entries = (sc->length - 6) / 2;
27 const uint8_t *path = ptr + sizeof(*sc);
28 for (size_t i = 0; i < path_entries; i++)
29 printf(format: " %02x.%x", path[i * 2] >> 3, path[i * 2] & 0x7);
30
31 if (sc->type == ACPI_DSS_IOAPIC ||
32 sc->type == ACPI_DSS_MSI_CAPABLE_HPET)
33 printf(format: " id=%u", sc->enumeration_id);
34
35 ptr += sc->length;
36 printf(format: "\n");
37 }
38}
39
40static void handle_drhd(const struct acpi_dmar_drhd *drhd) {
41 bool include_all = (drhd->flags & ACPI_INCLUDE_PCI_ALL) != 0;
42 uint32_t reg_size = 1u << (drhd->size + 12);
43
44 dmar_info("DRHD base=0x%016llx size=0x%x seg=%u %s", drhd->address,
45 reg_size, drhd->segment, include_all ? "(INCLUDE_ALL)" : "");
46
47 struct vtd_regs *regs = mmio_map(phys: drhd->address, PAGE_SIZE);
48
49 uint64_t cap = regs->capabilities;
50 uint64_t ecap = regs->extended_capabilities;
51
52 dmar_info(" VT-d ver=%u.%u domains=%u", (regs->version >> 4) & 0xF,
53 regs->version & 0xF, vtd_cap_domain_count(cap));
54
55 dmar_info(" CAP: SAGAW=%02x MGAW=%u FRO=0x%x NFR=%u CM=%u RWBF=%u",
56 CAP_SUPPORTED_ADDR_WIDTHS(cap), CAP_MAX_ADDR_WIDTH(cap),
57 CAP_FAULT_RECORD_OFFSET(cap), CAP_NUM_FAULT_RECORDS(cap),
58 CAP_CACHING_MODE(cap), CAP_REQUIRES_WRITE_BUF_FLUSH(cap));
59
60 dmar_info(" ECAP: QI=%u IR=%u PT=%u SC=%u C=%u IRO=0x%x",
61 ECAP_QUEUED_INVALIDATION(ecap), ECAP_INTERRUPT_REMAPPING(ecap),
62 ECAP_PASS_THROUGH(ecap), ECAP_SNOOP_CONTROL(ecap),
63 ECAP_COHERENCY(ecap), ECAP_IOTLB_REGISTER_OFFSET(ecap));
64
65 if (!(CAP_SUPPORTED_ADDR_WIDTHS(cap) & ADDR_WIDTH_48BIT))
66 dmar_warn(" WARNING: 4-level page tables not supported by this unit!");
67
68 const uint8_t *scope_end = (const uint8_t *) drhd + drhd->hdr.length;
69 print_device_scopes(ptr: (void *) drhd->entries, end: scope_end);
70
71 struct iommu *unit =
72 vtd_unit_create(base_phys: drhd->address, segment: drhd->segment, size_field: drhd->size);
73 if (!unit) {
74 dmar_warn("failed to create VT-d unit for DRHD at 0x%llx",
75 drhd->address);
76 return;
77 }
78}
79
80static void handle_rmrr(const struct acpi_dmar_rmrr *rmrr) {
81 dmar_info("RMRR base=0x%016llx limit=0x%016llx seg=%u\n", rmrr->base,
82 rmrr->limit, rmrr->segment);
83
84 const uint8_t *scope_ptr = (const uint8_t *) rmrr + sizeof(*rmrr);
85 const uint8_t *scope_end = (const uint8_t *) rmrr + rmrr->hdr.length;
86 print_device_scopes(ptr: scope_ptr, end: scope_end);
87}
88
89static void handle_atsr(const struct acpi_dmar_atsr *atsr) {
90 dmar_info((atsr->flags & 1) ? "(ALL_PORTS)" : "");
91}
92
93bool dmar_init(void) {
94 struct uacpi_table dmar_table;
95 if (uacpi_table_find_by_signature(signature: "DMAR", out_table: &dmar_table) != UACPI_STATUS_OK) {
96 return false;
97 }
98
99 const struct acpi_dmar *dmar = dmar_table.ptr;
100 uint8_t haw = dmar->haw + 1;
101
102 dmar_info("DMAR VT-d DMAR table found");
103 dmar_info("DMAR Host address width: %u bits", haw);
104 dmar_info("DMAR Flags: 0x%02x%s", dmar->flags,
105 (dmar->flags & 1) ? " (INTR_REMAP supported)" : "");
106
107 const uint8_t *ptr = (const uint8_t *) (dmar + 1);
108 const uint8_t *end = (const uint8_t *) dmar + dmar->hdr.length;
109
110 size_t drhd_count = 0, rmrr_count = 0, atsr_count = 0, unknown_count = 0;
111
112 while (ptr < end) {
113 const struct acpi_dmar_entry_hdr *rs = (const void *) ptr;
114
115 /* guard: malformed table */
116 if (rs->length < 4 || ptr + rs->length > end) {
117 log_warn_global(
118 LOG_HANDLE(dmar),
119 "malformed remapping struct at offset %zu (len=%u), stopping",
120 (size_t) (ptr - (const uint8_t *) dmar), rs->length);
121 break;
122 }
123
124 switch (rs->type) {
125 case ACPI_DMAR_ENTRY_TYPE_DRHD:
126 drhd_count++;
127 handle_drhd(drhd: (const struct acpi_dmar_drhd *) rs);
128 break;
129
130 case ACPI_DMAR_ENTRY_TYPE_RMRR:
131 rmrr_count++;
132 handle_rmrr(rmrr: (const struct acpi_dmar_rmrr *) rs);
133 break;
134
135 case ACPI_DMAR_ENTRY_TYPE_ATSR:
136 atsr_count++;
137 handle_atsr(atsr: (const struct acpi_dmar_atsr *) rs);
138 break;
139
140 case ACPI_DMAR_ENTRY_TYPE_RHSA:
141 dmar_info("RHSA (NUMA affinity hint, skipped)");
142 break;
143
144 case ACPI_DMAR_ENTRY_TYPE_ANDD:
145 dmar_info("ANDD (ACPI namespace decl, skipped)");
146 break;
147
148 default:
149 unknown_count++;
150 dmar_info("??? unknown type=%u len=%u", rs->type, rs->length);
151 break;
152 }
153
154 ptr += rs->length;
155 }
156
157 dmar_info("DMAR Summary: %d DRHD, %d RMRR, %d ATSR, %d unknown", drhd_count,
158 rmrr_count, atsr_count, unknown_count);
159
160 if (drhd_count == 0) {
161 log_warn_global(LOG_HANDLE(dmar),
162 "no DRHD units found, nothing to initialize");
163 }
164
165 return true;
166}
167