Root/drivers/staging/csr/csr_wifi_hip_xbv.c

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 */
30#include "csr_wifi_hip_unifiversion.h"
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
46typedef struct
47{
48    void *dlpriv;
49    s32 ioffset;
50    fwreadfn_t iread;
51} ct_t;
52
53/* Struct to represent a TLV field */
54typedef struct
55{
56    char t_name[4];
57    u32 t_len;
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. */
69typedef enum
70{
71    xbv_xbv1,
72    /*xbv_info,*/
73    xbv_fw,
74    xbv_vers,
75    xbv_vand,
76    xbv_ptch,
77    xbv_other
78} xbv_container;
79
80#define XBV_STACK_SIZE 6
81#define XBV_MAX_OFFS 0x7fffffff
82
83typedef struct
84{
85    struct
86    {
87        xbv_container container;
88        s32 ioffset_end;
89    } s[XBV_STACK_SIZE];
90    u32 ptr;
91} xbv_stack_t;
92
93static s32 read_tag(card_t *card, ct_t *ct, tag_t *tag);
94static s32 read_bytes(card_t *card, ct_t *ct, void *buf, u32 len);
95static s32 read_uint(card_t *card, ct_t *ct, u32 *u, u32 len);
96static s32 xbv_check(xbv1_t *fwinfo, const xbv_stack_t *stack,
97                          xbv_mode new_mode, xbv_container old_cont);
98static 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
102static u32 write_uint16(void *buf, const u32 offset,
103                              const u16 val);
104static u32 write_uint32(void *buf, const u32 offset,
105                              const u32 val);
106static u32 write_bytes(void *buf, const u32 offset,
107                             const u8 *data, const u32 len);
108static u32 write_tag(void *buf, const u32 offset,
109                           const char *tag_str);
110static u32 write_chunk(void *buf, const u32 offset,
111                             const char *tag_str,
112                             const u32 payload_len);
113static u16 calc_checksum(void *buf, const u32 offset,
114                               const u32 bytes_len);
115static u32 calc_patch_size(const xbv1_t *fwinfo);
116
117static u32 write_xbv_header(void *buf, const u32 offset,
118                                  const u32 file_payload_length);
119static u32 write_ptch_header(void *buf, const u32 offset,
120                                   const u32 fw_id);
121static u32 write_patchcmd(void *buf, const u32 offset,
122                                const u32 dst_genaddr, const u16 len);
123static u32 write_reset_ptdl(void *buf, const u32 offset,
124                                  const xbv1_t *fwinfo, u32 fw_id);
125static 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 */
160CsrResult xbv1_parse(card_t *card, fwreadfn_t readfn, void *dlpriv, xbv1_t *fwinfo)
161{
162    ct_t ct;
163    tag_t tag;
164    xbv_stack_t stack;
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");
177        return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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);
185        return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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");
201            return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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            {
220                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
221            }
222            if (version != 0)
223            {
224                unifi_error(NULL, "Unsupported firmware file version: %d.%d\n",
225                            version >> 8, version & 0xFF);
226                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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            {
238                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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                {
246                    return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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                {
254                    return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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                {
264                    return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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                {
278                    return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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            {
298                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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            {
312                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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            {
329                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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            {
351                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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            {
377                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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            {
389                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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            {
415                unifi_error(NULL,
416                            "XBV file has overrun stack'd segment %d (%d > %d)\n",
417                            stack.ptr, ct.ioffset, stack.s[stack.ptr].ioffset_end);
418                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
419            }
420            if (stack.ptr <= 0)
421            {
422                unifi_error(NULL, "XBV file has underrun stack pointer\n");
423                return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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");
432        return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
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. */
441static 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 */
469static 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
493static 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
508static 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
537static 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
551static 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
572static 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
581static 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
589static 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
602static 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
610static 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
620static 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
644static 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
677static 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
694static 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
715static 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
743static 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
814static 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 */
874s32 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
972void* 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
1077

Archive Download this file



interactive