/* * reloc.c * * DSP-BIOS Bridge driver support functions for TI OMAP processors. * * Copyright (C) 2005-2006 Texas Instruments, Inc. * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "header.h" #if TMS32060 /* the magic symbol for the start of BSS */ static const char bsssymbol[] = { ".bss" }; #endif #if TMS32060 #include "reloc_table_c6000.c" #endif #if TMS32060 /* From coff.h - ignore these relocation operations */ #define R_C60ALIGN 0x76 /* C60: Alignment info for compressor */ #define R_C60FPHEAD 0x77 /* C60: Explicit assembly directive */ #define R_C60NOCMP 0x100 /* C60: Don't compress this code scn */ #endif /************************************************************************** * Procedure dload_unpack * * Parameters: * data pointer to storage unit containing lowest host address of * image data * fieldsz Size of bit field, 0 < fieldsz <= sizeof(rvalue)*BITS_PER_AU * offset Offset from LSB, 0 <= offset < BITS_PER_AU * sgn Signedness of the field (ROP_SGN, ROP_UNS, ROP_MAX, ROP_ANY) * * Effect: * Extracts the specified field and returns it. ************************************************************************* */ rvalue dload_unpack(struct dload_state *dlthis, tgt_au_t * data, int fieldsz, int offset, unsigned sgn) { register rvalue objval; register int shift, direction; register tgt_au_t *dp = data; fieldsz -= 1; /* avoid nastiness with 32-bit shift of 32-bit value */ /* * collect up enough bits to contain the desired field */ if (TARGET_BIG_ENDIAN) { dp += (fieldsz + offset) >> LOG_TGTAU_BITS; direction = -1; } else direction = 1; objval = *dp >> offset; shift = TGTAU_BITS - offset; while (shift <= fieldsz) { dp += direction; objval += (rvalue) *dp << shift; shift += TGTAU_BITS; } /* * sign or zero extend the value appropriately */ if (sgn == ROP_UNS) objval &= (2 << fieldsz) - 1; else { shift = sizeof(rvalue) * BITS_PER_AU - 1 - fieldsz; objval = (objval << shift) >> shift; } return objval; } /* dload_unpack */ /************************************************************************** * Procedure dload_repack * * Parameters: * val Value to insert * data Pointer to storage unit containing lowest host address of * image data * fieldsz Size of bit field, 0 < fieldsz <= sizeof(rvalue)*BITS_PER_AU * offset Offset from LSB, 0 <= offset < BITS_PER_AU * sgn Signedness of the field (ROP_SGN, ROP_UNS, ROP_MAX, ROP_ANY) * * Effect: * Stuffs the specified value in the specified field. Returns 0 for * success * or 1 if the value will not fit in the specified field according to the * specified signedness rule. ************************************************************************* */ static const unsigned char ovf_limit[] = { 1, 2, 2 }; int dload_repack(struct dload_state *dlthis, rvalue val, tgt_au_t * data, int fieldsz, int offset, unsigned sgn) { register urvalue objval, mask; register int shift, direction; register tgt_au_t *dp = data; fieldsz -= 1; /* avoid nastiness with 32-bit shift of 32-bit value */ /* clip the bits */ mask = (2UL << fieldsz) - 1; objval = (val & mask); /* * store the bits through the specified mask */ if (TARGET_BIG_ENDIAN) { dp += (fieldsz + offset) >> LOG_TGTAU_BITS; direction = -1; } else direction = 1; /* insert LSBs */ *dp = (*dp & ~(mask << offset)) + (objval << offset); shift = TGTAU_BITS - offset; /* align mask and objval with AU boundary */ objval >>= shift; mask >>= shift; while (mask) { dp += direction; *dp = (*dp & ~mask) + objval; objval >>= TGTAU_BITS; mask >>= TGTAU_BITS; } /* * check for overflow */ if (sgn) { unsigned tmp = (val >> fieldsz) + (sgn & 0x1); if (tmp > ovf_limit[sgn - 1]) return 1; } return 0; } /* dload_repack */ /* lookup table for the scaling amount in a C6x instruction */ #if TMS32060 #define SCALE_BITS 4 /* there are 4 bits in the scale field */ #define SCALE_MASK 0x7 /* we really only use the bottom 3 bits */ static const u8 c60_scale[SCALE_MASK + 1] = { 1, 0, 0, 0, 1, 1, 2, 2 }; #endif /************************************************************************** * Procedure dload_relocate * * Parameters: * data Pointer to base of image data * rp Pointer to relocation operation * * Effect: * Performs the specified relocation operation ************************************************************************* */ void dload_relocate(struct dload_state *dlthis, tgt_au_t * data, struct reloc_record_t *rp, bool *tramps_generated, bool second_pass) { rvalue val, reloc_amt, orig_val = 0; unsigned int fieldsz = 0; unsigned int offset = 0; unsigned int reloc_info = 0; unsigned int reloc_action = 0; register int rx = 0; rvalue *stackp = NULL; int top; struct local_symbol *svp = NULL; #ifdef RFV_SCALE unsigned int scale = 0; #endif struct image_packet_t *img_pkt = NULL; /* The image packet data struct is only used during first pass * relocation in the event that a trampoline is needed. 2nd pass * relocation doesn't guarantee that data is coming from an * image_packet_t structure. See cload.c, dload_data for how img_data is * set. If that changes this needs to be updated!!! */ if (second_pass == false) img_pkt = (struct image_packet_t *)((u8 *) data - sizeof(struct image_packet_t)); rx = HASH_FUNC(rp->TYPE); while (rop_map1[rx] != rp->TYPE) { rx = HASH_L(rop_map2[rx]); if (rx < 0) { #if TMS32060 switch (rp->TYPE) { case R_C60ALIGN: case R_C60NOCMP: case R_C60FPHEAD: /* Ignore these reloc types and return */ break; default: /* Unknown reloc type, print error and return */ dload_error(dlthis, "Bad coff operator 0x%x", rp->TYPE); } #else dload_error(dlthis, "Bad coff operator 0x%x", rp->TYPE); #endif return; } } rx = HASH_I(rop_map2[rx]); if ((rx < (sizeof(rop_action) / sizeof(u16))) && (rx < (sizeof(rop_info) / sizeof(u16))) && (rx > 0)) { reloc_action = rop_action[rx]; reloc_info = rop_info[rx]; } else { dload_error(dlthis, "Buffer Overflow - Array Index Out " "of Bounds"); } /* Compute the relocation amount for the referenced symbol, if any */ reloc_amt = rp->UVAL; if (RFV_SYM(reloc_info)) { /* relocation uses a symbol reference */ /* If this is first pass, use the module local symbol table, * else use the trampoline symbol table. */ if (second_pass == false) { if ((u32) rp->SYMNDX < dlthis->dfile_hdr.df_no_syms) { /* real symbol reference */ svp = &dlthis->local_symtab[rp->SYMNDX]; reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ? svp->delta : svp->value; } /* reloc references current section */ else if (rp->SYMNDX == -1) { reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ? dlthis->delta_runaddr : dlthis->image_secn->run_addr; } } } /* relocation uses a symbol reference */ /* Handle stack adjustment */ val = 0; top = RFV_STK(reloc_info); if (top) { top += dlthis->relstkidx - RSTK_UOP; if (top >= STATIC_EXPR_STK_SIZE) { dload_error(dlthis, "Expression stack overflow in %s at offset " FMT_UI32, dlthis->image_secn->name, rp->vaddr + dlthis->image_offset); return; } val = dlthis->relstk[dlthis->relstkidx]; dlthis->relstkidx = top; stackp = &dlthis->relstk[top]; } /* Derive field position and size, if we need them */ if (reloc_info & ROP_RW) { /* read or write action in our future */ fieldsz = RFV_WIDTH(reloc_action); if (fieldsz) { /* field info from table */ offset = RFV_POSN(reloc_action); if (TARGET_BIG_ENDIAN) /* make sure vaddr is the lowest target * address containing bits */ rp->vaddr += RFV_BIGOFF(reloc_info); } else { /* field info from relocation op */ fieldsz = rp->FIELDSZ; offset = rp->OFFSET; if (TARGET_BIG_ENDIAN) /* make sure vaddr is the lowest target address containing bits */ rp->vaddr += (rp->WORDSZ - offset - fieldsz) >> LOG_TARGET_AU_BITS; } data = (tgt_au_t *) ((char *)data + TADDR_TO_HOST(rp->vaddr)); /* compute lowest host location of referenced data */ #if BITS_PER_AU > TARGET_AU_BITS /* conversion from target address to host address may lose address bits; add loss to offset */ if (TARGET_BIG_ENDIAN) { offset += -((rp->vaddr << LOG_TARGET_AU_BITS) + offset + fieldsz) & (BITS_PER_AU - TARGET_AU_BITS); } else { offset += (rp->vaddr << LOG_TARGET_AU_BITS) & (BITS_PER_AU - 1); } #endif #ifdef RFV_SCALE scale = RFV_SCALE(reloc_info); #endif } /* read the object value from the current image, if so ordered */ if (reloc_info & ROP_R) { /* relocation reads current image value */ val = dload_unpack(dlthis, data, fieldsz, offset, RFV_SIGN(reloc_info)); /* Save off the original value in case the relo overflows and * we can trampoline it. */ orig_val = val; #ifdef RFV_SCALE val <<= scale; #endif } /* perform the necessary arithmetic */ switch (RFV_ACTION(reloc_action)) { /* relocation actions */ case RACT_VAL: break; case RACT_ASGN: val = reloc_amt; break; case RACT_ADD: val += reloc_amt; break; case RACT_PCR: /*----------------------------------------------------------- * Handle special cases of jumping from absolute sections * (special reloc type) or to absolute destination * (symndx == -1). In either case, set the appropriate * relocation amount to 0. *----------------------------------------------------------- */ if (rp->SYMNDX == -1) reloc_amt = 0; val += reloc_amt - dlthis->delta_runaddr; break; case RACT_ADDISP: val += rp->R_DISP + reloc_amt; break; case RACT_ASGPC: val = dlthis->image_secn->run_addr + reloc_amt; break; case RACT_PLUS: if (stackp != NULL) val += *stackp; break; case RACT_SUB: if (stackp != NULL) val = *stackp - val; break; case RACT_NEG: val = -val; break; case RACT_MPY: if (stackp != NULL) val *= *stackp; break; case RACT_DIV: if (stackp != NULL) val = *stackp / val; break; case RACT_MOD: if (stackp != NULL) val = *stackp % val; break; case RACT_SR: if (val >= sizeof(rvalue) * BITS_PER_AU) val = 0; else if (stackp != NULL) val = (urvalue) *stackp >> val; break; case RACT_ASR: if (val >= sizeof(rvalue) * BITS_PER_AU) val = sizeof(rvalue) * BITS_PER_AU - 1; else if (stackp != NULL) val = *stackp >> val; break; case RACT_SL: if (val >= sizeof(rvalue) * BITS_PER_AU) val = 0; else if (stackp != NULL) val = *stackp << val; break; case RACT_AND: if (stackp != NULL) val &= *stackp; break; case RACT_OR: if (stackp != NULL) val |= *stackp; break; case RACT_XOR: if (stackp != NULL) val ^= *stackp; break; case RACT_NOT: val = ~val; break; #if TMS32060 case RACT_C6SECT: /* actually needed address of secn containing symbol */ if (svp != NULL) { if (rp->SYMNDX >= 0) if (svp->secnn > 0) reloc_amt = dlthis->ldr_sections [svp->secnn - 1].run_addr; } /* !!! FALL THRU !!! */ case RACT_C6BASE: if (dlthis->bss_run_base == 0) { struct dynload_symbol *symp; symp = dlthis->mysym->find_matching_symbol (dlthis->mysym, bsssymbol); /* lookup value of global BSS base */ if (symp) dlthis->bss_run_base = symp->value; else dload_error(dlthis, "Global BSS base referenced in %s " "offset" FMT_UI32 " but not " "defined", dlthis->image_secn->name, rp->vaddr + dlthis->image_offset); } reloc_amt -= dlthis->bss_run_base; /* !!! FALL THRU !!! */ case RACT_C6DSPL: /* scale factor determined by 3 LSBs of field */ scale = c60_scale[val & SCALE_MASK]; offset += SCALE_BITS; fieldsz -= SCALE_BITS; val >>= SCALE_BITS; /* ignore the scale field hereafter */ val <<= scale; val += reloc_amt; /* do the usual relocation */ if (((1 << scale) - 1) & val) dload_error(dlthis, "Unaligned reference in %s offset " FMT_UI32, dlthis->image_secn->name, rp->vaddr + dlthis->image_offset); break; #endif } /* relocation actions */ /* * Put back result as required */ if (reloc_info & ROP_W) { /* relocation writes image value */ #ifdef RFV_SCALE val >>= scale; #endif if (dload_repack(dlthis, val, data, fieldsz, offset, RFV_SIGN(reloc_info))) { /* Check to see if this relo can be trampolined, * but only in first phase relocation. 2nd phase * relocation cannot trampoline. */ if ((second_pass == false) && (dload_tramp_avail(dlthis, rp) == true)) { /* Before generating the trampoline, restore * the value to its original so the 2nd pass * relo will work. */ dload_repack(dlthis, orig_val, data, fieldsz, offset, RFV_SIGN(reloc_info)); if (!dload_tramp_generate(dlthis, (dlthis->image_secn - dlthis->ldr_sections), dlthis->image_offset, img_pkt, rp)) { dload_error(dlthis, "Failed to " "generate trampoline for " "bit overflow"); dload_error(dlthis, "Relocation val " FMT_UI32 " overflows %d bits in %s " "offset " FMT_UI32, val, fieldsz, dlthis->image_secn->name, dlthis->image_offset + rp->vaddr); } else *tramps_generated = true; } else { dload_error(dlthis, "Relocation value " FMT_UI32 " overflows %d bits in %s" " offset " FMT_UI32, val, fieldsz, dlthis->image_secn->name, dlthis->image_offset + rp->vaddr); } } } else if (top) *stackp = val; } /* reloc_value */