[U-Boot] [PATCH 0/7] add fw_cfg interface support for qemu-x86 targets
Saket Sinha
saket.sinha89 at gmail.com
Tue Dec 29 08:31:43 CET 2015
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
More information about the U-Boot
mailing list