On 25.03.2011, at 04:21, David Gibson wrote: > Currently, the emulated pSeries machine requires the use of the > -kernel parameter in order to explicitly load a guest kernel. This > means booting from the virtual disk, cdrom or network is not possible. > > This patch addresses this limitation by inserting a within-partition > firmware image (derived from the "SLOF" free Open Firmware project). > If -kernel is not specified, qemu will now load the SLOF image, which > has access to the qemu boot device list through the device tree, and > can boot from any of the usual virtual devices. > > In order to support the new firmware, an extension to the emulated > machine/hypervisor is necessary. Unlike Linux, which expects > multi-CPU entry to be handled kexec() style, the SLOF firmware expects > only one CPU to be active at entry, and to use a hypervisor RTAS > method to enable the other CPUs one by one. > > This patch also implements this 'start-cpu' method, so that SLOF can > start the secondary CPUs and marshal them into the kexec() holding > pattern ready for entry into the guest OS. Linux should, and in the > future might directly use the start-cpu method to enable initially > disabled CPUs, but for now it does require kexec() entry. > > Signed-off-by: Benjamin Herrenschmidt <b...@kernel.crashing.org> > Signed-off-by: Paul Mackerras <pau...@samba.org> > Signed-off-by: David Gibson <d...@au1.ibm.com> > --- > Makefile | 2 +- > hw/spapr.c | 35 +++++++++++++++++++++--- > hw/spapr_rtas.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > pc-bios/README | 5 +++ > pc-bios/slof.bin | Bin 0 -> 579072 bytes > 5 files changed, 115 insertions(+), 5 deletions(-) > create mode 100644 pc-bios/slof.bin > > diff --git a/Makefile b/Makefile > index e0b3fea..989622b 100644 > --- a/Makefile > +++ b/Makefile > @@ -214,7 +214,7 @@ pxe-rtl8139.bin pxe-virtio.bin \ > bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ > multiboot.bin linuxboot.bin \ > s390-zipl.rom \ > -spapr-rtas.bin > +spapr-rtas.bin slof.bin > else > BLOBS= > endif > diff --git a/hw/spapr.c b/hw/spapr.c > index 9d611a7..c6454e6 100644 > --- a/hw/spapr.c > +++ b/hw/spapr.c > @@ -44,6 +44,10 @@ > #define INITRD_LOAD_ADDR 0x02800000 > #define FDT_MAX_SIZE 0x10000 > #define RTAS_MAX_SIZE 0x10000 > +#define FW_MAX_SIZE 0x400000 > +#define FW_FILE_NAME "slof.bin" > + > +#define MIN_RAM_SLOF 512UL > > #define TIMEBASE_FREQ 512000000ULL > > @@ -56,6 +60,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t > ramsize, > sPAPREnvironment *spapr, > target_phys_addr_t initrd_base, > target_phys_addr_t initrd_size, > + const char *boot_device, > const char *kernel_cmdline, > target_phys_addr_t rtas_addr, > target_phys_addr_t rtas_size, > @@ -104,6 +109,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t > ramsize, > &start_prop, sizeof(start_prop)))); > _FDT((fdt_property(fdt, "linux,initrd-end", > &end_prop, sizeof(end_prop)))); > + _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); > > _FDT((fdt_end_node(fdt))); > > @@ -260,7 +266,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, > ram_addr_t ram_offset; > target_phys_addr_t fdt_addr, rtas_addr; > uint32_t kernel_base, initrd_base; > - long kernel_size, initrd_size, htab_size, rtas_size; > + long kernel_size, initrd_size, htab_size, rtas_size, fw_size; > long pteg_shift = 17; > int fdt_size; > char *filename; > @@ -392,13 +398,33 @@ static void ppc_spapr_init(ram_addr_t ram_size, > initrd_size = 0; > } > } else { > - fprintf(stderr, "pSeries machine needs -kernel for now"); > - exit(1); > + if (ram_size < (MIN_RAM_SLOF << 20)) { > + fprintf(stderr, "qemu: pSeries SLOF firmware requires >= " > + "%ldM guest RAM\n", MIN_RAM_SLOF); > + exit(1); > + } > + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin"); > + fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); > + if (fw_size < 0) { > + hw_error("qemu: could not load LPAR rtas '%s'\n", filename); > + exit(1); > + } > + qemu_free(filename); > + kernel_base = 0x100; > + initrd_base = 0; > + initrd_size = 0; > + > + /* SLOF will startup the secondary CPUs using RTAS, > + rather than expecting a kexec() style entry */ > + for (i = 0; i < smp_cpus; i++) { > + envs[i]->halted = 1; > + } > } > > /* Prepare the device tree */ > fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, envs, spapr, > - initrd_base, initrd_size, kernel_cmdline, > + initrd_base, initrd_size, > + boot_device, kernel_cmdline, > rtas_addr, rtas_size, pteg_shift + 7); > assert(fdt != NULL); > > @@ -409,6 +435,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, > envs[0]->gpr[3] = fdt_addr; > envs[0]->gpr[5] = 0; > envs[0]->hreset_vector = kernel_base; > + envs[0]->halted = 0; > } > > static QEMUMachine spapr_machine = { > diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c > index 7226853..16b6542 100644 > --- a/hw/spapr_rtas.c > +++ b/hw/spapr_rtas.c > @@ -90,6 +90,81 @@ static void rtas_power_off(sPAPREnvironment *spapr, > rtas_st(rets, 0, 0); > } > > +static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr, > + uint32_t token, uint32_t nargs, > + target_ulong args, > + uint32_t nret, target_ulong rets) > +{ > + target_ulong id; > + CPUState *env; > + > + if (nargs != 1 || nret != 2) { > + rtas_st(rets, 0, -3); > + return; > + } > + > + id = rtas_ld(args, 0); > + for (env = first_cpu; env; env = env->next_cpu) { > + if (env->cpu_index != id) { > + continue; > + } > + > + if (env->halted) { > + rtas_st(rets, 1, 0); > + } else { > + rtas_st(rets, 1, 2); > + } > + > + rtas_st(rets, 0, 0); > + return; > + } > + > + /* Didn't find a matching cpu */ > + rtas_st(rets, 0, -3); > +} > + > +static void rtas_start_cpu(sPAPREnvironment *spapr, > + uint32_t token, uint32_t nargs, > + target_ulong args, > + uint32_t nret, target_ulong rets) > +{ > + target_ulong id, start, r3; > + CPUState *env; > + > + if (nargs != 3 || nret != 1) { > + rtas_st(rets, 0, -3); > + return; > + } > + > + id = rtas_ld(args, 0); > + start = rtas_ld(args, 1); > + r3 = rtas_ld(args, 2); > + > + for (env = first_cpu; env; env = env->next_cpu) { > + if (env->cpu_index != id) { > + continue; > + } > + > + if (!env->halted) { > + rtas_st(rets, 0, -1); > + return; > + } > + > + env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); > + env->nip = start; > + env->gpr[3] = r3; > + env->halted = 0; > + > + qemu_cpu_kick(env); > + > + rtas_st(rets, 0, 0); > + return; > + } > + > + /* Didn't find a matching cpu */ > + rtas_st(rets, 0, -3); > +} > + > static struct rtas_call { > const char *name; > spapr_rtas_fn fn; > @@ -196,5 +271,8 @@ static void register_core_rtas(void) > spapr_rtas_register("display-character", rtas_display_character); > spapr_rtas_register("get-time-of-day", rtas_get_time_of_day); > spapr_rtas_register("power-off", rtas_power_off); > + spapr_rtas_register("query-cpu-stopped-state", > + rtas_query_cpu_stopped_state); > + spapr_rtas_register("start-cpu", rtas_start_cpu); > } > device_init(register_core_rtas); > diff --git a/pc-bios/README b/pc-bios/README > index 3fc0944..646a31a 100644 > --- a/pc-bios/README > +++ b/pc-bios/README > @@ -13,6 +13,11 @@ > The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32 > and Sparc64 are built from OpenBIOS SVN revision 1018. > > +- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware > + implementation for certain IBM POWER hardware. The sources are at > + https://github.com/dgibson/SLOF, and the image currently in qemu is > + built from git tag qemu-slof-20110323. > + > - The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0
Is this a line removal? Alex