diff options
author | David Gibson <david@gibson.dropbear.id.au> | 2011-04-01 15:15:23 +1100 |
---|---|---|
committer | Alexander Graf <agraf@suse.de> | 2011-04-01 18:34:56 +0200 |
commit | 39ac8455106af1ed669b8e10223420cf1ac5b190 (patch) | |
tree | 48c935a56286ff50f76982c9d7aab1a0eb835e03 /hw/spapr_hcall.c | |
parent | f43e35255cffb6ac6230dd09d308f7909f823f96 (diff) |
Implement hcall based RTAS for pSeries machines
On pSeries machines, operating systems can instantiate "RTAS" (Run-Time
Abstraction Services), a runtime component of the firmware which implements
a number of low-level, infrequently used operations. On logical partitions
under a hypervisor, many of the RTAS functions require hypervisor
privilege. For simplicity, therefore, hypervisor systems typically
implement the in-partition RTAS as just a tiny wrapper around a hypercall
which actually implements the various RTAS functions.
This patch implements such a hypercall based RTAS for our emulated pSeries
machine. A tiny in-partition "firmware" calls a new hypercall, which
looks up available RTAS services in a table.
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'hw/spapr_hcall.c')
-rw-r--r-- | hw/spapr_hcall.c | 44 |
1 files changed, 36 insertions, 8 deletions
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 0ba17172da..d8c721e472 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -248,20 +248,38 @@ static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -spapr_hcall_fn hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; +static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong rtas_r3 = args[0]; + uint32_t token = ldl_phys(rtas_r3); + uint32_t nargs = ldl_phys(rtas_r3 + 4); + uint32_t nret = ldl_phys(rtas_r3 + 8); + + return spapr_rtas_call(spapr, token, nargs, rtas_r3 + 12, + nret, rtas_r3 + 12 + 4*nargs); +} + +spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; +spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE]; void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn) { - spapr_hcall_fn old_fn; + spapr_hcall_fn *slot; + + if (opcode <= MAX_HCALL_OPCODE) { + assert((opcode & 0x3) == 0); - assert(opcode <= MAX_HCALL_OPCODE); - assert((opcode & 0x3) == 0); + slot = &papr_hypercall_table[opcode / 4]; + } else { + assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX)); - old_fn = hypercall_table[opcode / 4]; - assert(!old_fn || (fn == old_fn)); + slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE]; + } - hypercall_table[opcode / 4] = fn; + assert(!(*slot) || (fn == *slot)); + *slot = fn; } target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, @@ -274,7 +292,14 @@ target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, if ((opcode <= MAX_HCALL_OPCODE) && ((opcode & 0x3) == 0)) { - spapr_hcall_fn fn = hypercall_table[opcode / 4]; + spapr_hcall_fn fn = papr_hypercall_table[opcode / 4]; + + if (fn) { + return fn(env, spapr, opcode, args); + } + } else if ((opcode >= KVMPPC_HCALL_BASE) && + (opcode <= KVMPPC_HCALL_MAX)) { + spapr_hcall_fn fn = kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE]; if (fn) { return fn(env, spapr, opcode, args); @@ -291,5 +316,8 @@ static void hypercall_init(void) spapr_register_hypercall(H_ENTER, h_enter); spapr_register_hypercall(H_REMOVE, h_remove); spapr_register_hypercall(H_PROTECT, h_protect); + + /* qemu/KVM-PPC specific hcalls */ + spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); } device_init(hypercall_init); |