diff options
author | David Gibson <david@gibson.dropbear.id.au> | 2011-04-01 15:15:34 +1100 |
---|---|---|
committer | Alexander Graf <agraf@suse.de> | 2011-04-01 18:34:57 +0200 |
commit | a9f8ad8f2acdb2398da5d32a5efc19cb0196d79f (patch) | |
tree | a217360c44ef60c2ad654ecd3cd4836281e29c1f /hw/spapr_rtas.c | |
parent | ed120055c7f9b26b5707d3ceabbe5a3f06aaf937 (diff) |
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 <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'hw/spapr_rtas.c')
-rw-r--r-- | hw/spapr_rtas.c | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c index 72268537a8..16b65422b6 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); |