[PATCH v2 0/7] risc-v: add ACPI support on QEMU

QEMU 8.1.2 can create ACPI tables for the RISC-V architecture. Allow passing them through to the operating system. Provide a new defconfig that enables this.
This series depends on
[PATCH] acpi: move acpi_get_rsdp_addr() to acpi/acpi_table.h https://lore.kernel.org/u-boot/20231109172302.54324-1-heinrich.schuchardt@ca...
v2: provide a new Kconfig symbol CONFIG_QFW_ACPI carve out qfw_acpi.c use CONFIG_ACPI to select QFW support provide a Kconfig fragment instead of a new defconfig
Heinrich Schuchardt (7): acpi: Kconfig symbol CONFIG_QFW_ACPI acpi: carve out qfw_acpi.c risc-v: add ACPI fields to global data risc-v: add support for QEMU firmware tables riscv: qemu: copy ACPI tables from QEMU riscv: allow usage of ACPI configs: qemu: add config fragment for ACPI
MAINTAINERS | 1 + arch/Kconfig | 1 + arch/riscv/include/asm/global_data.h | 4 + board/emulation/configs/acpi.config | 3 + board/emulation/qemu-riscv/Kconfig | 2 + board/emulation/qemu-riscv/qemu-riscv.c | 17 ++ doc/board/emulation/acpi.rst | 23 +++ doc/board/emulation/index.rst | 1 + drivers/misc/Kconfig | 7 + drivers/misc/Makefile | 1 + drivers/misc/qfw.c | 240 ---------------------- drivers/misc/qfw_acpi.c | 256 ++++++++++++++++++++++++ lib/acpi/Makefile | 2 +- lib/acpi/acpi_writer.c | 4 +- 14 files changed, 319 insertions(+), 243 deletions(-) create mode 100644 board/emulation/configs/acpi.config create mode 100644 doc/board/emulation/acpi.rst create mode 100644 drivers/misc/qfw_acpi.c

We have two implementations of write_acpi_tables(). One for writing ACPI tables based on ACPI_WRITER() entries another based on copying tables from QEMU.
Create a symbol CONFIG_QFW_ACPI that signifies copying ACPI tables from QEMU and use it consistently.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- v2: new patch --- drivers/misc/Kconfig | 7 +++++++ drivers/misc/qfw.c | 4 ++-- lib/acpi/Makefile | 2 +- lib/acpi/acpi_writer.c | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 97057de8bf..86529efb2e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -540,6 +540,13 @@ config QFW Hidden option to enable QEMU fw_cfg interface and uclass. This will be selected by either CONFIG_CMD_QFW or CONFIG_GENERATE_ACPI_TABLE.
+config QFW_ACPI + bool + default y + depends on QFW && GENERATE_ACPI_TABLE && !SANDBOX + help + Hidden option to read ACPI tables from QEMU. + config QFW_PIO bool depends on QFW diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index e3b6b4cd74..307334faf4 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -21,7 +21,7 @@ #include <tables_csum.h> #include <asm/acpi_table.h>
-#if defined(CONFIG_GENERATE_ACPI_TABLE) && !defined(CONFIG_SANDBOX) +#ifdef QFW_ACPI /* * This function allocates memory for ACPI tables * @@ -259,7 +259,7 @@ ulong acpi_get_rsdp_addr(void) file = qfw_find_file(dev, "etc/acpi/rsdp"); return file->addr; } -#endif +#endif /* QFW_ACPI */
static void qfw_read_entry_io(struct qfw_dev *qdev, u16 entry, u32 size, void *address) diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index c1c9675b5d..cc2868488a 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_table.o obj-y += acpi_writer.o
# With QEMU the ACPI tables come from there, not from U-Boot -ifndef CONFIG_QEMU +ifndef CONFIG_QFW_ACPI obj-y += base.o obj-y += csrt.o obj-y += mcfg.o diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index 946f90e8e7..9b9fdc190b 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -48,7 +48,7 @@ int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry) return 0; }
-#ifndef CONFIG_QEMU +#ifndef CONFIG_QFW_ACPI static int acpi_write_all(struct acpi_ctx *ctx) { const struct acpi_writer *writer = @@ -115,7 +115,7 @@ ulong acpi_get_rsdp_addr(void)
return map_to_sysmem(gd->acpi_ctx->rsdp); } -#endif /* QEMU */ +#endif /* QFW_ACPI */
void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start) {

On Tue, Nov 21, 2023 at 04:27:34PM +0100, Heinrich Schuchardt wrote:
We have two implementations of write_acpi_tables(). One for writing ACPI tables based on ACPI_WRITER() entries another based on copying tables from QEMU.
Create a symbol CONFIG_QFW_ACPI that signifies copying ACPI tables from QEMU and use it consistently.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: new patch
drivers/misc/Kconfig | 7 +++++++ drivers/misc/qfw.c | 4 ++-- lib/acpi/Makefile | 2 +- lib/acpi/acpi_writer.c | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-)
I still feel like this is a weird direction to go in and that:
diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index 946f90e8e7..9b9fdc190b 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -48,7 +48,7 @@ int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry) return 0; }
-#ifndef CONFIG_QEMU +#ifndef CONFIG_QFW_ACPI static int acpi_write_all(struct acpi_ctx *ctx) { const struct acpi_writer *writer = @@ -115,7 +115,7 @@ ulong acpi_get_rsdp_addr(void)
return map_to_sysmem(gd->acpi_ctx->rsdp); } -#endif /* QEMU */ +#endif /* QFW_ACPI */
void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start) {
Will need to be tweaked later on still with some other symbol to denote "ACPI tables were passed along on real hardware by $mechanism". But we can cross that when we come to it.

On 11/21/23 20:00, Tom Rini wrote:
On Tue, Nov 21, 2023 at 04:27:34PM +0100, Heinrich Schuchardt wrote:
We have two implementations of write_acpi_tables(). One for writing ACPI tables based on ACPI_WRITER() entries another based on copying tables from QEMU.
Create a symbol CONFIG_QFW_ACPI that signifies copying ACPI tables from QEMU and use it consistently.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: new patch
drivers/misc/Kconfig | 7 +++++++ drivers/misc/qfw.c | 4 ++-- lib/acpi/Makefile | 2 +- lib/acpi/acpi_writer.c | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-)
I still feel like this is a weird direction to go in and that:
diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index 946f90e8e7..9b9fdc190b 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -48,7 +48,7 @@ int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry) return 0; }
-#ifndef CONFIG_QEMU +#ifndef CONFIG_QFW_ACPI static int acpi_write_all(struct acpi_ctx *ctx) { const struct acpi_writer *writer = @@ -115,7 +115,7 @@ ulong acpi_get_rsdp_addr(void)
return map_to_sysmem(gd->acpi_ctx->rsdp); } -#endif /* QEMU */ +#endif /* QFW_ACPI */
void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start) {
Will need to be tweaked later on still with some other symbol to denote "ACPI tables were passed along on real hardware by $mechanism". But we can cross that when we come to it.
QFW is only about QEMU. What I did here is separating the transfer of ACPI tables from QEMU from the code co-used by the sandbox. Do you think this is wrong?
Probably when booting U-Boot via Coreboot we should be able to copy ACPI tables. But this will not involve QFW. Is there any code, yet, where U-Boot is picking up ACPI tables from Coreboot?
In this case you would not set symbol CONFIG_GENERATE_ACPI_TABLES. Do we really need yet another symbol?
Best regards
Heinrich

On Tue, Nov 21, 2023 at 08:30:34PM +0100, Heinrich Schuchardt wrote:
On 11/21/23 20:00, Tom Rini wrote:
On Tue, Nov 21, 2023 at 04:27:34PM +0100, Heinrich Schuchardt wrote:
We have two implementations of write_acpi_tables(). One for writing ACPI tables based on ACPI_WRITER() entries another based on copying tables from QEMU.
Create a symbol CONFIG_QFW_ACPI that signifies copying ACPI tables from QEMU and use it consistently.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: new patch
drivers/misc/Kconfig | 7 +++++++ drivers/misc/qfw.c | 4 ++-- lib/acpi/Makefile | 2 +- lib/acpi/acpi_writer.c | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-)
I still feel like this is a weird direction to go in and that:
diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index 946f90e8e7..9b9fdc190b 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -48,7 +48,7 @@ int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry) return 0; } -#ifndef CONFIG_QEMU +#ifndef CONFIG_QFW_ACPI static int acpi_write_all(struct acpi_ctx *ctx) { const struct acpi_writer *writer = @@ -115,7 +115,7 @@ ulong acpi_get_rsdp_addr(void) return map_to_sysmem(gd->acpi_ctx->rsdp); } -#endif /* QEMU */ +#endif /* QFW_ACPI */ void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start) {
Will need to be tweaked later on still with some other symbol to denote "ACPI tables were passed along on real hardware by $mechanism". But we can cross that when we come to it.
QFW is only about QEMU. What I did here is separating the transfer of ACPI tables from QEMU from the code co-used by the sandbox. Do you think this is wrong?
This part is right, yes. But I quoted the generic code here and not the QEMU part of the code.
Probably when booting U-Boot via Coreboot we should be able to copy ACPI tables. But this will not involve QFW. Is there any code, yet, where U-Boot is picking up ACPI tables from Coreboot?
Is there, or should there, or could there be? I don't know the answer there, and is what I'm driving at. And that is..
In this case you would not set symbol CONFIG_GENERATE_ACPI_TABLES. Do we really need yet another symbol?
Yes, I think a number of our symbols are oddly named, and confusing and we probably do need a new symbol or two. We have platforms which use CONFIG_GENERATE_ACPI_TABLES to mean "allocate and populate ACPI structures" and other platforms to mean "copy ACPI tables someone generated for us and pass them along to the OS".

On Tue, 21 Nov 2023 at 08:28, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
We have two implementations of write_acpi_tables(). One for writing ACPI tables based on ACPI_WRITER() entries another based on copying tables from QEMU.
Create a symbol CONFIG_QFW_ACPI that signifies copying ACPI tables from QEMU and use it consistently.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: new patch
drivers/misc/Kconfig | 7 +++++++ drivers/misc/qfw.c | 4 ++-- lib/acpi/Makefile | 2 +- lib/acpi/acpi_writer.c | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

Move the code related to copying tables from QEMU to a separate code module.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- v2: new patch --- drivers/misc/Makefile | 1 + drivers/misc/qfw.c | 240 ------------------------------------- drivers/misc/qfw_acpi.c | 256 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+), 240 deletions(-) create mode 100644 drivers/misc/qfw_acpi.c
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b67b82358a..cda701d38e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o ifdef CONFIG_QFW obj-y += qfw.o +obj-$(CONFIG_QFW_ACPI) += qfw_acpi.o obj-$(CONFIG_QFW_PIO) += qfw_pio.o obj-$(CONFIG_QFW_MMIO) += qfw_mmio.o obj-$(CONFIG_SANDBOX) += qfw_sandbox.o diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index 307334faf4..db98619fdf 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -21,246 +21,6 @@ #include <tables_csum.h> #include <asm/acpi_table.h>
-#ifdef QFW_ACPI -/* - * This function allocates memory for ACPI tables - * - * @entry : BIOS linker command entry which tells where to allocate memory - * (either high memory or low memory) - * @addr : The address that should be used for low memory allcation. If the - * memory allocation request is 'ZONE_HIGH' then this parameter will - * be ignored. - * @return: 0 on success, or negative value on failure - */ -static int bios_linker_allocate(struct udevice *dev, - struct bios_linker_entry *entry, ulong *addr) -{ - uint32_t size, align; - struct fw_file *file; - unsigned long aligned_addr; - - align = le32_to_cpu(entry->alloc.align); - /* align must be power of 2 */ - if (align & (align - 1)) { - printf("error: wrong alignment %u\n", align); - return -EINVAL; - } - - file = qfw_find_file(dev, entry->alloc.file); - if (!file) { - printf("error: can't find file %s\n", entry->alloc.file); - return -ENOENT; - } - - size = be32_to_cpu(file->cfg.size); - - /* - * ZONE_HIGH means we need to allocate from high memory, since - * malloc space is already at the end of RAM, so we directly use it. - * If allocation zone is ZONE_FSEG, then we use the 'addr' passed - * in which is low memory - */ - if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { - aligned_addr = (unsigned long)memalign(align, size); - if (!aligned_addr) { - printf("error: allocating resource\n"); - return -ENOMEM; - } - if (aligned_addr < gd->arch.table_start_high) - gd->arch.table_start_high = aligned_addr; - if (aligned_addr + size > gd->arch.table_end_high) - gd->arch.table_end_high = aligned_addr + size; - - } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) { - aligned_addr = ALIGN(*addr, align); - } else { - printf("error: invalid allocation zone\n"); - return -EINVAL; - } - - debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n", - file->cfg.name, size, entry->alloc.zone, align, aligned_addr); - - qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, - (void *)aligned_addr); - file->addr = aligned_addr; - - /* adjust address for low memory allocation */ - if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) - *addr = (aligned_addr + size); - - return 0; -} - -/* - * This function patches ACPI tables previously loaded - * by bios_linker_allocate() - * - * @entry : BIOS linker command entry which tells how to patch - * ACPI tables - * @return: 0 on success, or negative value on failure - */ -static int bios_linker_add_pointer(struct udevice *dev, - struct bios_linker_entry *entry) -{ - struct fw_file *dest, *src; - uint32_t offset = le32_to_cpu(entry->pointer.offset); - uint64_t pointer = 0; - - dest = qfw_find_file(dev, entry->pointer.dest_file); - if (!dest || !dest->addr) - return -ENOENT; - src = qfw_find_file(dev, entry->pointer.src_file); - if (!src || !src->addr) - return -ENOENT; - - debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n", - dest->addr, src->addr, offset, entry->pointer.size, pointer); - - memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size); - pointer = le64_to_cpu(pointer); - pointer += (unsigned long)src->addr; - pointer = cpu_to_le64(pointer); - memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size); - - return 0; -} - -/* - * This function updates checksum fields of ACPI tables previously loaded - * by bios_linker_allocate() - * - * @entry : BIOS linker command entry which tells where to update ACPI table - * checksums - * @return: 0 on success, or negative value on failure - */ -static int bios_linker_add_checksum(struct udevice *dev, - struct bios_linker_entry *entry) -{ - struct fw_file *file; - uint8_t *data, cksum = 0; - uint8_t *cksum_start; - - file = qfw_find_file(dev, entry->cksum.file); - if (!file || !file->addr) - return -ENOENT; - - data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset)); - cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start)); - cksum = table_compute_checksum(cksum_start, - le32_to_cpu(entry->cksum.length)); - *data = cksum; - - return 0; -} - -/* This function loads and patches ACPI tables provided by QEMU */ -ulong write_acpi_tables(ulong addr) -{ - int i, ret; - struct fw_file *file; - struct bios_linker_entry *table_loader; - struct bios_linker_entry *entry; - uint32_t size; - struct udevice *dev; - - ret = qfw_get_dev(&dev); - if (ret) { - printf("error: no qfw\n"); - return addr; - } - - /* make sure fw_list is loaded */ - ret = qfw_read_firmware_list(dev); - if (ret) { - printf("error: can't read firmware file list\n"); - return addr; - } - - file = qfw_find_file(dev, "etc/table-loader"); - if (!file) { - printf("error: can't find etc/table-loader\n"); - return addr; - } - - size = be32_to_cpu(file->cfg.size); - if ((size % sizeof(*entry)) != 0) { - printf("error: table-loader maybe corrupted\n"); - return addr; - } - - table_loader = malloc(size); - if (!table_loader) { - printf("error: no memory for table-loader\n"); - return addr; - } - - /* QFW always puts tables at high addresses */ - gd->arch.table_start_high = (ulong)table_loader; - gd->arch.table_end_high = (ulong)table_loader; - - qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader); - - for (i = 0; i < (size / sizeof(*entry)); i++) { - entry = table_loader + i; - switch (le32_to_cpu(entry->command)) { - case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: - ret = bios_linker_allocate(dev, entry, &addr); - if (ret) - goto out; - break; - case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: - ret = bios_linker_add_pointer(dev, entry); - if (ret) - goto out; - break; - case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: - ret = bios_linker_add_checksum(dev, entry); - if (ret) - goto out; - break; - default: - break; - } - } - -out: - if (ret) { - struct fw_cfg_file_iter iter; - for (file = qfw_file_iter_init(dev, &iter); - !qfw_file_iter_end(&iter); - file = qfw_file_iter_next(&iter)) { - if (file->addr) { - free((void *)file->addr); - file->addr = 0; - } - } - } - - free(table_loader); - - gd_set_acpi_start(acpi_get_rsdp_addr()); - - return addr; -} - -ulong acpi_get_rsdp_addr(void) -{ - int ret; - struct fw_file *file; - struct udevice *dev; - - ret = qfw_get_dev(&dev); - if (ret) { - printf("error: no qfw\n"); - return 0; - } - - file = qfw_find_file(dev, "etc/acpi/rsdp"); - return file->addr; -} -#endif /* QFW_ACPI */ - static void qfw_read_entry_io(struct qfw_dev *qdev, u16 entry, u32 size, void *address) { diff --git a/drivers/misc/qfw_acpi.c b/drivers/misc/qfw_acpi.c new file mode 100644 index 0000000000..6e14b2a504 --- /dev/null +++ b/drivers/misc/qfw_acpi.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Miao Yan yanmiaobest@gmail.com + * (C) Copyright 2021 Asherah Connor ashe@kivikakk.ee + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include <acpi/acpi_table.h> +#include <errno.h> +#include <malloc.h> +#include <qfw.h> +#include <tables_csum.h> +#include <stdio.h> +#include <asm/byteorder.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * This function allocates memory for ACPI tables + * + * @entry : BIOS linker command entry which tells where to allocate memory + * (either high memory or low memory) + * @addr : The address that should be used for low memory allcation. If the + * memory allocation request is 'ZONE_HIGH' then this parameter will + * be ignored. + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_allocate(struct udevice *dev, + struct bios_linker_entry *entry, ulong *addr) +{ + uint32_t size, align; + struct fw_file *file; + unsigned long aligned_addr; + + align = le32_to_cpu(entry->alloc.align); + /* align must be power of 2 */ + if (align & (align - 1)) { + printf("error: wrong alignment %u\n", align); + return -EINVAL; + } + + file = qfw_find_file(dev, entry->alloc.file); + if (!file) { + printf("error: can't find file %s\n", entry->alloc.file); + return -ENOENT; + } + + size = be32_to_cpu(file->cfg.size); + + /* + * ZONE_HIGH means we need to allocate from high memory, since + * malloc space is already at the end of RAM, so we directly use it. + * If allocation zone is ZONE_FSEG, then we use the 'addr' passed + * in which is low memory + */ + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { + aligned_addr = (unsigned long)memalign(align, size); + if (!aligned_addr) { + printf("error: allocating resource\n"); + return -ENOMEM; + } + if (aligned_addr < gd->arch.table_start_high) + gd->arch.table_start_high = aligned_addr; + if (aligned_addr + size > gd->arch.table_end_high) + gd->arch.table_end_high = aligned_addr + size; + + } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) { + aligned_addr = ALIGN(*addr, align); + } else { + printf("error: invalid allocation zone\n"); + return -EINVAL; + } + + debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n", + file->cfg.name, size, entry->alloc.zone, align, aligned_addr); + + qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, + (void *)aligned_addr); + file->addr = aligned_addr; + + /* adjust address for low memory allocation */ + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) + *addr = (aligned_addr + size); + + return 0; +} + +/* + * This function patches ACPI tables previously loaded + * by bios_linker_allocate() + * + * @entry : BIOS linker command entry which tells how to patch + * ACPI tables + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_add_pointer(struct udevice *dev, + struct bios_linker_entry *entry) +{ + struct fw_file *dest, *src; + uint32_t offset = le32_to_cpu(entry->pointer.offset); + uint64_t pointer = 0; + + dest = qfw_find_file(dev, entry->pointer.dest_file); + if (!dest || !dest->addr) + return -ENOENT; + src = qfw_find_file(dev, entry->pointer.src_file); + if (!src || !src->addr) + return -ENOENT; + + debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n", + dest->addr, src->addr, offset, entry->pointer.size, pointer); + + memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size); + pointer = le64_to_cpu(pointer); + pointer += (unsigned long)src->addr; + pointer = cpu_to_le64(pointer); + memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size); + + return 0; +} + +/* + * This function updates checksum fields of ACPI tables previously loaded + * by bios_linker_allocate() + * + * @entry : BIOS linker command entry which tells where to update ACPI table + * checksums + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_add_checksum(struct udevice *dev, + struct bios_linker_entry *entry) +{ + struct fw_file *file; + uint8_t *data, cksum = 0; + uint8_t *cksum_start; + + file = qfw_find_file(dev, entry->cksum.file); + if (!file || !file->addr) + return -ENOENT; + + data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset)); + cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start)); + cksum = table_compute_checksum(cksum_start, + le32_to_cpu(entry->cksum.length)); + *data = cksum; + + return 0; +} + +/* This function loads and patches ACPI tables provided by QEMU */ +ulong write_acpi_tables(ulong addr) +{ + int i, ret; + struct fw_file *file; + struct bios_linker_entry *table_loader; + struct bios_linker_entry *entry; + uint32_t size; + struct udevice *dev; + + ret = qfw_get_dev(&dev); + if (ret) { + printf("error: no qfw\n"); + return addr; + } + + /* make sure fw_list is loaded */ + ret = qfw_read_firmware_list(dev); + if (ret) { + printf("error: can't read firmware file list\n"); + return addr; + } + + file = qfw_find_file(dev, "etc/table-loader"); + if (!file) { + printf("error: can't find etc/table-loader\n"); + return addr; + } + + size = be32_to_cpu(file->cfg.size); + if ((size % sizeof(*entry)) != 0) { + printf("error: table-loader maybe corrupted\n"); + return addr; + } + + table_loader = malloc(size); + if (!table_loader) { + printf("error: no memory for table-loader\n"); + return addr; + } + + /* QFW always puts tables at high addresses */ + gd->arch.table_start_high = (ulong)table_loader; + gd->arch.table_end_high = (ulong)table_loader; + + qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader); + + for (i = 0; i < (size / sizeof(*entry)); i++) { + entry = table_loader + i; + switch (le32_to_cpu(entry->command)) { + case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: + ret = bios_linker_allocate(dev, entry, &addr); + if (ret) + goto out; + break; + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: + ret = bios_linker_add_pointer(dev, entry); + if (ret) + goto out; + break; + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: + ret = bios_linker_add_checksum(dev, entry); + if (ret) + goto out; + break; + default: + break; + } + } + +out: + if (ret) { + struct fw_cfg_file_iter iter; + for (file = qfw_file_iter_init(dev, &iter); + !qfw_file_iter_end(&iter); + file = qfw_file_iter_next(&iter)) { + if (file->addr) { + free((void *)file->addr); + file->addr = 0; + } + } + } + + free(table_loader); + + gd_set_acpi_start(acpi_get_rsdp_addr()); + + return addr; +} + +ulong acpi_get_rsdp_addr(void) +{ + int ret; + struct fw_file *file; + struct udevice *dev; + + ret = qfw_get_dev(&dev); + if (ret) { + printf("error: no qfw\n"); + return 0; + } + + file = qfw_find_file(dev, "etc/acpi/rsdp"); + return file->addr; +}

On Tue, Nov 21, 2023 at 04:27:35PM +0100, Heinrich Schuchardt wrote:
Move the code related to copying tables from QEMU to a separate code module.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
Reviewed-by: Tom Rini trini@konsulko.com

On Tue, 21 Nov 2023 at 08:28, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
Move the code related to copying tables from QEMU to a separate code module.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: new patch
drivers/misc/Makefile | 1 + drivers/misc/qfw.c | 240 ------------------------------------- drivers/misc/qfw_acpi.c | 256 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+), 240 deletions(-) create mode 100644 drivers/misc/qfw_acpi.c
Reviewed-by: Simon Glass sjg@chromium.org

Add fields for the location of ACPI tables to the global data.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- v2: no change --- arch/riscv/include/asm/global_data.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h index 937fa4d154..a8f6a217f1 100644 --- a/arch/riscv/include/asm/global_data.h +++ b/arch/riscv/include/asm/global_data.h @@ -32,6 +32,10 @@ struct arch_global_data { ulong available_harts; #endif #endif + ulong table_start; /* Start address of ACPI tables */ + ulong table_end; /* End address of ACPI tables */ + ulong table_start_high; /* Start address of high ACPI tables */ + ulong table_end_high; /* End address of high ACPI tables */ #ifdef CONFIG_SMBIOS ulong smbios_start; /* Start address of SMBIOS table */ #endif

On Tue, 21 Nov 2023 at 08:28, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
Add fields for the location of ACPI tables to the global data.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: no change
arch/riscv/include/asm/global_data.h | 4 ++++ 1 file changed, 4 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

Enable the QEMU firmware interface if ACPI tables are to be supported on the QEMU platform.
Enable the QFW MMIO interface if the QEMU firmware interface is enabled.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- v2: depend on CONFIG_ACPI instead of CONFIG_GENERATE_ACPI_TABLES --- board/emulation/qemu-riscv/Kconfig | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/board/emulation/qemu-riscv/Kconfig b/board/emulation/qemu-riscv/Kconfig index 108e9fdb00..57bdf95523 100644 --- a/board/emulation/qemu-riscv/Kconfig +++ b/board/emulation/qemu-riscv/Kconfig @@ -33,6 +33,8 @@ config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select GENERIC_RISCV select SUPPORT_SPL + select QFW if ACPI + select QFW_MMIO if QFW imply AHCI imply SMP imply BOARD_LATE_INIT

On Tue, 21 Nov 2023 at 08:28, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
Enable the QEMU firmware interface if ACPI tables are to be supported on the QEMU platform.
Enable the QFW MMIO interface if the QEMU firmware interface is enabled.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: depend on CONFIG_ACPI instead of CONFIG_GENERATE_ACPI_TABLES
board/emulation/qemu-riscv/Kconfig | 2 ++ 1 file changed, 2 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

If CONFIG_GENERATE_ACPI_TABLES=y, read the ACPI tables provided by QEMU and make them available to U-Boot.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- v2: no change --- board/emulation/qemu-riscv/qemu-riscv.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/board/emulation/qemu-riscv/qemu-riscv.c b/board/emulation/qemu-riscv/qemu-riscv.c index 181abbbf97..2cebce2128 100644 --- a/board/emulation/qemu-riscv/qemu-riscv.c +++ b/board/emulation/qemu-riscv/qemu-riscv.c @@ -4,17 +4,20 @@ */
#include <common.h> +#include <acpi/acpi_table.h> #include <dm.h> #include <dm/ofnode.h> #include <env.h> #include <fdtdec.h> #include <image.h> #include <log.h> +#include <malloc.h> #include <spl.h> #include <init.h> #include <usb.h> #include <virtio_types.h> #include <virtio.h> +#include <linux/sizes.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -46,6 +49,20 @@ int board_late_init(void) if (CONFIG_IS_ENABLED(USB_KEYBOARD)) usb_init();
+ if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) { + uintptr_t addr; + void *ptr; + + /* Reserve 64K for ACPI tables, aligned to a 4K boundary */ + ptr = memalign(SZ_4K, SZ_64K); + addr = (uintptr_t)ptr; + + /* Generate ACPI tables */ + write_acpi_tables(addr); + gd->arch.table_start = addr; + gd->arch.table_end = addr; + } + return 0; }

On Tue, 21 Nov 2023 at 08:28, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
If CONFIG_GENERATE_ACPI_TABLES=y, read the ACPI tables provided by QEMU and make them available to U-Boot.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: no change
board/emulation/qemu-riscv/qemu-riscv.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org
Can we not use common code to do this? What is different about ARM and x86?
diff --git a/board/emulation/qemu-riscv/qemu-riscv.c b/board/emulation/qemu-riscv/qemu-riscv.c index 181abbbf97..2cebce2128 100644 --- a/board/emulation/qemu-riscv/qemu-riscv.c +++ b/board/emulation/qemu-riscv/qemu-riscv.c @@ -4,17 +4,20 @@ */
#include <common.h> +#include <acpi/acpi_table.h> #include <dm.h> #include <dm/ofnode.h> #include <env.h> #include <fdtdec.h> #include <image.h> #include <log.h> +#include <malloc.h> #include <spl.h> #include <init.h> #include <usb.h> #include <virtio_types.h> #include <virtio.h> +#include <linux/sizes.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -46,6 +49,20 @@ int board_late_init(void) if (CONFIG_IS_ENABLED(USB_KEYBOARD)) usb_init();
if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) {
uintptr_t addr;
void *ptr;
/* Reserve 64K for ACPI tables, aligned to a 4K boundary */
ptr = memalign(SZ_4K, SZ_64K);
addr = (uintptr_t)ptr;
/* Generate ACPI tables */
write_acpi_tables(addr);
gd->arch.table_start = addr;
gd->arch.table_end = addr;
}
return 0;
}
-- 2.40.1

On Sat, Dec 02, 2023 at 11:23:19AM -0700, Simon Glass wrote:
On Tue, 21 Nov 2023 at 08:28, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
If CONFIG_GENERATE_ACPI_TABLES=y, read the ACPI tables provided by QEMU and make them available to U-Boot.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: no change
board/emulation/qemu-riscv/qemu-riscv.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org
Can we not use common code to do this? What is different about ARM and x86?
This is why I was trying to convince people we need a symbol that means "handle ACPI tables for QEMU targets" as yes, this code too should be in that common area I bet.

Select CONFIG_SUPPORT_ACPI to allow usage of ACPI tables with RISC-V.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- v2: no change --- arch/Kconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/Kconfig b/arch/Kconfig index 2e0528d819..c23d57e4c4 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -108,6 +108,7 @@ config PPC config RISCV bool "RISC-V architecture" select CREATE_ARCH_SYMLINK + select SUPPORT_ACPI select SUPPORT_OF_CONTROL select OF_CONTROL select DM

On Tue, 21 Nov 2023 at 08:28, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
Select CONFIG_SUPPORT_ACPI to allow usage of ACPI tables with RISC-V.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: no change
arch/Kconfig | 1 + 1 file changed, 1 insertion(+)
Reviewed-by: Simon Glass sjg@chromium.org
Oh dear, first ARM and now RISC-V

Provide a configuration fragment to enable ACPI on QEMU.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- v2: new patch --- MAINTAINERS | 1 + board/emulation/configs/acpi.config | 3 +++ doc/board/emulation/acpi.rst | 23 +++++++++++++++++++++++ doc/board/emulation/index.rst | 1 + 4 files changed, 28 insertions(+) create mode 100644 board/emulation/configs/acpi.config create mode 100644 doc/board/emulation/acpi.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 7c1cb2dc4d..9f443674cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -53,6 +53,7 @@ Maintainers List (try to look for most precise areas first) ACPI: M: Simon Glass sjg@chromium.org S: Maintained +F: board/emulation/configs/acpi.config F: cmd/acpi.c F: lib/acpi/
diff --git a/board/emulation/configs/acpi.config b/board/emulation/configs/acpi.config new file mode 100644 index 0000000000..b7ed811e33 --- /dev/null +++ b/board/emulation/configs/acpi.config @@ -0,0 +1,3 @@ +CONFIG_CMD_QFW=y +CONFIG_ACPI=y +CONFIG_GENERATE_ACPI_TABLE=y diff --git a/doc/board/emulation/acpi.rst b/doc/board/emulation/acpi.rst new file mode 100644 index 0000000000..e1208ca51e --- /dev/null +++ b/doc/board/emulation/acpi.rst @@ -0,0 +1,23 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +ACPI on QEMU +============ + +QEMU can provide ACPI tables on ARM, RISC-V, and x86. + +The following settings are needed:: + + CONFIG_CMD_QFW=y + CONFIG_ACPI=y + CONFIG_GENERATE_ACPI_TABLE=y + +On x86 these settings are already included in the defconfig files. ARM and +RISC-V default to use device-trees. + +Instead of updating the configuration manually you can add the configuration +fragment `acpi.config` to the make command for initializing the configuration. +E.g. + +.. code-block:: bash + + make qemu-riscv64_smode_defconfig acpi.config diff --git a/doc/board/emulation/index.rst b/doc/board/emulation/index.rst index 932c65adeb..d3d6b8f3d8 100644 --- a/doc/board/emulation/index.rst +++ b/doc/board/emulation/index.rst @@ -6,6 +6,7 @@ Emulation .. toctree:: :maxdepth: 1
+ acpi blkdev ../../usage/semihosting qemu-arm

On Tue, 21 Nov 2023 at 17:28, Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
Provide a configuration fragment to enable ACPI on QEMU.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
v2: new patch
MAINTAINERS | 1 + board/emulation/configs/acpi.config | 3 +++ doc/board/emulation/acpi.rst | 23 +++++++++++++++++++++++ doc/board/emulation/index.rst | 1 + 4 files changed, 28 insertions(+) create mode 100644 board/emulation/configs/acpi.config create mode 100644 doc/board/emulation/acpi.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 7c1cb2dc4d..9f443674cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -53,6 +53,7 @@ Maintainers List (try to look for most precise areas first) ACPI: M: Simon Glass sjg@chromium.org S: Maintained +F: board/emulation/configs/acpi.config F: cmd/acpi.c F: lib/acpi/
diff --git a/board/emulation/configs/acpi.config b/board/emulation/configs/acpi.config new file mode 100644 index 0000000000..b7ed811e33 --- /dev/null +++ b/board/emulation/configs/acpi.config @@ -0,0 +1,3 @@ +CONFIG_CMD_QFW=y +CONFIG_ACPI=y +CONFIG_GENERATE_ACPI_TABLE=y diff --git a/doc/board/emulation/acpi.rst b/doc/board/emulation/acpi.rst new file mode 100644 index 0000000000..e1208ca51e --- /dev/null +++ b/doc/board/emulation/acpi.rst @@ -0,0 +1,23 @@ +.. SPDX-License-Identifier: GPL-2.0+
+ACPI on QEMU +============
+QEMU can provide ACPI tables on ARM, RISC-V, and x86.
+The following settings are needed::
- CONFIG_CMD_QFW=y
- CONFIG_ACPI=y
- CONFIG_GENERATE_ACPI_TABLE=y
+On x86 these settings are already included in the defconfig files. ARM and +RISC-V default to use device-trees.
+Instead of updating the configuration manually you can add the configuration +fragment `acpi.config` to the make command for initializing the configuration. +E.g.
+.. code-block:: bash
- make qemu-riscv64_smode_defconfig acpi.config
diff --git a/doc/board/emulation/index.rst b/doc/board/emulation/index.rst index 932c65adeb..d3d6b8f3d8 100644 --- a/doc/board/emulation/index.rst +++ b/doc/board/emulation/index.rst @@ -6,6 +6,7 @@ Emulation .. toctree:: :maxdepth: 1
- acpi blkdev ../../usage/semihosting qemu-arm
-- 2.40.1
Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org
participants (4)
-
Heinrich Schuchardt
-
Ilias Apalodimas
-
Simon Glass
-
Tom Rini