summaryrefslogtreecommitdiff
path: root/hw/spapr_hcall.c
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2011-04-01 15:15:33 +1100
committerAlexander Graf <agraf@suse.de>2011-04-01 18:34:57 +0200
commited120055c7f9b26b5707d3ceabbe5a3f06aaf937 (patch)
tree4c76031ec0bbf527e0de0a4bec2f635dd598aa56 /hw/spapr_hcall.c
parent08942ac17922d923a7cc5cf9854e9cc4b150b942 (diff)
downloadqemu-ed120055c7f9b26b5707d3ceabbe5a3f06aaf937.tar.gz
Implement PAPR VPA functions for pSeries shared processor partitions
Shared-processor partitions are those where a CPU is time-sliced between partitions, rather than being permanently dedicated to a single partition. qemu emulated partitions, since they are just scheduled with the qemu user process, behave mostly like shared processor partitions. In order to better support shared processor partitions (splpar), PAPR defines the "VPA" (Virtual Processor Area), a shared memory communication channel between the hypervisor and partitions. There are also two additional shared memory communication areas for specialized purposes associated with the VPA. A VPA is not essential for operating an splpar, though it can be necessary for obtaining accurate performance measurements in the presence of runtime partition switching. Most importantly, however, the VPA is a prerequisite for PAPR's H_CEDE, hypercall, which allows a partition OS to give up it's shared processor timeslices to other partitions when idle. This patch implements the VPA and H_CEDE hypercalls in qemu. We don't implement any of the more advanced statistics which can be communicated through the VPA. However, this is enough to make normal pSeries kernels do an effective power-save idle on an emulated pSeries, significantly reducing the host load of a qemu emulated pSeries running an idle guest OS. 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.c192
1 files changed, 192 insertions, 0 deletions
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index a47a97ba55..f88e1d2083 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -4,6 +4,8 @@
#include "sysemu.h"
#include "qemu-char.h"
#include "exec-all.h"
+#include "exec.h"
+#include "helper_regs.h"
#include "hw/spapr.h"
#define HPTES_PER_GROUP 8
@@ -255,6 +257,192 @@ static target_ulong h_set_dabr(CPUState *env, sPAPREnvironment *spapr,
return H_HARDWARE;
}
+#define FLAGS_REGISTER_VPA 0x0000200000000000ULL
+#define FLAGS_REGISTER_DTL 0x0000400000000000ULL
+#define FLAGS_REGISTER_SLBSHADOW 0x0000600000000000ULL
+#define FLAGS_DEREGISTER_VPA 0x0000a00000000000ULL
+#define FLAGS_DEREGISTER_DTL 0x0000c00000000000ULL
+#define FLAGS_DEREGISTER_SLBSHADOW 0x0000e00000000000ULL
+
+#define VPA_MIN_SIZE 640
+#define VPA_SIZE_OFFSET 0x4
+#define VPA_SHARED_PROC_OFFSET 0x9
+#define VPA_SHARED_PROC_VAL 0x2
+
+static target_ulong register_vpa(CPUState *env, target_ulong vpa)
+{
+ uint16_t size;
+ uint8_t tmp;
+
+ if (vpa == 0) {
+ hcall_dprintf("Can't cope with registering a VPA at logical 0\n");
+ return H_HARDWARE;
+ }
+
+ if (vpa % env->dcache_line_size) {
+ return H_PARAMETER;
+ }
+ /* FIXME: bounds check the address */
+
+ size = lduw_phys(vpa + 0x4);
+
+ if (size < VPA_MIN_SIZE) {
+ return H_PARAMETER;
+ }
+
+ /* VPA is not allowed to cross a page boundary */
+ if ((vpa / 4096) != ((vpa + size - 1) / 4096)) {
+ return H_PARAMETER;
+ }
+
+ env->vpa = vpa;
+
+ tmp = ldub_phys(env->vpa + VPA_SHARED_PROC_OFFSET);
+ tmp |= VPA_SHARED_PROC_VAL;
+ stb_phys(env->vpa + VPA_SHARED_PROC_OFFSET, tmp);
+
+ return H_SUCCESS;
+}
+
+static target_ulong deregister_vpa(CPUState *env, target_ulong vpa)
+{
+ if (env->slb_shadow) {
+ return H_RESOURCE;
+ }
+
+ if (env->dispatch_trace_log) {
+ return H_RESOURCE;
+ }
+
+ env->vpa = 0;
+ return H_SUCCESS;
+}
+
+static target_ulong register_slb_shadow(CPUState *env, target_ulong addr)
+{
+ uint32_t size;
+
+ if (addr == 0) {
+ hcall_dprintf("Can't cope with SLB shadow at logical 0\n");
+ return H_HARDWARE;
+ }
+
+ size = ldl_phys(addr + 0x4);
+ if (size < 0x8) {
+ return H_PARAMETER;
+ }
+
+ if ((addr / 4096) != ((addr + size - 1) / 4096)) {
+ return H_PARAMETER;
+ }
+
+ if (!env->vpa) {
+ return H_RESOURCE;
+ }
+
+ env->slb_shadow = addr;
+
+ return H_SUCCESS;
+}
+
+static target_ulong deregister_slb_shadow(CPUState *env, target_ulong addr)
+{
+ env->slb_shadow = 0;
+ return H_SUCCESS;
+}
+
+static target_ulong register_dtl(CPUState *env, target_ulong addr)
+{
+ uint32_t size;
+
+ if (addr == 0) {
+ hcall_dprintf("Can't cope with DTL at logical 0\n");
+ return H_HARDWARE;
+ }
+
+ size = ldl_phys(addr + 0x4);
+
+ if (size < 48) {
+ return H_PARAMETER;
+ }
+
+ if (!env->vpa) {
+ return H_RESOURCE;
+ }
+
+ env->dispatch_trace_log = addr;
+ env->dtl_size = size;
+
+ return H_SUCCESS;
+}
+
+static target_ulong deregister_dtl(CPUState *emv, target_ulong addr)
+{
+ env->dispatch_trace_log = 0;
+ env->dtl_size = 0;
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_register_vpa(CPUState *env, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong flags = args[0];
+ target_ulong procno = args[1];
+ target_ulong vpa = args[2];
+ target_ulong ret = H_PARAMETER;
+ CPUState *tenv;
+
+ for (tenv = first_cpu; tenv; tenv = tenv->next_cpu) {
+ if (tenv->cpu_index == procno) {
+ break;
+ }
+ }
+
+ if (!tenv) {
+ return H_PARAMETER;
+ }
+
+ switch (flags) {
+ case FLAGS_REGISTER_VPA:
+ ret = register_vpa(tenv, vpa);
+ break;
+
+ case FLAGS_DEREGISTER_VPA:
+ ret = deregister_vpa(tenv, vpa);
+ break;
+
+ case FLAGS_REGISTER_SLBSHADOW:
+ ret = register_slb_shadow(tenv, vpa);
+ break;
+
+ case FLAGS_DEREGISTER_SLBSHADOW:
+ ret = deregister_slb_shadow(tenv, vpa);
+ break;
+
+ case FLAGS_REGISTER_DTL:
+ ret = register_dtl(tenv, vpa);
+ break;
+
+ case FLAGS_DEREGISTER_DTL:
+ ret = deregister_dtl(tenv, vpa);
+ break;
+ }
+
+ return ret;
+}
+
+static target_ulong h_cede(CPUState *env, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ env->msr |= (1ULL << MSR_EE);
+ hreg_compute_hflags(env);
+ if (!cpu_has_work(env)) {
+ env->halted = 1;
+ }
+ return H_SUCCESS;
+}
+
static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
@@ -327,6 +515,10 @@ static void hypercall_init(void)
/* hcall-dabr */
spapr_register_hypercall(H_SET_DABR, h_set_dabr);
+ /* hcall-splpar */
+ spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
+ spapr_register_hypercall(H_CEDE, h_cede);
+
/* qemu/KVM-PPC specific hcalls */
spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
}