mirror of
https://github.com/clearlinux/kvmtool.git
synced 2026-06-15 18:05:49 +00:00
553a2baf53
We should hard-code less of this stuff, but for now this works. Signed-off-by: Michael Ellerman <michael@ellerman.id.au> Signed-off-by: Pekka Enberg <penberg@kernel.org>
211 lines
5.0 KiB
C
211 lines
5.0 KiB
C
/*
|
|
* PPC CPU identification
|
|
*
|
|
* This is a very simple "host CPU info" struct to get us going.
|
|
* For the little host information we need, I don't want to grub about
|
|
* parsing stuff in /proc/device-tree so just match host PVR to differentiate
|
|
* PPC970 and POWER7 (which is all that's currently supported).
|
|
*
|
|
* Qemu does something similar but this is MUCH simpler!
|
|
*
|
|
* Copyright 2012 Matt Evans <matt@ozlabs.org>, IBM Corporation.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 as published
|
|
* by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <kvm/kvm.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "cpu_info.h"
|
|
#include "kvm/util.h"
|
|
|
|
/* POWER7 */
|
|
|
|
static struct cpu_info cpu_power7_info = {
|
|
.name = "POWER7",
|
|
.tb_freq = 512000000,
|
|
.d_bsize = 128,
|
|
.i_bsize = 128,
|
|
.flags = CPUINFO_FLAG_DFP | CPUINFO_FLAG_VSX | CPUINFO_FLAG_VMX,
|
|
.mmu_info = {
|
|
.flags = KVM_PPC_PAGE_SIZES_REAL | KVM_PPC_1T_SEGMENTS,
|
|
.slb_size = 32,
|
|
},
|
|
};
|
|
|
|
/* POWER8 */
|
|
|
|
static struct cpu_info cpu_power8_info = {
|
|
.name = "POWER8",
|
|
.tb_freq = 512000000,
|
|
.d_bsize = 128,
|
|
.i_bsize = 128,
|
|
.flags = CPUINFO_FLAG_DFP | CPUINFO_FLAG_VSX | CPUINFO_FLAG_VMX,
|
|
.mmu_info = {
|
|
.flags = KVM_PPC_PAGE_SIZES_REAL | KVM_PPC_1T_SEGMENTS,
|
|
.slb_size = 32,
|
|
},
|
|
};
|
|
|
|
/* PPC970/G5 */
|
|
|
|
static struct cpu_info cpu_970_info = {
|
|
.name = "G5",
|
|
.tb_freq = 33333333,
|
|
.d_bsize = 128,
|
|
.i_bsize = 128,
|
|
.flags = CPUINFO_FLAG_VMX,
|
|
};
|
|
|
|
/* This is a default catchall for 'no match' on PVR: */
|
|
static struct cpu_info cpu_dummy_info = { .name = "unknown" };
|
|
|
|
static struct pvr_info host_pvr_info[] = {
|
|
{ 0xffffffff, 0x0f000003, &cpu_power7_info },
|
|
{ 0xffff0000, 0x003f0000, &cpu_power7_info },
|
|
{ 0xffff0000, 0x004a0000, &cpu_power7_info },
|
|
{ 0xffff0000, 0x004b0000, &cpu_power8_info },
|
|
{ 0xffff0000, 0x00390000, &cpu_970_info },
|
|
{ 0xffff0000, 0x003c0000, &cpu_970_info },
|
|
{ 0xffff0000, 0x00440000, &cpu_970_info },
|
|
{ 0xffff0000, 0x00450000, &cpu_970_info },
|
|
};
|
|
|
|
/* If we can't query the kernel for supported page sizes assume 4K and 16M */
|
|
static struct kvm_ppc_one_seg_page_size fallback_sps[] = {
|
|
[0] = {
|
|
.page_shift = 12,
|
|
.slb_enc = 0,
|
|
.enc = {
|
|
[0] = {
|
|
.page_shift = 12,
|
|
.pte_enc = 0,
|
|
},
|
|
},
|
|
},
|
|
[1] = {
|
|
.page_shift = 24,
|
|
.slb_enc = 0x100,
|
|
.enc = {
|
|
[0] = {
|
|
.page_shift = 24,
|
|
.pte_enc = 0,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
|
|
static void setup_mmu_info(struct kvm *kvm, struct cpu_info *cpu_info)
|
|
{
|
|
static struct kvm_ppc_smmu_info *mmu_info;
|
|
struct kvm_ppc_one_seg_page_size *sps;
|
|
int i, j, k, valid;
|
|
|
|
if (!kvm__supports_extension(kvm, KVM_CAP_PPC_GET_SMMU_INFO)) {
|
|
memcpy(&cpu_info->mmu_info.sps, fallback_sps, sizeof(fallback_sps));
|
|
} else if (ioctl(kvm->vm_fd, KVM_PPC_GET_SMMU_INFO, &cpu_info->mmu_info) < 0) {
|
|
die_perror("KVM_PPC_GET_SMMU_INFO failed");
|
|
}
|
|
|
|
mmu_info = &cpu_info->mmu_info;
|
|
|
|
if (!(mmu_info->flags & KVM_PPC_PAGE_SIZES_REAL))
|
|
/* Guest pages are not restricted by the backing page size */
|
|
return;
|
|
|
|
/* Filter based on backing page size */
|
|
|
|
for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) {
|
|
sps = &mmu_info->sps[i];
|
|
|
|
if (!sps->page_shift)
|
|
break;
|
|
|
|
if (kvm->ram_pagesize < (1ul << sps->page_shift)) {
|
|
/* Mark the whole segment size invalid */
|
|
sps->page_shift = 0;
|
|
continue;
|
|
}
|
|
|
|
/* Check each page size for the segment */
|
|
for (j = 0, valid = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
|
|
if (!sps->enc[j].page_shift)
|
|
break;
|
|
|
|
if (kvm->ram_pagesize < (1ul << sps->enc[j].page_shift))
|
|
sps->enc[j].page_shift = 0;
|
|
else
|
|
valid++;
|
|
}
|
|
|
|
if (!valid) {
|
|
/* Mark the whole segment size invalid */
|
|
sps->page_shift = 0;
|
|
continue;
|
|
}
|
|
|
|
/* Mark any trailing entries invalid if we broke out early */
|
|
for (k = j; k < KVM_PPC_PAGE_SIZES_MAX_SZ; k++)
|
|
sps->enc[k].page_shift = 0;
|
|
|
|
/* Collapse holes */
|
|
for (j = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
|
|
if (sps->enc[j].page_shift)
|
|
continue;
|
|
|
|
for (k = j + 1; k < KVM_PPC_PAGE_SIZES_MAX_SZ; k++) {
|
|
if (sps->enc[k].page_shift) {
|
|
sps->enc[j] = sps->enc[k];
|
|
sps->enc[k].page_shift = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Mark any trailing entries invalid if we broke out early */
|
|
for (j = i; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++)
|
|
mmu_info->sps[j].page_shift = 0;
|
|
|
|
/* Collapse holes */
|
|
for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) {
|
|
if (mmu_info->sps[i].page_shift)
|
|
continue;
|
|
|
|
for (j = i + 1; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
|
|
if (mmu_info->sps[j].page_shift) {
|
|
mmu_info->sps[i] = mmu_info->sps[j];
|
|
mmu_info->sps[j].page_shift = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct cpu_info *find_cpu_info(struct kvm *kvm)
|
|
{
|
|
struct cpu_info *info;
|
|
unsigned int i;
|
|
u32 pvr = kvm->arch.pvr;
|
|
|
|
for (info = NULL, i = 0; i < ARRAY_SIZE(host_pvr_info); i++) {
|
|
if ((pvr & host_pvr_info[i].pvr_mask) == host_pvr_info[i].pvr) {
|
|
info = host_pvr_info[i].cpu_info;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Didn't find anything? Rut-ro. */
|
|
if (!info) {
|
|
pr_warning("Host CPU unsupported by kvmtool\n");
|
|
info = &cpu_dummy_info;
|
|
}
|
|
|
|
setup_mmu_info(kvm, info);
|
|
|
|
return info;
|
|
}
|