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