blob: c6c052ac6c302ecbfa235cff27769df36f00ec54 [file] [log] [blame]
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
4 * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee>
5 */
6
7#define LOG_CATEGORY UCLASS_QFW
8
9#include <acpi/acpi_table.h>
Simon Glass4d8c5202025-01-10 17:00:17 -070010#include <bloblist.h>
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +010011#include <errno.h>
12#include <malloc.h>
Heinrich Schuchardt7028f9f2023-12-19 16:04:04 +010013#include <mapmem.h>
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +010014#include <qfw.h>
15#include <tables_csum.h>
16#include <stdio.h>
Heinrich Schuchardt7028f9f2023-12-19 16:04:04 +010017#include <linux/sizes.h>
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +010018#include <asm/byteorder.h>
19#include <asm/global_data.h>
20
21DECLARE_GLOBAL_DATA_PTR;
22
23/*
24 * This function allocates memory for ACPI tables
25 *
26 * @entry : BIOS linker command entry which tells where to allocate memory
27 * (either high memory or low memory)
Simon Glassf32df8d2025-03-15 14:25:55 +000028 * @addr : The address that should be used for low memory allocation. If the
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +010029 * memory allocation request is 'ZONE_HIGH' then this parameter will
30 * be ignored.
31 * @return: 0 on success, or negative value on failure
32 */
Simon Glassf32df8d2025-03-15 14:25:55 +000033static int bios_linker_allocate(struct acpi_ctx *ctx, struct udevice *dev,
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +010034 struct bios_linker_entry *entry, ulong *addr)
35{
36 uint32_t size, align;
37 struct fw_file *file;
38 unsigned long aligned_addr;
Simon Glassf32df8d2025-03-15 14:25:55 +000039 struct acpi_rsdp *rsdp;
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +010040
41 align = le32_to_cpu(entry->alloc.align);
42 /* align must be power of 2 */
43 if (align & (align - 1)) {
44 printf("error: wrong alignment %u\n", align);
45 return -EINVAL;
46 }
47
48 file = qfw_find_file(dev, entry->alloc.file);
49 if (!file) {
50 printf("error: can't find file %s\n", entry->alloc.file);
51 return -ENOENT;
52 }
53
54 size = be32_to_cpu(file->cfg.size);
55
56 /*
57 * ZONE_HIGH means we need to allocate from high memory, since
58 * malloc space is already at the end of RAM, so we directly use it.
59 * If allocation zone is ZONE_FSEG, then we use the 'addr' passed
60 * in which is low memory
61 */
Simon Glassf32df8d2025-03-15 14:25:55 +000062 if (IS_ENABLED(CONFIG_BLOBLIST_TABLES)) {
63 aligned_addr = ALIGN(*addr, align);
64 } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) {
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +010065 aligned_addr = (unsigned long)memalign(align, size);
66 if (!aligned_addr) {
67 printf("error: allocating resource\n");
68 return -ENOMEM;
69 }
70 if (aligned_addr < gd->arch.table_start_high)
71 gd->arch.table_start_high = aligned_addr;
72 if (aligned_addr + size > gd->arch.table_end_high)
73 gd->arch.table_end_high = aligned_addr + size;
74
75 } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) {
76 aligned_addr = ALIGN(*addr, align);
77 } else {
78 printf("error: invalid allocation zone\n");
79 return -EINVAL;
80 }
81
82 debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n",
83 file->cfg.name, size, entry->alloc.zone, align, aligned_addr);
84
85 qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size,
86 (void *)aligned_addr);
87 file->addr = aligned_addr;
88
Simon Glassf32df8d2025-03-15 14:25:55 +000089 rsdp = (void *)aligned_addr;
90 if (!strncmp(rsdp->signature, RSDP_SIG, sizeof(rsdp->signature)))
91 ctx->rsdp = rsdp;
92
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +010093 /* adjust address for low memory allocation */
Simon Glassf32df8d2025-03-15 14:25:55 +000094 if (IS_ENABLED(CONFIG_BLOBLIST_TABLES) ||
95 entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG)
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +010096 *addr = (aligned_addr + size);
97
98 return 0;
99}
100
101/*
102 * This function patches ACPI tables previously loaded
103 * by bios_linker_allocate()
104 *
105 * @entry : BIOS linker command entry which tells how to patch
106 * ACPI tables
107 * @return: 0 on success, or negative value on failure
108 */
109static int bios_linker_add_pointer(struct udevice *dev,
110 struct bios_linker_entry *entry)
111{
112 struct fw_file *dest, *src;
113 uint32_t offset = le32_to_cpu(entry->pointer.offset);
114 uint64_t pointer = 0;
115
116 dest = qfw_find_file(dev, entry->pointer.dest_file);
117 if (!dest || !dest->addr)
118 return -ENOENT;
119 src = qfw_find_file(dev, entry->pointer.src_file);
120 if (!src || !src->addr)
121 return -ENOENT;
122
123 debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n",
124 dest->addr, src->addr, offset, entry->pointer.size, pointer);
125
126 memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size);
127 pointer = le64_to_cpu(pointer);
128 pointer += (unsigned long)src->addr;
129 pointer = cpu_to_le64(pointer);
130 memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size);
131
132 return 0;
133}
134
135/*
136 * This function updates checksum fields of ACPI tables previously loaded
137 * by bios_linker_allocate()
138 *
139 * @entry : BIOS linker command entry which tells where to update ACPI table
140 * checksums
141 * @return: 0 on success, or negative value on failure
142 */
143static int bios_linker_add_checksum(struct udevice *dev,
144 struct bios_linker_entry *entry)
145{
146 struct fw_file *file;
147 uint8_t *data, cksum = 0;
148 uint8_t *cksum_start;
149
150 file = qfw_find_file(dev, entry->cksum.file);
151 if (!file || !file->addr)
152 return -ENOENT;
153
154 data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset));
155 cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start));
156 cksum = table_compute_checksum(cksum_start,
157 le32_to_cpu(entry->cksum.length));
158 *data = cksum;
159
160 return 0;
161}
162
163/* This function loads and patches ACPI tables provided by QEMU */
164ulong write_acpi_tables(ulong addr)
165{
166 int i, ret;
167 struct fw_file *file;
168 struct bios_linker_entry *table_loader;
169 struct bios_linker_entry *entry;
170 uint32_t size;
171 struct udevice *dev;
Simon Glass4d8c5202025-01-10 17:00:17 -0700172 struct acpi_ctx *ctx;
173
174 ctx = malloc(sizeof(*ctx));
175 if (!ctx) {
176 printf("error: out of memory for acpi ctx\n");
177 return addr;
178 }
179
180 acpi_setup_ctx(ctx, addr);
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +0100181
182 ret = qfw_get_dev(&dev);
183 if (ret) {
184 printf("error: no qfw\n");
185 return addr;
186 }
187
188 /* make sure fw_list is loaded */
189 ret = qfw_read_firmware_list(dev);
190 if (ret) {
191 printf("error: can't read firmware file list\n");
192 return addr;
193 }
194
195 file = qfw_find_file(dev, "etc/table-loader");
196 if (!file) {
197 printf("error: can't find etc/table-loader\n");
198 return addr;
199 }
200
201 size = be32_to_cpu(file->cfg.size);
202 if ((size % sizeof(*entry)) != 0) {
203 printf("error: table-loader maybe corrupted\n");
204 return addr;
205 }
206
207 table_loader = malloc(size);
208 if (!table_loader) {
209 printf("error: no memory for table-loader\n");
210 return addr;
211 }
212
213 /* QFW always puts tables at high addresses */
214 gd->arch.table_start_high = (ulong)table_loader;
215 gd->arch.table_end_high = (ulong)table_loader;
216
217 qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader);
218
219 for (i = 0; i < (size / sizeof(*entry)); i++) {
Simon Glassf32df8d2025-03-15 14:25:55 +0000220 log_content("entry %d: addr %lx\n", i, addr);
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +0100221 entry = table_loader + i;
222 switch (le32_to_cpu(entry->command)) {
223 case BIOS_LINKER_LOADER_COMMAND_ALLOCATE:
Simon Glassf32df8d2025-03-15 14:25:55 +0000224 log_content(" - %s\n", entry->alloc.file);
225 ret = bios_linker_allocate(ctx, dev, entry, &addr);
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +0100226 if (ret)
227 goto out;
228 break;
229 case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER:
Simon Glassf32df8d2025-03-15 14:25:55 +0000230 log_content(" - %s\n", entry->pointer.src_file);
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +0100231 ret = bios_linker_add_pointer(dev, entry);
232 if (ret)
233 goto out;
234 break;
235 case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM:
Simon Glassf32df8d2025-03-15 14:25:55 +0000236 log_content(" - %s\n", entry->cksum.file);
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +0100237 ret = bios_linker_add_checksum(dev, entry);
238 if (ret)
239 goto out;
240 break;
241 default:
242 break;
243 }
244 }
245
246out:
247 if (ret) {
248 struct fw_cfg_file_iter iter;
249 for (file = qfw_file_iter_init(dev, &iter);
250 !qfw_file_iter_end(&iter);
251 file = qfw_file_iter_next(&iter)) {
252 if (file->addr) {
253 free((void *)file->addr);
254 file->addr = 0;
255 }
256 }
257 }
258
259 free(table_loader);
260
Simon Glassf32df8d2025-03-15 14:25:55 +0000261 if (!ctx->rsdp) {
262 printf("error: no RSDP found\n");
263 return addr;
264 }
265 struct acpi_rsdp *rsdp = ctx->rsdp;
266
267 rsdp->length = sizeof(*rsdp);
268 rsdp->xsdt_address = 0;
269 rsdp->ext_checksum = table_compute_checksum((u8 *)rsdp, sizeof(*rsdp));
270
Heinrich Schuchardtd2cd3d62023-12-19 16:04:01 +0100271 gd_set_acpi_start(acpi_get_rsdp_addr());
272
273 return addr;
274}
275
276ulong acpi_get_rsdp_addr(void)
277{
278 int ret;
279 struct fw_file *file;
280 struct udevice *dev;
281
282 ret = qfw_get_dev(&dev);
283 if (ret) {
284 printf("error: no qfw\n");
285 return 0;
286 }
287
288 file = qfw_find_file(dev, "etc/acpi/rsdp");
289 return file->addr;
290}
Heinrich Schuchardt7028f9f2023-12-19 16:04:04 +0100291
Simon Glass4d8c5202025-01-10 17:00:17 -0700292void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt,
293 struct acpi_xsdt *xsdt)
294{
295 memset(rsdp, 0, sizeof(struct acpi_rsdp));
296
297 memcpy(rsdp->signature, RSDP_SIG, 8);
298 memcpy(rsdp->oem_id, OEM_ID, 6);
299
300 if (rsdt)
301 rsdp->rsdt_address = nomap_to_sysmem(rsdt);
302
303 if (xsdt)
304 rsdp->xsdt_address = nomap_to_sysmem(xsdt);
305
306 rsdp->length = sizeof(struct acpi_rsdp);
307 rsdp->revision = ACPI_RSDP_REV_ACPI_2_0;
308
309 /* Calculate checksums */
310 rsdp->checksum = table_compute_checksum(rsdp, 20);
311 rsdp->ext_checksum = table_compute_checksum(rsdp,
312 sizeof(struct acpi_rsdp));
313}
314
Heinrich Schuchardt7028f9f2023-12-19 16:04:04 +0100315#ifndef CONFIG_X86
316static int evt_write_acpi_tables(void)
317{
318 ulong addr, end;
319 void *ptr;
320
321 /* Reserve 64K for ACPI tables, aligned to a 4K boundary */
Simon Glass4d8c5202025-01-10 17:00:17 -0700322 ptr = bloblist_add(BLOBLISTT_ACPI_TABLES, SZ_64K, 12);
Heinrich Schuchardt7028f9f2023-12-19 16:04:04 +0100323 if (!ptr)
Simon Glass4d8c5202025-01-10 17:00:17 -0700324 return -ENOBUFS;
Heinrich Schuchardt7028f9f2023-12-19 16:04:04 +0100325 addr = map_to_sysmem(ptr);
326
327 /* Generate ACPI tables */
328 end = write_acpi_tables(addr);
329 gd->arch.table_start = addr;
Ilias Apalodimas4b1f0e72025-04-03 08:34:44 +0300330 gd->arch.table_end = end;
Heinrich Schuchardt7028f9f2023-12-19 16:04:04 +0100331
332 return 0;
333}
334
335EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, evt_write_acpi_tables);
336#endif