source: svn/newcon3bcm2_21bu/nexus/build/nfe_driver/nexus_driver_firmware.c

Last change on this file was 76, checked in by megakiss, 10 years ago

1W 대기전력을 만족시키기 위하여 POWEROFF시 튜너를 Standby 상태로 함

  • Property svn:executable set to *
File size: 21.8 KB
Line 
1/*
2 * Copyright (C) 2010-2011 Broadcom Corporation
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 */
17#include <linux/version.h>
18#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
19#include <linux/autoconf.h>
20#else
21#include <generated/autoconf.h>
22#endif
23#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/firmware.h>
26#include <linux/platform_device.h>
27#include <linux/elf.h>
28#include <linux/slab.h>
29#include <linux/vmalloc.h>
30#include <linux/moduleloader.h>
31#include <linux/err.h>
32#include <asm/r4kcache.h>
33#include <linux/string.h>
34#include "nexus_driver_firmware.h"
35#include "b_bare_os.h"
36
37/* Jumptable pointers - forward jumptable pointer is in firmware (fwd_jumptable.c) so we load it with the value in stubs_fwd.S */
38/* reverse jumptable pointer is below, we load it to the firmware (stubs_rev.S) */
39typedef int (*B_Forward_Jumptable_Entry)(void);
40B_Forward_Jumptable_Entry *pForward_Jumptable = NULL;
41static b_bare_os_interface *pReverse_Jumptable = NULL;
42
43/* Modified from arch/mips/kernel/vpe.c */
44#ifndef ARCH_SHF_SMALL
45#define ARCH_SHF_SMALL 0
46#endif
47
48#define NFE_PATH_MAX 256
49
50struct mips_hi16 {
51        struct mips_hi16 *next;
52        Elf32_Addr *addr;
53        Elf32_Addr value;
54};
55
56struct nfe {
57        /* elfloader stuff */
58        void *load_addr;
59        unsigned long len;
60        char *pbuffer;
61        unsigned long plen;
62        unsigned int uid, gid;
63        char cwd[NFE_PATH_MAX];
64
65        unsigned long fwd_jumptable;
66        unsigned long rev_jumptable_pointer;
67};
68
69static struct mips_hi16 *mips_hi16_list;
70static unsigned int gp_offs, gp_addr;
71
72static inline void save_gp_address(unsigned int secbase, unsigned int rel)
73{
74        gp_addr = secbase + rel;
75        gp_offs = gp_addr - (secbase & 0xffff0000);
76}
77
78/* Update size with this section: return offset. */
79static long get_offset(unsigned int *size, Elf_Shdr * sechdr)
80{
81        long ret;
82
83        ret = ALIGN(*size, sechdr->sh_addralign ? : 1);
84        *size = ret + sechdr->sh_size;
85        return ret;
86}
87
88/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
89   might -- code, read-only data, read-write data, small data.  Tally
90   sizes, and place the offsets into sh_entsize fields: high bit means it
91   belongs in init. */
92static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
93                            Elf_Shdr * sechdrs, const char *secstrings)
94{
95        static unsigned long const masks[][2] = {
96                /* NOTE: all executable code must be the first section
97                 * in this array; otherwise modify the text_size
98                 * finder in the two loops below */
99                {SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL},
100                {SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL},
101                {SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL},
102                {ARCH_SHF_SMALL | SHF_ALLOC, 0}
103        };
104        unsigned int m, i;
105
106        for (i = 0; i < hdr->e_shnum; i++)
107                sechdrs[i].sh_entsize = ~0UL;
108
109        for (m = 0; m < ARRAY_SIZE(masks); ++m) {
110                for (i = 0; i < hdr->e_shnum; ++i) {
111                        Elf_Shdr *s = &sechdrs[i];
112
113                        //  || strncmp(secstrings + s->sh_name, ".init", 5) == 0)
114                        if ((s->sh_flags & masks[m][0]) != masks[m][0]
115                            || (s->sh_flags & masks[m][1])
116                            || s->sh_entsize != ~0UL)
117                                continue;
118                        s->sh_entsize =
119                                get_offset(&mod->core_size, s);
120                }
121
122                if (m == 0)
123                        mod->core_text_size = mod->core_size;
124
125        }
126}
127
128static int apply_r_mips_none(struct module *me, uint32_t *location,
129                             Elf32_Addr v)
130{
131        return 0;
132}
133
134static int apply_r_mips_gprel16(struct module *me, uint32_t *location,
135                                Elf32_Addr v)
136{
137        int rel;
138
139        if( !(*location & 0xffff) ) {
140                rel = (int)v - gp_addr;
141        }
142        else {
143                /* .sbss + gp(relative) + offset */
144                /* kludge! */
145                rel =  (int)(short)((int)v + gp_offs +
146                                    (int)(short)(*location & 0xffff) - gp_addr);
147        }
148
149        if( (rel > 32768) || (rel < -32768) ) {
150                printk(KERN_DEBUG "NEXUS firmware loader: apply_r_mips_gprel16: "
151                       "relative address 0x%x out of range of gp register\n",
152                       rel);
153                return -ENOEXEC;
154        }
155
156        *location = (*location & 0xffff0000) | (rel & 0xffff);
157
158        return 0;
159}
160
161static int apply_r_mips_pc16(struct module *me, uint32_t *location,
162                             Elf32_Addr v)
163{
164        int rel;
165        rel = (((unsigned int)v - (unsigned int)location));
166        rel >>= 2;              // because the offset is in _instructions_ not bytes.
167        rel -= 1;               // and one instruction less due to the branch delay slot.
168
169        if( (rel > 32768) || (rel < -32768) ) {
170                printk(KERN_DEBUG "NEXUS firmware loader: "
171                       "apply_r_mips_pc16: relative address out of range 0x%x\n", rel);
172                return -ENOEXEC;
173        }
174
175        *location = (*location & 0xffff0000) | (rel & 0xffff);
176
177        return 0;
178}
179
180static int apply_r_mips_32(struct module *me, uint32_t *location,
181                           Elf32_Addr v)
182{
183        *location += v;
184
185        return 0;
186}
187
188static int apply_r_mips_26(struct module *me, uint32_t *location,
189                           Elf32_Addr v)
190{
191        if (v % 4) {
192                printk(KERN_DEBUG "NEXUS firmware loader: apply_r_mips_26 "
193                       " unaligned relocation\n");
194                return -ENOEXEC;
195        }
196/*
197 * Not desperately convinced this is a good check of an overflow condition
198 * anyway. But it gets in the way of handling undefined weak symbols which
199 * we want to set to zero.
200 * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
201 * printk(KERN_ERR
202 * "module %s: relocation overflow\n",
203 * me->name);
204 * return -ENOEXEC;
205 * }
206 */
207
208        *location = (*location & ~0x03ffffff) |
209                ((*location + (v >> 2)) & 0x03ffffff);
210        return 0;
211}
212
213
214static int apply_r_mips_32_rela(struct module *me, u32 *location, Elf_Addr v)
215{
216        *location = v;
217
218        return 0;
219}
220
221static int apply_r_mips_26_rela(struct module *me, u32 *location, Elf_Addr v)
222{
223        if (v % 4) {
224                pr_err("module %s: dangerous R_MIPS_26 RELArelocation\n",
225                       me->name);
226                return -ENOEXEC;
227        }
228
229        if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
230                printk(KERN_ERR
231                       "module %s: relocation overflow\n",
232                       me->name);
233                return -ENOEXEC;
234        }
235
236        *location = (*location & ~0x03ffffff) | ((v >> 2) & 0x03ffffff);
237
238        return 0;
239}
240
241static int apply_r_mips_hi16_rela(struct module *me, u32 *location, Elf_Addr v)
242{
243        *location = (*location & 0xffff0000) |
244                    ((((long long) v + 0x8000LL) >> 16) & 0xffff);
245
246        return 0;
247}
248
249static int apply_r_mips_lo16_rela(struct module *me, u32 *location, Elf_Addr v)
250{
251        *location = (*location & 0xffff0000) | (v & 0xffff);
252
253        return 0;
254}
255
256static int apply_r_mips_64_rela(struct module *me, u32 *location, Elf_Addr v)
257{
258        *(Elf_Addr *)location = v;
259
260        return 0;
261}
262
263static int apply_r_mips_higher_rela(struct module *me, u32 *location,
264                                    Elf_Addr v)
265{
266        *location = (*location & 0xffff0000) |
267                    ((((long long) v + 0x80008000LL) >> 32) & 0xffff);
268
269        return 0;
270}
271
272static int apply_r_mips_highest_rela(struct module *me, u32 *location,
273                                     Elf_Addr v)
274{
275        *location = (*location & 0xffff0000) |
276                    ((((long long) v + 0x800080008000LL) >> 48) & 0xffff);
277
278        return 0;
279}
280
281
282static int apply_r_mips_hi16(struct module *me, uint32_t *location,
283                             Elf32_Addr v)
284{
285        struct mips_hi16 *n;
286
287        /*
288         * We cannot relocate this one now because we don't know the value of
289         * the carry we need to add.  Save the information, and let LO16 do the
290         * actual relocation.
291         */
292        n = kmalloc(sizeof *n, GFP_KERNEL);
293        if (!n)
294                return -ENOMEM;
295
296        n->addr = location;
297        n->value = v;
298        n->next = mips_hi16_list;
299        mips_hi16_list = n;
300
301        return 0;
302}
303
304static int apply_r_mips_lo16(struct module *me, uint32_t *location,
305                             Elf32_Addr v)
306{
307        unsigned long insnlo = *location;
308        Elf32_Addr val, vallo;
309        struct mips_hi16 *l, *next;
310
311        /* Sign extend the addend we extract from the lo insn.  */
312        vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
313
314        if (mips_hi16_list != NULL) {
315
316                l = mips_hi16_list;
317                while (l != NULL) {
318                        unsigned long insn;
319
320                        /*
321                         * The value for the HI16 had best be the same.
322                         */
323                        if (v != l->value) {
324                                printk(KERN_DEBUG "NEXUS firmware loader: "
325                                       "apply_r_mips_lo16/hi16: \t"
326                                       "inconsistent value information\n");
327                                goto out_free;
328                        }
329
330                        /*
331                         * Do the HI16 relocation.  Note that we actually don't
332                         * need to know anything about the LO16 itself, except
333                         * where to find the low 16 bits of the addend needed
334                         * by the LO16.
335                         */
336                        insn = *l->addr;
337                        val = ((insn & 0xffff) << 16) + vallo;
338                        val += v;
339
340                        /*
341                         * Account for the sign extension that will happen in
342                         * the low bits.
343                         */
344                        val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff;
345
346                        insn = (insn & ~0xffff) | val;
347                        *l->addr = insn;
348
349                        next = l->next;
350                        kfree(l);
351                        l = next;
352                }
353
354                mips_hi16_list = NULL;
355        }
356
357        /*
358         * Ok, we're done with the HI16 relocs.  Now deal with the LO16.
359         */
360        val = v + vallo;
361        insnlo = (insnlo & ~0xffff) | (val & 0xffff);
362        *location = insnlo;
363
364        return 0;
365
366out_free:
367        while (l != NULL) {
368                next = l->next;
369                kfree(l);
370                l = next;
371        }
372        mips_hi16_list = NULL;
373
374        return -ENOEXEC;
375}
376
377
378static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
379                                Elf32_Addr v) = {
380        [R_MIPS_NONE]   = apply_r_mips_none,
381        [R_MIPS_32]     = apply_r_mips_32,
382        [R_MIPS_26]     = apply_r_mips_26,
383        [R_MIPS_HI16]   = apply_r_mips_hi16,
384        [R_MIPS_LO16]   = apply_r_mips_lo16,
385        [R_MIPS_GPREL16] = apply_r_mips_gprel16,
386        [R_MIPS_PC16] = apply_r_mips_pc16
387};
388
389static char *rstrs[] = {
390        [R_MIPS_NONE]   = "MIPS_NONE",
391        [R_MIPS_32]     = "MIPS_32",
392        [R_MIPS_26]     = "MIPS_26",
393        [R_MIPS_HI16]   = "MIPS_HI16",
394        [R_MIPS_LO16]   = "MIPS_LO16",
395        [R_MIPS_GPREL16] = "MIPS_GPREL16",
396        [R_MIPS_PC16] = "MIPS_PC16"
397};
398
399static int apply_relocations(Elf32_Shdr *sechdrs,
400                      const char *strtab,
401                      unsigned int symindex,
402                      unsigned int relsec,
403                      struct module *me)
404{
405        Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr;
406        Elf32_Sym *sym;
407        uint32_t *location;
408        unsigned int i;
409        Elf32_Addr v;
410        int res;
411
412        for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
413                Elf32_Word r_info = rel[i].r_info;
414
415                /* This is where to make the change */
416                location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
417                        + rel[i].r_offset;
418                /* This is the symbol it is referring to */
419                sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
420                        + ELF32_R_SYM(r_info);
421
422                if (!sym->st_value) {
423                        printk(KERN_DEBUG "%s: undefined weak symbol %s\n",
424                               me->name, strtab + sym->st_name);
425                        /* just print the warning, dont barf */
426                }
427
428                v = sym->st_value;
429
430                res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v);
431                if( res ) {
432                        char *r = rstrs[ELF32_R_TYPE(r_info)];
433                        printk(KERN_WARNING "NEXUS firmware loader: .text+0x%x "
434                               "relocation type %s for symbol \"%s\" failed\n",
435                               rel[i].r_offset, r ? r : "UNKNOWN",
436                               strtab + sym->st_name);
437                        return res;
438                }
439        }
440
441        return 0;
442}
443
444static int (*reloc_handlers_rela[]) (struct module *me, u32 *location,
445                                Elf_Addr v) = {
446        [R_MIPS_NONE]           = apply_r_mips_none,
447        [R_MIPS_32]             = apply_r_mips_32_rela,
448        [R_MIPS_26]             = apply_r_mips_26_rela,
449        [R_MIPS_HI16]           = apply_r_mips_hi16_rela,
450        [R_MIPS_LO16]           = apply_r_mips_lo16_rela,
451        [R_MIPS_64]             = apply_r_mips_64_rela,
452        [R_MIPS_HIGHER]         = apply_r_mips_higher_rela,
453        [R_MIPS_HIGHEST]        = apply_r_mips_highest_rela
454};
455
456int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
457                       unsigned int symindex, unsigned int relsec,
458                       struct module *me)
459{
460        Elf_Mips_Rela *rel = (void *) sechdrs[relsec].sh_addr;
461        Elf_Sym *sym;
462        u32 *location;
463        unsigned int i;
464        Elf_Addr v;
465        int res;
466
467        printk(KERN_WARNING "Applying relocate section %u to %u\n", relsec,
468               sechdrs[relsec].sh_info);
469
470        for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
471                /* This is where to make the change */
472                location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
473                        + rel[i].r_offset;
474                /* This is the symbol it is referring to */
475                sym = (Elf_Sym *)sechdrs[symindex].sh_addr
476                        + ELF_MIPS_R_SYM(rel[i]);
477                if (IS_ERR_VALUE(sym->st_value)) {
478                        /* Ignore unresolved weak symbol */
479                        if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
480                                continue;
481                        printk(KERN_WARNING "%s: Unknown symbol %s\n",
482                               me->name, strtab + sym->st_name);
483                        return -ENOENT;
484                }
485
486                v = sym->st_value + rel[i].r_addend;
487
488                res = reloc_handlers_rela[ELF_MIPS_R_TYPE(rel[i])](me, location, v);
489                if (res)
490                        return res;
491        }
492
493        return 0;
494}
495
496/* Change all symbols so that sh_value encodes the pointer directly. */
497static void simplify_symbols(Elf_Shdr * sechdrs,
498                            unsigned int symindex,
499                            const char *strtab,
500                            const char *secstrings,
501                            unsigned int nsecs, struct module *mod)
502{
503        Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
504        unsigned long secbase, bssbase = 0;
505        unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
506        int size;
507
508        /* find the .bss section for COMMON symbols */
509        for (i = 0; i < nsecs; i++) {
510                if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) {
511                        bssbase = sechdrs[i].sh_addr;
512                        break;
513                }
514        }
515
516        for (i = 1; i < n; i++) {
517                switch (sym[i].st_shndx) {
518                case SHN_COMMON:
519                        /* Allocate space for the symbol in the .bss section.
520                           st_value is currently size.
521                           We want it to have the address of the symbol. */
522
523                        size = sym[i].st_value;
524                        sym[i].st_value = bssbase;
525
526                        bssbase += size;
527                        break;
528
529                case SHN_ABS:
530                        /* Don't need to do anything */
531                        break;
532
533                case SHN_UNDEF:
534                        /* ret = -ENOENT; */
535                        break;
536
537                case SHN_MIPS_SCOMMON:
538                        printk(KERN_DEBUG "simplify_symbols: ignoring SHN_MIPS_SCOMMON "
539                               "symbol <%s> st_shndx %d\n", strtab + sym[i].st_name,
540                               sym[i].st_shndx);
541                        // .sbss section
542                        break;
543
544                default:
545                        secbase = sechdrs[sym[i].st_shndx].sh_addr;
546
547                        if (strncmp(strtab + sym[i].st_name, "_gp", 3) == 0) {
548                                save_gp_address(secbase, sym[i].st_value);
549                        }
550
551                        sym[i].st_value += secbase;
552                        break;
553                }
554        }
555}
556
557/* Find some program space  */
558static void *alloc_progmem(unsigned long len)
559{
560        void *addr;
561
562        /* simple grab some mem for now */
563        addr = vmalloc(len);
564        memset(addr,0,len);
565
566        return addr;
567}
568
569static void release_progmem(void *ptr)
570{
571        vfree(ptr);
572}
573
574static int find_jumptable_symbol(struct nfe * v, Elf_Shdr * sechdrs,
575                                      unsigned int symindex, const char *strtab,
576                                      struct module *mod)
577{
578        Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
579        unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
580
581        v->fwd_jumptable = 0;
582        v->rev_jumptable_pointer = 0;
583
584        for (i = 1; i < n; i++) {
585                if (strcmp(strtab + sym[i].st_name, "B_Forward_Jumptable") == 0) {
586                        v->fwd_jumptable = sym[i].st_value;
587                }
588                if (strcmp(strtab + sym[i].st_name, "pReverse_Jumptable") == 0) {
589                        v->rev_jumptable_pointer = sym[i].st_value;
590                }
591        }
592
593        if (v->fwd_jumptable == 0 || v->rev_jumptable_pointer == 0)
594                return -1;
595
596        return 0;
597}
598
599static int nexus_driver_firmware_relocate(struct nfe * v)
600{
601        Elf_Ehdr *hdr;
602        Elf_Shdr *sechdrs;
603        long err = 0;
604        char *secstrings, *strtab = NULL;
605        unsigned int len, i, symindex = 0, strindex = 0, relocate = 0;
606        unsigned int start, end;
607        struct module mod;      // so we can re-use the relocations code
608
609        memset(&mod, 0, sizeof(struct module));
610        strcpy(mod.name, "NEXUS firmware");
611
612        hdr = (Elf_Ehdr *) v->pbuffer;
613        len = v->plen;
614
615        /* Sanity checks against insmoding binaries or wrong arch,
616           weird elf version */
617        if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
618                || (hdr->e_type != ET_REL && hdr->e_type != ET_EXEC)
619                || !elf_check_arch(hdr)
620                || hdr->e_shentsize != sizeof(*sechdrs)) {
621                printk(KERN_WARNING
622                           "NEXUS firmware loader: program wrong arch or weird elf version\n");
623
624                return -ENOEXEC;
625        }
626
627        if (hdr->e_type == ET_REL)
628                relocate = 1;
629
630        if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
631                printk(KERN_ERR "NEXUS firmware loader: program length %u truncated\n",
632                           len);
633
634                return -ENOEXEC;
635        }
636
637        /* Convenience variables */
638        sechdrs = (void *)hdr + hdr->e_shoff;
639        secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
640        sechdrs[0].sh_addr = 0;
641
642        /* And these should exist, but gcc whinges if we don't init them */
643        symindex = strindex = 0;
644
645        if (relocate) {
646                for (i = 1; i < hdr->e_shnum; i++) {
647                        if (sechdrs[i].sh_type != SHT_NOBITS
648                                && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) {
649                                printk(KERN_ERR "NEXUS firmware length %u truncated\n",
650                                           len);
651                                return -ENOEXEC;
652                        }
653
654                        /* Mark all sections sh_addr with their address in the
655                           temporary image. */
656                        sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
657
658                        /* Internal symbols and strings. */
659                        if (sechdrs[i].sh_type == SHT_SYMTAB) {
660                                symindex = i;
661                                strindex = sechdrs[i].sh_link;
662                                strtab = (char *)hdr + sechdrs[strindex].sh_offset;
663                        }
664                }
665                layout_sections(&mod, hdr, sechdrs, secstrings);
666        }
667
668        v->load_addr = alloc_progmem(mod.core_size);
669        if (!v->load_addr)
670                return -ENOMEM;
671
672        v->len = mod.core_size;
673
674        printk(KERN_WARNING "NEXUS firmware loader: loading to %p relocate=%d\n", v->load_addr,relocate);
675
676        if (relocate) {
677                for (i = 0; i < hdr->e_shnum; i++) {
678                        void *dest;
679
680                        if (!(sechdrs[i].sh_flags & SHF_ALLOC))
681                                continue;
682
683                        dest = v->load_addr + sechdrs[i].sh_entsize;
684
685                        if (sechdrs[i].sh_type != SHT_NOBITS)
686                                memcpy(dest, (void *)sechdrs[i].sh_addr,
687                                           sechdrs[i].sh_size);
688                        /* Update sh_addr to point to copy in image. */
689                        sechdrs[i].sh_addr = (unsigned long)dest;
690
691                        printk(KERN_DEBUG " section sh_name %s sh_addr 0x%x\n",
692                                   secstrings + sechdrs[i].sh_name, sechdrs[i].sh_addr);
693                }
694
695                /* Fix up syms, so that st_value is a pointer to location. */
696                simplify_symbols(sechdrs, symindex, strtab, secstrings,
697                                 hdr->e_shnum, &mod);
698
699                /* Now do relocations. */
700                for (i = 1; i < hdr->e_shnum; i++) {
701                        const char *strtab = (char *)sechdrs[strindex].sh_addr;
702                        unsigned int info = sechdrs[i].sh_info;
703
704                        /* Not a valid relocation section? */
705                        if (info >= hdr->e_shnum)
706                                continue;
707
708                        /* Don't bother with non-allocated sections */
709                        if (!(sechdrs[info].sh_flags & SHF_ALLOC))
710                                continue;
711
712                        if (sechdrs[i].sh_type == SHT_REL)
713                                err = apply_relocations(sechdrs, strtab, symindex, i,
714                                                        &mod);
715                        else if (sechdrs[i].sh_type == SHT_RELA)
716                                err = apply_relocate_add(sechdrs, strtab, symindex, i,
717                                                         &mod);
718                        if (err < 0)
719                                return err;
720
721                }
722        } else {
723                struct elf_phdr *phdr = (struct elf_phdr *) ((char *)hdr + hdr->e_phoff);
724
725                for (i = 0; i < hdr->e_phnum; i++) {
726                        if (phdr->p_type == PT_LOAD) {
727                                memcpy((void *)phdr->p_paddr,
728                                           (char *)hdr + phdr->p_offset,
729                                           phdr->p_filesz);
730                                memset((void *)phdr->p_paddr + phdr->p_filesz,
731                                           0, phdr->p_memsz - phdr->p_filesz);
732                        }
733                        phdr++;
734                }
735
736                for (i = 0; i < hdr->e_shnum; i++) {
737                        /* Internal symbols and strings. */
738                        if (sechdrs[i].sh_type == SHT_SYMTAB) {
739                                symindex = i;
740                                strindex = sechdrs[i].sh_link;
741                                strtab = (char *)hdr + sechdrs[strindex].sh_offset;
742
743                                /* mark the symtab's address for when we try to find the
744                                   magic symbols */
745                                sechdrs[i].sh_addr = (size_t) hdr + sechdrs[i].sh_offset;
746                        }
747                }
748        }
749
750        /* make sure it's physically written out */
751        start = (unsigned long)v->load_addr;
752        start &= ~(cpu_icache_line_size() - 1);
753        end = (unsigned long)v->load_addr + v->len;
754        end &= ~(cpu_icache_line_size() - 1);
755
756        for (; start <= end; start += cpu_icache_line_size())
757                flush_icache_line(start);
758
759        if ((find_jumptable_symbol(v, sechdrs, symindex, strtab, &mod)) < 0) {
760                        if (!v->fwd_jumptable)printk(KERN_WARNING "NEXUS firmware loader: firmware does not contain "
761                                   "B_Forward_Jumptable symbol\n");
762                        if (!v->rev_jumptable_pointer)printk(KERN_WARNING "NEXUS firmware loader: firmware does not contain "
763                                          "pReverse_Jumptable symbol\n");
764                        return -ENOEXEC;
765        }
766        pReverse_Jumptable = pb_bare_os;
767        pForward_Jumptable = (B_Forward_Jumptable_Entry *) v->fwd_jumptable;
768        printk(KERN_WARNING "B_Forward_Jumptable=%x B_Reverse_Jumptable=%x\n",(unsigned int)pForward_Jumptable, (unsigned int)pReverse_Jumptable);
769
770        /* This sets the pointer in the firmware to pointer to the driver's b_bare_os structure */
771        *(unsigned long *)(v->rev_jumptable_pointer) = (unsigned long) pReverse_Jumptable;
772        return 0;
773
774}
775
776
777struct nfe v;
778
779int nexus_driver_firmware_init()
780{
781        int err;
782        struct platform_device *pdev;
783        const struct firmware *fw;
784        const char fw_name[] = "nexus_firmware.elf";
785        unsigned long *cast_ptr;
786        int i;
787
788
789        /* Register a simple device so we can load some firmware */
790        pdev = platform_device_register_simple("BCMnexus", 0, NULL, 0);
791        if (!pdev) {
792                printk(KERN_ERR "Failed to register device for \"%s\"\n",
793                           fw_name);
794                return -EINVAL;
795        }
796
797        /* Request the firmware */
798        err = request_firmware(&fw, fw_name, &pdev->dev);
799        platform_device_unregister(pdev);
800        if (err) {
801                printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
802                           fw_name, err);
803                return err;
804        }
805
806
807        v.pbuffer = (char *)fw->data;
808        v.plen = fw->size;
809
810        err = nexus_driver_firmware_relocate(&v);
811        if (err) {
812                printk(KERN_ERR "Failed to relocate firmware. err=%d\n", err);
813                release_firmware(fw);
814                nexus_driver_firmware_uninit();
815                return err;
816        }
817
818#if 0
819        cast_ptr = (unsigned long *) pForward_Jumptable;
820        printk("Entry[0] in fwd table %x\n",*cast_ptr++);
821        printk("Entry[1] in fwd table %x\n",*cast_ptr++);
822        printk("Entry[2] in fwd table %x\n",*cast_ptr++);
823        printk("Entry[3] in fwd table %x\n",*cast_ptr);
824        cast_ptr = (unsigned long *) pReverse_Jumptable;
825        printk("Entry[0] in rev table %x\n",*cast_ptr++);
826        printk("Entry[1] in rev table %x\n",*cast_ptr++);
827        printk("Entry[2] in rev table %x\n",*cast_ptr);
828#endif
829        /* Check tables for NULL entries */
830        for(i=0,cast_ptr = (unsigned long *) pForward_Jumptable;*cast_ptr!=(-1);i++,cast_ptr++)
831                        if(!*cast_ptr) {
832                                printk(KERN_ERR "Null entry at %d in forward table\n",i);
833                                release_firmware(fw);
834                                nexus_driver_firmware_uninit();
835                                return -EINVAL;
836                        }
837        pReverse_Jumptable->end_of_functions = (void *) -1;
838        for(i=0,cast_ptr = (unsigned long *) pReverse_Jumptable;*cast_ptr!=(-1);i++,cast_ptr++)
839                        if(!*cast_ptr) {
840                                printk(KERN_ERR "Null entry at %d in reverse table\n",i);
841                                release_firmware(fw);
842                                nexus_driver_firmware_uninit();
843                                return -EINVAL;
844                        }
845
846        release_firmware(fw);
847        return 0;
848}
849
850int nexus_driver_firmware_uninit()
851{
852        if (v.load_addr)
853                release_progmem(v.load_addr);
854        pForward_Jumptable = NULL;
855        return 0;
856}
857
858
Note: See TracBrowser for help on using the repository browser.