aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/tidspbridge/dynload/reloc.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2010-09-08 23:49:04 -0700
committerDavid S. Miller <davem@davemloft.net>2010-09-08 23:49:04 -0700
commite199e6136ce6b151e6638ae93dca60748424d900 (patch)
tree0d66e0b5d227c36b005e4f5537f4bbcfc6ed4904 /drivers/staging/tidspbridge/dynload/reloc.c
parentKS8851: Correct RX packet allocation (diff)
parentMerge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6 (diff)
downloadlinux-dev-e199e6136ce6b151e6638ae93dca60748424d900.tar.xz
linux-dev-e199e6136ce6b151e6638ae93dca60748424d900.zip
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/staging/tidspbridge/dynload/reloc.c')
-rw-r--r--drivers/staging/tidspbridge/dynload/reloc.c484
1 files changed, 484 insertions, 0 deletions
diff --git a/drivers/staging/tidspbridge/dynload/reloc.c b/drivers/staging/tidspbridge/dynload/reloc.c
new file mode 100644
index 000000000000..7b28c07ed7c5
--- /dev/null
+++ b/drivers/staging/tidspbridge/dynload/reloc.c
@@ -0,0 +1,484 @@
+/*
+ * 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 */