Skip to content

Commit a9f8ad8

Browse files
dgibsonagraf
authored andcommitted
Add SLOF-based partition firmware for pSeries machine, allowing more 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]>
1 parent ed12005 commit a9f8ad8

File tree

7 files changed

+119
-5
lines changed

7 files changed

+119
-5
lines changed

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@
44
[submodule "roms/seabios"]
55
path = roms/seabios
66
url = git://git.qemu.org/seabios.git/
7+
[submodule "roms/SLOF"]
8+
path = roms/SLOF
9+
url = git://git.qemu.org/SLOF.git

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ pxe-rtl8139.bin pxe-virtio.bin \
214214
bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
215215
multiboot.bin linuxboot.bin \
216216
s390-zipl.rom \
217-
spapr-rtas.bin
217+
spapr-rtas.bin slof.bin
218218
else
219219
BLOBS=
220220
endif

hw/spapr.c

+31-4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
#define INITRD_LOAD_ADDR 0x02800000
4545
#define FDT_MAX_SIZE 0x10000
4646
#define RTAS_MAX_SIZE 0x10000
47+
#define FW_MAX_SIZE 0x400000
48+
#define FW_FILE_NAME "slof.bin"
49+
50+
#define MIN_RAM_SLOF 512UL
4751

4852
#define TIMEBASE_FREQ 512000000ULL
4953

@@ -57,6 +61,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
5761
sPAPREnvironment *spapr,
5862
target_phys_addr_t initrd_base,
5963
target_phys_addr_t initrd_size,
64+
const char *boot_device,
6065
const char *kernel_cmdline,
6166
target_phys_addr_t rtas_addr,
6267
target_phys_addr_t rtas_size,
@@ -105,6 +110,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
105110
&start_prop, sizeof(start_prop))));
106111
_FDT((fdt_property(fdt, "linux,initrd-end",
107112
&end_prop, sizeof(end_prop))));
113+
_FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device)));
108114

109115
_FDT((fdt_end_node(fdt)));
110116

@@ -261,7 +267,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
261267
ram_addr_t ram_offset;
262268
target_phys_addr_t fdt_addr, rtas_addr;
263269
uint32_t kernel_base, initrd_base;
264-
long kernel_size, initrd_size, htab_size, rtas_size;
270+
long kernel_size, initrd_size, htab_size, rtas_size, fw_size;
265271
long pteg_shift = 17;
266272
int fdt_size;
267273
char *filename;
@@ -392,13 +398,33 @@ static void ppc_spapr_init(ram_addr_t ram_size,
392398
initrd_size = 0;
393399
}
394400
} else {
395-
fprintf(stderr, "pSeries machine needs -kernel for now");
396-
exit(1);
401+
if (ram_size < (MIN_RAM_SLOF << 20)) {
402+
fprintf(stderr, "qemu: pSeries SLOF firmware requires >= "
403+
"%ldM guest RAM\n", MIN_RAM_SLOF);
404+
exit(1);
405+
}
406+
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin");
407+
fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
408+
if (fw_size < 0) {
409+
hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
410+
exit(1);
411+
}
412+
qemu_free(filename);
413+
kernel_base = 0x100;
414+
initrd_base = 0;
415+
initrd_size = 0;
416+
417+
/* SLOF will startup the secondary CPUs using RTAS,
418+
rather than expecting a kexec() style entry */
419+
for (i = 0; i < smp_cpus; i++) {
420+
envs[i]->halted = 1;
421+
}
397422
}
398423

399424
/* Prepare the device tree */
400425
fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, envs, spapr,
401-
initrd_base, initrd_size, kernel_cmdline,
426+
initrd_base, initrd_size,
427+
boot_device, kernel_cmdline,
402428
rtas_addr, rtas_size, pteg_shift + 7);
403429
assert(fdt != NULL);
404430

@@ -409,6 +435,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
409435
envs[0]->gpr[3] = fdt_addr;
410436
envs[0]->gpr[5] = 0;
411437
envs[0]->hreset_vector = kernel_base;
438+
envs[0]->halted = 0;
412439
}
413440

414441
static QEMUMachine spapr_machine = {

hw/spapr_rtas.c

+78
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,81 @@ static void rtas_power_off(sPAPREnvironment *spapr,
9090
rtas_st(rets, 0, 0);
9191
}
9292

93+
static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr,
94+
uint32_t token, uint32_t nargs,
95+
target_ulong args,
96+
uint32_t nret, target_ulong rets)
97+
{
98+
target_ulong id;
99+
CPUState *env;
100+
101+
if (nargs != 1 || nret != 2) {
102+
rtas_st(rets, 0, -3);
103+
return;
104+
}
105+
106+
id = rtas_ld(args, 0);
107+
for (env = first_cpu; env; env = env->next_cpu) {
108+
if (env->cpu_index != id) {
109+
continue;
110+
}
111+
112+
if (env->halted) {
113+
rtas_st(rets, 1, 0);
114+
} else {
115+
rtas_st(rets, 1, 2);
116+
}
117+
118+
rtas_st(rets, 0, 0);
119+
return;
120+
}
121+
122+
/* Didn't find a matching cpu */
123+
rtas_st(rets, 0, -3);
124+
}
125+
126+
static void rtas_start_cpu(sPAPREnvironment *spapr,
127+
uint32_t token, uint32_t nargs,
128+
target_ulong args,
129+
uint32_t nret, target_ulong rets)
130+
{
131+
target_ulong id, start, r3;
132+
CPUState *env;
133+
134+
if (nargs != 3 || nret != 1) {
135+
rtas_st(rets, 0, -3);
136+
return;
137+
}
138+
139+
id = rtas_ld(args, 0);
140+
start = rtas_ld(args, 1);
141+
r3 = rtas_ld(args, 2);
142+
143+
for (env = first_cpu; env; env = env->next_cpu) {
144+
if (env->cpu_index != id) {
145+
continue;
146+
}
147+
148+
if (!env->halted) {
149+
rtas_st(rets, 0, -1);
150+
return;
151+
}
152+
153+
env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
154+
env->nip = start;
155+
env->gpr[3] = r3;
156+
env->halted = 0;
157+
158+
qemu_cpu_kick(env);
159+
160+
rtas_st(rets, 0, 0);
161+
return;
162+
}
163+
164+
/* Didn't find a matching cpu */
165+
rtas_st(rets, 0, -3);
166+
}
167+
93168
static struct rtas_call {
94169
const char *name;
95170
spapr_rtas_fn fn;
@@ -196,5 +271,8 @@ static void register_core_rtas(void)
196271
spapr_rtas_register("display-character", rtas_display_character);
197272
spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
198273
spapr_rtas_register("power-off", rtas_power_off);
274+
spapr_rtas_register("query-cpu-stopped-state",
275+
rtas_query_cpu_stopped_state);
276+
spapr_rtas_register("start-cpu", rtas_start_cpu);
199277
}
200278
device_init(register_core_rtas);

pc-bios/README

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32
1414
and Sparc64 are built from OpenBIOS SVN revision 1018.
1515

16+
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
17+
implementation for certain IBM POWER hardware. The sources are at
18+
https://github.com/dgibson/SLOF, and the image currently in qemu is
19+
built from git tag qemu-slof-20110323.
20+
1621
- The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0
1722

1823
e1000 8086:100E

pc-bios/slof.bin

566 KB
Binary file not shown.

roms/SLOF

Submodule SLOF added at d1d6b53

0 commit comments

Comments
 (0)