diff options
author | Omar Ramirez Luna <omar.ramirez@ti.com> | 2010-06-23 16:02:02 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-06-23 15:39:08 -0700 |
commit | 26f8db7d2e20218272cbf889edfdb8328b4cd9c3 (patch) | |
tree | 67a45300822a63cddae83fb8d120a3001fd830ff /drivers/staging/tidspbridge/dynload/cload.c | |
parent | 6a88a4fe0937325f1fc3df835f3e9b1698992899 (diff) | |
download | linux-3.10-26f8db7d2e20218272cbf889edfdb8328b4cd9c3.tar.gz linux-3.10-26f8db7d2e20218272cbf889edfdb8328b4cd9c3.tar.bz2 linux-3.10-26f8db7d2e20218272cbf889edfdb8328b4cd9c3.zip |
staging: ti dspbridge: add DOFF binaries loader
Add TI's DSP Bridge DOFF binaries dynamic loader driver sources
Signed-off-by: Omar Ramirez Luna <omar.ramirez@ti.com>
Signed-off-by: Kanigeri, Hari <h-kanigeri2@ti.com>
Signed-off-by: Ameya Palande <ameya.palande@nokia.com>
Signed-off-by: Guzman Lugo, Fernando <fernando.lugo@ti.com>
Signed-off-by: Hebbar, Shivananda <x0hebbar@ti.com>
Signed-off-by: Ramos Falcon, Ernesto <ernesto@ti.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Anna, Suman <s-anna@ti.com>
Signed-off-by: Gupta, Ramesh <grgupta@ti.com>
Signed-off-by: Gomez Castellanos, Ivan <ivan.gomez@ti.com>
Signed-off-by: Andy Shevchenko <ext-andriy.shevchenko@nokia.com>
Signed-off-by: Armando Uribe De Leon <x0095078@ti.com>
Signed-off-by: Deepak Chitriki <deepak.chitriki@ti.com>
Signed-off-by: Menon, Nishanth <nm@ti.com>
Signed-off-by: Phil Carmody <ext-phil.2.carmody@nokia.com>
Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/tidspbridge/dynload/cload.c')
-rw-r--r-- | drivers/staging/tidspbridge/dynload/cload.c | 1960 |
1 files changed, 1960 insertions, 0 deletions
diff --git a/drivers/staging/tidspbridge/dynload/cload.c b/drivers/staging/tidspbridge/dynload/cload.c new file mode 100644 index 00000000000..d4f71b585a5 --- /dev/null +++ b/drivers/staging/tidspbridge/dynload/cload.c @@ -0,0 +1,1960 @@ +/* + * cload.c + * + * DSP-BIOS Bridge driver support functions for TI OMAP processors. + * + * Copyright (C) 2005-2006 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "header.h" + +#include "module_list.h" +#define LINKER_MODULES_HEADER ("_" MODULES_HEADER) + +/* + * we use the fact that DOFF section records are shaped just like + * ldr_section_info to reduce our section storage usage. This macro marks + * the places where that assumption is made + */ +#define DOFFSEC_IS_LDRSEC(pdoffsec) ((struct ldr_section_info *)(pdoffsec)) + +/* + * forward references + */ +static void dload_symbols(struct dload_state *dlthis); +static void dload_data(struct dload_state *dlthis); +static void allocate_sections(struct dload_state *dlthis); +static void string_table_free(struct dload_state *dlthis); +static void symbol_table_free(struct dload_state *dlthis); +static void section_table_free(struct dload_state *dlthis); +static void init_module_handle(struct dload_state *dlthis); +#if BITS_PER_AU > BITS_PER_BYTE +static char *unpack_name(struct dload_state *dlthis, u32 soffset); +#endif + +static const char cinitname[] = { ".cinit" }; +static const char loader_dllview_root[] = { "?DLModules?" }; + +/* + * Error strings + */ +static const char readstrm[] = { "Error reading %s from input stream" }; +static const char err_alloc[] = { "Syms->dload_allocate( %d ) failed" }; +static const char tgtalloc[] = { + "Target memory allocate failed, section %s size " FMT_UI32 }; +static const char initfail[] = { "%s to target address " FMT_UI32 " failed" }; +static const char dlvwrite[] = { "Write to DLLview list failed" }; +static const char iconnect[] = { "Connect call to init interface failed" }; +static const char err_checksum[] = { "Checksum failed on %s" }; + +/************************************************************************* + * Procedure dload_error + * + * Parameters: + * errtxt description of the error, printf style + * ... additional information + * + * Effect: + * Reports or records the error as appropriate. + *********************************************************************** */ +void dload_error(struct dload_state *dlthis, const char *errtxt, ...) +{ + va_list args; + + va_start(args, errtxt); + dlthis->mysym->error_report(dlthis->mysym, errtxt, args); + va_end(args); + dlthis->dload_errcount += 1; + +} /* dload_error */ + +#define DL_ERROR(zza, zzb) dload_error(dlthis, zza, zzb) + +/************************************************************************* + * Procedure dload_syms_error + * + * Parameters: + * errtxt description of the error, printf style + * ... additional information + * + * Effect: + * Reports or records the error as appropriate. + *********************************************************************** */ +void dload_syms_error(struct dynamic_loader_sym *syms, const char *errtxt, ...) +{ + va_list args; + + va_start(args, errtxt); + syms->error_report(syms, errtxt, args); + va_end(args); +} + +/************************************************************************* + * Procedure dynamic_load_module + * + * Parameters: + * module The input stream that supplies the module image + * syms Host-side symbol table and malloc/free functions + * alloc Target-side memory allocation + * init Target-side memory initialization + * options Option flags DLOAD_* + * mhandle A module handle for use with Dynamic_Unload + * + * Effect: + * The module image is read using *module. Target storage for the new + * image is + * obtained from *alloc. Symbols defined and referenced by the module are + * managed using *syms. The image is then relocated and references + * resolved as necessary, and the resulting executable bits are placed + * into target memory using *init. + * + * Returns: + * On a successful load, a module handle is placed in *mhandle, + * and zero is returned. On error, the number of errors detected is + * returned. Individual errors are reported during the load process + * using syms->error_report(). + ********************************************************************** */ +int dynamic_load_module(struct dynamic_loader_stream *module, + struct dynamic_loader_sym *syms, + struct dynamic_loader_allocate *alloc, + struct dynamic_loader_initialize *init, + unsigned options, void **mhandle) +{ + register unsigned *dp, sz; + struct dload_state dl_state; /* internal state for this call */ + + /* blast our internal state */ + dp = (unsigned *)&dl_state; + for (sz = sizeof(dl_state) / sizeof(unsigned); sz > 0; sz -= 1) + *dp++ = 0; + + /* Enable _only_ BSS initialization if enabled by user */ + if ((options & DLOAD_INITBSS) == DLOAD_INITBSS) + dl_state.myoptions = DLOAD_INITBSS; + + /* Check that mandatory arguments are present */ + if (!module || !syms) { + dload_error(&dl_state, "Required parameter is NULL"); + } else { + dl_state.strm = module; + dl_state.mysym = syms; + dload_headers(&dl_state); + if (!dl_state.dload_errcount) + dload_strings(&dl_state, false); + if (!dl_state.dload_errcount) + dload_sections(&dl_state); + + if (init && !dl_state.dload_errcount) { + if (init->connect(init)) { + dl_state.myio = init; + dl_state.myalloc = alloc; + /* do now, before reducing symbols */ + allocate_sections(&dl_state); + } else + dload_error(&dl_state, iconnect); + } + + if (!dl_state.dload_errcount) { + /* fix up entry point address */ + unsigned sref = dl_state.dfile_hdr.df_entry_secn - 1; + if (sref < dl_state.allocated_secn_count) + dl_state.dfile_hdr.df_entrypt += + dl_state.ldr_sections[sref].run_addr; + + dload_symbols(&dl_state); + } + + if (init && !dl_state.dload_errcount) + dload_data(&dl_state); + + init_module_handle(&dl_state); + + /* dl_state.myio is init or 0 at this point. */ + if (dl_state.myio) { + if ((!dl_state.dload_errcount) && + (dl_state.dfile_hdr.df_entry_secn != DN_UNDEF) && + (!init->execute(init, + dl_state.dfile_hdr.df_entrypt))) + dload_error(&dl_state, "Init->Execute Failed"); + init->release(init); + } + + 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, + init); + dl_state.myhandle = NULL; + } + } + + if (mhandle) + *mhandle = dl_state.myhandle; /* give back the handle */ + + return dl_state.dload_errcount; +} /* DLOAD_File */ + +/************************************************************************* + * Procedure dynamic_open_module + * + * Parameters: + * module The input stream that supplies the module image + * syms Host-side symbol table and malloc/free functions + * alloc Target-side memory allocation + * init Target-side memory initialization + * options Option flags DLOAD_* + * mhandle A module handle for use with Dynamic_Unload + * + * Effect: + * The module image is read using *module. Target storage for the new + * image is + * obtained from *alloc. Symbols defined and referenced by the module are + * managed using *syms. The image is then relocated and references + * resolved as necessary, and the resulting executable bits are placed + * into target memory using *init. + * + * Returns: + * On a successful load, a module handle is placed in *mhandle, + * and zero is returned. On error, the number of errors detected is + * returned. Individual errors are reported during the load process + * using syms->error_report(). + ********************************************************************** */ +int +dynamic_open_module(struct dynamic_loader_stream *module, + struct dynamic_loader_sym *syms, + struct dynamic_loader_allocate *alloc, + struct dynamic_loader_initialize *init, + unsigned options, void **mhandle) +{ + register unsigned *dp, sz; + struct dload_state dl_state; /* internal state for this call */ + + /* blast our internal state */ + dp = (unsigned *)&dl_state; + for (sz = sizeof(dl_state) / sizeof(unsigned); sz > 0; sz -= 1) + *dp++ = 0; + + /* Enable _only_ BSS initialization if enabled by user */ + if ((options & DLOAD_INITBSS) == DLOAD_INITBSS) + dl_state.myoptions = DLOAD_INITBSS; + + /* Check that mandatory arguments are present */ + if (!module || !syms) { + dload_error(&dl_state, "Required parameter is NULL"); + } else { + dl_state.strm = module; + dl_state.mysym = syms; + dload_headers(&dl_state); + if (!dl_state.dload_errcount) + dload_strings(&dl_state, false); + if (!dl_state.dload_errcount) + dload_sections(&dl_state); + + if (init && !dl_state.dload_errcount) { + if (init->connect(init)) { + dl_state.myio = init; + dl_state.myalloc = alloc; + /* do now, before reducing symbols */ + allocate_sections(&dl_state); + } else + dload_error(&dl_state, iconnect); + } + + if (!dl_state.dload_errcount) { + /* fix up entry point address */ + unsigned sref = dl_state.dfile_hdr.df_entry_secn - 1; + if (sref < dl_state.allocated_secn_count) + dl_state.dfile_hdr.df_entrypt += + dl_state.ldr_sections[sref].run_addr; + + dload_symbols(&dl_state); + } + + init_module_handle(&dl_state); + + /* dl_state.myio is either 0 or init at this point. */ + if (dl_state.myio) { + if ((!dl_state.dload_errcount) && + (dl_state.dfile_hdr.df_entry_secn != DN_UNDEF) && + (!init->execute(init, + dl_state.dfile_hdr.df_entrypt))) + dload_error(&dl_state, "Init->Execute Failed"); + init->release(init); + } + + symbol_table_free(&dl_state); + section_table_free(&dl_state); + string_table_free(&dl_state); + + if (dl_state.dload_errcount) { + dynamic_unload_module(dl_state.myhandle, syms, alloc, + init); + dl_state.myhandle = NULL; + } + } + + if (mhandle) + *mhandle = dl_state.myhandle; /* give back the handle */ + + return dl_state.dload_errcount; +} /* DLOAD_File */ + +/************************************************************************* + * Procedure dload_headers + * + * Parameters: + * none + * + * Effect: + * Loads the DOFF header and verify record. Deals with any byte-order + * issues and checks them for validity. + *********************************************************************** */ +#define COMBINED_HEADER_SIZE (sizeof(struct doff_filehdr_t)+ \ + sizeof(struct doff_verify_rec_t)) + +void dload_headers(struct dload_state *dlthis) +{ + u32 map; + + /* Read the header and the verify record as one. If we don't get it + all, we're done */ + if (dlthis->strm->read_buffer(dlthis->strm, &dlthis->dfile_hdr, + COMBINED_HEADER_SIZE) != + COMBINED_HEADER_SIZE) { + DL_ERROR(readstrm, "File Headers"); + return; + } + /* + * Verify that we have the byte order of the file correct. + * If not, must fix it before we can continue + */ + map = REORDER_MAP(dlthis->dfile_hdr.df_byte_reshuffle); + if (map != REORDER_MAP(BYTE_RESHUFFLE_VALUE)) { + /* input is either byte-shuffled or bad */ + if ((map & 0xFCFCFCFC) == 0) { /* no obviously bogus bits */ + dload_reorder(&dlthis->dfile_hdr, COMBINED_HEADER_SIZE, + map); + } + if (dlthis->dfile_hdr.df_byte_reshuffle != + BYTE_RESHUFFLE_VALUE) { + /* didn't fix the problem, the byte swap map is bad */ + dload_error(dlthis, + "Bad byte swap map " FMT_UI32 " in header", + dlthis->dfile_hdr.df_byte_reshuffle); + return; + } + dlthis->reorder_map = map; /* keep map for future use */ + } + + /* + * Verify checksum of header and verify record + */ + if (~dload_checksum(&dlthis->dfile_hdr, + sizeof(struct doff_filehdr_t)) || + ~dload_checksum(&dlthis->verify, + sizeof(struct doff_verify_rec_t))) { + DL_ERROR(err_checksum, "header or verify record"); + return; + } +#if HOST_ENDIANNESS + dlthis->dfile_hdr.df_byte_reshuffle = map; /* put back for later */ +#endif + + /* Check for valid target ID */ + if ((dlthis->dfile_hdr.df_target_id != TARGET_ID) && + -(dlthis->dfile_hdr.df_target_id != TMS470_ID)) { + dload_error(dlthis, "Bad target ID 0x%x and TARGET_ID 0x%x", + dlthis->dfile_hdr.df_target_id, TARGET_ID); + return; + } + /* Check for valid file format */ + if ((dlthis->dfile_hdr.df_doff_version != DOFF0)) { + dload_error(dlthis, "Bad DOFF version 0x%x", + dlthis->dfile_hdr.df_doff_version); + return; + } + + /* + * Apply reasonableness checks to count fields + */ + if (dlthis->dfile_hdr.df_strtab_size > MAX_REASONABLE_STRINGTAB) { + dload_error(dlthis, "Excessive string table size " FMT_UI32, + dlthis->dfile_hdr.df_strtab_size); + return; + } + if (dlthis->dfile_hdr.df_no_scns > MAX_REASONABLE_SECTIONS) { + dload_error(dlthis, "Excessive section count 0x%x", + dlthis->dfile_hdr.df_no_scns); + return; + } +#ifndef TARGET_ENDIANNESS + /* + * Check that endianness does not disagree with explicit specification + */ + if ((dlthis->dfile_hdr.df_flags >> ALIGN_COFF_ENDIANNESS) & + dlthis->myoptions & ENDIANNESS_MASK) { + dload_error(dlthis, + "Input endianness disagrees with specified option"); + return; + } + dlthis->big_e_target = dlthis->dfile_hdr.df_flags & DF_BIG; +#endif + +} /* dload_headers */ + +/* COFF Section Processing + * + * COFF sections are read in and retained intact. Each record is embedded + * in a new structure that records the updated load and + * run addresses of the section */ + +static const char secn_errid[] = { "section" }; + +/************************************************************************* + * Procedure dload_sections + * + * Parameters: + * none + * + * Effect: + * Loads the section records into an internal table. + *********************************************************************** */ +void dload_sections(struct dload_state *dlthis) +{ + s16 siz; + struct doff_scnhdr_t *shp; + unsigned nsecs = dlthis->dfile_hdr.df_no_scns; + + /* allocate space for the DOFF section records */ + siz = nsecs * sizeof(struct doff_scnhdr_t); + shp = + (struct doff_scnhdr_t *)dlthis->mysym->dload_allocate(dlthis->mysym, + siz); + if (!shp) { /* not enough storage */ + DL_ERROR(err_alloc, siz); + return; + } + dlthis->sect_hdrs = shp; + + /* read in the section records */ + if (dlthis->strm->read_buffer(dlthis->strm, shp, siz) != siz) { + DL_ERROR(readstrm, secn_errid); + return; + } + + /* if we need to fix up byte order, do it now */ + if (dlthis->reorder_map) + dload_reorder(shp, siz, dlthis->reorder_map); + + /* check for validity */ + if (~dload_checksum(dlthis->sect_hdrs, siz) != + dlthis->verify.dv_scn_rec_checksum) { + DL_ERROR(err_checksum, secn_errid); + return; + } + +} /* dload_sections */ + +/***************************************************************************** + * Procedure allocate_sections + * + * Parameters: + * alloc target memory allocator class + * + * Effect: + * Assigns new (target) addresses for sections + **************************************************************************** */ +static void allocate_sections(struct dload_state *dlthis) +{ + u16 curr_sect, nsecs, siz; + struct doff_scnhdr_t *shp; + struct ldr_section_info *asecs; + struct my_handle *hndl; + nsecs = dlthis->dfile_hdr.df_no_scns; + if (!nsecs) + return; + if ((dlthis->myalloc == NULL) && + (dlthis->dfile_hdr.df_target_scns > 0)) { + DL_ERROR("Arg 3 (alloc) required but NULL", 0); + return; + } + /* + * 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->dload_allocate(dlthis->mysym, + siz); + if (!hndl) { /* not enough storage */ + DL_ERROR(err_alloc, siz); + return; + } + /* initialize the handle header */ + hndl->dm.hnext = hndl->dm.hprev = hndl; /* circular list */ + hndl->dm.hroot = NULL; + hndl->dm.dbthis = 0; + dlthis->myhandle = hndl; /* save away for return */ + /* pointer to the section list of allocated sections */ + dlthis->ldr_sections = asecs = hndl->secns; + /* * Insert names into all sections, make copies of + the sections we allocate */ + shp = dlthis->sect_hdrs; + for (curr_sect = 0; curr_sect < nsecs; curr_sect++) { + u32 soffset = shp->ds_offset; +#if BITS_PER_AU <= BITS_PER_BYTE + /* attempt to insert the name of this section */ + if (soffset < dlthis->dfile_hdr.df_strtab_size) + DOFFSEC_IS_LDRSEC(shp)->name = dlthis->str_head + + soffset; + else { + dload_error(dlthis, "Bad name offset in section %d", + curr_sect); + DOFFSEC_IS_LDRSEC(shp)->name = NULL; + } +#endif + /* allocate target storage for sections that require it */ + if (DS_NEEDS_ALLOCATION(shp)) { + *asecs = *DOFFSEC_IS_LDRSEC(shp); + asecs->context = 0; /* zero the context field */ +#if BITS_PER_AU > BITS_PER_BYTE + asecs->name = unpack_name(dlthis, soffset); + dlthis->debug_string_size = soffset + dlthis->temp_len; +#else + dlthis->debug_string_size = soffset; +#endif + if (dlthis->myalloc != NULL) { + if (!dlthis->myalloc-> + dload_allocate(dlthis->myalloc, asecs, + DS_ALIGNMENT(asecs->type))) { + dload_error(dlthis, tgtalloc, + asecs->name, asecs->size); + return; + } + } + /* keep address deltas in original section table */ + shp->ds_vaddr = asecs->load_addr - shp->ds_vaddr; + shp->ds_paddr = asecs->run_addr - shp->ds_paddr; + dlthis->allocated_secn_count += 1; + } /* allocate target storage */ + shp += 1; + asecs += 1; + } +#if BITS_PER_AU <= BITS_PER_BYTE + dlthis->debug_string_size += + strlen(dlthis->str_head + dlthis->debug_string_size) + 1; +#endif +} /* allocate sections */ + +/************************************************************************* + * Procedure section_table_free + * + * Parameters: + * none + * + * Effect: + * Frees any state used by the symbol table. + * + * WARNING: + * This routine is not allowed to declare errors! + *********************************************************************** */ +static void section_table_free(struct dload_state *dlthis) +{ + struct doff_scnhdr_t *shp; + + shp = dlthis->sect_hdrs; + if (shp) + dlthis->mysym->dload_deallocate(dlthis->mysym, shp); + +} /* section_table_free */ + +/************************************************************************* + * Procedure dload_strings + * + * Parameters: + * sec_names_only If true only read in the "section names" + * portion of the string table + * + * Effect: + * Loads the DOFF string table into memory. DOFF keeps all strings in a + * big unsorted array. We just read that array into memory in bulk. + *********************************************************************** */ +static const char stringtbl[] = { "string table" }; + +void dload_strings(struct dload_state *dlthis, bool sec_names_only) +{ + u32 ssiz; + char *strbuf; + + if (sec_names_only) { + ssiz = BYTE_TO_HOST(DOFF_ALIGN + (dlthis->dfile_hdr.df_scn_name_size)); + } else { + ssiz = BYTE_TO_HOST(DOFF_ALIGN + (dlthis->dfile_hdr.df_strtab_size)); + } + if (ssiz == 0) + return; + + /* get some memory for the string table */ +#if BITS_PER_AU > BITS_PER_BYTE + strbuf = (char *)dlthis->mysym->dload_allocate(dlthis->mysym, ssiz + + dlthis->dfile_hdr. + df_max_str_len); +#else + strbuf = (char *)dlthis->mysym->dload_allocate(dlthis->mysym, ssiz); +#endif + if (strbuf == NULL) { + DL_ERROR(err_alloc, ssiz); + return; + } + dlthis->str_head = strbuf; +#if BITS_PER_AU > BITS_PER_BYTE + dlthis->str_temp = strbuf + ssiz; +#endif + /* read in the strings and verify them */ + if ((unsigned)(dlthis->strm->read_buffer(dlthis->strm, strbuf, + ssiz)) != ssiz) { + DL_ERROR(readstrm, stringtbl); + } + /* if we need to fix up byte order, do it now */ +#ifndef _BIG_ENDIAN + if (dlthis->reorder_map) + dload_reorder(strbuf, ssiz, dlthis->reorder_map); + + if ((!sec_names_only) && (~dload_checksum(strbuf, ssiz) != + dlthis->verify.dv_str_tab_checksum)) { + DL_ERROR(err_checksum, stringtbl); + } +#else + if (dlthis->dfile_hdr.df_byte_reshuffle != + HOST_BYTE_ORDER(REORDER_MAP(BYTE_RESHUFFLE_VALUE))) { + /* put strings in big-endian order, not in PC order */ + dload_reorder(strbuf, ssiz, + HOST_BYTE_ORDER(dlthis-> + dfile_hdr.df_byte_reshuffle)); + } + if ((!sec_names_only) && (~dload_reverse_checksum(strbuf, ssiz) != + dlthis->verify.dv_str_tab_checksum)) { + DL_ERROR(err_checksum, stringtbl); + } +#endif +} /* dload_strings */ + +/************************************************************************* + * Procedure string_table_free + * + * Parameters: + * none + * + * Effect: + * Frees any state used by the string table. + * + * WARNING: + * This routine is not allowed to declare errors! + ************************************************************************ */ +static void string_table_free(struct dload_state *dlthis) +{ + if (dlthis->str_head) + dlthis->mysym->dload_deallocate(dlthis->mysym, + dlthis->str_head); + +} /* string_table_free */ + +/* + * Symbol Table Maintenance Functions + * + * COFF symbols are read by dload_symbols(), which is called after + * sections have been allocated. Symbols which might be used in + * relocation (ie, not debug info) are retained in an internal temporary + * compressed table (type local_symbol). A particular symbol is recovered + * by index by calling dload_find_symbol(). dload_find_symbol + * reconstructs a more explicit representation (type SLOTVEC) which is + * used by reloc.c + */ +/* real size of debug header */ +#define DBG_HDR_SIZE (sizeof(struct dll_module) - sizeof(struct dll_sect)) + +static const char sym_errid[] = { "symbol" }; + +/************************************************************************** + * Procedure dload_symbols + * + * Parameters: + * none + * + * Effect: + * Reads in symbols and retains ones that might be needed for relocation + * purposes. + *********************************************************************** */ +/* size of symbol buffer no bigger than target data buffer, to limit stack + * usage */ +#define MY_SYM_BUF_SIZ (BYTE_TO_HOST(IMAGE_PACKET_SIZE)/\ + sizeof(struct doff_syment_t)) + +static void dload_symbols(struct dload_state *dlthis) +{ + u32 sym_count, siz, dsiz, symbols_left; + u32 checks; + struct local_symbol *sp; + struct dynload_symbol *symp; + struct dynload_symbol *newsym; + + sym_count = dlthis->dfile_hdr.df_no_syms; + if (sym_count == 0) + return; + + /* + * 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. 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 = sym_count * sizeof(struct local_symbol); + dsiz = DBG_HDR_SIZE + + (sizeof(struct dll_sect) * dlthis->allocated_secn_count) + + BYTE_TO_HOST_ROUND(dlthis->debug_string_size + 1); + if (dsiz > siz) + siz = dsiz; /* larger of symbols and .dllview temp */ + sp = (struct local_symbol *)dlthis->mysym->dload_allocate(dlthis->mysym, + siz); + if (!sp) { + DL_ERROR(err_alloc, siz); + return; + } + dlthis->local_symtab = sp; + /* Read the symbols in the input, store them in the table, and post any + * globals to the global symbol table. In the process, externals + become defined from the global symbol table */ + checks = dlthis->verify.dv_sym_tab_checksum; + symbols_left = sym_count; + do { /* read all symbols */ + char *sname; + u32 val; + s32 delta; + struct doff_syment_t *input_sym; + unsigned syms_in_buf; + struct doff_syment_t my_sym_buf[MY_SYM_BUF_SIZ]; + input_sym = my_sym_buf; + syms_in_buf = symbols_left > MY_SYM_BUF_SIZ ? + MY_SYM_BUF_SIZ : symbols_left; + siz = syms_in_buf * sizeof(struct doff_syment_t); + if (dlthis->strm->read_buffer(dlthis->strm, input_sym, siz) != + siz) { + DL_ERROR(readstrm, sym_errid); + return; + } + if (dlthis->reorder_map) + dload_reorder(input_sym, siz, dlthis->reorder_map); + + checks += dload_checksum(input_sym, siz); + do { /* process symbols in buffer */ + symbols_left -= 1; + /* attempt to derive the name of this symbol */ + sname = NULL; + if (input_sym->dn_offset > 0) { +#if BITS_PER_AU <= BITS_PER_BYTE + if ((u32) input_sym->dn_offset < + dlthis->dfile_hdr.df_strtab_size) + sname = dlthis->str_head + + BYTE_TO_HOST(input_sym->dn_offset); + else + dload_error(dlthis, + "Bad name offset in symbol " + " %d", symbols_left); +#else + sname = unpack_name(dlthis, + input_sym->dn_offset); +#endif + } + val = input_sym->dn_value; + delta = 0; + sp->sclass = input_sym->dn_sclass; + sp->secnn = input_sym->dn_scnum; + /* if this is an undefined symbol, + * define it (or fail) now */ + if (sp->secnn == DN_UNDEF) { + /* pointless for static undefined */ + if (input_sym->dn_sclass != DN_EXT) + goto loop_cont; + + /* try to define symbol from previously + * loaded images */ + symp = dlthis->mysym->find_matching_symbol + (dlthis->mysym, sname); + if (!symp) { + DL_ERROR + ("Undefined external symbol %s", + sname); + 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 */ + if (sp->secnn > 0) { + /* symbol references a section */ + if ((unsigned)sp->secnn <= + dlthis->allocated_secn_count) { + /* section was allocated */ + struct doff_scnhdr_t *srefp = + &dlthis->sect_hdrs[sp->secnn - 1]; + + if (input_sym->dn_sclass == + DN_STATLAB || + input_sym->dn_sclass == DN_EXTLAB) { + /* load */ + delta = srefp->ds_vaddr; + } else { + /* run */ + delta = srefp->ds_paddr; + } + val += delta; + } + goto loop_itr; + } + /* This symbol is an absolute symbol */ + if (sp->secnn == DN_ABS && ((sp->sclass == DN_EXT) || + (sp->sclass == + DN_EXTLAB))) { + symp = + dlthis->mysym->find_matching_symbol(dlthis-> + mysym, + sname); + if (!symp) + goto loop_itr; + /* This absolute symbol is already defined. */ + if (symp->value == input_sym->dn_value) { + /* If symbol values are equal, continue + * but don't add to the global symbol + * table */ + sp->value = val; + sp->delta = delta; + sp += 1; + input_sym += 1; + continue; + } else { + /* If symbol values are not equal, + * return with redefinition error */ + DL_ERROR("Absolute symbol %s is " + "defined multiple times with " + "different values", sname); + return; + } + } +loop_itr: + /* if this is a global symbol, post it to the + * global table */ + if (input_sym->dn_sclass == DN_EXT || + input_sym->dn_sclass == DN_EXTLAB) { + /* Keep this global symbol for subsequent + * modules. Don't complain on error, to allow + * symbol API to suppress global symbols */ + if (!sname) + goto loop_cont; + + newsym = dlthis->mysym->add_to_symbol_table + (dlthis->mysym, sname, + (unsigned)dlthis->myhandle); + if (newsym) + newsym->value = val; + + } /* global */ +loop_cont: + sp->value = val; + sp->delta = delta; + sp += 1; + input_sym += 1; + } while ((syms_in_buf -= 1) > 0); /* process sym in buf */ + } while (symbols_left > 0); /* read all symbols */ + if (~checks) + dload_error(dlthis, "Checksum of symbols failed"); + +} /* dload_symbols */ + +/***************************************************************************** + * Procedure symbol_table_free + * + * Parameters: + * none + * + * Effect: + * Frees any state used by the symbol table. + * + * WARNING: + * This routine is not allowed to declare errors! + **************************************************************************** */ +static void symbol_table_free(struct dload_state *dlthis) +{ + if (dlthis->local_symtab) { + if (dlthis->dload_errcount) { /* blow off our symbols */ + dlthis->mysym->purge_symbol_table(dlthis->mysym, + (unsigned) + dlthis->myhandle); + } + dlthis->mysym->dload_deallocate(dlthis->mysym, + dlthis->local_symtab); + } +} /* symbol_table_free */ + +/* .cinit Processing + * + * The dynamic loader does .cinit interpretation. cload_cinit() + * acts as a special write-to-target function, in that it takes relocated + * data from the normal data flow, and interprets it as .cinit actions. + * Because the normal data flow does not necessarily process the whole + * .cinit section in one buffer, cload_cinit() must be prepared to + * interpret the data piecemeal. A state machine is used for this + * purpose. + */ + +/* The following are only for use by reloc.c and things it calls */ +static const struct ldr_section_info cinit_info_init = { cinitname, 0, 0, + (ldr_addr)-1, 0, DLOAD_BSS, 0 +}; + +/************************************************************************* + * Procedure cload_cinit + * + * Parameters: + * ipacket Pointer to data packet to be loaded + * + * Effect: + * Interprets the data in the buffer as .cinit data, and performs the + * appropriate initializations. + *********************************************************************** */ +static void cload_cinit(struct dload_state *dlthis, + struct image_packet_t *ipacket) +{ +#if TDATA_TO_HOST(CINIT_COUNT)*BITS_PER_AU > 16 + s32 init_count, left; +#else + s16 init_count, left; +#endif + unsigned char *pktp = ipacket->img_data; + unsigned char *pktend = pktp + BYTE_TO_HOST_ROUND(ipacket->packet_size); + int temp; + ldr_addr atmp; + struct ldr_section_info cinit_info; + + /* PROCESS ALL THE INITIALIZATION RECORDS IN THE BUFFER. */ + while (true) { + left = pktend - pktp; + switch (dlthis->cinit_state) { + case CI_COUNT: /* count field */ + if (left < TDATA_TO_HOST(CINIT_COUNT)) + goto loopexit; + temp = dload_unpack(dlthis, (tgt_au_t *) pktp, + CINIT_COUNT * TDATA_AU_BITS, 0, + ROP_SGN); + pktp += TDATA_TO_HOST(CINIT_COUNT); + /* negative signifies BSS table, zero means done */ + if (temp <= 0) { + dlthis->cinit_state = CI_DONE; + break; + } + dlthis->cinit_count = temp; + dlthis->cinit_state = CI_ADDRESS; + break; +#if CINIT_ALIGN < CINIT_ADDRESS + case CI_PARTADDRESS: + pktp -= TDATA_TO_HOST(CINIT_ALIGN); + /* back up pointer into space courtesy of caller */ + *(uint16_t *) pktp = dlthis->cinit_addr; + /* stuff in saved bits !! FALL THRU !! */ +#endif + case CI_ADDRESS: /* Address field for a copy packet */ + if (left < TDATA_TO_HOST(CINIT_ADDRESS)) { +#if CINIT_ALIGN < CINIT_ADDRESS + if (left == TDATA_TO_HOST(CINIT_ALIGN)) { + /* address broken into halves */ + dlthis->cinit_addr = *(uint16_t *) pktp; + /* remember 1st half */ + dlthis->cinit_state = CI_PARTADDRESS; + left = 0; + } +#endif + goto loopexit; + } + atmp = dload_unpack(dlthis, (tgt_au_t *) pktp, + CINIT_ADDRESS * TDATA_AU_BITS, 0, + ROP_UNS); + pktp += TDATA_TO_HOST(CINIT_ADDRESS); +#if CINIT_PAGE_BITS > 0 + dlthis->cinit_page = atmp & + ((1 << CINIT_PAGE_BITS) - 1); + atmp >>= CINIT_PAGE_BITS; +#else + dlthis->cinit_page = CINIT_DEFAULT_PAGE; +#endif + dlthis->cinit_addr = atmp; + dlthis->cinit_state = CI_COPY; + break; + case CI_COPY: /* copy bits to the target */ + init_count = HOST_TO_TDATA(left); + if (init_count > dlthis->cinit_count) + init_count = dlthis->cinit_count; + if (init_count == 0) + goto loopexit; /* get more bits */ + cinit_info = cinit_info_init; + cinit_info.page = dlthis->cinit_page; + if (!dlthis->myio->writemem(dlthis->myio, pktp, + TDATA_TO_TADDR + (dlthis->cinit_addr), + &cinit_info, + TDATA_TO_HOST(init_count))) { + dload_error(dlthis, initfail, "write", + dlthis->cinit_addr); + } + dlthis->cinit_count -= init_count; + if (dlthis->cinit_count <= 0) { + dlthis->cinit_state = CI_COUNT; + init_count = (init_count + CINIT_ALIGN - 1) & + -CINIT_ALIGN; + /* align to next init */ + } + pktp += TDATA_TO_HOST(init_count); + dlthis->cinit_addr += init_count; + break; + case CI_DONE: /* no more .cinit to do */ + return; + } /* switch (cinit_state) */ + } /* while */ + +loopexit: + if (left > 0) { + dload_error(dlthis, "%d bytes left over in cinit packet", left); + dlthis->cinit_state = CI_DONE; /* left over bytes are bad */ + } +} /* cload_cinit */ + +/* Functions to interface to reloc.c + * + * reloc.c is the relocation module borrowed from the linker, with + * minimal (we hope) changes for our purposes. cload_sect_data() invokes + * this module on a section to relocate and load the image data for that + * section. The actual read and write actions are supplied by the global + * routines below. + */ + +/************************************************************************ + * Procedure relocate_packet + * + * Parameters: + * ipacket Pointer to an image packet to relocate + * + * Effect: + * Performs the required relocations on the packet. Returns a checksum + * of the relocation operations. + *********************************************************************** */ +#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, bool *tramps_generated) +{ + u32 rnum; + *tramps_generated = false; + + rnum = ipacket->num_relocs; + do { /* all relocs */ + unsigned rinbuf; + int siz; + struct reloc_record_t *rp, rrec[MY_RELOC_BUF_SIZ]; + rp = rrec; + rinbuf = rnum > MY_RELOC_BUF_SIZ ? MY_RELOC_BUF_SIZ : rnum; + siz = rinbuf * sizeof(struct reloc_record_t); + if (dlthis->strm->read_buffer(dlthis->strm, rp, siz) != siz) { + DL_ERROR(readstrm, "relocation"); + return 0; + } + /* reorder the bytes if need be */ + if (dlthis->reorder_map) + dload_reorder(rp, siz, dlthis->reorder_map); + + *checks += dload_checksum(rp, siz); + do { + /* perform the relocation operation */ + dload_relocate(dlthis, (tgt_au_t *) ipacket->img_data, + 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 */ + +#define IPH_SIZE (sizeof(struct image_packet_t) - sizeof(u32)) + +/* VERY dangerous */ +static const char imagepak[] = { "image packet" }; + +/************************************************************************* + * Procedure dload_data + * + * Parameters: + * none + * + * Effect: + * Read image data from input file, relocate it, and download it to the + * target. + *********************************************************************** */ +static void dload_data(struct dload_state *dlthis) +{ + u16 curr_sect; + struct doff_scnhdr_t *sptr = dlthis->sect_hdrs; + struct ldr_section_info *lptr = dlthis->ldr_sections; +#ifdef OPT_ZERO_COPY_LOADER + bool zero_copy = false; +#endif + u8 *dest; + + struct { + struct image_packet_t ipacket; + u8 bufr[BYTE_TO_HOST(IMAGE_PACKET_SIZE)]; + } ibuf; + + /* Indicates whether CINIT processing has occurred */ + bool cinit_processed = false; + + /* Loop through the sections and load them one at a time. + */ + for (curr_sect = 0; curr_sect < dlthis->dfile_hdr.df_no_scns; + curr_sect += 1) { + if (DS_NEEDS_DOWNLOAD(sptr)) { + s32 nip; + ldr_addr image_offset = 0; + /* set relocation info for this section */ + if (curr_sect < dlthis->allocated_secn_count) + dlthis->delta_runaddr = sptr->ds_paddr; + else { + lptr = DOFFSEC_IS_LDRSEC(sptr); + dlthis->delta_runaddr = 0; + } + dlthis->image_secn = lptr; +#if BITS_PER_AU > BITS_PER_BYTE + lptr->name = unpack_name(dlthis, sptr->ds_offset); +#endif + nip = sptr->ds_nipacks; + while ((nip -= 1) >= 0) { /* process packets */ + + 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) { + DL_ERROR(readstrm, imagepak); + return; + } + /* reorder the header if need be */ + if (dlthis->reorder_map) { + dload_reorder(&ibuf.ipacket, IPH_SIZE, + dlthis->reorder_map); + } + /* now read the rest of the packet */ + ipsize = + BYTE_TO_HOST(DOFF_ALIGN + (ibuf.ipacket.packet_size)); + if (ipsize > BYTE_TO_HOST(IMAGE_PACKET_SIZE)) { + DL_ERROR("Bad image packet size %d", + ipsize); + return; + } + dest = ibuf.bufr; +#ifdef OPT_ZERO_COPY_LOADER + zero_copy = false; + if (DLOAD_SECT_TYPE(sptr) != DLOAD_CINIT) { + dlthis->myio->writemem(dlthis->myio, + &dest, + lptr->load_addr + + image_offset, + lptr, 0); + zero_copy = (dest != ibuf.bufr); + } +#endif + /* End of determination */ + + if (dlthis->strm->read_buffer(dlthis->strm, + ibuf.bufr, + ipsize) != + ipsize) { + DL_ERROR(readstrm, imagepak); + return; + } + ibuf.ipacket.img_data = dest; + + /* reorder the bytes if need be */ +#if !defined(_BIG_ENDIAN) || (TARGET_AU_BITS > 16) + if (dlthis->reorder_map) { + dload_reorder(dest, ipsize, + dlthis->reorder_map); + } + checks = dload_checksum(dest, ipsize); +#else + if (dlthis->dfile_hdr.df_byte_reshuffle != + TARGET_ORDER(REORDER_MAP + (BYTE_RESHUFFLE_VALUE))) { + /* put image bytes in big-endian order, + * not PC order */ + dload_reorder(dest, ipsize, + TARGET_ORDER + (dlthis->dfile_hdr. + df_byte_reshuffle)); + } +#if TARGET_AU_BITS > 8 + checks = dload_reverse_checksum16(dest, ipsize); +#else + checks = dload_reverse_checksum(dest, ipsize); +#endif +#endif + + checks += dload_checksum(&ibuf.ipacket, + IPH_SIZE); + /* relocate the image bits as needed */ + if (ibuf.ipacket.num_relocs) { + dlthis->image_offset = image_offset; + if (!relocate_packet(dlthis, + &ibuf.ipacket, + &checks, + &tramp_generated)) + return; /* serious error */ + } + if (~checks) + DL_ERROR(err_checksum, imagepak); + /* 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 (!zero_copy) { +#endif + /* FIXME */ + if (!dlthis->myio-> + writemem(dlthis-> + myio, + ibuf.bufr, + lptr-> + load_addr + + image_offset, + lptr, + BYTE_TO_HOST + (ibuf. + ipacket. + 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.packet_size); + } /* process packets */ + /* if this is a BSS section, we may want to fill it */ + if (DLOAD_SECT_TYPE(sptr) != DLOAD_BSS) + goto loop_cont; + + if (!(dlthis->myoptions & DLOAD_INITBSS)) + goto loop_cont; + + if (cinit_processed) { + /* Don't clear BSS after load-time + * initialization */ + DL_ERROR + ("Zero-initialization at " FMT_UI32 + " after " "load-time initialization!", + lptr->load_addr); + goto loop_cont; + } + /* fill the .bss area */ + dlthis->myio->fillmem(dlthis->myio, + TADDR_TO_HOST(lptr->load_addr), + lptr, TADDR_TO_HOST(lptr->size), + DLOAD_FILL_BSS); + goto loop_cont; + } + /* if DS_DOWNLOAD_MASK */ + /* If not loading, but BSS, zero initialize */ + if (DLOAD_SECT_TYPE(sptr) != DLOAD_BSS) + goto loop_cont; + + if (!(dlthis->myoptions & DLOAD_INITBSS)) + goto loop_cont; + + if (curr_sect >= dlthis->allocated_secn_count) + lptr = DOFFSEC_IS_LDRSEC(sptr); + + if (cinit_processed) { + /*Don't clear BSS after load-time initialization */ + DL_ERROR("Zero-initialization at " FMT_UI32 + " attempted after " + "load-time initialization!", lptr->load_addr); + goto loop_cont; + } + /* fill the .bss area */ + dlthis->myio->fillmem(dlthis->myio, + TADDR_TO_HOST(lptr->load_addr), lptr, + TADDR_TO_HOST(lptr->size), + DLOAD_FILL_BSS); +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 */ + +/************************************************************************* + * Procedure dload_reorder + * + * Parameters: + * data 32-bit aligned pointer to data to be byte-swapped + * dsiz size of the data to be reordered in sizeof() units. + * map 32-bit map defining how to reorder the data. Value + * must be REORDER_MAP() of some permutation + * of 0x00 01 02 03 + * + * Effect: + * Re-arranges the bytes in each word according to the map specified. + * + *********************************************************************** */ +/* mask for byte shift count */ +#define SHIFT_COUNT_MASK (3 << LOG_BITS_PER_BYTE) + +void dload_reorder(void *data, int dsiz, unsigned int map) +{ + register u32 tmp, tmap, datv; + u32 *dp = (u32 *) data; + + map <<= LOG_BITS_PER_BYTE; /* align map with SHIFT_COUNT_MASK */ + do { + tmp = 0; + datv = *dp; + tmap = map; + do { + tmp |= (datv & BYTE_MASK) << (tmap & SHIFT_COUNT_MASK); + tmap >>= BITS_PER_BYTE; + } while (datv >>= BITS_PER_BYTE); + *dp++ = tmp; + } while ((dsiz -= sizeof(u32)) > 0); +} /* dload_reorder */ + +/************************************************************************* + * Procedure dload_checksum + * + * Parameters: + * data 32-bit aligned pointer to data to be checksummed + * siz size of the data to be checksummed in sizeof() units. + * + * Effect: + * Returns a checksum of the specified block + * + *********************************************************************** */ +u32 dload_checksum(void *data, unsigned siz) +{ + u32 sum; + u32 *dp; + int left; + + sum = 0; + dp = (u32 *) data; + for (left = siz; left > 0; left -= sizeof(u32)) + sum += *dp++; + return sum; +} /* dload_checksum */ + +#if HOST_ENDIANNESS +/************************************************************************* + * Procedure dload_reverse_checksum + * + * Parameters: + * data 32-bit aligned pointer to data to be checksummed + * siz size of the data to be checksummed in sizeof() units. + * + * Effect: + * Returns a checksum of the specified block, which is assumed to be bytes + * in big-endian order. + * + * Notes: + * In a big-endian host, things like the string table are stored as bytes + * in host order. But dllcreate always checksums in little-endian order. + * It is most efficient to just handle the difference a word at a time. + * + ********************************************************************** */ +u32 dload_reverse_checksum(void *data, unsigned siz) +{ + u32 sum, temp; + u32 *dp; + int left; + + sum = 0; + dp = (u32 *) data; + + for (left = siz; left > 0; left -= sizeof(u32)) { + temp = *dp++; + sum += temp << BITS_PER_BYTE * 3; + sum += temp >> BITS_PER_BYTE * 3; + sum += (temp >> BITS_PER_BYTE) & (BYTE_MASK << BITS_PER_BYTE); + sum += (temp & (BYTE_MASK << BITS_PER_BYTE)) << BITS_PER_BYTE; + } + + return sum; +} /* dload_reverse_checksum */ + +#if (TARGET_AU_BITS > 8) && (TARGET_AU_BITS < 32) +u32 dload_reverse_checksum16(void *data, unsigned siz) +{ + uint_fast32_t sum, temp; + u32 *dp; + int left; + + sum = 0; + dp = (u32 *) data; + + for (left = siz; left > 0; left -= sizeof(u32)) { + temp = *dp++; + sum += temp << BITS_PER_BYTE * 2; + sum += temp >> BITS_PER_BYTE * 2; + } + + return sum; +} /* dload_reverse_checksum16 */ +#endif +#endif + +/************************************************************************* + * Procedure swap_words + * + * Parameters: + * data 32-bit aligned pointer to data to be swapped + * siz size of the data to be swapped. + * bitmap Bit map of how to swap each 32-bit word; 1 => 2 shorts, + * 0 => 1 long + * + * Effect: + * Swaps the specified data according to the specified map + * + *********************************************************************** */ +static void swap_words(void *data, unsigned siz, unsigned bitmap) +{ + register int i; +#if TARGET_AU_BITS < 16 + register u16 *sp; +#endif + register u32 *lp; + + siz /= sizeof(u16); + +#if TARGET_AU_BITS < 16 + /* pass 1: do all the bytes */ + i = siz; + sp = (u16 *) data; + do { + register u16 tmp; + tmp = *sp; + *sp++ = SWAP16BY8(tmp); + } while ((i -= 1) > 0); +#endif + +#if TARGET_AU_BITS < 32 + /* pass 2: fixup the 32-bit words */ + i = siz >> 1; + lp = (u32 *) data; + do { + if ((bitmap & 1) == 0) { + register u32 tmp; + tmp = *lp; + *lp = SWAP32BY16(tmp); + } + lp += 1; + bitmap >>= 1; + } while ((i -= 1) > 0); +#endif +} /* swap_words */ + +/************************************************************************* + * Procedure copy_tgt_strings + * + * Parameters: + * dstp Destination address. Assumed to be 32-bit aligned + * srcp Source address. Assumed to be 32-bit aligned + * charcount Number of characters to copy. + * + * Effect: + * Copies strings from the source (which is in usual .dof file order on + * the loading processor) to the destination buffer (which should be in proper + * target addressable unit order). Makes sure the last string in the + * buffer is NULL terminated (for safety). + * Returns the first unused destination address. + *********************************************************************** */ +static char *copy_tgt_strings(void *dstp, void *srcp, unsigned charcount) +{ + register tgt_au_t *src = (tgt_au_t *) srcp; + register tgt_au_t *dst = (tgt_au_t *) dstp; + register int cnt = charcount; + do { +#if TARGET_AU_BITS <= BITS_PER_AU + /* byte-swapping issues may exist for strings on target */ + *dst++ = *src++; +#else + *dst++ = *src++; +#endif + } while ((cnt -= (sizeof(tgt_au_t) * BITS_PER_AU / BITS_PER_BYTE)) > 0); + /*apply force to make sure that the string table has null terminator */ +#if (BITS_PER_AU == BITS_PER_BYTE) && (TARGET_AU_BITS == BITS_PER_BYTE) + dst[-1] = 0; +#else + /* little endian */ + dst[-1] &= (1 << (BITS_PER_AU - BITS_PER_BYTE)) - 1; +#endif + return (char *)dst; +} /* copy_tgt_strings */ + +/************************************************************************* + * Procedure init_module_handle + * + * Parameters: + * none + * + * Effect: + * Initializes the module handle we use to enable unloading, and installs + * the debug information required by the target. + * + * Notes: + * The handle returned from dynamic_load_module needs to encapsulate all the + * allocations done for the module, and enable them plus the modules symbols to + * be deallocated. + * + *********************************************************************** */ +#ifndef _BIG_ENDIAN +static const struct ldr_section_info dllview_info_init = { ".dllview", 0, 0, + (ldr_addr)-1, DBG_LIST_PAGE, DLOAD_DATA, 0 +}; +#else +static const struct ldr_section_info dllview_info_init = { ".dllview", 0, 0, + (ldr_addr)-1, DLOAD_DATA, DBG_LIST_PAGE, 0 +}; +#endif +static void init_module_handle(struct dload_state *dlthis) +{ + struct my_handle *hndl; + u16 curr_sect; + struct ldr_section_info *asecs; + struct dll_module *dbmod; + struct dll_sect *dbsec; + struct dbg_mirror_root *mlist; + register char *cp; + struct modules_header mhdr; + struct ldr_section_info dllview_info; + struct dynload_symbol *debug_mirror_sym; + 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) + hndl->secn_count += 1; /* flag for big-endian */ +#endif + if (dlthis->dload_errcount) + return; /* abandon if errors detected */ + /* Locate the symbol that names the header for the CCS debug list + of modules. If not found, we just don't generate the debug record. + If found, we create our modules list. We make sure to create the + loader_dllview_root even if there is no relocation info to record, + just to try to put both symbols in the same symbol table and + module. */ + debug_mirror_sym = dlthis->mysym->find_matching_symbol(dlthis->mysym, + loader_dllview_root); + if (!debug_mirror_sym) { + struct dynload_symbol *dlmodsym; + struct dbg_mirror_root *mlst; + + /* our root symbol is not yet present; + check if we have DLModules defined */ + dlmodsym = dlthis->mysym->find_matching_symbol(dlthis->mysym, + LINKER_MODULES_HEADER); + if (!dlmodsym) + return; /* no DLModules list so no debug info */ + /* if we have DLModules defined, construct our header */ + mlst = (struct dbg_mirror_root *) + dlthis->mysym->dload_allocate(dlthis->mysym, + sizeof(struct + dbg_mirror_root)); + if (!mlst) { + DL_ERROR(err_alloc, sizeof(struct dbg_mirror_root)); + return; + } + mlst->hnext = NULL; + mlst->changes = 0; + mlst->refcount = 0; + mlst->dbthis = TDATA_TO_TADDR(dlmodsym->value); + /* add our root symbol */ + debug_mirror_sym = dlthis->mysym->add_to_symbol_table + (dlthis->mysym, loader_dllview_root, + (unsigned)dlthis->myhandle); + if (!debug_mirror_sym) { + /* failed, recover memory */ + dlthis->mysym->dload_deallocate(dlthis->mysym, mlst); + return; + } + debug_mirror_sym->value = (u32) mlst; + } + /* First create the DLLview record and stuff it into the buffer. + Then write it to the DSP. Record pertinent locations in our hndl, + and add it to the per-processor list of handles with debug info. */ +#ifndef DEBUG_HEADER_IN_LOADER + mlist = (struct dbg_mirror_root *)debug_mirror_sym->value; + if (!mlist) + return; +#else + mlist = (struct dbg_mirror_root *)&debug_list_header; +#endif + hndl->dm.hroot = mlist; /* set pointer to root into our handle */ + if (!dlthis->allocated_secn_count) + return; /* no load addresses to be recorded */ + /* reuse temporary symbol storage */ + dbmod = (struct dll_module *)dlthis->local_symtab; + /* Create the DLLview record in the memory we retain for our handle */ + dbmod->num_sects = dlthis->allocated_secn_count; + dbmod->timestamp = dlthis->verify.dv_timdat; + dbmod->version = INIT_VERSION; + dbmod->verification = VERIFICATION; + asecs = dlthis->ldr_sections; + dbsec = dbmod->sects; + for (curr_sect = dlthis->allocated_secn_count; + curr_sect > 0; curr_sect -= 1) { + dbsec->sect_load_adr = asecs->load_addr; + dbsec->sect_run_adr = asecs->run_addr; + 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 */ + dllview_info = dllview_info_init; + dllview_info.size = TDATA_TO_TADDR(hndl->dm.dbsiz); + /* Initialize memory context to default heap */ + dllview_info.context = 0; + hndl->dm.context = 0; + /* fill in next pointer and size */ + if (mlist->hnext) { + dbmod->next_module = TADDR_TO_TDATA(mlist->hnext->dm.dbthis); + dbmod->next_module_size = mlist->hnext->dm.dbsiz; + } else { + dbmod->next_module_size = 0; + dbmod->next_module = 0; + } + /* allocate memory for on-DSP DLLview debug record */ + if (!dlthis->myalloc) + return; + if (!dlthis->myalloc->dload_allocate(dlthis->myalloc, &dllview_info, + HOST_TO_TADDR(sizeof(u32)))) { + return; + } + /* Store load address of .dllview section */ + hndl->dm.dbthis = dllview_info.load_addr; + /* Store memory context (segid) in which .dllview section + * was allocated */ + hndl->dm.context = dllview_info.context; + mlist->refcount += 1; + /* swap bytes in the entire debug record, but not the string table */ + if (TARGET_ENDIANNESS_DIFFERS(TARGET_BIG_ENDIAN)) { + swap_words(dbmod, (char *)dbsec - (char *)dbmod, + DLL_MODULE_BITMAP); + } + /* Update the DLLview list on the DSP write new record */ + if (!dlthis->myio->writemem(dlthis->myio, dbmod, + dllview_info.load_addr, &dllview_info, + TADDR_TO_HOST(dllview_info.size))) { + return; + } + /* write new header */ + mhdr.first_module_size = hndl->dm.dbsiz; + mhdr.first_module = TADDR_TO_TDATA(dllview_info.load_addr); + /* swap bytes in the module header, if needed */ + if (TARGET_ENDIANNESS_DIFFERS(TARGET_BIG_ENDIAN)) { + swap_words(&mhdr, sizeof(struct modules_header) - sizeof(u16), + MODULES_HEADER_BITMAP); + } + dllview_info = dllview_info_init; + if (!dlthis->myio->writemem(dlthis->myio, &mhdr, mlist->dbthis, + &dllview_info, + sizeof(struct modules_header) - + sizeof(u16))) { + return; + } + /* Add the module handle to this processor's list + of handles with debug info */ + hndl->dm.hnext = mlist->hnext; + if (hndl->dm.hnext) + hndl->dm.hnext->dm.hprev = hndl; + hndl->dm.hprev = (struct my_handle *)mlist; + mlist->hnext = hndl; /* insert after root */ +} /* init_module_handle */ + +/************************************************************************* + * Procedure dynamic_unload_module + * + * Parameters: + * mhandle A module handle from dynamic_load_module + * syms Host-side symbol table and malloc/free functions + * alloc Target-side memory allocation + * + * Effect: + * The module specified by mhandle is unloaded. Unloading causes all + * target memory to be deallocated, all symbols defined by the module to + * be purged, and any host-side storage used by the dynamic loader for + * this module to be released. + * + * Returns: + * Zero for success. On error, the number of errors detected is returned. + * Individual errors are reported using syms->error_report(). + *********************************************************************** */ +int dynamic_unload_module(void *mhandle, + struct dynamic_loader_sym *syms, + struct dynamic_loader_allocate *alloc, + struct dynamic_loader_initialize *init) +{ + s16 curr_sect; + struct ldr_section_info *asecs; + struct my_handle *hndl; + struct dbg_mirror_root *root; + unsigned errcount = 0; + struct ldr_section_info dllview_info = dllview_info_init; + struct modules_header mhdr; + + hndl = (struct my_handle *)mhandle; + if (!hndl) + return 0; /* if handle is null, nothing to do */ + /* Clear out the module symbols + * Note that if this is the module that defined MODULES_HEADER + (the head of the target debug list) + * then this operation will blow away that symbol. + It will therefore be impossible for subsequent + * operations to add entries to this un-referenceable list. */ + if (!syms) + return 1; + syms->purge_symbol_table(syms, (unsigned)hndl); + /* 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; + curr_sect -= 1) { + asecs->name = NULL; + alloc->dload_deallocate(alloc, asecs++); + } + root = hndl->dm.hroot; + if (!root) { + /* there is a debug list containing this module */ + goto func_end; + } + if (!hndl->dm.dbthis) { /* target-side dllview record exists */ + goto loop_end; + } + /* Retrieve memory context in which .dllview was allocated */ + dllview_info.context = hndl->dm.context; + if (hndl->dm.hprev == hndl) + goto exitunltgt; + + /* target-side dllview record is in list */ + /* dequeue this record from our GPP-side mirror list */ + hndl->dm.hprev->dm.hnext = hndl->dm.hnext; + if (hndl->dm.hnext) + hndl->dm.hnext->dm.hprev = hndl->dm.hprev; + /* Update next_module of previous entry in target list + * We are using mhdr here as a surrogate for either a + struct modules_header or a dll_module */ + if (hndl->dm.hnext) { + mhdr.first_module = TADDR_TO_TDATA(hndl->dm.hnext->dm.dbthis); + mhdr.first_module_size = hndl->dm.hnext->dm.dbsiz; + } else { + mhdr.first_module = 0; + mhdr.first_module_size = 0; + } + if (!init) + goto exitunltgt; + + if (!init->connect(init)) { + dload_syms_error(syms, iconnect); + errcount += 1; + goto exitunltgt; + } + /* swap bytes in the module header, if needed */ + if (TARGET_ENDIANNESS_DIFFERS(hndl->secn_count & 0x1)) { + swap_words(&mhdr, sizeof(struct modules_header) - sizeof(u16), + MODULES_HEADER_BITMAP); + } + if (!init->writemem(init, &mhdr, hndl->dm.hprev->dm.dbthis, + &dllview_info, sizeof(struct modules_header) - + sizeof(mhdr.update_flag))) { + dload_syms_error(syms, dlvwrite); + errcount += 1; + } + /* update change counter */ + root->changes += 1; + if (!init->writemem(init, &(root->changes), + root->dbthis + HOST_TO_TADDR + (sizeof(mhdr.first_module) + + sizeof(mhdr.first_module_size)), + &dllview_info, sizeof(mhdr.update_flag))) { + dload_syms_error(syms, dlvwrite); + errcount += 1; + } + init->release(init); +exitunltgt: + /* release target storage */ + dllview_info.size = TDATA_TO_TADDR(hndl->dm.dbsiz); + dllview_info.load_addr = hndl->dm.dbthis; + if (alloc) + alloc->dload_deallocate(alloc, &dllview_info); + root->refcount -= 1; + /* target-side dllview record exists */ +loop_end: +#ifndef DEBUG_HEADER_IN_LOADER + if (root->refcount <= 0) { + /* if all references gone, blow off the header */ + /* our root symbol may be gone due to the Purge above, + but if not, do not destroy the root */ + if (syms->find_matching_symbol + (syms, loader_dllview_root) == NULL) + syms->dload_deallocate(syms, root); + } +#endif +func_end: + /* there is a debug list containing this module */ + syms->dload_deallocate(syms, mhandle); /* release our storage */ + return errcount; +} /* dynamic_unload_module */ + +#if BITS_PER_AU > BITS_PER_BYTE +/************************************************************************* + * Procedure unpack_name + * + * Parameters: + * soffset Byte offset into the string table + * + * Effect: + * Returns a pointer to the string specified by the offset supplied, or + * NULL for error. + * + *********************************************************************** */ +static char *unpack_name(struct dload_state *dlthis, u32 soffset) +{ + u8 tmp, *src; + char *dst; + + if (soffset >= dlthis->dfile_hdr.df_strtab_size) { + dload_error(dlthis, "Bad string table offset " FMT_UI32, + soffset); + return NULL; + } + src = (uint_least8_t *) dlthis->str_head + + (soffset >> (LOG_BITS_PER_AU - LOG_BITS_PER_BYTE)); + dst = dlthis->str_temp; + if (soffset & 1) + *dst++ = *src++; /* only 1 character in first word */ + do { + tmp = *src++; + *dst = (tmp >> BITS_PER_BYTE); + if (!(*dst++)) + break; + } while ((*dst++ = tmp & BYTE_MASK)); + dlthis->temp_len = dst - dlthis->str_temp; + /* squirrel away length including terminating null */ + return dlthis->str_temp; +} /* unpack_name */ +#endif |