xref: /kvmtool/powerpc/cpu_info.c (revision de494ec5a1b557d4ac61d6cd67d1a62630a5498a)
1d391177aSMatt Evans /*
2d391177aSMatt Evans  * PPC CPU identification
3d391177aSMatt Evans  *
4d391177aSMatt Evans  * This is a very simple "host CPU info" struct to get us going.
5d391177aSMatt Evans  * For the little host information we need, I don't want to grub about
6d391177aSMatt Evans  * parsing stuff in /proc/device-tree so just match host PVR to differentiate
7d391177aSMatt Evans  * PPC970 and POWER7 (which is all that's currently supported).
8d391177aSMatt Evans  *
9d391177aSMatt Evans  * Qemu does something similar but this is MUCH simpler!
10d391177aSMatt Evans  *
11d391177aSMatt Evans  * Copyright 2012 Matt Evans <matt@ozlabs.org>, IBM Corporation.
12d391177aSMatt Evans  *
13d391177aSMatt Evans  * This program is free software; you can redistribute it and/or modify it
14d391177aSMatt Evans  * under the terms of the GNU General Public License version 2 as published
15d391177aSMatt Evans  * by the Free Software Foundation.
16d391177aSMatt Evans  */
17d391177aSMatt Evans 
18*de494ec5SMichael Ellerman #include <kvm/kvm.h>
19*de494ec5SMichael Ellerman #include <sys/ioctl.h>
20*de494ec5SMichael Ellerman 
21d391177aSMatt Evans #include "cpu_info.h"
22d391177aSMatt Evans #include "kvm/util.h"
23d391177aSMatt Evans 
24d391177aSMatt Evans /* POWER7 */
25d391177aSMatt Evans 
26d391177aSMatt Evans /* POWER7 has 1T segments, so advertise these */
27d391177aSMatt Evans static u32 power7_segment_sizes_prop[] = {0x1c, 0x28, 0xffffffff, 0xffffffff};
28d391177aSMatt Evans 
29d391177aSMatt Evans static struct cpu_info cpu_power7_info = {
3095477af3SMichael Ellerman 	.name = "POWER7",
3195477af3SMichael Ellerman 	.segment_sizes_prop = power7_segment_sizes_prop,
3295477af3SMichael Ellerman 	.segment_sizes_prop_len = sizeof(power7_segment_sizes_prop),
3395477af3SMichael Ellerman 	.slb_size = 32,
3495477af3SMichael Ellerman 	.tb_freq = 512000000,
3595477af3SMichael Ellerman 	.d_bsize = 128,
3695477af3SMichael Ellerman 	.i_bsize = 128,
3795477af3SMichael Ellerman 	.flags = CPUINFO_FLAG_DFP | CPUINFO_FLAG_VSX | CPUINFO_FLAG_VMX,
38*de494ec5SMichael Ellerman 	.mmu_info = {
39*de494ec5SMichael Ellerman 		.flags = KVM_PPC_PAGE_SIZES_REAL | KVM_PPC_1T_SEGMENTS,
40*de494ec5SMichael Ellerman 	},
41d391177aSMatt Evans };
42d391177aSMatt Evans 
43d391177aSMatt Evans /* PPC970/G5 */
44d391177aSMatt Evans 
45d391177aSMatt Evans static struct cpu_info cpu_970_info = {
4695477af3SMichael Ellerman 	.name = "G5",
4795477af3SMichael Ellerman 	.segment_sizes_prop = NULL /* no segment sizes prop, use defaults */,
4895477af3SMichael Ellerman 	.segment_sizes_prop_len = 0,
4995477af3SMichael Ellerman 	.slb_size = 0,
5095477af3SMichael Ellerman 	.tb_freq = 33333333,
5195477af3SMichael Ellerman 	.d_bsize = 128,
5295477af3SMichael Ellerman 	.i_bsize = 128,
5395477af3SMichael Ellerman 	.flags = CPUINFO_FLAG_VMX,
54d391177aSMatt Evans };
55d391177aSMatt Evans 
56d391177aSMatt Evans /* This is a default catchall for 'no match' on PVR: */
5795477af3SMichael Ellerman static struct cpu_info cpu_dummy_info = { .name = "unknown" };
58d391177aSMatt Evans 
59d391177aSMatt Evans static struct pvr_info host_pvr_info[] = {
60d391177aSMatt Evans 	{ 0xffffffff, 0x0f000003, &cpu_power7_info },
61d391177aSMatt Evans 	{ 0xffff0000, 0x003f0000, &cpu_power7_info },
62d391177aSMatt Evans 	{ 0xffff0000, 0x004a0000, &cpu_power7_info },
63d391177aSMatt Evans 	{ 0xffff0000, 0x00390000, &cpu_970_info },
64d391177aSMatt Evans 	{ 0xffff0000, 0x003c0000, &cpu_970_info },
65d391177aSMatt Evans         { 0xffff0000, 0x00440000, &cpu_970_info },
66d391177aSMatt Evans         { 0xffff0000, 0x00450000, &cpu_970_info },
67d391177aSMatt Evans };
68d391177aSMatt Evans 
69*de494ec5SMichael Ellerman /* If we can't query the kernel for supported page sizes assume 4K and 16M */
70*de494ec5SMichael Ellerman static struct kvm_ppc_one_seg_page_size fallback_sps[] = {
71*de494ec5SMichael Ellerman 	[0] = {
72*de494ec5SMichael Ellerman 		.page_shift = 12,
73*de494ec5SMichael Ellerman 		.slb_enc    = 0,
74*de494ec5SMichael Ellerman 		.enc =  {
75*de494ec5SMichael Ellerman 			[0] = {
76*de494ec5SMichael Ellerman 				.page_shift = 12,
77*de494ec5SMichael Ellerman 				.pte_enc    = 0,
78*de494ec5SMichael Ellerman 			},
79*de494ec5SMichael Ellerman 		},
80*de494ec5SMichael Ellerman 	},
81*de494ec5SMichael Ellerman 	[1] = {
82*de494ec5SMichael Ellerman 		.page_shift = 24,
83*de494ec5SMichael Ellerman 		.slb_enc    = 0x100,
84*de494ec5SMichael Ellerman 		.enc =  {
85*de494ec5SMichael Ellerman 			[0] = {
86*de494ec5SMichael Ellerman 				.page_shift = 24,
87*de494ec5SMichael Ellerman 				.pte_enc    = 0,
88*de494ec5SMichael Ellerman 			},
89*de494ec5SMichael Ellerman 		},
90*de494ec5SMichael Ellerman 	},
91*de494ec5SMichael Ellerman };
92*de494ec5SMichael Ellerman 
93*de494ec5SMichael Ellerman 
94*de494ec5SMichael Ellerman static void setup_mmu_info(struct kvm *kvm, struct cpu_info *cpu_info)
95*de494ec5SMichael Ellerman {
96*de494ec5SMichael Ellerman 	static struct kvm_ppc_smmu_info *mmu_info;
97*de494ec5SMichael Ellerman 	struct kvm_ppc_one_seg_page_size *sps;
98*de494ec5SMichael Ellerman 	int i, j, k, valid;
99*de494ec5SMichael Ellerman 
100*de494ec5SMichael Ellerman 	if (!kvm__supports_extension(kvm, KVM_CAP_PPC_GET_SMMU_INFO)) {
101*de494ec5SMichael Ellerman 		memcpy(&cpu_info->mmu_info.sps, fallback_sps, sizeof(fallback_sps));
102*de494ec5SMichael Ellerman 	} else if (ioctl(kvm->vm_fd, KVM_PPC_GET_SMMU_INFO, &cpu_info->mmu_info) < 0) {
103*de494ec5SMichael Ellerman 			die_perror("KVM_PPC_GET_SMMU_INFO failed");
104*de494ec5SMichael Ellerman 	}
105*de494ec5SMichael Ellerman 
106*de494ec5SMichael Ellerman 	mmu_info = &cpu_info->mmu_info;
107*de494ec5SMichael Ellerman 
108*de494ec5SMichael Ellerman 	if (!(mmu_info->flags & KVM_PPC_PAGE_SIZES_REAL))
109*de494ec5SMichael Ellerman 		/* Guest pages are not restricted by the backing page size */
110*de494ec5SMichael Ellerman 		return;
111*de494ec5SMichael Ellerman 
112*de494ec5SMichael Ellerman 	/* Filter based on backing page size */
113*de494ec5SMichael Ellerman 
114*de494ec5SMichael Ellerman 	for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) {
115*de494ec5SMichael Ellerman 		sps = &mmu_info->sps[i];
116*de494ec5SMichael Ellerman 
117*de494ec5SMichael Ellerman 		if (!sps->page_shift)
118*de494ec5SMichael Ellerman 			break;
119*de494ec5SMichael Ellerman 
120*de494ec5SMichael Ellerman 		if (kvm->ram_pagesize < (1ul << sps->page_shift)) {
121*de494ec5SMichael Ellerman 			/* Mark the whole segment size invalid */
122*de494ec5SMichael Ellerman 			sps->page_shift = 0;
123*de494ec5SMichael Ellerman 			continue;
124*de494ec5SMichael Ellerman 		}
125*de494ec5SMichael Ellerman 
126*de494ec5SMichael Ellerman 		/* Check each page size for the segment */
127*de494ec5SMichael Ellerman 		for (j = 0, valid = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
128*de494ec5SMichael Ellerman 			if (!sps->enc[j].page_shift)
129*de494ec5SMichael Ellerman 				break;
130*de494ec5SMichael Ellerman 
131*de494ec5SMichael Ellerman 			if (kvm->ram_pagesize < (1ul << sps->enc[j].page_shift))
132*de494ec5SMichael Ellerman 				sps->enc[j].page_shift = 0;
133*de494ec5SMichael Ellerman 			else
134*de494ec5SMichael Ellerman 				valid++;
135*de494ec5SMichael Ellerman 		}
136*de494ec5SMichael Ellerman 
137*de494ec5SMichael Ellerman 		if (!valid) {
138*de494ec5SMichael Ellerman 			/* Mark the whole segment size invalid */
139*de494ec5SMichael Ellerman 			sps->page_shift = 0;
140*de494ec5SMichael Ellerman 			continue;
141*de494ec5SMichael Ellerman 		}
142*de494ec5SMichael Ellerman 
143*de494ec5SMichael Ellerman 		/* Mark any trailing entries invalid if we broke out early */
144*de494ec5SMichael Ellerman 		for (k = j; k < KVM_PPC_PAGE_SIZES_MAX_SZ; k++)
145*de494ec5SMichael Ellerman 			sps->enc[k].page_shift = 0;
146*de494ec5SMichael Ellerman 
147*de494ec5SMichael Ellerman 		/* Collapse holes */
148*de494ec5SMichael Ellerman 		for (j = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
149*de494ec5SMichael Ellerman 			if (sps->enc[j].page_shift)
150*de494ec5SMichael Ellerman 				continue;
151*de494ec5SMichael Ellerman 
152*de494ec5SMichael Ellerman 			for (k = j + 1; k < KVM_PPC_PAGE_SIZES_MAX_SZ; k++) {
153*de494ec5SMichael Ellerman 				if (sps->enc[k].page_shift) {
154*de494ec5SMichael Ellerman 					sps->enc[j] = sps->enc[k];
155*de494ec5SMichael Ellerman 					sps->enc[k].page_shift = 0;
156*de494ec5SMichael Ellerman 					break;
157*de494ec5SMichael Ellerman 				}
158*de494ec5SMichael Ellerman 			}
159*de494ec5SMichael Ellerman 		}
160*de494ec5SMichael Ellerman 	}
161*de494ec5SMichael Ellerman 
162*de494ec5SMichael Ellerman 	/* Mark any trailing entries invalid if we broke out early */
163*de494ec5SMichael Ellerman 	for (j = i; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++)
164*de494ec5SMichael Ellerman 		mmu_info->sps[j].page_shift = 0;
165*de494ec5SMichael Ellerman 
166*de494ec5SMichael Ellerman 	/* Collapse holes */
167*de494ec5SMichael Ellerman 	for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) {
168*de494ec5SMichael Ellerman 		if (mmu_info->sps[i].page_shift)
169*de494ec5SMichael Ellerman 			continue;
170*de494ec5SMichael Ellerman 
171*de494ec5SMichael Ellerman 		for (j = i + 1; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
172*de494ec5SMichael Ellerman 			if (mmu_info->sps[j].page_shift) {
173*de494ec5SMichael Ellerman 				mmu_info->sps[i] = mmu_info->sps[j];
174*de494ec5SMichael Ellerman 				mmu_info->sps[j].page_shift = 0;
175*de494ec5SMichael Ellerman 				break;
176*de494ec5SMichael Ellerman 			}
177*de494ec5SMichael Ellerman 		}
178*de494ec5SMichael Ellerman 	}
179*de494ec5SMichael Ellerman }
180*de494ec5SMichael Ellerman 
1814a75c603SMichael Ellerman struct cpu_info *find_cpu_info(struct kvm *kvm)
182d391177aSMatt Evans {
1834a75c603SMichael Ellerman 	struct cpu_info *info;
184d391177aSMatt Evans 	unsigned int i;
1854a75c603SMichael Ellerman 	u32 pvr = kvm->pvr;
186ac92dd43SMichael Ellerman 
1874a75c603SMichael Ellerman 	for (info = NULL, i = 0; i < ARRAY_SIZE(host_pvr_info); i++) {
188ac92dd43SMichael Ellerman 		if ((pvr & host_pvr_info[i].pvr_mask) == host_pvr_info[i].pvr) {
1894a75c603SMichael Ellerman 			info = host_pvr_info[i].cpu_info;
1904a75c603SMichael Ellerman 			break;
191d391177aSMatt Evans 		}
192d391177aSMatt Evans 	}
193ac92dd43SMichael Ellerman 
194d391177aSMatt Evans 	/* Didn't find anything? Rut-ro. */
1954a75c603SMichael Ellerman 	if (!info) {
196d391177aSMatt Evans 		pr_warning("Host CPU unsupported by kvmtool\n");
1974a75c603SMichael Ellerman 		info = &cpu_dummy_info;
1984a75c603SMichael Ellerman 	}
199ac92dd43SMichael Ellerman 
200*de494ec5SMichael Ellerman 	setup_mmu_info(kvm, info);
201*de494ec5SMichael Ellerman 
2024a75c603SMichael Ellerman 	return info;
203d391177aSMatt Evans }
204