/* * 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 /* ----------------------------------- Host OS */ #include /* ----------------------------------- DSP/BIOS Bridge */ #include /* ----------------------------------- Trace & Debug */ #include #include /* ----------------------------------- OS Adaptation Layer */ /* Dynamic loader library interface */ #include #include /* ----------------------------------- This */ #include #include /* 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 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); 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->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->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_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->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->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->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_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); } /* * ======== 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 Address * @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