Message ID | 1461980340-787-1-git-send-email-trini@konsulko.com |
---|---|
State | Changes Requested |
Delegated to: | Tom Rini |
Headers | show |
Hi Tom, 2016-04-30 9:39 GMT+08:00 Tom Rini <trini@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 <trini@konsulko.com> > --- > The QEMU firmware interface is more generic feature than just x86/qemu so > we need to start by moving the code out of arch/x86 and into cmd > > 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/qemu.c | 2 +- > arch/x86/lib/acpi_table.c | 2 +- > cmd/Kconfig | 6 + > cmd/Makefile | 1 + > arch/x86/cpu/qemu/fw_cfg.c => cmd/qemu_fw_cfg.c | 245 +-------------------- > configs/qemu-x86_defconfig | 1 + > .../include/asm/fw_cfg.h => include/qemu_fw_cfg.h | 5 + > 11 files changed, 271 insertions(+), 241 deletions(-) > create mode 100644 arch/x86/cpu/qemu/acpi_table.c > rename arch/x86/cpu/qemu/fw_cfg.c => cmd/qemu_fw_cfg.c (53%) > rename arch/x86/include/asm/fw_cfg.h => include/qemu_fw_cfg.h (94%) > > 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 <yanmiaobest@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/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/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..7f00684 100644 > --- a/cmd/Kconfig > +++ b/cmd/Kconfig > @@ -593,6 +593,12 @@ config CMD_SOUND > sound init - set up sound system > sound play - play a sound > > +config CMD_QEMU_FW_CFG > + bool "qfw" > + 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 Can you limit this only to x86 for now ? Otherwise, it'll break build on other architectures like ARM if this option is enabled, for example, inb()/outw() have to be replaced by readl() on ARM to compile and function; there are also some board/arch specific macros used by this file. Or can we make this change until some other architectures want this ? 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/arch/x86/cpu/qemu/fw_cfg.c b/cmd/qemu_fw_cfg.c > similarity index 53% > rename from arch/x86/cpu/qemu/fw_cfg.c > rename to cmd/qemu_fw_cfg.c > index 2e2794e..48ae476 100644 > --- a/arch/x86/cpu/qemu/fw_cfg.c > +++ b/cmd/qemu_fw_cfg.c > @@ -8,12 +8,9 @@ > #include <command.h> > #include <errno.h> > #include <malloc.h> > +#include <qemu_fw_cfg.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; > @@ -84,8 +81,7 @@ static bool qemu_fwcfg_dma_present(void) > return false; > } > > -static void qemu_fwcfg_read_entry(uint16_t entry, > - uint32_t length, void *address) > +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); > @@ -168,7 +164,7 @@ static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) > return 0; > } > > -static int qemu_fwcfg_read_firmware_list(void) > +int qemu_fwcfg_read_firmware_list(void) > { > int i; > uint32_t count; > @@ -207,8 +203,7 @@ err: > return -ENOMEM; > } > > -#ifdef CONFIG_QEMU_ACPI_TABLE > -static struct fw_file *qemu_fwcfg_find_file(const char *name) > +struct fw_file *qemu_fwcfg_find_file(const char *name) > { > struct list_head *entry; > struct fw_file *file; > @@ -222,239 +217,17 @@ static struct fw_file *qemu_fwcfg_find_file(const char *name) > 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) > +void qemu_fwcfg_free_files(void) > { > 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; > + list_for_each(list, &fw_list) { > + file = list_entry(list, struct fw_file, list); > + if (file->addr) > + free((void *)file->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) > { > 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/arch/x86/include/asm/fw_cfg.h b/include/qemu_fw_cfg.h > similarity index 94% > rename from arch/x86/include/asm/fw_cfg.h > rename to include/qemu_fw_cfg.h > index e9450c6..e21f150 100644 > --- a/arch/x86/include/asm/fw_cfg.h > +++ b/include/qemu_fw_cfg.h > @@ -147,6 +147,11 @@ struct bios_linker_entry { > */ > 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 > * > -- > 1.9.1 > > _______________________________________________ > U-Boot mailing list > U-Boot@lists.denx.de > http://lists.denx.de/mailman/listinfo/u-boot
On Tue, May 03, 2016 at 10:33:38AM +0800, Miao Yan wrote: > Hi Tom, > > 2016-04-30 9:39 GMT+08:00 Tom Rini <trini@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 <trini@konsulko.com> > > --- > > The QEMU firmware interface is more generic feature than just x86/qemu so > > we need to start by moving the code out of arch/x86 and into cmd > > > > 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/qemu.c | 2 +- > > arch/x86/lib/acpi_table.c | 2 +- > > cmd/Kconfig | 6 + > > cmd/Makefile | 1 + > > arch/x86/cpu/qemu/fw_cfg.c => cmd/qemu_fw_cfg.c | 245 +-------------------- > > configs/qemu-x86_defconfig | 1 + > > .../include/asm/fw_cfg.h => include/qemu_fw_cfg.h | 5 + > > 11 files changed, 271 insertions(+), 241 deletions(-) > > create mode 100644 arch/x86/cpu/qemu/acpi_table.c > > rename arch/x86/cpu/qemu/fw_cfg.c => cmd/qemu_fw_cfg.c (53%) > > rename arch/x86/include/asm/fw_cfg.h => include/qemu_fw_cfg.h (94%) > > > > 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 <yanmiaobest@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/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/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..7f00684 100644 > > --- a/cmd/Kconfig > > +++ b/cmd/Kconfig > > @@ -593,6 +593,12 @@ config CMD_SOUND > > sound init - set up sound system > > sound play - play a sound > > > > +config CMD_QEMU_FW_CFG > > + bool "qfw" > > + 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 > > > Can you limit this only to x86 for now ? Otherwise, it'll break build > on other architectures like ARM if this option is enabled, for > example, inb()/outw() have to be replaced by readl() on ARM to compile > and function; there are also some board/arch specific macros used by > this file. > > Or can we make this change until some other architectures want this ? I can make this depend on X86 for now and then drop that with the patches which enable it for more architectures.
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 <yanmiaobest@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/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/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..7f00684 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -593,6 +593,12 @@ config CMD_SOUND sound init - set up sound system sound play - play a sound +config CMD_QEMU_FW_CFG + bool "qfw" + 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 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/arch/x86/cpu/qemu/fw_cfg.c b/cmd/qemu_fw_cfg.c similarity index 53% rename from arch/x86/cpu/qemu/fw_cfg.c rename to cmd/qemu_fw_cfg.c index 2e2794e..48ae476 100644 --- a/arch/x86/cpu/qemu/fw_cfg.c +++ b/cmd/qemu_fw_cfg.c @@ -8,12 +8,9 @@ #include <command.h> #include <errno.h> #include <malloc.h> +#include <qemu_fw_cfg.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; @@ -84,8 +81,7 @@ static bool qemu_fwcfg_dma_present(void) return false; } -static void qemu_fwcfg_read_entry(uint16_t entry, - uint32_t length, void *address) +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); @@ -168,7 +164,7 @@ static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) return 0; } -static int qemu_fwcfg_read_firmware_list(void) +int qemu_fwcfg_read_firmware_list(void) { int i; uint32_t count; @@ -207,8 +203,7 @@ err: return -ENOMEM; } -#ifdef CONFIG_QEMU_ACPI_TABLE -static struct fw_file *qemu_fwcfg_find_file(const char *name) +struct fw_file *qemu_fwcfg_find_file(const char *name) { struct list_head *entry; struct fw_file *file; @@ -222,239 +217,17 @@ static struct fw_file *qemu_fwcfg_find_file(const char *name) 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) +void qemu_fwcfg_free_files(void) { 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; + list_for_each(list, &fw_list) { + file = list_entry(list, struct fw_file, list); + if (file->addr) + free((void *)file->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) { 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/arch/x86/include/asm/fw_cfg.h b/include/qemu_fw_cfg.h similarity index 94% rename from arch/x86/include/asm/fw_cfg.h rename to include/qemu_fw_cfg.h index e9450c6..e21f150 100644 --- a/arch/x86/include/asm/fw_cfg.h +++ b/include/qemu_fw_cfg.h @@ -147,6 +147,11 @@ struct bios_linker_entry { */ 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 *
- 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 <trini@konsulko.com> --- The QEMU firmware interface is more generic feature than just x86/qemu so we need to start by moving the code out of arch/x86 and into cmd 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/qemu.c | 2 +- arch/x86/lib/acpi_table.c | 2 +- cmd/Kconfig | 6 + cmd/Makefile | 1 + arch/x86/cpu/qemu/fw_cfg.c => cmd/qemu_fw_cfg.c | 245 +-------------------- configs/qemu-x86_defconfig | 1 + .../include/asm/fw_cfg.h => include/qemu_fw_cfg.h | 5 + 11 files changed, 271 insertions(+), 241 deletions(-) create mode 100644 arch/x86/cpu/qemu/acpi_table.c rename arch/x86/cpu/qemu/fw_cfg.c => cmd/qemu_fw_cfg.c (53%) rename arch/x86/include/asm/fw_cfg.h => include/qemu_fw_cfg.h (94%)