blob: 4ad1f56e961dff568379f68053fe3702445c7396 [file] [log] [blame]
Simon Glassb2672ea2020-04-08 16:57:38 -06001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Generic code used to generate ACPI tables
4 *
5 * Copyright 2019 Google LLC
6 */
7
Patrick Rudolph158efd62024-10-23 15:19:57 +02008#include <bloblist.h>
Simon Glasse9629892020-04-08 16:57:39 -06009#include <cpu.h>
Patrick Rudolph158efd62024-10-23 15:19:57 +020010#include <dm.h>
11#include <efi_api.h>
12#include <efi_loader.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Simon Glass575a5472020-04-26 09:19:50 -060014#include <mapmem.h>
15#include <tables_csum.h>
Maximilian Bruned7fa54b2024-10-23 15:19:44 +020016#include <serial.h>
Simon Glass90b01272023-04-29 19:21:46 -060017#include <version_string.h>
Simon Glass0e113842020-04-26 09:19:47 -060018#include <acpi/acpi_table.h>
Maximilian Bruned7fa54b2024-10-23 15:19:44 +020019#include <acpi/acpi_device.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060020#include <asm/global_data.h>
Simon Glass0e113842020-04-26 09:19:47 -060021#include <dm/acpi.h>
Patrick Rudolph158efd62024-10-23 15:19:57 +020022#include <linux/sizes.h>
23#include <linux/log2.h>
24
25enum {
26 TABLE_SIZE = SZ_64K,
27};
28
29DECLARE_GLOBAL_DATA_PTR;
Simon Glasse9629892020-04-08 16:57:39 -060030
Pali Rohárf0de20e2021-07-10 13:10:01 +020031/*
32 * OEM_REVISION is 32-bit unsigned number. It should be increased only when
33 * changing software version. Therefore it should not depend on build time.
34 * U-Boot calculates it from U-Boot version and represent it in hexadecimal
35 * notation. As U-Boot version is in form year.month set low 8 bits to 0x01
36 * to have valid date. So for U-Boot version 2021.04 OEM_REVISION is set to
37 * value 0x20210401.
38 */
Simon Glass90b01272023-04-29 19:21:46 -060039#define OEM_REVISION ((((version_num / 1000) % 10) << 28) | \
40 (((version_num / 100) % 10) << 24) | \
41 (((version_num / 10) % 10) << 20) | \
42 ((version_num % 10) << 16) | \
43 (((version_num_patch / 10) % 10) << 12) | \
44 ((version_num_patch % 10) << 8) | \
Pali Rohárf0de20e2021-07-10 13:10:01 +020045 0x01)
46
Simon Glasse9629892020-04-08 16:57:39 -060047int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags)
48{
49 struct acpi_table_header *header = &dmar->header;
50 struct cpu_info info;
51 struct udevice *cpu;
52 int ret;
53
Michal Suchanekac12a2f2022-10-12 21:57:59 +020054 ret = uclass_first_device_err(UCLASS_CPU, &cpu);
Simon Glasse9629892020-04-08 16:57:39 -060055 if (ret)
56 return log_msg_ret("cpu", ret);
57 ret = cpu_get_info(cpu, &info);
58 if (ret)
59 return log_msg_ret("info", ret);
60 memset((void *)dmar, 0, sizeof(struct acpi_dmar));
61
62 /* Fill out header fields. */
63 acpi_fill_header(&dmar->header, "DMAR");
64 header->length = sizeof(struct acpi_dmar);
65 header->revision = acpi_get_table_revision(ACPITAB_DMAR);
66
67 dmar->host_address_width = info.address_width - 1;
68 dmar->flags = flags;
69
70 return 0;
71}
Simon Glassb2672ea2020-04-08 16:57:38 -060072
73int acpi_get_table_revision(enum acpi_tables table)
74{
75 switch (table) {
76 case ACPITAB_FADT:
Patrick Rudolphf317fce2024-10-23 15:19:52 +020077 return ACPI_FADT_REV_ACPI_6_0;
Simon Glassb2672ea2020-04-08 16:57:38 -060078 case ACPITAB_MADT:
Patrick Rudolphf317fce2024-10-23 15:19:52 +020079 return ACPI_MADT_REV_ACPI_6_2;
Simon Glassb2672ea2020-04-08 16:57:38 -060080 case ACPITAB_MCFG:
81 return ACPI_MCFG_REV_ACPI_3_0;
82 case ACPITAB_TCPA:
83 /* This version and the rest are open-coded */
84 return 2;
85 case ACPITAB_TPM2:
86 return 4;
87 case ACPITAB_SSDT: /* ACPI 3.0 upto 6.3: 2 */
88 return 2;
89 case ACPITAB_SRAT: /* ACPI 2.0: 1, ACPI 3.0: 2, ACPI 4.0 to 6.3: 3 */
90 return 1; /* TODO Should probably be upgraded to 2 */
91 case ACPITAB_DMAR:
92 return 1;
93 case ACPITAB_SLIT: /* ACPI 2.0 upto 6.3: 1 */
94 return 1;
95 case ACPITAB_SPMI: /* IMPI 2.0 */
96 return 5;
97 case ACPITAB_HPET: /* Currently 1. Table added in ACPI 2.0 */
98 return 1;
99 case ACPITAB_VFCT: /* ACPI 2.0/3.0/4.0: 1 */
100 return 1;
101 case ACPITAB_IVRS:
102 return IVRS_FORMAT_FIXED;
103 case ACPITAB_DBG2:
104 return 0;
105 case ACPITAB_FACS: /* ACPI 2.0/3.0: 1, ACPI 4.0 to 6.3: 2 */
106 return 1;
107 case ACPITAB_RSDT: /* ACPI 1.0 upto 6.3: 1 */
108 return 1;
109 case ACPITAB_XSDT: /* ACPI 2.0 upto 6.3: 1 */
110 return 1;
111 case ACPITAB_RSDP: /* ACPI 2.0 upto 6.3: 2 */
112 return 2;
113 case ACPITAB_HEST:
114 return 1;
115 case ACPITAB_NHLT:
116 return 5;
117 case ACPITAB_BERT:
118 return 1;
119 case ACPITAB_SPCR:
120 return 2;
Patrick Rudolph4d3cf1a2024-10-23 15:19:53 +0200121 case ACPITAB_PPTT: /* ACPI 6.2: 1 */
122 return 1;
123 case ACPITAB_GTDT: /* ACPI 6.2: 2, ACPI 6.3: 3 */
124 return 2;
Simon Glassb2672ea2020-04-08 16:57:38 -0600125 default:
126 return -EINVAL;
127 }
128}
Simon Glass17968c32020-04-26 09:19:46 -0600129
130void acpi_fill_header(struct acpi_table_header *header, char *signature)
131{
132 memcpy(header->signature, signature, 4);
133 memcpy(header->oem_id, OEM_ID, 6);
134 memcpy(header->oem_table_id, OEM_TABLE_ID, 8);
Pali Rohárf0de20e2021-07-10 13:10:01 +0200135 header->oem_revision = OEM_REVISION;
Heinrich Schuchardt10de8a82024-01-21 12:52:48 +0100136 memcpy(header->creator_id, ASLC_ID, 4);
Heinrich Schuchardt9838d342024-04-18 05:11:13 +0200137 header->creator_revision = ASL_REVISION;
Simon Glass17968c32020-04-26 09:19:46 -0600138}
Simon Glass0e113842020-04-26 09:19:47 -0600139
140void acpi_align(struct acpi_ctx *ctx)
141{
142 ctx->current = (void *)ALIGN((ulong)ctx->current, 16);
143}
144
145void acpi_align64(struct acpi_ctx *ctx)
146{
147 ctx->current = (void *)ALIGN((ulong)ctx->current, 64);
148}
149
150void acpi_inc(struct acpi_ctx *ctx, uint amount)
151{
152 ctx->current += amount;
153}
154
155void acpi_inc_align(struct acpi_ctx *ctx, uint amount)
156{
157 ctx->current += amount;
158 acpi_align(ctx);
159}
Simon Glass575a5472020-04-26 09:19:50 -0600160
161/**
162 * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length
163 * and checksum.
164 */
165int acpi_add_table(struct acpi_ctx *ctx, void *table)
166{
167 int i, entries_num;
168 struct acpi_rsdt *rsdt;
169 struct acpi_xsdt *xsdt;
170
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200171 /* On legacy x86 platforms the RSDT is mandatory while the XSDT is not.
172 * On other platforms there might be no memory below 4GiB, thus RSDT is NULL.
173 */
174 if (ctx->rsdt) {
175 rsdt = ctx->rsdt;
Simon Glass575a5472020-04-26 09:19:50 -0600176
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200177 /* This should always be MAX_ACPI_TABLES */
178 entries_num = ARRAY_SIZE(rsdt->entry);
Simon Glass575a5472020-04-26 09:19:50 -0600179
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200180 for (i = 0; i < entries_num; i++) {
181 if (rsdt->entry[i] == 0)
182 break;
183 }
Simon Glass575a5472020-04-26 09:19:50 -0600184
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200185 if (i >= entries_num) {
186 log_err("ACPI: Error: too many tables\n");
187 return -E2BIG;
188 }
189
190 /* Add table to the RSDT */
191 rsdt->entry[i] = nomap_to_sysmem(table);
192
193 /* Fix RSDT length or the kernel will assume invalid entries */
194 rsdt->header.length = sizeof(struct acpi_table_header) +
195 (sizeof(u32) * (i + 1));
196
197 /* Re-calculate checksum */
198 rsdt->header.checksum = 0;
199 rsdt->header.checksum = table_compute_checksum((u8 *)rsdt,
200 rsdt->header.length);
Simon Glass575a5472020-04-26 09:19:50 -0600201 }
202
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200203 if (ctx->xsdt) {
204 /*
205 * And now the same thing for the XSDT. We use the same index as for
206 * now we want the XSDT and RSDT to always be in sync in U-Boot
207 */
208 xsdt = ctx->xsdt;
Simon Glass575a5472020-04-26 09:19:50 -0600209
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200210 /* This should always be MAX_ACPI_TABLES */
211 entries_num = ARRAY_SIZE(xsdt->entry);
Simon Glass575a5472020-04-26 09:19:50 -0600212
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200213 for (i = 0; i < entries_num; i++) {
214 if (xsdt->entry[i] == 0)
215 break;
216 }
Simon Glass575a5472020-04-26 09:19:50 -0600217
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200218 if (i >= entries_num) {
219 log_err("ACPI: Error: too many tables\n");
220 return -E2BIG;
221 }
Simon Glass575a5472020-04-26 09:19:50 -0600222
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200223 /* Add table to the XSDT */
224 xsdt->entry[i] = nomap_to_sysmem(table);
Simon Glass575a5472020-04-26 09:19:50 -0600225
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200226 /* Fix XSDT length */
227 xsdt->header.length = sizeof(struct acpi_table_header) +
228 (sizeof(u64) * (i + 1));
Simon Glass575a5472020-04-26 09:19:50 -0600229
Patrick Rudolphd69eb292024-10-23 15:19:56 +0200230 /* Re-calculate checksum */
231 xsdt->header.checksum = 0;
232 xsdt->header.checksum = table_compute_checksum((u8 *)xsdt,
233 xsdt->header.length);
234 }
Simon Glass575a5472020-04-26 09:19:50 -0600235
236 return 0;
237}
Simon Glass9c442a62020-04-26 09:19:51 -0600238
Maximilian Brune8dc45122024-10-23 15:19:45 +0200239int acpi_write_fadt(struct acpi_ctx *ctx, const struct acpi_writer *entry)
240{
241 struct acpi_table_header *header;
242 struct acpi_fadt *fadt;
243
244 fadt = ctx->current;
245 header = &fadt->header;
246
247 memset((void *)fadt, '\0', sizeof(struct acpi_fadt));
248
249 acpi_fill_header(header, "FACP");
250 header->length = sizeof(struct acpi_fadt);
251 header->revision = acpi_get_table_revision(ACPITAB_FADT);
252 memcpy(header->oem_id, OEM_ID, 6);
253 memcpy(header->oem_table_id, OEM_TABLE_ID, 8);
254 memcpy(header->creator_id, ASLC_ID, 4);
255 header->creator_revision = 1;
Patrick Rudolphf317fce2024-10-23 15:19:52 +0200256 fadt->minor_revision = 2;
Maximilian Brune8dc45122024-10-23 15:19:45 +0200257
258 fadt->x_firmware_ctrl = map_to_sysmem(ctx->facs);
259 fadt->x_dsdt = map_to_sysmem(ctx->dsdt);
260
261 if (fadt->x_firmware_ctrl < 0x100000000ULL)
262 fadt->firmware_ctrl = fadt->x_firmware_ctrl;
263
264 if (fadt->x_dsdt < 0x100000000ULL)
265 fadt->dsdt = fadt->x_dsdt;
266
267 fadt->preferred_pm_profile = ACPI_PM_UNSPECIFIED;
268
269 acpi_fill_fadt(fadt);
270
271 header->checksum = table_compute_checksum(fadt, header->length);
272
273 return acpi_add_fadt(ctx, fadt);
274}
275
Heinrich Schuchardt08839712024-12-20 01:37:59 +0100276#ifndef CONFIG_QFW_ACPI
Maximilian Brune8dc45122024-10-23 15:19:45 +0200277ACPI_WRITER(5fadt, "FADT", acpi_write_fadt, 0);
Heinrich Schuchardt08839712024-12-20 01:37:59 +0100278#endif
Maximilian Brune8dc45122024-10-23 15:19:45 +0200279
Patrick Rudolph97b4c8a2024-10-23 15:19:46 +0200280int acpi_write_madt(struct acpi_ctx *ctx, const struct acpi_writer *entry)
281{
282 struct acpi_table_header *header;
283 struct acpi_madt *madt;
284 void *current;
285
286 madt = ctx->current;
287
288 memset(madt, '\0', sizeof(struct acpi_madt));
289 header = &madt->header;
290
291 /* Fill out header fields */
292 acpi_fill_header(header, "APIC");
293 header->length = sizeof(struct acpi_madt);
Patrick Rudolphf317fce2024-10-23 15:19:52 +0200294 header->revision = acpi_get_table_revision(ACPITAB_MADT);
Patrick Rudolph97b4c8a2024-10-23 15:19:46 +0200295
296 acpi_inc(ctx, sizeof(struct acpi_madt));
Patrick Rudolphbff7c572024-10-23 15:19:51 +0200297 /* TODO: Get rid of acpi_fill_madt and use driver model */
Patrick Rudolph97b4c8a2024-10-23 15:19:46 +0200298 current = acpi_fill_madt(madt, ctx);
299
300 /* (Re)calculate length and checksum */
301 header->length = (uintptr_t)current - (uintptr_t)madt;
Patrick Rudolph2f6f8d92024-10-23 15:20:13 +0200302
303 if (IS_ENABLED(CONFIG_ACPI_PARKING_PROTOCOL))
304 acpi_write_park(madt);
305
Patrick Rudolph97b4c8a2024-10-23 15:19:46 +0200306 header->checksum = table_compute_checksum((void *)madt, header->length);
307 acpi_add_table(ctx, madt);
308 ctx->current = (void *)madt + madt->header.length;
309
310 return 0;
311}
312
Heinrich Schuchardt08839712024-12-20 01:37:59 +0100313#ifndef CONFIG_QFW_ACPI
Patrick Rudolph97b4c8a2024-10-23 15:19:46 +0200314ACPI_WRITER(5madt, "MADT", acpi_write_madt, 0);
Heinrich Schuchardt08839712024-12-20 01:37:59 +0100315#endif
Patrick Rudolph97b4c8a2024-10-23 15:19:46 +0200316
Simon Glass95971892020-09-22 12:45:10 -0600317void acpi_create_dbg2(struct acpi_dbg2_header *dbg2,
318 int port_type, int port_subtype,
319 struct acpi_gen_regaddr *address, u32 address_size,
320 const char *device_path)
321{
322 uintptr_t current;
323 struct acpi_dbg2_device *device;
324 u32 *dbg2_addr_size;
325 struct acpi_table_header *header;
326 size_t path_len;
327 const char *path;
328 char *namespace;
329
330 /* Fill out header fields. */
331 current = (uintptr_t)dbg2;
332 memset(dbg2, '\0', sizeof(struct acpi_dbg2_header));
333 header = &dbg2->header;
334
335 header->revision = acpi_get_table_revision(ACPITAB_DBG2);
336 acpi_fill_header(header, "DBG2");
Simon Glass95971892020-09-22 12:45:10 -0600337
338 /* One debug device defined */
339 dbg2->devices_offset = sizeof(struct acpi_dbg2_header);
340 dbg2->devices_count = 1;
341 current += sizeof(struct acpi_dbg2_header);
342
343 /* Device comes after the header */
344 device = (struct acpi_dbg2_device *)current;
345 memset(device, 0, sizeof(struct acpi_dbg2_device));
346 current += sizeof(struct acpi_dbg2_device);
347
348 device->revision = 0;
349 device->address_count = 1;
350 device->port_type = port_type;
351 device->port_subtype = port_subtype;
352
353 /* Base Address comes after device structure */
354 memcpy((void *)current, address, sizeof(struct acpi_gen_regaddr));
355 device->base_address_offset = current - (uintptr_t)device;
356 current += sizeof(struct acpi_gen_regaddr);
357
358 /* Address Size comes after address structure */
359 dbg2_addr_size = (uint32_t *)current;
360 device->address_size_offset = current - (uintptr_t)device;
361 *dbg2_addr_size = address_size;
362 current += sizeof(uint32_t);
363
364 /* Namespace string comes last, use '.' if not provided */
365 path = device_path ? : ".";
366 /* Namespace string length includes NULL terminator */
367 path_len = strlen(path) + 1;
368 namespace = (char *)current;
369 device->namespace_string_length = path_len;
370 device->namespace_string_offset = current - (uintptr_t)device;
371 strncpy(namespace, path, path_len);
372 current += path_len;
373
374 /* Update structure lengths and checksum */
375 device->length = current - (uintptr_t)device;
376 header->length = current - (uintptr_t)dbg2;
377 header->checksum = table_compute_checksum(dbg2, header->length);
378}
Maximilian Bruned7fa54b2024-10-23 15:19:44 +0200379
380int acpi_write_dbg2_pci_uart(struct acpi_ctx *ctx, struct udevice *dev,
381 uint access_size)
382{
383 struct acpi_dbg2_header *dbg2 = ctx->current;
384 char path[ACPI_PATH_MAX];
385 struct acpi_gen_regaddr address;
386 u64 addr;
387 int ret;
388
389 if (!device_active(dev)) {
390 log_info("Device not enabled\n");
391 return -EACCES;
392 }
393 /*
394 * PCI devices don't remember their resource allocation information in
395 * U-Boot at present. We assume that MMIO is used for the UART and that
396 * the address space is 32 bytes: ns16550 uses 8 registers of up to
397 * 32-bits each. This is only for debugging so it is not a big deal.
398 */
399 addr = dm_pci_read_bar32(dev, 0);
400 log_debug("UART addr %lx\n", (ulong)addr);
401
402 ret = acpi_device_path(dev, path, sizeof(path));
403 if (ret)
404 return log_msg_ret("path", ret);
405
406 memset(&address, '\0', sizeof(address));
407 address.space_id = ACPI_ADDRESS_SPACE_MEMORY;
408 address.addrl = (uint32_t)addr;
409 address.addrh = (uint32_t)((addr >> 32) & 0xffffffff);
410 address.access_size = access_size;
411
412 ret = acpi_device_path(dev, path, sizeof(path));
413 if (ret)
414 return log_msg_ret("path", ret);
415 acpi_create_dbg2(dbg2, ACPI_DBG2_SERIAL_PORT,
416 ACPI_DBG2_16550_COMPATIBLE, &address, 0x1000, path);
417
418 acpi_inc_align(ctx, dbg2->header.length);
419 acpi_add_table(ctx, dbg2);
420
421 return 0;
422}
423
424static int acpi_write_spcr(struct acpi_ctx *ctx, const struct acpi_writer *entry)
425{
426 struct serial_device_info serial_info = {0};
Patrick Rudolph1bf4cb22024-10-30 14:11:46 +0100427 u64 serial_address, serial_offset;
Maximilian Bruned7fa54b2024-10-23 15:19:44 +0200428 struct acpi_table_header *header;
429 struct acpi_spcr *spcr;
430 struct udevice *dev;
431 uint serial_config;
432 uint serial_width;
433 int access_size;
434 int space_id;
435 int ret = -ENODEV;
436
437 spcr = ctx->current;
438 header = &spcr->header;
439
440 memset(spcr, '\0', sizeof(struct acpi_spcr));
441
442 /* Fill out header fields */
443 acpi_fill_header(header, "SPCR");
444 header->length = sizeof(struct acpi_spcr);
445 header->revision = 2;
446
447 /* Read the device once, here. It is reused below */
448 dev = gd->cur_serial_dev;
449 if (dev)
450 ret = serial_getinfo(dev, &serial_info);
451 if (ret)
452 serial_info.type = SERIAL_CHIP_UNKNOWN;
453
454 /* Encode chip type */
455 switch (serial_info.type) {
456 case SERIAL_CHIP_16550_COMPATIBLE:
457 spcr->interface_type = ACPI_DBG2_16550_COMPATIBLE;
458 break;
459 case SERIAL_CHIP_PL01X:
460 spcr->interface_type = ACPI_DBG2_ARM_PL011;
461 break;
462 case SERIAL_CHIP_UNKNOWN:
463 default:
464 spcr->interface_type = ACPI_DBG2_UNKNOWN;
465 break;
466 }
467
468 /* Encode address space */
469 switch (serial_info.addr_space) {
470 case SERIAL_ADDRESS_SPACE_MEMORY:
471 space_id = ACPI_ADDRESS_SPACE_MEMORY;
472 break;
473 case SERIAL_ADDRESS_SPACE_IO:
474 default:
475 space_id = ACPI_ADDRESS_SPACE_IO;
476 break;
477 }
478
479 serial_width = serial_info.reg_width * 8;
Patrick Rudolph1bf4cb22024-10-30 14:11:46 +0100480 serial_offset = ((u64)serial_info.reg_offset) << serial_info.reg_shift;
Maximilian Bruned7fa54b2024-10-23 15:19:44 +0200481 serial_address = serial_info.addr + serial_offset;
482
483 /* Encode register access size */
484 switch (serial_info.reg_shift) {
485 case 0:
486 access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
487 break;
488 case 1:
489 access_size = ACPI_ACCESS_SIZE_WORD_ACCESS;
490 break;
491 case 2:
492 access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
493 break;
494 case 3:
495 access_size = ACPI_ACCESS_SIZE_QWORD_ACCESS;
496 break;
497 default:
498 access_size = ACPI_ACCESS_SIZE_UNDEFINED;
499 break;
500 }
501
Patrick Rudolph1bf4cb22024-10-30 14:11:46 +0100502 debug("UART type %u @ %llx\n", spcr->interface_type, serial_address);
Maximilian Bruned7fa54b2024-10-23 15:19:44 +0200503
504 /* Fill GAS */
505 spcr->serial_port.space_id = space_id;
506 spcr->serial_port.bit_width = serial_width;
507 spcr->serial_port.bit_offset = 0;
508 spcr->serial_port.access_size = access_size;
509 spcr->serial_port.addrl = lower_32_bits(serial_address);
510 spcr->serial_port.addrh = upper_32_bits(serial_address);
511
512 /* Encode baud rate */
513 switch (serial_info.baudrate) {
514 case 9600:
515 spcr->baud_rate = 3;
516 break;
517 case 19200:
518 spcr->baud_rate = 4;
519 break;
520 case 57600:
521 spcr->baud_rate = 6;
522 break;
523 case 115200:
524 spcr->baud_rate = 7;
525 break;
526 default:
527 spcr->baud_rate = 0;
528 break;
529 }
530
531 serial_config = SERIAL_DEFAULT_CONFIG;
532 if (dev)
533 ret = serial_getconfig(dev, &serial_config);
534
535 spcr->parity = SERIAL_GET_PARITY(serial_config);
536 spcr->stop_bits = SERIAL_GET_STOP(serial_config);
537
538 /* No PCI devices for now */
539 spcr->pci_device_id = 0xffff;
540 spcr->pci_vendor_id = 0xffff;
541
542 /*
543 * SPCR has no clue if the UART base clock speed is different
544 * to the default one. However, the SPCR 1.04 defines baud rate
545 * 0 as a preconfigured state of UART and OS is supposed not
546 * to touch the configuration of the serial device.
547 */
548 if (serial_info.clock != SERIAL_DEFAULT_CLOCK)
549 spcr->baud_rate = 0;
550
551 /* Fix checksum */
552 header->checksum = table_compute_checksum((void *)spcr, header->length);
553
554 acpi_add_table(ctx, spcr);
555 acpi_inc(ctx, spcr->header.length);
556
557 return 0;
558}
559
560ACPI_WRITER(5spcr, "SPCR", acpi_write_spcr, 0);
Patrick Rudolph1669ce72024-10-23 15:19:54 +0200561
562__weak int acpi_fill_iort(struct acpi_ctx *ctx)
563{
564 return 0;
565}
566
567int acpi_iort_add_its_group(struct acpi_ctx *ctx,
568 const u32 its_count,
569 const u32 *identifiers)
570{
571 struct acpi_iort_node *node;
572 struct acpi_iort_its_group *group;
573 int offset;
574
575 offset = ctx->current - ctx->tab_start;
576
577 node = ctx->current;
578 memset(node, '\0', sizeof(struct acpi_iort_node));
579
580 node->type = ACPI_IORT_NODE_ITS_GROUP;
581 node->revision = 1;
582
583 node->length = sizeof(struct acpi_iort_node);
584 node->length += sizeof(struct acpi_iort_its_group);
585 node->length += sizeof(u32) * its_count;
586
587 group = (struct acpi_iort_its_group *)node->node_data;
588 group->its_count = its_count;
589 memcpy(&group->identifiers, identifiers, sizeof(u32) * its_count);
590
591 ctx->current += node->length;
592
593 return offset;
594}
595
596int acpi_iort_add_named_component(struct acpi_ctx *ctx,
597 const u32 node_flags,
598 const u64 memory_properties,
599 const u8 memory_address_limit,
600 const char *device_name)
601{
602 struct acpi_iort_node *node;
603 struct acpi_iort_named_component *comp;
604 int offset;
605
606 offset = ctx->current - ctx->tab_start;
607
608 node = ctx->current;
609 memset(node, '\0', sizeof(struct acpi_iort_node));
610
611 node->type = ACPI_IORT_NODE_NAMED_COMPONENT;
612 node->revision = 4;
613 node->length = sizeof(struct acpi_iort_node);
614 node->length += sizeof(struct acpi_iort_named_component);
615 node->length += strlen(device_name) + 1;
616
617 comp = (struct acpi_iort_named_component *)node->node_data;
Patrick Rudolph0eafaf22025-03-16 09:32:54 +0100618 memset(comp, '\0', sizeof(struct acpi_iort_named_component));
Patrick Rudolph1669ce72024-10-23 15:19:54 +0200619
620 comp->node_flags = node_flags;
621 comp->memory_properties = memory_properties;
622 comp->memory_address_limit = memory_address_limit;
623 memcpy(comp->device_name, device_name, strlen(device_name) + 1);
624
625 ctx->current += node->length;
626
627 return offset;
628}
629
630int acpi_iort_add_rc(struct acpi_ctx *ctx,
631 const u64 mem_access_properties,
632 const u32 ats_attributes,
633 const u32 pci_segment_number,
634 const u8 memory_address_size_limit,
635 const int num_mappings,
636 const struct acpi_iort_id_mapping *map)
637{
638 struct acpi_iort_id_mapping *mapping;
Patrick Rudolph9b6b3882025-03-16 09:32:53 +0100639 struct acpi_iort_node *output_node;
Patrick Rudolph1669ce72024-10-23 15:19:54 +0200640 struct acpi_iort_node *node;
641 struct acpi_iort_rc *rc;
642 int offset;
643
644 offset = ctx->current - ctx->tab_start;
645
646 node = ctx->current;
647 memset(node, '\0', sizeof(struct acpi_iort_node));
648
649 node->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
650 node->revision = 2;
Patrick Rudolphfcfbecb2025-03-16 09:32:52 +0100651 node->mapping_count = num_mappings;
Patrick Rudolph15b48eb2025-03-16 09:32:55 +0100652 if (num_mappings)
653 node->mapping_offset = sizeof(struct acpi_iort_node) +
654 sizeof(struct acpi_iort_rc);
Patrick Rudolph1669ce72024-10-23 15:19:54 +0200655
656 node->length = sizeof(struct acpi_iort_node);
657 node->length += sizeof(struct acpi_iort_rc);
658 node->length += sizeof(struct acpi_iort_id_mapping) * num_mappings;
659
660 rc = (struct acpi_iort_rc *)node->node_data;
Patrick Rudolph0eafaf22025-03-16 09:32:54 +0100661 memset(rc, '\0', sizeof(struct acpi_iort_rc));
662
Patrick Rudolph1669ce72024-10-23 15:19:54 +0200663 rc->mem_access_properties = mem_access_properties;
664 rc->ats_attributes = ats_attributes;
665 rc->pci_segment_number = pci_segment_number;
666 rc->memory_address_size_limit = memory_address_size_limit;
667
668 mapping = (struct acpi_iort_id_mapping *)(rc + 1);
669 for (int i = 0; i < num_mappings; i++) {
Patrick Rudolph9b6b3882025-03-16 09:32:53 +0100670 /* Validate input */
671 output_node = (struct acpi_iort_node *)ctx->tab_start + map[i].output_reference;
672 /* ID mappings can use SMMUs or ITS groups as output references */
673 assert(output_node && ((output_node->type == ACPI_IORT_NODE_ITS_GROUP) ||
674 (output_node->type == ACPI_IORT_NODE_SMMU) ||
675 (output_node->type == ACPI_IORT_NODE_SMMU_V3)));
676
Patrick Rudolph1669ce72024-10-23 15:19:54 +0200677 memcpy(mapping, &map[i], sizeof(struct acpi_iort_id_mapping));
678 mapping++;
679 }
680
681 ctx->current += node->length;
682
683 return offset;
684}
685
686int acpi_iort_add_smmu_v3(struct acpi_ctx *ctx,
687 const u64 base_address,
688 const u32 flags,
689 const u64 vatos_address,
690 const u32 model,
691 const u32 event_gsiv,
692 const u32 pri_gsiv,
693 const u32 gerr_gsiv,
694 const u32 sync_gsiv,
695 const u32 pxm,
696 const u32 id_mapping_index,
697 const int num_mappings,
698 const struct acpi_iort_id_mapping *map)
699{
700 struct acpi_iort_node *node;
Patrick Rudolph9b6b3882025-03-16 09:32:53 +0100701 struct acpi_iort_node *output_node;
Patrick Rudolph1669ce72024-10-23 15:19:54 +0200702 struct acpi_iort_smmu_v3 *smmu;
703 struct acpi_iort_id_mapping *mapping;
704 int offset;
705
706 offset = ctx->current - ctx->tab_start;
707
708 node = ctx->current;
709 memset(node, '\0', sizeof(struct acpi_iort_node));
710
711 node->type = ACPI_IORT_NODE_SMMU_V3;
712 node->revision = 5;
713 node->mapping_count = num_mappings;
Patrick Rudolph15b48eb2025-03-16 09:32:55 +0100714 if (num_mappings)
715 node->mapping_offset = sizeof(struct acpi_iort_node) +
716 sizeof(struct acpi_iort_smmu_v3);
Patrick Rudolph1669ce72024-10-23 15:19:54 +0200717
718 node->length = sizeof(struct acpi_iort_node);
719 node->length += sizeof(struct acpi_iort_smmu_v3);
720 node->length += sizeof(struct acpi_iort_id_mapping) * num_mappings;
721
722 smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
Patrick Rudolph0eafaf22025-03-16 09:32:54 +0100723 memset(smmu, '\0', sizeof(struct acpi_iort_smmu_v3));
Patrick Rudolph1669ce72024-10-23 15:19:54 +0200724
725 smmu->base_address = base_address;
726 smmu->flags = flags;
727 smmu->vatos_address = vatos_address;
728 smmu->model = model;
729 smmu->event_gsiv = event_gsiv;
730 smmu->pri_gsiv = pri_gsiv;
731 smmu->gerr_gsiv = gerr_gsiv;
732 smmu->sync_gsiv = sync_gsiv;
733 smmu->pxm = pxm;
734 smmu->id_mapping_index = id_mapping_index;
735
736 mapping = (struct acpi_iort_id_mapping *)(smmu + 1);
737 for (int i = 0; i < num_mappings; i++) {
Patrick Rudolph9b6b3882025-03-16 09:32:53 +0100738 /* Validate input */
739 output_node = (struct acpi_iort_node *)ctx->tab_start + map[i].output_reference;
740 /*
741 * ID mappings of an SMMUv3 node can only have ITS group nodes
742 * as output references.
743 */
744 assert(output_node && output_node->type == ACPI_IORT_NODE_ITS_GROUP);
745
Patrick Rudolph1669ce72024-10-23 15:19:54 +0200746 memcpy(mapping, &map[i], sizeof(struct acpi_iort_id_mapping));
747 mapping++;
748 }
749
750 ctx->current += node->length;
751
752 return offset;
753}
754
755static int acpi_write_iort(struct acpi_ctx *ctx, const struct acpi_writer *entry)
756{
757 struct acpi_table_iort *iort;
758 struct acpi_iort_node *node;
759 u32 offset;
760 int ret;
761
762 iort = ctx->current;
763 ctx->tab_start = ctx->current;
764 memset(iort, '\0', sizeof(struct acpi_table_iort));
765
766 acpi_fill_header(&iort->header, "IORT");
767 iort->header.revision = 1;
768 iort->header.creator_revision = 1;
769 iort->header.length = sizeof(struct acpi_table_iort);
770 iort->node_offset = sizeof(struct acpi_table_iort);
771
772 acpi_inc(ctx, sizeof(struct acpi_table_iort));
773
774 offset = sizeof(struct acpi_table_iort);
775 ret = acpi_fill_iort(ctx);
776 if (ret) {
777 ctx->current = iort;
778 return log_msg_ret("fill", ret);
779 }
780
781 /* Count nodes filled in */
782 for (node = (void *)iort + iort->node_offset;
783 node->length > 0 && (void *)node < ctx->current;
784 node = (void *)node + node->length)
785 iort->node_count++;
786
787 /* (Re)calculate length and checksum */
788 iort->header.length = ctx->current - (void *)iort;
789 iort->header.checksum = table_compute_checksum((void *)iort, iort->header.length);
790 log_debug("IORT at %p, length %x\n", iort, iort->header.length);
791
792 /* Drop the table if it is empty */
793 if (iort->header.length == sizeof(struct acpi_table_iort))
794 return log_msg_ret("fill", -ENOENT);
795 acpi_add_table(ctx, iort);
796
797 return 0;
798}
799
800ACPI_WRITER(5iort, "IORT", acpi_write_iort, 0);
Patrick Rudolph158efd62024-10-23 15:19:57 +0200801
802/*
803 * Allocate memory for ACPI tables and write ACPI tables to the
804 * allocated buffer.
805 *
806 * Return: status code
807 */
808static int alloc_write_acpi_tables(void)
809{
810 u64 table_end;
811 void *addr;
812
813 if (IS_ENABLED(CONFIG_X86) ||
814 IS_ENABLED(CONFIG_QFW_ACPI) ||
815 IS_ENABLED(CONFIG_SANDBOX)) {
816 log_debug("Skipping writing ACPI tables as already done\n");
817 return 0;
818 }
819
820 if (!IS_ENABLED(CONFIG_BLOBLIST_TABLES)) {
821 log_debug("Skipping writing ACPI tables as BLOBLIST_TABLES is not selected\n");
822 return 0;
823 }
824
825 /* Align the table to a 4KB boundary to keep EFI happy */
826 addr = bloblist_add(BLOBLISTT_ACPI_TABLES, TABLE_SIZE,
827 ilog2(SZ_4K));
828
829 if (!addr)
830 return log_msg_ret("mem", -ENOMEM);
831
832 gd->arch.table_start_high = virt_to_phys(addr);
833 gd->arch.table_end_high = gd->arch.table_start_high + TABLE_SIZE;
834
835 table_end = write_acpi_tables(gd->arch.table_start_high);
836 if (!table_end) {
837 log_err("Can't create ACPI configuration table\n");
838 return -EINTR;
839 }
840
841 log_debug("- wrote 'acpi' to %lx, end %llx\n", gd->arch.table_start_high, table_end);
842 if (table_end > gd->arch.table_end_high) {
843 log_err("Out of space for configuration tables: need %llx, have %x\n",
844 table_end - gd->arch.table_start_high, TABLE_SIZE);
845 return log_msg_ret("acpi", -ENOSPC);
846 }
847
848 log_debug("- done writing tables\n");
849
850 return 0;
851}
852
853EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, alloc_write_acpi_tables);