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
18de494ec5SMichael Ellerman #include <kvm/kvm.h>
19de494ec5SMichael Ellerman #include <sys/ioctl.h>
20de494ec5SMichael Ellerman
21d391177aSMatt Evans #include "cpu_info.h"
22d391177aSMatt Evans #include "kvm/util.h"
23d391177aSMatt Evans
24d391177aSMatt Evans /* POWER7 */
25d391177aSMatt Evans
26d391177aSMatt Evans static struct cpu_info cpu_power7_info = {
2795477af3SMichael Ellerman .name = "POWER7",
2895477af3SMichael Ellerman .tb_freq = 512000000,
2995477af3SMichael Ellerman .d_bsize = 128,
3095477af3SMichael Ellerman .i_bsize = 128,
3195477af3SMichael Ellerman .flags = CPUINFO_FLAG_DFP | CPUINFO_FLAG_VSX | CPUINFO_FLAG_VMX,
32de494ec5SMichael Ellerman .mmu_info = {
33de494ec5SMichael Ellerman .flags = KVM_PPC_PAGE_SIZES_REAL | KVM_PPC_1T_SEGMENTS,
34cb90966cSMichael Ellerman .slb_size = 32,
35de494ec5SMichael Ellerman },
36d391177aSMatt Evans };
37d391177aSMatt Evans
38*553a2bafSMichael Ellerman /* POWER8 */
39*553a2bafSMichael Ellerman
40*553a2bafSMichael Ellerman static struct cpu_info cpu_power8_info = {
41*553a2bafSMichael Ellerman .name = "POWER8",
42*553a2bafSMichael Ellerman .tb_freq = 512000000,
43*553a2bafSMichael Ellerman .d_bsize = 128,
44*553a2bafSMichael Ellerman .i_bsize = 128,
45*553a2bafSMichael Ellerman .flags = CPUINFO_FLAG_DFP | CPUINFO_FLAG_VSX | CPUINFO_FLAG_VMX,
46*553a2bafSMichael Ellerman .mmu_info = {
47*553a2bafSMichael Ellerman .flags = KVM_PPC_PAGE_SIZES_REAL | KVM_PPC_1T_SEGMENTS,
48*553a2bafSMichael Ellerman .slb_size = 32,
49*553a2bafSMichael Ellerman },
50*553a2bafSMichael Ellerman };
51*553a2bafSMichael Ellerman
52d391177aSMatt Evans /* PPC970/G5 */
53d391177aSMatt Evans
54d391177aSMatt Evans static struct cpu_info cpu_970_info = {
5595477af3SMichael Ellerman .name = "G5",
5695477af3SMichael Ellerman .tb_freq = 33333333,
5795477af3SMichael Ellerman .d_bsize = 128,
5895477af3SMichael Ellerman .i_bsize = 128,
5995477af3SMichael Ellerman .flags = CPUINFO_FLAG_VMX,
60d391177aSMatt Evans };
61d391177aSMatt Evans
62d391177aSMatt Evans /* This is a default catchall for 'no match' on PVR: */
6395477af3SMichael Ellerman static struct cpu_info cpu_dummy_info = { .name = "unknown" };
64d391177aSMatt Evans
65d391177aSMatt Evans static struct pvr_info host_pvr_info[] = {
66d391177aSMatt Evans { 0xffffffff, 0x0f000003, &cpu_power7_info },
67d391177aSMatt Evans { 0xffff0000, 0x003f0000, &cpu_power7_info },
68d391177aSMatt Evans { 0xffff0000, 0x004a0000, &cpu_power7_info },
69*553a2bafSMichael Ellerman { 0xffff0000, 0x004b0000, &cpu_power8_info },
70d391177aSMatt Evans { 0xffff0000, 0x00390000, &cpu_970_info },
71d391177aSMatt Evans { 0xffff0000, 0x003c0000, &cpu_970_info },
72d391177aSMatt Evans { 0xffff0000, 0x00440000, &cpu_970_info },
73d391177aSMatt Evans { 0xffff0000, 0x00450000, &cpu_970_info },
74d391177aSMatt Evans };
75d391177aSMatt Evans
76de494ec5SMichael Ellerman /* If we can't query the kernel for supported page sizes assume 4K and 16M */
77de494ec5SMichael Ellerman static struct kvm_ppc_one_seg_page_size fallback_sps[] = {
78de494ec5SMichael Ellerman [0] = {
79de494ec5SMichael Ellerman .page_shift = 12,
80de494ec5SMichael Ellerman .slb_enc = 0,
81de494ec5SMichael Ellerman .enc = {
82de494ec5SMichael Ellerman [0] = {
83de494ec5SMichael Ellerman .page_shift = 12,
84de494ec5SMichael Ellerman .pte_enc = 0,
85de494ec5SMichael Ellerman },
86de494ec5SMichael Ellerman },
87de494ec5SMichael Ellerman },
88de494ec5SMichael Ellerman [1] = {
89de494ec5SMichael Ellerman .page_shift = 24,
90de494ec5SMichael Ellerman .slb_enc = 0x100,
91de494ec5SMichael Ellerman .enc = {
92de494ec5SMichael Ellerman [0] = {
93de494ec5SMichael Ellerman .page_shift = 24,
94de494ec5SMichael Ellerman .pte_enc = 0,
95de494ec5SMichael Ellerman },
96de494ec5SMichael Ellerman },
97de494ec5SMichael Ellerman },
98de494ec5SMichael Ellerman };
99de494ec5SMichael Ellerman
100de494ec5SMichael Ellerman
setup_mmu_info(struct kvm * kvm,struct cpu_info * cpu_info)101de494ec5SMichael Ellerman static void setup_mmu_info(struct kvm *kvm, struct cpu_info *cpu_info)
102de494ec5SMichael Ellerman {
103de494ec5SMichael Ellerman static struct kvm_ppc_smmu_info *mmu_info;
104de494ec5SMichael Ellerman struct kvm_ppc_one_seg_page_size *sps;
105de494ec5SMichael Ellerman int i, j, k, valid;
106de494ec5SMichael Ellerman
107de494ec5SMichael Ellerman if (!kvm__supports_extension(kvm, KVM_CAP_PPC_GET_SMMU_INFO)) {
108de494ec5SMichael Ellerman memcpy(&cpu_info->mmu_info.sps, fallback_sps, sizeof(fallback_sps));
109de494ec5SMichael Ellerman } else if (ioctl(kvm->vm_fd, KVM_PPC_GET_SMMU_INFO, &cpu_info->mmu_info) < 0) {
110de494ec5SMichael Ellerman die_perror("KVM_PPC_GET_SMMU_INFO failed");
111de494ec5SMichael Ellerman }
112de494ec5SMichael Ellerman
113de494ec5SMichael Ellerman mmu_info = &cpu_info->mmu_info;
114de494ec5SMichael Ellerman
115de494ec5SMichael Ellerman if (!(mmu_info->flags & KVM_PPC_PAGE_SIZES_REAL))
116de494ec5SMichael Ellerman /* Guest pages are not restricted by the backing page size */
117de494ec5SMichael Ellerman return;
118de494ec5SMichael Ellerman
119de494ec5SMichael Ellerman /* Filter based on backing page size */
120de494ec5SMichael Ellerman
121de494ec5SMichael Ellerman for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) {
122de494ec5SMichael Ellerman sps = &mmu_info->sps[i];
123de494ec5SMichael Ellerman
124de494ec5SMichael Ellerman if (!sps->page_shift)
125de494ec5SMichael Ellerman break;
126de494ec5SMichael Ellerman
127de494ec5SMichael Ellerman if (kvm->ram_pagesize < (1ul << sps->page_shift)) {
128de494ec5SMichael Ellerman /* Mark the whole segment size invalid */
129de494ec5SMichael Ellerman sps->page_shift = 0;
130de494ec5SMichael Ellerman continue;
131de494ec5SMichael Ellerman }
132de494ec5SMichael Ellerman
133de494ec5SMichael Ellerman /* Check each page size for the segment */
134de494ec5SMichael Ellerman for (j = 0, valid = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
135de494ec5SMichael Ellerman if (!sps->enc[j].page_shift)
136de494ec5SMichael Ellerman break;
137de494ec5SMichael Ellerman
138de494ec5SMichael Ellerman if (kvm->ram_pagesize < (1ul << sps->enc[j].page_shift))
139de494ec5SMichael Ellerman sps->enc[j].page_shift = 0;
140de494ec5SMichael Ellerman else
141de494ec5SMichael Ellerman valid++;
142de494ec5SMichael Ellerman }
143de494ec5SMichael Ellerman
144de494ec5SMichael Ellerman if (!valid) {
145de494ec5SMichael Ellerman /* Mark the whole segment size invalid */
146de494ec5SMichael Ellerman sps->page_shift = 0;
147de494ec5SMichael Ellerman continue;
148de494ec5SMichael Ellerman }
149de494ec5SMichael Ellerman
150de494ec5SMichael Ellerman /* Mark any trailing entries invalid if we broke out early */
151de494ec5SMichael Ellerman for (k = j; k < KVM_PPC_PAGE_SIZES_MAX_SZ; k++)
152de494ec5SMichael Ellerman sps->enc[k].page_shift = 0;
153de494ec5SMichael Ellerman
154de494ec5SMichael Ellerman /* Collapse holes */
155de494ec5SMichael Ellerman for (j = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
156de494ec5SMichael Ellerman if (sps->enc[j].page_shift)
157de494ec5SMichael Ellerman continue;
158de494ec5SMichael Ellerman
159de494ec5SMichael Ellerman for (k = j + 1; k < KVM_PPC_PAGE_SIZES_MAX_SZ; k++) {
160de494ec5SMichael Ellerman if (sps->enc[k].page_shift) {
161de494ec5SMichael Ellerman sps->enc[j] = sps->enc[k];
162de494ec5SMichael Ellerman sps->enc[k].page_shift = 0;
163de494ec5SMichael Ellerman break;
164de494ec5SMichael Ellerman }
165de494ec5SMichael Ellerman }
166de494ec5SMichael Ellerman }
167de494ec5SMichael Ellerman }
168de494ec5SMichael Ellerman
169de494ec5SMichael Ellerman /* Mark any trailing entries invalid if we broke out early */
170de494ec5SMichael Ellerman for (j = i; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++)
171de494ec5SMichael Ellerman mmu_info->sps[j].page_shift = 0;
172de494ec5SMichael Ellerman
173de494ec5SMichael Ellerman /* Collapse holes */
174de494ec5SMichael Ellerman for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) {
175de494ec5SMichael Ellerman if (mmu_info->sps[i].page_shift)
176de494ec5SMichael Ellerman continue;
177de494ec5SMichael Ellerman
178de494ec5SMichael Ellerman for (j = i + 1; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
179de494ec5SMichael Ellerman if (mmu_info->sps[j].page_shift) {
180de494ec5SMichael Ellerman mmu_info->sps[i] = mmu_info->sps[j];
181de494ec5SMichael Ellerman mmu_info->sps[j].page_shift = 0;
182de494ec5SMichael Ellerman break;
183de494ec5SMichael Ellerman }
184de494ec5SMichael Ellerman }
185de494ec5SMichael Ellerman }
186de494ec5SMichael Ellerman }
187de494ec5SMichael Ellerman
find_cpu_info(struct kvm * kvm)1884a75c603SMichael Ellerman struct cpu_info *find_cpu_info(struct kvm *kvm)
189d391177aSMatt Evans {
1904a75c603SMichael Ellerman struct cpu_info *info;
191d391177aSMatt Evans unsigned int i;
19242ac24f9SSasha Levin u32 pvr = kvm->arch.pvr;
193ac92dd43SMichael Ellerman
1944a75c603SMichael Ellerman for (info = NULL, i = 0; i < ARRAY_SIZE(host_pvr_info); i++) {
195ac92dd43SMichael Ellerman if ((pvr & host_pvr_info[i].pvr_mask) == host_pvr_info[i].pvr) {
1964a75c603SMichael Ellerman info = host_pvr_info[i].cpu_info;
1974a75c603SMichael Ellerman break;
198d391177aSMatt Evans }
199d391177aSMatt Evans }
200ac92dd43SMichael Ellerman
201d391177aSMatt Evans /* Didn't find anything? Rut-ro. */
2024a75c603SMichael Ellerman if (!info) {
203d391177aSMatt Evans pr_warning("Host CPU unsupported by kvmtool\n");
2044a75c603SMichael Ellerman info = &cpu_dummy_info;
2054a75c603SMichael Ellerman }
206ac92dd43SMichael Ellerman
207de494ec5SMichael Ellerman setup_mmu_info(kvm, info);
208de494ec5SMichael Ellerman
2094a75c603SMichael Ellerman return info;
210d391177aSMatt Evans }
211