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 * Copyright (c) 2015 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> 15 */ 16 17 #include <sys/cpuid_impl.h> 18 #include <sys/param.h> 19 #include <sys/bootconf.h> 20 #include <vm/vm_dep.h> 21 #include <sys/armv7_bsmf.h> 22 23 /* 24 * Handle classification and identification of ARM processors. 25 * 26 * Currently we do a single pass which reads in information and asserts that the 27 * basic information which we receive here matches what we'd expect and are able 28 * to do everything that we need with this ARM CPU. 29 * 30 * TODO We'll eventually do another pass to make sure that we properly determine 31 * the feature set to expose to userland. 32 */ 33 34 static arm_cpuid_t cpuid_data0; 35 36 static uint32_t 37 extract(uint32_t line, uint32_t mask, uint32_t shift) 38 { 39 return ((line & mask) >> shift); 40 } 41 42 static void 43 cpuid_fill_main(arm_cpuid_t *cpd) 44 { 45 cpd->ac_pfr[0] = arm_cpuid_pfr0(); 46 cpd->ac_pfr[1] = arm_cpuid_pfr1(); 47 cpd->ac_dfr = arm_cpuid_dfr0(); 48 cpd->ac_mmfr[0] = arm_cpuid_mmfr0(); 49 cpd->ac_mmfr[1] = arm_cpuid_mmfr1(); 50 cpd->ac_mmfr[2] = arm_cpuid_mmfr2(); 51 cpd->ac_mmfr[3] = arm_cpuid_mmfr3(); 52 cpd->ac_isar[0] = arm_cpuid_isar0(); 53 cpd->ac_isar[1] = arm_cpuid_isar1(); 54 cpd->ac_isar[2] = arm_cpuid_isar2(); 55 cpd->ac_isar[3] = arm_cpuid_isar3(); 56 cpd->ac_isar[4] = arm_cpuid_isar4(); 57 cpd->ac_isar[5] = arm_cpuid_isar5(); 58 } 59 60 static void 61 cpuid_fill_fpu(arm_cpuid_t *cpd) 62 { 63 cpd->ac_mvfr[0] = arm_cpuid_mvfr0(); 64 cpd->ac_mvfr[1] = arm_cpuid_mvfr1(); 65 } 66 67 #define CCSIDR_WT 0x80000000 68 #define CCSIDR_WB 0x40000000 69 #define CCSIDR_RA 0x20000000 70 #define CCSIDR_WA 0x10000000 71 #define CCSIDR_NUMSETS_MASK 0x0fffe000 72 #define CCSIDR_NUMSETS_SHIFT 13 73 #define CCSIDR_ASSOC_MASK 0x00001ff8 74 #define CCSIDR_ASSOC_SHIFT 3 75 #define CCSIDR_LINESIZE_MASK 0x00000007 76 #define CCSIDR_LINESIZE_SHIFT 0 77 78 static void 79 cpuid_fill_onecache(arm_cpuid_t *cpd, int level, boolean_t icache) 80 { 81 arm_cpuid_cache_t *cache = &cpd->ac_caches[icache][level]; 82 uint32_t ccsidr; 83 84 ccsidr = arm_cpuid_ccsidr(level, icache); 85 cpd->ac_ccsidr[icache][level] = ccsidr; 86 87 cache->acc_exists = B_TRUE; 88 cache->acc_wt = (ccsidr & CCSIDR_WT) == CCSIDR_WT; 89 cache->acc_wb = (ccsidr & CCSIDR_WB) == CCSIDR_WB; 90 cache->acc_ra = (ccsidr & CCSIDR_RA) == CCSIDR_RA; 91 cache->acc_wa = (ccsidr & CCSIDR_WA) == CCSIDR_WA; 92 cache->acc_sets = extract(ccsidr, CCSIDR_NUMSETS_MASK, 93 CCSIDR_NUMSETS_SHIFT) + 1; 94 cache->acc_assoc = extract(ccsidr, CCSIDR_ASSOC_MASK, 95 CCSIDR_ASSOC_SHIFT) + 1; 96 cache->acc_linesz = sizeof (uint32_t) << (extract(ccsidr, 97 CCSIDR_LINESIZE_MASK, CCSIDR_LINESIZE_SHIFT) + 2); 98 99 /* 100 * XXX? 101 #warning "set acc_size?" 102 */ 103 } 104 105 static void 106 cpuid_fill_caches(arm_cpuid_t *cpd) 107 { 108 uint32_t erg, cwg; 109 uint32_t l1ip; 110 uint32_t ctr; 111 uint32_t clidr; 112 int level; 113 114 clidr = arm_cpuid_clidr(); 115 cpd->ac_clidr = clidr; 116 117 /* default all caches to not existing, and not unified */ 118 for (level = 0; level < 7; level++) { 119 cpd->ac_caches[B_TRUE][level].acc_exists = B_FALSE; 120 cpd->ac_caches[B_FALSE][level].acc_exists = B_FALSE; 121 cpd->ac_caches[B_TRUE][level].acc_unified = B_FALSE; 122 cpd->ac_caches[B_FALSE][level].acc_unified = B_FALSE; 123 } 124 125 /* retrieve cache info for each level */ 126 for (level = 0; level < 7; level++) { 127 arm_cpuid_cache_t *icache = &cpd->ac_caches[B_TRUE][level]; 128 arm_cpuid_cache_t *dcache = &cpd->ac_caches[B_FALSE][level]; 129 uint32_t ctype = (cpd->ac_clidr >> (3 * level)) & 0x7; 130 131 /* stop looking we find the first non-existent level */ 132 if (!ctype) 133 break; 134 135 switch (ctype) { 136 case 1: 137 cpuid_fill_onecache(cpd, level, B_TRUE); 138 break; 139 case 2: 140 cpuid_fill_onecache(cpd, level, B_FALSE); 141 break; 142 case 3: 143 cpuid_fill_onecache(cpd, level, B_TRUE); 144 cpuid_fill_onecache(cpd, level, B_FALSE); 145 break; 146 case 4: 147 cpuid_fill_onecache(cpd, level, B_FALSE); 148 dcache->acc_unified = B_TRUE; 149 break; 150 default: 151 bop_panic("unsupported cache type"); 152 } 153 } 154 155 /* 156 * We require L1-I/D & L2-D. Unified caches are OK as well. 157 */ 158 if (!cpd->ac_caches[B_TRUE][0].acc_exists && 159 (!cpd->ac_caches[B_FALSE][0].acc_exists || 160 !cpd->ac_caches[B_FALSE][0].acc_unified)) 161 bop_panic("no L1 instructian cache detected"); 162 163 if (!cpd->ac_caches[B_FALSE][1].acc_exists) 164 bop_panic("no L2 data cache detected"); 165 166 /* 167 * set globals with cache size info 168 */ 169 l2cache_sz = cpd->ac_caches[B_FALSE][1].acc_size; 170 l2cache_linesz = cpd->ac_caches[B_FALSE][1].acc_linesz; 171 l2cache_assoc = cpd->ac_caches[B_FALSE][1].acc_assoc; 172 } 173 174 175 /* 176 * We need to do is go through and check for a few features that we know 177 * we're going to need. 178 */ 179 static void 180 cpuid_verify(void) 181 { 182 arm_cpuid_mem_vmsa_t vmsa; 183 arm_cpuid_mem_barrier_t barrier; 184 int sync, syncf; 185 186 arm_cpuid_t *cpd = &cpuid_data0; 187 188 /* v7 vmsa */ 189 vmsa = extract(cpd->ac_mmfr[0], ARM_CPUID_MMFR0_STATE0_MASK, 190 ARM_CPUID_MMFR0_STATE0_SHIFT); 191 if (vmsa != ARM_CPUID_MEM_VMSA_V7) { 192 bop_printf(NULL, "invalid vmsa setting, found 0x%x\n", vmsa); 193 bop_panic("unsupported cpu"); 194 } 195 196 /* check for ISB, DSB, etc. in cp15 */ 197 barrier = extract(cpd->ac_mmfr[2], ARM_CPUID_MMFR2_STATE5_MASK, 198 ARM_CPUID_MMFR2_STATE5_SHIFT); 199 if (barrier != ARM_CPUID_MEM_BARRIER_INSTR) { 200 bop_printf(NULL, "missing support for memory barrier " 201 "instructions\n"); 202 bop_panic("unsupported CPU"); 203 } 204 205 /* synch prims */ 206 sync = extract(cpd->ac_isar[3], ARM_CPUID_ISAR3_STATE3_SHIFT, 207 ARM_CPUID_ISAR3_STATE3_SHIFT); 208 syncf = extract(cpd->ac_isar[4], ARM_CPUID_ISAR4_STATE5_SHIFT, 209 ARM_CPUID_ISAR4_STATE5_SHIFT); 210 if (sync != 0x2 && syncf != 0x0) { 211 bop_printf(NULL, "unsupported synch primitives: sync,frac: " 212 "%x,%x\n", sync, syncf); 213 bop_panic("unsupported CPU"); 214 } 215 } 216 217 static void 218 cpuid_valid_ident(uint32_t ident) 219 { 220 arm_cpuid_ident_arch_t arch; 221 222 /* 223 * We don't support anything older than ARMv7. 224 */ 225 arch = (ident & ARM_CPUID_IDENT_ARCH_MASK) >> 226 ARM_CPUID_IDENT_ARCH_SHIFT; 227 if (arch != ARM_CPUID_IDENT_ARCH_CPUID) { 228 bop_printf(NULL, "encountered unsupported CPU arch: 0x%x", 229 arch); 230 bop_panic("unsupported CPU"); 231 } 232 } 233 234 static void 235 cpuid_valid_fpident(uint32_t ident) 236 { 237 arm_cpuid_vfp_arch_t vfp; 238 239 vfp = extract(ident, ARM_CPUID_VFP_ARCH_MASK, ARM_CPUID_VFP_ARCH_SHIFT); 240 // XXX: _V3_V2BASE? _V3_NOBASE? _V3_V3BASE? 241 if (vfp != ARM_CPUID_VFP_ARCH_V2) { 242 bop_printf(NULL, "unsupported vfp version: %x\n", vfp); 243 bop_panic("unsupported CPU"); 244 } 245 246 if ((ident & ARM_CPUID_VFP_SW_MASK) != 0) { 247 bop_printf(NULL, "encountered software-only vfp\n"); 248 bop_panic("unsuppored CPU"); 249 } 250 } 251 void 252 cpuid_setup(void) 253 { 254 arm_cpuid_t *cpd = &cpuid_data0; 255 256 cpd->ac_ident = arm_cpuid_midr(); 257 cpuid_valid_ident(cpd->ac_ident); 258 cpuid_fill_main(cpd); 259 260 cpd->ac_fpident = arm_cpuid_vfpidreg(); 261 cpuid_valid_fpident(cpd->ac_fpident); 262 cpuid_fill_fpu(cpd); 263 264 cpuid_fill_caches(cpd); 265 266 cpuid_verify(); 267 }