[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