[U-Boot] [PATCH v2 0/8] x86: qemu: add fw_cfg interface support for qemu-x86 targets

The fw_cfg interface provided by QEMU allow guests to retrieve various information about the system, e.g. cpu number, variaous firmware data, kernel setup, etc. The fw_cfg interface can be accessed through 3 IO ports (on x86), using x86 in/out instructions.
- 0x510: select configuration items to access - 0x511: reading this port will return data selected in 0x510 - 0x514: this can be used to detect if DMA interface is available
If fw_cfg DMA interface is available, it can be used to accelerate accesses.
This patchset adds the following supports for qemu-x86 targets:
+ the fw_cfg driver itself
+ add a U-Boot command 'fw' to support direct accessing kernel informtion from fw_cfg interface, this saves the time of loading them from hard disk or network again, through emulated devices.
+ use fw_cfg to get cpu number at runtime, so smp boot no longer relies on the cpu node hard-coded in dts files.
Changes in v2: - rebase to u-boot-x86/next - various cleanups
Miao Yan (8): x86: qemu: add fw_cfg support x86: qemu: add a cpu uclass driver for qemu target x86: fix a typo in function name x86: use actual CPU number for allocating memory x86: qemu: add qemu_fwcfg_fdt_fixup() x86: qemu: fix up cpu node in device tree x86: reserve more spaces for dtb x86: qemu: add documentaion for the fw_cfg interface
arch/x86/cpu/mp_init.c | 12 +- arch/x86/cpu/qemu/Makefile | 2 +- arch/x86/cpu/qemu/cpu.c | 57 ++++++++ arch/x86/cpu/qemu/fw_cfg.c | 306 +++++++++++++++++++++++++++++++++++++++ arch/x86/cpu/qemu/fw_cfg.h | 108 ++++++++++++++ arch/x86/cpu/qemu/qemu.c | 7 + arch/x86/dts/Makefile | 2 +- arch/x86/dts/qemu-x86_i440fx.dts | 19 +-- arch/x86/dts/qemu-x86_q35.dts | 19 +-- doc/README.x86 | 36 ++++- 10 files changed, 521 insertions(+), 47 deletions(-) create mode 100644 arch/x86/cpu/qemu/cpu.c create mode 100644 arch/x86/cpu/qemu/fw_cfg.c create mode 100644 arch/x86/cpu/qemu/fw_cfg.h

The QEMU fw_cfg interface allows the guest to retrieve various data information from QEMU. For example, APCI/SMBios tables, number of online cpus, kernel data and command line, etc.
This patch adds support for QEMU fw_cfg interface.
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- Changes in v2: - rewrite command handling logic - various cleanups
arch/x86/cpu/qemu/Makefile | 2 +- arch/x86/cpu/qemu/fw_cfg.c | 241 +++++++++++++++++++++++++++++++++++++++++++++ arch/x86/cpu/qemu/fw_cfg.h | 97 ++++++++++++++++++ arch/x86/cpu/qemu/qemu.c | 3 + 4 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 arch/x86/cpu/qemu/fw_cfg.c create mode 100644 arch/x86/cpu/qemu/fw_cfg.h
diff --git a/arch/x86/cpu/qemu/Makefile b/arch/x86/cpu/qemu/Makefile index 3f3958a..ad424ec 100644 --- a/arch/x86/cpu/qemu/Makefile +++ b/arch/x86/cpu/qemu/Makefile @@ -7,5 +7,5 @@ ifndef CONFIG_EFI_STUB obj-y += car.o dram.o endif -obj-y += qemu.o +obj-y += qemu.o fw_cfg.o obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o dsdt.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..5f5cc68 --- /dev/null +++ b/arch/x86/cpu/qemu/fw_cfg.c @@ -0,0 +1,241 @@ +/* + * (C) Copyright 2015 Miao Yan yanmiaoebst@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <malloc.h> +#include <asm/io.h> +#include "fw_cfg.h" + +static bool fwcfg_present; +static bool fwcfg_dma_present; + +static void qemu_fwcfg_read_entry_pio(uint16_t entry, + uint32_t size, void *address) +{ + uint32_t i = 0; + uint8_t *data = address; + + if (entry != FW_CFG_INVALID) + outw(entry, FW_CONTROL_PORT); + while (size--) + data[i++] = inb(FW_DATA_PORT); +} + +static void qemu_fwcfg_read_entry_dma(uint16_t entry, + uint32_t size, void *address) +{ + struct fw_cfg_dma_access dma; + + dma.length = cpu_to_be32(size); + dma.address = cpu_to_be64((uintptr_t)address); + dma.control = cpu_to_be32(FW_CFG_DMA_READ); + if (entry != FW_CFG_INVALID) + dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16)); + + barrier(); + + debug("qemu_fwcfg_dma_read_entry: addr %p, length %u control 0x%x\n", + address, size, be32_to_cpu(dma.control)); + + outl(cpu_to_be32((uint32_t)&dma), FW_DMA_PORT_HIGH); + + while (dma.control & ~FW_CFG_DMA_ERROR) + __asm__ __volatile__ ("pause"); +} + +static bool qemu_fwcfg_present(void) +{ + uint32_t qemu; + + qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu); + return be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE; +} + +static bool qemu_fwcfg_dma_present(void) +{ + uint8_t dma_enabled; + + qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled); + if (dma_enabled & FW_CFG_DMA_ENABLED) + return 1; + + return 0; +} + +static void qemu_fwcfg_read_entry(uint16_t entry, + uint32_t length, void *address) +{ + if (fwcfg_dma_present) + qemu_fwcfg_read_entry_dma(entry, length, address); + else + qemu_fwcfg_read_entry_pio(entry, length, address); +} + +int qemu_fwcfg_online_cpus(void) +{ + uint16_t nb_cpus; + + if (!fwcfg_present) + return 1; + + qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus); + + return le16_to_cpu(nb_cpus); +} + +static int qemu_fwcfg_setup_kernel(void *load_addr) +{ + char *data_addr; + uint32_t setup_size, kernel_size, cmdline_size, initrd_size; + + qemu_fwcfg_read_entry(FW_CFG_SETUP_SIZE, 4, &setup_size); + qemu_fwcfg_read_entry(FW_CFG_KERNEL_SIZE, 4, &kernel_size); + + if (setup_size == 0 || kernel_size == 0) { + printf("warning: no kernel available\n"); + return -1; + } + + data_addr = load_addr; + qemu_fwcfg_read_entry(FW_CFG_SETUP_DATA, + le32_to_cpu(setup_size), data_addr); + data_addr += le32_to_cpu(setup_size); + + qemu_fwcfg_read_entry(FW_CFG_KERNEL_DATA, + le32_to_cpu(kernel_size), data_addr); + data_addr += le32_to_cpu(kernel_size); + + qemu_fwcfg_read_entry(FW_CFG_INITRD_SIZE, 4, &initrd_size); + if (initrd_size == 0) { + printf("warning: no initrd available\n"); + } else { + qemu_fwcfg_read_entry(FW_CFG_INITRD_DATA, + le32_to_cpu(initrd_size), data_addr); + data_addr += le32_to_cpu(initrd_size); + } + + qemu_fwcfg_read_entry(FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); + qemu_fwcfg_read_entry(FW_CFG_CMDLINE_DATA, + le32_to_cpu(cmdline_size), data_addr); + + printf("loading kernel to address %p", load_addr); + if (initrd_size) + printf(" initrd %p\n", + (char *)load_addr + + le32_to_cpu(setup_size) + + le32_to_cpu(kernel_size)); + else + printf("\n"); + + return setenv("bootargs", data_addr); +} + +static int qemu_fwcfg_list_firmware(void) +{ + int i; + uint32_t count; + struct fw_cfg_files *files; + + qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count); + if (!count) + return 0; + + count = be32_to_cpu(count); + files = malloc(count * sizeof(struct fw_cfg_file)); + if (!files) + return -ENOMEM; + + files->count = count; + qemu_fwcfg_read_entry(FW_CFG_INVALID, + count * sizeof(struct fw_cfg_file), + files->files); + + for (i = 0; i < files->count; i++) + printf("%-56s\n", files->files[i].name); + free(files); + return 0; +} + +void qemu_fwcfg_init(void) +{ + fwcfg_present = qemu_fwcfg_present(); + if (fwcfg_present) + fwcfg_dma_present = qemu_fwcfg_dma_present(); +} + +static int qemu_fwcfg_do_list(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + qemu_fwcfg_list_firmware(); + + return 0; +} + +static int qemu_fwcfg_do_cpus(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + printf("%u cpu(s) online\n", qemu_fwcfg_online_cpus()); + + return 0; +} + +static int qemu_fwcfg_do_load(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + void *load_addr; + + if (argc == 3) { + load_addr = (void *)simple_strtoul(argv[2], NULL, 16); + } else { + load_addr = getenv("loadaddr"); + if (!load_addr) + load_addr = (void *)CONFIG_SYS_LOAD_ADDR; + else + load_addr = (void *)simple_strtoul(load_addr, + NULL, 16); + } + + return qemu_fwcfg_setup_kernel(load_addr); +} + +static cmd_tbl_t fwcfg_commands[] = { + U_BOOT_CMD_MKENT(list, 0, 1, qemu_fwcfg_do_list, "", ""), + U_BOOT_CMD_MKENT(cpus, 0, 1, qemu_fwcfg_do_cpus, "", ""), + U_BOOT_CMD_MKENT(load, 1, 1, qemu_fwcfg_do_load, "", ""), +}; + +int do_qemu_fw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret; + cmd_tbl_t *fwcfg_cmd; + + if (!fwcfg_present) { + printf("QEMU fw_cfg interface not found\n"); + return CMD_RET_USAGE; + } + + fwcfg_cmd = find_cmd_tbl(argv[1], fwcfg_commands, + ARRAY_SIZE(fwcfg_commands)); + argc -= 3; + argv += 3; + if (!fwcfg_cmd || argc > fwcfg_cmd->maxargs) + return CMD_RET_USAGE; + + ret = fwcfg_cmd->cmd(fwcfg_cmd, flag, argc, argv); + + return cmd_process_error(fwcfg_cmd, ret); +} + +U_BOOT_CMD( + fw, 3, 1, do_qemu_fw, + "QEMU firmware interface", + "<command>\n" + " - list : print firmware(s) currently loaded\n" + " - cpus : print online cpu number\n" + " - load <addr> : load kernel (if any) to address <addr>, and setup for zboot\n" +) diff --git a/arch/x86/cpu/qemu/fw_cfg.h b/arch/x86/cpu/qemu/fw_cfg.h new file mode 100644 index 0000000..f3d06d1 --- /dev/null +++ b/arch/x86/cpu/qemu/fw_cfg.h @@ -0,0 +1,97 @@ +/* + * (C) Copyright 2015 Miao Yan yanmiaobest@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __FW_CFG__ +#define __FW_CFG__ + +#define FW_CONTROL_PORT 0x510 +#define FW_DATA_PORT 0x511 +#define FW_DMA_PORT_LOW 0x514 +#define FW_DMA_PORT_HIGH 0x518 + +enum qemu_fwcfg_items { + FW_CFG_SIGNATURE = 0x00, + FW_CFG_ID = 0x01, + FW_CFG_UUID = 0x02, + FW_CFG_RAM_SIZE = 0x03, + FW_CFG_NOGRAPHIC = 0x04, + FW_CFG_NB_CPUS = 0x05, + FW_CFG_MACHINE_ID = 0x06, + FW_CFG_KERNEL_ADDR = 0x07, + FW_CFG_KERNEL_SIZE = 0x08, + FW_CFG_KERNEL_CMDLINE = 0x09, + FW_CFG_INITRD_ADDR = 0x0a, + FW_CFG_INITRD_SIZE = 0x0b, + FW_CFG_BOOT_DEVICE = 0x0c, + FW_CFG_NUMA = 0x0d, + FW_CFG_BOOT_MENU = 0x0e, + FW_CFG_MAX_CPUS = 0x0f, + FW_CFG_KERNEL_ENTRY = 0x10, + FW_CFG_KERNEL_DATA = 0x11, + FW_CFG_INITRD_DATA = 0x12, + FW_CFG_CMDLINE_ADDR = 0x13, + FW_CFG_CMDLINE_SIZE = 0x14, + FW_CFG_CMDLINE_DATA = 0x15, + FW_CFG_SETUP_ADDR = 0x16, + FW_CFG_SETUP_SIZE = 0x17, + FW_CFG_SETUP_DATA = 0x18, + FW_CFG_FILE_DIR = 0x19, + FW_CFG_FILE_FIRST = 0x20, + FW_CFG_FILE_SLOTS = 0x10, + FW_CFG_WRITE_CHANNEL = 0x4000, + FW_CFG_ARCH_LOCAL = 0x8000, + FW_CFG_INVALID = 0xffff, +}; + +#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST+FW_CFG_FILE_SLOTS) +#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) + +#define FW_CFG_MAX_FILE_PATH 56 + +#define QEMU_FW_CFG_SIGNATURE (('Q' << 24) | ('E' << 16) | ('M' << 8) | 'U') + +#define FW_CFG_DMA_ERROR (0x1 << 0) +#define FW_CFG_DMA_READ (0x1 << 1) +#define FW_CFG_DMA_SKIP (0x1 << 2) +#define FW_CFG_DMA_SELECT (0x1 << 3) + +#define FW_CFG_DMA_ENABLED 0x1 + +struct fw_cfg_file { + __be32 size; + __be16 select; + __be16 reserved; + char name[FW_CFG_MAX_FILE_PATH]; +}; + +struct fw_cfg_files { + __be32 count; + struct fw_cfg_file files[]; +}; + +struct fw_cfg_dma_access { + __be32 control; + __be32 length; + __be64 address; +}; + +/** + * Initialize QEMU fw_cfg interface + * + * @retval: None + */ + +void qemu_fwcfg_init(void); + +/** + * Get system cpu number + * + * @retval: cpu number in system + */ + +int qemu_fwcfg_online_cpus(void); + +#endif diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index 1f93f72..d9ae066 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -11,6 +11,7 @@ #include <asm/processor.h> #include <asm/arch/device.h> #include <asm/arch/qemu.h> +#include "fw_cfg.h"
static bool i440fx;
@@ -57,6 +58,8 @@ static void qemu_chipset_init(void) x86_pci_write_config32(PCI_BDF(0, 0, 0), PCIEX_BAR, CONFIG_PCIE_ECAM_BASE | BAR_EN); } + + qemu_fwcfg_init(); }
int arch_cpu_init(void)

2015-12-29 17:45 GMT+08:00 Miao Yan yanmiaobest@gmail.com:
The QEMU fw_cfg interface allows the guest to retrieve various data information from QEMU. For example, APCI/SMBios tables, number of online cpus, kernel data and command line, etc.
This patch adds support for QEMU fw_cfg interface.
Signed-off-by: Miao Yan yanmiaobest@gmail.com
Changes in v2:
- rewrite command handling logic
- various cleanups
arch/x86/cpu/qemu/Makefile | 2 +- arch/x86/cpu/qemu/fw_cfg.c | 241 +++++++++++++++++++++++++++++++++++++++++++++ arch/x86/cpu/qemu/fw_cfg.h | 97 ++++++++++++++++++ arch/x86/cpu/qemu/qemu.c | 3 + 4 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 arch/x86/cpu/qemu/fw_cfg.c create mode 100644 arch/x86/cpu/qemu/fw_cfg.h
diff --git a/arch/x86/cpu/qemu/Makefile b/arch/x86/cpu/qemu/Makefile index 3f3958a..ad424ec 100644 --- a/arch/x86/cpu/qemu/Makefile +++ b/arch/x86/cpu/qemu/Makefile @@ -7,5 +7,5 @@ ifndef CONFIG_EFI_STUB obj-y += car.o dram.o endif -obj-y += qemu.o +obj-y += qemu.o fw_cfg.o obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o dsdt.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..5f5cc68 --- /dev/null +++ b/arch/x86/cpu/qemu/fw_cfg.c @@ -0,0 +1,241 @@ +/*
- (C) Copyright 2015 Miao Yan yanmiaoebst@gmail.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <command.h> +#include <errno.h> +#include <malloc.h> +#include <asm/io.h> +#include "fw_cfg.h"
+static bool fwcfg_present; +static bool fwcfg_dma_present;
+static void qemu_fwcfg_read_entry_pio(uint16_t entry,
uint32_t size, void *address)
+{
uint32_t i = 0;
uint8_t *data = address;
if (entry != FW_CFG_INVALID)
outw(entry, FW_CONTROL_PORT);
while (size--)
data[i++] = inb(FW_DATA_PORT);
+}
+static void qemu_fwcfg_read_entry_dma(uint16_t entry,
uint32_t size, void *address)
+{
struct fw_cfg_dma_access dma;
dma.length = cpu_to_be32(size);
dma.address = cpu_to_be64((uintptr_t)address);
dma.control = cpu_to_be32(FW_CFG_DMA_READ);
if (entry != FW_CFG_INVALID)
dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16));
barrier();
debug("qemu_fwcfg_dma_read_entry: addr %p, length %u control 0x%x\n",
address, size, be32_to_cpu(dma.control));
outl(cpu_to_be32((uint32_t)&dma), FW_DMA_PORT_HIGH);
while (dma.control & ~FW_CFG_DMA_ERROR)
__asm__ __volatile__ ("pause");
+}
+static bool qemu_fwcfg_present(void) +{
uint32_t qemu;
qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu);
return be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE;
+}
+static bool qemu_fwcfg_dma_present(void) +{
uint8_t dma_enabled;
qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled);
if (dma_enabled & FW_CFG_DMA_ENABLED)
return 1;
return 0;
+}
+static void qemu_fwcfg_read_entry(uint16_t entry,
uint32_t length, void *address)
+{
if (fwcfg_dma_present)
qemu_fwcfg_read_entry_dma(entry, length, address);
else
qemu_fwcfg_read_entry_pio(entry, length, address);
+}
+int qemu_fwcfg_online_cpus(void) +{
uint16_t nb_cpus;
if (!fwcfg_present)
return 1;
qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus);
return le16_to_cpu(nb_cpus);
+}
+static int qemu_fwcfg_setup_kernel(void *load_addr) +{
char *data_addr;
uint32_t setup_size, kernel_size, cmdline_size, initrd_size;
qemu_fwcfg_read_entry(FW_CFG_SETUP_SIZE, 4, &setup_size);
qemu_fwcfg_read_entry(FW_CFG_KERNEL_SIZE, 4, &kernel_size);
if (setup_size == 0 || kernel_size == 0) {
printf("warning: no kernel available\n");
return -1;
}
data_addr = load_addr;
qemu_fwcfg_read_entry(FW_CFG_SETUP_DATA,
le32_to_cpu(setup_size), data_addr);
data_addr += le32_to_cpu(setup_size);
qemu_fwcfg_read_entry(FW_CFG_KERNEL_DATA,
le32_to_cpu(kernel_size), data_addr);
data_addr += le32_to_cpu(kernel_size);
qemu_fwcfg_read_entry(FW_CFG_INITRD_SIZE, 4, &initrd_size);
if (initrd_size == 0) {
printf("warning: no initrd available\n");
} else {
qemu_fwcfg_read_entry(FW_CFG_INITRD_DATA,
le32_to_cpu(initrd_size), data_addr);
data_addr += le32_to_cpu(initrd_size);
}
qemu_fwcfg_read_entry(FW_CFG_CMDLINE_SIZE, 4, &cmdline_size);
qemu_fwcfg_read_entry(FW_CFG_CMDLINE_DATA,
le32_to_cpu(cmdline_size), data_addr);
printf("loading kernel to address %p", load_addr);
if (initrd_size)
printf(" initrd %p\n",
(char *)load_addr +
le32_to_cpu(setup_size) +
le32_to_cpu(kernel_size));
else
printf("\n");
return setenv("bootargs", data_addr);
+}
+static int qemu_fwcfg_list_firmware(void) +{
int i;
uint32_t count;
struct fw_cfg_files *files;
qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count);
if (!count)
return 0;
count = be32_to_cpu(count);
files = malloc(count * sizeof(struct fw_cfg_file));
if (!files)
return -ENOMEM;
files->count = count;
qemu_fwcfg_read_entry(FW_CFG_INVALID,
count * sizeof(struct fw_cfg_file),
files->files);
for (i = 0; i < files->count; i++)
printf("%-56s\n", files->files[i].name);
free(files);
return 0;
+}
+void qemu_fwcfg_init(void) +{
fwcfg_present = qemu_fwcfg_present();
if (fwcfg_present)
fwcfg_dma_present = qemu_fwcfg_dma_present();
+}
+static int qemu_fwcfg_do_list(cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
+{
qemu_fwcfg_list_firmware();
return 0;
+}
+static int qemu_fwcfg_do_cpus(cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
+{
printf("%u cpu(s) online\n", qemu_fwcfg_online_cpus());
return 0;
+}
+static int qemu_fwcfg_do_load(cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
+{
void *load_addr;
if (argc == 3) {
load_addr = (void *)simple_strtoul(argv[2], NULL, 16);
} else {
load_addr = getenv("loadaddr");
if (!load_addr)
load_addr = (void *)CONFIG_SYS_LOAD_ADDR;
else
load_addr = (void *)simple_strtoul(load_addr,
NULL, 16);
}
return qemu_fwcfg_setup_kernel(load_addr);
+}
+static cmd_tbl_t fwcfg_commands[] = {
U_BOOT_CMD_MKENT(list, 0, 1, qemu_fwcfg_do_list, "", ""),
U_BOOT_CMD_MKENT(cpus, 0, 1, qemu_fwcfg_do_cpus, "", ""),
U_BOOT_CMD_MKENT(load, 1, 1, qemu_fwcfg_do_load, "", ""),
+};
+int do_qemu_fw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
int ret;
cmd_tbl_t *fwcfg_cmd;
if (!fwcfg_present) {
printf("QEMU fw_cfg interface not found\n");
return CMD_RET_USAGE;
}
fwcfg_cmd = find_cmd_tbl(argv[1], fwcfg_commands,
ARRAY_SIZE(fwcfg_commands));
argc -= 3;
argv += 3;
oops, misunderstood the meaning here, will send v3 to fix.
if (!fwcfg_cmd || argc > fwcfg_cmd->maxargs)
return CMD_RET_USAGE;
ret = fwcfg_cmd->cmd(fwcfg_cmd, flag, argc, argv);
return cmd_process_error(fwcfg_cmd, ret);
+}
+U_BOOT_CMD(
fw, 3, 1, do_qemu_fw,
"QEMU firmware interface",
"<command>\n"
" - list : print firmware(s) currently loaded\n"
" - cpus : print online cpu number\n"
" - load <addr> : load kernel (if any) to address <addr>, and setup for zboot\n"
+) diff --git a/arch/x86/cpu/qemu/fw_cfg.h b/arch/x86/cpu/qemu/fw_cfg.h new file mode 100644 index 0000000..f3d06d1 --- /dev/null +++ b/arch/x86/cpu/qemu/fw_cfg.h @@ -0,0 +1,97 @@ +/*
- (C) Copyright 2015 Miao Yan yanmiaobest@gmail.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __FW_CFG__ +#define __FW_CFG__
+#define FW_CONTROL_PORT 0x510 +#define FW_DATA_PORT 0x511 +#define FW_DMA_PORT_LOW 0x514 +#define FW_DMA_PORT_HIGH 0x518
+enum qemu_fwcfg_items {
FW_CFG_SIGNATURE = 0x00,
FW_CFG_ID = 0x01,
FW_CFG_UUID = 0x02,
FW_CFG_RAM_SIZE = 0x03,
FW_CFG_NOGRAPHIC = 0x04,
FW_CFG_NB_CPUS = 0x05,
FW_CFG_MACHINE_ID = 0x06,
FW_CFG_KERNEL_ADDR = 0x07,
FW_CFG_KERNEL_SIZE = 0x08,
FW_CFG_KERNEL_CMDLINE = 0x09,
FW_CFG_INITRD_ADDR = 0x0a,
FW_CFG_INITRD_SIZE = 0x0b,
FW_CFG_BOOT_DEVICE = 0x0c,
FW_CFG_NUMA = 0x0d,
FW_CFG_BOOT_MENU = 0x0e,
FW_CFG_MAX_CPUS = 0x0f,
FW_CFG_KERNEL_ENTRY = 0x10,
FW_CFG_KERNEL_DATA = 0x11,
FW_CFG_INITRD_DATA = 0x12,
FW_CFG_CMDLINE_ADDR = 0x13,
FW_CFG_CMDLINE_SIZE = 0x14,
FW_CFG_CMDLINE_DATA = 0x15,
FW_CFG_SETUP_ADDR = 0x16,
FW_CFG_SETUP_SIZE = 0x17,
FW_CFG_SETUP_DATA = 0x18,
FW_CFG_FILE_DIR = 0x19,
FW_CFG_FILE_FIRST = 0x20,
FW_CFG_FILE_SLOTS = 0x10,
FW_CFG_WRITE_CHANNEL = 0x4000,
FW_CFG_ARCH_LOCAL = 0x8000,
FW_CFG_INVALID = 0xffff,
+};
+#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST+FW_CFG_FILE_SLOTS) +#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
+#define FW_CFG_MAX_FILE_PATH 56
+#define QEMU_FW_CFG_SIGNATURE (('Q' << 24) | ('E' << 16) | ('M' << 8) | 'U')
+#define FW_CFG_DMA_ERROR (0x1 << 0) +#define FW_CFG_DMA_READ (0x1 << 1) +#define FW_CFG_DMA_SKIP (0x1 << 2) +#define FW_CFG_DMA_SELECT (0x1 << 3)
+#define FW_CFG_DMA_ENABLED 0x1
+struct fw_cfg_file {
__be32 size;
__be16 select;
__be16 reserved;
char name[FW_CFG_MAX_FILE_PATH];
+};
+struct fw_cfg_files {
__be32 count;
struct fw_cfg_file files[];
+};
+struct fw_cfg_dma_access {
__be32 control;
__be32 length;
__be64 address;
+};
+/**
- Initialize QEMU fw_cfg interface
- @retval: None
- */
+void qemu_fwcfg_init(void);
+/**
- Get system cpu number
- @retval: cpu number in system
- */
+int qemu_fwcfg_online_cpus(void);
+#endif diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index 1f93f72..d9ae066 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -11,6 +11,7 @@ #include <asm/processor.h> #include <asm/arch/device.h> #include <asm/arch/qemu.h> +#include "fw_cfg.h"
static bool i440fx;
@@ -57,6 +58,8 @@ static void qemu_chipset_init(void) x86_pci_write_config32(PCI_BDF(0, 0, 0), PCIEX_BAR, CONFIG_PCIE_ECAM_BASE | BAR_EN); }
qemu_fwcfg_init();
}
int arch_cpu_init(void)
1.9.1

Add a cpu uclass driver for qemu. Previously, the qemu target gets cpu number from board dts files, which are manually created at compile time. This does not scale when more cpus are assigned to guest as the dts files must be modified as well.
This patch adds a cpu uclass driver for qemu targets to directly read online cpu number from firmware.
Signed-off-by: yanmiaobest@gmail.com --- Changes in v2: - return int from qemu_fwcfg_online_cpus()
arch/x86/cpu/qemu/Makefile | 2 +- arch/x86/cpu/qemu/cpu.c | 57 ++++++++++++++++++++++++++++++++++++++++ arch/x86/dts/qemu-x86_i440fx.dts | 4 +-- arch/x86/dts/qemu-x86_q35.dts | 4 +-- 4 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 arch/x86/cpu/qemu/cpu.c
diff --git a/arch/x86/cpu/qemu/Makefile b/arch/x86/cpu/qemu/Makefile index ad424ec..027a12f 100644 --- a/arch/x86/cpu/qemu/Makefile +++ b/arch/x86/cpu/qemu/Makefile @@ -7,5 +7,5 @@ ifndef CONFIG_EFI_STUB obj-y += car.o dram.o endif -obj-y += qemu.o fw_cfg.o +obj-y += qemu.o fw_cfg.o cpu.o obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o dsdt.o diff --git a/arch/x86/cpu/qemu/cpu.c b/arch/x86/cpu/qemu/cpu.c new file mode 100644 index 0000000..9bf9a7c --- /dev/null +++ b/arch/x86/cpu/qemu/cpu.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015, Miao Yan yanmiaobest@gmail.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <cpu.h> +#include <dm.h> +#include <errno.h> +#include <asm/cpu.h> +#include "fw_cfg.h" + +DECLARE_GLOBAL_DATA_PTR; + +int cpu_qemu_bind(struct udevice *dev) +{ + struct cpu_platdata *plat = dev_get_parent_platdata(dev); + + plat->cpu_id = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "intel,apic-id", -1); + + return 0; +} + +int cpu_qemu_get_desc(struct udevice *dev, char *buf, int size) +{ + if (size < CPU_MAX_NAME_LEN) + return -ENOSPC; + + cpu_get_name(buf); + + return 0; +} + +static int cpu_qemu_get_count(struct udevice *dev) +{ + return qemu_fwcfg_online_cpus(); +} + +static const struct cpu_ops cpu_qemu_ops = { + .get_desc = cpu_qemu_get_desc, + .get_count = cpu_qemu_get_count, +}; + +static const struct udevice_id cpu_qemu_ids[] = { + { .compatible = "cpu-qemu" }, + { } +}; + +U_BOOT_DRIVER(cpu_qemu_drv) = { + .name = "cpu_qemu", + .id = UCLASS_CPU, + .of_match = cpu_qemu_ids, + .bind = cpu_qemu_bind, + .ops = &cpu_qemu_ops, +}; diff --git a/arch/x86/dts/qemu-x86_i440fx.dts b/arch/x86/dts/qemu-x86_i440fx.dts index 8a06229..4332204 100644 --- a/arch/x86/dts/qemu-x86_i440fx.dts +++ b/arch/x86/dts/qemu-x86_i440fx.dts @@ -32,14 +32,14 @@
cpu@0 { device_type = "cpu"; - compatible = "cpu-x86"; + compatible = "cpu-qemu"; reg = <0>; intel,apic-id = <0>; };
cpu@1 { device_type = "cpu"; - compatible = "cpu-x86"; + compatible = "cpu-qemu"; reg = <1>; intel,apic-id = <1>; }; diff --git a/arch/x86/dts/qemu-x86_q35.dts b/arch/x86/dts/qemu-x86_q35.dts index 0b685c8..3e2cfac 100644 --- a/arch/x86/dts/qemu-x86_q35.dts +++ b/arch/x86/dts/qemu-x86_q35.dts @@ -43,14 +43,14 @@
cpu@0 { device_type = "cpu"; - compatible = "cpu-x86"; + compatible = "cpu-qemu"; reg = <0>; intel,apic-id = <0>; };
cpu@1 { device_type = "cpu"; - compatible = "cpu-x86"; + compatible = "cpu-qemu"; reg = <1>; intel,apic-id = <1>; };

Rename 'find_cpu_by_apid_id' to 'find_cpu_by_apic_id'. This should be a typo.
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- arch/x86/cpu/mp_init.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/x86/cpu/mp_init.c b/arch/x86/cpu/mp_init.c index 4334f5b..2f34317 100644 --- a/arch/x86/cpu/mp_init.c +++ b/arch/x86/cpu/mp_init.c @@ -104,7 +104,7 @@ static void ap_do_flight_plan(struct udevice *cpu) } }
-static int find_cpu_by_apid_id(int apic_id, struct udevice **devp) +static int find_cpu_by_apic_id(int apic_id, struct udevice **devp) { struct udevice *dev;
@@ -137,7 +137,7 @@ static void ap_init(unsigned int cpu_index) enable_lapic();
apic_id = lapicid(); - ret = find_cpu_by_apid_id(apic_id, &dev); + ret = find_cpu_by_apic_id(apic_id, &dev); if (ret) { debug("Unknown CPU apic_id %x\n", apic_id); goto done; @@ -432,7 +432,7 @@ static int init_bsp(struct udevice **devp) lapic_setup();
apic_id = lapicid(); - ret = find_cpu_by_apid_id(apic_id, devp); + ret = find_cpu_by_apic_id(apic_id, devp); if (ret) { printf("Cannot find boot CPU, APIC ID %d\n", apic_id); return ret;

Use actual CPU number, instead of maximum cpu configured, to allocate stack memory in 'load_sipi_vector'
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- Changes in v2: - fix commit message
arch/x86/cpu/mp_init.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/x86/cpu/mp_init.c b/arch/x86/cpu/mp_init.c index 2f34317..2a3ce48 100644 --- a/arch/x86/cpu/mp_init.c +++ b/arch/x86/cpu/mp_init.c @@ -210,7 +210,7 @@ static int save_bsp_msrs(char *start, int size) return msr_count; }
-static int load_sipi_vector(atomic_t **ap_countp) +static int load_sipi_vector(atomic_t **ap_countp, int num_cpus) { struct sipi_params_16bit *params16; struct sipi_params *params; @@ -239,7 +239,7 @@ static int load_sipi_vector(atomic_t **ap_countp) params->idt_ptr = (uint32_t)x86_get_idt();
params->stack_size = CONFIG_AP_STACK_SIZE; - size = params->stack_size * CONFIG_MAX_CPUS; + size = params->stack_size * num_cpus; stack = memalign(size, 4096); if (!stack) return -ENOMEM; @@ -483,7 +483,7 @@ int mp_init(struct mp_params *p) mp_info.records = p->flight_plan;
/* Load the SIPI vector */ - ret = load_sipi_vector(&ap_count); + ret = load_sipi_vector(&ap_count, num_cpus); if (ap_count == NULL) return -1;

Add a function to fixup 'cpus' node in dts files for qemu target.
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- Changes in v2: - various cleanups
arch/x86/cpu/qemu/fw_cfg.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++ arch/x86/cpu/qemu/fw_cfg.h | 11 ++++++++ 2 files changed, 76 insertions(+)
diff --git a/arch/x86/cpu/qemu/fw_cfg.c b/arch/x86/cpu/qemu/fw_cfg.c index 5f5cc68..1366b04 100644 --- a/arch/x86/cpu/qemu/fw_cfg.c +++ b/arch/x86/cpu/qemu/fw_cfg.c @@ -9,6 +9,7 @@ #include <errno.h> #include <malloc.h> #include <asm/io.h> +#include <libfdt.h> #include "fw_cfg.h"
static bool fwcfg_present; @@ -88,6 +89,70 @@ int qemu_fwcfg_online_cpus(void) return le16_to_cpu(nb_cpus); }
+void qemu_fwcfg_fdt_fixup(void *fdt_addr, int cpu_num) +{ + int i; + char cpus[10]; + int off, err, sub_off, id; + + off = fdt_path_offset(fdt_addr, "/cpus"); + if (off != -FDT_ERR_NOTFOUND) { + printf("error detecting cpus subnode: %s (%d)\n", + fdt_strerror(off), off); + return; + } + + off = fdt_add_subnode(fdt_addr, 0, "cpus"); + if (off < 0) { + printf("error adding cpus subnode: %s (%d)\n", + fdt_strerror(off), off); + return; + } + + for (i = cpu_num - 1; i >= 0; i--) { + sprintf(cpus, "%s@%d", "cpu", i); + sub_off = fdt_add_subnode(fdt_addr, off, cpus); + if (sub_off < 0) { + printf("error adding subnode cpu %d: %s (%d)\n", + i, fdt_strerror(sub_off), sub_off); + return; + } + + id = cpu_to_fdt32(i); + err = fdt_setprop(fdt_addr, sub_off, "intel,apic-id", + (void *)&id, sizeof(id)); + if (err < 0) { + printf("error adding apic-id %d: %s (%d)\n", + i, fdt_strerror(err), err); + return; + } + + err = fdt_setprop(fdt_addr, sub_off, "reg", + (void *)&id, sizeof(id)); + if (err < 0) { + printf("error adding reg %d: %s (%d)\n", + i, fdt_strerror(err), err); + return; + } + + err = fdt_setprop(fdt_addr, sub_off, "compatible", + "cpu-qemu", sizeof("cpu-qemu")); + if (err < 0) { + printf("error adding compatible %d: %s (%d)\n", + i, fdt_strerror(err), err); + return; + } + + err = fdt_setprop(fdt_addr, sub_off, "device_type", + "cpu", sizeof("cpu")); + if (err < 0) { + printf("error adding device_type %d: %s (%d)\n", + i, fdt_strerror(err), err); + return; + } + } +} + static int qemu_fwcfg_setup_kernel(void *load_addr) { char *data_addr; diff --git a/arch/x86/cpu/qemu/fw_cfg.h b/arch/x86/cpu/qemu/fw_cfg.h index f3d06d1..a92c9e4 100644 --- a/arch/x86/cpu/qemu/fw_cfg.h +++ b/arch/x86/cpu/qemu/fw_cfg.h @@ -94,4 +94,15 @@ void qemu_fwcfg_init(void);
int qemu_fwcfg_online_cpus(void);
+/** + * Fix 'cpu' node in device tree for qemu targets + * + * @fdt_addr: device tree blob address + * @cpu_num: cpu number in system + * + * @retval: None + */ + +void qemu_fwcfg_fdt_fixup(void *fdt_addr, int cpu_num); + #endif

Remove 'cpus' node in dts files for QEMU targets, retrieve cpu number through 'fw_cfg' interface and fix up device tree blob at runtime.
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- Changes in v2: - rebase to u-boot-x86/next
arch/x86/cpu/qemu/qemu.c | 4 ++++ arch/x86/dts/qemu-x86_i440fx.dts | 19 +------------------ arch/x86/dts/qemu-x86_q35.dts | 19 +------------------ 3 files changed, 6 insertions(+), 36 deletions(-)
diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index d9ae066..3be3af0 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -15,6 +15,8 @@
static bool i440fx;
+DECLARE_GLOBAL_DATA_PTR; + static void qemu_chipset_init(void) { u16 device, xbcs; @@ -93,6 +95,8 @@ int arch_early_init_r(void) { qemu_chipset_init();
+ qemu_fwcfg_fdt_fixup((void *)gd->fdt_blob, qemu_fwcfg_online_cpus()); + return 0; }
diff --git a/arch/x86/dts/qemu-x86_i440fx.dts b/arch/x86/dts/qemu-x86_i440fx.dts index 4332204..79bafbd 100644 --- a/arch/x86/dts/qemu-x86_i440fx.dts +++ b/arch/x86/dts/qemu-x86_i440fx.dts @@ -26,24 +26,7 @@ stdout-path = "/serial"; };
- cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu@0 { - device_type = "cpu"; - compatible = "cpu-qemu"; - reg = <0>; - intel,apic-id = <0>; - }; - - cpu@1 { - device_type = "cpu"; - compatible = "cpu-qemu"; - reg = <1>; - intel,apic-id = <1>; - }; - }; + /* cpu node will be dynamically filled by U-Boot */
tsc-timer { clock-frequency = <1000000000>; diff --git a/arch/x86/dts/qemu-x86_q35.dts b/arch/x86/dts/qemu-x86_q35.dts index 3e2cfac..9563344 100644 --- a/arch/x86/dts/qemu-x86_q35.dts +++ b/arch/x86/dts/qemu-x86_q35.dts @@ -37,24 +37,7 @@ stdout-path = "/serial"; };
- cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu@0 { - device_type = "cpu"; - compatible = "cpu-qemu"; - reg = <0>; - intel,apic-id = <0>; - }; - - cpu@1 { - device_type = "cpu"; - compatible = "cpu-qemu"; - reg = <1>; - intel,apic-id = <1>; - }; - }; + /* cpu node will be dynamically filled by U-Boot */
tsc-timer { clock-frequency = <1000000000>;

Reserve more spaces for x86 dtb files. Otherwise when booting on qemu targets, fixing 'cpu' node may fail due to lack of space left in dtb.
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- arch/x86/dts/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/dts/Makefile b/arch/x86/dts/Makefile index 64e5694..753e8fa 100644 --- a/arch/x86/dts/Makefile +++ b/arch/x86/dts/Makefile @@ -15,7 +15,7 @@ dtb-y += bayleybay.dtb \
targets += $(dtb-y)
-DTC_FLAGS += -R 4 -p 0x1000 +DTC_FLAGS += -R 4 -p 0x3000
PHONY += dtbs dtbs: $(addprefix $(obj)/, $(dtb-y))

Document the usage of 'fw' command
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- Changes in v2: - various cleanup
doc/README.x86 | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/doc/README.x86 b/doc/README.x86 index 1271e5e..0687dc1 100644 --- a/doc/README.x86 +++ b/doc/README.x86 @@ -295,9 +295,39 @@ show QEMU's VGA console window. Note this will disable QEMU's serial output. If you want to check both consoles, use '-serial stdio'.
Multicore is also supported by QEMU via '-smp n' where n is the number of cores -to instantiate. Currently the default U-Boot built for QEMU supports 2 cores. -In order to support more cores, you need add additional cpu nodes in the device -tree and change CONFIG_MAX_CPUS accordingly. +to instantiate. U-Boot uses fw_cfg interface provided by QEMU to detect certain +system information, such as cpu number, so 'n' can be any number allowed by +QEMU. + +The fw_cfg interface in QEMU also provides information about kernel data, initrd +,command-line arguments and more. U-Boot supports directly accessing these informtion +from fw_cfg interface, this saves the time of loading them from hard disk or +network again, through emulated devices. To use it , simply providing them in +QEMU command line: + +$ qemu-system-i386 -nographic -bios path/to/u-boot.rom -m 1024 -kernel /path/to/bzImage + -append 'root=/dev/sda1 console=ttyS0' -initrd /path/to/initrd -smp 8 + +Note: -initrd and -smp are both optional + +Then start QEMU, in U-Boot command line use the following U-Boot command to setup kernel: + + => fw +fw - QEMU firmware interface + +Usage: +fw <command> + - list : print firmware(s) currently loaded + - cpus : print online cpu number + - load <addr> : load kernel (if any) to address <addr> + +=> fw load +loading kernel to address 01000000, initrd 015dd010 + +Here the kernel (bzImage) is loaded to 01000000 and initrd is to 0x15dd010. Then, 'zboot' +can be used to boot the kernel: + +=> zboot 01000000 - 015dd010
CPU Microcode -------------
participants (1)
-
Miao Yan