diff options
Diffstat (limited to 'drivers/staging/tidspbridge/pmgr/dbll.c')
-rw-r--r-- | drivers/staging/tidspbridge/pmgr/dbll.c | 1585 |
1 files changed, 1585 insertions, 0 deletions
diff --git a/drivers/staging/tidspbridge/pmgr/dbll.c b/drivers/staging/tidspbridge/pmgr/dbll.c new file mode 100644 index 00000000000..23406386f61 --- /dev/null +++ b/drivers/staging/tidspbridge/pmgr/dbll.c @@ -0,0 +1,1585 @@ +/* + * dbll.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 <linux/types.h> + +/* ----------------------------------- Host OS */ +#include <dspbridge/host_os.h> + +/* ----------------------------------- DSP/BIOS Bridge */ +#include <dspbridge/dbdefs.h> + +/* ----------------------------------- Trace & Debug */ +#include <dspbridge/dbc.h> +#include <dspbridge/gh.h> + +/* ----------------------------------- OS Adaptation Layer */ + +/* Dynamic loader library interface */ +#include <dspbridge/dynamic_loader.h> +#include <dspbridge/getsection.h> + +/* ----------------------------------- This */ +#include <dspbridge/dbll.h> +#include <dspbridge/rmm.h> + +/* Number of buckets for symbol hash table */ +#define MAXBUCKETS 211 + +/* Max buffer length */ +#define MAXEXPR 128 + +#define DOFF_ALIGN(x) (((x) + 3) & ~3UL) + +/* + * ======== struct dbll_tar_obj* ======== + * A target may have one or more libraries of symbols/code/data loaded + * onto it, where a library is simply the symbols/code/data contained + * in a DOFF file. + */ +/* + * ======== dbll_tar_obj ======== + */ +struct dbll_tar_obj { + struct dbll_attrs attrs; + struct dbll_library_obj *head; /* List of all opened libraries */ +}; + +/* + * The following 4 typedefs are "super classes" of the dynamic loader + * library types used in dynamic loader functions (dynamic_loader.h). + */ +/* + * ======== dbll_stream ======== + * Contains dynamic_loader_stream + */ +struct dbll_stream { + struct dynamic_loader_stream dl_stream; + struct dbll_library_obj *lib; +}; + +/* + * ======== ldr_symbol ======== + */ +struct ldr_symbol { + struct dynamic_loader_sym dl_symbol; + struct dbll_library_obj *lib; +}; + +/* + * ======== dbll_alloc ======== + */ +struct dbll_alloc { + struct dynamic_loader_allocate dl_alloc; + struct dbll_library_obj *lib; +}; + +/* + * ======== dbll_init_obj ======== + */ +struct dbll_init_obj { + struct dynamic_loader_initialize dl_init; + struct dbll_library_obj *lib; +}; + +/* + * ======== DBLL_Library ======== + * A library handle is returned by DBLL_Open() and is passed to dbll_load() + * to load symbols/code/data, and to dbll_unload(), to remove the + * symbols/code/data loaded by dbll_load(). + */ + +/* + * ======== dbll_library_obj ======== + */ +struct dbll_library_obj { + struct dbll_library_obj *next; /* Next library in target's list */ + struct dbll_library_obj *prev; /* Previous in the list */ + struct dbll_tar_obj *target_obj; /* target for this library */ + + /* Objects needed by dynamic loader */ + struct dbll_stream stream; + struct ldr_symbol symbol; + struct dbll_alloc allocate; + struct dbll_init_obj init; + void *dload_mod_obj; + + char *file_name; /* COFF file name */ + void *fp; /* Opaque file handle */ + u32 entry; /* Entry point */ + void *desc; /* desc of DOFF file loaded */ + u32 open_ref; /* Number of times opened */ + u32 load_ref; /* Number of times loaded */ + struct gh_t_hash_tab *sym_tab; /* Hash table of symbols */ + u32 ul_pos; +}; + +/* + * ======== dbll_symbol ======== + */ +struct dbll_symbol { + struct dbll_sym_val value; + char *name; +}; + +static void dof_close(struct dbll_library_obj *zl_lib); +static int dof_open(struct dbll_library_obj *zl_lib); +static s32 no_op(struct dynamic_loader_initialize *thisptr, void *bufr, + ldr_addr locn, struct ldr_section_info *info, + unsigned bytsize); + +/* + * Functions called by dynamic loader + * + */ +/* dynamic_loader_stream */ +static int dbll_read_buffer(struct dynamic_loader_stream *this, void *buffer, + unsigned bufsize); +static int dbll_set_file_posn(struct dynamic_loader_stream *this, + unsigned int pos); +/* dynamic_loader_sym */ +static struct dynload_symbol *dbll_find_symbol(struct dynamic_loader_sym *this, + const char *name); +static struct dynload_symbol *dbll_add_to_symbol_table(struct dynamic_loader_sym + *this, const char *name, + unsigned module_id); +static struct dynload_symbol *find_in_symbol_table(struct dynamic_loader_sym + *this, const char *name, + unsigned moduleid); +static void dbll_purge_symbol_table(struct dynamic_loader_sym *this, + unsigned module_id); +static void *allocate(struct dynamic_loader_sym *this, unsigned memsize); +static void deallocate(struct dynamic_loader_sym *this, void *mem_ptr); +static void dbll_err_report(struct dynamic_loader_sym *this, const char *errstr, + va_list args); +/* dynamic_loader_allocate */ +static int dbll_rmm_alloc(struct dynamic_loader_allocate *this, + struct ldr_section_info *info, unsigned align); +static void rmm_dealloc(struct dynamic_loader_allocate *this, + struct ldr_section_info *info); + +/* dynamic_loader_initialize */ +static int connect(struct dynamic_loader_initialize *this); +static int read_mem(struct dynamic_loader_initialize *this, void *buf, + ldr_addr addr, struct ldr_section_info *info, + unsigned bytes); +static int write_mem(struct dynamic_loader_initialize *this, void *buf, + ldr_addr addr, struct ldr_section_info *info, + unsigned nbytes); +static int fill_mem(struct dynamic_loader_initialize *this, ldr_addr addr, + struct ldr_section_info *info, unsigned bytes, + unsigned val); +static int execute(struct dynamic_loader_initialize *this, ldr_addr start); +static void release(struct dynamic_loader_initialize *this); + +/* symbol table hash functions */ +static u16 name_hash(void *key, u16 max_bucket); +static bool name_match(void *key, void *sp); +static void sym_delete(void *value); + +static u32 refs; /* module reference count */ + +/* Symbol Redefinition */ +static int redefined_symbol; +static int gbl_search = 1; + +/* + * ======== dbll_close ======== + */ +void dbll_close(struct dbll_library_obj *zl_lib) +{ + struct dbll_tar_obj *zl_target; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(zl_lib); + DBC_REQUIRE(zl_lib->open_ref > 0); + zl_target = zl_lib->target_obj; + zl_lib->open_ref--; + if (zl_lib->open_ref == 0) { + /* Remove library from list */ + if (zl_target->head == zl_lib) + zl_target->head = zl_lib->next; + + if (zl_lib->prev) + (zl_lib->prev)->next = zl_lib->next; + + if (zl_lib->next) + (zl_lib->next)->prev = zl_lib->prev; + + /* Free DOF resources */ + dof_close(zl_lib); + kfree(zl_lib->file_name); + + /* remove symbols from symbol table */ + if (zl_lib->sym_tab) + gh_delete(zl_lib->sym_tab); + + /* remove the library object itself */ + kfree(zl_lib); + zl_lib = NULL; + } +} + +/* + * ======== dbll_create ======== + */ +int dbll_create(struct dbll_tar_obj **target_obj, + struct dbll_attrs *pattrs) +{ + struct dbll_tar_obj *pzl_target; + int status = 0; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(pattrs != NULL); + DBC_REQUIRE(target_obj != NULL); + + /* Allocate DBL target object */ + pzl_target = kzalloc(sizeof(struct dbll_tar_obj), GFP_KERNEL); + if (target_obj != NULL) { + if (pzl_target == NULL) { + *target_obj = NULL; + status = -ENOMEM; + } else { + pzl_target->attrs = *pattrs; + *target_obj = (struct dbll_tar_obj *)pzl_target; + } + DBC_ENSURE((!status && *target_obj) || + (status && *target_obj == NULL)); + } + + return status; +} + +/* + * ======== dbll_delete ======== + */ +void dbll_delete(struct dbll_tar_obj *target) +{ + struct dbll_tar_obj *zl_target = (struct dbll_tar_obj *)target; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(zl_target); + + if (zl_target != NULL) + kfree(zl_target); + +} + +/* + * ======== dbll_exit ======== + * Discontinue usage of DBL module. + */ +void dbll_exit(void) +{ + DBC_REQUIRE(refs > 0); + + refs--; + + if (refs == 0) + gh_exit(); + + DBC_ENSURE(refs >= 0); +} + +/* + * ======== dbll_get_addr ======== + * Get address of name in the specified library. + */ +bool dbll_get_addr(struct dbll_library_obj *zl_lib, char *name, + struct dbll_sym_val **sym_val) +{ + struct dbll_symbol *sym; + bool status = false; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(zl_lib); + DBC_REQUIRE(name != NULL); + DBC_REQUIRE(sym_val != NULL); + DBC_REQUIRE(zl_lib->sym_tab != NULL); + + sym = (struct dbll_symbol *)gh_find(zl_lib->sym_tab, name); + if (sym != NULL) { + *sym_val = &sym->value; + status = true; + } + + dev_dbg(bridge, "%s: lib: %p name: %s paddr: %p, status 0x%x\n", + __func__, zl_lib, name, sym_val, status); + return status; +} + +/* + * ======== dbll_get_attrs ======== + * Retrieve the attributes of the target. + */ +void dbll_get_attrs(struct dbll_tar_obj *target, struct dbll_attrs *pattrs) +{ + struct dbll_tar_obj *zl_target = (struct dbll_tar_obj *)target; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(zl_target); + DBC_REQUIRE(pattrs != NULL); + + if ((pattrs != NULL) && (zl_target != NULL)) + *pattrs = zl_target->attrs; + +} + +/* + * ======== dbll_get_c_addr ======== + * Get address of a "C" name in the specified library. + */ +bool dbll_get_c_addr(struct dbll_library_obj *zl_lib, char *name, + struct dbll_sym_val **sym_val) +{ + struct dbll_symbol *sym; + char cname[MAXEXPR + 1]; + bool status = false; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(zl_lib); + DBC_REQUIRE(sym_val != NULL); + DBC_REQUIRE(zl_lib->sym_tab != NULL); + DBC_REQUIRE(name != NULL); + + cname[0] = '_'; + + strncpy(cname + 1, name, sizeof(cname) - 2); + cname[MAXEXPR] = '\0'; /* insure '\0' string termination */ + + /* Check for C name, if not found */ + sym = (struct dbll_symbol *)gh_find(zl_lib->sym_tab, cname); + + if (sym != NULL) { + *sym_val = &sym->value; + status = true; + } + + return status; +} + +/* + * ======== dbll_get_sect ======== + * Get the base address and size (in bytes) of a COFF section. + */ +int dbll_get_sect(struct dbll_library_obj *lib, char *name, u32 *paddr, + u32 *psize) +{ + u32 byte_size; + bool opened_doff = false; + const struct ldr_section_info *sect = NULL; + struct dbll_library_obj *zl_lib = (struct dbll_library_obj *)lib; + int status = 0; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(name != NULL); + DBC_REQUIRE(paddr != NULL); + DBC_REQUIRE(psize != NULL); + DBC_REQUIRE(zl_lib); + + /* If DOFF file is not open, we open it. */ + if (zl_lib != NULL) { + if (zl_lib->fp == NULL) { + status = dof_open(zl_lib); + if (!status) + opened_doff = true; + + } else { + (*(zl_lib->target_obj->attrs.fseek)) (zl_lib->fp, + zl_lib->ul_pos, + SEEK_SET); + } + } else { + status = -EFAULT; + } + if (!status) { + byte_size = 1; + if (dload_get_section_info(zl_lib->desc, name, §)) { + *paddr = sect->load_addr; + *psize = sect->size * byte_size; + /* Make sure size is even for good swap */ + if (*psize % 2) + (*psize)++; + + /* Align size */ + *psize = DOFF_ALIGN(*psize); + } else { + status = -ENXIO; + } + } + if (opened_doff) { + dof_close(zl_lib); + opened_doff = false; + } + + dev_dbg(bridge, "%s: lib: %p name: %s paddr: %p psize: %p, " + "status 0x%x\n", __func__, lib, name, paddr, psize, status); + + return status; +} + +/* + * ======== dbll_init ======== + */ +bool dbll_init(void) +{ + DBC_REQUIRE(refs >= 0); + + if (refs == 0) + gh_init(); + + refs++; + + return true; +} + +/* + * ======== dbll_load ======== + */ +int dbll_load(struct dbll_library_obj *lib, dbll_flags flags, + struct dbll_attrs *attrs, u32 *entry) +{ + struct dbll_library_obj *zl_lib = (struct dbll_library_obj *)lib; + struct dbll_tar_obj *dbzl; + bool got_symbols = true; + s32 err; + int status = 0; + bool opened_doff = false; + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(zl_lib); + DBC_REQUIRE(entry != NULL); + DBC_REQUIRE(attrs != NULL); + + /* + * Load if not already loaded. + */ + if (zl_lib->load_ref == 0 || !(flags & DBLL_DYNAMIC)) { + dbzl = zl_lib->target_obj; + dbzl->attrs = *attrs; + /* Create a hash table for symbols if not already created */ + if (zl_lib->sym_tab == NULL) { + got_symbols = false; + zl_lib->sym_tab = gh_create(MAXBUCKETS, + sizeof(struct dbll_symbol), + name_hash, + name_match, sym_delete); + if (zl_lib->sym_tab == NULL) + status = -ENOMEM; + + } + /* + * Set up objects needed by the dynamic loader + */ + /* Stream */ + zl_lib->stream.dl_stream.read_buffer = dbll_read_buffer; + zl_lib->stream.dl_stream.set_file_posn = dbll_set_file_posn; + zl_lib->stream.lib = zl_lib; + /* Symbol */ + zl_lib->symbol.dl_symbol.find_matching_symbol = + dbll_find_symbol; + if (got_symbols) { + zl_lib->symbol.dl_symbol.add_to_symbol_table = + find_in_symbol_table; + } else { + zl_lib->symbol.dl_symbol.add_to_symbol_table = + dbll_add_to_symbol_table; + } + zl_lib->symbol.dl_symbol.purge_symbol_table = + dbll_purge_symbol_table; + zl_lib->symbol.dl_symbol.dload_allocate = allocate; + zl_lib->symbol.dl_symbol.dload_deallocate = deallocate; + zl_lib->symbol.dl_symbol.error_report = dbll_err_report; + zl_lib->symbol.lib = zl_lib; + /* Allocate */ + zl_lib->allocate.dl_alloc.dload_allocate = dbll_rmm_alloc; + zl_lib->allocate.dl_alloc.dload_deallocate = rmm_dealloc; + zl_lib->allocate.lib = zl_lib; + /* Init */ + zl_lib->init.dl_init.connect = connect; + zl_lib->init.dl_init.readmem = read_mem; + zl_lib->init.dl_init.writemem = write_mem; + zl_lib->init.dl_init.fillmem = fill_mem; + zl_lib->init.dl_init.execute = execute; + zl_lib->init.dl_init.release = release; + zl_lib->init.lib = zl_lib; + /* If COFF file is not open, we open it. */ + if (zl_lib->fp == NULL) { + status = dof_open(zl_lib); + if (!status) + opened_doff = true; + + } + if (!status) { + zl_lib->ul_pos = (*(zl_lib->target_obj->attrs.ftell)) + (zl_lib->fp); + /* Reset file cursor */ + (*(zl_lib->target_obj->attrs.fseek)) (zl_lib->fp, + (long)0, + SEEK_SET); + symbols_reloaded = true; + /* The 5th argument, DLOAD_INITBSS, tells the DLL + * module to zero-init all BSS sections. In general, + * this is not necessary and also increases load time. + * We may want to make this configurable by the user */ + err = dynamic_load_module(&zl_lib->stream.dl_stream, + &zl_lib->symbol.dl_symbol, + &zl_lib->allocate.dl_alloc, + &zl_lib->init.dl_init, + DLOAD_INITBSS, + &zl_lib->dload_mod_obj); + + if (err != 0) { + status = -EILSEQ; + } else if (redefined_symbol) { + zl_lib->load_ref++; + dbll_unload(zl_lib, (struct dbll_attrs *)attrs); + redefined_symbol = false; + status = -EILSEQ; + } else { + *entry = zl_lib->entry; + } + } + } + if (!status) + zl_lib->load_ref++; + + /* Clean up DOFF resources */ + if (opened_doff) + dof_close(zl_lib); + + DBC_ENSURE(status || zl_lib->load_ref > 0); + + dev_dbg(bridge, "%s: lib: %p flags: 0x%x entry: %p, status 0x%x\n", + __func__, lib, flags, entry, status); + + return status; +} + +/* + * ======== dbll_load_sect ======== + * Not supported for COFF. + */ +int dbll_load_sect(struct dbll_library_obj *zl_lib, char *sec_name, + struct dbll_attrs *attrs) +{ + DBC_REQUIRE(zl_lib); + + return -ENOSYS; +} + +/* + * ======== dbll_open ======== + */ +int dbll_open(struct dbll_tar_obj *target, char *file, dbll_flags flags, + struct dbll_library_obj **lib_obj) +{ + struct dbll_tar_obj *zl_target = (struct dbll_tar_obj *)target; + struct dbll_library_obj *zl_lib = NULL; + s32 err; + int status = 0; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(zl_target); + DBC_REQUIRE(zl_target->attrs.fopen != NULL); + DBC_REQUIRE(file != NULL); + DBC_REQUIRE(lib_obj != NULL); + + zl_lib = zl_target->head; + while (zl_lib != NULL) { + if (strcmp(zl_lib->file_name, file) == 0) { + /* Library is already opened */ + zl_lib->open_ref++; + break; + } + zl_lib = zl_lib->next; + } + if (zl_lib == NULL) { + /* Allocate DBL library object */ + zl_lib = kzalloc(sizeof(struct dbll_library_obj), GFP_KERNEL); + if (zl_lib == NULL) { + status = -ENOMEM; + } else { + zl_lib->ul_pos = 0; + /* Increment ref count to allow close on failure + * later on */ + zl_lib->open_ref++; + zl_lib->target_obj = zl_target; + /* Keep a copy of the file name */ + zl_lib->file_name = kzalloc(strlen(file) + 1, + GFP_KERNEL); + if (zl_lib->file_name == NULL) { + status = -ENOMEM; + } else { + strncpy(zl_lib->file_name, file, + strlen(file) + 1); + } + zl_lib->sym_tab = NULL; + } + } + /* + * Set up objects needed by the dynamic loader + */ + if (status) + goto func_cont; + + /* Stream */ + zl_lib->stream.dl_stream.read_buffer = dbll_read_buffer; + zl_lib->stream.dl_stream.set_file_posn = dbll_set_file_posn; + zl_lib->stream.lib = zl_lib; + /* Symbol */ + zl_lib->symbol.dl_symbol.add_to_symbol_table = dbll_add_to_symbol_table; + zl_lib->symbol.dl_symbol.find_matching_symbol = dbll_find_symbol; + zl_lib->symbol.dl_symbol.purge_symbol_table = dbll_purge_symbol_table; + zl_lib->symbol.dl_symbol.dload_allocate = allocate; + zl_lib->symbol.dl_symbol.dload_deallocate = deallocate; + zl_lib->symbol.dl_symbol.error_report = dbll_err_report; + zl_lib->symbol.lib = zl_lib; + /* Allocate */ + zl_lib->allocate.dl_alloc.dload_allocate = dbll_rmm_alloc; + zl_lib->allocate.dl_alloc.dload_deallocate = rmm_dealloc; + zl_lib->allocate.lib = zl_lib; + /* Init */ + zl_lib->init.dl_init.connect = connect; + zl_lib->init.dl_init.readmem = read_mem; + zl_lib->init.dl_init.writemem = write_mem; + zl_lib->init.dl_init.fillmem = fill_mem; + zl_lib->init.dl_init.execute = execute; + zl_lib->init.dl_init.release = release; + zl_lib->init.lib = zl_lib; + if (!status && zl_lib->fp == NULL) + status = dof_open(zl_lib); + + zl_lib->ul_pos = (*(zl_lib->target_obj->attrs.ftell)) (zl_lib->fp); + (*(zl_lib->target_obj->attrs.fseek)) (zl_lib->fp, (long)0, SEEK_SET); + /* Create a hash table for symbols if flag is set */ + if (zl_lib->sym_tab != NULL || !(flags & DBLL_SYMB)) + goto func_cont; + + zl_lib->sym_tab = + gh_create(MAXBUCKETS, sizeof(struct dbll_symbol), name_hash, + name_match, sym_delete); + if (zl_lib->sym_tab == NULL) { + status = -ENOMEM; + } else { + /* Do a fake load to get symbols - set write func to no_op */ + zl_lib->init.dl_init.writemem = no_op; + err = dynamic_open_module(&zl_lib->stream.dl_stream, + &zl_lib->symbol.dl_symbol, + &zl_lib->allocate.dl_alloc, + &zl_lib->init.dl_init, 0, + &zl_lib->dload_mod_obj); + if (err != 0) { + status = -EILSEQ; + } else { + /* Now that we have the symbol table, we can unload */ + err = dynamic_unload_module(zl_lib->dload_mod_obj, + &zl_lib->symbol.dl_symbol, + &zl_lib->allocate.dl_alloc, + &zl_lib->init.dl_init); + if (err != 0) + status = -EILSEQ; + + zl_lib->dload_mod_obj = NULL; + } + } +func_cont: + if (!status) { + if (zl_lib->open_ref == 1) { + /* First time opened - insert in list */ + if (zl_target->head) + (zl_target->head)->prev = zl_lib; + + zl_lib->prev = NULL; + zl_lib->next = zl_target->head; + zl_target->head = zl_lib; + } + *lib_obj = (struct dbll_library_obj *)zl_lib; + } else { + *lib_obj = NULL; + if (zl_lib != NULL) + dbll_close((struct dbll_library_obj *)zl_lib); + + } + DBC_ENSURE((!status && (zl_lib->open_ref > 0) && *lib_obj) + || (status && *lib_obj == NULL)); + + dev_dbg(bridge, "%s: target: %p file: %s lib_obj: %p, status 0x%x\n", + __func__, target, file, lib_obj, status); + + return status; +} + +/* + * ======== dbll_read_sect ======== + * Get the content of a COFF section. + */ +int dbll_read_sect(struct dbll_library_obj *lib, char *name, + char *buf, u32 size) +{ + struct dbll_library_obj *zl_lib = (struct dbll_library_obj *)lib; + bool opened_doff = false; + u32 byte_size; /* size of bytes */ + u32 ul_sect_size; /* size of section */ + const struct ldr_section_info *sect = NULL; + int status = 0; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(zl_lib); + DBC_REQUIRE(name != NULL); + DBC_REQUIRE(buf != NULL); + DBC_REQUIRE(size != 0); + + /* If DOFF file is not open, we open it. */ + if (zl_lib != NULL) { + if (zl_lib->fp == NULL) { + status = dof_open(zl_lib); + if (!status) + opened_doff = true; + + } else { + (*(zl_lib->target_obj->attrs.fseek)) (zl_lib->fp, + zl_lib->ul_pos, + SEEK_SET); + } + } else { + status = -EFAULT; + } + if (status) + goto func_cont; + + byte_size = 1; + if (!dload_get_section_info(zl_lib->desc, name, §)) { + status = -ENXIO; + goto func_cont; + } + /* + * Ensure the supplied buffer size is sufficient to store + * the section buf to be read. + */ + ul_sect_size = sect->size * byte_size; + /* Make sure size is even for good swap */ + if (ul_sect_size % 2) + ul_sect_size++; + + /* Align size */ + ul_sect_size = DOFF_ALIGN(ul_sect_size); + if (ul_sect_size > size) { + status = -EPERM; + } else { + if (!dload_get_section(zl_lib->desc, sect, buf)) + status = -EBADF; + + } +func_cont: + if (opened_doff) { + dof_close(zl_lib); + opened_doff = false; + } + + dev_dbg(bridge, "%s: lib: %p name: %s buf: %p size: 0x%x, " + "status 0x%x\n", __func__, lib, name, buf, size, status); + return status; +} + +/* + * ======== dbll_set_attrs ======== + * Set the attributes of the target. + */ +void dbll_set_attrs(struct dbll_tar_obj *target, struct dbll_attrs *pattrs) +{ + struct dbll_tar_obj *zl_target = (struct dbll_tar_obj *)target; + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(zl_target); + DBC_REQUIRE(pattrs != NULL); + + if ((pattrs != NULL) && (zl_target != NULL)) + zl_target->attrs = *pattrs; + +} + +/* + * ======== dbll_unload ======== + */ +void dbll_unload(struct dbll_library_obj *lib, struct dbll_attrs *attrs) +{ + struct dbll_library_obj *zl_lib = (struct dbll_library_obj *)lib; + s32 err = 0; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(zl_lib); + DBC_REQUIRE(zl_lib->load_ref > 0); + dev_dbg(bridge, "%s: lib: %p\n", __func__, lib); + zl_lib->load_ref--; + /* Unload only if reference count is 0 */ + if (zl_lib->load_ref != 0) + goto func_end; + + zl_lib->target_obj->attrs = *attrs; + if (zl_lib->dload_mod_obj) { + err = dynamic_unload_module(zl_lib->dload_mod_obj, + &zl_lib->symbol.dl_symbol, + &zl_lib->allocate.dl_alloc, + &zl_lib->init.dl_init); + if (err != 0) + dev_dbg(bridge, "%s: failed: 0x%x\n", __func__, err); + } + /* remove symbols from symbol table */ + if (zl_lib->sym_tab != NULL) { + gh_delete(zl_lib->sym_tab); + zl_lib->sym_tab = NULL; + } + /* delete DOFF desc since it holds *lots* of host OS + * resources */ + dof_close(zl_lib); +func_end: + DBC_ENSURE(zl_lib->load_ref >= 0); +} + +/* + * ======== dbll_unload_sect ======== + * Not supported for COFF. + */ +int dbll_unload_sect(struct dbll_library_obj *lib, char *sec_name, + struct dbll_attrs *attrs) +{ + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(sec_name != NULL); + + return -ENOSYS; +} + +/* + * ======== dof_close ======== + */ +static void dof_close(struct dbll_library_obj *zl_lib) +{ + if (zl_lib->desc) { + dload_module_close(zl_lib->desc); + zl_lib->desc = NULL; + } + /* close file */ + if (zl_lib->fp) { + (zl_lib->target_obj->attrs.fclose) (zl_lib->fp); + zl_lib->fp = NULL; + } +} + +/* + * ======== dof_open ======== + */ +static int dof_open(struct dbll_library_obj *zl_lib) +{ + void *open = *(zl_lib->target_obj->attrs.fopen); + int status = 0; + + /* First open the file for the dynamic loader, then open COF */ + zl_lib->fp = + (void *)((dbll_f_open_fxn) (open)) (zl_lib->file_name, "rb"); + + /* Open DOFF module */ + if (zl_lib->fp && zl_lib->desc == NULL) { + (*(zl_lib->target_obj->attrs.fseek)) (zl_lib->fp, (long)0, + SEEK_SET); + zl_lib->desc = + dload_module_open(&zl_lib->stream.dl_stream, + &zl_lib->symbol.dl_symbol); + if (zl_lib->desc == NULL) { + (zl_lib->target_obj->attrs.fclose) (zl_lib->fp); + zl_lib->fp = NULL; + status = -EBADF; + } + } else { + status = -EBADF; + } + + return status; +} + +/* + * ======== name_hash ======== + */ +static u16 name_hash(void *key, u16 max_bucket) +{ + u16 ret; + u16 hash; + char *name = (char *)key; + + DBC_REQUIRE(name != NULL); + + hash = 0; + + while (*name) { + hash <<= 1; + hash ^= *name++; + } + + ret = hash % max_bucket; + + return ret; +} + +/* + * ======== name_match ======== + */ +static bool name_match(void *key, void *sp) +{ + DBC_REQUIRE(key != NULL); + DBC_REQUIRE(sp != NULL); + + if ((key != NULL) && (sp != NULL)) { + if (strcmp((char *)key, ((struct dbll_symbol *)sp)->name) == + 0) + return true; + } + return false; +} + +/* + * ======== no_op ======== + */ +static int no_op(struct dynamic_loader_initialize *thisptr, void *bufr, + ldr_addr locn, struct ldr_section_info *info, unsigned bytsize) +{ + return 1; +} + +/* + * ======== sym_delete ======== + */ +static void sym_delete(void *value) +{ + struct dbll_symbol *sp = (struct dbll_symbol *)value; + + kfree(sp->name); +} + +/* + * Dynamic Loader Functions + */ + +/* dynamic_loader_stream */ +/* + * ======== dbll_read_buffer ======== + */ +static int dbll_read_buffer(struct dynamic_loader_stream *this, void *buffer, + unsigned bufsize) +{ + struct dbll_stream *pstream = (struct dbll_stream *)this; + struct dbll_library_obj *lib; + int bytes_read = 0; + + DBC_REQUIRE(this != NULL); + lib = pstream->lib; + DBC_REQUIRE(lib); + + if (lib != NULL) { + bytes_read = + (*(lib->target_obj->attrs.fread)) (buffer, 1, bufsize, + lib->fp); + } + return bytes_read; +} + +/* + * ======== dbll_set_file_posn ======== + */ +static int dbll_set_file_posn(struct dynamic_loader_stream *this, + unsigned int pos) +{ + struct dbll_stream *pstream = (struct dbll_stream *)this; + struct dbll_library_obj *lib; + int status = 0; /* Success */ + + DBC_REQUIRE(this != NULL); + lib = pstream->lib; + DBC_REQUIRE(lib); + + if (lib != NULL) { + status = (*(lib->target_obj->attrs.fseek)) (lib->fp, (long)pos, + SEEK_SET); + } + + return status; +} + +/* dynamic_loader_sym */ + +/* + * ======== dbll_find_symbol ======== + */ +static struct dynload_symbol *dbll_find_symbol(struct dynamic_loader_sym *this, + const char *name) +{ + struct dynload_symbol *ret_sym; + struct ldr_symbol *ldr_sym = (struct ldr_symbol *)this; + struct dbll_library_obj *lib; + struct dbll_sym_val *dbll_sym = NULL; + bool status = false; /* Symbol not found yet */ + + DBC_REQUIRE(this != NULL); + lib = ldr_sym->lib; + DBC_REQUIRE(lib); + + if (lib != NULL) { + if (lib->target_obj->attrs.sym_lookup) { + /* Check current lib + base lib + dep lib + + * persistent lib */ + status = (*(lib->target_obj->attrs.sym_lookup)) + (lib->target_obj->attrs.sym_handle, + lib->target_obj->attrs.sym_arg, + lib->target_obj->attrs.rmm_handle, name, + &dbll_sym); + } else { + /* Just check current lib for symbol */ + status = dbll_get_addr((struct dbll_library_obj *)lib, + (char *)name, &dbll_sym); + if (!status) { + status = + dbll_get_c_addr((struct dbll_library_obj *) + lib, (char *)name, + &dbll_sym); + } + } + } + + if (!status && gbl_search) + dev_dbg(bridge, "%s: Symbol not found: %s\n", __func__, name); + + DBC_ASSERT((status && (dbll_sym != NULL)) + || (!status && (dbll_sym == NULL))); + + ret_sym = (struct dynload_symbol *)dbll_sym; + return ret_sym; +} + +/* + * ======== find_in_symbol_table ======== + */ +static struct dynload_symbol *find_in_symbol_table(struct dynamic_loader_sym + *this, const char *name, + unsigned moduleid) +{ + struct dynload_symbol *ret_sym; + struct ldr_symbol *ldr_sym = (struct ldr_symbol *)this; + struct dbll_library_obj *lib; + struct dbll_symbol *sym; + + DBC_REQUIRE(this != NULL); + lib = ldr_sym->lib; + DBC_REQUIRE(lib); + DBC_REQUIRE(lib->sym_tab != NULL); + + sym = (struct dbll_symbol *)gh_find(lib->sym_tab, (char *)name); + + ret_sym = (struct dynload_symbol *)&sym->value; + return ret_sym; +} + +/* + * ======== dbll_add_to_symbol_table ======== + */ +static struct dynload_symbol *dbll_add_to_symbol_table(struct dynamic_loader_sym + *this, const char *name, + unsigned module_id) +{ + struct dbll_symbol *sym_ptr = NULL; + struct dbll_symbol symbol; + struct dynload_symbol *dbll_sym = NULL; + struct ldr_symbol *ldr_sym = (struct ldr_symbol *)this; + struct dbll_library_obj *lib; + struct dynload_symbol *ret; + + DBC_REQUIRE(this != NULL); + DBC_REQUIRE(name); + lib = ldr_sym->lib; + DBC_REQUIRE(lib); + + /* Check to see if symbol is already defined in symbol table */ + if (!(lib->target_obj->attrs.base_image)) { + gbl_search = false; + dbll_sym = dbll_find_symbol(this, name); + gbl_search = true; + if (dbll_sym) { + redefined_symbol = true; + dev_dbg(bridge, "%s already defined in symbol table\n", + name); + return NULL; + } + } + /* Allocate string to copy symbol name */ + symbol.name = kzalloc(strlen((char *const)name) + 1, GFP_KERNEL); + if (symbol.name == NULL) + return NULL; + + if (symbol.name != NULL) { + /* Just copy name (value will be filled in by dynamic loader) */ + strncpy(symbol.name, (char *const)name, + strlen((char *const)name) + 1); + + /* Add symbol to symbol table */ + sym_ptr = + (struct dbll_symbol *)gh_insert(lib->sym_tab, (void *)name, + (void *)&symbol); + if (sym_ptr == NULL) + kfree(symbol.name); + + } + if (sym_ptr != NULL) + ret = (struct dynload_symbol *)&sym_ptr->value; + else + ret = NULL; + + return ret; +} + +/* + * ======== dbll_purge_symbol_table ======== + */ +static void dbll_purge_symbol_table(struct dynamic_loader_sym *this, + unsigned module_id) +{ + struct ldr_symbol *ldr_sym = (struct ldr_symbol *)this; + struct dbll_library_obj *lib; + + DBC_REQUIRE(this != NULL); + lib = ldr_sym->lib; + DBC_REQUIRE(lib); + + /* May not need to do anything */ +} + +/* + * ======== allocate ======== + */ +static void *allocate(struct dynamic_loader_sym *this, unsigned memsize) +{ + struct ldr_symbol *ldr_sym = (struct ldr_symbol *)this; + struct dbll_library_obj *lib; + void *buf; + + DBC_REQUIRE(this != NULL); + lib = ldr_sym->lib; + DBC_REQUIRE(lib); + + buf = kzalloc(memsize, GFP_KERNEL); + + return buf; +} + +/* + * ======== deallocate ======== + */ +static void deallocate(struct dynamic_loader_sym *this, void *mem_ptr) +{ + struct ldr_symbol *ldr_sym = (struct ldr_symbol *)this; + struct dbll_library_obj *lib; + + DBC_REQUIRE(this != NULL); + lib = ldr_sym->lib; + DBC_REQUIRE(lib); + + kfree(mem_ptr); +} + +/* + * ======== dbll_err_report ======== + */ +static void dbll_err_report(struct dynamic_loader_sym *this, const char *errstr, + va_list args) +{ + struct ldr_symbol *ldr_sym = (struct ldr_symbol *)this; + struct dbll_library_obj *lib; + char temp_buf[MAXEXPR]; + + DBC_REQUIRE(this != NULL); + lib = ldr_sym->lib; + DBC_REQUIRE(lib); + vsnprintf((char *)temp_buf, MAXEXPR, (char *)errstr, args); + dev_dbg(bridge, "%s\n", temp_buf); +} + +/* dynamic_loader_allocate */ + +/* + * ======== dbll_rmm_alloc ======== + */ +static int dbll_rmm_alloc(struct dynamic_loader_allocate *this, + struct ldr_section_info *info, unsigned align) +{ + struct dbll_alloc *dbll_alloc_obj = (struct dbll_alloc *)this; + struct dbll_library_obj *lib; + int status = 0; + u32 mem_sect_type; + struct rmm_addr rmm_addr_obj; + s32 ret = true; + unsigned stype = DLOAD_SECTION_TYPE(info->type); + char *token = NULL; + char *sz_sec_last_token = NULL; + char *sz_last_token = NULL; + char *sz_sect_name = NULL; + char *psz_cur; + s32 token_len = 0; + s32 seg_id = -1; + s32 req = -1; + s32 count = 0; + u32 alloc_size = 0; + u32 run_addr_flag = 0; + + DBC_REQUIRE(this != NULL); + lib = dbll_alloc_obj->lib; + DBC_REQUIRE(lib); + + mem_sect_type = + (stype == DLOAD_TEXT) ? DBLL_CODE : (stype == + DLOAD_BSS) ? DBLL_BSS : + DBLL_DATA; + + /* Attempt to extract the segment ID and requirement information from + the name of the section */ + DBC_REQUIRE(info->name); + token_len = strlen((char *)(info->name)) + 1; + + sz_sect_name = kzalloc(token_len, GFP_KERNEL); + sz_last_token = kzalloc(token_len, GFP_KERNEL); + sz_sec_last_token = kzalloc(token_len, GFP_KERNEL); + + if (sz_sect_name == NULL || sz_sec_last_token == NULL || + sz_last_token == NULL) { + status = -ENOMEM; + goto func_cont; + } + strncpy(sz_sect_name, (char *)(info->name), token_len); + psz_cur = sz_sect_name; + while ((token = strsep(&psz_cur, ":")) && *token != '\0') { + strncpy(sz_sec_last_token, sz_last_token, + strlen(sz_last_token) + 1); + strncpy(sz_last_token, token, strlen(token) + 1); + token = strsep(&psz_cur, ":"); + count++; /* optimizes processing */ + } + /* If token is 0 or 1, and sz_sec_last_token is DYN_DARAM or DYN_SARAM, + or DYN_EXTERNAL, then mem granularity information is present + within the section name - only process if there are at least three + tokens within the section name (just a minor optimization) */ + if (count >= 3) + strict_strtol(sz_last_token, 10, (long *)&req); + + if ((req == 0) || (req == 1)) { + if (strcmp(sz_sec_last_token, "DYN_DARAM") == 0) { + seg_id = 0; + } else { + if (strcmp(sz_sec_last_token, "DYN_SARAM") == 0) { + seg_id = 1; + } else { + if (strcmp(sz_sec_last_token, + "DYN_EXTERNAL") == 0) + seg_id = 2; + } + } + } +func_cont: + kfree(sz_sect_name); + sz_sect_name = NULL; + kfree(sz_last_token); + sz_last_token = NULL; + kfree(sz_sec_last_token); + sz_sec_last_token = NULL; + + if (mem_sect_type == DBLL_CODE) + alloc_size = info->size + GEM_L1P_PREFETCH_SIZE; + else + alloc_size = info->size; + + if (info->load_addr != info->run_addr) + run_addr_flag = 1; + /* TODO - ideally, we can pass the alignment requirement also + * from here */ + if (lib != NULL) { + status = + (lib->target_obj->attrs.alloc) (lib->target_obj->attrs. + rmm_handle, mem_sect_type, + alloc_size, align, + (u32 *) &rmm_addr_obj, + seg_id, req, false); + } + if (status) { + ret = false; + } else { + /* RMM gives word address. Need to convert to byte address */ + info->load_addr = rmm_addr_obj.addr * DSPWORDSIZE; + if (!run_addr_flag) + info->run_addr = info->load_addr; + info->context = (u32) rmm_addr_obj.segid; + dev_dbg(bridge, "%s: %s base = 0x%x len = 0x%x, " + "info->run_addr 0x%x, info->load_addr 0x%x\n", + __func__, info->name, info->load_addr / DSPWORDSIZE, + info->size / DSPWORDSIZE, info->run_addr, + info->load_addr); + } + return ret; +} + +/* + * ======== rmm_dealloc ======== + */ +static void rmm_dealloc(struct dynamic_loader_allocate *this, + struct ldr_section_info *info) +{ + struct dbll_alloc *dbll_alloc_obj = (struct dbll_alloc *)this; + struct dbll_library_obj *lib; + u32 segid; + int status = 0; + unsigned stype = DLOAD_SECTION_TYPE(info->type); + u32 mem_sect_type; + u32 free_size = 0; + + mem_sect_type = + (stype == DLOAD_TEXT) ? DBLL_CODE : (stype == + DLOAD_BSS) ? DBLL_BSS : + DBLL_DATA; + DBC_REQUIRE(this != NULL); + lib = dbll_alloc_obj->lib; + DBC_REQUIRE(lib); + /* segid was set by alloc function */ + segid = (u32) info->context; + if (mem_sect_type == DBLL_CODE) + free_size = info->size + GEM_L1P_PREFETCH_SIZE; + else + free_size = info->size; + if (lib != NULL) { + status = + (lib->target_obj->attrs.free) (lib->target_obj->attrs. + sym_handle, segid, + info->load_addr / + DSPWORDSIZE, free_size, + false); + } +} + +/* dynamic_loader_initialize */ +/* + * ======== connect ======== + */ +static int connect(struct dynamic_loader_initialize *this) +{ + return true; +} + +/* + * ======== read_mem ======== + * This function does not need to be implemented. + */ +static int read_mem(struct dynamic_loader_initialize *this, void *buf, + ldr_addr addr, struct ldr_section_info *info, + unsigned nbytes) +{ + struct dbll_init_obj *init_obj = (struct dbll_init_obj *)this; + struct dbll_library_obj *lib; + int bytes_read = 0; + + DBC_REQUIRE(this != NULL); + lib = init_obj->lib; + DBC_REQUIRE(lib); + /* Need bridge_brd_read function */ + return bytes_read; +} + +/* + * ======== write_mem ======== + */ +static int write_mem(struct dynamic_loader_initialize *this, void *buf, + ldr_addr addr, struct ldr_section_info *info, + unsigned bytes) +{ + struct dbll_init_obj *init_obj = (struct dbll_init_obj *)this; + struct dbll_library_obj *lib; + struct dbll_tar_obj *target_obj; + struct dbll_sect_info sect_info; + u32 mem_sect_type; + bool ret = true; + + DBC_REQUIRE(this != NULL); + lib = init_obj->lib; + if (!lib) + return false; + + target_obj = lib->target_obj; + + mem_sect_type = + (DLOAD_SECTION_TYPE(info->type) == + DLOAD_TEXT) ? DBLL_CODE : DBLL_DATA; + if (target_obj && target_obj->attrs.write) { + ret = + (*target_obj->attrs.write) (target_obj->attrs.input_params, + addr, buf, bytes, + mem_sect_type); + + if (target_obj->attrs.log_write) { + sect_info.name = info->name; + sect_info.sect_run_addr = info->run_addr; + sect_info.sect_load_addr = info->load_addr; + sect_info.size = info->size; + sect_info.type = mem_sect_type; + /* Pass the information about what we've written to + * another module */ + (*target_obj->attrs.log_write) (target_obj->attrs. + log_write_handle, + §_info, addr, + bytes); + } + } + return ret; +} + +/* + * ======== fill_mem ======== + * Fill bytes of memory at a given address with a given value by + * writing from a buffer containing the given value. Write in + * sets of MAXEXPR (128) bytes to avoid large stack buffer issues. + */ +static int fill_mem(struct dynamic_loader_initialize *this, ldr_addr addr, + struct ldr_section_info *info, unsigned bytes, unsigned val) +{ + bool ret = true; + char *pbuf; + struct dbll_library_obj *lib; + struct dbll_init_obj *init_obj = (struct dbll_init_obj *)this; + + DBC_REQUIRE(this != NULL); + lib = init_obj->lib; + pbuf = NULL; + /* Pass the NULL pointer to write_mem to get the start address of Shared + memory. This is a trick to just get the start address, there is no + writing taking place with this Writemem + */ + if ((lib->target_obj->attrs.write) != (dbll_write_fxn) no_op) + write_mem(this, &pbuf, addr, info, 0); + if (pbuf) + memset(pbuf, val, bytes); + + return ret; +} + +/* + * ======== execute ======== + */ +static int execute(struct dynamic_loader_initialize *this, ldr_addr start) +{ + struct dbll_init_obj *init_obj = (struct dbll_init_obj *)this; + struct dbll_library_obj *lib; + bool ret = true; + + DBC_REQUIRE(this != NULL); + lib = init_obj->lib; + DBC_REQUIRE(lib); + /* Save entry point */ + if (lib != NULL) + lib->entry = (u32) start; + + return ret; +} + +/* + * ======== release ======== + */ +static void release(struct dynamic_loader_initialize *this) +{ +} + +#ifdef CONFIG_TIDSPBRIDGE_BACKTRACE +/** + * find_symbol_context - Basic symbol context structure + * @address: Symbol Adress + * @offset_range: Offset range where the search for the DSP symbol + * started. + * @cur_best_offset: Best offset to start looking for the DSP symbol + * @sym_addr: Address of the DSP symbol + * @name: Symbol name + * + */ +struct find_symbol_context { + /* input */ + u32 address; + u32 offset_range; + /* state */ + u32 cur_best_offset; + /* output */ + u32 sym_addr; + char name[120]; +}; + +/** + * find_symbol_callback() - Validates symbol address and copies the symbol name + * to the user data. + * @elem: dsp library context + * @user_data: Find symbol context + * + */ +void find_symbol_callback(void *elem, void *user_data) +{ + struct dbll_symbol *symbol = elem; + struct find_symbol_context *context = user_data; + u32 symbol_addr = symbol->value.value; + u32 offset = context->address - symbol_addr; + + /* + * Address given should be greater than symbol address, + * symbol address should be within specified range + * and the offset should be better than previous one + */ + if (context->address >= symbol_addr && symbol_addr < (u32)-1 && + offset < context->cur_best_offset) { + context->cur_best_offset = offset; + context->sym_addr = symbol_addr; + strncpy(context->name, symbol->name, sizeof(context->name)); + } + + return; +} + +/** + * dbll_find_dsp_symbol() - This function retrieves the dsp symbol from the dsp binary. + * @zl_lib: DSP binary obj library pointer + * @address: Given address to find the dsp symbol + * @offset_range: offset range to look for dsp symbol + * @sym_addr_output: Symbol Output address + * @name_output: String with the dsp symbol + * + * This function retrieves the dsp symbol from the dsp binary. + */ +bool dbll_find_dsp_symbol(struct dbll_library_obj *zl_lib, u32 address, + u32 offset_range, u32 *sym_addr_output, + char *name_output) +{ + bool status = false; + struct find_symbol_context context; + + context.address = address; + context.offset_range = offset_range; + context.cur_best_offset = offset_range; + context.sym_addr = 0; + context.name[0] = '\0'; + + gh_iterate(zl_lib->sym_tab, find_symbol_callback, &context); + + if (context.name[0]) { + status = true; + strcpy(name_output, context.name); + *sym_addr_output = context.sym_addr; + } + + return status; +} +#endif |