On Thu, Feb 11, 2016 at 01:47:21PM +0100, Thomas Huth wrote: > 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; > + } I think we need to be a little more careful with the address checking. is_ram_adress() just determines whether the address is a potentially valid RAM address - essentially, it will accept anything up to maxram. If the address is for some memory that could be hotplugged but hasn't been (so far), we'll come through to the memory_map() call. This shouldn't do anything dangerous, since the map call will fail, but H_HARDWARE may not be the right error for that situation. > + 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)); > + } > + } Hmm.. I think this might be a little cleaner if instead of individual dcbst and icbi helpers there's a single helper to handle the cache sync of the range - basically copy the kernel's flush_dcache_icache() function into qemu. flush_dcache_icache(). > + 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) I think you need a "memory" clobber on this, or the compiler could move stores from before the dcbst to after, which would break things. > +#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 -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson