From: Fernando Guzman Lugo <x0095840@xxxxxx> "Trampolines" utilize memory space containing a local jump table to hit external locations. Instead of the "near" function call directly calling the external location it "calls" a trampoline entry. A "near" function call requires the target to be within a signed, 21-bit offset from the current PC so all calls have to be within 4MB of the current execution point. The trampoline entry (location in memory) contains an unconditional long branch to the external address to be called. When the called function returns, the PC is returned to the original calling location since that is where the link register points, as the trampoline utilizes a branch instruction. Utilizing this method removes the 4MB location restriction as well since the long branch can be done within all of DSP addressable space. This doesn't require any change on the dsp side, it is an enhancement for newer DSP side binaries. Signed-off-by: Fernando Guzman Lugo <x0095840@xxxxxx> --- drivers/dsp/bridge/Makefile | 3 +- drivers/dsp/bridge/dynload/cload.c | 142 +++- drivers/dsp/bridge/dynload/dload_internal.h | 125 +++- drivers/dsp/bridge/dynload/reloc.c | 81 ++- drivers/dsp/bridge/dynload/tramp.c | 1110 ++++++++++++++++++++++++ drivers/dsp/bridge/dynload/tramp_table_c6000.c | 164 ++++ 6 files changed, 1574 insertions(+), 51 deletions(-) create mode 100644 drivers/dsp/bridge/dynload/tramp.c create mode 100644 drivers/dsp/bridge/dynload/tramp_table_c6000.c diff --git a/drivers/dsp/bridge/Makefile b/drivers/dsp/bridge/Makefile index e10a2da..cb6d1ce 100644 --- a/drivers/dsp/bridge/Makefile +++ b/drivers/dsp/bridge/Makefile @@ -14,7 +14,8 @@ libpmgr = pmgr/chnl.o pmgr/io.o pmgr/msg.o pmgr/cod.o pmgr/dev.o pmgr/wcd.o \ librmgr = rmgr/dbdcd.o rmgr/disp.o rmgr/drv.o rmgr/mgr.o rmgr/node.o \ rmgr/proc.o rmgr/pwr.o rmgr/rmm.o rmgr/strm.o rmgr/dspdrv.o \ rmgr/nldr.o rmgr/drv_interface.o -libdload = dynload/cload.o dynload/getsection.o dynload/reloc.o +libdload = dynload/cload.o dynload/getsection.o dynload/reloc.o \ + dynload/tramp.o libhw = hw/hw_prcm.o hw/hw_dspssC64P.o hw/hw_mmu.o hw/hw_mbox.o bridgedriver-objs = $(libgen) $(libservices) $(libwmd) $(libpmgr) $(librmgr) \ diff --git a/drivers/dsp/bridge/dynload/cload.c b/drivers/dsp/bridge/dynload/cload.c index 4fead55..505fcdf 100644 --- a/drivers/dsp/bridge/dynload/cload.c +++ b/drivers/dsp/bridge/dynload/cload.c @@ -191,6 +191,8 @@ int Dynamic_Load_Module(struct Dynamic_Loader_Stream *module, symbol_table_free(&dl_state); section_table_free(&dl_state); string_table_free(&dl_state); + dload_tramp_cleanup(&dl_state); + if (dl_state.dload_errcount) { Dynamic_Unload_Module(dl_state.myhandle, syms, alloc, @@ -489,10 +491,14 @@ static void allocate_sections(struct dload_state *dlthis) DL_ERROR("Arg 3 (alloc) required but NULL", 0); return; } - /* allocate space for the module handle, which we will - * keep for unload purposes */ - siz = dlthis->dfile_hdr.df_target_scns * - sizeof(struct LDR_SECTION_INFO) + MY_HANDLE_SIZE; + /* + * allocate space for the module handle, which we will keep for unload + * purposes include an additional section store for an auto-generated + * trampoline section in case we need it. + */ + siz = (dlthis->dfile_hdr.df_target_scns + 1) * + sizeof(struct LDR_SECTION_INFO) + MY_HANDLE_SIZE; + hndl = (struct my_handle *)dlthis->mysym->Allocate(dlthis->mysym, siz); if (!hndl) { /* not enough storage */ DL_ERROR(E_ALLOC, siz); @@ -587,7 +593,7 @@ static void section_table_free(struct dload_state *dlthis) * big unsorted array. We just read that array into memory in bulk. ************************************************************************/ static const char S_STRINGTBL[] = { "string table" }; -void dload_strings(struct dload_state *dlthis, boolean sec_names_only) +void dload_strings(struct dload_state *dlthis, bool sec_names_only) { u32 ssiz; char *strbuf; @@ -707,11 +713,16 @@ static void dload_symbols(struct dload_state *dlthis) if (s_count == 0) return; - /* We keep a local symbol table for all of the symbols in the input. + /* + * We keep a local symbol table for all of the symbols in the input. * This table contains only section & value info, as we do not have * to do any name processing for locals. We reuse this storage * as a temporary for .dllview record construction. - * Allocate storage for the whole table.*/ + * Allocate storage for the whole table. Add 1 to the section count + * in case a trampoline section is auto-generated as well as the + * size of the trampoline section name so DLLView doens't get lost. + */ + siz = s_count * sizeof(struct Local_Symbol); dsiz = DBG_HDR_SIZE + (sizeof(struct dll_sect) * dlthis->allocated_secn_count) + @@ -790,6 +801,12 @@ static void dload_symbols(struct dload_state *dlthis) goto loop_cont; } val = delta = symp->value; +#ifdef ENABLE_TRAMP_DEBUG + dload_syms_error(dlthis->mysym, + "===> ext sym [%s] at %x", + sname, val); +#endif + goto loop_cont; } /* symbol defined by this module */ @@ -1045,9 +1062,11 @@ loopexit: #define MY_RELOC_BUF_SIZ 8 /* careful! exists at the same time as the image buffer*/ static int relocate_packet(struct dload_state *dlthis, - struct image_packet_t *ipacket, u32 *checks) + struct image_packet_t *ipacket, + u32 *checks, bool *tramps_generated) { u32 rnum; + *tramps_generated = false; rnum = ipacket->i_num_relocs; do { /* all relocs */ @@ -1068,11 +1087,21 @@ static int relocate_packet(struct dload_state *dlthis, *checks += dload_checksum(rp, siz); do { /* perform the relocation operation */ - dload_relocate(dlthis, (TgtAU_t *) ipacket->i_bits, rp); + dload_relocate(dlthis, (TgtAU_t *) ipacket->i_bits, rp, + tramps_generated, false); rp += 1; rnum -= 1; } while ((rinbuf -= 1) > 0); } while (rnum > 0); /* all relocs */ + /* If trampoline(s) were generated, we need to do an update of the + * trampoline copy of the packet since a 2nd phase relo will be done + * later. */ + if (*tramps_generated == true) { + dload_tramp_pkt_udpate(dlthis, + (dlthis->image_secn - dlthis->ldr_sections), + dlthis->image_offset, ipacket); + } + return 1; } /* dload_read_reloc */ @@ -1097,7 +1126,7 @@ static void dload_data(struct dload_state *dlthis) struct doff_scnhdr_t *sptr = dlthis->sect_hdrs; struct LDR_SECTION_INFO *lptr = dlthis->ldr_sections; #ifdef OPT_ZERO_COPY_LOADER - boolean bZeroCopy = false; + bool bZeroCopy = false; #endif u8 *pDest; @@ -1107,7 +1136,7 @@ static void dload_data(struct dload_state *dlthis) } ibuf; /* Indicates whether CINIT processing has occurred */ - boolean cinit_processed = false; + bool cinit_processed = false; /* Loop through the sections and load them one at a time. */ @@ -1132,6 +1161,8 @@ static void dload_data(struct dload_state *dlthis) s32 ipsize; u32 checks; + bool tramp_generated = false; + /* get the fixed header bits */ if (dlthis->strm->read_buffer(dlthis->strm, &ibuf.ipacket, IPH_SIZE) != IPH_SIZE) { @@ -1203,33 +1234,44 @@ static void dload_data(struct dload_state *dlthis) if (ibuf.ipacket.i_num_relocs) { dlthis->image_offset = image_offset; if (!relocate_packet(dlthis, - &ibuf.ipacket, &checks)) + &ibuf.ipacket, &checks, + &tramp_generated)) return; /* serious error */ } if (~checks) DL_ERROR(E_CHECKSUM, IMAGEPAK); - /* stuff the result into target memory */ - if (DLOAD_SECT_TYPE(sptr) == DLOAD_CINIT) { - cload_cinit(dlthis, &ibuf.ipacket); - cinit_processed = true; - } else { + /* Only write the result to the target if no + * trampoline was generated. Otherwise it + *will be done during trampoline finalize. */ + + if (tramp_generated == false) { + + /* stuff the result into target + * memory */ + if (DLOAD_SECT_TYPE(sptr) == + DLOAD_CINIT) { + cload_cinit(dlthis, + &ibuf.ipacket); + cinit_processed = true; + } else { #ifdef OPT_ZERO_COPY_LOADER - if (!bZeroCopy) { + if (!bZeroCopy) { #endif - - if (!dlthis->myio->writemem - (dlthis->myio, ibuf.bufr, - lptr->load_addr + image_offset, lptr, - BYTE_TO_HOST - (ibuf.ipacket.i_packet_size))) { - DL_ERROR( - "Write to " FMT_UI32 " failed", - lptr->load_addr + image_offset); - } + if (!dlthis->myio->writemem + (dlthis->myio, ibuf.bufr, + lptr->load_addr + image_offset, + lptr, BYTE_TO_HOST + (ibuf.ipacket.i_packet_size))) { + DL_ERROR( + "Write to " FMT_UI32 + " failed", + lptr->load_addr + + image_offset); + } #ifdef OPT_ZERO_COPY_LOADER - } + } #endif - + } } image_offset += BYTE_TO_TADDR(ibuf.ipacket.i_packet_size); @@ -1281,6 +1323,12 @@ loop_cont: sptr += 1; lptr += 1; } /* load sections */ + + /* Finalize any trampolines that were created during the load */ + if (dload_tramp_finalize(dlthis) == 0) { + DL_ERROR("Finalization of auto-trampolines (size = " FMT_UI32 + ") failed", dlthis->tramp.tramp_sect_next_addr); + } } /* dload_data */ /************************************************************************* @@ -1526,6 +1574,16 @@ static void init_module_handle(struct dload_state *dlthis) hndl = dlthis->myhandle; if (!hndl) return; /* must be errors detected, so forget it */ + + /* Store the section count */ + hndl->secn_count = dlthis->allocated_secn_count; + + /* If a trampoline section was created, add it in */ + if (dlthis->tramp.tramp_sect_next_addr != 0) + hndl->secn_count += 1; + + hndl->secn_count = hndl->secn_count << 1; + hndl->secn_count = dlthis->allocated_secn_count << 1; #ifndef TARGET_ENDIANNESS if (dlthis->big_e_target) @@ -1603,10 +1661,30 @@ static void init_module_handle(struct dload_state *dlthis) dbsec += 1; asecs += 1; } + + /* If a trampoline section was created go ahead and add its info */ + if (dlthis->tramp.tramp_sect_next_addr != 0) { + dbmod->num_sects++; + dbsec->sect_load_adr = asecs->load_addr; + dbsec->sect_run_adr = asecs->run_addr; + dbsec++; + asecs++; + } + /* now cram in the names */ cp = copy_tgt_strings(dbsec, dlthis->str_head, dlthis->debug_string_size); + + /* If a trampoline section was created, add its name so DLLView + * can show the user the section info. */ + if (dlthis->tramp.tramp_sect_next_addr != 0) { + cp = copy_tgt_strings(cp, + dlthis->tramp.final_string_table, + strlen(dlthis->tramp.final_string_table) + 1); + } + + /* round off the size of the debug record, and remember same */ hndl->dm.dbsiz = HOST_TO_TDATA_ROUND(cp - (char *)dbmod); *cp = 0; /* strictly to make our test harness happy */ @@ -1713,7 +1791,9 @@ int Dynamic_Unload_Module(DLOAD_mhandle mhandle, if (!syms) return 1; syms->Purge_Symbol_Table(syms, (unsigned) hndl); - /* Deallocate target memory for sections */ + /* Deallocate target memory for sections + * NOTE: The trampoline section, if created, gets deleted here, too */ + asecs = hndl->secns; if (alloc) for (curr_sect = (hndl->secn_count >> 1); curr_sect > 0; diff --git a/drivers/dsp/bridge/dynload/dload_internal.h b/drivers/dsp/bridge/dynload/dload_internal.h index 78f5058..65d1f47 100644 --- a/drivers/dsp/bridge/dynload/dload_internal.h +++ b/drivers/dsp/bridge/dynload/dload_internal.h @@ -120,7 +120,103 @@ struct Local_Symbol { s32 delta; /* Original value in input file */ s16 secnn; /* section number */ s16 sclass; /* symbol class */ -} ; +}; + + +/* + * Trampoline data structures + */ +#define TRAMP_NO_GEN_AVAIL 65535 +#define TRAMP_SYM_PREFIX "__$dbTR__" +#define TRAMP_SECT_NAME ".dbTR" +#define TRAMP_SYM_PREFIX_LEN 9 /* MUST MATCH THE LENGTH ABOVE!! */ +#define TRAMP_SYM_HEX_ASCII_LEN 9 /* Includes NULL termination */ + +#define GET_CONTAINER(ptr, type, field) ((type *)((unsigned long)ptr -\ + (unsigned long)(&((type *)0)->field))) +#ifndef FIELD_OFFSET +#define FIELD_OFFSET(type, field) ((unsigned long)(&((type *)0)->field)) +#endif + + +/* + The trampoline code for the target is located in a table called + "tramp_gen_info" with is indexed by looking up the index in the table + "tramp_map". The tramp_map index is acquired using the target + HASH_FUNC on the relocation type that caused the trampoline. Each + trampoline code table entry MUST follow this format: + + |----------------------------------------------| + | tramp_gen_code_hdr | + |----------------------------------------------| + | Trampoline image code | + | (the raw instruction code for the target) | + |----------------------------------------------| + | Relocation entries for the image code | + |----------------------------------------------| + + This is very similar to how image data is laid out in the DOFF file + itself. +*/ +struct tramp_gen_code_hdr { + u32 tramp_code_size; /* in BYTES */ + u32 num_relos; + u32 relo_offset; /* in BYTES */ +}; + +struct tramp_img_pkt { + struct tramp_img_pkt *next; /* MUST BE FIRST */ + u32 base; + struct tramp_gen_code_hdr hdr; + u8 payload[VARIABLE_SIZE]; +}; + +struct tramp_img_dup_relo { + struct tramp_img_dup_relo *next; + struct reloc_record_t relo; +}; + +struct tramp_img_dup_pkt { + struct tramp_img_dup_pkt *next; /* MUST BE FIRST */ + s16 secnn; + u32 offset; + struct image_packet_t img_pkt; + struct tramp_img_dup_relo *relo_chain; + + /* PAYLOAD OF IMG PKT FOLLOWS */ +}; + +struct tramp_sym { + struct tramp_sym *next; /* MUST BE FIRST */ + u32 index; + u32 str_index; + struct Local_Symbol sym_info; +}; + +struct tramp_string { + struct tramp_string *next; /* MUST BE FIRST */ + u32 index; + char str[VARIABLE_SIZE]; /* NULL terminated */ +}; + +struct tramp_info { + u32 tramp_sect_next_addr; + struct LDR_SECTION_INFO sect_info; + + struct tramp_sym *symbol_head; + struct tramp_sym *symbol_tail; + u32 tramp_sym_next_index; + struct Local_Symbol *final_sym_table; + + struct tramp_string *string_head; + struct tramp_string *string_tail; + u32 tramp_string_next_index; + u32 tramp_string_size; + char *final_string_table; + + struct tramp_img_pkt *tramp_pkts; + struct tramp_img_dup_pkt *dup_pkts; +}; /* * States of the .cinit state machine @@ -187,6 +283,8 @@ struct dload_state { struct doff_filehdr_t dfile_hdr; /* DOFF file header structure */ struct doff_verify_rec_t verify; /* Verify record */ + struct tramp_info tramp; /* Trampoline data, if needed */ + int relstkidx; /* index into relocation value stack */ /* relocation value stack used in relexp.c */ RVALUE relstk[STATIC_EXPR_STK_SIZE]; @@ -206,7 +304,7 @@ extern void dload_error(struct dload_state *dlthis, const char *errtxt, ...); extern void dload_syms_error(struct Dynamic_Loader_Sym *syms, const char *errtxt, ...); extern void dload_headers(struct dload_state *dlthis); -extern void dload_strings(struct dload_state *dlthis, boolean sec_names_only); +extern void dload_strings(struct dload_state *dlthis, bool sec_names_only); extern void dload_sections(struct dload_state *dlthis); extern void dload_reorder(void *data, int dsiz, u32 map); extern u32 dload_checksum(void *data, unsigned siz); @@ -226,7 +324,8 @@ extern uint32_t dload_reverse_checksum_16(void *data, unsigned siz); * exported by reloc.c */ extern void dload_relocate(struct dload_state *dlthis, TgtAU_t *data, - struct reloc_record_t *rp); + struct reloc_record_t *rp, bool *tramps_generated, + bool second_pass); extern RVALUE dload_unpack(struct dload_state *dlthis, TgtAU_t *data, int fieldsz, int offset, unsigned sgn); @@ -234,4 +333,24 @@ extern RVALUE dload_unpack(struct dload_state *dlthis, TgtAU_t *data, extern int dload_repack(struct dload_state *dlthis, RVALUE val, TgtAU_t *data, int fieldsz, int offset, unsigned sgn); + +/* + * exported by tramp.c + */ +extern bool dload_tramp_avail(struct dload_state *dlthis, + struct reloc_record_t *rp); + +int dload_tramp_generate(struct dload_state *dlthis, s16 secnn, + u32 image_offset, struct image_packet_t *ipacket, + struct reloc_record_t *rp); + +extern int dload_tramp_pkt_udpate(struct dload_state *dlthis, + s16 secnn, u32 image_offset, + struct image_packet_t *ipacket); + +extern int dload_tramp_finalize(struct dload_state *dlthis); + +extern void dload_tramp_cleanup(struct dload_state *dlthis); + + #endif /* __DLOAD_INTERNAL__ */ diff --git a/drivers/dsp/bridge/dynload/reloc.c b/drivers/dsp/bridge/dynload/reloc.c index 54e460e..d4457c5 100644 --- a/drivers/dsp/bridge/dynload/reloc.c +++ b/drivers/dsp/bridge/dynload/reloc.c @@ -163,10 +163,10 @@ static const u8 C60_Scale[SCALE_MASK+1] = { * Performs the specified relocation operation **************************************************************************/ void dload_relocate(struct dload_state *dlthis, TgtAU_t *data, - struct reloc_record_t *rp) + struct reloc_record_t *rp, bool *tramps_genereted, + bool second_pass) { - RVALUE val = 0; - RVALUE reloc_amt = 0; + RVALUE val, reloc_amt, orig_val = 0; unsigned int fieldsz = 0; unsigned int offset = 0; unsigned int reloc_info = 0; @@ -178,6 +178,17 @@ void dload_relocate(struct dload_state *dlthis, TgtAU_t *data, #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 i_bits 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->r_type); while (rop_map1[rx] != rp->r_type) { @@ -211,16 +222,22 @@ void dload_relocate(struct dload_state *dlthis, TgtAU_t *data, /* Compute the relocation amount for the referenced symbol, if any */ reloc_amt = rp->r_uval; if (RFV_SYM(reloc_info)) { /* relocation uses a symbol reference */ - if ((u32)rp->r_symndx < dlthis->dfile_hdr.df_no_syms) { - /* real symbol reference */ - svp = &dlthis->local_symtab[rp->r_symndx]; - reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ? - svp->delta : svp->value; + /* If this is first pass, use the module local symbol table, + * else use the trampoline symbol table. */ + if (second_pass == false) { + if ((u32)rp->r_symndx < dlthis->dfile_hdr.df_no_syms) { + /* real symbol reference */ + svp = &dlthis->local_symtab[rp->r_symndx]; + reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ? + svp->delta : svp->value; + } + /* reloc references current section */ + else if (rp->r_symndx == -1) { + reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ? + dlthis->delta_runaddr : + dlthis->image_secn->run_addr; + } } - /* reloc references current section */ - else if (rp->r_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; @@ -277,6 +294,10 @@ void dload_relocate(struct dload_state *dlthis, TgtAU_t *data, 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 @@ -414,10 +435,38 @@ void dload_relocate(struct dload_state *dlthis, TgtAU_t *data, #endif if (dload_repack(dlthis, val, data, fieldsz, offset, RFV_SIGN(reloc_info))) { - 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->r_vaddr); + /* 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 value " + FMT_UI32 " overflows %d bits in %s " + "offset " FMT_UI32, val, fieldsz, + dlthis->image_secn->name, + dlthis->image_offset + rp->r_vaddr); + } else + *tramps_genereted = 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->r_vaddr); + } } } else if (top) *stackp = val; diff --git a/drivers/dsp/bridge/dynload/tramp.c b/drivers/dsp/bridge/dynload/tramp.c new file mode 100644 index 0000000..8c725c7 --- /dev/null +++ b/drivers/dsp/bridge/dynload/tramp.c @@ -0,0 +1,1110 @@ +/* + * Copyright 2009 by Texas Instruments Incorporated. + * All rights reserved. Property of Texas Instruments Incorporated. + * Restricted rights to use, duplicate or disclose this code are + * granted through contract. + * + * @(#) DSP/BIOS Bridge + */ +#include "header.h" + +#if TMS32060 +#include "tramp_table_c6000.c" +#endif + +#define MAX_RELOS_PER_PASS 4 + +/* + * Function: priv_tramp_sect_tgt_alloc + * Description: Allocate target memory for the trampoline section. The + * target mem size is easily obtained as the next available address. + */ +static int priv_tramp_sect_tgt_alloc(struct dload_state *dlthis) +{ + int ret_val = 0; + struct LDR_SECTION_INFO *sect_info; + + /* Populate the trampoline loader section and allocate it on the + * target. The section name is ALWAYS the first string in the final + * string table for trampolines. The trampoline section is always + * 1 beyond the total number of allocated sections. */ + sect_info = &dlthis->ldr_sections[dlthis->allocated_secn_count]; + + sect_info->name = dlthis->tramp.final_string_table; + sect_info->size = dlthis->tramp.tramp_sect_next_addr; + sect_info->context = 0; + sect_info->type = + (4 << 8) | DLOAD_TEXT | DS_ALLOCATE_MASK | DS_DOWNLOAD_MASK; + sect_info->page = 0; + sect_info->run_addr = 0; + sect_info->load_addr = 0; + ret_val = dlthis->myalloc->Allocate(dlthis->myalloc, + sect_info, DS_ALIGNMENT(sect_info->type)); + + if (ret_val == 0) + dload_error(dlthis, "Failed to allocate target memory for" + " trampoline"); + + return ret_val; +} + +/* + * Function: priv_h2a + * Description: Helper function to convert a hex value to its ASCII + * representation. Used for trampoline symbol name generation. + */ +static u8 priv_h2a(u8 value) +{ + if (value > 0xF) + return 0xFF; + + if (value <= 9) + value += 0x30; + else + value += 0x37; + + return value; +} + +/* + * Function: priv_tramp_sym_gen_name + * Description: Generate a trampoline symbol name (ASCII) using the value + * of the symbol. This places the new name into the user buffer. + * The name is fixed in length and of the form: __$dbTR__xxxxxxxx + * (where "xxxxxxxx" is the hex value. + */ +static void priv_tramp_sym_gen_name(u32 value, char *dst) +{ + u32 i; + volatile char *prefix = TRAMP_SYM_PREFIX; + volatile char *dst_local = dst; + u8 tmp; + + /* Clear out the destination, including the ending NULL */ + for (i = 0; i < (TRAMP_SYM_PREFIX_LEN + TRAMP_SYM_HEX_ASCII_LEN); i++) + *(dst_local + i) = 0; + + /* Copy the prefix to start */ + for (i = 0; i < strlen(TRAMP_SYM_PREFIX); i++) { + *dst_local = *(prefix + i); + dst_local++; + } + + /* Now convert the value passed in to a string equiv of the hex */ + for (i = 0; i < sizeof(value); i++) { +#ifndef _BIG_ENDIAN + tmp = *(((u8 *)&value) + (sizeof(value) - 1) - i); + *dst_local = priv_h2a((tmp & 0xF0) >> 4); + dst_local++; + *dst_local = priv_h2a(tmp & 0x0F); + dst_local++; +#else + tmp = *(((u8 *)&value) + i); + *dst_local = priv_h2a((tmp & 0xF0) >> 4); + dst_local++; + *dst_local = priv_h2a(tmp & 0x0F); + dst_local++; +#endif + } + + /* NULL terminate */ + *dst_local = 0; +} + +/* + * Function: priv_tramp_string_create + * Description: Create a new string specific to the trampoline loading and add + * it to the trampoline string list. This list contains the + * trampoline section name and trampoline point symbols. + */ +static struct tramp_string *priv_tramp_string_create(struct dload_state *dlthis, + u32 str_len, char *str) +{ + struct tramp_string *new_string = NULL; + u32 i; + + /* Create a new string object with the specified size. */ + new_string = (struct tramp_string *)dlthis->mysym->Allocate( + dlthis->mysym, (sizeof(struct tramp_string) + str_len + 1)); + if (new_string != NULL) { + /* Clear the string first. This ensures the ending NULL is + * present and the optimizer won't touch it. */ + for (i = 0; i < (sizeof(struct tramp_string) + str_len + 1); + i++) + *((u8 *)new_string + i) = 0; + + /* Add this string to our virtual table by assigning it the + * next index and pushing it to the tail of the list. */ + new_string->index = dlthis->tramp.tramp_string_next_index; + dlthis->tramp.tramp_string_next_index++; + dlthis->tramp.tramp_string_size += str_len + 1; + + new_string->next = NULL; + if (dlthis->tramp.string_head == NULL) + dlthis->tramp.string_head = new_string; + else + dlthis->tramp.string_tail->next = new_string; + + dlthis->tramp.string_tail = new_string; + + /* Copy the string over to the new object */ + for (i = 0; i < str_len; i++) + new_string->str[i] = str[i]; + } + + return new_string; +} + +/* + * Function: priv_tramp_string_find + * Description: Walk the trampoline string list and find a match for the + * provided string. If not match is found, NULL is returned. + */ +static struct tramp_string *priv_tramp_string_find(struct dload_state *dlthis, + char *str) +{ + struct tramp_string *cur_str = NULL; + struct tramp_string *ret_val = NULL; + u32 i; + u32 str_len = strlen(str); + + for (cur_str = dlthis->tramp.string_head; + (ret_val == NULL) && (cur_str != NULL); + cur_str = cur_str->next) { + /* If the string lengths aren't equal, don't bother + * comparing */ + if (str_len != strlen(cur_str->str)) + continue; + + /* Walk the strings until one of them ends */ + for (i = 0; i < str_len; i++) { + /* If they don't match in the current position then + * break out now, no sense in continuing to look at + * this string. */ + if (str[i] != cur_str->str[i]) + break; + } + + if (i == str_len) + ret_val = cur_str; + } + + return ret_val; +} + +/* + * Function: priv_string_tbl_finalize + * Description: Flatten the trampoline string list into a table of NULL + * terminated strings. This is the same format of string table + * as used by the COFF/DOFF file. + */ +static int priv_string_tbl_finalize(struct dload_state *dlthis) +{ + int ret_val = 0; + struct tramp_string *cur_string; + char *cur_loc; + char *tmp; + + /* Allocate enough space for all strings that have been created. The + * table is simply all strings concatenated together will NULL + * endings. */ + dlthis->tramp.final_string_table = + (char *)dlthis->mysym->Allocate(dlthis->mysym, + dlthis->tramp.tramp_string_size); + if (dlthis->tramp.final_string_table != NULL) { + /* We got our buffer, walk the list and release the nodes as* + * we go */ + cur_loc = dlthis->tramp.final_string_table; + cur_string = dlthis->tramp.string_head; + while (cur_string != NULL) { + /* Move the head/tail pointers */ + dlthis->tramp.string_head = cur_string->next; + if (dlthis->tramp.string_tail == cur_string) + dlthis->tramp.string_tail = NULL; + + /* Copy the string contents */ + for (tmp = cur_string->str; + *tmp != '\0'; + tmp++, cur_loc++) + *cur_loc = *tmp; + + /* Pick up the NULL termination since it was missed by + * breaking using it to end the above loop. */ + *cur_loc = '\0'; + cur_loc++; + + /* Free the string node, we don't need it any more. */ + dlthis->mysym->Deallocate(dlthis->mysym, cur_string); + + /* Move our pointer to the next one */ + cur_string = dlthis->tramp.string_head; + } + + /* Update our return value to success */ + ret_val = 1; + } else + dload_error(dlthis, "Failed to allocate trampoline " + "string table"); + + return ret_val; +} + +/* + * Function: priv_tramp_sect_alloc + * Description: Virtually allocate space from the trampoline section. This + * function returns the next offset within the trampoline section + * that is available and moved the next available offset by the + * requested size. NO TARGET ALLOCATION IS DONE AT THIS TIME. + */ +static u32 priv_tramp_sect_alloc(struct dload_state *dlthis, u32 tramp_size) +{ + u32 ret_val; + + /* If the next available address is 0, this is our first allocation. + * Create a section name string to go into the string table . */ + if (dlthis->tramp.tramp_sect_next_addr == 0) { + dload_syms_error(dlthis->mysym, "*** WARNING *** created " + "dynamic TRAMPOLINE section for module %s", + dlthis->str_head); + } + + /* Reserve space for the new trampoline */ + ret_val = dlthis->tramp.tramp_sect_next_addr; + dlthis->tramp.tramp_sect_next_addr += tramp_size; + return ret_val; +} + +/* + * Function: priv_tramp_sym_create + * Description: Allocate and create a new trampoline specific symbol and add + * it to the trampoline symbol list. These symbols will include + * trampoline points as well as the external symbols they + * reference. + */ +static struct tramp_sym *priv_tramp_sym_create(struct dload_state *dlthis, + u32 str_index, struct Local_Symbol *tmp_sym) +{ + struct tramp_sym *new_sym = NULL; + u32 i; + + /* Allocate new space for the symbol in the symbol table. */ + new_sym = (struct tramp_sym *)dlthis->mysym->Allocate(dlthis->mysym, + sizeof(struct tramp_sym)); + if (new_sym != NULL) { + for (i = 0; i != sizeof(struct tramp_sym); i++) + *((char *)new_sym + i) = 0; + + /* Assign this symbol the next symbol index for easier + * reference later during relocation. */ + new_sym->index = dlthis->tramp.tramp_sym_next_index; + dlthis->tramp.tramp_sym_next_index++; + + /* Populate the symbol information. At this point any + * trampoline symbols will be the offset location, not the + * final. Copy over the symbol info to start, then be sure to + * get the string index from the trampoline string table. */ + new_sym->sym_info = *tmp_sym; + new_sym->str_index = str_index; + + /* Push the new symbol to the tail of the symbol table list */ + new_sym->next = NULL; + if (dlthis->tramp.symbol_head == NULL) + dlthis->tramp.symbol_head = new_sym; + else + dlthis->tramp.symbol_tail->next = new_sym; + + dlthis->tramp.symbol_tail = new_sym; + } + + return new_sym; +} + +/* + * Function: priv_tramp_sym_get + * Description: Search for the symbol with the matching string index (from + * the trampoline string table) and return the trampoline + * symbol object, if found. Otherwise return NULL. + */ +static struct tramp_sym *priv_tramp_sym_get(struct dload_state *dlthis, + u32 string_index) +{ + struct tramp_sym *sym_found = NULL; + + /* Walk the symbol table list and search vs. the string index */ + for (sym_found = dlthis->tramp.symbol_head; + sym_found != NULL; + sym_found = sym_found->next) { + if (sym_found->str_index == string_index) + break; + } + + return sym_found; +} + +/* + * Function: priv_tramp_sym_find + * Description: Search for a trampoline symbol based on the string name of + * the symbol. Return the symbol object, if found, otherwise + * return NULL. + */ +static struct tramp_sym *priv_tramp_sym_find(struct dload_state *dlthis, + char *string) +{ + struct tramp_sym *sym_found = NULL; + struct tramp_string *str_found = NULL; + + /* First, search for the string, then search for the sym based on the + string index. */ + str_found = priv_tramp_string_find(dlthis, string); + if (str_found != NULL) + sym_found = priv_tramp_sym_get(dlthis, str_found->index); + + return sym_found; +} + +/* + * Function: priv_tramp_sym_finalize + * Description: Allocate a flat symbol table for the trampoline section, + * put each trampoline symbol into the table, adjust the + * symbol value based on the section address on the target and + * free the trampoline symbol list nodes. + */ +static int priv_tramp_sym_finalize(struct dload_state *dlthis) +{ + int ret_val = 0; + struct tramp_sym *cur_sym; + struct LDR_SECTION_INFO *tramp_sect = + &dlthis->ldr_sections[dlthis->allocated_secn_count]; + struct Local_Symbol *new_sym; + + /* Allocate a table to hold a flattened version of all symbols + * created. */ + dlthis->tramp.final_sym_table = + (struct Local_Symbol *)dlthis->mysym->Allocate( + dlthis->mysym, (sizeof(struct Local_Symbol) * + dlthis->tramp.tramp_sym_next_index)); + if (dlthis->tramp.final_sym_table != NULL) { + /* Walk the list of all symbols, copy it over to the flattened + * table. After it has been copied, the node can be freed as + * it is no longer needed. */ + new_sym = dlthis->tramp.final_sym_table; + cur_sym = dlthis->tramp.symbol_head; + while (cur_sym != NULL) { + /* Pop it off the list */ + dlthis->tramp.symbol_head = cur_sym->next; + if (cur_sym == dlthis->tramp.symbol_tail) + dlthis->tramp.symbol_tail = NULL; + + /* Copy the symbol contents into the flat table */ + *new_sym = cur_sym->sym_info; + + /* Now finaize the symbol. If it is in the tramp + * section, we need to adjust for the section start. + * If it is external then we don't need to adjust at + * all. + * NOTE: THIS CODE ASSUMES THAT THE TRAMPOLINE IS + * REFERENCED LIKE A CALL TO AN EXTERNAL SO VALUE AND + * DELTA ARE THE SAME. SEE THE FUNCTION dload_symbols + * WHERE DN_UNDEF IS HANDLED FOR MORE REFERENCE. */ + if (new_sym->secnn < 0) { + new_sym->value += tramp_sect->load_addr; + new_sym->delta = new_sym->value; + } + + /* Let go of the symbol node */ + dlthis->mysym->Deallocate(dlthis->mysym, cur_sym); + + /* Move to the next node */ + cur_sym = dlthis->tramp.symbol_head; + new_sym++; + } + + ret_val = 1; + } else + dload_error(dlthis, "Failed to alloc trampoline sym table"); + + return ret_val; +} + +/* + * Function: priv_tgt_img_gen + * Description: Allocate storage for and copy the target specific image data + * and fix up its relocations for the new external symbol. If + * a trampoline image packet was successfully created it is added + * to the trampoline list. + */ +static int priv_tgt_img_gen(struct dload_state *dlthis, u32 base, + u32 gen_index, struct tramp_sym *new_ext_sym) +{ + struct tramp_img_pkt *new_img_pkt = NULL; + u32 i; + u32 pkt_size = tramp_img_pkt_size_get(); + u8 *gen_tbl_entry; + u8 *pkt_data; + struct reloc_record_t *cur_relo; + int ret_val = 0; + + /* Allocate a new image packet and set it up. */ + new_img_pkt = + (struct tramp_img_pkt *)dlthis->mysym->Allocate(dlthis->mysym, + pkt_size); + if (new_img_pkt != NULL) { + /* Save the base, this is where it goes in the section */ + new_img_pkt->base = base; + + /* Copy over the image data and relos from the target table */ + pkt_data = (u8 *)&new_img_pkt->hdr; + gen_tbl_entry = (u8 *)&tramp_gen_info[gen_index]; + for (i = 0; i < pkt_size; i++) { + *pkt_data = *gen_tbl_entry; + pkt_data++; + gen_tbl_entry++; + } + + /* Update the relocations to point to the external symbol */ + cur_relo = + (struct reloc_record_t *)((u8 *)&new_img_pkt->hdr + + new_img_pkt->hdr.relo_offset); + for (i = 0; i < new_img_pkt->hdr.num_relos; i++) + cur_relo[i].r_symndx = new_ext_sym->index; + + /* Add it to the trampoline list. */ + new_img_pkt->next = dlthis->tramp.tramp_pkts; + dlthis->tramp.tramp_pkts = new_img_pkt; + + ret_val = 1; + } + + return ret_val; +} + +/* + * Function: priv_pkt_relo + * Description: Take the provided image data and the collection of relocations + * for it and perform the relocations. Note that all relocations + * at this stage are considered SECOND PASS since the original + * image has already been processed in the first pass. This means + * TRAMPOLINES ARE TREATED AS 2ND PASS even though this is really + * the first (and only) relocation that will be performed on them. + */ +static int priv_pkt_relo(struct dload_state *dlthis, TgtAU_t *data, + struct reloc_record_t *rp[], u32 relo_count) +{ + int ret_val = 1; + u32 i; + bool tmp; + + /* Walk through all of the relos and process them. This function is + * the equivalent of relocate_packet() from cload.c, but specialized + * for trampolines and 2nd phase relocations. */ + for (i = 0; i < relo_count; i++) + dload_relocate(dlthis, data, rp[i], &tmp, true); + + return ret_val; +} + +/* + * Function: priv_tramp_pkt_finalize + * Description: Walk the list of all trampoline packets and finalize them. + * Each trampoline image packet will be relocated now that the + * trampoline section has been allocated on the target. Once + * all of the relocations are done the trampoline image data + * is written into target memory and the trampoline packet + * is freed: it is no longer needed after this point. + */ +static int priv_tramp_pkt_finalize(struct dload_state *dlthis) +{ + int ret_val = 1; + struct tramp_img_pkt *cur_pkt = NULL; + struct reloc_record_t *relos[MAX_RELOS_PER_PASS]; + u32 relos_done; + u32 i; + struct reloc_record_t *cur_relo; + struct LDR_SECTION_INFO *sect_info = + &dlthis->ldr_sections[dlthis->allocated_secn_count]; + + /* Walk the list of trampoline packets and relocate each packet. This + * function is the trampoline equivalent of dload_data() from + * cload.c. */ + cur_pkt = dlthis->tramp.tramp_pkts; + while ((ret_val != 0) && (cur_pkt != NULL)) { + /* Remove the pkt from the list */ + dlthis->tramp.tramp_pkts = cur_pkt->next; + + /* Setup section and image offset information for the relo */ + dlthis->image_secn = sect_info; + dlthis->image_offset = cur_pkt->base; + dlthis->delta_runaddr = sect_info->run_addr; + + /* Walk through all relos for the packet */ + relos_done = 0; + cur_relo = (struct reloc_record_t *)((u8 *)&cur_pkt->hdr + + cur_pkt->hdr.relo_offset); + while (relos_done < cur_pkt->hdr.num_relos) { +#ifdef ENABLE_TRAMP_DEBUG + dload_syms_error(dlthis->mysym, + "===> Trampoline %x branches to %x", + sect_info->run_addr + dlthis->image_offset, + dlthis->tramp. + final_sym_table[cur_relo->r_symndx].value); +#endif + + for (i = 0; + ((i < MAX_RELOS_PER_PASS) && + ((i + relos_done) < cur_pkt->hdr.num_relos)); + i++) + relos[i] = cur_relo + i; + + /* Do the actual relo */ + ret_val = priv_pkt_relo(dlthis, + (TgtAU_t *)&cur_pkt->payload, + relos, i); + if (ret_val == 0) { + dload_error(dlthis, + "Relocation of trampoline pkt at %x failed", + cur_pkt->base + sect_info->run_addr); + break; + } + + relos_done += i; + cur_relo += i; + } + + /* Make sure we didn't hit a problem */ + if (ret_val != 0) { + /* Relos are done for the packet, write it to the + * target */ + ret_val = dlthis->myio->writemem(dlthis->myio, + &cur_pkt->payload, + sect_info->load_addr + cur_pkt->base + , sect_info, BYTE_TO_HOST( + cur_pkt->hdr.tramp_code_size)); + if (ret_val == 0) { + dload_error(dlthis, + "Write to " FMT_UI32 " failed", + sect_info->load_addr + cur_pkt->base); + } + + /* Done with the pkt, let it go */ + dlthis->mysym->Deallocate(dlthis->mysym, cur_pkt); + + /* Get the next packet to process */ + cur_pkt = dlthis->tramp.tramp_pkts; + } + } + + return ret_val; +} + +/* + * Function: priv_dup_pkt_finalize + * Description: Walk the list of duplicate image packets and finalize them. + * Each duplicate packet will be relocated again for the + * relocations that previously failed and have been adjusted + * to point at a trampoline. Once all relocations for a packet + * have been done, write the packet into target memory. The + * duplicate packet and its relocation chain are all freed + * after use here as they are no longer needed after this. + */ +static int priv_dup_pkt_finalize(struct dload_state *dlthis) +{ + int ret_val = 1; + struct tramp_img_dup_pkt *cur_pkt; + struct tramp_img_dup_relo *cur_relo; + struct reloc_record_t *relos[MAX_RELOS_PER_PASS]; + struct doff_scnhdr_t *sect_hdr = NULL; + s32 i; + + /* Similar to the trampoline pkt finalize, this function walks each dup + * pkt that was generated and performs all relocations that were + * deferred to a 2nd pass. This is the equivalent of dload_data() from + * cload.c, but does not need the additional reorder and checksum + * processing as it has already been done. */ + cur_pkt = dlthis->tramp.dup_pkts; + while ((ret_val != 0) && (cur_pkt != NULL)) { + /* Remove the node from the list, we'll be freeing it + * shortly */ + dlthis->tramp.dup_pkts = cur_pkt->next; + + /* Setup the section and image offset for relocation */ + dlthis->image_secn = &dlthis->ldr_sections[cur_pkt->secnn]; + dlthis->image_offset = cur_pkt->offset; + + /* In order to get the delta run address, we need to reference + * the original section header. It's a bit ugly, but needed + * for relo. */ + i = (s32)(dlthis->image_secn - dlthis->ldr_sections); + sect_hdr = dlthis->sect_hdrs + i; + dlthis->delta_runaddr = sect_hdr->ds_paddr; + + /* Walk all relos in the chain and process each. */ + cur_relo = cur_pkt->relo_chain; + while (cur_relo != NULL) { + /* Process them a chunk at a time to be efficient */ + for (i = 0; (i < MAX_RELOS_PER_PASS) + && (cur_relo != NULL); + i++, cur_relo = cur_relo->next) { + relos[i] = &cur_relo->relo; + cur_pkt->relo_chain = cur_relo->next; + } + + /* Do the actual relo */ + ret_val = priv_pkt_relo(dlthis, + cur_pkt->img_pkt.i_bits, + relos, i); + if (ret_val == 0) { + dload_error(dlthis, + "Relocation of dup pkt at %x failed", + cur_pkt->offset + dlthis->image_secn-> + run_addr); + break; + } + + /* Release all of these relos, we're done with them */ + while (i > 0) { + dlthis->mysym->Deallocate(dlthis->mysym, + GET_CONTAINER(relos[i - 1], + struct tramp_img_dup_relo, relo)); + i--; + } + + /* DO NOT ADVANCE cur_relo, IT IS ALREADY READY TO + * GO! */ + } + + /* Done with all relos. Make sure we didn't have a problem and + * write it out to the target */ + if (ret_val != 0) { + ret_val = dlthis->myio->writemem(dlthis->myio, + cur_pkt->img_pkt.i_bits, + dlthis->image_secn->load_addr + + cur_pkt->offset, dlthis->image_secn, + BYTE_TO_HOST(cur_pkt-> + img_pkt.i_packet_size)); + if (ret_val == 0) { + dload_error(dlthis, + "Write to " FMT_UI32 " failed", + dlthis->image_secn->load_addr + + cur_pkt->offset); + } + + dlthis->mysym->Deallocate(dlthis->mysym, cur_pkt); + + /* Advance to the next packet */ + cur_pkt = dlthis->tramp.dup_pkts; + } + } + + return ret_val; +} + +/* + * Function: priv_dup_find + * Description: Walk the list of existing duplicate packets and find a + * match based on the section number and image offset. Return + * the duplicate packet if found, otherwise NULL. + */ +static struct tramp_img_dup_pkt *priv_dup_find(struct dload_state *dlthis, + s16 secnn, u32 image_offset) +{ + struct tramp_img_dup_pkt *cur_pkt = NULL; + + for (cur_pkt = dlthis->tramp.dup_pkts; + cur_pkt != NULL; + cur_pkt = cur_pkt->next) { + if ((cur_pkt->secnn == secnn) && + (cur_pkt->offset == image_offset)) { + /* Found a match, break out */ + break; + } + } + + return cur_pkt; +} + +/* + * Function: priv_img_pkt_dup + * Description: Duplicate the original image packet. If this is the first + * time this image packet has been seen (based on section number + * and image offset), create a new duplicate packet and add it + * to the dup packet list. If not, just get the existing one and + * update it with the current packet contents (since relocation + * on the packet is still ongoing in first pass.) Create a + * duplicate of the provided relocation, but update it to point + * to the new trampoline symbol. Add the new relocation dup to + * the dup packet's relo chain for 2nd pass relocation later. + */ +static int priv_img_pkt_dup(struct dload_state *dlthis, + s16 secnn, u32 image_offset, struct image_packet_t *ipacket, + struct reloc_record_t *rp, struct tramp_sym *new_tramp_sym) +{ + struct tramp_img_dup_pkt *dup_pkt = NULL; + u32 new_dup_size; + s32 i; + int ret_val = 0; + struct tramp_img_dup_relo *dup_relo = NULL; + + /* Determinne if this image packet is already being tracked in the + dup list for other trampolines. */ + dup_pkt = priv_dup_find(dlthis, secnn, image_offset); + + if (dup_pkt == NULL) { + /* This image packet does not exist in our tracking, so create + * a new one and add it to the head of the list. */ + new_dup_size = sizeof(struct tramp_img_dup_pkt) + + ipacket->i_packet_size; + + dup_pkt = (struct tramp_img_dup_pkt *) + dlthis->mysym->Allocate(dlthis->mysym, new_dup_size); + if (dup_pkt != NULL) { + /* Save off the section and offset information */ + dup_pkt->secnn = secnn; + dup_pkt->offset = image_offset; + dup_pkt->relo_chain = NULL; + + /* Copy the original packet content */ + dup_pkt->img_pkt = *ipacket; + dup_pkt->img_pkt.i_bits = (u8 *)(dup_pkt + 1); + for (i = 0; i < ipacket->i_packet_size; i++) + *(dup_pkt->img_pkt.i_bits + i) = + *(ipacket->i_bits + i); + + /* Add the packet to the dup list */ + dup_pkt->next = dlthis->tramp.dup_pkts; + dlthis->tramp.dup_pkts = dup_pkt; + } else + dload_error(dlthis, "Failed to create dup packet!"); + } else { + /* The image packet contents could have changed since + * trampoline detection happens during relocation of the image + * packets. So, we need to update the image packet contents + * before adding relo information. */ + for (i = 0; i < dup_pkt->img_pkt.i_packet_size; i++) + *(dup_pkt->img_pkt.i_bits + i) = + *(ipacket->i_bits + i); + } + + /* Since the previous code may have allocated a new dup packet for us, + double check that we actually have one. */ + if (dup_pkt != NULL) { + /* Allocate a new node for the relo chain. Each image packet + * can potentially have multiple relocations that cause a + * trampoline to be generated. So, we keep them in a chain, + * order is not important. */ + dup_relo = dlthis->mysym->Allocate(dlthis->mysym, + sizeof(struct tramp_img_dup_relo)); + if (dup_relo != NULL) { + /* Copy the relo contents, adjust for the new + * trampoline and add it to the list. */ + dup_relo->relo = *rp; + dup_relo->relo.r_symndx = new_tramp_sym->index; + + dup_relo->next = dup_pkt->relo_chain; + dup_pkt->relo_chain = dup_relo; + + /* That's it, we're done. Make sure we update our + * return value to be success since everything finished + * ok */ + ret_val = 1; + } else + dload_error(dlthis, "Unable to alloc dup relo"); + } + + return ret_val; +} + +/* + * Function: dload_tramp_avail + * Description: Check to see if the target supports a trampoline for this type + * of relocation. Return true if it does, otherwise false. + */ +bool dload_tramp_avail(struct dload_state *dlthis, struct reloc_record_t *rp) +{ + bool ret_val = false; + u16 map_index; + u16 gen_index; + + /* Check type hash vs. target tramp table */ + map_index = HASH_FUNC(rp->r_type); + gen_index = tramp_map[map_index]; + if (gen_index != TRAMP_NO_GEN_AVAIL) + ret_val = true; + + return ret_val; +} + +/* + * Function: dload_tramp_generate + * Description: Create a new trampoline for the provided image packet and + * relocation causing problems. This will create the trampoline + * as well as duplicate/update the image packet and relocation + * causing the problem, which will be relo'd again during + * finalization. + */ +int dload_tramp_generate(struct dload_state *dlthis, s16 secnn, + u32 image_offset, struct image_packet_t *ipacket, + struct reloc_record_t *rp) +{ + u16 map_index; + u16 gen_index; + int ret_val = 1; + char tramp_sym_str[TRAMP_SYM_PREFIX_LEN + TRAMP_SYM_HEX_ASCII_LEN]; + struct Local_Symbol *ref_sym; + struct tramp_sym *new_tramp_sym; + struct tramp_sym *new_ext_sym; + struct tramp_string *new_tramp_str; + u32 new_tramp_base; + struct Local_Symbol tmp_sym; + struct Local_Symbol ext_tmp_sym; + + /* Hash the relo type to get our generator information */ + map_index = HASH_FUNC(rp->r_type); + gen_index = tramp_map[map_index]; + if (gen_index != TRAMP_NO_GEN_AVAIL) { + /* If this is the first trampoline, create the section name in + * our string table for debug help later. */ + if (dlthis->tramp.string_head == NULL) { + priv_tramp_string_create(dlthis, + strlen(TRAMP_SECT_NAME), TRAMP_SECT_NAME); + } + +#ifdef ENABLE_TRAMP_DEBUG + dload_syms_error(dlthis->mysym, + "Trampoline at img loc %x, references %x", + dlthis->ldr_sections[secnn].run_addr + image_offset + + rp->r_vaddr, + dlthis->local_symtab[rp->r_symndx].value); +#endif + + /* Generate the trampoline string, check if already defined. + * If the relo symbol index is -1, it means we need the section + * info for relo later. To do this we'll dummy up a symbol + * with the section delta and run addresses. */ + if (rp->r_symndx == -1) { + ext_tmp_sym.value = + dlthis->ldr_sections[secnn].run_addr; + ext_tmp_sym.delta = dlthis->sect_hdrs[secnn].ds_paddr; + ref_sym = &ext_tmp_sym; + } else + ref_sym = &(dlthis->local_symtab[rp->r_symndx]); + + priv_tramp_sym_gen_name(ref_sym->value, tramp_sym_str); + new_tramp_sym = priv_tramp_sym_find(dlthis, tramp_sym_str); + if (new_tramp_sym == NULL) { + /* If tramp string not defined, create it and a new + * string, and symbol for it as well as the original + * symbol which caused the trampoline. */ + new_tramp_str = priv_tramp_string_create(dlthis, + strlen(tramp_sym_str), tramp_sym_str); + if (new_tramp_str == NULL) { + dload_error(dlthis, "Failed to create new " + "trampoline string\n"); + ret_val = 0; + } else { + /* Allocate tramp section space for the new + * tramp from the target */ + new_tramp_base = priv_tramp_sect_alloc(dlthis, + tramp_size_get()); + + /* We have a string, create the new symbol and + * duplicate the external. */ + tmp_sym.value = new_tramp_base; + tmp_sym.delta = 0; + tmp_sym.secnn = -1; + tmp_sym.sclass = 0; + new_tramp_sym = priv_tramp_sym_create(dlthis, + new_tramp_str->index, &tmp_sym); + + new_ext_sym = priv_tramp_sym_create(dlthis, + -1, ref_sym); + + if ((new_tramp_sym != NULL) && + (new_ext_sym != NULL)) { + /* Call the image generator to get the + * new image data and fix up its + * relocations for the external + * symbol. */ + ret_val = priv_tgt_img_gen(dlthis, + new_tramp_base, gen_index, + new_ext_sym); + + /* Add generated image data to tramp + * image list */ + if (ret_val != 1) { + dload_error(dlthis, "Failed to" + " create image packet for " + "trampoline\n"); + } + } else { + dload_error(dlthis, "Failed to create " + "new tramp syms (%8.8X, %8.8X)\n", + new_tramp_sym, new_ext_sym); + ret_val = 0; + } + } + } + + /* Duplicate the image data and relo record that caused the + * tramp, including update the relo data to point to the tramp + * symbol. */ + if (ret_val == 1) { + ret_val = priv_img_pkt_dup(dlthis, secnn, image_offset, + ipacket, rp, new_tramp_sym); + if (ret_val != 1) { + dload_error(dlthis, "Failed to create dup of " + "original img pkt\n"); + } + } + } + + return ret_val; +} + +/* + * Function: dload_tramp_pkt_update + * Description: Update the duplicate copy of this image packet, which the + * trampoline layer is already tracking. This is call is critical + * to make if trampolines were generated anywhere within the + * packet and first pass relo continued on the remainder. The + * trampoline layer needs the updates image data so when 2nd + * pass relo is done during finalize the image packet can be + * written to the target since all relo is done. + */ +int dload_tramp_pkt_udpate(struct dload_state *dlthis, s16 secnn, + u32 image_offset, struct image_packet_t *ipacket) +{ + struct tramp_img_dup_pkt *dup_pkt = NULL; + s32 i; + int ret_val = 0; + + /* Find the image packet in question, the caller needs us to update it + since a trampoline was previously generated. */ + dup_pkt = priv_dup_find(dlthis, secnn, image_offset); + if (dup_pkt != NULL) { + for (i = 0; i < dup_pkt->img_pkt.i_packet_size; i++) + *(dup_pkt->img_pkt.i_bits + i) = *(ipacket->i_bits + i); + + ret_val = 1; + } else { + dload_error(dlthis, + "Unable to find existing DUP pkt for %x, offset %x", + secnn, image_offset); + + } + + return ret_val; +} + +/* + * Function: dload_tramp_finalize + * Description: If any trampolines were created, finalize everything on the + * target by allocating the trampoline section on the target, + * finalizing the trampoline symbols, finalizing the trampoline + * packets (write the new section to target memory) and finalize + * the duplicate packets by doing 2nd pass relo over them. + */ +int dload_tramp_finalize(struct dload_state *dlthis) +{ + int ret_val = 1; + + if (dlthis->tramp.tramp_sect_next_addr != 0) { + /* Finalize strings into a flat table. This is needed so it + * can be added to the debug string table later. */ + ret_val = priv_string_tbl_finalize(dlthis); + + /* Do target allocation for section BEFORE finalizing + * symbols. */ + if (ret_val != 0) + ret_val = priv_tramp_sect_tgt_alloc(dlthis); + + /* Finalize symbols with their correct target information and + * flatten */ + if (ret_val != 0) + ret_val = priv_tramp_sym_finalize(dlthis); + + /* Finalize all trampoline packets. This performs the + * relocation on the packets as well as writing them to target + * memory. */ + if (ret_val != 0) + ret_val = priv_tramp_pkt_finalize(dlthis); + + /* Perform a 2nd pass relocation on the dup list. */ + if (ret_val != 0) + ret_val = priv_dup_pkt_finalize(dlthis); + } + + return ret_val; +} + +/* + * Function: dload_tramp_cleanup + * Description: Release all temporary resources used in the trampoline layer. + * Note that the target memory which may have been allocated and + * written to store the trampolines is NOT RELEASED HERE since it + * is potentially still in use. It is automatically released + * when the module is unloaded. + */ +void dload_tramp_cleanup(struct dload_state *dlthis) +{ + struct tramp_info *tramp = &dlthis->tramp; + struct tramp_sym *cur_sym; + struct tramp_string *cur_string; + struct tramp_img_pkt *cur_tramp_pkt; + struct tramp_img_dup_pkt *cur_dup_pkt; + struct tramp_img_dup_relo *cur_dup_relo; + + /* If there were no tramps generated, just return */ + if (tramp->tramp_sect_next_addr == 0) + return; + + /* Destroy all tramp information */ + for (cur_sym = tramp->symbol_head; + cur_sym != NULL; + cur_sym = tramp->symbol_head) { + tramp->symbol_head = cur_sym->next; + if (tramp->symbol_tail == cur_sym) + tramp->symbol_tail = NULL; + + dlthis->mysym->Deallocate(dlthis->mysym, cur_sym); + } + + if (tramp->final_sym_table != NULL) + dlthis->mysym->Deallocate(dlthis->mysym, + tramp->final_sym_table); + + for (cur_string = tramp->string_head; + cur_string != NULL; + cur_string = tramp->string_head) { + tramp->string_head = cur_string->next; + if (tramp->string_tail == cur_string) + tramp->string_tail = NULL; + + dlthis->mysym->Deallocate(dlthis->mysym, cur_string); + } + + if (tramp->final_string_table != NULL) + dlthis->mysym->Deallocate(dlthis->mysym, + tramp->final_string_table); + + for (cur_tramp_pkt = tramp->tramp_pkts; + cur_tramp_pkt != NULL; + cur_tramp_pkt = tramp->tramp_pkts) { + tramp->tramp_pkts = cur_tramp_pkt->next; + dlthis->mysym->Deallocate(dlthis->mysym, cur_tramp_pkt); + } + + for (cur_dup_pkt = tramp->dup_pkts; + cur_dup_pkt != NULL; + cur_dup_pkt = tramp->dup_pkts) { + tramp->dup_pkts = cur_dup_pkt->next; + + for (cur_dup_relo = cur_dup_pkt->relo_chain; + cur_dup_relo != NULL; + cur_dup_relo = cur_dup_pkt->relo_chain) { + cur_dup_pkt->relo_chain = cur_dup_relo->next; + dlthis->mysym->Deallocate(dlthis->mysym, cur_dup_relo); + } + + dlthis->mysym->Deallocate(dlthis->mysym, cur_dup_pkt); + } +} diff --git a/drivers/dsp/bridge/dynload/tramp_table_c6000.c b/drivers/dsp/bridge/dynload/tramp_table_c6000.c new file mode 100644 index 0000000..1a7d974 --- /dev/null +++ b/drivers/dsp/bridge/dynload/tramp_table_c6000.c @@ -0,0 +1,164 @@ +/* + * Copyright 2009 by Texas Instruments Incorporated. + * All rights reserved. Property of Texas Instruments Incorporated. + * Restricted rights to use, duplicate or disclose this code are + * granted through contract. + * + * @(#) DSP/BIOS Bridge + */ +#include "dload_internal.h" + +/* These are defined in coff.h, but may not be available on all platforms + so we'll go ahead and define them here. */ +#ifndef R_C60LO16 +#define R_C60LO16 0x54 /* C60: MVK Low Half Register */ +#define R_C60HI16 0x55 /* C60: MVKH/MVKLH High Half Register */ +#endif + +#define C6X_TRAMP_WORD_COUNT 8 +#define C6X_TRAMP_MAX_RELOS 8 + +/* THIS HASH FUNCTION MUST MATCH THE ONE IN reloc_table_c6000.c */ +#define HASH_FUNC(zz) (((((zz) + 1) * UINT32_C(1845)) >> 11) & 63) + + +/* THIS MUST MATCH reloc_record_t FOR A SYMBOL BASED RELO */ +struct c6000_relo_record { + s32 r_vaddr; + s32 symndx; +#ifndef _BIG_ENDIAN + u16 disp; + u16 type; +#else + u16 type; + u16 disp; +#endif +}; + +struct c6000_gen_code { + struct tramp_gen_code_hdr hdr; + u32 tramp_instrs[C6X_TRAMP_WORD_COUNT]; + struct c6000_relo_record relos[C6X_TRAMP_MAX_RELOS]; +}; + + +/* Hash mapping for relos that can cause trampolines. */ +static const u16 tramp_map[] = +{ + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 0, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535, + 65535 +}; + + +static const struct c6000_gen_code tramp_gen_info[] = +{ + /* Tramp caused by R_C60PCR21 */ + { + /* Header - 8 instructions, 2 relos */ + { + sizeof(u32) * C6X_TRAMP_WORD_COUNT, + 2, + FIELD_OFFSET(struct c6000_gen_code, relos) + }, + + /* Trampoline instructions */ + { + 0x053C54F7, /* STW.D2T2 B10, *sp--[2] */ + 0x0500002A, /* || MVK.S2 <blank>, B10 */ + 0x0500006A, /* MVKH.S2 <blank>, B10 */ + 0x00280362, /* B.S2 B10 */ + 0x053C52E6, /* LDW.D2T2 *++sp[2], B10 */ + 0x00006000, /* NOP 4 */ + 0x00000000, /* NOP */ + 0x00000000 /* NOP */ + }, + + /* Relocations */ + { + { 4, 0, 0, R_C60LO16 }, + { 8, 0, 0, R_C60HI16 }, + { 0, 0, 0, 0x0000 }, + { 0, 0, 0, 0x0000 }, + { 0, 0, 0, 0x0000 }, + { 0, 0, 0, 0x0000 }, + { 0, 0, 0, 0x0000 }, + { 0, 0, 0, 0x0000 } + } + } +}; + + + +/* TARGET SPECIFIC FUNCTIONS THAT MUST BE DEFINED */ +static u32 tramp_size_get(void) +{ + return sizeof(u32) * C6X_TRAMP_WORD_COUNT; +} + + +static u32 tramp_img_pkt_size_get(void) +{ + return sizeof(struct c6000_gen_code); +} -- 1.6.2.4 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html