Hi Miao,
> > Are you referring to "Initial fw_cfg support for qemu-x86 targets" ? > I didn't find any patch in that email you sent to me. > YES. The first thread of the mail contains the patch. Anyways I am copy-pasting patch below. > But I did give Seabios a try and it works. So I don't think there's > anything wrong with the acpi tables generated by qemu. It seems > other than just loading files into memory, guests are supposed > to perform some "fix-ups" to those table according to qemu's instructions. > Good. Then there may be some error in my patch. > But I can't say what's wrong without seeing your original patches. > Please find the patch below - --- arch/x86/cpu/qemu/Makefile | 2 +- arch/x86/cpu/qemu/fw_cfg.c | 288 ++++++++++++++++++++++++++++++++++++++++++ arch/x86/include/asm/fw_cfg.h | 81 ++++++++++++ arch/x86/lib/tables.c | 3 + 4 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 arch/x86/cpu/qemu/fw_cfg.c create mode 100644 arch/x86/include/asm/fw_cfg.h diff --git a/arch/x86/cpu/qemu/Makefile b/arch/x86/cpu/qemu/Makefile index be79723..52a82f0 100644 --- a/arch/x86/cpu/qemu/Makefile +++ b/arch/x86/cpu/qemu/Makefile @@ -4,5 +4,5 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y += car.o dram.o qemu.o +obj-y += car.o dram.o qemu.o fw_cfg.o obj-$(CONFIG_PCI) += pci.o diff --git a/arch/x86/cpu/qemu/fw_cfg.c b/arch/x86/cpu/qemu/fw_cfg.c new file mode 100644 index 0000000..6e8c4d3 --- /dev/null +++ b/arch/x86/cpu/qemu/fw_cfg.c @@ -0,0 +1,288 @@ +#include <asm/fw_cfg.h> +#include <linux/string.h> +#include <malloc.h> +#include <common.h> +#include <asm/post.h> +#include <asm/arch/qemu.h> + + +#define FW_CFG_PORT_CTL 0x0510 +#define FW_CFG_PORT_DATA 0x0511 + +static unsigned char fw_cfg_detected = 0xff; +static FWCfgFiles *fw_files; + +static int fw_cfg_present(void) +{ + static const char qsig[] = "QEMU"; + unsigned char sig[4]; + + if (fw_cfg_detected == 0xff) { + fw_cfg_get(FW_CFG_SIGNATURE, sig, sizeof(sig)); + fw_cfg_detected = (memcmp(sig, qsig, 4) == 0) ? 1 : 0; + } + return fw_cfg_detected; +} + +void fw_cfg_get(int entry, void *dst, int dstlen) +{ + outw(entry, FW_CFG_PORT_CTL); + insb(FW_CFG_PORT_DATA, dst, dstlen); +} + +static void fw_cfg_init_file(void) +{ + u32 i, size, count = 0; + + if (fw_files != NULL) + return; + + fw_cfg_get(FW_CFG_FILE_DIR, &count, sizeof(count)); + count = swab32(count); + size = count * sizeof(FWCfgFile) + sizeof(count); + fw_files = malloc(size); + fw_cfg_get(FW_CFG_FILE_DIR, fw_files, size); + fw_files->count = swab32(fw_files->count); + for (i = 0; i < count; i++) { + fw_files->f[i].size = swab32(fw_files->f[i].size); + fw_files->f[i].select = swab16(fw_files->f[i].select); + } +} + +static FWCfgFile *fw_cfg_find_file(const char *name) +{ + int i; + + fw_cfg_init_file(); + for (i = 0; i < fw_files->count; i++) + if (strcmp(fw_files->f[i].name, name) == 0) + return fw_files->f + i; + return NULL; +} + +int fw_cfg_check_file(const char *name) +{ + FWCfgFile *file; + + if (!fw_cfg_present()) + return -1; + file = fw_cfg_find_file(name); + if (!file) + return -1; + return file->size; +} + +void fw_cfg_load_file(const char *name, void *dst) +{ + FWCfgFile *file; + + if (!fw_cfg_present()) + return; + file = fw_cfg_find_file(name); + if (!file) + return; + fw_cfg_get(file->select, dst, file->size); +} + +int fw_cfg_max_cpus(void) +{ + unsigned short max_cpus; + + if (!fw_cfg_present()) + return -1; + + fw_cfg_get(FW_CFG_MAX_CPUS, &max_cpus, sizeof(max_cpus)); + return max_cpus; +} + +/* ---------------------------------------------------------------------- */ + +/* + * Starting with release 1.7 qemu provides acpi tables via fw_cfg. + * Main advantage is that new (virtual) hardware which needs acpi + * support JustWorks[tm] without having to patch & update the firmware + * accordingly. + * + * Qemu provides a etc/table-loader file with instructions for the + * firmware: + * - A "load" instruction to fetch acpi data from fw_cfg. + * - A "pointer" instruction to patch a pointer. This is needed to + * get table-to-table references right, it is basically a + * primitive dynamic linker for acpi tables. + * - A "checksum" instruction to generate acpi table checksums. + * + * If a etc/table-loader file is found we'll go try loading the acpi + * tables from fw_cfg. + */ + +#define BIOS_LINKER_LOADER_FILESZ 56 + +struct BiosLinkerLoaderEntry { + uint32_t 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]; + uint32_t 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]; + uint32_t 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]; + uint32_t offset; + uint32_t start; + uint32_t length; + } cksum; + + /* padding */ + char pad[124]; + }; +} __attribute__((packed)); +typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry; + +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, +}; + +unsigned long fw_cfg_acpi_tables(unsigned long start) +{ + BiosLinkerLoaderEntry *s; + unsigned long *addrs, current; + uint32_t *ptr4; + uint64_t *ptr8; + int rc, i, j, src, dst, max; + + rc = fw_cfg_check_file("etc/table-loader"); + if (rc < 0) + return 0; + + + max = rc / sizeof(*s); + s = malloc(rc); + addrs = malloc(max * sizeof(*addrs)); + fw_cfg_load_file("etc/table-loader", s); + + current = start; + for (i = 0; i < max && s[i].command != 0; i++) { + switch (s[i].command) { + case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: + current = ALIGN(current, s[i].alloc.align); + rc = fw_cfg_check_file(s[i].alloc.file); + if (rc < 0) + goto err; + fw_cfg_load_file(s[i].alloc.file, (void*)current); + addrs[i] = current; + current += rc; + break; + + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: + src = -1; dst = -1; + for (j = 0; j < i; j++) { + if (s[j].command != BIOS_LINKER_LOADER_COMMAND_ALLOCATE) + continue; + if (strcmp(s[j].alloc.file, s[i].pointer.dest_file) == 0) + dst = j; + if (strcmp(s[j].alloc.file, s[i].pointer.src_file) == 0) + src = j; + } + if (src == -1 || dst == -1) + goto err; + + switch (s[i].pointer.size) { + case 4: + ptr4 = (uint32_t*)(addrs[dst] + s[i].pointer.offset); + *ptr4 += addrs[src]; + break; + + case 8: + ptr8 = (uint64_t*)(addrs[dst] + s[i].pointer.offset); + *ptr8 += addrs[src]; + break; + + default: + /* + * Should not happen. acpi knows 1 and 2 byte ptrs + * too, but we are operating with 32bit offsets which + * would simply not fit in there ... + */ + goto err; + } + break; + + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: + dst = -1; + for (j = 0; j < i; j++) { + if (s[j].command != BIOS_LINKER_LOADER_COMMAND_ALLOCATE) + continue; + if (strcmp(s[j].alloc.file, s[i].cksum.file) == 0) + dst = j; + } + if (dst == -1) + goto err; + + ptr4 = (uint32_t*)(addrs[dst] + s[i].cksum.offset); + *ptr4 = 0; + *ptr4 = acpi_checksum((void *)(addrs[dst] + s[i].cksum.start), + s[i].cksum.length); + break; + + default: + goto err; + }; + } + + free(s); + free(addrs); + return ALIGN(current, 16); + +err: + free(s); + free(addrs); + return 0; +} + +u8 acpi_checksum(u8 *table, u32 length) +{ + u8 ret=0; + + while(length --){ + ret += *table; + table++; + } + return -ret; +} + diff --git a/arch/x86/include/asm/fw_cfg.h b/arch/x86/include/asm/fw_cfg.h new file mode 100644 index 0000000..9f512ca --- /dev/null +++ b/arch/x86/include/asm/fw_cfg.h @@ -0,0 +1,81 @@ +#include <linux/types.h> + +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID 0x02 +#define FW_CFG_RAM_SIZE 0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA 0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS 0x0f +#define FW_CFG_KERNEL_ENTRY 0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR 0x13 +#define FW_CFG_CMDLINE_SIZE 0x14 +#define FW_CFG_CMDLINE_DATA 0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR 0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS 0x10 +#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST+FW_CFG_FILE_SLOTS) + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) + +#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) +#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) +#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2) +#define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3) +#define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4) + +#define FW_CFG_INVALID 0xffff + +typedef struct FWCfgFile { + uint32_t size; /* file size */ + uint16_t select; /* write this to 0x510 to read it */ + uint16_t reserved; + char name[56]; +} FWCfgFile; + +typedef struct FWCfgFiles { + uint32_t count; + FWCfgFile f[]; +} FWCfgFiles; + +typedef struct FwCfgE820Entry { + uint64_t address; + uint64_t length; + uint32_t type; +} FwCfgE820Entry __attribute((__aligned__(4))); + + +#define SMBIOS_FIELD_ENTRY 0 +#define SMBIOS_TABLE_ENTRY 1 + +typedef struct FwCfgSmbios { + uint16_t length; + uint8_t headertype; + uint8_t tabletype; + uint16_t fieldoffset; +} FwCfgSmbios; + + +void fw_cfg_get(int entry, void *dst, int dstlen); +int fw_cfg_check_file(const char *name); +void fw_cfg_load_file(const char *name, void *dst); +int fw_cfg_max_cpus(void); +unsigned long fw_cfg_acpi_tables(unsigned long start); +u8 acpi_checksum(u8 *table, u32 length); diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c index 8031201..eb49478 100644 --- a/arch/x86/lib/tables.c +++ b/arch/x86/lib/tables.c @@ -7,6 +7,7 @@ #include <common.h> #include <asm/sfi.h> #include <asm/tables.h> +#include <asm/fw_cfg.h> u8 table_compute_checksum(void *v, int len) { @@ -32,4 +33,6 @@ void write_tables(void) rom_table_end = write_sfi_table(rom_table_end); rom_table_end = ALIGN(rom_table_end, 1024); #endif + rom_table_end = fw_cfg_acpi_tables(rom_table_end); + rom_table_end = ALIGN(rom_table_end, 1024); } Apart from this, there is Bin's fix for a bug in the patch - > diff --git a/arch/x86/cpu/qemu/fw_cfg.c b/arch/x86/cpu/qemu/fw_cfg.c > index c6ef5cd..b5f3e0a 100644 > --- a/arch/x86/cpu/qemu/fw_cfg.c > +++ b/arch/x86/cpu/qemu/fw_cfg.c > @@ -34,7 +34,8 @@ void fw_cfg_get(int entry, void *dst, int dstlen) > > static void fw_cfg_init_file(void) > { > - u32 i, size, count = 0; > + u32 i, size; > + volatile u32 count = 0; > > if (fw_files != NULL) > return; > Lets see if we can get this patch functional and able to utilize fw_cfg to load tables for U-boot like SeaBIOS for qemu targets. Regards, Saket Sinha _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot