This option provides the infrastructure for specifying apicids when boot VM, For example:
#boot with apicid 0 and 2: -smp 2,apics=0xA,maxcpus=4 /* 1010 */ #boot with apicid 1 and 7: -smp 2,apics=0x41,maxcpus=8 /* 0100 0001 */ Signed-off-by: Chen Fan <chen.fan.f...@cn.fujitsu.com> --- hw/i386/pc.c | 9 +++++-- include/sysemu/sysemu.h | 4 ++++ qemu-options.hx | 15 +++++++++--- vl.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 84 insertions(+), 6 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 963446f..3582167 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -991,8 +991,13 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge) current_cpu_model = cpu_model; for (i = 0; i < smp_cpus; i++) { - cpu = pc_new_cpu(cpu_model, x86_cpu_apic_id_from_index(i), - icc_bridge, &error); + int64_t apic_id; + if (nb_boot_apics == 0) { + apic_id = x86_cpu_apic_id_from_index(i); + } else { + apic_id = x86_cpu_apic_id_from_index(boot_apics[i]); + } + cpu = pc_new_cpu(cpu_model, apic_id, icc_bridge, &error); if (error) { error_report("%s", error_get_pretty(error)); error_free(error); diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 495dae8..510a626 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -149,6 +149,10 @@ extern int nb_option_roms; extern const char *prom_envs[MAX_PROM_ENVS]; extern unsigned int nb_prom_envs; +#define MAX_APICS 255 +extern int nb_boot_apics; +extern int64_t boot_apics[MAX_APICS]; + /* pci-hotplug */ void pci_device_hot_add(Monitor *mon, const QDict *qdict); int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo); diff --git a/qemu-options.hx b/qemu-options.hx index bcfe9ea..7f86519 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -73,16 +73,17 @@ Select CPU model (@code{-cpu help} for list and additional feature selection) ETEXI DEF("smp", HAS_ARG, QEMU_OPTION_smp, - "-smp [cpus=]n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets]\n" + "-smp [cpus=]n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets][,apics=apics]\n" " set the number of CPUs to 'n' [default=1]\n" " maxcpus= maximum number of total cpus, including\n" " offline CPUs for hotplug, etc\n" " cores= number of CPU cores on one socket\n" " threads= number of threads on one CPU core\n" - " sockets= number of discrete sockets in the system\n", + " sockets= number of discrete sockets in the system\n" + " apics= a hex number with leading '0x' as boot bitmap of existed apicid\n", QEMU_ARCH_ALL) STEXI -@item -smp [cpus=]@var{n}[,cores=@var{cores}][,threads=@var{threads}][,sockets=@var{sockets}][,maxcpus=@var{maxcpus}] +@item -smp [cpus=]@var{n}[,cores=@var{cores}][,threads=@var{threads}][,sockets=@var{sockets}][,maxcpus=@var{maxcpus}][,apics=@var{apics}] @findex -smp Simulate an SMP system with @var{n} CPUs. On the PC target, up to 255 CPUs are supported. On Sparc32 target, Linux limits the number of usable CPUs @@ -92,6 +93,14 @@ of @var{threads} per cores and the total number of @var{sockets} can be specified. Missing values will be computed. If any on the three values is given, the total number of CPUs @var{n} can be omitted. @var{maxcpus} specifies the maximum number of hotpluggable CPUs. +@var{apics} specifies the boot bitmap of existed apicid. + +@example +#specify the boot bitmap of apicid with 0 and 2: +qemu-system-i386 -smp 2,apics=0xA,maxcpus=4 /* 1010 */ +#specify the boot bitmap of apicid with 1 and 7: +qemu-system-i386 -smp 2,apics=0x41,maxcpus=8 /* 0100 0001 */ +@end example ETEXI DEF("numa", HAS_ARG, QEMU_OPTION_numa, diff --git a/vl.c b/vl.c index 7511e70..870b1bd 100644 --- a/vl.c +++ b/vl.c @@ -254,6 +254,9 @@ unsigned long *node_cpumask[MAX_NODES]; uint8_t qemu_uuid[16]; bool qemu_uuid_set; +int nb_boot_apics; +int64_t boot_apics[MAX_APICS]; + static QEMUBootSetHandler *boot_set_handler; static void *boot_set_opaque; @@ -1379,6 +1382,9 @@ static QemuOptsList qemu_smp_opts = { }, { .name = "maxcpus", .type = QEMU_OPT_NUMBER, + }, { + .name = "apics", + .type = QEMU_OPT_STRING, }, { /*End of list */ } }, @@ -1392,6 +1398,7 @@ static void smp_parse(QemuOpts *opts) unsigned sockets = qemu_opt_get_number(opts, "sockets", 0); unsigned cores = qemu_opt_get_number(opts, "cores", 0); unsigned threads = qemu_opt_get_number(opts, "threads", 0); + const char *apics = qemu_opt_get(opts, "apics"); /* compute missing values, prefer sockets over cores over threads */ if (cpus == 0 || sockets == 0) { @@ -1416,6 +1423,55 @@ static void smp_parse(QemuOpts *opts) smp_cores = cores > 0 ? cores : 1; smp_threads = threads > 0 ? threads : 1; + if (apics) { + if (strstart(apics, "0x", &apics)) { + if (*apics != '\0') { + int i, count; + int64_t max_apicid = 0; + uint32_t val; + char tmp[2]; + + count = strlen(apics); + + for (i = 0; i < count; i++) { + tmp[0] = apics[i]; + tmp[1] = '\0'; + sscanf(tmp, "%x", &val); + + if (val & 1 << 3) { + boot_apics[nb_boot_apics++] = i * 4; + } + if (val & 1 << 2) { + boot_apics[nb_boot_apics++] = i * 4 + 1; + } + if (val & 1 << 1) { + boot_apics[nb_boot_apics++] = i * 4 + 2; + } + if (val & 1 << 0) { + boot_apics[nb_boot_apics++] = i * 4 + 3; + } + if (max_apicid < boot_apics[nb_boot_apics - 1]) { + max_apicid = boot_apics[nb_boot_apics - 1]; + } + } + + if ((max_cpus == 0 && max_apicid > smp_cpus - 1) || + (max_cpus != 0 && max_apicid > max_cpus - 1)) { + fprintf(stderr, "specified apicid" + " must be smaller than maxcpus\n"); + exit(1); + } + } else { + fprintf(stderr, "must specify" + " one or more digits in apics\n"); + exit(1); + } + } else { + fprintf(stderr, "could not parse apics: %s," + " apics must use a leading '0x' digits\n", apics); + exit(1); + } + } } if (max_cpus == 0) { @@ -1430,7 +1486,11 @@ static void smp_parse(QemuOpts *opts) fprintf(stderr, "maxcpus must be equal to or greater than smp\n"); exit(1); } - + if (nb_boot_apics && nb_boot_apics != smp_cpus) { + fprintf(stderr, "the count of specified apics: %d" + " is not equal to smp: %d\n", nb_boot_apics, smp_cpus); + exit(1); + } } static void configure_realtime(QemuOpts *opts) -- 1.8.1.4