ec59777dc060e9921d35adcc4daa86df24ef8988
[linux-2.6.git] / drivers / staging / tidspbridge / dynload / reloc.c
1 /*
2  * reloc.c
3  *
4  * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5  *
6  * Copyright (C) 2005-2006 Texas Instruments, Inc.
7  *
8  * This package is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
13  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
14  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
15  */
16
17 #include "header.h"
18
19 #if TMS32060
20 /* the magic symbol for the start of BSS */
21 static const char bsssymbol[] = { ".bss" };
22 #endif
23
24 #if TMS32060
25 #include "reloc_table_c6000.c"
26 #endif
27
28 #if TMS32060
29 /* From coff.h - ignore these relocation operations */
30 #define R_C60ALIGN     0x76     /* C60: Alignment info for compressor */
31 #define R_C60FPHEAD    0x77     /* C60: Explicit assembly directive */
32 #define R_C60NOCMP    0x100     /* C60: Don't compress this code scn */
33 #endif
34
35 /**************************************************************************
36  * Procedure dload_unpack
37  *
38  * Parameters:
39  *      data    pointer to storage unit containing lowest host address of
40  *              image data
41  *      fieldsz Size of bit field, 0 < fieldsz <= sizeof(rvalue)*BITS_PER_AU
42  *      offset  Offset from LSB, 0 <= offset < BITS_PER_AU
43  *      sgn     Signedness of the field (ROP_SGN, ROP_UNS, ROP_MAX, ROP_ANY)
44  *
45  * Effect:
46  *      Extracts the specified field and returns it.
47  ************************************************************************* */
48 rvalue dload_unpack(struct dload_state *dlthis, tgt_au_t * data, int fieldsz,
49                     int offset, unsigned sgn)
50 {
51         register rvalue objval;
52         register int shift, direction;
53         register tgt_au_t *dp = data;
54
55         fieldsz -= 1;   /* avoid nastiness with 32-bit shift of 32-bit value */
56         /* * collect up enough bits to contain the desired field */
57         if (TARGET_BIG_ENDIAN) {
58                 dp += (fieldsz + offset) >> LOG_TGTAU_BITS;
59                 direction = -1;
60         } else
61                 direction = 1;
62         objval = *dp >> offset;
63         shift = TGTAU_BITS - offset;
64         while (shift <= fieldsz) {
65                 dp += direction;
66                 objval += (rvalue) *dp << shift;
67                 shift += TGTAU_BITS;
68         }
69
70         /* * sign or zero extend the value appropriately */
71         if (sgn == ROP_UNS)
72                 objval &= (2 << fieldsz) - 1;
73         else {
74                 shift = sizeof(rvalue) * BITS_PER_AU - 1 - fieldsz;
75                 objval = (objval << shift) >> shift;
76         }
77
78         return objval;
79
80 }                               /* dload_unpack */
81
82 /**************************************************************************
83  * Procedure dload_repack
84  *
85  * Parameters:
86  *      val             Value to insert
87  *      data    Pointer to storage unit containing lowest host address of
88  *              image data
89  *      fieldsz Size of bit field, 0 < fieldsz <= sizeof(rvalue)*BITS_PER_AU
90  *      offset  Offset from LSB, 0 <= offset < BITS_PER_AU
91  *      sgn     Signedness of the field (ROP_SGN, ROP_UNS, ROP_MAX, ROP_ANY)
92  *
93  * Effect:
94  *      Stuffs the specified value in the specified field.  Returns 0 for
95  *      success
96  * or 1 if the value will not fit in the specified field according to the
97  * specified signedness rule.
98  ************************************************************************* */
99 static const unsigned char ovf_limit[] = { 1, 2, 2 };
100
101 int dload_repack(struct dload_state *dlthis, rvalue val, tgt_au_t * data,
102                  int fieldsz, int offset, unsigned sgn)
103 {
104         register urvalue objval, mask;
105         register int shift, direction;
106         register tgt_au_t *dp = data;
107
108         fieldsz -= 1;   /* avoid nastiness with 32-bit shift of 32-bit value */
109         /* clip the bits */
110         mask = ((UINT32_C(2) << fieldsz) - 1);
111         objval = (val & mask);
112         /* * store the bits through the specified mask */
113         if (TARGET_BIG_ENDIAN) {
114                 dp += (fieldsz + offset) >> LOG_TGTAU_BITS;
115                 direction = -1;
116         } else
117                 direction = 1;
118
119         /* insert LSBs */
120         *dp = (*dp & ~(mask << offset)) + (objval << offset);
121         shift = TGTAU_BITS - offset;
122         /* align mask and objval with AU boundary */
123         objval >>= shift;
124         mask >>= shift;
125
126         while (mask) {
127                 dp += direction;
128                 *dp = (*dp & ~mask) + objval;
129                 objval >>= TGTAU_BITS;
130                 mask >>= TGTAU_BITS;
131         }
132
133         /*
134          * check for overflow
135          */
136         if (sgn) {
137                 unsigned tmp = (val >> fieldsz) + (sgn & 0x1);
138                 if (tmp > ovf_limit[sgn - 1])
139                         return 1;
140         }
141         return 0;
142
143 }                               /* dload_repack */
144
145 /* lookup table for the scaling amount in a C6x instruction */
146 #if TMS32060
147 #define SCALE_BITS 4            /* there are 4 bits in the scale field */
148 #define SCALE_MASK 0x7          /* we really only use the bottom 3 bits */
149 static const u8 c60_scale[SCALE_MASK + 1] = {
150         1, 0, 0, 0, 1, 1, 2, 2
151 };
152 #endif
153
154 /**************************************************************************
155  * Procedure dload_relocate
156  *
157  * Parameters:
158  *      data    Pointer to base of image data
159  *      rp              Pointer to relocation operation
160  *
161  * Effect:
162  *      Performs the specified relocation operation
163  ************************************************************************* */
164 void dload_relocate(struct dload_state *dlthis, tgt_au_t * data,
165                     struct reloc_record_t *rp, bool *tramps_generated,
166                     bool second_pass)
167 {
168         rvalue val, reloc_amt, orig_val = 0;
169         unsigned int fieldsz = 0;
170         unsigned int offset = 0;
171         unsigned int reloc_info = 0;
172         unsigned int reloc_action = 0;
173         register int rx = 0;
174         rvalue *stackp = NULL;
175         int top;
176         struct local_symbol *svp = NULL;
177 #ifdef RFV_SCALE
178         unsigned int scale = 0;
179 #endif
180         struct image_packet_t *img_pkt = NULL;
181
182         /* The image packet data struct is only used during first pass
183          * relocation in the event that a trampoline is needed.  2nd pass
184          * relocation doesn't guarantee that data is coming from an
185          * image_packet_t structure. See cload.c, dload_data for how img_data is
186          * set. If that changes this needs to be updated!!! */
187         if (second_pass == false)
188                 img_pkt = (struct image_packet_t *)((u8 *) data -
189                                                     sizeof(struct
190                                                            image_packet_t));
191
192         rx = HASH_FUNC(rp->TYPE);
193         while (rop_map1[rx] != rp->TYPE) {
194                 rx = HASH_L(rop_map2[rx]);
195                 if (rx < 0) {
196 #if TMS32060
197                         switch (rp->TYPE) {
198                         case R_C60ALIGN:
199                         case R_C60NOCMP:
200                         case R_C60FPHEAD:
201                                 /* Ignore these reloc types and return */
202                                 break;
203                         default:
204                                 /* Unknown reloc type, print error and return */
205                                 dload_error(dlthis, "Bad coff operator 0x%x",
206                                             rp->TYPE);
207                         }
208 #else
209                         dload_error(dlthis, "Bad coff operator 0x%x", rp->TYPE);
210 #endif
211                         return;
212                 }
213         }
214         rx = HASH_I(rop_map2[rx]);
215         if ((rx < (sizeof(rop_action) / sizeof(u16)))
216             && (rx < (sizeof(rop_info) / sizeof(u16))) && (rx > 0)) {
217                 reloc_action = rop_action[rx];
218                 reloc_info = rop_info[rx];
219         } else {
220                 dload_error(dlthis, "Buffer Overflow - Array Index Out "
221                             "of Bounds");
222         }
223
224         /* Compute the relocation amount for the referenced symbol, if any */
225         reloc_amt = rp->UVAL;
226         if (RFV_SYM(reloc_info)) {      /* relocation uses a symbol reference */
227                 /* If this is first pass, use the module local symbol table,
228                  * else use the trampoline symbol table. */
229                 if (second_pass == false) {
230                         if ((u32) rp->SYMNDX < dlthis->dfile_hdr.df_no_syms) {
231                                 /* real symbol reference */
232                                 svp = &dlthis->local_symtab[rp->SYMNDX];
233                                 reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ?
234                                     svp->delta : svp->value;
235                         }
236                         /* reloc references current section */
237                         else if (rp->SYMNDX == -1) {
238                                 reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ?
239                                     dlthis->delta_runaddr :
240                                     dlthis->image_secn->run_addr;
241                         }
242                 }
243         }
244         /* relocation uses a symbol reference */
245         /* Handle stack adjustment */
246         val = 0;
247         top = RFV_STK(reloc_info);
248         if (top) {
249                 top += dlthis->relstkidx - RSTK_UOP;
250                 if (top >= STATIC_EXPR_STK_SIZE) {
251                         dload_error(dlthis,
252                                     "Expression stack overflow in %s at offset "
253                                     FMT_UI32, dlthis->image_secn->name,
254                                     rp->vaddr + dlthis->image_offset);
255                         return;
256                 }
257                 val = dlthis->relstk[dlthis->relstkidx];
258                 dlthis->relstkidx = top;
259                 stackp = &dlthis->relstk[top];
260         }
261         /* Derive field position and size, if we need them */
262         if (reloc_info & ROP_RW) {      /* read or write action in our future */
263                 fieldsz = RFV_WIDTH(reloc_action);
264                 if (fieldsz) {  /* field info from table */
265                         offset = RFV_POSN(reloc_action);
266                         if (TARGET_BIG_ENDIAN)
267                                 /* make sure vaddr is the lowest target
268                                  * address containing bits */
269                                 rp->vaddr += RFV_BIGOFF(reloc_info);
270                 } else {        /* field info from relocation op */
271                         fieldsz = rp->FIELDSZ;
272                         offset = rp->OFFSET;
273                         if (TARGET_BIG_ENDIAN)
274                                 /* make sure vaddr is the lowest target
275                                    address containing bits */
276                                 rp->vaddr += (rp->WORDSZ - offset - fieldsz)
277                                     >> LOG_TARGET_AU_BITS;
278                 }
279                 data = (tgt_au_t *) ((char *)data + TADDR_TO_HOST(rp->vaddr));
280                 /* compute lowest host location of referenced data */
281 #if BITS_PER_AU > TARGET_AU_BITS
282                 /* conversion from target address to host address may lose
283                    address bits; add loss to offset */
284                 if (TARGET_BIG_ENDIAN) {
285                         offset += -((rp->vaddr << LOG_TARGET_AU_BITS) +
286                                     offset + fieldsz) &
287                             (BITS_PER_AU - TARGET_AU_BITS);
288                 } else {
289                         offset += (rp->vaddr << LOG_TARGET_AU_BITS) &
290                             (BITS_PER_AU - 1);
291                 }
292 #endif
293 #ifdef RFV_SCALE
294                 scale = RFV_SCALE(reloc_info);
295 #endif
296         }
297         /* read the object value from the current image, if so ordered */
298         if (reloc_info & ROP_R) {
299                 /* relocation reads current image value */
300                 val = dload_unpack(dlthis, data, fieldsz, offset,
301                                    RFV_SIGN(reloc_info));
302                 /* Save off the original value in case the relo overflows and
303                  * we can trampoline it. */
304                 orig_val = val;
305
306 #ifdef RFV_SCALE
307                 val <<= scale;
308 #endif
309         }
310         /* perform the necessary arithmetic */
311         switch (RFV_ACTION(reloc_action)) {     /* relocation actions */
312         case RACT_VAL:
313                 break;
314         case RACT_ASGN:
315                 val = reloc_amt;
316                 break;
317         case RACT_ADD:
318                 val += reloc_amt;
319                 break;
320         case RACT_PCR:
321                 /*-----------------------------------------------------------
322                  * Handle special cases of jumping from absolute sections
323                  * (special reloc type) or to absolute destination
324                  * (symndx == -1).  In either case, set the appropriate
325                  * relocation amount to 0.
326                  *----------------------------------------------------------- */
327                 if (rp->SYMNDX == -1)
328                         reloc_amt = 0;
329                 val += reloc_amt - dlthis->delta_runaddr;
330                 break;
331         case RACT_ADDISP:
332                 val += rp->R_DISP + reloc_amt;
333                 break;
334         case RACT_ASGPC:
335                 val = dlthis->image_secn->run_addr + reloc_amt;
336                 break;
337         case RACT_PLUS:
338                 if (stackp != NULL)
339                         val += *stackp;
340                 break;
341         case RACT_SUB:
342                 if (stackp != NULL)
343                         val = *stackp - val;
344                 break;
345         case RACT_NEG:
346                 val = -val;
347                 break;
348         case RACT_MPY:
349                 if (stackp != NULL)
350                         val *= *stackp;
351                 break;
352         case RACT_DIV:
353                 if (stackp != NULL)
354                         val = *stackp / val;
355                 break;
356         case RACT_MOD:
357                 if (stackp != NULL)
358                         val = *stackp % val;
359                 break;
360         case RACT_SR:
361                 if (val >= sizeof(rvalue) * BITS_PER_AU)
362                         val = 0;
363                 else if (stackp != NULL)
364                         val = (urvalue) *stackp >> val;
365                 break;
366         case RACT_ASR:
367                 if (val >= sizeof(rvalue) * BITS_PER_AU)
368                         val = sizeof(rvalue) * BITS_PER_AU - 1;
369                 else if (stackp != NULL)
370                         val = *stackp >> val;
371                 break;
372         case RACT_SL:
373                 if (val >= sizeof(rvalue) * BITS_PER_AU)
374                         val = 0;
375                 else if (stackp != NULL)
376                         val = *stackp << val;
377                 break;
378         case RACT_AND:
379                 if (stackp != NULL)
380                         val &= *stackp;
381                 break;
382         case RACT_OR:
383                 if (stackp != NULL)
384                         val |= *stackp;
385                 break;
386         case RACT_XOR:
387                 if (stackp != NULL)
388                         val ^= *stackp;
389                 break;
390         case RACT_NOT:
391                 val = ~val;
392                 break;
393 #if TMS32060
394         case RACT_C6SECT:
395                 /* actually needed address of secn containing symbol */
396                 if (svp != NULL) {
397                         if (rp->SYMNDX >= 0)
398                                 if (svp->secnn > 0)
399                                         reloc_amt = dlthis->ldr_sections
400                                             [svp->secnn - 1].run_addr;
401                 }
402                 /* !!! FALL THRU !!! */
403         case RACT_C6BASE:
404                 if (dlthis->bss_run_base == 0) {
405                         struct dynload_symbol *symp;
406                         symp = dlthis->mysym->find_matching_symbol
407                             (dlthis->mysym, bsssymbol);
408                         /* lookup value of global BSS base */
409                         if (symp)
410                                 dlthis->bss_run_base = symp->value;
411                         else
412                                 dload_error(dlthis,
413                                             "Global BSS base referenced in %s "
414                                             "offset" FMT_UI32 " but not "
415                                             "defined",
416                                             dlthis->image_secn->name,
417                                             rp->vaddr + dlthis->image_offset);
418                 }
419                 reloc_amt -= dlthis->bss_run_base;
420                 /* !!! FALL THRU !!! */
421         case RACT_C6DSPL:
422                 /* scale factor determined by 3 LSBs of field */
423                 scale = c60_scale[val & SCALE_MASK];
424                 offset += SCALE_BITS;
425                 fieldsz -= SCALE_BITS;
426                 val >>= SCALE_BITS;     /* ignore the scale field hereafter */
427                 val <<= scale;
428                 val += reloc_amt;       /* do the usual relocation */
429                 if (((1 << scale) - 1) & val)
430                         dload_error(dlthis,
431                                     "Unaligned reference in %s offset "
432                                     FMT_UI32, dlthis->image_secn->name,
433                                     rp->vaddr + dlthis->image_offset);
434                 break;
435 #endif
436         }                       /* relocation actions */
437         /* * Put back result as required */
438         if (reloc_info & ROP_W) {       /* relocation writes image value */
439 #ifdef RFV_SCALE
440                 val >>= scale;
441 #endif
442                 if (dload_repack(dlthis, val, data, fieldsz, offset,
443                                  RFV_SIGN(reloc_info))) {
444                         /* Check to see if this relo can be trampolined,
445                          * but only in first phase relocation.  2nd phase
446                          * relocation cannot trampoline. */
447                         if ((second_pass == false) &&
448                             (dload_tramp_avail(dlthis, rp) == true)) {
449
450                                 /* Before generating the trampoline, restore
451                                  * the value to its original so the 2nd pass
452                                  *  relo will work. */
453                                 dload_repack(dlthis, orig_val, data, fieldsz,
454                                              offset, RFV_SIGN(reloc_info));
455                                 if (!dload_tramp_generate(dlthis,
456                                                         (dlthis->image_secn -
457                                                          dlthis->ldr_sections),
458                                                          dlthis->image_offset,
459                                                          img_pkt, rp)) {
460                                         dload_error(dlthis,
461                                                     "Failed to "
462                                                     "generate trampoline for "
463                                                     "bit overflow");
464                                         dload_error(dlthis,
465                                                     "Relocation val " FMT_UI32
466                                                     " overflows %d bits in %s "
467                                                     "offset " FMT_UI32, val,
468                                                     fieldsz,
469                                                     dlthis->image_secn->name,
470                                                     dlthis->image_offset +
471                                                     rp->vaddr);
472                                 } else
473                                         *tramps_generated = true;
474                         } else {
475                                 dload_error(dlthis, "Relocation value "
476                                             FMT_UI32 " overflows %d bits in %s"
477                                             " offset " FMT_UI32, val, fieldsz,
478                                             dlthis->image_secn->name,
479                                             dlthis->image_offset + rp->vaddr);
480                         }
481                 }
482         } else if (top)
483                 *stackp = val;
484 }                               /* reloc_value */