Patchwork [15/22] machine: make max_cpus a -machine option

login
register
mail settings
Submitter Anthony Liguori
Date June 7, 2010, 11:52 p.m.
Message ID <1275954730-8196-16-git-send-email-aliguori@us.ibm.com>
Download mbox | patch
Permalink /patch/54918/
State New
Headers show

Comments

Anthony Liguori - June 7, 2010, 11:52 p.m.
max_cpus is a weird property today.  On the one hand, it represents the maximum
CPUs a board can support and is used to validate the number of vcpus requested
by the user.

On the other hand, max_cpus can be set by the user in which case it is taken
to mean the number of physical sockets that should be advertised by the
firmware.  Furthermore, if max_cpus isn't explicitly set by the user, it's
defaulted to the number of smp_cpus.  But there's actually a second copy of
max_cpus that still represents the maximum cpus spported by the platform.

Yes, it's confusing.  So let's be a bit more obvious.  This patch introduces
a sockets parameter that allows a user to explicitly set the number of
advertised sockets apart from the number of maximum cpus.

This is something of a stop gap.  We really ought to specify a more rich
NUMA topology as machine options but that will come later.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Paul Brook - June 8, 2010, 1:01 a.m.
> diff --git a/hw/realview.c b/hw/realview.c
> index a36bdbe..8dcef80 100644
> --- a/hw/realview.c
> +++ b/hw/realview.c
> @@ -444,9 +444,9 @@ static QEMUMachine realview_eb_mpcore_machine = {
>      .init = realview_eb_mpcore_init,
>      .opts_default = (QemuOptValue[]) {
>          QOPT_VALUE("default_drive", "scsi"),
> +        QOPT_VALUE("max_cpus", "4"),
>          { /* end of list */ }
>      },
> -    .max_cpus = 4,
>  };

By my reading this allows the user to modify this value. If so it is wrong. 

This is a fundamental property/limitation of the hardware. Expect qemu to 
crash if the value is modified.

Paul
Anthony Liguori - June 8, 2010, 1:56 a.m.
On 06/07/2010 08:01 PM, Paul Brook wrote:
>> diff --git a/hw/realview.c b/hw/realview.c
>> index a36bdbe..8dcef80 100644
>> --- a/hw/realview.c
>> +++ b/hw/realview.c
>> @@ -444,9 +444,9 @@ static QEMUMachine realview_eb_mpcore_machine = {
>>       .init = realview_eb_mpcore_init,
>>       .opts_default = (QemuOptValue[]) {
>>           QOPT_VALUE("default_drive", "scsi"),
>> +        QOPT_VALUE("max_cpus", "4"),
>>           { /* end of list */ }
>>       },
>> -    .max_cpus = 4,
>>   };
>>      
> By my reading this allows the user to modify this value. If so it is wrong.
>    

max_cpus is complicated because it was used for multiple purposes.

I think max_cpus ought to be configurable.  The reason it's useful to 
configure it that it lets a downstream specify a recommended max cpu 
that's mostly relevant from a scalability perspective.  For instance, 
you may want to set the pc max_cpus to 64 since this is the highest that 
KVM supports today.

> This is a fundamental property/limitation of the hardware. Expect qemu to
> crash if the value is modified.
>    

In this case, the machine cores should be rejecting totally invalid 
values to prevent crashes from occurring.  My reading of realview does 
not suggest that a crash would occur.  Are you speaking in general terms 
or do you think it will actually fail in realview?

Regards,

Anthony Liguori


> Paul
>
>
Paul Brook - June 8, 2010, 2:56 a.m.
> max_cpus is complicated because it was used for multiple purposes.

I don't see any such uses.

> > This is a fundamental property/limitation of the hardware. Expect qemu to
> > crash if the value is modified.
> 
> In this case, the machine cores should be rejecting totally invalid
> values to prevent crashes from occurring.  My reading of realview does
> not suggest that a crash would occur.  Are you speaking in general terms
> or do you think it will actually fail in realview?

    qemu_irq cpu_irq[4];
    ...
    for (n = 0; n < smp_cpus; n++) {
        cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];

I'm pretty sure bad things will happen there if smp_cpus is > 4.
Similar assumptions exist in apic.c, and probably others.

Paul
Jes Sorensen - June 9, 2010, 7:44 a.m.
On 06/08/10 03:56, Anthony Liguori wrote:
> On 06/07/2010 08:01 PM, Paul Brook wrote:
>>> diff --git a/hw/realview.c b/hw/realview.c
>>> index a36bdbe..8dcef80 100644
>>> --- a/hw/realview.c
>>> +++ b/hw/realview.c
>>> @@ -444,9 +444,9 @@ static QEMUMachine realview_eb_mpcore_machine = {
>>>       .init = realview_eb_mpcore_init,
>>>       .opts_default = (QemuOptValue[]) {
>>>           QOPT_VALUE("default_drive", "scsi"),
>>> +        QOPT_VALUE("max_cpus", "4"),
>>>           { /* end of list */ }
>>>       },
>>> -    .max_cpus = 4,
>>>   };
>>>      
>> By my reading this allows the user to modify this value. If so it is
>> wrong.  
> 
> max_cpus is complicated because it was used for multiple purposes.
> 
> I think max_cpus ought to be configurable.  The reason it's useful to
> configure it that it lets a downstream specify a recommended max cpu
> that's mostly relevant from a scalability perspective.  For instance,
> you may want to set the pc max_cpus to 64 since this is the highest that
> KVM supports today.

max_cpus was designed to set the upper limit of allowable cpus on a
system. The main intention for it was to offer limits to things like
SeaBIOS to tell it how many entries to put into the BIOS tables for
systems where we allow hotplug etc. It was not meant to be used to set
limits higher than what the system is able to support, like on with the
current ACPI implementation in SeaBIOS we cannot go above 255 on PC
hardware.

However I don't really see the point in allowing max_cpus to be
configurable on a system like this where 4 is the possible max, per what
Paul is saying. If the user wants to run < 4 cpus, he/she should specify
-smp X with 1 <= X <= 4.

Cheers,
Jes
Jes Sorensen - June 9, 2010, 7:47 a.m.
On 06/08/10 01:52, Anthony Liguori wrote:
> max_cpus is a weird property today.  On the one hand, it represents the maximum
> CPUs a board can support and is used to validate the number of vcpus requested
> by the user.
> 
> On the other hand, max_cpus can be set by the user in which case it is taken
> to mean the number of physical sockets that should be advertised by the
> firmware.  Furthermore, if max_cpus isn't explicitly set by the user, it's
> defaulted to the number of smp_cpus.  But there's actually a second copy of
> max_cpus that still represents the maximum cpus spported by the platform.

Sorry this is wrong, max_cpus never meant to advertise the number of
sockets, it means the number of cores (ie. cpus) as the limit advertised
by firmware.

> Yes, it's confusing.  So let's be a bit more obvious.  This patch introduces
> a sockets parameter that allows a user to explicitly set the number of
> advertised sockets apart from the number of maximum cpus.
> 
> This is something of a stop gap.  We really ought to specify a more rich
> NUMA topology as machine options but that will come later.
> 
> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
> 
[snip]
> diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c
> index 22ebb50..de4454f 100644
> --- a/hw/fw_cfg.c
> +++ b/hw/fw_cfg.c
> @@ -321,7 +321,8 @@ int fw_cfg_add_file(FWCfgState *s,  const char *dir, const char *filename,
>  }
>  
>  FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
> -                        target_phys_addr_t ctl_addr, target_phys_addr_t data_addr)
> +                        target_phys_addr_t ctl_addr, target_phys_addr_t data_addr,
> +                        QemuOpts *opts)
>  {
>      FWCfgState *s;
>      int io_ctl_memory, io_data_memory;
> @@ -349,7 +350,8 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
>      fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
>      fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
>      fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
> -    fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
> +    fw_cfg_add_i16(s, FW_CFG_MAX_CPUS,
> +                   (uint16_t)qemu_opt_get_number(opts, "sockets", smp_cpus));
>      fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);

NACK this is just plain wrong!

Sorry Anthony, but max_cpus never meant number of sockets. It is used by
the BIOS to calculate table sizes for CPU entries, and that number is
based on cores, not sockets.

If you want to pass a number of sockets onto the BIOS, I will totally
support that, but you need to introduce FW_CFG_MAX_SOCKETS for that
instead, and tell the BIOS to use that for the SRAT.

Cheers,
Jes

Patch

diff --git a/hw/boards.h b/hw/boards.h
index 887487e..5176e95 100644
--- a/hw/boards.h
+++ b/hw/boards.h
@@ -14,7 +14,6 @@  struct QEMUMachine {
     const char *alias;
     const char *desc;
     QEMUMachineInitFunc *init;
-    int max_cpus;
     int is_default;
     QemuOptDesc *opts_desc;
     QemuOptValue *opts_default;
@@ -77,6 +76,12 @@  extern QEMUMachine *current_machine;
     },{                                 \
         .name = "default_drive",        \
         .type = QEMU_OPT_STRING,        \
+    },{                                 \
+        .name = "max_cpus",             \
+        .type = QEMU_OPT_NUMBER,        \
+    },{                                 \
+        .name = "sockets",              \
+        .type = QEMU_OPT_NUMBER,        \
     }
 
 
diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c
index 22ebb50..de4454f 100644
--- a/hw/fw_cfg.c
+++ b/hw/fw_cfg.c
@@ -321,7 +321,8 @@  int fw_cfg_add_file(FWCfgState *s,  const char *dir, const char *filename,
 }
 
 FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
-                        target_phys_addr_t ctl_addr, target_phys_addr_t data_addr)
+                        target_phys_addr_t ctl_addr, target_phys_addr_t data_addr,
+                        QemuOpts *opts)
 {
     FWCfgState *s;
     int io_ctl_memory, io_data_memory;
@@ -349,7 +350,8 @@  FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
     fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
     fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
     fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
-    fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
+    fw_cfg_add_i16(s, FW_CFG_MAX_CPUS,
+                   (uint16_t)qemu_opt_get_number(opts, "sockets", smp_cpus));
     fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);
 
     vmstate_register(-1, &vmstate_fw_cfg, s);
diff --git a/hw/fw_cfg.h b/hw/fw_cfg.h
index 4d13a4f..bdf9227 100644
--- a/hw/fw_cfg.h
+++ b/hw/fw_cfg.h
@@ -63,7 +63,8 @@  int fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
 int fw_cfg_add_file(FWCfgState *s, const char *dir, const char *filename,
                     uint8_t *data, uint32_t len);
 FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
-                        target_phys_addr_t crl_addr, target_phys_addr_t data_addr);
+                        target_phys_addr_t crl_addr, target_phys_addr_t data_addr,
+                        QemuOpts *opts);
 
 #endif /* NO_QEMU_PROTOS */
 
diff --git a/hw/multiboot.c b/hw/multiboot.c
index a1b665c..7cdc77b 100644
--- a/hw/multiboot.c
+++ b/hw/multiboot.c
@@ -22,6 +22,8 @@ 
  * THE SOFTWARE.
  */
 
+#include "qemu-common.h"
+#include "qemu-option.h"
 #include "hw.h"
 #include "fw_cfg.h"
 #include "multiboot.h"
diff --git a/hw/pc.c b/hw/pc.c
index 44f5b62..55681d8 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -449,7 +449,7 @@  int e820_add_entry(uint64_t address, uint64_t length, uint32_t type)
     return e820_table.count;
 }
 
-static void *bochs_bios_init(void)
+static void *bochs_bios_init(QemuOpts *opts)
 {
     void *fw_cfg;
     uint8_t *smbios_table;
@@ -468,7 +468,7 @@  static void *bochs_bios_init(void)
     register_ioport_write(0x500, 1, 1, bochs_bios_write, NULL);
     register_ioport_write(0x503, 1, 1, bochs_bios_write, NULL);
 
-    fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0);
+    fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0, opts);
 
     fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
     fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
@@ -804,7 +804,8 @@  void pc_memory_init(ram_addr_t ram_size,
                     const char *kernel_cmdline,
                     const char *initrd_filename,
                     ram_addr_t *below_4g_mem_size_p,
-                    ram_addr_t *above_4g_mem_size_p)
+                    ram_addr_t *above_4g_mem_size_p,
+                    QemuOpts *opts)
 {
     char *filename;
     int ret, linux_boot, i;
@@ -882,7 +883,7 @@  void pc_memory_init(ram_addr_t ram_size,
     cpu_register_physical_memory((uint32_t)(-bios_size),
                                  bios_size, bios_offset | IO_MEM_ROM);
 
-    fw_cfg = bochs_bios_init();
+    fw_cfg = bochs_bios_init(opts);
     rom_set_fw(fw_cfg);
 
     if (linux_boot) {
diff --git a/hw/pc.h b/hw/pc.h
index 1ae61e0..1f838c1 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -91,7 +91,8 @@  void pc_memory_init(ram_addr_t ram_size,
                     const char *kernel_cmdline,
                     const char *initrd_filename,
                     ram_addr_t *below_4g_mem_size_p,
-                    ram_addr_t *above_4g_mem_size_p);
+                    ram_addr_t *above_4g_mem_size_p,
+                    QemuOpts *opts);
 qemu_irq *pc_allocate_cpu_irq(void);
 void pc_vga_init(PCIBus *pci_bus);
 void pc_basic_device_init(qemu_irq *isa_irq,
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 104206c..d02bf20 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -70,7 +70,7 @@  static void pc_init(QemuOpts *opts)
 
     /* allocate ram and load rom/bios */
     pc_memory_init(ram_size, kernel_filename, kernel_cmdline, initrd_filename,
-                   &below_4g_mem_size, &above_4g_mem_size);
+                   &below_4g_mem_size, &above_4g_mem_size, opts);
 
     cpu_irq = pc_allocate_cpu_irq();
     i8259 = i8259_init(cpu_irq[0]);
@@ -186,13 +186,13 @@  static QEMUMachine pc_machine = {
     .alias = "pc",
     .desc = "Standard PC",
     .init = pc_init,
-    .max_cpus = 255,
     .is_default = 1,
     .opts_desc = pc_opts_desc,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("acpi", "on"),
         QOPT_VALUE("pci", "on"),
         QOPT_VALUE("cpu", PC_DEFAULT_CPU_MODEL),
+        QOPT_VALUE("max_cpus", "255"),
         { /* end of list */ }
     },
 };
@@ -201,12 +201,12 @@  static QEMUMachine pc_machine_v0_12 = {
     .name = "pc-0.12",
     .desc = "Standard PC",
     .init = pc_init,
-    .max_cpus = 255,
     .opts_desc = pc_opts_desc,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("acpi", "on"),
         QOPT_VALUE("pci", "on"),
         QOPT_VALUE("cpu", PC_DEFAULT_CPU_MODEL),
+        QOPT_VALUE("max_cpus", "255"),
         QOPT_COMPAT_INT("virtio-serial-pci", "max_nr_ports", 1),
         QOPT_COMPAT_INT("virtio-serial-pci", "vectors", 0),
         { /* end of list */ }
@@ -217,12 +217,12 @@  static QEMUMachine pc_machine_v0_11 = {
     .name = "pc-0.11",
     .desc = "Standard PC, qemu 0.11",
     .init = pc_init,
-    .max_cpus = 255,
     .opts_desc = pc_opts_desc,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("acpi", "on"),
         QOPT_VALUE("pci", "on"),
         QOPT_VALUE("cpu", PC_DEFAULT_CPU_MODEL),
+        QOPT_VALUE("max_cpus", "255"),
         QOPT_COMPAT_INT("virtio-blk-pci", "vectors", 0),
         QOPT_COMPAT_INT("virtio-serial-pci", "max_nr_ports", 1),
         QOPT_COMPAT_INT("virtio-serial-pci", "vectors", 0),
@@ -237,12 +237,12 @@  static QEMUMachine pc_machine_v0_10 = {
     .name = "pc-0.10",
     .desc = "Standard PC, qemu 0.10",
     .init = pc_init,
-    .max_cpus = 255,
     .opts_desc = pc_opts_desc,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("acpi", "on"),
         QOPT_VALUE("pci", "on"),
         QOPT_VALUE("cpu", PC_DEFAULT_CPU_MODEL),
+        QOPT_VALUE("max_cpu", "255"),
         QOPT_COMPAT_INT("virtio-blk-pci", "class", PCI_CLASS_STORAGE_OTHER),
         QOPT_COMPAT_INT("virtio-serial-pci", "class", PCI_CLASS_DISPLAY_OTHER),
         QOPT_COMPAT_INT("virtio-net-pci", "vectors", 0),
@@ -265,9 +265,9 @@  static QEMUMachine isapc_machine = {
         QOPT_VALUE("acpi", "off"),
         QOPT_VALUE("pci", "off"),
         QOPT_VALUE("cpu", "486"),
+        QOPT_VALUE("max_cpus", "1"),
         { /* end of list */ }
     },
-    .max_cpus = 1,
 };
 
 static void pc_machine_init(void)
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
index 1737cc4..3be0c2d 100644
--- a/hw/ppc_newworld.c
+++ b/hw/ppc_newworld.c
@@ -407,7 +407,7 @@  static void ppc_core99_init (QemuOpts *opts)
     macio_nvram_map(nvr, 0xFFF04000);
     /* No PCI init: the BIOS will do it */
 
-    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
+    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2, opts);
     fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
     fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
     fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, machine_arch);
@@ -442,7 +442,10 @@  static QEMUMachine core99_machine = {
     .name = "mac99",
     .desc = "Mac99 based PowerMAC",
     .init = ppc_core99_init,
-    .max_cpus = MAX_CPUS,
+    .opts_default = (QemuOptValue[]) {
+        QOPT_VALUE("max_cpus", stringify(MAX_CPUS)),
+        { /* end of list */ }
+    },
 #ifdef TARGET_PPC64
     .is_default = 1,
 #endif
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index b1ce88d..7fab893 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -379,7 +379,7 @@  static void ppc_heathrow_init (QemuOpts *opts)
 
     /* No PCI init: the BIOS will do it */
 
-    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
+    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2, opts);
     fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
     fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
     fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_HEATHROW);
@@ -414,7 +414,10 @@  static QEMUMachine heathrow_machine = {
     .name = "g3beige",
     .desc = "Heathrow based PowerMAC",
     .init = ppc_heathrow_init,
-    .max_cpus = MAX_CPUS,
+    .opts_default = (QemuOptValue[]) {
+        QOPT_VALUE("max_cpus", stringify(MAX_CPUS)),
+        { /* end of list */ }
+    },
 #ifndef TARGET_PPC64
     .is_default = 1,
 #endif
diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c
index 090c8ac..868ef17 100644
--- a/hw/ppc_prep.c
+++ b/hw/ppc_prep.c
@@ -793,7 +793,10 @@  static QEMUMachine prep_machine = {
     .name = "prep",
     .desc = "PowerPC PREP platform",
     .init = ppc_prep_init,
-    .max_cpus = MAX_CPUS,
+    .opts_default = (QemuOptValue[]) {
+        QOPT_VALUE("max_cpus", stringify(MAX_CPUS)),
+        { /* end of list */ }
+    },
 };
 
 static void prep_machine_init(void)
diff --git a/hw/realview.c b/hw/realview.c
index a36bdbe..8dcef80 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -444,9 +444,9 @@  static QEMUMachine realview_eb_mpcore_machine = {
     .init = realview_eb_mpcore_init,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("default_drive", "scsi"),
+        QOPT_VALUE("max_cpus", "4"),
         { /* end of list */ }
     },
-    .max_cpus = 4,
 };
 
 static QEMUMachine realview_pb_a8_machine = {
@@ -461,9 +461,9 @@  static QEMUMachine realview_pbx_a9_machine = {
     .init = realview_pbx_a9_init,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("default_drive", "scsi"),
+        QOPT_VALUE("max_cpus", "4"),
         { /* end of list */ }
     },
-    .max_cpus = 4,
 };
 
 static void realview_machine_init(void)
diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
index e4d6ecd..a769ba0 100644
--- a/hw/s390-virtio.c
+++ b/hw/s390-virtio.c
@@ -278,10 +278,9 @@  static QEMUMachine s390_machine = {
         QOPT_VALUE("parallel", "off"),
         QOPT_VALUE("virtcon", "on"),
         QOPT_VALUE("vga", "off"),
+        QOPT_VALUE("max_cpus", "255"),
         { /* end of list */ }
     },
-    .no_vga = 1,
-    .max_cpus = 255,
     .is_default = 1,
 };
 
diff --git a/hw/sun4m.c b/hw/sun4m.c
index a400530..7d24873 100644
--- a/hw/sun4m.c
+++ b/hw/sun4m.c
@@ -798,7 +798,8 @@  static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
                           const char *boot_device,
                           const char *kernel_filename,
                           const char *kernel_cmdline,
-                          const char *initrd_filename, const char *cpu_model)
+                          const char *initrd_filename, const char *cpu_model,
+                          QemuOpts *opts)
 {
     unsigned int i;
     void *iommu, *espdma, *ledma, *nvram;
@@ -932,7 +933,7 @@  static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
         ecc_init(hwdef->ecc_base, slavio_irq[28],
                  hwdef->ecc_version);
 
-    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
+    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2, opts);
     fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
     fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
     fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id);
@@ -1208,7 +1209,7 @@  static void ss5_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4m_hw_init(&sun4m_hwdefs[0], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 /* SPARCstation 10 hardware initialisation */
@@ -1221,7 +1222,7 @@  static void ss10_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4m_hw_init(&sun4m_hwdefs[1], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 /* SPARCserver 600MP hardware initialisation */
@@ -1234,7 +1235,7 @@  static void ss600mp_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4m_hw_init(&sun4m_hwdefs[2], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 /* SPARCstation 20 hardware initialisation */
@@ -1247,7 +1248,7 @@  static void ss20_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4m_hw_init(&sun4m_hwdefs[3], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 /* SPARCstation Voyager hardware initialisation */
@@ -1260,7 +1261,7 @@  static void vger_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4m_hw_init(&sun4m_hwdefs[4], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 /* SPARCstation LX hardware initialisation */
@@ -1273,7 +1274,7 @@  static void ss_lx_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4m_hw_init(&sun4m_hwdefs[5], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 /* SPARCstation 4 hardware initialisation */
@@ -1286,7 +1287,7 @@  static void ss4_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4m_hw_init(&sun4m_hwdefs[6], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 /* SPARCClassic hardware initialisation */
@@ -1299,7 +1300,7 @@  static void scls_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4m_hw_init(&sun4m_hwdefs[7], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 /* SPARCbook hardware initialisation */
@@ -1312,7 +1313,7 @@  static void sbook_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4m_hw_init(&sun4m_hwdefs[8], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 static QEMUMachine ss5_machine = {
@@ -1332,9 +1333,9 @@  static QEMUMachine ss10_machine = {
     .init = ss10_init,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("default_drive", "scsi"),
+        QOPT_VALUE("max_cpus", "4"),
         { /* end of list */ }
     },
-    .max_cpus = 4,
 };
 
 static QEMUMachine ss600mp_machine = {
@@ -1343,9 +1344,9 @@  static QEMUMachine ss600mp_machine = {
     .init = ss600mp_init,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("default_drive", "scsi"),
+        QOPT_VALUE("max_cpus", "4"),
         { /* end of list */ }
     },
-    .max_cpus = 4,
 };
 
 static QEMUMachine ss20_machine = {
@@ -1354,9 +1355,9 @@  static QEMUMachine ss20_machine = {
     .init = ss20_init,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("default_drive", "scsi"),
+        QOPT_VALUE("max_cpus", "4"),
         { /* end of list */ }
     },
-    .max_cpus = 4,
 };
 
 static QEMUMachine voyager_machine = {
@@ -1488,7 +1489,8 @@  static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size,
                           const char *boot_device,
                           const char *kernel_filename,
                           const char *kernel_cmdline,
-                          const char *initrd_filename, const char *cpu_model)
+                          const char *initrd_filename, const char *cpu_model,
+                          QemuOpts *opts)
 {
     unsigned int i;
     void *iounits[MAX_IOUNITS], *espdma, *ledma, *nvram;
@@ -1574,7 +1576,7 @@  static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size,
                graphic_height, graphic_depth, hwdef->nvram_machine_id,
                "Sun4d");
 
-    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
+    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2, opts);
     fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
     fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
     fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id);
@@ -1606,7 +1608,7 @@  static void ss1000_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4d_hw_init(&sun4d_hwdefs[0], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 /* SPARCcenter 2000 hardware initialisation */
@@ -1619,7 +1621,7 @@  static void ss2000_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4d_hw_init(&sun4d_hwdefs[1], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 static QEMUMachine ss1000_machine = {
@@ -1628,9 +1630,9 @@  static QEMUMachine ss1000_machine = {
     .init = ss1000_init,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("default_drive", "scsi"),
+        QOPT_VALUE("max_cpus", "8"),
         { /* end of list */ }
     },
-    .max_cpus = 8,
 };
 
 static QEMUMachine ss2000_machine = {
@@ -1639,9 +1641,9 @@  static QEMUMachine ss2000_machine = {
     .init = ss2000_init,
     .opts_default = (QemuOptValue[]) {
         QOPT_VALUE("default_drive", "scsi"),
+        QOPT_VALUE("max_cpus", "20"),
         { /* end of list */ }
     },
-    .max_cpus = 20,
 };
 
 static const struct sun4c_hwdef sun4c_hwdefs[] = {
@@ -1691,7 +1693,8 @@  static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size,
                           const char *boot_device,
                           const char *kernel_filename,
                           const char *kernel_cmdline,
-                          const char *initrd_filename, const char *cpu_model)
+                          const char *initrd_filename, const char *cpu_model,
+                          QemuOpts *opts)
 {
     void *iommu, *espdma, *ledma, *nvram;
     qemu_irq *cpu_irqs, slavio_irq[8], espdma_irq, ledma_irq;
@@ -1776,7 +1779,7 @@  static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size,
                graphic_height, graphic_depth, hwdef->nvram_machine_id,
                "Sun4c");
 
-    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
+    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2, opts);
     fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
     fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
     fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id);
@@ -1808,7 +1811,7 @@  static void ss2_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4c_hw_init(&sun4c_hwdefs[0], RAM_size, boot_device, kernel_filename,
-                  kernel_cmdline, initrd_filename, cpu_model);
+                  kernel_cmdline, initrd_filename, cpu_model, opts);
 }
 
 static QEMUMachine ss2_machine = {
diff --git a/hw/sun4u.c b/hw/sun4u.c
index 7abb027..8b112be 100644
--- a/hw/sun4u.c
+++ b/hw/sun4u.c
@@ -742,7 +742,7 @@  static void sun4uv_init(ram_addr_t RAM_size,
                         const char *boot_devices,
                         const char *kernel_filename, const char *kernel_cmdline,
                         const char *initrd_filename, const char *cpu_model,
-                        const struct hwdef *hwdef)
+                        const struct hwdef *hwdef, QemuOpts *opts)
 {
     CPUState *env;
     M48t59State *nvram;
@@ -824,7 +824,7 @@  static void sun4uv_init(ram_addr_t RAM_size,
                            graphic_width, graphic_height, graphic_depth,
                            (uint8_t *)&nd_table[0].macaddr);
 
-    fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0);
+    fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0, opts);
     fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
     fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
     fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id);
@@ -890,7 +890,7 @@  static void sun4u_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4uv_init(RAM_size, boot_devices, kernel_filename,
-                kernel_cmdline, initrd_filename, cpu_model, &hwdefs[0]);
+                kernel_cmdline, initrd_filename, cpu_model, &hwdefs[0], opts);
 }
 
 /* Sun4v hardware initialisation */
@@ -903,7 +903,7 @@  static void sun4v_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4uv_init(RAM_size, boot_devices, kernel_filename,
-                kernel_cmdline, initrd_filename, cpu_model, &hwdefs[1]);
+                kernel_cmdline, initrd_filename, cpu_model, &hwdefs[1], opts);
 }
 
 /* Niagara hardware initialisation */
@@ -916,14 +916,13 @@  static void niagara_init(QemuOpts *opts)
     const char *initrd_filename = qemu_opt_get(opts, "initrd");
     const char *cpu_model = qemu_opt_get(opts, "cpu");
     sun4uv_init(RAM_size, boot_devices, kernel_filename,
-                kernel_cmdline, initrd_filename, cpu_model, &hwdefs[2]);
+                kernel_cmdline, initrd_filename, cpu_model, &hwdefs[2], opts);
 }
 
 static QEMUMachine sun4u_machine = {
     .name = "sun4u",
     .desc = "Sun4u platform",
     .init = sun4u_init,
-    .max_cpus = 1, // XXX for now
     .is_default = 1,
 };
 
@@ -931,14 +930,12 @@  static QEMUMachine sun4v_machine = {
     .name = "sun4v",
     .desc = "Sun4v platform",
     .init = sun4v_init,
-    .max_cpus = 1, // XXX for now
 };
 
 static QEMUMachine niagara_machine = {
     .name = "Niagara",
     .desc = "Sun4v platform, Niagara",
     .init = niagara_init,
-    .max_cpus = 1, // XXX for now
 };
 
 static void sun4u_machine_init(void)
diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c
index 58c68de..077c0f6 100644
--- a/hw/xen_machine_pv.c
+++ b/hw/xen_machine_pv.c
@@ -112,7 +112,6 @@  static QEMUMachine xenpv_machine = {
     .name = "xenpv",
     .desc = "Xen Para-virtualized PC",
     .init = xen_init_pv,
-    .max_cpus = 1,
 };
 
 static void xenpv_machine_init(void)
diff --git a/sysemu.h b/sysemu.h
index 879446a..ff73582 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -124,7 +124,6 @@  extern int alt_grab;
 extern int ctrl_grab;
 extern int usb_enabled;
 extern int smp_cpus;
-extern int max_cpus;
 extern int cursor_hide;
 extern int graphic_rotate;
 extern int no_quit;
diff --git a/vl.c b/vl.c
index 1065233..34e48f8 100644
--- a/vl.c
+++ b/vl.c
@@ -205,7 +205,6 @@  int rtc_td_hack = 0;
 int usb_enabled = 0;
 int singlestep = 0;
 int smp_cpus = 1;
-int max_cpus = 0;
 int smp_cores = 1;
 int smp_threads = 1;
 const char *vnc_display;
@@ -1278,6 +1277,7 @@  static void smp_parse(const char *optarg)
     int smp, sockets = 0, threads = 0, cores = 0;
     char *endptr;
     char option[128];
+    int max_cpus = 0;
 
     smp = strtoul(optarg, &endptr, 10);
     if (endptr != optarg) {
@@ -1315,8 +1315,13 @@  static void smp_parse(const char *optarg)
     smp_cpus = smp;
     smp_cores = cores > 0 ? cores : 1;
     smp_threads = threads > 0 ? threads : 1;
-    if (max_cpus == 0)
+    if (max_cpus == 0) {
         max_cpus = smp_cpus;
+    }
+
+    if (max_cpus) {
+        qemu_opts_parsef(&qemu_machine_opts, "max_cpus=%d", max_cpus);
+    }
 }
 
 /***********************************************************/
@@ -2642,6 +2647,7 @@  int main(int argc, char **argv, char **envp)
     int show_vnc_port = 0;
     int defconfig = 1;
     QemuOpts *machine_opts = NULL;
+    int max_cpus = 0;
 
     error_set_progname(argv[0]);
 
@@ -3277,15 +3283,6 @@  int main(int argc, char **argv, char **envp)
                     fprintf(stderr, "Invalid number of CPUs\n");
                     exit(1);
                 }
-                if (max_cpus < smp_cpus) {
-                    fprintf(stderr, "maxcpus must be equal to or greater than "
-                            "smp\n");
-                    exit(1);
-                }
-                if (max_cpus > 255) {
-                    fprintf(stderr, "Unsupported number of maxcpus\n");
-                    exit(1);
-                }
                 break;
 	    case QEMU_OPTION_vnc:
                 display_type = DT_VNC;
@@ -3490,18 +3487,11 @@  int main(int argc, char **argv, char **envp)
         }
     }
     
-    /*
-     * Default to max_cpus = smp_cpus, in case the user doesn't
-     * specify a max_cpus value.
-     */
-    if (!max_cpus)
-        max_cpus = smp_cpus;
-
-    machine->max_cpus = machine->max_cpus ?: 1; /* Default to UP */
-    if (smp_cpus > machine->max_cpus) {
+    max_cpus = qemu_opt_get_number(machine_opts, "max_cpus", 1);
+    if (smp_cpus > max_cpus) {
         fprintf(stderr, "Number of SMP cpus requested (%d), exceeds max cpus "
                 "supported by machine `%s' (%d)\n", smp_cpus,  machine->name,
-                machine->max_cpus);
+                max_cpus);
         exit(1);
     }