2016-05-06 22:40 GMT+08:00 Tom Rini <tr...@konsulko.com>: > - Move the command portion of arch/x86/cpu/qemu/fw_cfg.c into > cmd/qemu_fw_cfg.c > - Move arch/x86/include/asm/fw_cfg.h to include/qemu_fw_cfg.h > - Rename ACPI table portion to arch/x86/cpu/qemu/acpi_table.c > > Signed-off-by: Tom Rini <tr...@konsulko.com> > --- > Changes in v2: > - Depend on X86 (per Miao Yan)
I am afraid this still doesn't build. At least you need to make the ACPI stuff depend on the new config option CONFIG_CMD_QEMU_FW_CFG, as well as some tweaks in qemu.c and mp_init.c because they call functions from the origin fw_cfg.c and they used to be guarded by CONFIG_QEMU, something like the following diff: diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 4ef27dc..efede32 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -444,7 +444,7 @@ config GENERATE_ACPI_TABLE config QEMU_ACPI_TABLE bool "Load ACPI table from QEMU fw_cfg interface" - depends on GENERATE_ACPI_TABLE && QEMU + depends on GENERATE_ACPI_TABLE && CMD_QEMU_FW_CFG default y help By default, U-Boot generates its own ACPI tables. This option, if diff --git a/arch/x86/cpu/mp_init.c b/arch/x86/cpu/mp_init.c index 13bec7a..7b870b7 100644 --- a/arch/x86/cpu/mp_init.c +++ b/arch/x86/cpu/mp_init.c @@ -420,7 +420,7 @@ static int init_bsp(struct udevice **devp) return 0; } -#ifdef CONFIG_QEMU +#ifdef CONFIG_CMD_QEMU_FW_CFG static int qemu_cpu_fixup(void) { int ret; @@ -496,7 +496,7 @@ int mp_init(struct mp_params *p) if (ret) return ret; -#ifdef CONFIG_QEMU +#ifdef CONFIG_CMD_QEMU_FW_CFG ret = qemu_cpu_fixup(); if (ret) return ret; diff --git a/arch/x86/cpu/qemu/Makefile b/arch/x86/cpu/qemu/Makefile index 97b965c..d0e146e 100644 --- a/arch/x86/cpu/qemu/Makefile +++ b/arch/x86/cpu/qemu/Makefile @@ -7,5 +7,11 @@ ifndef CONFIG_EFI_STUB obj-y += car.o dram.o endif -obj-y += cpu.o qemu.o + +obj-y += qemu.o + +ifdef CONFIG_CMD_QEMU_FW_CFG +obj-y += cpu.o obj-$(CONFIG_QEMU_ACPI_TABLE) += acpi_table.o +endif + diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index b41e4ec..2730a5f 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -88,7 +88,9 @@ static void qemu_chipset_init(void) enable_pm_ich9(); } +#ifdef CONFIG_CMD_QEMU_FW_CFG qemu_fwcfg_init(); +#endif } int arch_cpu_init(void) > --- > arch/x86/cpu/mp_init.c | 2 +- > arch/x86/cpu/qemu/Makefile | 3 +- > arch/x86/cpu/qemu/acpi_table.c | 243 ++++++++++++++++++ > arch/x86/cpu/qemu/cpu.c | 2 +- > arch/x86/cpu/qemu/fw_cfg.c | 570 > ----------------------------------------- > arch/x86/cpu/qemu/qemu.c | 2 +- > arch/x86/include/asm/fw_cfg.h | 157 ------------ > arch/x86/lib/acpi_table.c | 2 +- > cmd/Kconfig | 7 + > cmd/Makefile | 1 + > cmd/qemu_fw_cfg.c | 343 +++++++++++++++++++++++++ > configs/qemu-x86_defconfig | 1 + > include/qemu_fw_cfg.h | 162 ++++++++++++ > 13 files changed, 763 insertions(+), 732 deletions(-) > create mode 100644 arch/x86/cpu/qemu/acpi_table.c > delete mode 100644 arch/x86/cpu/qemu/fw_cfg.c > delete mode 100644 arch/x86/include/asm/fw_cfg.h > create mode 100644 cmd/qemu_fw_cfg.c > create mode 100644 include/qemu_fw_cfg.h > > diff --git a/arch/x86/cpu/mp_init.c b/arch/x86/cpu/mp_init.c > index 2604a68..13bec7a 100644 > --- a/arch/x86/cpu/mp_init.c > +++ b/arch/x86/cpu/mp_init.c > @@ -11,6 +11,7 @@ > #include <dm.h> > #include <errno.h> > #include <malloc.h> > +#include <qemu_fw_cfg.h> > #include <asm/atomic.h> > #include <asm/cpu.h> > #include <asm/interrupt.h> > @@ -21,7 +22,6 @@ > #include <asm/mtrr.h> > #include <asm/processor.h> > #include <asm/sipi.h> > -#include <asm/fw_cfg.h> > #include <dm/device-internal.h> > #include <dm/uclass-internal.h> > #include <dm/lists.h> > diff --git a/arch/x86/cpu/qemu/Makefile b/arch/x86/cpu/qemu/Makefile > index 6eeddf1..97b965c 100644 > --- a/arch/x86/cpu/qemu/Makefile > +++ b/arch/x86/cpu/qemu/Makefile > @@ -7,4 +7,5 @@ > ifndef CONFIG_EFI_STUB > obj-y += car.o dram.o > endif > -obj-y += cpu.o fw_cfg.o qemu.o > +obj-y += cpu.o qemu.o > +obj-$(CONFIG_QEMU_ACPI_TABLE) += acpi_table.o > diff --git a/arch/x86/cpu/qemu/acpi_table.c b/arch/x86/cpu/qemu/acpi_table.c > new file mode 100644 > index 0000000..49381ac > --- /dev/null > +++ b/arch/x86/cpu/qemu/acpi_table.c > @@ -0,0 +1,243 @@ > +/* > + * (C) Copyright 2015 Miao Yan <yanmiaob...@gmail.com> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <command.h> > +#include <errno.h> > +#include <malloc.h> > +#include <qemu_fw_cfg.h> > +#include <asm/io.h> > +#include <asm/tables.h> > +#include <asm/e820.h> > +#include <linux/list.h> > +#include <memalign.h> > + > +/* > + * 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 bios_linker_entry *entry, u32 *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 = qemu_fwcfg_find_file(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; > + } > + } 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); > + > + qemu_fwcfg_read_entry(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 bios_linker_entry *entry) > +{ > + struct fw_file *dest, *src; > + uint32_t offset = le32_to_cpu(entry->pointer.offset); > + uint64_t pointer = 0; > + > + dest = qemu_fwcfg_find_file(entry->pointer.dest_file); > + if (!dest || !dest->addr) > + return -ENOENT; > + src = qemu_fwcfg_find_file(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 bios_linker_entry *entry) > +{ > + struct fw_file *file; > + uint8_t *data, cksum = 0; > + uint8_t *cksum_start; > + > + file = qemu_fwcfg_find_file(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; > +} > + > +unsigned install_e820_map(unsigned max_entries, struct e820entry *entries) > +{ > + entries[0].addr = 0; > + entries[0].size = ISA_START_ADDRESS; > + entries[0].type = E820_RAM; > + > + entries[1].addr = ISA_START_ADDRESS; > + entries[1].size = ISA_END_ADDRESS - ISA_START_ADDRESS; > + entries[1].type = E820_RESERVED; > + > + /* > + * since we use memalign(malloc) to allocate high memory for > + * storing ACPI tables, we need to reserve them in e820 tables, > + * otherwise kernel will reclaim them and data will be corrupted > + */ > + entries[2].addr = ISA_END_ADDRESS; > + entries[2].size = gd->relocaddr - TOTAL_MALLOC_LEN - ISA_END_ADDRESS; > + entries[2].type = E820_RAM; > + > + /* for simplicity, reserve entire malloc space */ > + entries[3].addr = gd->relocaddr - TOTAL_MALLOC_LEN; > + entries[3].size = TOTAL_MALLOC_LEN; > + entries[3].type = E820_RESERVED; > + > + entries[4].addr = gd->relocaddr; > + entries[4].size = gd->ram_size - gd->relocaddr; > + entries[4].type = E820_RESERVED; > + > + entries[5].addr = CONFIG_PCIE_ECAM_BASE; > + entries[5].size = CONFIG_PCIE_ECAM_SIZE; > + entries[5].type = E820_RESERVED; > + > + return 6; > +} > + > +/* This function loads and patches ACPI tables provided by QEMU */ > +u32 write_acpi_tables(u32 addr) > +{ > + int i, ret = 0; > + struct fw_file *file; > + struct bios_linker_entry *table_loader; > + struct bios_linker_entry *entry; > + uint32_t size; > + > + /* make sure fw_list is loaded */ > + ret = qemu_fwcfg_read_firmware_list(); > + if (ret) { > + printf("error: can't read firmware file list\n"); > + return addr; > + } > + > + file = qemu_fwcfg_find_file("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; > + } > + > + qemu_fwcfg_read_entry(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(entry, &addr); > + if (ret) > + goto out; > + break; > + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: > + ret = bios_linker_add_pointer(entry); > + if (ret) > + goto out; > + break; > + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: > + ret = bios_linker_add_checksum(entry); > + if (ret) > + goto out; > + break; > + default: > + break; > + } > + } > + > +out: > + if (ret) > + qemu_fwcfg_free_files(); > + > + free(table_loader); > + return addr; > +} > diff --git a/arch/x86/cpu/qemu/cpu.c b/arch/x86/cpu/qemu/cpu.c > index a1b70c6..4d2989a 100644 > --- a/arch/x86/cpu/qemu/cpu.c > +++ b/arch/x86/cpu/qemu/cpu.c > @@ -8,8 +8,8 @@ > #include <cpu.h> > #include <dm.h> > #include <errno.h> > +#include <qemu_fw_cfg.h> > #include <asm/cpu.h> > -#include <asm/fw_cfg.h> > > DECLARE_GLOBAL_DATA_PTR; > > diff --git a/arch/x86/cpu/qemu/fw_cfg.c b/arch/x86/cpu/qemu/fw_cfg.c > deleted file mode 100644 > index 2e2794e..0000000 > --- a/arch/x86/cpu/qemu/fw_cfg.c > +++ /dev/null > @@ -1,570 +0,0 @@ > -/* > - * (C) Copyright 2015 Miao Yan <yanmiaob...@gmail.com> > - * > - * SPDX-License-Identifier: GPL-2.0+ > - */ > - > -#include <common.h> > -#include <command.h> > -#include <errno.h> > -#include <malloc.h> > -#include <asm/io.h> > -#include <asm/fw_cfg.h> > -#include <asm/tables.h> > -#include <asm/e820.h> > -#include <linux/list.h> > -#include <memalign.h> > - > -static bool fwcfg_present; > -static bool fwcfg_dma_present; > - > -static LIST_HEAD(fw_list); > - > -/* Read configuration item using fw_cfg PIO interface */ > -static void qemu_fwcfg_read_entry_pio(uint16_t entry, > - uint32_t size, void *address) > -{ > - uint32_t i = 0; > - uint8_t *data = address; > - > - /* > - * writting FW_CFG_INVALID will cause read operation to resume at > - * last offset, otherwise read will start at offset 0 > - */ > - if (entry != FW_CFG_INVALID) > - outw(entry, FW_CONTROL_PORT); > - while (size--) > - data[i++] = inb(FW_DATA_PORT); > -} > - > -/* Read configuration item using fw_cfg DMA interface */ > -static void qemu_fwcfg_read_entry_dma(uint16_t entry, > - uint32_t size, void *address) > -{ > - struct fw_cfg_dma_access dma; > - > - dma.length = cpu_to_be32(size); > - dma.address = cpu_to_be64((uintptr_t)address); > - dma.control = cpu_to_be32(FW_CFG_DMA_READ); > - > - /* > - * writting FW_CFG_INVALID will cause read operation to resume at > - * last offset, otherwise read will start at offset 0 > - */ > - if (entry != FW_CFG_INVALID) > - dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16)); > - > - barrier(); > - > - debug("qemu_fwcfg_dma_read_entry: addr %p, length %u control 0x%x\n", > - address, size, be32_to_cpu(dma.control)); > - > - outl(cpu_to_be32((uint32_t)&dma), FW_DMA_PORT_HIGH); > - > - while (be32_to_cpu(dma.control) & ~FW_CFG_DMA_ERROR) > - __asm__ __volatile__ ("pause"); > -} > - > -static bool qemu_fwcfg_present(void) > -{ > - uint32_t qemu; > - > - qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu); > - return be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE; > -} > - > -static bool qemu_fwcfg_dma_present(void) > -{ > - uint8_t dma_enabled; > - > - qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled); > - if (dma_enabled & FW_CFG_DMA_ENABLED) > - return true; > - > - return false; > -} > - > -static void qemu_fwcfg_read_entry(uint16_t entry, > - uint32_t length, void *address) > -{ > - if (fwcfg_dma_present) > - qemu_fwcfg_read_entry_dma(entry, length, address); > - else > - qemu_fwcfg_read_entry_pio(entry, length, address); > -} > - > -int qemu_fwcfg_online_cpus(void) > -{ > - uint16_t nb_cpus; > - > - if (!fwcfg_present) > - return -ENODEV; > - > - qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus); > - > - return le16_to_cpu(nb_cpus); > -} > - > -/* > - * This function prepares kernel for zboot. It loads kernel data > - * to 'load_addr', initrd to 'initrd_addr' and kernel command > - * line using qemu fw_cfg interface. > - */ > -static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) > -{ > - char *data_addr; > - uint32_t setup_size, kernel_size, cmdline_size, initrd_size; > - > - qemu_fwcfg_read_entry(FW_CFG_SETUP_SIZE, 4, &setup_size); > - qemu_fwcfg_read_entry(FW_CFG_KERNEL_SIZE, 4, &kernel_size); > - > - if (setup_size == 0 || kernel_size == 0) { > - printf("warning: no kernel available\n"); > - return -1; > - } > - > - data_addr = load_addr; > - qemu_fwcfg_read_entry(FW_CFG_SETUP_DATA, > - le32_to_cpu(setup_size), data_addr); > - data_addr += le32_to_cpu(setup_size); > - > - qemu_fwcfg_read_entry(FW_CFG_KERNEL_DATA, > - le32_to_cpu(kernel_size), data_addr); > - data_addr += le32_to_cpu(kernel_size); > - > - data_addr = initrd_addr; > - qemu_fwcfg_read_entry(FW_CFG_INITRD_SIZE, 4, &initrd_size); > - if (initrd_size == 0) { > - printf("warning: no initrd available\n"); > - } else { > - qemu_fwcfg_read_entry(FW_CFG_INITRD_DATA, > - le32_to_cpu(initrd_size), data_addr); > - data_addr += le32_to_cpu(initrd_size); > - } > - > - qemu_fwcfg_read_entry(FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); > - if (cmdline_size) { > - qemu_fwcfg_read_entry(FW_CFG_CMDLINE_DATA, > - le32_to_cpu(cmdline_size), data_addr); > - /* > - * if kernel cmdline only contains '\0', (e.g. no -append > - * when invoking qemu), do not update bootargs > - */ > - if (*data_addr != '\0') { > - if (setenv("bootargs", data_addr) < 0) > - printf("warning: unable to change > bootargs\n"); > - } > - } > - > - printf("loading kernel to address %p size %x", load_addr, > - le32_to_cpu(kernel_size)); > - if (initrd_size) > - printf(" initrd %p size %x\n", > - initrd_addr, > - le32_to_cpu(initrd_size)); > - else > - printf("\n"); > - > - return 0; > -} > - > -static int qemu_fwcfg_read_firmware_list(void) > -{ > - int i; > - uint32_t count; > - struct fw_file *file; > - struct list_head *entry; > - > - /* don't read it twice */ > - if (!list_empty(&fw_list)) > - return 0; > - > - qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count); > - if (!count) > - return 0; > - > - count = be32_to_cpu(count); > - for (i = 0; i < count; i++) { > - file = malloc(sizeof(*file)); > - if (!file) { > - printf("error: allocating resource\n"); > - goto err; > - } > - qemu_fwcfg_read_entry(FW_CFG_INVALID, > - sizeof(struct fw_cfg_file), &file->cfg); > - file->addr = 0; > - list_add_tail(&file->list, &fw_list); > - } > - > - return 0; > - > -err: > - list_for_each(entry, &fw_list) { > - file = list_entry(entry, struct fw_file, list); > - free(file); > - } > - > - return -ENOMEM; > -} > - > -#ifdef CONFIG_QEMU_ACPI_TABLE > -static struct fw_file *qemu_fwcfg_find_file(const char *name) > -{ > - struct list_head *entry; > - struct fw_file *file; > - > - list_for_each(entry, &fw_list) { > - file = list_entry(entry, struct fw_file, list); > - if (!strcmp(file->cfg.name, name)) > - return file; > - } > - > - return NULL; > -} > - > -/* > - * 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 bios_linker_entry *entry, u32 *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 = qemu_fwcfg_find_file(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; > - } > - } 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); > - > - qemu_fwcfg_read_entry(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 bios_linker_entry *entry) > -{ > - struct fw_file *dest, *src; > - uint32_t offset = le32_to_cpu(entry->pointer.offset); > - uint64_t pointer = 0; > - > - dest = qemu_fwcfg_find_file(entry->pointer.dest_file); > - if (!dest || !dest->addr) > - return -ENOENT; > - src = qemu_fwcfg_find_file(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 bios_linker_entry *entry) > -{ > - struct fw_file *file; > - uint8_t *data, cksum = 0; > - uint8_t *cksum_start; > - > - file = qemu_fwcfg_find_file(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; > -} > - > -unsigned install_e820_map(unsigned max_entries, struct e820entry *entries) > -{ > - entries[0].addr = 0; > - entries[0].size = ISA_START_ADDRESS; > - entries[0].type = E820_RAM; > - > - entries[1].addr = ISA_START_ADDRESS; > - entries[1].size = ISA_END_ADDRESS - ISA_START_ADDRESS; > - entries[1].type = E820_RESERVED; > - > - /* > - * since we use memalign(malloc) to allocate high memory for > - * storing ACPI tables, we need to reserve them in e820 tables, > - * otherwise kernel will reclaim them and data will be corrupted > - */ > - entries[2].addr = ISA_END_ADDRESS; > - entries[2].size = gd->relocaddr - TOTAL_MALLOC_LEN - ISA_END_ADDRESS; > - entries[2].type = E820_RAM; > - > - /* for simplicity, reserve entire malloc space */ > - entries[3].addr = gd->relocaddr - TOTAL_MALLOC_LEN; > - entries[3].size = TOTAL_MALLOC_LEN; > - entries[3].type = E820_RESERVED; > - > - entries[4].addr = gd->relocaddr; > - entries[4].size = gd->ram_size - gd->relocaddr; > - entries[4].type = E820_RESERVED; > - > - entries[5].addr = CONFIG_PCIE_ECAM_BASE; > - entries[5].size = CONFIG_PCIE_ECAM_SIZE; > - entries[5].type = E820_RESERVED; > - > - return 6; > -} > - > -/* This function loads and patches ACPI tables provided by QEMU */ > -u32 write_acpi_tables(u32 addr) > -{ > - int i, ret = 0; > - struct fw_file *file; > - struct bios_linker_entry *table_loader; > - struct bios_linker_entry *entry; > - uint32_t size; > - struct list_head *list; > - > - /* make sure fw_list is loaded */ > - ret = qemu_fwcfg_read_firmware_list(); > - if (ret) { > - printf("error: can't read firmware file list\n"); > - return addr; > - } > - > - file = qemu_fwcfg_find_file("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; > - } > - > - qemu_fwcfg_read_entry(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(entry, &addr); > - if (ret) > - goto out; > - break; > - case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: > - ret = bios_linker_add_pointer(entry); > - if (ret) > - goto out; > - break; > - case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: > - ret = bios_linker_add_checksum(entry); > - if (ret) > - goto out; > - break; > - default: > - break; > - } > - } > - > -out: > - if (ret) { > - list_for_each(list, &fw_list) { > - file = list_entry(list, struct fw_file, list); > - if (file->addr) > - free((void *)file->addr); > - } > - } > - > - free(table_loader); > - return addr; > -} > -#endif > - > -static int qemu_fwcfg_list_firmware(void) > -{ > - int ret; > - struct list_head *entry; > - struct fw_file *file; > - > - /* make sure fw_list is loaded */ > - ret = qemu_fwcfg_read_firmware_list(); > - if (ret) > - return ret; > - > - list_for_each(entry, &fw_list) { > - file = list_entry(entry, struct fw_file, list); > - printf("%-56s\n", file->cfg.name); > - } > - > - return 0; > -} > - > -void qemu_fwcfg_init(void) > -{ > - fwcfg_present = qemu_fwcfg_present(); > - if (fwcfg_present) > - fwcfg_dma_present = qemu_fwcfg_dma_present(); > -} > - > -static int qemu_fwcfg_do_list(cmd_tbl_t *cmdtp, int flag, > - int argc, char * const argv[]) > -{ > - if (qemu_fwcfg_list_firmware() < 0) > - return CMD_RET_FAILURE; > - > - return 0; > -} > - > -static int qemu_fwcfg_do_cpus(cmd_tbl_t *cmdtp, int flag, > - int argc, char * const argv[]) > -{ > - int ret = qemu_fwcfg_online_cpus(); > - if (ret < 0) { > - printf("QEMU fw_cfg interface not found\n"); > - return CMD_RET_FAILURE; > - } > - > - printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus()); > - > - return 0; > -} > - > -static int qemu_fwcfg_do_load(cmd_tbl_t *cmdtp, int flag, > - int argc, char * const argv[]) > -{ > - char *env; > - void *load_addr; > - void *initrd_addr; > - > - env = getenv("loadaddr"); > - load_addr = env ? > - (void *)simple_strtoul(env, NULL, 16) : > - (void *)CONFIG_LOADADDR; > - > - env = getenv("ramdiskaddr"); > - initrd_addr = env ? > - (void *)simple_strtoul(env, NULL, 16) : > - (void *)CONFIG_RAMDISK_ADDR; > - > - if (argc == 2) { > - load_addr = (void *)simple_strtoul(argv[0], NULL, 16); > - initrd_addr = (void *)simple_strtoul(argv[1], NULL, 16); > - } else if (argc == 1) { > - load_addr = (void *)simple_strtoul(argv[0], NULL, 16); > - } > - > - return qemu_fwcfg_setup_kernel(load_addr, initrd_addr); > -} > - > -static cmd_tbl_t fwcfg_commands[] = { > - U_BOOT_CMD_MKENT(list, 0, 1, qemu_fwcfg_do_list, "", ""), > - U_BOOT_CMD_MKENT(cpus, 0, 1, qemu_fwcfg_do_cpus, "", ""), > - U_BOOT_CMD_MKENT(load, 2, 1, qemu_fwcfg_do_load, "", ""), > -}; > - > -static int do_qemu_fw(cmd_tbl_t *cmdtp, int flag, int argc, char * const > argv[]) > -{ > - int ret; > - cmd_tbl_t *fwcfg_cmd; > - > - if (!fwcfg_present) { > - printf("QEMU fw_cfg interface not found\n"); > - return CMD_RET_USAGE; > - } > - > - fwcfg_cmd = find_cmd_tbl(argv[1], fwcfg_commands, > - ARRAY_SIZE(fwcfg_commands)); > - argc -= 2; > - argv += 2; > - if (!fwcfg_cmd || argc > fwcfg_cmd->maxargs) > - return CMD_RET_USAGE; > - > - ret = fwcfg_cmd->cmd(fwcfg_cmd, flag, argc, argv); > - > - return cmd_process_error(fwcfg_cmd, ret); > -} > - > -U_BOOT_CMD( > - qfw, 4, 1, do_qemu_fw, > - "QEMU firmware interface", > - "<command>\n" > - " - list : print firmware(s) currently > loaded\n" > - " - cpus : print online cpu number\n" > - " - load <kernel addr> <initrd addr> : load kernel and initrd (if > any), and setup for zboot\n" > -) > diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c > index 7ad0ee4..b41e4ec 100644 > --- a/arch/x86/cpu/qemu/qemu.c > +++ b/arch/x86/cpu/qemu/qemu.c > @@ -6,12 +6,12 @@ > > #include <common.h> > #include <pci.h> > +#include <qemu_fw_cfg.h> > #include <asm/irq.h> > #include <asm/post.h> > #include <asm/processor.h> > #include <asm/arch/device.h> > #include <asm/arch/qemu.h> > -#include <asm/fw_cfg.h> > > static bool i440fx; > > diff --git a/arch/x86/include/asm/fw_cfg.h b/arch/x86/include/asm/fw_cfg.h > deleted file mode 100644 > index e9450c6..0000000 > --- a/arch/x86/include/asm/fw_cfg.h > +++ /dev/null > @@ -1,157 +0,0 @@ > -/* > - * (C) Copyright 2015 Miao Yan <yanmiaob...@gmail.com> > - * > - * SPDX-License-Identifier: GPL-2.0+ > - */ > - > -#ifndef __FW_CFG__ > -#define __FW_CFG__ > - > -#define FW_CONTROL_PORT 0x510 > -#define FW_DATA_PORT 0x511 > -#define FW_DMA_PORT_LOW 0x514 > -#define FW_DMA_PORT_HIGH 0x518 > - > -#include <linux/list.h> > - > -enum qemu_fwcfg_items { > - FW_CFG_SIGNATURE = 0x00, > - FW_CFG_ID = 0x01, > - FW_CFG_UUID = 0x02, > - FW_CFG_RAM_SIZE = 0x03, > - FW_CFG_NOGRAPHIC = 0x04, > - FW_CFG_NB_CPUS = 0x05, > - FW_CFG_MACHINE_ID = 0x06, > - FW_CFG_KERNEL_ADDR = 0x07, > - FW_CFG_KERNEL_SIZE = 0x08, > - FW_CFG_KERNEL_CMDLINE = 0x09, > - FW_CFG_INITRD_ADDR = 0x0a, > - FW_CFG_INITRD_SIZE = 0x0b, > - FW_CFG_BOOT_DEVICE = 0x0c, > - FW_CFG_NUMA = 0x0d, > - FW_CFG_BOOT_MENU = 0x0e, > - FW_CFG_MAX_CPUS = 0x0f, > - FW_CFG_KERNEL_ENTRY = 0x10, > - FW_CFG_KERNEL_DATA = 0x11, > - FW_CFG_INITRD_DATA = 0x12, > - FW_CFG_CMDLINE_ADDR = 0x13, > - FW_CFG_CMDLINE_SIZE = 0x14, > - FW_CFG_CMDLINE_DATA = 0x15, > - FW_CFG_SETUP_ADDR = 0x16, > - FW_CFG_SETUP_SIZE = 0x17, > - FW_CFG_SETUP_DATA = 0x18, > - FW_CFG_FILE_DIR = 0x19, > - FW_CFG_FILE_FIRST = 0x20, > - FW_CFG_WRITE_CHANNEL = 0x4000, > - FW_CFG_ARCH_LOCAL = 0x8000, > - FW_CFG_INVALID = 0xffff, > -}; > - > -enum { > - BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1, > - BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2, > - BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3, > -}; > - > -enum { > - BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1, > - BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2, > -}; > - > -#define FW_CFG_FILE_SLOTS 0x10 > -#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS) > -#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) > - > -#define FW_CFG_MAX_FILE_PATH 56 > -#define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH > - > -#define QEMU_FW_CFG_SIGNATURE (('Q' << 24) | ('E' << 16) | ('M' << 8) | 'U') > - > -#define FW_CFG_DMA_ERROR (1 << 0) > -#define FW_CFG_DMA_READ (1 << 1) > -#define FW_CFG_DMA_SKIP (1 << 2) > -#define FW_CFG_DMA_SELECT (1 << 3) > - > -#define FW_CFG_DMA_ENABLED (1 << 1) > - > -struct fw_cfg_file { > - __be32 size; > - __be16 select; > - __be16 reserved; > - char name[FW_CFG_MAX_FILE_PATH]; > -}; > - > -struct fw_file { > - struct fw_cfg_file cfg; /* firmware file information */ > - unsigned long addr; /* firmware file in-memory address */ > - struct list_head list; /* list node to link to fw_list */ > -}; > - > -struct fw_cfg_dma_access { > - __be32 control; > - __be32 length; > - __be64 address; > -}; > - > -struct bios_linker_entry { > - __le32 command; > - union { > - /* > - * COMMAND_ALLOCATE - allocate a table from @alloc.file > - * subject to @alloc.align alignment (must be power of 2) > - * and @alloc.zone (can be HIGH or FSEG) requirements. > - * > - * Must appear exactly once for each file, and before > - * this file is referenced by any other command. > - */ > - struct { > - char file[BIOS_LINKER_LOADER_FILESZ]; > - __le32 align; > - uint8_t zone; > - } alloc; > - > - /* > - * COMMAND_ADD_POINTER - patch the table (originating from > - * @dest_file) at @pointer.offset, by adding a pointer to the > - * table originating from @src_file. 1,2,4 or 8 byte unsigned > - * addition is used depending on @pointer.size. > - */ > - struct { > - char dest_file[BIOS_LINKER_LOADER_FILESZ]; > - char src_file[BIOS_LINKER_LOADER_FILESZ]; > - __le32 offset; > - uint8_t size; > - } pointer; > - > - /* > - * COMMAND_ADD_CHECKSUM - calculate checksum of the range > - * specified by @cksum_start and @cksum_length fields, > - * and then add the value at @cksum.offset. > - * Checksum simply sums -X for each byte X in the range > - * using 8-bit math. > - */ > - struct { > - char file[BIOS_LINKER_LOADER_FILESZ]; > - __le32 offset; > - __le32 start; > - __le32 length; > - } cksum; > - > - /* padding */ > - char pad[124]; > - }; > -} __packed; > - > -/** > - * Initialize QEMU fw_cfg interface > - */ > -void qemu_fwcfg_init(void); > - > -/** > - * Get system cpu number > - * > - * @return: cpu number in system > - */ > -int qemu_fwcfg_online_cpus(void); > - > -#endif > diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c > index 790f6fb..bb4139a 100644 > --- a/arch/x86/lib/acpi_table.c > +++ b/arch/x86/lib/acpi_table.c > @@ -333,7 +333,7 @@ static void acpi_create_ssdt_generator(acpi_header_t > *ssdt, > > /* > * QEMU's version of write_acpi_tables is defined in > - * arch/x86/cpu/qemu/fw_cfg.c > + * arch/x86/cpu/qemu/acpi_table.c > */ > u32 write_acpi_tables(u32 start) > { > diff --git a/cmd/Kconfig b/cmd/Kconfig > index 9336752..c0fffe3 100644 > --- a/cmd/Kconfig > +++ b/cmd/Kconfig > @@ -593,6 +593,13 @@ config CMD_SOUND > sound init - set up sound system > sound play - play a sound > > +config CMD_QEMU_FW_CFG > + bool "qfw" > + depends on X86 > + help > + This provides access to the QEMU firmware interface. The main > + feature is to allow easy loading of files passed to qemu-system > + via -kernel / -initrd > endmenu This should really be "depends on QEMU". Sorry for my earlier comment. Thanks, Miao > > config CMD_BOOTSTAGE > diff --git a/cmd/Makefile b/cmd/Makefile > index f95759e..f99e67d 100644 > --- a/cmd/Makefile > +++ b/cmd/Makefile > @@ -105,6 +105,7 @@ endif > obj-y += pcmcia.o > obj-$(CONFIG_CMD_PORTIO) += portio.o > obj-$(CONFIG_CMD_PXE) += pxe.o > +obj-$(CONFIG_CMD_QEMU_FW_CFG) += qemu_fw_cfg.o > obj-$(CONFIG_CMD_READ) += read.o > obj-$(CONFIG_CMD_REGINFO) += reginfo.o > obj-$(CONFIG_CMD_REISER) += reiser.o > diff --git a/cmd/qemu_fw_cfg.c b/cmd/qemu_fw_cfg.c > new file mode 100644 > index 0000000..48ae476 > --- /dev/null > +++ b/cmd/qemu_fw_cfg.c > @@ -0,0 +1,343 @@ > +/* > + * (C) Copyright 2015 Miao Yan <yanmiaob...@gmail.com> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <command.h> > +#include <errno.h> > +#include <malloc.h> > +#include <qemu_fw_cfg.h> > +#include <asm/io.h> > +#include <linux/list.h> > + > +static bool fwcfg_present; > +static bool fwcfg_dma_present; > + > +static LIST_HEAD(fw_list); > + > +/* Read configuration item using fw_cfg PIO interface */ > +static void qemu_fwcfg_read_entry_pio(uint16_t entry, > + uint32_t size, void *address) > +{ > + uint32_t i = 0; > + uint8_t *data = address; > + > + /* > + * writting FW_CFG_INVALID will cause read operation to resume at > + * last offset, otherwise read will start at offset 0 > + */ > + if (entry != FW_CFG_INVALID) > + outw(entry, FW_CONTROL_PORT); > + while (size--) > + data[i++] = inb(FW_DATA_PORT); > +} > + > +/* Read configuration item using fw_cfg DMA interface */ > +static void qemu_fwcfg_read_entry_dma(uint16_t entry, > + uint32_t size, void *address) > +{ > + struct fw_cfg_dma_access dma; > + > + dma.length = cpu_to_be32(size); > + dma.address = cpu_to_be64((uintptr_t)address); > + dma.control = cpu_to_be32(FW_CFG_DMA_READ); > + > + /* > + * writting FW_CFG_INVALID will cause read operation to resume at > + * last offset, otherwise read will start at offset 0 > + */ > + if (entry != FW_CFG_INVALID) > + dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16)); > + > + barrier(); > + > + debug("qemu_fwcfg_dma_read_entry: addr %p, length %u control 0x%x\n", > + address, size, be32_to_cpu(dma.control)); > + > + outl(cpu_to_be32((uint32_t)&dma), FW_DMA_PORT_HIGH); > + > + while (be32_to_cpu(dma.control) & ~FW_CFG_DMA_ERROR) > + __asm__ __volatile__ ("pause"); > +} > + > +static bool qemu_fwcfg_present(void) > +{ > + uint32_t qemu; > + > + qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu); > + return be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE; > +} > + > +static bool qemu_fwcfg_dma_present(void) > +{ > + uint8_t dma_enabled; > + > + qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled); > + if (dma_enabled & FW_CFG_DMA_ENABLED) > + return true; > + > + return false; > +} > + > +void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address) > +{ > + if (fwcfg_dma_present) > + qemu_fwcfg_read_entry_dma(entry, length, address); > + else > + qemu_fwcfg_read_entry_pio(entry, length, address); > +} > + > +int qemu_fwcfg_online_cpus(void) > +{ > + uint16_t nb_cpus; > + > + if (!fwcfg_present) > + return -ENODEV; > + > + qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus); > + > + return le16_to_cpu(nb_cpus); > +} > + > +/* > + * This function prepares kernel for zboot. It loads kernel data > + * to 'load_addr', initrd to 'initrd_addr' and kernel command > + * line using qemu fw_cfg interface. > + */ > +static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) > +{ > + char *data_addr; > + uint32_t setup_size, kernel_size, cmdline_size, initrd_size; > + > + qemu_fwcfg_read_entry(FW_CFG_SETUP_SIZE, 4, &setup_size); > + qemu_fwcfg_read_entry(FW_CFG_KERNEL_SIZE, 4, &kernel_size); > + > + if (setup_size == 0 || kernel_size == 0) { > + printf("warning: no kernel available\n"); > + return -1; > + } > + > + data_addr = load_addr; > + qemu_fwcfg_read_entry(FW_CFG_SETUP_DATA, > + le32_to_cpu(setup_size), data_addr); > + data_addr += le32_to_cpu(setup_size); > + > + qemu_fwcfg_read_entry(FW_CFG_KERNEL_DATA, > + le32_to_cpu(kernel_size), data_addr); > + data_addr += le32_to_cpu(kernel_size); > + > + data_addr = initrd_addr; > + qemu_fwcfg_read_entry(FW_CFG_INITRD_SIZE, 4, &initrd_size); > + if (initrd_size == 0) { > + printf("warning: no initrd available\n"); > + } else { > + qemu_fwcfg_read_entry(FW_CFG_INITRD_DATA, > + le32_to_cpu(initrd_size), data_addr); > + data_addr += le32_to_cpu(initrd_size); > + } > + > + qemu_fwcfg_read_entry(FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); > + if (cmdline_size) { > + qemu_fwcfg_read_entry(FW_CFG_CMDLINE_DATA, > + le32_to_cpu(cmdline_size), data_addr); > + /* > + * if kernel cmdline only contains '\0', (e.g. no -append > + * when invoking qemu), do not update bootargs > + */ > + if (*data_addr != '\0') { > + if (setenv("bootargs", data_addr) < 0) > + printf("warning: unable to change > bootargs\n"); > + } > + } > + > + printf("loading kernel to address %p size %x", load_addr, > + le32_to_cpu(kernel_size)); > + if (initrd_size) > + printf(" initrd %p size %x\n", > + initrd_addr, > + le32_to_cpu(initrd_size)); > + else > + printf("\n"); > + > + return 0; > +} > + > +int qemu_fwcfg_read_firmware_list(void) > +{ > + int i; > + uint32_t count; > + struct fw_file *file; > + struct list_head *entry; > + > + /* don't read it twice */ > + if (!list_empty(&fw_list)) > + return 0; > + > + qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count); > + if (!count) > + return 0; > + > + count = be32_to_cpu(count); > + for (i = 0; i < count; i++) { > + file = malloc(sizeof(*file)); > + if (!file) { > + printf("error: allocating resource\n"); > + goto err; > + } > + qemu_fwcfg_read_entry(FW_CFG_INVALID, > + sizeof(struct fw_cfg_file), &file->cfg); > + file->addr = 0; > + list_add_tail(&file->list, &fw_list); > + } > + > + return 0; > + > +err: > + list_for_each(entry, &fw_list) { > + file = list_entry(entry, struct fw_file, list); > + free(file); > + } > + > + return -ENOMEM; > +} > + > +struct fw_file *qemu_fwcfg_find_file(const char *name) > +{ > + struct list_head *entry; > + struct fw_file *file; > + > + list_for_each(entry, &fw_list) { > + file = list_entry(entry, struct fw_file, list); > + if (!strcmp(file->cfg.name, name)) > + return file; > + } > + > + return NULL; > +} > + > +void qemu_fwcfg_free_files(void) > +{ > + struct fw_file *file; > + struct list_head *list; > + > + list_for_each(list, &fw_list) { > + file = list_entry(list, struct fw_file, list); > + if (file->addr) > + free((void *)file->addr); > + } > +} > + > +static int qemu_fwcfg_list_firmware(void) > +{ > + int ret; > + struct list_head *entry; > + struct fw_file *file; > + > + /* make sure fw_list is loaded */ > + ret = qemu_fwcfg_read_firmware_list(); > + if (ret) > + return ret; > + > + list_for_each(entry, &fw_list) { > + file = list_entry(entry, struct fw_file, list); > + printf("%-56s\n", file->cfg.name); > + } > + > + return 0; > +} > + > +void qemu_fwcfg_init(void) > +{ > + fwcfg_present = qemu_fwcfg_present(); > + if (fwcfg_present) > + fwcfg_dma_present = qemu_fwcfg_dma_present(); > +} > + > +static int qemu_fwcfg_do_list(cmd_tbl_t *cmdtp, int flag, > + int argc, char * const argv[]) > +{ > + if (qemu_fwcfg_list_firmware() < 0) > + return CMD_RET_FAILURE; > + > + return 0; > +} > + > +static int qemu_fwcfg_do_cpus(cmd_tbl_t *cmdtp, int flag, > + int argc, char * const argv[]) > +{ > + int ret = qemu_fwcfg_online_cpus(); > + if (ret < 0) { > + printf("QEMU fw_cfg interface not found\n"); > + return CMD_RET_FAILURE; > + } > + > + printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus()); > + > + return 0; > +} > + > +static int qemu_fwcfg_do_load(cmd_tbl_t *cmdtp, int flag, > + int argc, char * const argv[]) > +{ > + char *env; > + void *load_addr; > + void *initrd_addr; > + > + env = getenv("loadaddr"); > + load_addr = env ? > + (void *)simple_strtoul(env, NULL, 16) : > + (void *)CONFIG_LOADADDR; > + > + env = getenv("ramdiskaddr"); > + initrd_addr = env ? > + (void *)simple_strtoul(env, NULL, 16) : > + (void *)CONFIG_RAMDISK_ADDR; > + > + if (argc == 2) { > + load_addr = (void *)simple_strtoul(argv[0], NULL, 16); > + initrd_addr = (void *)simple_strtoul(argv[1], NULL, 16); > + } else if (argc == 1) { > + load_addr = (void *)simple_strtoul(argv[0], NULL, 16); > + } > + > + return qemu_fwcfg_setup_kernel(load_addr, initrd_addr); > +} > + > +static cmd_tbl_t fwcfg_commands[] = { > + U_BOOT_CMD_MKENT(list, 0, 1, qemu_fwcfg_do_list, "", ""), > + U_BOOT_CMD_MKENT(cpus, 0, 1, qemu_fwcfg_do_cpus, "", ""), > + U_BOOT_CMD_MKENT(load, 2, 1, qemu_fwcfg_do_load, "", ""), > +}; > + > +static int do_qemu_fw(cmd_tbl_t *cmdtp, int flag, int argc, char * const > argv[]) > +{ > + int ret; > + cmd_tbl_t *fwcfg_cmd; > + > + if (!fwcfg_present) { > + printf("QEMU fw_cfg interface not found\n"); > + return CMD_RET_USAGE; > + } > + > + fwcfg_cmd = find_cmd_tbl(argv[1], fwcfg_commands, > + ARRAY_SIZE(fwcfg_commands)); > + argc -= 2; > + argv += 2; > + if (!fwcfg_cmd || argc > fwcfg_cmd->maxargs) > + return CMD_RET_USAGE; > + > + ret = fwcfg_cmd->cmd(fwcfg_cmd, flag, argc, argv); > + > + return cmd_process_error(fwcfg_cmd, ret); > +} > + > +U_BOOT_CMD( > + qfw, 4, 1, do_qemu_fw, > + "QEMU firmware interface", > + "<command>\n" > + " - list : print firmware(s) currently > loaded\n" > + " - cpus : print online cpu number\n" > + " - load <kernel addr> <initrd addr> : load kernel and initrd (if > any), and setup for zboot\n" > +) > diff --git a/configs/qemu-x86_defconfig b/configs/qemu-x86_defconfig > index 53b1ff6..a813e5b 100644 > --- a/configs/qemu-x86_defconfig > +++ b/configs/qemu-x86_defconfig > @@ -20,6 +20,7 @@ CONFIG_CMD_DHCP=y > # CONFIG_CMD_NFS is not set > CONFIG_CMD_PING=y > CONFIG_CMD_TIME=y > +CONFIG_CMD_QEMU_FW_CFG=y > CONFIG_CMD_BOOTSTAGE=y > CONFIG_CMD_EXT2=y > CONFIG_CMD_EXT4=y > diff --git a/include/qemu_fw_cfg.h b/include/qemu_fw_cfg.h > new file mode 100644 > index 0000000..e21f150 > --- /dev/null > +++ b/include/qemu_fw_cfg.h > @@ -0,0 +1,162 @@ > +/* > + * (C) Copyright 2015 Miao Yan <yanmiaob...@gmail.com> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#ifndef __FW_CFG__ > +#define __FW_CFG__ > + > +#define FW_CONTROL_PORT 0x510 > +#define FW_DATA_PORT 0x511 > +#define FW_DMA_PORT_LOW 0x514 > +#define FW_DMA_PORT_HIGH 0x518 > + > +#include <linux/list.h> > + > +enum qemu_fwcfg_items { > + FW_CFG_SIGNATURE = 0x00, > + FW_CFG_ID = 0x01, > + FW_CFG_UUID = 0x02, > + FW_CFG_RAM_SIZE = 0x03, > + FW_CFG_NOGRAPHIC = 0x04, > + FW_CFG_NB_CPUS = 0x05, > + FW_CFG_MACHINE_ID = 0x06, > + FW_CFG_KERNEL_ADDR = 0x07, > + FW_CFG_KERNEL_SIZE = 0x08, > + FW_CFG_KERNEL_CMDLINE = 0x09, > + FW_CFG_INITRD_ADDR = 0x0a, > + FW_CFG_INITRD_SIZE = 0x0b, > + FW_CFG_BOOT_DEVICE = 0x0c, > + FW_CFG_NUMA = 0x0d, > + FW_CFG_BOOT_MENU = 0x0e, > + FW_CFG_MAX_CPUS = 0x0f, > + FW_CFG_KERNEL_ENTRY = 0x10, > + FW_CFG_KERNEL_DATA = 0x11, > + FW_CFG_INITRD_DATA = 0x12, > + FW_CFG_CMDLINE_ADDR = 0x13, > + FW_CFG_CMDLINE_SIZE = 0x14, > + FW_CFG_CMDLINE_DATA = 0x15, > + FW_CFG_SETUP_ADDR = 0x16, > + FW_CFG_SETUP_SIZE = 0x17, > + FW_CFG_SETUP_DATA = 0x18, > + FW_CFG_FILE_DIR = 0x19, > + FW_CFG_FILE_FIRST = 0x20, > + FW_CFG_WRITE_CHANNEL = 0x4000, > + FW_CFG_ARCH_LOCAL = 0x8000, > + FW_CFG_INVALID = 0xffff, > +}; > + > +enum { > + BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1, > + BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2, > + BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3, > +}; > + > +enum { > + BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1, > + BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2, > +}; > + > +#define FW_CFG_FILE_SLOTS 0x10 > +#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS) > +#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) > + > +#define FW_CFG_MAX_FILE_PATH 56 > +#define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH > + > +#define QEMU_FW_CFG_SIGNATURE (('Q' << 24) | ('E' << 16) | ('M' << 8) | 'U') > + > +#define FW_CFG_DMA_ERROR (1 << 0) > +#define FW_CFG_DMA_READ (1 << 1) > +#define FW_CFG_DMA_SKIP (1 << 2) > +#define FW_CFG_DMA_SELECT (1 << 3) > + > +#define FW_CFG_DMA_ENABLED (1 << 1) > + > +struct fw_cfg_file { > + __be32 size; > + __be16 select; > + __be16 reserved; > + char name[FW_CFG_MAX_FILE_PATH]; > +}; > + > +struct fw_file { > + struct fw_cfg_file cfg; /* firmware file information */ > + unsigned long addr; /* firmware file in-memory address */ > + struct list_head list; /* list node to link to fw_list */ > +}; > + > +struct fw_cfg_dma_access { > + __be32 control; > + __be32 length; > + __be64 address; > +}; > + > +struct bios_linker_entry { > + __le32 command; > + union { > + /* > + * COMMAND_ALLOCATE - allocate a table from @alloc.file > + * subject to @alloc.align alignment (must be power of 2) > + * and @alloc.zone (can be HIGH or FSEG) requirements. > + * > + * Must appear exactly once for each file, and before > + * this file is referenced by any other command. > + */ > + struct { > + char file[BIOS_LINKER_LOADER_FILESZ]; > + __le32 align; > + uint8_t zone; > + } alloc; > + > + /* > + * COMMAND_ADD_POINTER - patch the table (originating from > + * @dest_file) at @pointer.offset, by adding a pointer to the > + * table originating from @src_file. 1,2,4 or 8 byte unsigned > + * addition is used depending on @pointer.size. > + */ > + struct { > + char dest_file[BIOS_LINKER_LOADER_FILESZ]; > + char src_file[BIOS_LINKER_LOADER_FILESZ]; > + __le32 offset; > + uint8_t size; > + } pointer; > + > + /* > + * COMMAND_ADD_CHECKSUM - calculate checksum of the range > + * specified by @cksum_start and @cksum_length fields, > + * and then add the value at @cksum.offset. > + * Checksum simply sums -X for each byte X in the range > + * using 8-bit math. > + */ > + struct { > + char file[BIOS_LINKER_LOADER_FILESZ]; > + __le32 offset; > + __le32 start; > + __le32 length; > + } cksum; > + > + /* padding */ > + char pad[124]; > + }; > +} __packed; > + > +/** > + * Initialize QEMU fw_cfg interface > + */ > +void qemu_fwcfg_init(void); > + > +void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address); > +int qemu_fwcfg_read_firmware_list(void); > +struct fw_file *qemu_fwcfg_find_file(const char *name); > +void qemu_fwcfg_free_files(void); > + > +/** > + * Get system cpu number > + * > + * @return: cpu number in system > + */ > +int qemu_fwcfg_online_cpus(void); > + > +#endif > -- > 1.9.1 > _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot