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