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