Skip to content

Commit

Permalink
Add SLOF-based partition firmware for pSeries machine, allowing more …
Browse files Browse the repository at this point in the history
…boot options

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 <[email protected]>
Signed-off-by: Paul Mackerras <[email protected]>
Signed-off-by: David Gibson <[email protected]>
Signed-off-by: Alexander Graf <[email protected]>
  • Loading branch information
dgibson authored and agraf committed Apr 1, 2011
1 parent ed12005 commit a9f8ad8
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "roms/seabios"]
path = roms/seabios
url = git://git.qemu.org/seabios.git/
[submodule "roms/SLOF"]
path = roms/SLOF
url = git://git.qemu.org/SLOF.git
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 31 additions & 4 deletions hw/spapr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -57,6 +61,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,
Expand Down Expand Up @@ -105,6 +110,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)));

Expand Down Expand Up @@ -261,7 +267,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;
Expand Down Expand Up @@ -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);

Expand All @@ -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 = {
Expand Down
78 changes: 78 additions & 0 deletions hw/spapr_rtas.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
5 changes: 5 additions & 0 deletions pc-bios/README
Original file line number Diff line number Diff line change
Expand Up @@ -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

e1000 8086:100E
Expand Down
Binary file added pc-bios/slof.bin
Binary file not shown.
1 change: 1 addition & 0 deletions roms/SLOF
Submodule SLOF added at d1d6b5

0 comments on commit a9f8ad8

Please sign in to comment.