1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright (c) 2014 Joyent, Inc. All rights reserved. 14 */ 15 16 #include <sys/cpuid_impl.h> 17 #include <sys/param.h> 18 #include <sys/bootconf.h> 19 #include <vm/vm_dep.h> 20 #include <sys/armv7_bsmf.h> 21 22 /* 23 * Handle classification and identification of ARM processors. 24 * 25 * Currently we do a single pass which reads in information and asserts that the 26 * basic information which we receive here matches what we'd expect and are able 27 * to do everything that we need with this ARM CPU. 28 * 29 * TODO We'll eventually do another pass to make sure that we properly determine 30 * the feature set to expose to userland. 31 */ 32 33 static arm_cpuid_t cpuid_data0; 34 35 static void 36 cpuid_parse_stage(uint32_t line, uint32_t mask, uint32_t shift, int *out) 37 { 38 *out = (line & mask) >> shift; 39 } 40 41 static void 42 cpuid_fill_main(arm_cpuid_t *cpd) 43 { 44 cpd->ac_pfr[0] = arm_cpuid_pfr0(); 45 cpd->ac_pfr[1] = arm_cpuid_pfr1(); 46 cpd->ac_dfr = arm_cpuid_dfr0(); 47 cpd->ac_mmfr[0] = arm_cpuid_mmfr0(); 48 cpd->ac_mmfr[1] = arm_cpuid_mmfr1(); 49 cpd->ac_mmfr[2] = arm_cpuid_mmfr2(); 50 cpd->ac_mmfr[3] = arm_cpuid_mmfr3(); 51 cpd->ac_isar[0] = arm_cpuid_isar0(); 52 cpd->ac_isar[1] = arm_cpuid_isar1(); 53 cpd->ac_isar[2] = arm_cpuid_isar2(); 54 cpd->ac_isar[3] = arm_cpuid_isar3(); 55 cpd->ac_isar[4] = arm_cpuid_isar4(); 56 cpd->ac_isar[5] = arm_cpuid_isar5(); 57 } 58 59 static void 60 cpuid_fill_fpu(arm_cpuid_t *cpd) 61 { 62 cpd->ac_mvfr[0] = arm_cpuid_mvfr0(); 63 cpd->ac_mvfr[1] = arm_cpuid_mvfr1(); 64 } 65 66 #define CACHE_LEN_MASK 0x003 67 #define CACHE_M_BIT 0x004 68 #define CACHE_ASSOC_MASK 0x038 69 #define CACHE_ASSOC_SHIFT 3 70 #define CACHE_SIZE_MASK 0x3c0 71 #define CACHE_SIZE_SHIFT 6 72 #define CACHE_COLOR_BIT 0x800 73 #define CACHE_MASK 0xfff 74 #define CACHE_DCACHE_SHIFT 12 75 #define CACHE_SEPARATE 0x1000000 76 77 /* 78 * On ARMv6 the value of the cache size and the cache associativity depends on 79 * the value of the M bit, which modifies the value that's in the actual index. 80 */ 81 static uint32_t armv6_cpuid_cache_sizes[2][9] = { 82 { 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x100000, 83 0x20000 }, 84 { 0x300, 0x600, 0xc00, 0x1800, 0x3000, 0x6000, 0xc000, 0x18000, 85 0x30000 } 86 }; 87 88 static int8_t armv6_cpuid_cache_assoc[2][9] = { 89 { 1, 2, 4, 8, 16, 32, 64, 128 }, 90 { -1, 3, 6, 12, 24, 48, 96, 192 } 91 }; 92 93 static uint8_t armv6_cpuid_cache_linesz[] = { 94 8, 95 16, 96 32, 97 64 98 }; 99 100 static void 101 cpuid_fill_onecache(arm_cpuid_cache_t *accp, uint32_t val) 102 { 103 int mbit, index, assoc; 104 105 mbit = (val & CACHE_M_BIT) != 0 ? 1 : 0; 106 index = (val & CACHE_ASSOC_MASK) >> CACHE_ASSOC_SHIFT; 107 assoc = armv6_cpuid_cache_assoc[mbit][index]; 108 if (assoc == -1) { 109 accp->acc_exists = B_FALSE; 110 return; 111 } 112 ASSERT(assoc > 0); 113 accp->acc_assoc = assoc; 114 accp->acc_rcolor = (val & CACHE_COLOR_BIT) == 0 ? 115 B_FALSE : B_TRUE; 116 index = val & CACHE_LEN_MASK; 117 accp->acc_linesz = armv6_cpuid_cache_linesz[index]; 118 index = (val & CACHE_SIZE_MASK) >> CACHE_SIZE_SHIFT; 119 accp->acc_size = armv6_cpuid_cache_sizes[mbit][index]; 120 accp->acc_exists = B_TRUE; 121 } 122 123 static void 124 cpuid_fill_caches(arm_cpuid_t *cpd) 125 { 126 uint32_t val, icache, dcache; 127 128 val = arm_cpuid_ctr(); 129 icache = val & CACHE_MASK; 130 cpuid_fill_onecache(&cpd->ac_icache, icache); 131 dcache = (val >> CACHE_DCACHE_SHIFT) & CACHE_MASK; 132 cpuid_fill_onecache(&cpd->ac_dcache, dcache); 133 134 if (val & CACHE_SEPARATE) { 135 cpd->ac_unifiedl1 = B_FALSE; 136 } else { 137 cpd->ac_unifiedl1 = B_TRUE; 138 } 139 140 armv7_bsmdep_l2cacheinfo(); 141 armv6_cachesz = cpd->ac_dcache.acc_size; 142 armv6_cache_assoc = cpd->ac_dcache.acc_assoc; 143 } 144 145 146 /* 147 * There isn't a specific way to indicate that we're on ARMv6k. Instead what we 148 * need to do is go through and check for a few features that we know we're 149 * going to need. 150 * 151 * TODO This will have to be revisited with ARMv7 support 152 */ 153 static void 154 cpuid_verify(void) 155 { 156 arm_cpuid_mem_vmsa_t vmsa; 157 arm_cpuid_mem_barrier_t barrier; 158 int sync, syncf; 159 160 arm_cpuid_t *cpd = &cpuid_data0; 161 162 /* v6 vmsa */ 163 cpuid_parse_stage(cpd->ac_mmfr[0], ARM_CPUID_MMFR0_STATE0_MASK, 164 ARM_CPUID_MMFR0_STATE0_SHIFT, (int *)&vmsa); 165 /* TODO We might be able to support v6, but bcm2835+qvpb are this */ 166 if (vmsa != ARM_CPUID_MEM_VMSA_V7) { 167 bop_printf(NULL, "invalid vmsa setting, found 0x%x\n", vmsa); 168 bop_panic("unsupported cpu"); 169 } 170 171 /* check for ISB, DSB, etc. in cp15 */ 172 cpuid_parse_stage(cpd->ac_mmfr[2], ARM_CPUID_MMFR2_STATE5_MASK, 173 ARM_CPUID_MMFR2_STATE5_SHIFT, (int *)&barrier); 174 if (barrier != ARM_CPUID_MEM_BARRIER_CP15 && 175 barrier != ARM_CPUID_MEM_BARRIER_INSTR) { 176 bop_printf(NULL, "missing support for CP15 memory barriers\n"); 177 bop_panic("unsupported CPU"); 178 } 179 180 /* synch prims */ 181 cpuid_parse_stage(cpd->ac_isar[4], ARM_CPUID_ISAR3_STATE3_SHIFT, 182 ARM_CPUID_ISAR4_STATE5_SHIFT, (int *)&sync); 183 cpuid_parse_stage(cpd->ac_isar[4], ARM_CPUID_ISAR4_STATE3_SHIFT, 184 ARM_CPUID_ISAR4_STATE5_SHIFT, (int *)&syncf); 185 if (sync != 0x2 && syncf != 0x0) { 186 bop_printf(NULL, "unsupported synch primitives: sync,frac: " 187 "%x,%x\n", sync, syncf); 188 bop_panic("unsupported CPU"); 189 } 190 191 if (cpd->ac_icache.acc_exists == B_FALSE) { 192 bop_printf(NULL, "icache not defined to exist\n"); 193 bop_panic("unsupported CPU"); 194 } 195 196 if (cpd->ac_dcache.acc_exists == B_FALSE) { 197 bop_printf(NULL, "dcache not defined to exist\n"); 198 bop_panic("unsupported CPU"); 199 } 200 } 201 202 static void 203 cpuid_valid_ident(uint32_t ident) 204 { 205 arm_cpuid_ident_arch_t arch; 206 207 /* 208 * We don't support stock ARMv6 or older. 209 */ 210 arch = (ident & ARM_CPUID_IDENT_ARCH_MASK) >> 211 ARM_CPUID_IDENT_ARCH_SHIFT; 212 if (arch != ARM_CPUID_IDENT_ARCH_CPUID) { 213 bop_printf(NULL, "encountered unsupported CPU arch: 0x%x", 214 arch); 215 bop_panic("unsupported CPU"); 216 } 217 } 218 219 static void 220 cpuid_valid_fpident(uint32_t ident) 221 { 222 arm_cpuid_vfp_arch_t vfp; 223 224 cpuid_parse_stage(ident, ARM_CPUID_VFP_ARCH_MASK, 225 ARM_CPUID_VFP_ARCH_SHIFT, (int *)&vfp); 226 if (vfp != ARM_CPUID_VFP_ARCH_V2) { 227 bop_printf(NULL, "unsupported vfp version: %x\n", vfp); 228 bop_panic("unsupported CPU"); 229 } 230 231 if ((ident & ARM_CPUID_VFP_SW_MASK) != 0) { 232 bop_printf(NULL, "encountered software-only vfp\n"); 233 bop_panic("unsuppored CPU"); 234 } 235 } 236 void 237 cpuid_setup(void) 238 { 239 arm_cpuid_t *cpd = &cpuid_data0; 240 241 cpd->ac_ident = arm_cpuid_idreg(); 242 cpuid_valid_ident(cpd->ac_ident); 243 cpuid_fill_main(cpd); 244 245 cpd->ac_fpident = arm_cpuid_vfpidreg(); 246 cpuid_valid_fpident(cpd->ac_fpident); 247 cpuid_fill_fpu(cpd); 248 cpuid_fill_caches(cpd); 249 250 cpuid_verify(); 251 }