Merge tag 'v3.3.7' into 3.3/master
[zen-stable.git] / drivers / staging / tidspbridge / dynload / reloc.c
blob7b28c07ed7c5983ec8757cc6245e1704c2875de3
1 /*
2 * reloc.c
4 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
6 * Copyright (C) 2005-2006 Texas Instruments, Inc.
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.
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.
17 #include "header.h"
19 #if TMS32060
20 /* the magic symbol for the start of BSS */
21 static const char bsssymbol[] = { ".bss" };
22 #endif
24 #if TMS32060
25 #include "reloc_table_c6000.c"
26 #endif
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
35 /**************************************************************************
36 * Procedure dload_unpack
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)
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)
51 register rvalue objval;
52 register int shift, direction;
53 register tgt_au_t *dp = data;
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;
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;
78 return objval;
80 } /* dload_unpack */
82 /**************************************************************************
83 * Procedure dload_repack
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)
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 };
101 int dload_repack(struct dload_state *dlthis, rvalue val, tgt_au_t * data,
102 int fieldsz, int offset, unsigned sgn)
104 register urvalue objval, mask;
105 register int shift, direction;
106 register tgt_au_t *dp = data;
108 fieldsz -= 1; /* avoid nastiness with 32-bit shift of 32-bit value */
109 /* clip the bits */
110 mask = (2UL << 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;
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;
126 while (mask) {
127 dp += direction;
128 *dp = (*dp & ~mask) + objval;
129 objval >>= TGTAU_BITS;
130 mask >>= TGTAU_BITS;
134 * check for overflow
136 if (sgn) {
137 unsigned tmp = (val >> fieldsz) + (sgn & 0x1);
138 if (tmp > ovf_limit[sgn - 1])
139 return 1;
141 return 0;
143 } /* dload_repack */
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
152 #endif
154 /**************************************************************************
155 * Procedure dload_relocate
157 * Parameters:
158 * data Pointer to base of image data
159 * rp Pointer to relocation operation
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)
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;
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));
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);
208 #else
209 dload_error(dlthis, "Bad coff operator 0x%x", rp->TYPE);
210 #endif
211 return;
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");
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;
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;
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;
257 val = dlthis->relstk[dlthis->relstkidx];
258 dlthis->relstkidx = top;
259 stackp = &dlthis->relstk[top];
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;
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);
292 #endif
293 #ifdef RFV_SCALE
294 scale = RFV_SCALE(reloc_info);
295 #endif
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;
306 #ifdef RFV_SCALE
307 val <<= scale;
308 #endif
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;
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);
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)) {
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);
482 } else if (top)
483 *stackp = val;
484 } /* reloc_value */