
Currently, when booting with more that one CPU enabled, U-Boot scans 'cpu' node in device tree and calculates CPU number. This does not scale well as changing CPU number also requires modifying .dts and re-compiling U-Boot.
This patch uses fw_cfg interface provided by QEMU to detect online CPU number at runtime, and dynamically adds 'cpu' device to U-Boot's driver model.
Signed-off-by: Miao Yan yanmiaobest@gmail.com --- arch/x86/cpu/mp_init.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ arch/x86/cpu/qemu/cpu.c | 5 ---- 2 files changed, 73 insertions(+), 5 deletions(-)
diff --git a/arch/x86/cpu/mp_init.c b/arch/x86/cpu/mp_init.c index 2a3ce48..d217ff0 100644 --- a/arch/x86/cpu/mp_init.c +++ b/arch/x86/cpu/mp_init.c @@ -20,8 +20,11 @@ #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> +#include <dm/root.h> #include <linux/linkage.h>
DECLARE_GLOBAL_DATA_PTR; @@ -441,6 +444,70 @@ static int init_bsp(struct udevice **devp) return 0; }
+#ifdef CONFIG_QEMU +static int qemu_cpu_fixup(void) +{ + int ret; + int cpu_num; + int cpu_online; + struct udevice *dev, *pdev; + struct cpu_platdata *plat; + char *cpu; + + /* first we need to find '/cpus' */ + for (device_find_first_child(dm_root(), &pdev); + pdev; + device_find_next_child(&pdev)) { + if (!strcmp(pdev->name, "cpus")) + break; + } + if (!pdev) { + printf("unable to find cpus device\n"); + return -ENODEV; + } + + /* calculate cpus that are already bound */ + cpu_num = 0; + for (uclass_find_first_device(UCLASS_CPU, &dev); + dev; + uclass_find_next_device(&dev)) { + cpu_num++; + } + + /* get actual cpu number */ + cpu_online = qemu_fwcfg_online_cpus(); + if (cpu_online < 0) { + printf("unable to get online cpu number: %d\n", cpu_online); + return cpu_online; + } + + /* bind addtional cpus */ + dev = NULL; + for (; cpu_num < cpu_online; cpu_num++) { + /* + * allocate device name here as device_bind_driver() does + * not copy device name, 8 bytes are enough for + * sizeof("cpu@") + 3 digits cpu number + '\0' + */ + cpu = malloc(8); + if (!cpu) { + printf("unable to allocate device name\n"); + return -ENOMEM; + } + sprintf(cpu, "cpu@%d", cpu_num); + cpu[7] = '\0'; + ret = device_bind_driver(pdev, "cpu_qemu", cpu, &dev); + if (ret) { + printf("binding cpu@%d failed: %d\n", cpu_num, ret); + return ret; + } + plat = dev_get_parent_platdata(dev); + plat->cpu_id = cpu_num; + } + return 0; +} +#endif + int mp_init(struct mp_params *p) { int num_aps; @@ -454,6 +521,12 @@ int mp_init(struct mp_params *p) if (ret) return ret;
+#ifdef CONFIG_QEMU + ret = qemu_cpu_fixup(); + if (ret) + return ret; +#endif + ret = init_bsp(&cpu); if (ret) { debug("Cannot init boot CPU: err=%d\n", ret); diff --git a/arch/x86/cpu/qemu/cpu.c b/arch/x86/cpu/qemu/cpu.c index a4bf53d..e8f486d 100644 --- a/arch/x86/cpu/qemu/cpu.c +++ b/arch/x86/cpu/qemu/cpu.c @@ -15,11 +15,6 @@ 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; }