Print this page
cpuid for ARMv7

@@ -9,10 +9,11 @@
  * http://www.illumos.org/license/CDDL.
  */
 
 /*
  * Copyright (c) 2014 Joyent, Inc.  All rights reserved.
+ * Copyright (c) 2015 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  */
 
 #include <sys/cpuid_impl.h>
 #include <sys/param.h>
 #include <sys/bootconf.h>

@@ -30,14 +31,14 @@
  * the feature set to expose to userland.
  */
 
 static arm_cpuid_t cpuid_data0;
 
-static void
-cpuid_parse_stage(uint32_t line, uint32_t mask, uint32_t shift, int *out)
+static uint32_t
+extract(uint32_t line, uint32_t mask, uint32_t shift)
 {
-        *out = (line & mask) >> shift;
+        return ((line & mask) >> shift);
 }
 
 static void
 cpuid_fill_main(arm_cpuid_t *cpd)
 {

@@ -61,153 +62,167 @@
 {
         cpd->ac_mvfr[0] = arm_cpuid_mvfr0();
         cpd->ac_mvfr[1] = arm_cpuid_mvfr1();
 }
 
-#define CACHE_LEN_MASK          0x003
-#define CACHE_M_BIT             0x004
-#define CACHE_ASSOC_MASK        0x038
-#define CACHE_ASSOC_SHIFT       3
-#define CACHE_SIZE_MASK         0x3c0
-#define CACHE_SIZE_SHIFT        6
-#define CACHE_COLOR_BIT         0x800
-#define CACHE_MASK              0xfff
-#define CACHE_DCACHE_SHIFT      12
-#define CACHE_SEPARATE          0x1000000
+#define CCSIDR_WT               0x80000000
+#define CCSIDR_WB               0x40000000
+#define CCSIDR_RA               0x20000000
+#define CCSIDR_WA               0x10000000
+#define CCSIDR_NUMSETS_MASK     0x0fffe000
+#define CCSIDR_NUMSETS_SHIFT    13
+#define CCSIDR_ASSOC_MASK       0x00001ff8
+#define CCSIDR_ASSOC_SHIFT      3
+#define CCSIDR_LINESIZE_MASK    0x00000007
+#define CCSIDR_LINESIZE_SHIFT   0
+
+static void
+cpuid_fill_onecache(arm_cpuid_t *cpd, int level, boolean_t icache)
+{
+        arm_cpuid_cache_t *cache = &cpd->ac_caches[icache][level];
+        uint32_t ccsidr;
+
+        ccsidr = arm_cpuid_ccsidr(level, icache);
+        cpd->ac_ccsidr[icache][level] = ccsidr;
+
+        cache->acc_exists = B_TRUE;
+        cache->acc_wt = (ccsidr & CCSIDR_WT) == CCSIDR_WT;
+        cache->acc_wb = (ccsidr & CCSIDR_WB) == CCSIDR_WB;
+        cache->acc_ra = (ccsidr & CCSIDR_RA) == CCSIDR_RA;
+        cache->acc_wa = (ccsidr & CCSIDR_WA) == CCSIDR_WA;
+        cache->acc_sets = extract(ccsidr, CCSIDR_NUMSETS_MASK,
+            CCSIDR_NUMSETS_SHIFT) + 1;
+        cache->acc_assoc = extract(ccsidr, CCSIDR_ASSOC_MASK,
+            CCSIDR_ASSOC_SHIFT) + 1;
+        cache->acc_linesz = sizeof (uint32_t) << (extract(ccsidr,
+            CCSIDR_LINESIZE_MASK, CCSIDR_LINESIZE_SHIFT) + 2);
 
 /*
- * On ARMv6 the value of the cache size and the cache associativity depends on
- * the value of the M bit, which modifies the value that's in the actual index.
+ * XXX?
+#warning "set acc_size?"
  */
-static uint32_t armv6_cpuid_cache_sizes[2][9] = {
-        { 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x100000,
-                0x20000 },
-        { 0x300, 0x600, 0xc00, 0x1800, 0x3000, 0x6000, 0xc000, 0x18000,
-                0x30000 }
-};
-
-static int8_t armv6_cpuid_cache_assoc[2][9] = {
-        { 1, 2, 4, 8, 16, 32, 64, 128 },
-        { -1, 3, 6, 12, 24, 48, 96, 192 }
-};
-
-static uint8_t armv6_cpuid_cache_linesz[] = {
-        8,
-        16,
-        32,
-        64
-};
-
-static void
-cpuid_fill_onecache(arm_cpuid_cache_t *accp, uint32_t val)
-{
-        int mbit, index, assoc;
-
-        mbit = (val & CACHE_M_BIT) != 0 ? 1 : 0;
-        index = (val & CACHE_ASSOC_MASK) >> CACHE_ASSOC_SHIFT;
-        assoc = armv6_cpuid_cache_assoc[mbit][index];
-        if (assoc == -1) {
-                accp->acc_exists = B_FALSE;
-                return;
-        }
-        ASSERT(assoc > 0);
-        accp->acc_assoc = assoc;
-        accp->acc_rcolor = (val & CACHE_COLOR_BIT) == 0 ?
-            B_FALSE : B_TRUE;
-        index = val & CACHE_LEN_MASK;
-        accp->acc_linesz = armv6_cpuid_cache_linesz[index];
-        index = (val & CACHE_SIZE_MASK) >> CACHE_SIZE_SHIFT;
-        accp->acc_size = armv6_cpuid_cache_sizes[mbit][index];
-        accp->acc_exists = B_TRUE;
 }
 
 static void
 cpuid_fill_caches(arm_cpuid_t *cpd)
 {
-        uint32_t val, icache, dcache;
+        uint32_t erg, cwg;
+        uint32_t l1ip;
+        uint32_t ctr;
+        uint32_t clidr;
+        int level;
+
+        clidr = arm_cpuid_clidr();
+        cpd->ac_clidr = clidr;
+
+        /* default all caches to not existing, and not unified */
+        for (level = 0; level < 7; level++) {
+                cpd->ac_caches[B_TRUE][level].acc_exists = B_FALSE;
+                cpd->ac_caches[B_FALSE][level].acc_exists = B_FALSE;
+                cpd->ac_caches[B_TRUE][level].acc_unified = B_FALSE;
+                cpd->ac_caches[B_FALSE][level].acc_unified = B_FALSE;
+        }
+
+        /* retrieve cache info for each level */
+        for (level = 0; level < 7; level++) {
+                arm_cpuid_cache_t *icache = &cpd->ac_caches[B_TRUE][level];
+                arm_cpuid_cache_t *dcache = &cpd->ac_caches[B_FALSE][level];
+                uint32_t ctype = (cpd->ac_clidr >> (3 * level)) & 0x7;
+
+                /* stop looking we find the first non-existent level */
+                if (!ctype)
+                        break;
+
+                switch (ctype) {
+                case 1:
+                        cpuid_fill_onecache(cpd, level, B_TRUE);
+                        break;
+                case 2:
+                        cpuid_fill_onecache(cpd, level, B_FALSE);
+                        break;
+                case 3:
+                        cpuid_fill_onecache(cpd, level, B_TRUE);
+                        cpuid_fill_onecache(cpd, level, B_FALSE);
+                        break;
+                case 4:
+                        cpuid_fill_onecache(cpd, level, B_FALSE);
+                        dcache->acc_unified = B_TRUE;
+                        break;
+                default:
+                        bop_panic("unsupported cache type");
+                }
+        }
+
+        /*
+         * We require L1-I/D & L2-D.  Unified caches are OK as well.
+         */
+        if (!cpd->ac_caches[B_TRUE][0].acc_exists &&
+            (!cpd->ac_caches[B_FALSE][0].acc_exists ||
+            !cpd->ac_caches[B_FALSE][0].acc_unified))
+                bop_panic("no L1 instructian cache detected");
+
+        if (!cpd->ac_caches[B_FALSE][1].acc_exists)
+                bop_panic("no L2 data cache detected");
 
-        val = arm_cpuid_ctr();
-        icache = val & CACHE_MASK;
-        cpuid_fill_onecache(&cpd->ac_icache, icache);
-        dcache = (val >> CACHE_DCACHE_SHIFT) & CACHE_MASK;
-        cpuid_fill_onecache(&cpd->ac_dcache, dcache);
-
-        if (val & CACHE_SEPARATE) {
-                cpd->ac_unifiedl1 = B_FALSE;
-        } else {
-                cpd->ac_unifiedl1 = B_TRUE;
-        }
-
-        armv7_bsmdep_l2cacheinfo();
-        armv6_cachesz = cpd->ac_dcache.acc_size;
-        armv6_cache_assoc = cpd->ac_dcache.acc_assoc;
+        /*
+         * set globals with cache size info
+         */
+        l2cache_sz = cpd->ac_caches[B_FALSE][1].acc_size;
+        l2cache_linesz = cpd->ac_caches[B_FALSE][1].acc_linesz;
+        l2cache_assoc = cpd->ac_caches[B_FALSE][1].acc_assoc;
 }
 
 
 /*
- * There isn't a specific way to indicate that we're on ARMv6k. Instead what we
- * need to do is go through and check for a few features that we know we're
- * going to need.
- *
- * TODO This will have to be revisited with ARMv7 support
+ * We need to do is go through and check for a few features that we know
+ * we're going to need.
  */
 static void
 cpuid_verify(void)
 {
         arm_cpuid_mem_vmsa_t vmsa;
         arm_cpuid_mem_barrier_t barrier;
         int sync, syncf;
 
         arm_cpuid_t *cpd = &cpuid_data0;
 
-        /* v6 vmsa */
-        cpuid_parse_stage(cpd->ac_mmfr[0], ARM_CPUID_MMFR0_STATE0_MASK,
-            ARM_CPUID_MMFR0_STATE0_SHIFT, (int *)&vmsa);
-        /* TODO We might be able to support v6, but bcm2835+qvpb are this */
+        /* v7 vmsa */
+        vmsa = extract(cpd->ac_mmfr[0], ARM_CPUID_MMFR0_STATE0_MASK,
+            ARM_CPUID_MMFR0_STATE0_SHIFT);
         if (vmsa != ARM_CPUID_MEM_VMSA_V7) {
                 bop_printf(NULL, "invalid vmsa setting, found 0x%x\n", vmsa);
                 bop_panic("unsupported cpu");
         }
 
         /* check for ISB, DSB, etc. in cp15 */
-        cpuid_parse_stage(cpd->ac_mmfr[2], ARM_CPUID_MMFR2_STATE5_MASK,
-            ARM_CPUID_MMFR2_STATE5_SHIFT, (int *)&barrier);
-        if (barrier != ARM_CPUID_MEM_BARRIER_CP15 &&
-            barrier != ARM_CPUID_MEM_BARRIER_INSTR) {
-                bop_printf(NULL, "missing support for CP15 memory barriers\n");
+        barrier = extract(cpd->ac_mmfr[2], ARM_CPUID_MMFR2_STATE5_MASK,
+            ARM_CPUID_MMFR2_STATE5_SHIFT);
+        if (barrier != ARM_CPUID_MEM_BARRIER_INSTR) {
+                bop_printf(NULL, "missing support for memory barrier "
+                    "instructions\n");
                 bop_panic("unsupported CPU");
         }
 
         /* synch prims */
-        cpuid_parse_stage(cpd->ac_isar[4], ARM_CPUID_ISAR3_STATE3_SHIFT,
-            ARM_CPUID_ISAR4_STATE5_SHIFT, (int *)&sync);
-        cpuid_parse_stage(cpd->ac_isar[4], ARM_CPUID_ISAR4_STATE3_SHIFT,
-            ARM_CPUID_ISAR4_STATE5_SHIFT, (int *)&syncf);
+        sync = extract(cpd->ac_isar[3], ARM_CPUID_ISAR3_STATE3_SHIFT,
+            ARM_CPUID_ISAR3_STATE3_SHIFT);
+        syncf = extract(cpd->ac_isar[4], ARM_CPUID_ISAR4_STATE5_SHIFT,
+            ARM_CPUID_ISAR4_STATE5_SHIFT);
         if (sync != 0x2 && syncf != 0x0) {
                 bop_printf(NULL, "unsupported synch primitives: sync,frac: "
                     "%x,%x\n", sync, syncf);
                 bop_panic("unsupported CPU");
         }
-
-        if (cpd->ac_icache.acc_exists == B_FALSE) {
-                bop_printf(NULL, "icache not defined to exist\n");
-                bop_panic("unsupported CPU");
-        }
-
-        if (cpd->ac_dcache.acc_exists == B_FALSE) {
-                bop_printf(NULL, "dcache not defined to exist\n");
-                bop_panic("unsupported CPU");
-        }
 }
 
 static void
 cpuid_valid_ident(uint32_t ident)
 {
         arm_cpuid_ident_arch_t arch;
 
         /*
-         * We don't support stock ARMv6 or older.
+         * We don't support anything older than ARMv7.
          */
         arch = (ident & ARM_CPUID_IDENT_ARCH_MASK) >>
             ARM_CPUID_IDENT_ARCH_SHIFT;
         if (arch != ARM_CPUID_IDENT_ARCH_CPUID) {
                 bop_printf(NULL, "encountered unsupported CPU arch: 0x%x",

@@ -219,12 +234,12 @@
 static void
 cpuid_valid_fpident(uint32_t ident)
 {
         arm_cpuid_vfp_arch_t vfp;
 
-        cpuid_parse_stage(ident, ARM_CPUID_VFP_ARCH_MASK,
-            ARM_CPUID_VFP_ARCH_SHIFT, (int *)&vfp);
+        vfp = extract(ident, ARM_CPUID_VFP_ARCH_MASK, ARM_CPUID_VFP_ARCH_SHIFT);
+        // XXX: _V3_V2BASE? _V3_NOBASE? _V3_V3BASE?
         if (vfp != ARM_CPUID_VFP_ARCH_V2) {
                 bop_printf(NULL, "unsupported vfp version: %x\n", vfp);
                 bop_panic("unsupported CPU");
         }
 

@@ -236,16 +251,17 @@
 void
 cpuid_setup(void)
 {
         arm_cpuid_t *cpd = &cpuid_data0;
 
-        cpd->ac_ident = arm_cpuid_idreg();
+        cpd->ac_ident = arm_cpuid_midr();
         cpuid_valid_ident(cpd->ac_ident);
         cpuid_fill_main(cpd);
 
         cpd->ac_fpident = arm_cpuid_vfpidreg();
         cpuid_valid_fpident(cpd->ac_fpident);
         cpuid_fill_fpu(cpd);
+
         cpuid_fill_caches(cpd);
 
         cpuid_verify();
 }