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 }