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 }