From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:37641) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aTqek-0006HB-Ie for qemu-devel@nongnu.org; Thu, 11 Feb 2016 07:47:35 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aTqej-0005A0-Dd for qemu-devel@nongnu.org; Thu, 11 Feb 2016 07:47:34 -0500 From: Thomas Huth Date: Thu, 11 Feb 2016 13:47:21 +0100 Message-Id: <1455194841-4283-5-git-send-email-thuth@redhat.com> In-Reply-To: <1455194841-4283-1-git-send-email-thuth@redhat.com> References: <1455194841-4283-1-git-send-email-thuth@redhat.com> Subject: [Qemu-devel] [PATCH v2 4/4] hw/ppc/spapr: Implement the h_page_init hypercall List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-ppc@nongnu.org, david@gibson.dropbear.id.au Cc: qemu-devel@nongnu.org This hypercall either initializes a page with zeros, or copies another page. According to LoPAPR, the i-cache of the page should also be flushed if using H_ICACHE_INVALIDATE or H_ICACHE_SYNCHRONIZE, and the d-cache should be synchronized to the RAM if the H_ICACHE_SYNCHRONIZE flag is used. For this, two new macros are introduced, kvmppc_dcbst() and kvmppc_icbi(), which use the corresponding assembler instructions to flush the caches if running with KVM on Power. If the code runs with TCG instead, the code only uses tb_flush(), assuming that this will be enough for synchronization. Signed-off-by: Thomas Huth --- hw/ppc/spapr_hcall.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ target-ppc/kvm_ppc.h | 24 ++++++++++++++++-- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 6e9b6be..b6d9eee 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -386,6 +386,75 @@ static target_ulong h_set_xdabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } +static target_ulong h_page_init(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + hwaddr dst = args[1]; + hwaddr src = args[2]; + hwaddr len = TARGET_PAGE_SIZE; + uint8_t *pdst, *psrc, *ptr; + + if (flags & ~(H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE + | H_COPY_PAGE | H_ZERO_PAGE)) { + qemu_log_mask(LOG_UNIMP, "h_page_init: Bad flags (" TARGET_FMT_lx "\n", + flags); + return H_PARAMETER; + } + + if (!is_ram_address(spapr, dst) || (dst & ~TARGET_PAGE_MASK) != 0) { + return H_PARAMETER; + } + + /* Map-in source */ + if (flags & H_COPY_PAGE) { + if (!is_ram_address(spapr, src) || (src & ~TARGET_PAGE_MASK) != 0) { + return H_PARAMETER; + } + psrc = cpu_physical_memory_map(src, &len, 0); + if (!psrc || len != TARGET_PAGE_SIZE) { + return H_HARDWARE; + } + } + + /* Map-in destination */ + pdst = cpu_physical_memory_map(dst, &len, 1); + if (!pdst || len != TARGET_PAGE_SIZE) { + if (flags & H_COPY_PAGE) { + cpu_physical_memory_unmap(psrc, len, 0, 0); + } + return H_HARDWARE; + } + + if (flags & H_ZERO_PAGE) { + memset(pdst, 0, len); + } + if (flags & H_COPY_PAGE) { + memcpy(pdst, psrc, len); + cpu_physical_memory_unmap(psrc, len, 0, len); + } + + if (kvm_enabled() && (flags & H_ICACHE_SYNCHRONIZE) != 0) { + for (ptr = pdst; ptr < pdst + len; ptr += env->dcache_line_size) { + kvmppc_dcbst(ptr); + } + } + + if (flags & (H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE)) { + if (kvm_enabled()) { + for (ptr = pdst; ptr < pdst + len; ptr += env->icache_line_size) { + kvmppc_icbi(ptr); + } + } else { + tb_flush(CPU(cpu)); + } + } + + cpu_physical_memory_unmap(pdst, len, 1, len); + return H_SUCCESS; +} + #define FLAGS_REGISTER_VPA 0x0000200000000000ULL #define FLAGS_REGISTER_DTL 0x0000400000000000ULL #define FLAGS_REGISTER_SLBSHADOW 0x0000600000000000ULL @@ -1045,6 +1114,7 @@ static void hypercall_register_types(void) spapr_register_hypercall(H_SET_SPRG0, h_set_sprg0); spapr_register_hypercall(H_SET_DABR, h_set_dabr); spapr_register_hypercall(H_SET_XDABR, h_set_xdabr); + spapr_register_hypercall(H_PAGE_INIT, h_page_init); spapr_register_hypercall(H_SET_MODE, h_set_mode); /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 62406ce..2354aaf 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -254,15 +254,35 @@ static inline int kvmppc_enable_hwrng(void) #endif #ifndef CONFIG_KVM + #define kvmppc_eieio() do { } while (0) -#else +#define kvmppc_dcbst(addr) do { } while (0) +#define kvmppc_icbi(addr) do { } while (0) + +#else /* CONFIG_KVM */ + #define kvmppc_eieio() \ do { \ if (kvm_enabled()) { \ asm volatile("eieio" : : : "memory"); \ } \ } while (0) -#endif + +#define kvmppc_dcbst(addr) \ + do { \ + if (kvm_enabled()) { \ + asm volatile("dcbst 0,%0" : : "r"(addr)); \ + } \ + } while (0) + +#define kvmppc_icbi(addr) \ + do { \ + if (kvm_enabled()) { \ + asm volatile("icbi 0,%0" : : "r"(addr)); \ + } \ + } while (0) + +#endif /* CONFIG_KVM */ #ifndef KVM_INTERRUPT_SET #define KVM_INTERRUPT_SET -1 -- 1.8.3.1