aboutsummaryrefslogtreecommitdiff
path: root/target/ppc/kvm.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/ppc/kvm.c')
-rw-r--r--target/ppc/kvm.c144
1 files changed, 131 insertions, 13 deletions
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index 64017acfad..8574c369e6 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -49,6 +49,8 @@
#if defined(TARGET_PPC64)
#include "hw/ppc/spapr_cpu_core.h"
#endif
+#include "elf.h"
+#include "sysemu/kvm_int.h"
//#define DEBUG_KVM
@@ -73,6 +75,7 @@ static int cap_booke_sregs;
static int cap_ppc_smt;
static int cap_ppc_rma;
static int cap_spapr_tce;
+static int cap_spapr_tce_64;
static int cap_spapr_multitce;
static int cap_spapr_vfio;
static int cap_hior;
@@ -83,6 +86,8 @@ static int cap_papr;
static int cap_htab_fd;
static int cap_fixup_hcalls;
static int cap_htm; /* Hardware transactional memory support */
+static int cap_mmu_radix;
+static int cap_mmu_hash_v3;
static uint32_t debug_inst_opcode;
@@ -125,6 +130,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
cap_ppc_smt = kvm_check_extension(s, KVM_CAP_PPC_SMT);
cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA);
cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE);
+ cap_spapr_tce_64 = kvm_check_extension(s, KVM_CAP_SPAPR_TCE_64);
cap_spapr_multitce = kvm_check_extension(s, KVM_CAP_SPAPR_MULTITCE);
cap_spapr_vfio = false;
cap_one_reg = kvm_check_extension(s, KVM_CAP_ONE_REG);
@@ -136,6 +142,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
cap_htab_fd = kvm_check_extension(s, KVM_CAP_PPC_HTAB_FD);
cap_fixup_hcalls = kvm_check_extension(s, KVM_CAP_PPC_FIXUP_HCALL);
cap_htm = kvm_vm_check_extension(s, KVM_CAP_PPC_HTM);
+ cap_mmu_radix = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_RADIX);
+ cap_mmu_hash_v3 = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_HASH_V3);
if (!cap_interrupt_level) {
fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the "
@@ -330,6 +338,61 @@ static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info)
kvm_get_fallback_smmu_info(cpu, info);
}
+struct ppc_radix_page_info *kvm_get_radix_page_info(void)
+{
+ KVMState *s = KVM_STATE(current_machine->accelerator);
+ struct ppc_radix_page_info *radix_page_info;
+ struct kvm_ppc_rmmu_info rmmu_info;
+ int i;
+
+ if (!kvm_check_extension(s, KVM_CAP_PPC_MMU_RADIX)) {
+ return NULL;
+ }
+ if (kvm_vm_ioctl(s, KVM_PPC_GET_RMMU_INFO, &rmmu_info)) {
+ return NULL;
+ }
+ radix_page_info = g_malloc0(sizeof(*radix_page_info));
+ radix_page_info->count = 0;
+ for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) {
+ if (rmmu_info.ap_encodings[i]) {
+ radix_page_info->entries[i] = rmmu_info.ap_encodings[i];
+ radix_page_info->count++;
+ }
+ }
+ return radix_page_info;
+}
+
+target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu,
+ bool radix, bool gtse,
+ uint64_t proc_tbl)
+{
+ CPUState *cs = CPU(cpu);
+ int ret;
+ uint64_t flags = 0;
+ struct kvm_ppc_mmuv3_cfg cfg = {
+ .process_table = proc_tbl,
+ };
+
+ if (radix) {
+ flags |= KVM_PPC_MMUV3_RADIX;
+ }
+ if (gtse) {
+ flags |= KVM_PPC_MMUV3_GTSE;
+ }
+ cfg.flags = flags;
+ ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_CONFIGURE_V3_MMU, &cfg);
+ switch (ret) {
+ case 0:
+ return H_SUCCESS;
+ case -EINVAL:
+ return H_PARAMETER;
+ case -ENODEV:
+ return H_NOT_AVAILABLE;
+ default:
+ return H_HARDWARE;
+ }
+}
+
static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift)
{
if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) {
@@ -509,8 +572,11 @@ int kvm_arch_init_vcpu(CPUState *cs)
case POWERPC_MMU_2_07:
if (!cap_htm && !kvmppc_is_pr(cs->kvm_state)) {
/* KVM-HV has transactional memory on POWER8 also without the
- * KVM_CAP_PPC_HTM extension, so enable it here instead. */
- cap_htm = true;
+ * KVM_CAP_PPC_HTM extension, so enable it here instead as
+ * long as it's availble to userspace on the host. */
+ if (qemu_getauxval(AT_HWCAP2) & PPC_FEATURE2_HAS_HTM) {
+ cap_htm = true;
+ }
}
break;
default:
@@ -2132,13 +2198,24 @@ bool kvmppc_spapr_use_multitce(void)
return cap_spapr_multitce;
}
-void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd,
- bool need_vfio)
+int kvmppc_spapr_enable_inkernel_multitce(void)
+{
+ int ret;
+
+ ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_ENABLE_HCALL, 0,
+ H_PUT_TCE_INDIRECT, 1);
+ if (!ret) {
+ ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_ENABLE_HCALL, 0,
+ H_STUFF_TCE, 1);
+ }
+
+ return ret;
+}
+
+void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift,
+ uint64_t bus_offset, uint32_t nb_table,
+ int *pfd, bool need_vfio)
{
- struct kvm_create_spapr_tce args = {
- .liobn = liobn,
- .window_size = window_size,
- };
long len;
int fd;
void *table;
@@ -2151,14 +2228,41 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd,
return NULL;
}
- fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE, &args);
- if (fd < 0) {
- fprintf(stderr, "KVM: Failed to create TCE table for liobn 0x%x\n",
- liobn);
+ if (cap_spapr_tce_64) {
+ struct kvm_create_spapr_tce_64 args = {
+ .liobn = liobn,
+ .page_shift = page_shift,
+ .offset = bus_offset >> page_shift,
+ .size = nb_table,
+ .flags = 0
+ };
+ fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE_64, &args);
+ if (fd < 0) {
+ fprintf(stderr,
+ "KVM: Failed to create TCE64 table for liobn 0x%x\n",
+ liobn);
+ return NULL;
+ }
+ } else if (cap_spapr_tce) {
+ uint64_t window_size = (uint64_t) nb_table << page_shift;
+ struct kvm_create_spapr_tce args = {
+ .liobn = liobn,
+ .window_size = window_size,
+ };
+ if ((window_size != args.window_size) || bus_offset) {
+ return NULL;
+ }
+ fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE, &args);
+ if (fd < 0) {
+ fprintf(stderr, "KVM: Failed to create TCE table for liobn 0x%x\n",
+ liobn);
+ return NULL;
+ }
+ } else {
return NULL;
}
- len = (window_size / SPAPR_TCE_PAGE_SIZE) * sizeof(uint64_t);
+ len = nb_table * sizeof(uint64_t);
/* FIXME: round this up to page size */
table = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
@@ -2273,6 +2377,10 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data)
if (icache_size != -1) {
pcc->l1_icache_size = icache_size;
}
+
+#if defined(TARGET_PPC64)
+ pcc->radix_page_info = kvm_get_radix_page_info();
+#endif /* defined(TARGET_PPC64) */
}
bool kvmppc_has_cap_epr(void)
@@ -2295,6 +2403,16 @@ bool kvmppc_has_cap_htm(void)
return cap_htm;
}
+bool kvmppc_has_cap_mmu_radix(void)
+{
+ return cap_mmu_radix;
+}
+
+bool kvmppc_has_cap_mmu_hash_v3(void)
+{
+ return cap_mmu_hash_v3;
+}
+
static PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc)
{
ObjectClass *oc = OBJECT_CLASS(pcc);