]>
Commit | Line | Data |
---|---|---|
91fe8b79 SG |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Generic code used to generate ACPI tables | |
4 | * | |
5 | * Copyright 2019 Google LLC | |
6 | */ | |
7 | ||
8 | #include <common.h> | |
bfeb5d46 SG |
9 | #include <dm.h> |
10 | #include <cpu.h> | |
f7ae49fc | 11 | #include <log.h> |
29b35112 SG |
12 | #include <mapmem.h> |
13 | #include <tables_csum.h> | |
121a165c | 14 | #include <timestamp.h> |
93f7f827 | 15 | #include <version.h> |
86e1778d | 16 | #include <acpi/acpi_table.h> |
401d1c4f | 17 | #include <asm/global_data.h> |
86e1778d | 18 | #include <dm/acpi.h> |
bfeb5d46 | 19 | |
a3423b3f T |
20 | /* |
21 | * OEM_REVISION is 32-bit unsigned number. It should be increased only when | |
22 | * changing software version. Therefore it should not depend on build time. | |
23 | * U-Boot calculates it from U-Boot version and represent it in hexadecimal | |
24 | * notation. As U-Boot version is in form year.month set low 8 bits to 0x01 | |
25 | * to have valid date. So for U-Boot version 2021.04 OEM_REVISION is set to | |
26 | * value 0x20210401. | |
27 | */ | |
28 | #define OEM_REVISION ((((U_BOOT_VERSION_NUM / 1000) % 10) << 28) | \ | |
29 | (((U_BOOT_VERSION_NUM / 100) % 10) << 24) | \ | |
30 | (((U_BOOT_VERSION_NUM / 10) % 10) << 20) | \ | |
31 | ((U_BOOT_VERSION_NUM % 10) << 16) | \ | |
32 | (((U_BOOT_VERSION_NUM_PATCH / 10) % 10) << 12) | \ | |
33 | ((U_BOOT_VERSION_NUM_PATCH % 10) << 8) | \ | |
34 | 0x01) | |
35 | ||
bfeb5d46 SG |
36 | int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags) |
37 | { | |
38 | struct acpi_table_header *header = &dmar->header; | |
39 | struct cpu_info info; | |
40 | struct udevice *cpu; | |
41 | int ret; | |
42 | ||
43 | ret = uclass_first_device(UCLASS_CPU, &cpu); | |
44 | if (ret) | |
45 | return log_msg_ret("cpu", ret); | |
46 | ret = cpu_get_info(cpu, &info); | |
47 | if (ret) | |
48 | return log_msg_ret("info", ret); | |
49 | memset((void *)dmar, 0, sizeof(struct acpi_dmar)); | |
50 | ||
51 | /* Fill out header fields. */ | |
52 | acpi_fill_header(&dmar->header, "DMAR"); | |
53 | header->length = sizeof(struct acpi_dmar); | |
54 | header->revision = acpi_get_table_revision(ACPITAB_DMAR); | |
55 | ||
56 | dmar->host_address_width = info.address_width - 1; | |
57 | dmar->flags = flags; | |
58 | ||
59 | return 0; | |
60 | } | |
91fe8b79 SG |
61 | |
62 | int acpi_get_table_revision(enum acpi_tables table) | |
63 | { | |
64 | switch (table) { | |
65 | case ACPITAB_FADT: | |
66 | return ACPI_FADT_REV_ACPI_3_0; | |
67 | case ACPITAB_MADT: | |
68 | return ACPI_MADT_REV_ACPI_3_0; | |
69 | case ACPITAB_MCFG: | |
70 | return ACPI_MCFG_REV_ACPI_3_0; | |
71 | case ACPITAB_TCPA: | |
72 | /* This version and the rest are open-coded */ | |
73 | return 2; | |
74 | case ACPITAB_TPM2: | |
75 | return 4; | |
76 | case ACPITAB_SSDT: /* ACPI 3.0 upto 6.3: 2 */ | |
77 | return 2; | |
78 | case ACPITAB_SRAT: /* ACPI 2.0: 1, ACPI 3.0: 2, ACPI 4.0 to 6.3: 3 */ | |
79 | return 1; /* TODO Should probably be upgraded to 2 */ | |
80 | case ACPITAB_DMAR: | |
81 | return 1; | |
82 | case ACPITAB_SLIT: /* ACPI 2.0 upto 6.3: 1 */ | |
83 | return 1; | |
84 | case ACPITAB_SPMI: /* IMPI 2.0 */ | |
85 | return 5; | |
86 | case ACPITAB_HPET: /* Currently 1. Table added in ACPI 2.0 */ | |
87 | return 1; | |
88 | case ACPITAB_VFCT: /* ACPI 2.0/3.0/4.0: 1 */ | |
89 | return 1; | |
90 | case ACPITAB_IVRS: | |
91 | return IVRS_FORMAT_FIXED; | |
92 | case ACPITAB_DBG2: | |
93 | return 0; | |
94 | case ACPITAB_FACS: /* ACPI 2.0/3.0: 1, ACPI 4.0 to 6.3: 2 */ | |
95 | return 1; | |
96 | case ACPITAB_RSDT: /* ACPI 1.0 upto 6.3: 1 */ | |
97 | return 1; | |
98 | case ACPITAB_XSDT: /* ACPI 2.0 upto 6.3: 1 */ | |
99 | return 1; | |
100 | case ACPITAB_RSDP: /* ACPI 2.0 upto 6.3: 2 */ | |
101 | return 2; | |
102 | case ACPITAB_HEST: | |
103 | return 1; | |
104 | case ACPITAB_NHLT: | |
105 | return 5; | |
106 | case ACPITAB_BERT: | |
107 | return 1; | |
108 | case ACPITAB_SPCR: | |
109 | return 2; | |
110 | default: | |
111 | return -EINVAL; | |
112 | } | |
113 | } | |
93f7f827 SG |
114 | |
115 | void acpi_fill_header(struct acpi_table_header *header, char *signature) | |
116 | { | |
117 | memcpy(header->signature, signature, 4); | |
118 | memcpy(header->oem_id, OEM_ID, 6); | |
119 | memcpy(header->oem_table_id, OEM_TABLE_ID, 8); | |
a3423b3f | 120 | header->oem_revision = OEM_REVISION; |
93f7f827 SG |
121 | memcpy(header->aslc_id, ASLC_ID, 4); |
122 | } | |
86e1778d SG |
123 | |
124 | void acpi_align(struct acpi_ctx *ctx) | |
125 | { | |
126 | ctx->current = (void *)ALIGN((ulong)ctx->current, 16); | |
127 | } | |
128 | ||
129 | void acpi_align64(struct acpi_ctx *ctx) | |
130 | { | |
131 | ctx->current = (void *)ALIGN((ulong)ctx->current, 64); | |
132 | } | |
133 | ||
134 | void acpi_inc(struct acpi_ctx *ctx, uint amount) | |
135 | { | |
136 | ctx->current += amount; | |
137 | } | |
138 | ||
139 | void acpi_inc_align(struct acpi_ctx *ctx, uint amount) | |
140 | { | |
141 | ctx->current += amount; | |
142 | acpi_align(ctx); | |
143 | } | |
29b35112 SG |
144 | |
145 | /** | |
146 | * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length | |
147 | * and checksum. | |
148 | */ | |
149 | int acpi_add_table(struct acpi_ctx *ctx, void *table) | |
150 | { | |
151 | int i, entries_num; | |
152 | struct acpi_rsdt *rsdt; | |
153 | struct acpi_xsdt *xsdt; | |
154 | ||
155 | /* The RSDT is mandatory while the XSDT is not */ | |
156 | rsdt = ctx->rsdt; | |
157 | ||
158 | /* This should always be MAX_ACPI_TABLES */ | |
159 | entries_num = ARRAY_SIZE(rsdt->entry); | |
160 | ||
161 | for (i = 0; i < entries_num; i++) { | |
162 | if (rsdt->entry[i] == 0) | |
163 | break; | |
164 | } | |
165 | ||
166 | if (i >= entries_num) { | |
167 | log_err("ACPI: Error: too many tables\n"); | |
168 | return -E2BIG; | |
169 | } | |
170 | ||
171 | /* Add table to the RSDT */ | |
172 | rsdt->entry[i] = map_to_sysmem(table); | |
173 | ||
174 | /* Fix RSDT length or the kernel will assume invalid entries */ | |
175 | rsdt->header.length = sizeof(struct acpi_table_header) + | |
176 | (sizeof(u32) * (i + 1)); | |
177 | ||
178 | /* Re-calculate checksum */ | |
179 | rsdt->header.checksum = 0; | |
180 | rsdt->header.checksum = table_compute_checksum((u8 *)rsdt, | |
181 | rsdt->header.length); | |
182 | ||
183 | /* | |
184 | * And now the same thing for the XSDT. We use the same index as for | |
185 | * now we want the XSDT and RSDT to always be in sync in U-Boot | |
186 | */ | |
b38309b7 | 187 | xsdt = ctx->xsdt; |
29b35112 SG |
188 | |
189 | /* Add table to the XSDT */ | |
190 | xsdt->entry[i] = map_to_sysmem(table); | |
191 | ||
192 | /* Fix XSDT length */ | |
193 | xsdt->header.length = sizeof(struct acpi_table_header) + | |
194 | (sizeof(u64) * (i + 1)); | |
195 | ||
196 | /* Re-calculate checksum */ | |
197 | xsdt->header.checksum = 0; | |
198 | xsdt->header.checksum = table_compute_checksum((u8 *)xsdt, | |
199 | xsdt->header.length); | |
200 | ||
201 | return 0; | |
202 | } | |
7e586f69 | 203 | |
d2cb7a22 SG |
204 | void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, |
205 | struct acpi_xsdt *xsdt) | |
7e586f69 SG |
206 | { |
207 | memset(rsdp, 0, sizeof(struct acpi_rsdp)); | |
208 | ||
209 | memcpy(rsdp->signature, RSDP_SIG, 8); | |
210 | memcpy(rsdp->oem_id, OEM_ID, 6); | |
211 | ||
212 | rsdp->length = sizeof(struct acpi_rsdp); | |
213 | rsdp->rsdt_address = map_to_sysmem(rsdt); | |
214 | ||
215 | rsdp->xsdt_address = map_to_sysmem(xsdt); | |
216 | rsdp->revision = ACPI_RSDP_REV_ACPI_2_0; | |
217 | ||
218 | /* Calculate checksums */ | |
219 | rsdp->checksum = table_compute_checksum(rsdp, 20); | |
220 | rsdp->ext_checksum = table_compute_checksum(rsdp, | |
221 | sizeof(struct acpi_rsdp)); | |
222 | } | |
223 | ||
224 | static void acpi_write_rsdt(struct acpi_rsdt *rsdt) | |
225 | { | |
226 | struct acpi_table_header *header = &rsdt->header; | |
227 | ||
228 | /* Fill out header fields */ | |
229 | acpi_fill_header(header, "RSDT"); | |
230 | header->length = sizeof(struct acpi_rsdt); | |
231 | header->revision = 1; | |
232 | ||
233 | /* Entries are filled in later, we come with an empty set */ | |
234 | ||
235 | /* Fix checksum */ | |
236 | header->checksum = table_compute_checksum(rsdt, | |
237 | sizeof(struct acpi_rsdt)); | |
238 | } | |
239 | ||
240 | static void acpi_write_xsdt(struct acpi_xsdt *xsdt) | |
241 | { | |
242 | struct acpi_table_header *header = &xsdt->header; | |
243 | ||
244 | /* Fill out header fields */ | |
245 | acpi_fill_header(header, "XSDT"); | |
246 | header->length = sizeof(struct acpi_xsdt); | |
247 | header->revision = 1; | |
248 | ||
249 | /* Entries are filled in later, we come with an empty set */ | |
250 | ||
251 | /* Fix checksum */ | |
252 | header->checksum = table_compute_checksum(xsdt, | |
253 | sizeof(struct acpi_xsdt)); | |
254 | } | |
255 | ||
256 | void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) | |
257 | { | |
61cc9339 | 258 | ctx->base = start; |
7e586f69 SG |
259 | ctx->current = start; |
260 | ||
261 | /* Align ACPI tables to 16 byte */ | |
262 | acpi_align(ctx); | |
0b885bcf | 263 | gd->arch.acpi_start = map_to_sysmem(ctx->current); |
7e586f69 SG |
264 | |
265 | /* We need at least an RSDP and an RSDT Table */ | |
266 | ctx->rsdp = ctx->current; | |
267 | acpi_inc_align(ctx, sizeof(struct acpi_rsdp)); | |
268 | ctx->rsdt = ctx->current; | |
269 | acpi_inc_align(ctx, sizeof(struct acpi_rsdt)); | |
b38309b7 | 270 | ctx->xsdt = ctx->current; |
7e586f69 SG |
271 | acpi_inc_align(ctx, sizeof(struct acpi_xsdt)); |
272 | ||
273 | /* clear all table memory */ | |
274 | memset((void *)start, '\0', ctx->current - start); | |
275 | ||
b38309b7 | 276 | acpi_write_rsdp(ctx->rsdp, ctx->rsdt, ctx->xsdt); |
7e586f69 | 277 | acpi_write_rsdt(ctx->rsdt); |
b38309b7 | 278 | acpi_write_xsdt(ctx->xsdt); |
7e586f69 SG |
279 | /* |
280 | * Per ACPI spec, the FACS table address must be aligned to a 64 byte | |
281 | * boundary (Windows checks this, but Linux does not). | |
282 | */ | |
283 | acpi_align64(ctx); | |
284 | } | |
f37979e7 SG |
285 | |
286 | void acpi_create_dbg2(struct acpi_dbg2_header *dbg2, | |
287 | int port_type, int port_subtype, | |
288 | struct acpi_gen_regaddr *address, u32 address_size, | |
289 | const char *device_path) | |
290 | { | |
291 | uintptr_t current; | |
292 | struct acpi_dbg2_device *device; | |
293 | u32 *dbg2_addr_size; | |
294 | struct acpi_table_header *header; | |
295 | size_t path_len; | |
296 | const char *path; | |
297 | char *namespace; | |
298 | ||
299 | /* Fill out header fields. */ | |
300 | current = (uintptr_t)dbg2; | |
301 | memset(dbg2, '\0', sizeof(struct acpi_dbg2_header)); | |
302 | header = &dbg2->header; | |
303 | ||
304 | header->revision = acpi_get_table_revision(ACPITAB_DBG2); | |
305 | acpi_fill_header(header, "DBG2"); | |
306 | header->aslc_revision = ASL_REVISION; | |
307 | ||
308 | /* One debug device defined */ | |
309 | dbg2->devices_offset = sizeof(struct acpi_dbg2_header); | |
310 | dbg2->devices_count = 1; | |
311 | current += sizeof(struct acpi_dbg2_header); | |
312 | ||
313 | /* Device comes after the header */ | |
314 | device = (struct acpi_dbg2_device *)current; | |
315 | memset(device, 0, sizeof(struct acpi_dbg2_device)); | |
316 | current += sizeof(struct acpi_dbg2_device); | |
317 | ||
318 | device->revision = 0; | |
319 | device->address_count = 1; | |
320 | device->port_type = port_type; | |
321 | device->port_subtype = port_subtype; | |
322 | ||
323 | /* Base Address comes after device structure */ | |
324 | memcpy((void *)current, address, sizeof(struct acpi_gen_regaddr)); | |
325 | device->base_address_offset = current - (uintptr_t)device; | |
326 | current += sizeof(struct acpi_gen_regaddr); | |
327 | ||
328 | /* Address Size comes after address structure */ | |
329 | dbg2_addr_size = (uint32_t *)current; | |
330 | device->address_size_offset = current - (uintptr_t)device; | |
331 | *dbg2_addr_size = address_size; | |
332 | current += sizeof(uint32_t); | |
333 | ||
334 | /* Namespace string comes last, use '.' if not provided */ | |
335 | path = device_path ? : "."; | |
336 | /* Namespace string length includes NULL terminator */ | |
337 | path_len = strlen(path) + 1; | |
338 | namespace = (char *)current; | |
339 | device->namespace_string_length = path_len; | |
340 | device->namespace_string_offset = current - (uintptr_t)device; | |
341 | strncpy(namespace, path, path_len); | |
342 | current += path_len; | |
343 | ||
344 | /* Update structure lengths and checksum */ | |
345 | device->length = current - (uintptr_t)device; | |
346 | header->length = current - (uintptr_t)dbg2; | |
347 | header->checksum = table_compute_checksum(dbg2, header->length); | |
348 | } |