1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * SBP2 config ROM routines
29 */
30
31 #include <sys/types.h>
32 #include <sys/ddi.h>
33 #include <sys/sunddi.h>
34
35 #include <sys/1394/ieee1212.h>
36 #include <sys/sbp2/impl.h>
37
38 static int sbp2_cfgrom_rq(sbp2_tgt_t *, void *, uint64_t, uint32_t *);
39 static int sbp2_cfgrom_parse_dir(sbp2_tgt_t *, void *,
40 sbp2_cfgrom_parse_arg_t *);
41 static int sbp2_cfgrom_read_leaf(sbp2_tgt_t *, void *,
42 sbp2_cfgrom_ent_t *);
43 static int sbp2_cfgrom_read_bib(sbp2_tgt_t *, void *, sbp2_cfgrom_bib_t *);
44 static void sbp2_cfgrom_free_bib(sbp2_tgt_t *, sbp2_cfgrom_bib_t *);
45 static void sbp2_cfgrom_dir_grow(sbp2_cfgrom_dir_t *, int);
46 static sbp2_cfgrom_ent_t *sbp2_cfgrom_dir_new_ent(sbp2_cfgrom_dir_t *);
47 static int sbp2_cfgrom_walk_impl(sbp2_cfgrom_ent_t *,
48 int (*)(void *, sbp2_cfgrom_ent_t *, int), void *, int);
49 static int sbp2_cfgrom_ent_by_key_walker(void *, sbp2_cfgrom_ent_t *,
50 int);
51 static void sbp2_cfgrom_walk_free(sbp2_cfgrom_ent_t *);
52
53 static hrtime_t sbp2_cfgrom_read_delay = 20 * 1000000; /* in ns */
54
55 /* imitate throwing an exception when read fails */
56 #define SBP2_CFGROM_RQ(tp, cmd, addr, q) \
57 if ((ret = sbp2_cfgrom_rq(tp, cmd, addr, q)) != 0) { \
58 goto rq_error; \
59 }
60
61 static int
sbp2_cfgrom_rq(sbp2_tgt_t * tp,void * cmd,uint64_t addr,uint32_t * q)62 sbp2_cfgrom_rq(sbp2_tgt_t *tp, void *cmd, uint64_t addr, uint32_t *q)
63 {
64 hrtime_t tm; /* time since last read */
65 int berr;
66 int ret;
67
68 tm = gethrtime() - tp->t_last_cfgrd;
69 if (tm < sbp2_cfgrom_read_delay) {
70 delay(drv_usectohz((sbp2_cfgrom_read_delay - tm) / 1000));
71 }
72 ret = SBP2_RQ(tp, cmd, addr, q, &berr);
73 *q = SBP2_SWAP32(*q);
74 tp->t_last_cfgrd = gethrtime();
75 return (ret);
76 }
77
78 int
sbp2_cfgrom_parse(sbp2_tgt_t * tp,sbp2_cfgrom_t * crp)79 sbp2_cfgrom_parse(sbp2_tgt_t *tp, sbp2_cfgrom_t *crp)
80 {
81 sbp2_cfgrom_ent_t *root_dir = &crp->cr_root;
82 sbp2_cfgrom_bib_t *bib = &crp->cr_bib;
83 void *cmd;
84 int ret;
85 sbp2_cfgrom_parse_arg_t pa;
86
87 if ((ret = SBP2_ALLOC_CMD(tp, &cmd, 0)) != SBP2_SUCCESS) {
88 return (ret);
89 }
90
91 if ((ret = sbp2_cfgrom_read_bib(tp, cmd, bib)) != SBP2_SUCCESS) {
92 SBP2_FREE_CMD(tp, cmd);
93 return (ret);
94 }
95
96 /* parse root directory and everything underneath */
97 bzero(root_dir, sizeof (sbp2_cfgrom_ent_t));
98 root_dir->ce_kt = IEEE1212_DIRECTORY_TYPE;
99 root_dir->ce_offset = SBP2_CFGROM_ADDR(tp) + 4 + bib->cb_len * 4;
100 pa.pa_dir = root_dir;
101 pa.pa_pdir = NULL;
102 pa.pa_ref = NULL;
103 pa.pa_depth = 0;
104
105 if ((ret = sbp2_cfgrom_parse_dir(tp, cmd, &pa)) != SBP2_SUCCESS) {
106 sbp2_cfgrom_free(tp, crp);
107 }
108
109 SBP2_FREE_CMD(tp, cmd);
110 return (ret);
111 }
112
113
114 /*
115 * Caller must initialize pa and pa->pa_dir.
116 */
117 static int
sbp2_cfgrom_parse_dir(sbp2_tgt_t * tp,void * cmd,sbp2_cfgrom_parse_arg_t * pa)118 sbp2_cfgrom_parse_dir(sbp2_tgt_t *tp, void *cmd, sbp2_cfgrom_parse_arg_t *pa)
119 {
120 sbp2_cfgrom_ent_t *dir = pa->pa_dir; /* directory being parsed */
121 sbp2_cfgrom_ent_t *cep; /* current entry structure */
122 sbp2_cfgrom_ent_t *pcep = NULL; /* previous entry structure */
123 sbp2_cfgrom_parse_arg_t this_pa; /* parse args */
124 uint64_t addr; /* current address */
125 uint32_t entry; /* current entry */
126 uint8_t t, k; /* key type and value */
127 uint32_t v; /* entry value */
128 int i;
129 int ret = 0;
130
131 this_pa.pa_pdir = dir;
132 this_pa.pa_ref = pa->pa_ref;
133 this_pa.pa_depth = pa->pa_depth + 1;
134
135 /* read directory entry and initialize the structure */
136 SBP2_CFGROM_RQ(tp, cmd, dir->ce_offset, &entry);
137 dir->ce_len = IEEE1212_DIR_LEN(entry);
138 sbp2_cfgrom_dir_grow(&dir->ce_data.dir, dir->ce_len);
139
140 /* walk directory entries */
141 addr = dir->ce_offset + 4;
142 for (i = 0; i < dir->ce_len; i++, addr += 4) {
143 SBP2_CFGROM_RQ(tp, cmd, addr, &entry);
144 CFGROM_TYPE_KEY_VALUE(entry, t, k, v);
145
146 cep = sbp2_cfgrom_dir_new_ent(&dir->ce_data.dir);
147 cep->ce_kt = t;
148 cep->ce_kv = k;
149 switch (t) {
150 case IEEE1212_IMMEDIATE_TYPE:
151 cep->ce_len = 1;
152 cep->ce_offset = addr;
153 cep->ce_data.imm = v;
154 break;
155 case IEEE1212_CSR_OFFSET_TYPE:
156 cep->ce_len = 1;
157 cep->ce_offset = addr;
158 cep->ce_data.offset = v;
159 break;
160 case IEEE1212_LEAF_TYPE:
161 cep->ce_offset = addr + 4 * v;
162 if (dir->ce_kv != IEEE1212_TEXTUAL_DESCRIPTOR) {
163 /* text leaf describes preceding entry */
164 cep->ce_ref = pcep;
165 } else {
166 /* text directory describes preceding entry */
167 cep->ce_ref = this_pa.pa_ref;
168 }
169 ret = sbp2_cfgrom_read_leaf(tp, cmd, cep);
170 break;
171 case IEEE1212_DIRECTORY_TYPE:
172 cep->ce_offset = addr + 4 * v;
173 this_pa.pa_dir = cep;
174 this_pa.pa_ref = pcep;
175 if (this_pa.pa_depth < SBP2_CFGROM_MAX_DEPTH) {
176 ret = sbp2_cfgrom_parse_dir(tp, cmd, &this_pa);
177 }
178 break;
179 default:
180 ASSERT(0);
181 }
182 pcep = cep;
183 }
184
185 rq_error:
186 return (ret);
187 }
188
189
190 static int
sbp2_cfgrom_read_leaf(sbp2_tgt_t * tp,void * cmd,sbp2_cfgrom_ent_t * cep)191 sbp2_cfgrom_read_leaf(sbp2_tgt_t *tp, void *cmd, sbp2_cfgrom_ent_t *cep)
192 {
193 uint32_t val;
194 int ret;
195 int i;
196 uint64_t addr = cep->ce_offset;
197
198 /* header */
199 SBP2_CFGROM_RQ(tp, cmd, addr, &val);
200 addr += 4;
201
202 /* verify data length */
203 cep->ce_len = (val >> 16);
204 if (cep->ce_len < 1) {
205 return (SBP2_EDATA);
206 }
207 cep->ce_data.leaf = kmem_zalloc(cep->ce_len * 4, KM_SLEEP);
208
209 /* data */
210 for (i = 0; i < cep->ce_len; i++, addr += 4) {
211 SBP2_CFGROM_RQ(tp, cmd, addr, &cep->ce_data.leaf[i]);
212 }
213
214 return (ret);
215
216 rq_error:
217 if (cep->ce_data.leaf) {
218 kmem_free(cep->ce_data.leaf, cep->ce_len * 4);
219 }
220 return (ret);
221 }
222
223
224 static int
sbp2_cfgrom_read_bib(sbp2_tgt_t * tp,void * cmd,sbp2_cfgrom_bib_t * cbp)225 sbp2_cfgrom_read_bib(sbp2_tgt_t *tp, void *cmd, sbp2_cfgrom_bib_t *cbp)
226 {
227 uint32_t val;
228 int ret;
229 int i;
230 uint64_t addr = SBP2_CFGROM_ADDR(tp);
231
232 /* header */
233 SBP2_CFGROM_RQ(tp, cmd, addr, &val);
234 addr += 4;
235
236 /* verify data length */
237 cbp->cb_len = (val >> 24);
238 if (cbp->cb_len < 1) {
239 return (SBP2_EDATA);
240 }
241 cbp->cb_buf = kmem_zalloc(cbp->cb_len * 4, KM_SLEEP);
242
243 /* data */
244 for (i = 0; i < cbp->cb_len; i++, addr += 4) {
245 SBP2_CFGROM_RQ(tp, cmd, addr, &cbp->cb_buf[i]);
246 }
247
248 rq_error:
249 sbp2_cfgrom_free_bib(tp, cbp);
250 return (ret);
251 }
252
253
254 /*ARGSUSED*/
255 static void
sbp2_cfgrom_free_bib(sbp2_tgt_t * tp,sbp2_cfgrom_bib_t * cbp)256 sbp2_cfgrom_free_bib(sbp2_tgt_t *tp, sbp2_cfgrom_bib_t *cbp)
257 {
258 if ((cbp->cb_buf != NULL) && (cbp->cb_len > 0)) {
259 kmem_free(cbp->cb_buf, cbp->cb_len * 4);
260 cbp->cb_buf = NULL;
261 }
262 }
263
264 static void
sbp2_cfgrom_dir_grow(sbp2_cfgrom_dir_t * dir,int incr)265 sbp2_cfgrom_dir_grow(sbp2_cfgrom_dir_t *dir, int incr)
266 {
267 int new_size, old_size;
268 void *new_ent;
269
270 ASSERT(incr > 0);
271
272 new_size = (dir->cd_size + incr) * sizeof (sbp2_cfgrom_ent_t);
273 new_ent = kmem_zalloc(new_size, KM_SLEEP);
274 if (dir->cd_size > 0) {
275 old_size = dir->cd_size * sizeof (sbp2_cfgrom_ent_t);
276 bcopy(dir->cd_ent, new_ent, old_size);
277 kmem_free(dir->cd_ent, old_size);
278 }
279 dir->cd_ent = new_ent;
280 dir->cd_size += incr;
281 }
282
283 static sbp2_cfgrom_ent_t *
sbp2_cfgrom_dir_new_ent(sbp2_cfgrom_dir_t * dir)284 sbp2_cfgrom_dir_new_ent(sbp2_cfgrom_dir_t *dir)
285 {
286 /* grow if out of entries */
287 if (dir->cd_cnt >= dir->cd_size) {
288 ASSERT(dir->cd_cnt == dir->cd_size);
289 sbp2_cfgrom_dir_grow(dir, SBP2_CFGROM_GROW_INCR);
290 }
291
292 return (&dir->cd_ent[dir->cd_cnt++]);
293 }
294
295 /*
296 * walk Config ROM entries calling the specified function for each
297 */
298 void
sbp2_cfgrom_walk(sbp2_cfgrom_ent_t * dir,int (* func)(void *,sbp2_cfgrom_ent_t *,int),void * arg)299 sbp2_cfgrom_walk(sbp2_cfgrom_ent_t *dir,
300 int (*func)(void *, sbp2_cfgrom_ent_t *, int), void *arg)
301 {
302 ASSERT(dir->ce_kt == IEEE1212_DIRECTORY_TYPE);
303 (void) sbp2_cfgrom_walk_impl(dir, func, arg, 0);
304 }
305
306 static int
sbp2_cfgrom_walk_impl(sbp2_cfgrom_ent_t * dir,int (* func)(void *,sbp2_cfgrom_ent_t *,int),void * arg,int level)307 sbp2_cfgrom_walk_impl(sbp2_cfgrom_ent_t *dir,
308 int (*func)(void *, sbp2_cfgrom_ent_t *, int), void *arg, int level)
309 {
310 int i;
311 sbp2_cfgrom_ent_t *ent;
312
313 for (i = 0; i < dir->ce_data.dir.cd_cnt; i++) {
314 ent = &dir->ce_data.dir.cd_ent[i];
315 if (func(arg, ent, level) == SBP2_WALK_STOP) {
316 return (SBP2_WALK_STOP);
317 }
318 if (ent->ce_kt == IEEE1212_DIRECTORY_TYPE) {
319 if (sbp2_cfgrom_walk_impl(ent, func, arg, level + 1) ==
320 SBP2_WALK_STOP) {
321 return (SBP2_WALK_STOP);
322 }
323 }
324 }
325 return (SBP2_WALK_CONTINUE);
326 }
327
328
329 sbp2_cfgrom_ent_t *
sbp2_cfgrom_ent_by_key(sbp2_cfgrom_ent_t * dir,int8_t kt,int8_t kv,int num)330 sbp2_cfgrom_ent_by_key(sbp2_cfgrom_ent_t *dir, int8_t kt, int8_t kv, int num)
331 {
332 sbp2_cfgrom_ent_by_key_t ebk;
333
334 ebk.kt = kt;
335 ebk.kv = kv;
336 ebk.num = num;
337 ebk.ent = NULL;
338 ebk.cnt = 0;
339 sbp2_cfgrom_walk(dir, sbp2_cfgrom_ent_by_key_walker, &ebk);
340
341 return (ebk.ent);
342 }
343
344 /*ARGSUSED*/
345 static int
sbp2_cfgrom_ent_by_key_walker(void * arg,sbp2_cfgrom_ent_t * ent,int level)346 sbp2_cfgrom_ent_by_key_walker(void *arg, sbp2_cfgrom_ent_t *ent, int level)
347 {
348 sbp2_cfgrom_ent_by_key_t *ebk = arg;
349
350 if ((ent->ce_kt == ebk->kt) && (ent->ce_kv == ebk->kv)) {
351 if (ebk->cnt == ebk->num) {
352 ebk->ent = ent;
353 return (SBP2_WALK_STOP);
354 }
355 ebk->cnt++;
356 }
357 return (SBP2_WALK_CONTINUE);
358 }
359
360
361 void
sbp2_cfgrom_free(sbp2_tgt_t * tp,sbp2_cfgrom_t * crp)362 sbp2_cfgrom_free(sbp2_tgt_t *tp, sbp2_cfgrom_t *crp)
363 {
364 sbp2_cfgrom_free_bib(tp, &crp->cr_bib);
365 sbp2_cfgrom_walk_free(&crp->cr_root);
366 }
367
368 static void
sbp2_cfgrom_walk_free(sbp2_cfgrom_ent_t * dir)369 sbp2_cfgrom_walk_free(sbp2_cfgrom_ent_t *dir)
370 {
371 int i;
372 sbp2_cfgrom_dir_t *cdp = &dir->ce_data.dir;
373 sbp2_cfgrom_ent_t *ent = cdp->cd_ent;
374
375 for (i = 0; i < cdp->cd_cnt; i++) {
376 if (ent[i].ce_kt == IEEE1212_DIRECTORY_TYPE) {
377 sbp2_cfgrom_walk_free(&ent[i]);
378 } else if ((ent[i].ce_kt == IEEE1212_LEAF_TYPE) &&
379 (ent[i].ce_data.leaf != NULL)) {
380 kmem_free(ent[i].ce_data.leaf, ent[i].ce_len * 4);
381 }
382 }
383 if (ent) {
384 kmem_free(ent, cdp->cd_size * sizeof (sbp2_cfgrom_ent_t));
385 }
386 }
387