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