1 /*        $NetBSD: fwcrom.c,v 1.17 2014/11/21 23:37:25 joerg Exp $    */
2 /*-
3  * Copyright (c) 2002-2003
4  *        Hidetoshi Shimokawa. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *
17  *        This product includes software developed by Hidetoshi Shimokawa.
18  *
19  * 4. Neither the name of the author nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: fwcrom.c,v 1.17 2014/11/21 23:37:25 joerg Exp $");
38 
39 #include <sys/param.h>
40 #ifdef _KERNEL
41 #include <sys/device.h>
42 #include <sys/errno.h>
43 #include <sys/systm.h>
44 #else
45 #include <stdio.h>
46 #include <string.h>
47 #endif
48 #include <dev/ieee1394/firewire.h>
49 #include <dev/ieee1394/iec13213.h>
50 
51 #define MAX_ROM (1024 - sizeof(uint32_t) * 5)
52 #define CROM_END(cc) ((char *)(cc)->stack[0].dir + MAX_ROM - 1)
53 
54 void
crom_init_context(struct crom_context * cc,uint32_t * p)55 crom_init_context(struct crom_context *cc, uint32_t *p)
56 {
57           struct csrhdr *hdr;
58 
59           hdr = (struct csrhdr *)p;
60           if (hdr->info_len <= 1) {
61                     /* minimum or invalid ROM */
62                     cc->depth = -1;
63                     return;
64           }
65           p += 1 + hdr->info_len;
66 
67           /* check size of root directory */
68           if (((struct csrdirectory *)p)->crc_len == 0) {
69                     cc->depth = -1;
70                     return;
71           }
72           cc->depth = 0;
73           cc->stack[0].dir = (struct csrdirectory *)p;
74           cc->stack[0].index = 0;
75 }
76 
77 struct csrreg *
crom_get(struct crom_context * cc)78 crom_get(struct crom_context *cc)
79 {
80           struct crom_ptr *ptr;
81 
82           ptr = &cc->stack[cc->depth];
83           return &ptr->dir->entry[ptr->index];
84 }
85 
86 void
crom_next(struct crom_context * cc)87 crom_next(struct crom_context *cc)
88 {
89           struct crom_ptr *ptr;
90           struct csrreg *reg;
91 
92           if (cc->depth < 0)
93                     return;
94           reg = crom_get(cc);
95           if ((reg->key & CSRTYPE_MASK) == CSRTYPE_D) {
96                     if (cc->depth >= CROM_MAX_DEPTH) {
97                               printf("crom_next: too deep\n");
98                               goto again;
99                     }
100                     cc->depth++;
101 
102                     ptr = &cc->stack[cc->depth];
103                     ptr->dir = (struct csrdirectory *)(reg + reg->val);
104                     ptr->index = 0;
105                     goto check;
106           }
107 again:
108           ptr = &cc->stack[cc->depth];
109           ptr->index++;
110 check:
111           if (ptr->index < ptr->dir->crc_len &&
112               (char *)crom_get(cc) <= CROM_END(cc))
113                     return;
114 
115           if (ptr->index < ptr->dir->crc_len)
116                     printf("crom_next: bound check failed\n");
117 
118           if (cc->depth > 0) {
119                     cc->depth--;
120                     goto again;
121           }
122           /* no more data */
123           cc->depth = -1;
124 }
125 
126 
127 struct csrreg *
crom_search_key(struct crom_context * cc,uint8_t key)128 crom_search_key(struct crom_context *cc, uint8_t key)
129 {
130           struct csrreg *reg;
131 
132           while (cc->depth >= 0) {
133                     reg = crom_get(cc);
134                     if (reg->key == key)
135                               return reg;
136                     crom_next(cc);
137           }
138           return NULL;
139 }
140 
141 int
crom_has_specver(uint32_t * p,uint32_t spec,uint32_t ver)142 crom_has_specver(uint32_t *p, uint32_t spec, uint32_t ver)
143 {
144           struct csrreg *reg;
145           struct crom_context c, *cc;
146           int state = 0;
147 
148           cc = &c;
149           crom_init_context(cc, p);
150           while (cc->depth >= 0) {
151                     reg = crom_get(cc);
152                     if (state == 0) {
153                               if (reg->key == CSRKEY_SPEC && reg->val == spec)
154                                         state = 1;
155                               else
156                                         state = 0;
157                     } else {
158                               if (reg->key == CSRKEY_VER && reg->val == ver)
159                                         return 1;
160                               else
161                                         state = 0;
162                     }
163                     crom_next(cc);
164           }
165           return 0;
166 }
167 
168 void
crom_parse_text(struct crom_context * cc,char * buf,int len)169 crom_parse_text(struct crom_context *cc, char *buf, int len)
170 {
171           struct csrreg *reg;
172           struct csrtext *textleaf;
173           uint32_t *bp;
174           int i, qlen;
175           static const char nullstr[] = "(null)";
176 
177           if (cc->depth < 0)
178                     return;
179 
180           reg = crom_get(cc);
181           switch (reg->key) {
182           case CROM_TEXTLEAF:
183           case CROM_TEXTLEAF2:
184                     break;
185           default:
186                     if ((char *)(reg + reg->val) > CROM_END(cc)) {
187                               strncpy(buf, nullstr, len);
188                               return;
189                     }
190                     break;
191           }
192           textleaf = (struct csrtext *)(reg + reg->val);
193 
194           if ((char *)textleaf + textleaf->crc_len > CROM_END(cc)) {
195                     strncpy(buf, nullstr, len);
196                     return;
197           }
198 
199           /* XXX should check spec and type */
200 
201           bp = (uint32_t *)buf;
202           qlen = textleaf->crc_len - 2;
203           if (len < qlen * 4)
204                     qlen = len/4;
205           for (i = 0; i < qlen; i++)
206                     *bp++ = ntohl(textleaf->text[i]);
207           /* make sure to terminate the string */
208           if (len <= qlen * 4)
209                     buf[len - 1] = 0;
210           else
211                     buf[qlen * 4] = 0;
212 }
213 
214 uint16_t
crom_crc(uint32_t * ptr,int len)215 crom_crc(uint32_t *ptr, int len)
216 {
217           int i, shift;
218           uint32_t data, sum, crc = 0;
219 
220           for (i = 0; i < len; i++) {
221                     data = ptr[i];
222                     for (shift = 28; shift >= 0; shift -= 4) {
223                               sum = ((crc >> 12) ^ (data >> shift)) & 0xf;
224                               crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum;
225                     }
226                     crc &= 0xffff;
227           }
228           return (uint16_t)crc;
229 }
230 
231 #if !defined(_KERNEL) && !defined(_BOOT)
232 static void
crom_desc_specver(uint32_t spec,uint32_t ver,char * buf,int len)233 crom_desc_specver(uint32_t spec, uint32_t ver, char *buf, int len)
234 {
235           const char *s = NULL;
236 
237           if (spec == CSRVAL_ANSIT10 || spec == 0) {
238                     switch (ver) {
239                     case CSRVAL_T10SBP2:
240                               s = "SBP-2";
241                               break;
242                     default:
243                               if (spec != 0)
244                                         s = "unknown ANSIT10";
245                     }
246           }
247           if (spec == CSRVAL_1394TA || spec == 0) {
248                     switch (ver) {
249                     case CSR_PROTAVC:
250                               s = "AV/C";
251                               break;
252                     case CSR_PROTCAL:
253                               s = "CAL";
254                               break;
255                     case CSR_PROTEHS:
256                               s = "EHS";
257                               break;
258                     case CSR_PROTHAVI:
259                               s = "HAVi";
260                               break;
261                     case CSR_PROTCAM104:
262                               s = "1394 Cam 1.04";
263                               break;
264                     case CSR_PROTCAM120:
265                               s = "1394 Cam 1.20";
266                               break;
267                     case CSR_PROTCAM130:
268                               s = "1394 Cam 1.30";
269                               break;
270                     case CSR_PROTDPP:
271                               s = "1394 Direct print";
272                               break;
273                     case CSR_PROTIICP:
274                               s = "Industrial & Instrument";
275                               break;
276                     default:
277                               if (spec != 0)
278                                         s = "unknown 1394TA";
279                     }
280           }
281           if (s != NULL)
282                     snprintf(buf, len, "%s", s);
283 }
284 
285 const char *
crom_desc(struct crom_context * cc,char * buf,size_t len)286 crom_desc(struct crom_context *cc, char *buf, size_t len)
287 {
288           struct csrreg *reg;
289           struct csrdirectory *dir;
290           const char *desc;
291           uint16_t crc;
292           size_t l = 0;
293 
294           reg = crom_get(cc);
295           switch (reg->key & CSRTYPE_MASK) {
296           case CSRTYPE_I:
297 #if 0
298                     l += snprintf(buf + l, len - l, "0x%x", reg->val);
299 #else
300                     *buf = '\0';
301 #endif
302                     break;
303           case CSRTYPE_C:
304                     l += snprintf(buf + l, len - l, "offset=0x%04x(%d)",
305                         reg->val, reg->val);
306                     break;
307           case CSRTYPE_L:
308                     /* XXX fall through */
309           case CSRTYPE_D:
310                     dir = (struct csrdirectory *) (reg + reg->val);
311                     crc = crom_crc((uint32_t *)dir->entry, dir->crc_len);
312                     l += snprintf(buf + l, len - l, "len=%d crc=0x%04x ",
313                         dir->crc_len, crc);
314 
315                     if (l > len)
316                               l = len;
317                     if (crc == dir->crc)
318                               l += snprintf(buf + l, len - l, "(OK) ");
319                     else
320                               l += snprintf(buf + l, len - l, "(NG, 0x%x) ",
321                                   dir->crc);
322           }
323           if (l > len)
324                     l = len;
325           switch (reg->key) {
326           case CSRKEY_VENDOR: /* 0x03 */
327                     desc = "module_vendor_ID";
328                     break;
329           case CSRKEY_HW: /* 0x04 */
330                     desc = "hardware_version";
331                     break;
332           case CSRKEY_NCAP: /* 0x0c */
333                     desc = "node_capabilities";
334                     break;
335           case CSRKEY_SPEC: /* 0x12 */
336                     desc = "unit_spec_ID";
337                     break;
338           case CSRKEY_VER: /* 0x13 */
339                     desc = "unit_sw_version";
340                     crom_desc_specver(0, reg->val, buf, len);
341                     break;
342           case CSRKEY_DINFO: /* 0x14 */
343                     desc = "logical_unit_number";
344                     break;
345           case CSRKEY_MODEL: /* 0x17 */
346                     desc = "model_ID";
347                     break;
348           case CSRKEY_REV: /* 0x21 */
349                     desc = "revision_ID";
350                     break;
351           case 0x38:
352                     desc = "command_set_spec_ID";
353                     break;
354           case 0x39:
355                     desc = "command_set";
356                     break;
357           case 0x3a:
358                     desc = "unit_characteristics";
359                     break;
360           case 0x3b:
361                     desc = "command_set_revision";
362                     break;
363           case 0x3c:
364                     desc = "firmware_revision";
365                     break;
366           case 0x3d:
367                     desc = "reconnect_timeout";
368                     break;
369           case 0x40:
370                     desc = "command_regs_base";
371                     break;
372           case 0x54:
373                     desc = "management_agent";
374                     break;
375           case CROM_TEXTLEAF: /* 0x81 */
376           case CROM_TEXTLEAF2: /* 0x82 */
377                     desc = "text_leaf";
378                     crom_parse_text(cc, buf + l, len - l);
379                     break;
380           case CROM_NODEID: /* 0x8d */
381                     desc = "node_unique_ID";
382                     break;
383           case 0xd1:
384                     desc = "unit_directory";
385                     break;
386           case 0xd4:
387                     desc = "logical_unit_directory";
388                     break;
389           default:
390                     desc = "unknown";
391           }
392           return desc;
393 }
394 #endif
395 
396 #if defined(_KERNEL) || defined(_BOOT) || defined(TEST)
397 
398 int
crom_add_quad(struct crom_chunk * chunk,uint32_t entry)399 crom_add_quad(struct crom_chunk *chunk, uint32_t entry)
400 {
401           int index;
402 
403           index = chunk->data.crc_len;
404           if (index >= CROM_MAX_CHUNK_LEN - 1) {
405                     printf("too large chunk %d\n", index);
406                     return -1;
407           }
408           chunk->data.buf[index] = entry;
409           chunk->data.crc_len++;
410           return index;
411 }
412 
413 int
crom_add_entry(struct crom_chunk * chunk,int key,int val)414 crom_add_entry(struct crom_chunk *chunk, int key, int val)
415 {
416           union {
417                     struct csrreg reg;
418                     uint32_t i;
419           } foo;
420 
421           foo.reg.key = key;
422           foo.reg.val = val;
423 
424           return crom_add_quad(chunk, foo.i);
425 }
426 
427 int
crom_add_chunk(struct crom_src * src,struct crom_chunk * parent,struct crom_chunk * child,int key)428 crom_add_chunk(struct crom_src *src, struct crom_chunk *parent,
429                  struct crom_chunk *child, int key)
430 {
431           int index;
432 
433           if (parent == NULL) {
434                     STAILQ_INSERT_TAIL(&src->chunk_list, child, link);
435                     return 0;
436           }
437 
438           index = crom_add_entry(parent, key, 0);
439           if (index < 0)
440                     return -1;
441           child->ref_chunk = parent;
442           child->ref_index = index;
443           STAILQ_INSERT_TAIL(&src->chunk_list, child, link);
444           return index;
445 }
446 
447 #define MAX_TEXT (int)((CROM_MAX_CHUNK_LEN + 1) * 4 - sizeof(struct csrtext))
448 int
crom_add_simple_text(struct crom_src * src,struct crom_chunk * parent,struct crom_chunk * chunk,const char * buf)449 crom_add_simple_text(struct crom_src *src, struct crom_chunk *parent,
450                          struct crom_chunk *chunk, const char *buf)
451 {
452           struct csrtext *tl;
453           uint32_t *p;
454           int len, i;
455           char t[MAX_TEXT];
456 
457           len = strlen(buf);
458           if (len > MAX_TEXT) {
459                     printf("text(%d) trancated to %d.\n", len, MAX_TEXT);
460                     len = MAX_TEXT;
461           }
462 
463           tl = (struct csrtext *) &chunk->data;
464           tl->crc_len = howmany(sizeof(struct csrtext) + len, sizeof(uint32_t));
465           tl->spec_id = 0;
466           tl->spec_type = 0;
467           tl->lang_id = 0;
468           memset(t, 0, roundup2(len, sizeof(uint32_t)));
469           memcpy(t, buf, len);
470           p = (uint32_t *)t;
471           for (i = 0; i < howmany(len, sizeof(uint32_t)); i++)
472                     tl->text[i] = ntohl(*p++);
473           return crom_add_chunk(src, parent, chunk, CROM_TEXTLEAF);
474 }
475 
476 static int
crom_copy(uint32_t * src,uint32_t * dst,int * offset,int len,int maxlen)477 crom_copy(uint32_t *src, uint32_t *dst, int *offset, int len, int maxlen)
478 {
479 
480           if (*offset + len > maxlen) {
481                     printf("Config. ROM is too large for the buffer\n");
482                     return -1;
483           }
484           memcpy((char *)(dst + *offset), src, len * sizeof(uint32_t));
485           *offset += len;
486           return 0;
487 }
488 
489 int
crom_load(struct crom_src * src,uint32_t * buf,int maxlen)490 crom_load(struct crom_src *src, uint32_t *buf, int maxlen)
491 {
492           struct crom_chunk *chunk, *parent;
493           struct csrhdr *hdr;
494 #if defined(_KERNEL) || defined(_BOOT)
495           uint32_t *ptr;
496           int i;
497 #endif
498           int count, offset;
499           int len;
500 
501           offset = 0;
502           /* Determine offset */
503           STAILQ_FOREACH(chunk, &src->chunk_list, link) {
504                     chunk->offset = offset;
505                     /* Assume the offset of the parent is already known */
506                     parent = chunk->ref_chunk;
507                     if (parent != NULL) {
508                               struct csrreg *reg;
509                               const int ref_index = chunk->ref_index;
510 
511                               reg = (struct csrreg *)&parent->data.buf[ref_index];
512                               reg->val =
513                                   offset - (parent->offset + 1 + chunk->ref_index);
514                     }
515                     offset += (1 + chunk->data.crc_len);
516           }
517 
518           /* Calculate CRC and dump to the buffer */
519           len = 1 + src->hdr.info_len;
520           count = 0;
521           if (crom_copy((uint32_t *)&src->hdr, buf, &count, len, maxlen) < 0)
522                     return -1;
523           STAILQ_FOREACH(chunk, &src->chunk_list, link) {
524                     chunk->data.crc =
525                         crom_crc(chunk->data.buf, chunk->data.crc_len);
526 
527                     len = 1 + chunk->data.crc_len;
528                     if (crom_copy((uint32_t *)&chunk->data, buf, &count, len,
529                                                                                 maxlen) < 0)
530                               return -1;
531           }
532           hdr = (struct csrhdr *)buf;
533           hdr->crc_len = count - 1;
534           hdr->crc = crom_crc(&buf[1], hdr->crc_len);
535 
536 #if defined(_KERNEL) || defined(_BOOT)
537           /* byte swap */
538           ptr = buf;
539           for (i = 0; i < count; i++) {
540                     *ptr = htonl(*ptr);
541                     ptr++;
542           }
543 #endif
544 
545           return count;
546 }
547 #endif
548 
549 #ifdef TEST
550 int
main(void)551 main(void)
552 {
553           struct crom_src src;
554           struct crom_chunk root, unit[3], text[7];
555           uint32_t buf[256], *p;
556           int i;
557           extern const char ostype[];
558 
559           memset(&src, 0, sizeof(src));
560           memset(&root, 0, sizeof(root));
561           memset(unit, 0, sizeof(unit));
562           memset(text, 0, sizeof(text));
563           memset(buf, 0, sizeof(buf));
564 
565           /* BUS info sample */
566           src.hdr.info_len = 4;
567           src.businfo.bus_name = CSR_BUS_NAME_IEEE1394;
568           src.businfo.eui64.hi = 0x11223344;
569           src.businfo.eui64.lo = 0x55667788;
570           src.businfo.link_spd = FWSPD_S400;
571           src.businfo.generation = 0;
572           src.businfo.max_rom = MAXROM_4;
573           src.businfo.max_rec = 10;
574           src.businfo.cyc_clk_acc = 100;
575           src.businfo.pmc = 0;
576           src.businfo.bmc = 1;
577           src.businfo.isc = 1;
578           src.businfo.cmc = 1;
579           src.businfo.irmc = 1;
580           STAILQ_INIT(&src.chunk_list);
581 
582           /* Root directory */
583           crom_add_chunk(&src, NULL, &root, 0);
584           crom_add_entry(&root, CSRKEY_NCAP, 0x123456);
585           /* private company_id */
586           crom_add_entry(&root, CSRKEY_VENDOR, 0xacde48);
587 
588           crom_add_simple_text(&src, &root, &text[0], ostype);
589           crom_add_entry(&root, CSRKEY_HW, __NetBSD_Version__);
590           crom_add_simple_text(&src, &root, &text[1], OS_VER_STR);
591 
592           /* SBP unit directory */
593           crom_add_chunk(&src, &root, &unit[0], CROM_UDIR);
594           crom_add_entry(&unit[0], CSRKEY_SPEC, CSRVAL_ANSIT10);
595           crom_add_entry(&unit[0], CSRKEY_VER, CSRVAL_T10SBP2);
596           crom_add_entry(&unit[0], CSRKEY_COM_SPEC, CSRVAL_ANSIT10);
597           crom_add_entry(&unit[0], CSRKEY_COM_SET, CSRVAL_SCSI);
598           /* management_agent */
599           crom_add_entry(&unit[0], CROM_MGM, 0x1000);
600           crom_add_entry(&unit[0], CSRKEY_UNIT_CH, (10<<8) | 8);
601           /* Device type and LUN */
602           crom_add_entry(&unit[0], CROM_LUN, 0);
603           crom_add_entry(&unit[0], CSRKEY_MODEL, 1);
604           crom_add_simple_text(&src, &unit[0], &text[2], "scsi_target");
605 
606           /* RFC2734 IPv4 over IEEE1394 */
607           crom_add_chunk(&src, &root, &unit[1], CROM_UDIR);
608           crom_add_entry(&unit[1], CSRKEY_SPEC, CSRVAL_IETF);
609           crom_add_simple_text(&src, &unit[1], &text[3], "IANA");
610           crom_add_entry(&unit[1], CSRKEY_VER, 1);
611           crom_add_simple_text(&src, &unit[1], &text[4], "IPv4");
612 
613           /* RFC3146 IPv6 over IEEE1394 */
614           crom_add_chunk(&src, &root, &unit[2], CROM_UDIR);
615           crom_add_entry(&unit[2], CSRKEY_SPEC, CSRVAL_IETF);
616           crom_add_simple_text(&src, &unit[2], &text[5], "IANA");
617           crom_add_entry(&unit[2], CSRKEY_VER, 2);
618           crom_add_simple_text(&src, &unit[2], &text[6], "IPv6");
619 
620           crom_load(&src, buf, 256);
621           p = buf;
622 #define DUMP_FORMAT "%08x %08x %08x %08x %08x %08x %08x %08x\n"
623           for (i = 0; i < 256 / 8; i++) {
624                     printf(DUMP_FORMAT,
625                         p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
626                     p += 8;
627           }
628           return 0;
629 }
630 #endif
631