blob: 7c01bf23d53bd2e085d2201d3c37e2d87184fcc5 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Miao Yanfcf5c042016-05-22 19:37:14 -07002/*
3 * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
Asherah Connor5b0b43e2021-03-19 18:21:40 +11004 * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee>
Miao Yanfcf5c042016-05-22 19:37:14 -07005 */
6
Asherah Connor5b0b43e2021-03-19 18:21:40 +11007#define LOG_CATEGORY UCLASS_QFW
8
Miao Yanfcf5c042016-05-22 19:37:14 -07009#include <common.h>
Simon Glassdd4bd9a2023-01-28 15:00:24 -070010#include <bootdev.h>
11#include <bootflow.h>
12#include <bootmeth.h>
Miao Yanfcf5c042016-05-22 19:37:14 -070013#include <command.h>
14#include <errno.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060015#include <log.h>
Miao Yanfcf5c042016-05-22 19:37:14 -070016#include <malloc.h>
Miao Yan18686592016-05-22 19:37:17 -070017#include <qfw.h>
Asherah Connor5b0b43e2021-03-19 18:21:40 +110018#include <dm.h>
19#include <misc.h>
Simon Glass0679cca2021-12-01 09:02:40 -070020#include <tables_csum.h>
Simon Glass854624c2023-07-15 21:38:50 -060021#include <asm/acpi_table.h>
Miao Yanfcf5c042016-05-22 19:37:14 -070022
Simon Glass31c27eb2021-12-01 09:02:49 -070023#if defined(CONFIG_GENERATE_ACPI_TABLE) && !defined(CONFIG_SANDBOX)
Miao Yaneece4932016-05-22 19:37:20 -070024/*
25 * This function allocates memory for ACPI tables
26 *
27 * @entry : BIOS linker command entry which tells where to allocate memory
28 * (either high memory or low memory)
29 * @addr : The address that should be used for low memory allcation. If the
30 * memory allocation request is 'ZONE_HIGH' then this parameter will
31 * be ignored.
32 * @return: 0 on success, or negative value on failure
33 */
Asherah Connor5b0b43e2021-03-19 18:21:40 +110034static int bios_linker_allocate(struct udevice *dev,
35 struct bios_linker_entry *entry, ulong *addr)
Miao Yaneece4932016-05-22 19:37:20 -070036{
37 uint32_t size, align;
38 struct fw_file *file;
39 unsigned long aligned_addr;
40
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
Asherah Connor5b0b43e2021-03-19 18:21:40 +110048 file = qfw_find_file(dev, entry->alloc.file);
Miao Yaneece4932016-05-22 19:37:20 -070049 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 */
62 if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) {
63 aligned_addr = (unsigned long)memalign(align, size);
64 if (!aligned_addr) {
65 printf("error: allocating resource\n");
66 return -ENOMEM;
67 }
Simon Glass6a324892023-07-15 21:39:10 -060068 if (aligned_addr < gd->arch.table_start_high)
69 gd->arch.table_start_high = aligned_addr;
70 if (aligned_addr + size > gd->arch.table_end_high)
71 gd->arch.table_end_high = aligned_addr + size;
72
Miao Yaneece4932016-05-22 19:37:20 -070073 } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) {
74 aligned_addr = ALIGN(*addr, align);
75 } else {
76 printf("error: invalid allocation zone\n");
77 return -EINVAL;
78 }
79
80 debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n",
81 file->cfg.name, size, entry->alloc.zone, align, aligned_addr);
82
Asherah Connor5b0b43e2021-03-19 18:21:40 +110083 qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size,
84 (void *)aligned_addr);
Miao Yaneece4932016-05-22 19:37:20 -070085 file->addr = aligned_addr;
86
87 /* adjust address for low memory allocation */
88 if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG)
89 *addr = (aligned_addr + size);
90
91 return 0;
92}
93
94/*
95 * This function patches ACPI tables previously loaded
96 * by bios_linker_allocate()
97 *
98 * @entry : BIOS linker command entry which tells how to patch
99 * ACPI tables
100 * @return: 0 on success, or negative value on failure
101 */
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100102static int bios_linker_add_pointer(struct udevice *dev,
103 struct bios_linker_entry *entry)
Miao Yaneece4932016-05-22 19:37:20 -0700104{
105 struct fw_file *dest, *src;
106 uint32_t offset = le32_to_cpu(entry->pointer.offset);
107 uint64_t pointer = 0;
108
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100109 dest = qfw_find_file(dev, entry->pointer.dest_file);
Miao Yaneece4932016-05-22 19:37:20 -0700110 if (!dest || !dest->addr)
111 return -ENOENT;
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100112 src = qfw_find_file(dev, entry->pointer.src_file);
Miao Yaneece4932016-05-22 19:37:20 -0700113 if (!src || !src->addr)
114 return -ENOENT;
115
116 debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n",
117 dest->addr, src->addr, offset, entry->pointer.size, pointer);
118
119 memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size);
120 pointer = le64_to_cpu(pointer);
121 pointer += (unsigned long)src->addr;
122 pointer = cpu_to_le64(pointer);
123 memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size);
124
125 return 0;
126}
127
128/*
129 * This function updates checksum fields of ACPI tables previously loaded
130 * by bios_linker_allocate()
131 *
132 * @entry : BIOS linker command entry which tells where to update ACPI table
133 * checksums
134 * @return: 0 on success, or negative value on failure
135 */
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100136static int bios_linker_add_checksum(struct udevice *dev,
137 struct bios_linker_entry *entry)
Miao Yaneece4932016-05-22 19:37:20 -0700138{
139 struct fw_file *file;
140 uint8_t *data, cksum = 0;
141 uint8_t *cksum_start;
142
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100143 file = qfw_find_file(dev, entry->cksum.file);
Miao Yaneece4932016-05-22 19:37:20 -0700144 if (!file || !file->addr)
145 return -ENOENT;
146
147 data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset));
148 cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start));
149 cksum = table_compute_checksum(cksum_start,
150 le32_to_cpu(entry->cksum.length));
151 *data = cksum;
152
153 return 0;
154}
155
156/* This function loads and patches ACPI tables provided by QEMU */
Simon Glass42fd8c12017-01-16 07:03:35 -0700157ulong write_acpi_tables(ulong addr)
Miao Yaneece4932016-05-22 19:37:20 -0700158{
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100159 int i, ret;
Miao Yaneece4932016-05-22 19:37:20 -0700160 struct fw_file *file;
161 struct bios_linker_entry *table_loader;
162 struct bios_linker_entry *entry;
163 uint32_t size;
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100164 struct udevice *dev;
165
166 ret = qfw_get_dev(&dev);
167 if (ret) {
168 printf("error: no qfw\n");
169 return addr;
170 }
Miao Yaneece4932016-05-22 19:37:20 -0700171
172 /* make sure fw_list is loaded */
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100173 ret = qfw_read_firmware_list(dev);
Miao Yaneece4932016-05-22 19:37:20 -0700174 if (ret) {
175 printf("error: can't read firmware file list\n");
176 return addr;
177 }
178
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100179 file = qfw_find_file(dev, "etc/table-loader");
Miao Yaneece4932016-05-22 19:37:20 -0700180 if (!file) {
181 printf("error: can't find etc/table-loader\n");
182 return addr;
183 }
184
185 size = be32_to_cpu(file->cfg.size);
186 if ((size % sizeof(*entry)) != 0) {
187 printf("error: table-loader maybe corrupted\n");
188 return addr;
189 }
190
191 table_loader = malloc(size);
192 if (!table_loader) {
193 printf("error: no memory for table-loader\n");
194 return addr;
195 }
196
Simon Glass6a324892023-07-15 21:39:10 -0600197 /* QFW always puts tables at high addresses */
198 gd->arch.table_start_high = (ulong)table_loader;
199 gd->arch.table_end_high = (ulong)table_loader;
200
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100201 qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader);
Miao Yaneece4932016-05-22 19:37:20 -0700202
203 for (i = 0; i < (size / sizeof(*entry)); i++) {
204 entry = table_loader + i;
205 switch (le32_to_cpu(entry->command)) {
206 case BIOS_LINKER_LOADER_COMMAND_ALLOCATE:
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100207 ret = bios_linker_allocate(dev, entry, &addr);
Miao Yaneece4932016-05-22 19:37:20 -0700208 if (ret)
209 goto out;
210 break;
211 case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER:
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100212 ret = bios_linker_add_pointer(dev, entry);
Miao Yaneece4932016-05-22 19:37:20 -0700213 if (ret)
214 goto out;
215 break;
216 case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM:
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100217 ret = bios_linker_add_checksum(dev, entry);
Miao Yaneece4932016-05-22 19:37:20 -0700218 if (ret)
219 goto out;
220 break;
221 default:
222 break;
223 }
224 }
225
226out:
227 if (ret) {
228 struct fw_cfg_file_iter iter;
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100229 for (file = qfw_file_iter_init(dev, &iter);
230 !qfw_file_iter_end(&iter);
231 file = qfw_file_iter_next(&iter)) {
Miao Yaneece4932016-05-22 19:37:20 -0700232 if (file->addr) {
233 free((void *)file->addr);
234 file->addr = 0;
235 }
236 }
237 }
238
239 free(table_loader);
Simon Glass854624c2023-07-15 21:38:50 -0600240
241 gd_set_acpi_start(acpi_get_rsdp_addr());
242
Miao Yaneece4932016-05-22 19:37:20 -0700243 return addr;
244}
Bin Meng2d1c6612018-01-30 05:01:17 -0800245
246ulong acpi_get_rsdp_addr(void)
247{
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100248 int ret;
Bin Meng2d1c6612018-01-30 05:01:17 -0800249 struct fw_file *file;
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100250 struct udevice *dev;
Bin Meng2d1c6612018-01-30 05:01:17 -0800251
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100252 ret = qfw_get_dev(&dev);
253 if (ret) {
254 printf("error: no qfw\n");
255 return 0;
256 }
257
258 file = qfw_find_file(dev, "etc/acpi/rsdp");
Bin Meng2d1c6612018-01-30 05:01:17 -0800259 return file->addr;
260}
Miao Yaneece4932016-05-22 19:37:20 -0700261#endif
262
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100263static void qfw_read_entry_io(struct qfw_dev *qdev, u16 entry, u32 size,
264 void *address)
Miao Yanfcf5c042016-05-22 19:37:14 -0700265{
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100266 struct dm_qfw_ops *ops = dm_qfw_get_ops(qdev->dev);
Miao Yanfcf5c042016-05-22 19:37:14 -0700267
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100268 debug("%s: entry 0x%x, size %u address %p\n", __func__, entry, size,
269 address);
270
271 ops->read_entry_io(qdev->dev, entry, size, address);
Miao Yanfcf5c042016-05-22 19:37:14 -0700272}
273
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100274static void qfw_read_entry_dma(struct qfw_dev *qdev, u16 entry, u32 size,
275 void *address)
Miao Yanfcf5c042016-05-22 19:37:14 -0700276{
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100277 struct dm_qfw_ops *ops = dm_qfw_get_ops(qdev->dev);
Miao Yanfcf5c042016-05-22 19:37:14 -0700278
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100279 struct qfw_dma dma = {
280 .length = cpu_to_be32(size),
281 .address = cpu_to_be64((uintptr_t)address),
282 .control = cpu_to_be32(FW_CFG_DMA_READ),
283 };
Miao Yanfcf5c042016-05-22 19:37:14 -0700284
285 /*
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100286 * writing FW_CFG_INVALID will cause read operation to resume at last
287 * offset, otherwise read will start at offset 0
Miao Yanfcf5c042016-05-22 19:37:14 -0700288 */
289 if (entry != FW_CFG_INVALID)
290 dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16));
291
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100292 debug("%s: entry 0x%x, size %u address %p, control 0x%x\n", __func__,
Miao Yan2e82e742016-05-22 19:37:15 -0700293 entry, size, address, be32_to_cpu(dma.control));
Miao Yanfcf5c042016-05-22 19:37:14 -0700294
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100295 barrier();
296
297 ops->read_entry_dma(qdev->dev, &dma);
Miao Yanfcf5c042016-05-22 19:37:14 -0700298}
299
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100300void qfw_read_entry(struct udevice *dev, u16 entry, u32 size, void *address)
Miao Yanfcf5c042016-05-22 19:37:14 -0700301{
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100302 struct qfw_dev *qdev = dev_get_uclass_priv(dev);
Miao Yanfcf5c042016-05-22 19:37:14 -0700303
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100304 if (qdev->dma_present)
305 qfw_read_entry_dma(qdev, entry, size, address);
Miao Yanfcf5c042016-05-22 19:37:14 -0700306 else
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100307 qfw_read_entry_io(qdev, entry, size, address);
Miao Yanfcf5c042016-05-22 19:37:14 -0700308}
309
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100310int qfw_register(struct udevice *dev)
Miao Yanfcf5c042016-05-22 19:37:14 -0700311{
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100312 struct qfw_dev *qdev = dev_get_uclass_priv(dev);
313 u32 qemu, dma_enabled;
Miao Yanfcf5c042016-05-22 19:37:14 -0700314
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100315 qdev->dev = dev;
316 INIT_LIST_HEAD(&qdev->fw_list);
317
318 qfw_read_entry_io(qdev, FW_CFG_SIGNATURE, 4, &qemu);
319 if (be32_to_cpu(qemu) != QEMU_FW_CFG_SIGNATURE)
Miao Yanfcf5c042016-05-22 19:37:14 -0700320 return -ENODEV;
321
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100322 qfw_read_entry_io(qdev, FW_CFG_ID, 1, &dma_enabled);
323 if (dma_enabled & FW_CFG_DMA_ENABLED)
324 qdev->dma_present = true;
Miao Yanfcf5c042016-05-22 19:37:14 -0700325
326 return 0;
Miao Yanfcf5c042016-05-22 19:37:14 -0700327}
328
Simon Glassdd4bd9a2023-01-28 15:00:24 -0700329static int qfw_post_bind(struct udevice *dev)
330{
331 int ret;
332
333 ret = bootdev_setup_for_dev(dev, "qfw_bootdev");
334 if (ret)
335 return log_msg_ret("dev", ret);
336
337 return 0;
338}
339
340static int qfw_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
341 struct bootflow *bflow)
342{
343 const struct udevice *media = dev_get_parent(dev);
344 int ret;
345
346 if (!CONFIG_IS_ENABLED(BOOTSTD))
347 return -ENOSYS;
348
349 log_debug("media=%s\n", media->name);
350 ret = bootmeth_check(bflow->method, iter);
351 if (ret)
352 return log_msg_ret("check", ret);
353
354 log_debug("iter->part=%d\n", iter->part);
355
356 /* We only support the whole device, not partitions */
357 if (iter->part)
358 return log_msg_ret("max", -ESHUTDOWN);
359
360 log_debug("reading bootflow with method: %s\n", bflow->method->name);
361 ret = bootmeth_read_bootflow(bflow->method, bflow);
362 if (ret)
363 return log_msg_ret("method", ret);
364
365 return 0;
366}
367
368static int qfw_bootdev_bind(struct udevice *dev)
369{
370 struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
371
372 ucp->prio = BOOTDEVP_4_SCAN_FAST;
373
374 return 0;
375}
376
377static int qfw_bootdev_hunt(struct bootdev_hunter *info, bool show)
378{
379 int ret;
380
381 ret = uclass_probe_all(UCLASS_QFW);
382 if (ret && ret != -ENOENT)
383 return log_msg_ret("vir", ret);
384
385 return 0;
386}
387
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100388UCLASS_DRIVER(qfw) = {
389 .id = UCLASS_QFW,
390 .name = "qfw",
Simon Glassdd4bd9a2023-01-28 15:00:24 -0700391 .post_bind = qfw_post_bind,
Asherah Connor5b0b43e2021-03-19 18:21:40 +1100392 .per_device_auto = sizeof(struct qfw_dev),
393};
Simon Glassdd4bd9a2023-01-28 15:00:24 -0700394
395struct bootdev_ops qfw_bootdev_ops = {
396 .get_bootflow = qfw_get_bootflow,
397};
398
399static const struct udevice_id qfw_bootdev_ids[] = {
400 { .compatible = "u-boot,bootdev-qfw" },
401 { }
402};
403
404U_BOOT_DRIVER(qfw_bootdev) = {
405 .name = "qfw_bootdev",
406 .id = UCLASS_BOOTDEV,
407 .ops = &qfw_bootdev_ops,
408 .bind = qfw_bootdev_bind,
409 .of_match = qfw_bootdev_ids,
410};
411
412BOOTDEV_HUNTER(qfw_bootdev_hunter) = {
413 .prio = BOOTDEVP_4_SCAN_FAST,
414 .uclass = UCLASS_QFW,
415 .hunt = qfw_bootdev_hunt,
416 .drv = DM_DRIVER_REF(qfw_bootdev),
417};