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