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 }
|
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 }
|