Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
csr_wifi_hip_xbv.c
Go to the documentation of this file.
1 /*****************************************************************************
2 
3  (c) Cambridge Silicon Radio Limited 2012
4  All rights reserved and confidential information of CSR
5 
6  Refer to LICENSE.txt included with this source for details
7  on the license terms.
8 
9 *****************************************************************************/
10 
11 /*
12  * ---------------------------------------------------------------------------
13  * FILE: csr_wifi_hip_xbv.c
14  *
15  * PURPOSE:
16  * Routines for downloading firmware to UniFi.
17  *
18  * UniFi firmware files use a nested TLV (Tag-Length-Value) format.
19  *
20  * ---------------------------------------------------------------------------
21  */
22 #include <linux/slab.h>
23 
24 #ifdef CSR_WIFI_XBV_TEST
25 /* Standalone test harness */
26 #include "unifi_xbv.h"
27 #include "csr_wifi_hip_unifihw.h"
28 #else
29 /* Normal driver build */
31 #include "csr_wifi_hip_card.h"
32 #define DBG_TAG(t)
33 #endif
34 
35 #include "csr_wifi_hip_xbv.h"
36 
37 #define STREAM_CHECKSUM 0x6d34 /* Sum of uint16s in each patch stream */
38 
39 /* XBV sizes used in patch conversion
40  */
41 #define PTDL_MAX_SIZE 2048 /* Max bytes allowed per PTDL */
42 #define PTDL_HDR_SIZE (4 + 2 + 6 + 2) /* sizeof(fw_id, sec_len, patch_cmd, csum) */
43 
44 /* Struct to represent a buffer for reading firmware file */
45 
46 typedef struct
47 {
48  void *dlpriv;
51 } ct_t;
52 
53 /* Struct to represent a TLV field */
54 typedef struct
55 {
56  char t_name[4];
58 } tag_t;
59 
60 
61 #define TAG_EQ(i, v) (((i)[0] == (v)[0]) && \
62  ((i)[1] == (v)[1]) && \
63  ((i)[2] == (v)[2]) && \
64  ((i)[3] == (v)[3]))
65 
66 /* We create a small stack on the stack that contains an enum
67  * indicating the containing list segments, and the offset at which
68  * those lists end. This enables a lot more error checking. */
69 typedef enum
70 {
72  /*xbv_info,*/
79 
80 #define XBV_STACK_SIZE 6
81 #define XBV_MAX_OFFS 0x7fffffff
82 
83 typedef struct
84 {
85  struct
86  {
89  } s[XBV_STACK_SIZE];
91 } xbv_stack_t;
92 
93 static s32 read_tag(card_t *card, ct_t *ct, tag_t *tag);
94 static s32 read_bytes(card_t *card, ct_t *ct, void *buf, u32 len);
95 static s32 read_uint(card_t *card, ct_t *ct, u32 *u, u32 len);
96 static s32 xbv_check(xbv1_t *fwinfo, const xbv_stack_t *stack,
97  xbv_mode new_mode, xbv_container old_cont);
98 static s32 xbv_push(xbv1_t *fwinfo, xbv_stack_t *stack,
99  xbv_mode new_mode, xbv_container old_cont,
100  xbv_container new_cont, u32 ioff);
101 
102 static u32 write_uint16(void *buf, const u32 offset,
103  const u16 val);
104 static u32 write_uint32(void *buf, const u32 offset,
105  const u32 val);
106 static u32 write_bytes(void *buf, const u32 offset,
107  const u8 *data, const u32 len);
108 static u32 write_tag(void *buf, const u32 offset,
109  const char *tag_str);
110 static u32 write_chunk(void *buf, const u32 offset,
111  const char *tag_str,
112  const u32 payload_len);
113 static u16 calc_checksum(void *buf, const u32 offset,
114  const u32 bytes_len);
115 static u32 calc_patch_size(const xbv1_t *fwinfo);
116 
117 static u32 write_xbv_header(void *buf, const u32 offset,
118  const u32 file_payload_length);
119 static u32 write_ptch_header(void *buf, const u32 offset,
120  const u32 fw_id);
121 static u32 write_patchcmd(void *buf, const u32 offset,
122  const u32 dst_genaddr, const u16 len);
123 static u32 write_reset_ptdl(void *buf, const u32 offset,
124  const xbv1_t *fwinfo, u32 fw_id);
125 static u32 write_fwdl_to_ptdl(void *buf, const u32 offset,
126  fwreadfn_t readfn, const struct FWDL *fwdl,
127  const void *fw_buf, const u32 fw_id,
128  void *rdbuf);
129 
130 /*
131  * ---------------------------------------------------------------------------
132  * parse_xbv1
133  *
134  * Scan the firmware file to find the TLVs we are interested in.
135  * Actions performed:
136  * - check we support the file format version in VERF
137  * Store these TLVs if we have a firmware image:
138  * - SLTP Symbol Lookup Table Pointer
139  * - FWDL firmware download segments
140  * - FWOL firmware overlay segment
141  * - VMEQ Register probe tests to verify matching h/w
142  * Store these TLVs if we have a patch file:
143  * - FWID the firmware build ID that this file patches
144  * - PTDL The actual patches
145  *
146  * The structure pointed to by fwinfo is cleared and
147  * 'fwinfo->mode' is set to 'unknown'. The 'fwinfo->mode'
148  * variable is set to 'firmware' or 'patch' once we know which
149  * sort of XBV file we have.
150  *
151  * Arguments:
152  * readfn Pointer to function to call to read from the file.
153  * dlpriv Opaque pointer arg to pass to readfn.
154  * fwinfo Pointer to fwinfo struct to fill in.
155  *
156  * Returns:
157  * CSR_RESULT_SUCCESS on success, CSR error code on failure
158  * ---------------------------------------------------------------------------
159  */
161 {
162  ct_t ct;
163  tag_t tag;
165 
166  ct.dlpriv = dlpriv;
167  ct.ioffset = 0;
168  ct.iread = readfn;
169 
170  memset(fwinfo, 0, sizeof(xbv1_t));
171  fwinfo->mode = xbv_unknown;
172 
173  /* File must start with XBV1 triplet */
174  if (read_tag(card, &ct, &tag) <= 0)
175  {
176  unifi_error(NULL, "File is not UniFi firmware\n");
178  }
179 
180  DBG_TAG(tag.t_name);
181 
182  if (!TAG_EQ(tag.t_name, "XBV1"))
183  {
184  unifi_error(NULL, "File is not UniFi firmware (%s)\n", tag.t_name);
186  }
187 
188  stack.ptr = 0;
189  stack.s[stack.ptr].container = xbv_xbv1;
190  stack.s[stack.ptr].ioffset_end = XBV_MAX_OFFS;
191 
192  /* Now scan the file */
193  while (1)
194  {
195  s32 n;
196 
197  n = read_tag(card, &ct, &tag);
198  if (n < 0)
199  {
200  unifi_error(NULL, "No tag\n");
202  }
203  if (n == 0)
204  {
205  /* End of file */
206  break;
207  }
208 
209  DBG_TAG(tag.t_name);
210 
211  /* File format version */
212  if (TAG_EQ(tag.t_name, "VERF"))
213  {
214  u32 version;
215 
216  if (xbv_check(fwinfo, &stack, xbv_unknown, xbv_xbv1) ||
217  (tag.t_len != 2) ||
218  read_uint(card, &ct, &version, 2))
219  {
221  }
222  if (version != 0)
223  {
224  unifi_error(NULL, "Unsupported firmware file version: %d.%d\n",
225  version >> 8, version & 0xFF);
227  }
228  }
229  else if (TAG_EQ(tag.t_name, "LIST"))
230  {
231  char name[4];
232  u32 list_end;
233 
234  list_end = ct.ioffset + tag.t_len;
235 
236  if (read_bytes(card, &ct, name, 4))
237  {
239  }
240 
241  DBG_TAG(name);
242  if (TAG_EQ(name, "FW "))
243  {
244  if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_xbv1, xbv_fw, list_end))
245  {
247  }
248  }
249  else if (TAG_EQ(name, "VERS"))
250  {
251  if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_fw, xbv_vers, list_end) ||
252  (fwinfo->vers.num_vand != 0))
253  {
255  }
256  }
257  else if (TAG_EQ(name, "VAND"))
258  {
259  struct VAND *vand;
260 
261  if (xbv_push(fwinfo, &stack, xbv_firmware, xbv_vers, xbv_vand, list_end) ||
262  (fwinfo->vers.num_vand >= MAX_VAND))
263  {
265  }
266 
267  /* Get a new VAND */
268  vand = fwinfo->vand + fwinfo->vers.num_vand++;
269 
270  /* Fill it in */
271  vand->first = fwinfo->num_vmeq;
272  vand->count = 0;
273  }
274  else if (TAG_EQ(name, "PTCH"))
275  {
276  if (xbv_push(fwinfo, &stack, xbv_patch, xbv_xbv1, xbv_ptch, list_end))
277  {
279  }
280  }
281  else
282  {
283  /* Skip over any other lists. We dont bother to push
284  * the new list type now as we would only pop it at
285  * the end of the outer loop. */
286  ct.ioffset += tag.t_len - 4;
287  }
288  }
289  else if (TAG_EQ(tag.t_name, "SLTP"))
290  {
291  u32 addr;
292 
293  if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
294  (tag.t_len != 4) ||
295  (fwinfo->slut_addr != 0) ||
296  read_uint(card, &ct, &addr, 4))
297  {
299  }
300 
301  fwinfo->slut_addr = addr;
302  }
303  else if (TAG_EQ(tag.t_name, "FWDL"))
304  {
305  u32 addr;
306  struct FWDL *fwdl;
307 
308  if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
309  (fwinfo->num_fwdl >= MAX_FWDL) ||
310  (read_uint(card, &ct, &addr, 4)))
311  {
313  }
314 
315  fwdl = fwinfo->fwdl + fwinfo->num_fwdl++;
316 
317  fwdl->dl_size = tag.t_len - 4;
318  fwdl->dl_addr = addr;
319  fwdl->dl_offset = ct.ioffset;
320 
321  ct.ioffset += tag.t_len - 4;
322  }
323  else if (TAG_EQ(tag.t_name, "FWOV"))
324  {
325  if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_fw) ||
326  (fwinfo->fwov.dl_size != 0) ||
327  (fwinfo->fwov.dl_offset != 0))
328  {
330  }
331 
332  fwinfo->fwov.dl_size = tag.t_len;
333  fwinfo->fwov.dl_offset = ct.ioffset;
334 
335  ct.ioffset += tag.t_len;
336  }
337  else if (TAG_EQ(tag.t_name, "VMEQ"))
338  {
339  u32 temp[3];
340  struct VAND *vand;
341  struct VMEQ *vmeq;
342 
343  if (xbv_check(fwinfo, &stack, xbv_firmware, xbv_vand) ||
344  (fwinfo->num_vmeq >= MAX_VMEQ) ||
345  (fwinfo->vers.num_vand == 0) ||
346  (tag.t_len != 8) ||
347  read_uint(card, &ct, &temp[0], 4) ||
348  read_uint(card, &ct, &temp[1], 2) ||
349  read_uint(card, &ct, &temp[2], 2))
350  {
352  }
353 
354  /* Get the last VAND */
355  vand = fwinfo->vand + (fwinfo->vers.num_vand - 1);
356 
357  /* Get a new VMEQ */
358  vmeq = fwinfo->vmeq + fwinfo->num_vmeq++;
359 
360  /* Note that this VAND contains another VMEQ */
361  vand->count++;
362 
363  /* Fill in the VMEQ */
364  vmeq->addr = temp[0];
365  vmeq->mask = (u16)temp[1];
366  vmeq->value = (u16)temp[2];
367  }
368  else if (TAG_EQ(tag.t_name, "FWID"))
369  {
370  u32 build_id;
371 
372  if (xbv_check(fwinfo, &stack, xbv_patch, xbv_ptch) ||
373  (tag.t_len != 4) ||
374  (fwinfo->build_id != 0) ||
375  read_uint(card, &ct, &build_id, 4))
376  {
378  }
379 
380  fwinfo->build_id = build_id;
381  }
382  else if (TAG_EQ(tag.t_name, "PTDL"))
383  {
384  struct PTDL *ptdl;
385 
386  if (xbv_check(fwinfo, &stack, xbv_patch, xbv_ptch) ||
387  (fwinfo->num_ptdl >= MAX_PTDL))
388  {
390  }
391 
392  /* Allocate a new PTDL */
393  ptdl = fwinfo->ptdl + fwinfo->num_ptdl++;
394 
395  ptdl->dl_size = tag.t_len;
396  ptdl->dl_offset = ct.ioffset;
397 
398  ct.ioffset += tag.t_len;
399  }
400  else
401  {
402  /*
403  * If we get here it is a tag we are not interested in,
404  * just skip over it.
405  */
406  ct.ioffset += tag.t_len;
407  }
408 
409  /* Check to see if we are at the end of the currently stacked
410  * segment. We could finish more than one list at a time. */
411  while (ct.ioffset >= stack.s[stack.ptr].ioffset_end)
412  {
413  if (ct.ioffset > stack.s[stack.ptr].ioffset_end)
414  {
416  "XBV file has overrun stack'd segment %d (%d > %d)\n",
417  stack.ptr, ct.ioffset, stack.s[stack.ptr].ioffset_end);
419  }
420  if (stack.ptr <= 0)
421  {
422  unifi_error(NULL, "XBV file has underrun stack pointer\n");
424  }
425  stack.ptr--;
426  }
427  }
428 
429  if (stack.ptr != 0)
430  {
431  unifi_error(NULL, "Last list of XBV is not complete.\n");
433  }
434 
435  return CSR_RESULT_SUCCESS;
436 } /* xbv1_parse() */
437 
438 
439 /* Check the the XBV file is of a consistant sort (either firmware or
440  * patch) and that we are in the correct containing list type. */
441 static s32 xbv_check(xbv1_t *fwinfo, const xbv_stack_t *stack,
442  xbv_mode new_mode, xbv_container old_cont)
443 {
444  /* If the new file mode is unknown the current packet could be in
445  * either (any) type of XBV file, and we cant make a decission at
446  * this time. */
447  if (new_mode != xbv_unknown)
448  {
449  if (fwinfo->mode == xbv_unknown)
450  {
451  fwinfo->mode = new_mode;
452  }
453  else if (fwinfo->mode != new_mode)
454  {
455  return -1;
456  }
457  }
458  /* If the current stack top doesn't match what we expect then the
459  * file is corrupt. */
460  if (stack->s[stack->ptr].container != old_cont)
461  {
462  return -1;
463  }
464  return 0;
465 }
466 
467 
468 /* Make checks as above and then enter a new list */
469 static s32 xbv_push(xbv1_t *fwinfo, xbv_stack_t *stack,
470  xbv_mode new_mode, xbv_container old_cont,
471  xbv_container new_cont, u32 new_ioff)
472 {
473  if (xbv_check(fwinfo, stack, new_mode, old_cont))
474  {
475  return -1;
476  }
477 
478  /* Check that our stack won't overflow. */
479  if (stack->ptr >= (XBV_STACK_SIZE - 1))
480  {
481  return -1;
482  }
483 
484  /* Add the new list element to the top of the stack. */
485  stack->ptr++;
486  stack->s[stack->ptr].container = new_cont;
487  stack->s[stack->ptr].ioffset_end = new_ioff;
488 
489  return 0;
490 }
491 
492 
493 static u32 xbv2uint(u8 *ptr, s32 len)
494 {
495  u32 u = 0;
496  s16 i;
497 
498  for (i = 0; i < len; i++)
499  {
500  u32 b;
501  b = ptr[i];
502  u += b << (i * 8);
503  }
504  return u;
505 }
506 
507 
508 static s32 read_tag(card_t *card, ct_t *ct, tag_t *tag)
509 {
510  u8 buf[8];
511  s32 n;
512 
513  n = (*ct->iread)(card->ospriv, ct->dlpriv, ct->ioffset, buf, 8);
514  if (n <= 0)
515  {
516  return n;
517  }
518 
519  /* read the tag and length */
520  if (n != 8)
521  {
522  return -1;
523  }
524 
525  /* get section tag */
526  memcpy(tag->t_name, buf, 4);
527 
528  /* get section length */
529  tag->t_len = xbv2uint(buf + 4, 4);
530 
531  ct->ioffset += 8;
532 
533  return 8;
534 } /* read_tag() */
535 
536 
537 static s32 read_bytes(card_t *card, ct_t *ct, void *buf, u32 len)
538 {
539  /* read the tag value */
540  if ((*ct->iread)(card->ospriv, ct->dlpriv, ct->ioffset, buf, len) != (s32)len)
541  {
542  return -1;
543  }
544 
545  ct->ioffset += len;
546 
547  return 0;
548 } /* read_bytes() */
549 
550 
551 static s32 read_uint(card_t *card, ct_t *ct, u32 *u, u32 len)
552 {
553  u8 buf[4];
554 
555  /* Integer cannot be more than 4 bytes */
556  if (len > 4)
557  {
558  return -1;
559  }
560 
561  if (read_bytes(card, ct, buf, len))
562  {
563  return -1;
564  }
565 
566  *u = xbv2uint(buf, len);
567 
568  return 0;
569 } /* read_uint() */
570 
571 
572 static u32 write_uint16(void *buf, const u32 offset, const u16 val)
573 {
574  u8 *dst = (u8 *)buf + offset;
575  *dst++ = (u8)(val & 0xff); /* LSB first */
576  *dst = (u8)(val >> 8);
577  return sizeof(u16);
578 }
579 
580 
581 static u32 write_uint32(void *buf, const u32 offset, const u32 val)
582 {
583  (void)write_uint16(buf, offset + 0, (u16)(val & 0xffff));
584  (void)write_uint16(buf, offset + 2, (u16)(val >> 16));
585  return sizeof(u32);
586 }
587 
588 
589 static u32 write_bytes(void *buf, const u32 offset, const u8 *data, const u32 len)
590 {
591  u32 i;
592  u8 *dst = (u8 *)buf + offset;
593 
594  for (i = 0; i < len; i++)
595  {
596  *dst++ = *((u8 *)data + i);
597  }
598  return len;
599 }
600 
601 
602 static u32 write_tag(void *buf, const u32 offset, const char *tag_str)
603 {
604  u8 *dst = (u8 *)buf + offset;
605  memcpy(dst, tag_str, 4);
606  return 4;
607 }
608 
609 
610 static u32 write_chunk(void *buf, const u32 offset, const char *tag_str, const u32 payload_len)
611 {
612  u32 written = 0;
613  written += write_tag(buf, offset, tag_str);
614  written += write_uint32(buf, written + offset, (u32)payload_len);
615 
616  return written;
617 }
618 
619 
620 static u16 calc_checksum(void *buf, const u32 offset, const u32 bytes_len)
621 {
622  u32 i;
623  u8 *src = (u8 *)buf + offset;
624  u16 sum = 0;
625  u16 val;
626 
627  for (i = 0; i < bytes_len / 2; i++)
628  {
629  /* Contents copied to file is LE, host might not be */
630  val = (u16) * src++; /* LSB */
631  val += (u16)(*src++) << 8; /* MSB */
632  sum += val;
633  }
634 
635  /* Total of uint16s in the stream plus the stored check value
636  * should equal STREAM_CHECKSUM when decoded.
637  */
638  return (STREAM_CHECKSUM - sum);
639 }
640 
641 
642 #define PTDL_RESET_DATA_SIZE 20 /* Size of reset vectors PTDL */
643 
644 static u32 calc_patch_size(const xbv1_t *fwinfo)
645 {
646  s16 i;
647  u32 size = 0;
648 
649  /*
650  * Work out how big an equivalent patch format file must be for this image.
651  * This only needs to be approximate, so long as it's large enough.
652  */
653  if (fwinfo->mode != xbv_firmware)
654  {
655  return 0;
656  }
657 
658  /* Payload (which will get put into a series of PTDLs) */
659  for (i = 0; i < fwinfo->num_fwdl; i++)
660  {
661  size += fwinfo->fwdl[i].dl_size;
662  }
663 
664  /* Another PTDL at the end containing reset vectors */
665  size += PTDL_RESET_DATA_SIZE;
666 
667  /* PTDL headers. Add one for remainder, one for reset vectors */
668  size += ((fwinfo->num_fwdl / PTDL_MAX_SIZE) + 2) * PTDL_HDR_SIZE;
669 
670  /* Another 1K sufficient to cover miscellaneous headers */
671  size += 1024;
672 
673  return size;
674 }
675 
676 
677 static u32 write_xbv_header(void *buf, const u32 offset, const u32 file_payload_length)
678 {
679  u32 written = 0;
680 
681  /* The length value given to the XBV chunk is the length of all subsequent
682  * contents of the file, excluding the 8 byte size of the XBV1 header itself
683  * (The added 6 bytes thus accounts for the size of the VERF)
684  */
685  written += write_chunk(buf, offset + written, (char *)"XBV1", file_payload_length + 6);
686 
687  written += write_chunk(buf, offset + written, (char *)"VERF", 2);
688  written += write_uint16(buf, offset + written, 0); /* File version */
689 
690  return written;
691 }
692 
693 
694 static u32 write_ptch_header(void *buf, const u32 offset, const u32 fw_id)
695 {
696  u32 written = 0;
697 
698  /* LIST is written with a zero length, to be updated later */
699  written += write_chunk(buf, offset + written, (char *)"LIST", 0);
700  written += write_tag(buf, offset + written, (char *)"PTCH"); /* List type */
701 
702  written += write_chunk(buf, offset + written, (char *)"FWID", 4);
703  written += write_uint32(buf, offset + written, fw_id);
704 
705 
706  return written;
707 }
708 
709 
710 #define UF_REGION_PHY 1
711 #define UF_REGION_MAC 2
712 #define UF_MEMPUT_MAC 0x0000
713 #define UF_MEMPUT_PHY 0x1000
714 
715 static u32 write_patchcmd(void *buf, const u32 offset, const u32 dst_genaddr, const u16 len)
716 {
717  u32 written = 0;
718  u32 region = (dst_genaddr >> 28);
719  u16 cmd_and_len = UF_MEMPUT_MAC;
720 
721  if (region == UF_REGION_PHY)
722  {
723  cmd_and_len = UF_MEMPUT_PHY;
724  }
725  else if (region != UF_REGION_MAC)
726  {
727  return 0; /* invalid */
728  }
729 
730  /* Write the command and data length */
731  cmd_and_len |= len;
732  written += write_uint16(buf, offset + written, cmd_and_len);
733 
734  /* Write the destination generic address */
735  written += write_uint16(buf, offset + written, (u16)(dst_genaddr >> 16));
736  written += write_uint16(buf, offset + written, (u16)(dst_genaddr & 0xffff));
737 
738  /* The data payload should be appended to the command */
739  return written;
740 }
741 
742 
743 static u32 write_fwdl_to_ptdl(void *buf, const u32 offset, fwreadfn_t readfn,
744  const struct FWDL *fwdl, const void *dlpriv,
745  const u32 fw_id, void *fw_buf)
746 {
747  u32 written = 0;
748  s16 chunks = 0;
749  u32 left = fwdl->dl_size; /* Bytes left in this fwdl */
750  u32 dl_addr = fwdl->dl_addr; /* Target address of fwdl image on XAP */
751  u32 dl_offs = fwdl->dl_offset; /* Offset of fwdl image data in source */
752  u16 csum;
753  u32 csum_start_offs; /* first offset to include in checksum */
754  u32 sec_data_len; /* section data byte count */
755  u32 sec_len; /* section data + header byte count */
756 
757  /* FWDL maps to one or more PTDLs, as max size for a PTDL is 1K words */
758  while (left)
759  {
760  /* Calculate amount to be transferred */
761  sec_data_len = CSRMIN(left, PTDL_MAX_SIZE - PTDL_HDR_SIZE);
762  sec_len = sec_data_len + PTDL_HDR_SIZE;
763 
764  /* Write PTDL header + entire PTDL size */
765  written += write_chunk(buf, offset + written, (char *)"PTDL", sec_len);
766  /* bug digest implies 4 bytes of padding here, but that seems wrong */
767 
768  /* Checksum starts here */
769  csum_start_offs = offset + written;
770 
771  /* Patch-chunk header: fw_id. Note that this is in XAP word order */
772  written += write_uint16(buf, offset + written, (u16)(fw_id >> 16));
773  written += write_uint16(buf, offset + written, (u16)(fw_id & 0xffff));
774 
775  /* Patch-chunk header: section length in uint16s */
776  written += write_uint16(buf, offset + written, (u16)(sec_len / 2));
777 
778 
779  /* Write the appropriate patch command for the data's destination ptr */
780  written += write_patchcmd(buf, offset + written, dl_addr, (u16)(sec_data_len / 2));
781 
782  /* Write the data itself (limited to the max chunk length) */
783  if (readfn(NULL, (void *)dlpriv, dl_offs, fw_buf, sec_data_len) < 0)
784  {
785  return 0;
786  }
787 
788  written += write_bytes(buf,
789  offset + written,
790  fw_buf,
791  sec_data_len);
792 
793  /* u16 checksum calculated over data written */
794  csum = calc_checksum(buf, csum_start_offs, written - (csum_start_offs - offset));
795  written += write_uint16(buf, offset + written, csum);
796 
797  left -= sec_data_len;
798  dl_addr += sec_data_len;
799  dl_offs += sec_data_len;
800  chunks++;
801  }
802 
803  return written;
804 }
805 
806 
807 #define SEC_CMD_LEN ((4 + 2) * 2) /* sizeof(cmd, vector) per XAP */
808 #define PTDL_VEC_HDR_SIZE (4 + 2 + 2) /* sizeof(fw_id, sec_len, csum) */
809 #define UF_MAC_START_VEC 0x00c00000 /* Start address of image on MAC */
810 #define UF_PHY_START_VEC 0x00c00000 /* Start address of image on PHY */
811 #define UF_MAC_START_CMD 0x6000 /* MAC "Set start address" command */
812 #define UF_PHY_START_CMD 0x7000 /* PHY "Set start address" command */
813 
814 static u32 write_reset_ptdl(void *buf, const u32 offset, const xbv1_t *fwinfo, u32 fw_id)
815 {
816  u32 written = 0;
817  u16 csum;
818  u32 csum_start_offs; /* first offset to include in checksum */
819  u32 sec_len; /* section data + header byte count */
820 
821  sec_len = SEC_CMD_LEN + PTDL_VEC_HDR_SIZE; /* Total section byte length */
822 
823  /* Write PTDL header + entire PTDL size */
824  written += write_chunk(buf, offset + written, (char *)"PTDL", sec_len);
825 
826  /* Checksum starts here */
827  csum_start_offs = offset + written;
828 
829  /* Patch-chunk header: fw_id. Note that this is in XAP word order */
830  written += write_uint16(buf, offset + written, (u16)(fw_id >> 16));
831  written += write_uint16(buf, offset + written, (u16)(fw_id & 0xffff));
832 
833  /* Patch-chunk header: section length in uint16s */
834  written += write_uint16(buf, offset + written, (u16)(sec_len / 2));
835 
836  /*
837  * Restart addresses to be executed on subsequent loader restart command.
838  */
839 
840  /* Setup the MAC start address, note word ordering */
841  written += write_uint16(buf, offset + written, UF_MAC_START_CMD);
842  written += write_uint16(buf, offset + written, (UF_MAC_START_VEC >> 16));
843  written += write_uint16(buf, offset + written, (UF_MAC_START_VEC & 0xffff));
844 
845  /* Setup the PHY start address, note word ordering */
846  written += write_uint16(buf, offset + written, UF_PHY_START_CMD);
847  written += write_uint16(buf, offset + written, (UF_PHY_START_VEC >> 16));
848  written += write_uint16(buf, offset + written, (UF_PHY_START_VEC & 0xffff));
849 
850  /* u16 checksum calculated over data written */
851  csum = calc_checksum(buf, csum_start_offs, written - (csum_start_offs - offset));
852  written += write_uint16(buf, offset + written, csum);
853 
854  return written;
855 }
856 
857 
858 /*
859  * ---------------------------------------------------------------------------
860  * read_slut
861  *
862  * desc
863  *
864  * Arguments:
865  * readfn Pointer to function to call to read from the file.
866  * dlpriv Opaque pointer arg to pass to readfn.
867  * addr Offset into firmware image of SLUT.
868  * fwinfo Pointer to fwinfo struct to fill in.
869  *
870  * Returns:
871  * Number of SLUT entries in the f/w, or -1 if the image was corrupt.
872  * ---------------------------------------------------------------------------
873  */
874 s32 xbv1_read_slut(card_t *card, fwreadfn_t readfn, void *dlpriv, xbv1_t *fwinfo,
875  symbol_t *slut, u32 slut_len)
876 {
877  s16 i;
878  s32 offset;
879  u32 magic;
880  u32 count = 0;
881  ct_t ct;
882 
883  if (fwinfo->mode != xbv_firmware)
884  {
885  return -1;
886  }
887 
888  /* Find the d/l segment containing the SLUT */
889  /* This relies on the SLUT being entirely contained in one segment */
890  offset = -1;
891  for (i = 0; i < fwinfo->num_fwdl; i++)
892  {
893  if ((fwinfo->slut_addr >= fwinfo->fwdl[i].dl_addr) &&
894  (fwinfo->slut_addr < (fwinfo->fwdl[i].dl_addr + fwinfo->fwdl[i].dl_size)))
895  {
896  offset = fwinfo->fwdl[i].dl_offset +
897  (fwinfo->slut_addr - fwinfo->fwdl[i].dl_addr);
898  }
899  }
900  if (offset < 0)
901  {
902  return -1;
903  }
904 
905  ct.dlpriv = dlpriv;
906  ct.ioffset = offset;
907  ct.iread = readfn;
908 
909  if (read_uint(card, &ct, &magic, 2))
910  {
911  return -1;
912  }
913  if (magic != SLUT_FINGERPRINT)
914  {
915  return -1;
916  }
917 
918  while (count < slut_len)
919  {
920  u32 id, obj;
921 
922  /* Read Symbol Id */
923  if (read_uint(card, &ct, &id, 2))
924  {
925  return -1;
926  }
927 
928  /* Check for end of table marker */
929  if (id == CSR_SLT_END)
930  {
931  break;
932  }
933 
934  /* Read Symbol Value */
935  if (read_uint(card, &ct, &obj, 4))
936  {
937  return -1;
938  }
939 
940  slut[count].id = (u16)id;
941  slut[count].obj = obj;
942  count++;
943  }
944 
945  return count;
946 } /* read_slut() */
947 
948 
949 /*
950  * ---------------------------------------------------------------------------
951  * xbv_to_patch
952  *
953  * Convert (the relevant parts of) a firmware xbv file into a patch xbv
954  *
955  * Arguments:
956  * card
957  * fw_buf - pointer to xbv firmware image
958  * fwinfo - structure describing the firmware image
959  * size - pointer to location into which size of f/w is written.
960  *
961  * Returns:
962  * Pointer to firmware image, or NULL on error. Caller must free this
963  * buffer via kfree() once it's finished with.
964  *
965  * Notes:
966  * The input fw_buf should have been checked via xbv1_parse prior to
967  * calling this function, so the input image is assumed valid.
968  * ---------------------------------------------------------------------------
969  */
970 #define PTCH_LIST_SIZE 16 /* sizeof PTCH+FWID chunk in LIST header */
971 
972 void* xbv_to_patch(card_t *card, fwreadfn_t readfn,
973  const void *fw_buf, const xbv1_t *fwinfo, u32 *size)
974 {
975  void *patch_buf = NULL;
976  u32 patch_buf_size;
977  u32 payload_offs = 0; /* Start of XBV payload */
978  s16 i;
979  u32 patch_offs = 0;
980  u32 list_len_offs = 0; /* Offset of PTDL LIST length parameter */
981  u32 ptdl_start_offs = 0; /* Offset of first PTDL chunk */
982  u32 fw_id;
983  void *rdbuf;
984 
985  if (!fw_buf || !fwinfo || !card)
986  {
987  return NULL;
988  }
989 
990  if (fwinfo->mode != xbv_firmware)
991  {
992  unifi_error(NULL, "Not a firmware file\n");
993  return NULL;
994  }
995 
996  /* Pre-allocate read buffer for chunk conversion */
997  rdbuf = kmalloc(PTDL_MAX_SIZE, GFP_KERNEL);
998  if (!rdbuf)
999  {
1000  unifi_error(card, "Couldn't alloc conversion buffer\n");
1001  return NULL;
1002  }
1003 
1004  /* Loader requires patch file's build ID to match the running firmware's */
1005  fw_id = card->build_id;
1006 
1007  /* Firmware XBV1 contains VERF, optional INFO, SLUT(s), FWDL(s) */
1008  /* Other chunks should get skipped. */
1009  /* VERF should be sanity-checked against chip version */
1010 
1011  /* Patch XBV1 contains VERF, optional INFO, PTCH */
1012  /* PTCH contains FWID, optional INFO, PTDL(s), PTDL(start_vec) */
1013  /* Each FWDL is split into PTDLs (each is 1024 XAP words max) */
1014  /* Each PTDL contains running ROM f/w version, and checksum */
1015  /* MAC/PHY reset addresses (known) are added into a final PTDL */
1016 
1017  /* The input image has already been parsed, and loaded into fwinfo, so we
1018  * can use that to build the output image
1019  */
1020  patch_buf_size = calc_patch_size(fwinfo);
1021 
1022  patch_buf = kmalloc(patch_buf_size, GFP_KERNEL);
1023  if (!patch_buf)
1024  {
1025  kfree(rdbuf);
1026  unifi_error(NULL, "Can't malloc buffer for patch conversion\n");
1027  return NULL;
1028  }
1029 
1030  memset(patch_buf, 0xdd, patch_buf_size);
1031 
1032  /* Write XBV + VERF headers */
1033  patch_offs += write_xbv_header(patch_buf, patch_offs, 0);
1034  payload_offs = patch_offs;
1035 
1036  /* Write patch (LIST) header */
1037  list_len_offs = patch_offs + 4; /* Save LIST.length offset for later update */
1038  patch_offs += write_ptch_header(patch_buf, patch_offs, fw_id);
1039 
1040  /* Save start offset of the PTDL chunks */
1041  ptdl_start_offs = patch_offs;
1042 
1043  /* Write LIST of firmware PTDL blocks */
1044  for (i = 0; i < fwinfo->num_fwdl; i++)
1045  {
1046  patch_offs += write_fwdl_to_ptdl(patch_buf,
1047  patch_offs,
1048  readfn,
1049  &fwinfo->fwdl[i],
1050  fw_buf,
1051  fw_id,
1052  rdbuf);
1053  }
1054 
1055  /* Write restart-vector PTDL last */
1056  patch_offs += write_reset_ptdl(patch_buf, patch_offs, fwinfo, fw_id);
1057 
1058  /* Now the length is known, update the LIST.length */
1059  (void)write_uint32(patch_buf, list_len_offs,
1060  (patch_offs - ptdl_start_offs) + PTCH_LIST_SIZE);
1061 
1062  /* Re write XBV headers just to fill in the correct file size */
1063  (void)write_xbv_header(patch_buf, 0, (patch_offs - payload_offs));
1064 
1065  unifi_trace(card->ospriv, UDBG1, "XBV:PTCH size %u, fw_id %u\n",
1066  patch_offs, fw_id);
1067  if (size)
1068  {
1069  *size = patch_offs;
1070  }
1071  kfree(rdbuf);
1072 
1073  return patch_buf;
1074 }
1075 
1076