| /* |
| * tramp.c |
| * |
| * DSP-BIOS Bridge driver support functions for TI OMAP processors. |
| * |
| * Copyright (C) 2009 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 |
| #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->dload_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; |
| char *prefix = TRAMP_SYM_PREFIX; |
| 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->dload_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->dload_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->dload_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->dload_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->dload_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->dload_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->dload_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].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, tgt_au_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-> |
| 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, |
| (tgt_au_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->dload_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.img_data, |
| 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->dload_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. |
| img_data, |
| dlthis->image_secn-> |
| load_addr + |
| cur_pkt->offset, |
| dlthis->image_secn, |
| BYTE_TO_HOST |
| (cur_pkt->img_pkt. |
| packet_size)); |
| if (ret_val == 0) { |
| dload_error(dlthis, |
| "Write to " FMT_UI32 " failed", |
| dlthis->image_secn->load_addr + |
| cur_pkt->offset); |
| } |
| |
| dlthis->mysym->dload_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->packet_size; |
| |
| dup_pkt = (struct tramp_img_dup_pkt *) |
| dlthis->mysym->dload_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.img_data = (u8 *) (dup_pkt + 1); |
| for (i = 0; i < ipacket->packet_size; i++) |
| *(dup_pkt->img_pkt.img_data + i) = |
| *(ipacket->img_data + 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.packet_size; i++) |
| *(dup_pkt->img_pkt.img_data + i) = |
| *(ipacket->img_data + 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->dload_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.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->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->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->vaddr, |
| dlthis->local_symtab[rp->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->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->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 img pkt 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.packet_size; i++) |
| *(dup_pkt->img_pkt.img_data + i) = |
| *(ipacket->img_data + 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->dload_deallocate(dlthis->mysym, cur_sym); |
| } |
| |
| if (tramp->final_sym_table != NULL) |
| dlthis->mysym->dload_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->dload_deallocate(dlthis->mysym, cur_string); |
| } |
| |
| if (tramp->final_string_table != NULL) |
| dlthis->mysym->dload_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->dload_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->dload_deallocate(dlthis->mysym, |
| cur_dup_relo); |
| } |
| |
| dlthis->mysym->dload_deallocate(dlthis->mysym, cur_dup_pkt); |
| } |
| } |