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