diff options
Diffstat (limited to 'drivers/staging/tidspbridge/rmgr/nldr.c')
-rw-r--r-- | drivers/staging/tidspbridge/rmgr/nldr.c | 1999 |
1 files changed, 1999 insertions, 0 deletions
diff --git a/drivers/staging/tidspbridge/rmgr/nldr.c b/drivers/staging/tidspbridge/rmgr/nldr.c new file mode 100644 index 000000000000..d0138af4e246 --- /dev/null +++ b/drivers/staging/tidspbridge/rmgr/nldr.c @@ -0,0 +1,1999 @@ +/* + * nldr.c + * + * DSP-BIOS Bridge driver support functions for TI OMAP processors. + * + * DSP/BIOS Bridge dynamic + overlay Node loader. + * + * 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 <dspbridge/host_os.h> + +#include <dspbridge/std.h> +#include <dspbridge/dbdefs.h> + +#include <dspbridge/dbc.h> + +/* Platform manager */ +#include <dspbridge/cod.h> +#include <dspbridge/dev.h> + +/* Resource manager */ +#include <dspbridge/dbll.h> +#include <dspbridge/dbdcd.h> +#include <dspbridge/rmm.h> +#include <dspbridge/uuidutil.h> + +#include <dspbridge/nldr.h> + +/* Name of section containing dynamic load mem */ +#define DYNMEMSECT ".dspbridge_mem" + +/* Name of section containing dependent library information */ +#define DEPLIBSECT ".dspbridge_deplibs" + +/* Max depth of recursion for loading node's dependent libraries */ +#define MAXDEPTH 5 + +/* Max number of persistent libraries kept by a node */ +#define MAXLIBS 5 + +/* + * Defines for extracting packed dynamic load memory requirements from two + * masks. + * These defines must match node.cdb and dynm.cdb + * Format of data/code mask is: + * uuuuuuuu|fueeeeee|fudddddd|fucccccc| + * where + * u = unused + * cccccc = prefered/required dynamic mem segid for create phase data/code + * dddddd = prefered/required dynamic mem segid for delete phase data/code + * eeeeee = prefered/req. dynamic mem segid for execute phase data/code + * f = flag indicating if memory is preferred or required: + * f = 1 if required, f = 0 if preferred. + * + * The 6 bits of the segid are interpreted as follows: + * + * If the 6th bit (bit 5) is not set, then this specifies a memory segment + * between 0 and 31 (a maximum of 32 dynamic loading memory segments). + * If the 6th bit (bit 5) is set, segid has the following interpretation: + * segid = 32 - Any internal memory segment can be used. + * segid = 33 - Any external memory segment can be used. + * segid = 63 - Any memory segment can be used (in this case the + * required/preferred flag is irrelevant). + * + */ +/* Maximum allowed dynamic loading memory segments */ +#define MAXMEMSEGS 32 + +#define MAXSEGID 3 /* Largest possible (real) segid */ +#define MEMINTERNALID 32 /* Segid meaning use internal mem */ +#define MEMEXTERNALID 33 /* Segid meaning use external mem */ +#define NULLID 63 /* Segid meaning no memory req/pref */ +#define FLAGBIT 7 /* 7th bit is pref./req. flag */ +#define SEGMASK 0x3f /* Bits 0 - 5 */ + +#define CREATEBIT 0 /* Create segid starts at bit 0 */ +#define DELETEBIT 8 /* Delete segid starts at bit 8 */ +#define EXECUTEBIT 16 /* Execute segid starts at bit 16 */ + +/* + * Masks that define memory type. Must match defines in dynm.cdb. + */ +#define DYNM_CODE 0x2 +#define DYNM_DATA 0x4 +#define DYNM_CODEDATA (DYNM_CODE | DYNM_DATA) +#define DYNM_INTERNAL 0x8 +#define DYNM_EXTERNAL 0x10 + +/* + * Defines for packing memory requirement/preference flags for code and + * data of each of the node's phases into one mask. + * The bit is set if the segid is required for loading code/data of the + * given phase. The bit is not set, if the segid is preferred only. + * + * These defines are also used as indeces into a segid array for the node. + * eg node's segid[CREATEDATAFLAGBIT] is the memory segment id that the + * create phase data is required or preferred to be loaded into. + */ +#define CREATEDATAFLAGBIT 0 +#define CREATECODEFLAGBIT 1 +#define EXECUTEDATAFLAGBIT 2 +#define EXECUTECODEFLAGBIT 3 +#define DELETEDATAFLAGBIT 4 +#define DELETECODEFLAGBIT 5 +#define MAXFLAGS 6 + +#define IS_INTERNAL(nldr_obj, segid) (((segid) <= MAXSEGID && \ + nldr_obj->seg_table[(segid)] & DYNM_INTERNAL) || \ + (segid) == MEMINTERNALID) + +#define IS_EXTERNAL(nldr_obj, segid) (((segid) <= MAXSEGID && \ + nldr_obj->seg_table[(segid)] & DYNM_EXTERNAL) || \ + (segid) == MEMEXTERNALID) + +#define SWAPLONG(x) ((((x) << 24) & 0xFF000000) | (((x) << 8) & 0xFF0000L) | \ + (((x) >> 8) & 0xFF00L) | (((x) >> 24) & 0xFF)) + +#define SWAPWORD(x) ((((x) << 8) & 0xFF00) | (((x) >> 8) & 0xFF)) + + /* + * These names may be embedded in overlay sections to identify which + * node phase the section should be overlayed. + */ +#define PCREATE "create" +#define PDELETE "delete" +#define PEXECUTE "execute" + +#define IS_EQUAL_UUID(uuid1, uuid2) (\ + ((uuid1).ul_data1 == (uuid2).ul_data1) && \ + ((uuid1).us_data2 == (uuid2).us_data2) && \ + ((uuid1).us_data3 == (uuid2).us_data3) && \ + ((uuid1).uc_data4 == (uuid2).uc_data4) && \ + ((uuid1).uc_data5 == (uuid2).uc_data5) && \ + (strncmp((void *)(uuid1).uc_data6, (void *)(uuid2).uc_data6, 6)) == 0) + + /* + * ======== mem_seg_info ======== + * Format of dynamic loading memory segment info in coff file. + * Must match dynm.h55. + */ +struct mem_seg_info { + u32 segid; /* Dynamic loading memory segment number */ + u32 base; + u32 len; + u32 type; /* Mask of DYNM_CODE, DYNM_INTERNAL, etc. */ +}; + +/* + * ======== lib_node ======== + * For maintaining a tree of library dependencies. + */ +struct lib_node { + struct dbll_library_obj *lib; /* The library */ + u16 dep_libs; /* Number of dependent libraries */ + struct lib_node *dep_libs_tree; /* Dependent libraries of lib */ +}; + +/* + * ======== ovly_sect ======== + * Information needed to overlay a section. + */ +struct ovly_sect { + struct ovly_sect *next_sect; + u32 sect_load_addr; /* Load address of section */ + u32 sect_run_addr; /* Run address of section */ + u32 size; /* Size of section */ + u16 page; /* DBL_CODE, DBL_DATA */ +}; + +/* + * ======== ovly_node ======== + * For maintaining a list of overlay nodes, with sections that need to be + * overlayed for each of the nodes phases. + */ +struct ovly_node { + struct dsp_uuid uuid; + char *node_name; + struct ovly_sect *create_sects_list; + struct ovly_sect *delete_sects_list; + struct ovly_sect *execute_sects_list; + struct ovly_sect *other_sects_list; + u16 create_sects; + u16 delete_sects; + u16 execute_sects; + u16 other_sects; + u16 create_ref; + u16 delete_ref; + u16 execute_ref; + u16 other_ref; +}; + +/* + * ======== nldr_object ======== + * Overlay loader object. + */ +struct nldr_object { + struct dev_object *hdev_obj; /* Device object */ + struct dcd_manager *hdcd_mgr; /* Proc/Node data manager */ + struct dbll_tar_obj *dbll; /* The DBL loader */ + struct dbll_library_obj *base_lib; /* Base image library */ + struct rmm_target_obj *rmm; /* Remote memory manager for DSP */ + struct dbll_fxns ldr_fxns; /* Loader function table */ + struct dbll_attrs ldr_attrs; /* attrs to pass to loader functions */ + nldr_ovlyfxn ovly_fxn; /* "write" for overlay nodes */ + nldr_writefxn write_fxn; /* "write" for dynamic nodes */ + struct ovly_node *ovly_table; /* Table of overlay nodes */ + u16 ovly_nodes; /* Number of overlay nodes in base */ + u16 ovly_nid; /* Index for tracking overlay nodes */ + u16 dload_segs; /* Number of dynamic load mem segs */ + u32 *seg_table; /* memtypes of dynamic memory segs + * indexed by segid + */ + u16 us_dsp_mau_size; /* Size of DSP MAU */ + u16 us_dsp_word_size; /* Size of DSP word */ +}; + +/* + * ======== nldr_nodeobject ======== + * Dynamic node object. This object is created when a node is allocated. + */ +struct nldr_nodeobject { + struct nldr_object *nldr_obj; /* Dynamic loader handle */ + void *priv_ref; /* Handle to pass to dbl_write_fxn */ + struct dsp_uuid uuid; /* Node's UUID */ + bool dynamic; /* Dynamically loaded node? */ + bool overlay; /* Overlay node? */ + bool *pf_phase_split; /* Multiple phase libraries? */ + struct lib_node root; /* Library containing node phase */ + struct lib_node create_lib; /* Library with create phase lib */ + struct lib_node execute_lib; /* Library with execute phase lib */ + struct lib_node delete_lib; /* Library with delete phase lib */ + /* libs remain loaded until Delete */ + struct lib_node pers_lib_table[MAXLIBS]; + s32 pers_libs; /* Number of persistent libraries */ + /* Path in lib dependency tree */ + struct dbll_library_obj *lib_path[MAXDEPTH + 1]; + enum nldr_phase phase; /* Node phase currently being loaded */ + + /* + * Dynamic loading memory segments for data and code of each phase. + */ + u16 seg_id[MAXFLAGS]; + + /* + * Mask indicating whether each mem segment specified in seg_id[] + * is preferred or required. + * For example + * if (code_data_flag_mask & (1 << EXECUTEDATAFLAGBIT)) != 0, + * then it is required to load execute phase data into the memory + * specified by seg_id[EXECUTEDATAFLAGBIT]. + */ + u32 code_data_flag_mask; +}; + +/* Dynamic loader function table */ +static struct dbll_fxns ldr_fxns = { + (dbll_close_fxn) dbll_close, + (dbll_create_fxn) dbll_create, + (dbll_delete_fxn) dbll_delete, + (dbll_exit_fxn) dbll_exit, + (dbll_get_attrs_fxn) dbll_get_attrs, + (dbll_get_addr_fxn) dbll_get_addr, + (dbll_get_c_addr_fxn) dbll_get_c_addr, + (dbll_get_sect_fxn) dbll_get_sect, + (dbll_init_fxn) dbll_init, + (dbll_load_fxn) dbll_load, + (dbll_load_sect_fxn) dbll_load_sect, + (dbll_open_fxn) dbll_open, + (dbll_read_sect_fxn) dbll_read_sect, + (dbll_set_attrs_fxn) dbll_set_attrs, + (dbll_unload_fxn) dbll_unload, + (dbll_unload_sect_fxn) dbll_unload_sect, +}; + +static u32 refs; /* module reference count */ + +static int add_ovly_info(void *handle, struct dbll_sect_info *sect_info, + u32 addr, u32 bytes); +static int add_ovly_node(struct dsp_uuid *uuid_obj, + enum dsp_dcdobjtype obj_type, IN void *handle); +static int add_ovly_sect(struct nldr_object *nldr_obj, + struct ovly_sect **pList, + struct dbll_sect_info *pSectInfo, + bool *pExists, u32 addr, u32 bytes); +static s32 fake_ovly_write(void *handle, u32 dspAddr, void *buf, u32 bytes, + s32 mtype); +static void free_sects(struct nldr_object *nldr_obj, + struct ovly_sect *phase_sects, u16 alloc_num); +static bool get_symbol_value(void *handle, void *parg, void *rmm_handle, + char *symName, struct dbll_sym_val **sym); +static int load_lib(struct nldr_nodeobject *nldr_node_obj, + struct lib_node *root, struct dsp_uuid uuid, + bool rootPersistent, + struct dbll_library_obj **lib_path, + enum nldr_phase phase, u16 depth); +static int load_ovly(struct nldr_nodeobject *nldr_node_obj, + enum nldr_phase phase); +static int remote_alloc(void **pRef, u16 mem_sect_type, u32 size, + u32 align, u32 *dspAddr, OPTIONAL s32 segmentId, + OPTIONAL s32 req, bool reserve); +static int remote_free(void **pRef, u16 space, u32 dspAddr, u32 size, + bool reserve); + +static void unload_lib(struct nldr_nodeobject *nldr_node_obj, + struct lib_node *root); +static void unload_ovly(struct nldr_nodeobject *nldr_node_obj, + enum nldr_phase phase); +static bool find_in_persistent_lib_array(struct nldr_nodeobject *nldr_node_obj, + struct dbll_library_obj *lib); +static u32 find_lcm(u32 a, u32 b); +static u32 find_gcf(u32 a, u32 b); + +/* + * ======== nldr_allocate ======== + */ +int nldr_allocate(struct nldr_object *nldr_obj, void *priv_ref, + IN CONST struct dcd_nodeprops *node_props, + OUT struct nldr_nodeobject **phNldrNode, + IN bool *pf_phase_split) +{ + struct nldr_nodeobject *nldr_node_obj = NULL; + int status = 0; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(node_props != NULL); + DBC_REQUIRE(phNldrNode != NULL); + DBC_REQUIRE(nldr_obj); + + /* Initialize handle in case of failure */ + *phNldrNode = NULL; + /* Allocate node object */ + nldr_node_obj = kzalloc(sizeof(struct nldr_nodeobject), GFP_KERNEL); + + if (nldr_node_obj == NULL) { + status = -ENOMEM; + } else { + nldr_node_obj->pf_phase_split = pf_phase_split; + nldr_node_obj->pers_libs = 0; + nldr_node_obj->nldr_obj = nldr_obj; + nldr_node_obj->priv_ref = priv_ref; + /* Save node's UUID. */ + nldr_node_obj->uuid = node_props->ndb_props.ui_node_id; + /* + * Determine if node is a dynamically loaded node from + * ndb_props. + */ + if (node_props->us_load_type == NLDR_DYNAMICLOAD) { + /* Dynamic node */ + nldr_node_obj->dynamic = true; + /* + * Extract memory requirements from ndb_props masks + */ + /* Create phase */ + nldr_node_obj->seg_id[CREATEDATAFLAGBIT] = (u16) + (node_props->ul_data_mem_seg_mask >> CREATEBIT) & + SEGMASK; + nldr_node_obj->code_data_flag_mask |= + ((node_props->ul_data_mem_seg_mask >> + (CREATEBIT + FLAGBIT)) & 1) << CREATEDATAFLAGBIT; + nldr_node_obj->seg_id[CREATECODEFLAGBIT] = (u16) + (node_props->ul_code_mem_seg_mask >> + CREATEBIT) & SEGMASK; + nldr_node_obj->code_data_flag_mask |= + ((node_props->ul_code_mem_seg_mask >> + (CREATEBIT + FLAGBIT)) & 1) << CREATECODEFLAGBIT; + /* Execute phase */ + nldr_node_obj->seg_id[EXECUTEDATAFLAGBIT] = (u16) + (node_props->ul_data_mem_seg_mask >> + EXECUTEBIT) & SEGMASK; + nldr_node_obj->code_data_flag_mask |= + ((node_props->ul_data_mem_seg_mask >> + (EXECUTEBIT + FLAGBIT)) & 1) << + EXECUTEDATAFLAGBIT; + nldr_node_obj->seg_id[EXECUTECODEFLAGBIT] = (u16) + (node_props->ul_code_mem_seg_mask >> + EXECUTEBIT) & SEGMASK; + nldr_node_obj->code_data_flag_mask |= + ((node_props->ul_code_mem_seg_mask >> + (EXECUTEBIT + FLAGBIT)) & 1) << + EXECUTECODEFLAGBIT; + /* Delete phase */ + nldr_node_obj->seg_id[DELETEDATAFLAGBIT] = (u16) + (node_props->ul_data_mem_seg_mask >> DELETEBIT) & + SEGMASK; + nldr_node_obj->code_data_flag_mask |= + ((node_props->ul_data_mem_seg_mask >> + (DELETEBIT + FLAGBIT)) & 1) << DELETEDATAFLAGBIT; + nldr_node_obj->seg_id[DELETECODEFLAGBIT] = (u16) + (node_props->ul_code_mem_seg_mask >> + DELETEBIT) & SEGMASK; + nldr_node_obj->code_data_flag_mask |= + ((node_props->ul_code_mem_seg_mask >> + (DELETEBIT + FLAGBIT)) & 1) << DELETECODEFLAGBIT; + } else { + /* Non-dynamically loaded nodes are part of the + * base image */ + nldr_node_obj->root.lib = nldr_obj->base_lib; + /* Check for overlay node */ + if (node_props->us_load_type == NLDR_OVLYLOAD) + nldr_node_obj->overlay = true; + + } + *phNldrNode = (struct nldr_nodeobject *)nldr_node_obj; + } + /* Cleanup on failure */ + if (DSP_FAILED(status) && nldr_node_obj) + kfree(nldr_node_obj); + + DBC_ENSURE((DSP_SUCCEEDED(status) && *phNldrNode) + || (DSP_FAILED(status) && *phNldrNode == NULL)); + return status; +} + +/* + * ======== nldr_create ======== + */ +int nldr_create(OUT struct nldr_object **phNldr, + struct dev_object *hdev_obj, + IN CONST struct nldr_attrs *pattrs) +{ + struct cod_manager *cod_mgr; /* COD manager */ + char *psz_coff_buf = NULL; + char sz_zl_file[COD_MAXPATHLENGTH]; + struct nldr_object *nldr_obj = NULL; + struct dbll_attrs save_attrs; + struct dbll_attrs new_attrs; + dbll_flags flags; + u32 ul_entry; + u16 dload_segs = 0; + struct mem_seg_info *mem_info_obj; + u32 ul_len = 0; + u32 ul_addr; + struct rmm_segment *rmm_segs = NULL; + u16 i; + int status = 0; + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(phNldr != NULL); + DBC_REQUIRE(hdev_obj != NULL); + DBC_REQUIRE(pattrs != NULL); + DBC_REQUIRE(pattrs->pfn_ovly != NULL); + DBC_REQUIRE(pattrs->pfn_write != NULL); + + /* Allocate dynamic loader object */ + nldr_obj = kzalloc(sizeof(struct nldr_object), GFP_KERNEL); + if (nldr_obj) { + nldr_obj->hdev_obj = hdev_obj; + /* warning, lazy status checking alert! */ + dev_get_cod_mgr(hdev_obj, &cod_mgr); + if (cod_mgr) { + status = cod_get_loader(cod_mgr, &nldr_obj->dbll); + DBC_ASSERT(DSP_SUCCEEDED(status)); + status = cod_get_base_lib(cod_mgr, &nldr_obj->base_lib); + DBC_ASSERT(DSP_SUCCEEDED(status)); + status = + cod_get_base_name(cod_mgr, sz_zl_file, + COD_MAXPATHLENGTH); + DBC_ASSERT(DSP_SUCCEEDED(status)); + } + status = 0; + /* end lazy status checking */ + nldr_obj->us_dsp_mau_size = pattrs->us_dsp_mau_size; + nldr_obj->us_dsp_word_size = pattrs->us_dsp_word_size; + nldr_obj->ldr_fxns = ldr_fxns; + if (!(nldr_obj->ldr_fxns.init_fxn())) + status = -ENOMEM; + + } else { + status = -ENOMEM; + } + /* Create the DCD Manager */ + if (DSP_SUCCEEDED(status)) + status = dcd_create_manager(NULL, &nldr_obj->hdcd_mgr); + + /* Get dynamic loading memory sections from base lib */ + if (DSP_SUCCEEDED(status)) { + status = + nldr_obj->ldr_fxns.get_sect_fxn(nldr_obj->base_lib, + DYNMEMSECT, &ul_addr, + &ul_len); + if (DSP_SUCCEEDED(status)) { + psz_coff_buf = + kzalloc(ul_len * nldr_obj->us_dsp_mau_size, + GFP_KERNEL); + if (!psz_coff_buf) + status = -ENOMEM; + } else { + /* Ok to not have dynamic loading memory */ + status = 0; + ul_len = 0; + dev_dbg(bridge, "%s: failed - no dynamic loading mem " + "segments: 0x%x\n", __func__, status); + } + } + if (DSP_SUCCEEDED(status) && ul_len > 0) { + /* Read section containing dynamic load mem segments */ + status = + nldr_obj->ldr_fxns.read_sect_fxn(nldr_obj->base_lib, + DYNMEMSECT, psz_coff_buf, + ul_len); + } + if (DSP_SUCCEEDED(status) && ul_len > 0) { + /* Parse memory segment data */ + dload_segs = (u16) (*((u32 *) psz_coff_buf)); + if (dload_segs > MAXMEMSEGS) + status = -EBADF; + } + /* Parse dynamic load memory segments */ + if (DSP_SUCCEEDED(status) && dload_segs > 0) { + rmm_segs = kzalloc(sizeof(struct rmm_segment) * dload_segs, + GFP_KERNEL); + nldr_obj->seg_table = + kzalloc(sizeof(u32) * dload_segs, GFP_KERNEL); + if (rmm_segs == NULL || nldr_obj->seg_table == NULL) { + status = -ENOMEM; + } else { + nldr_obj->dload_segs = dload_segs; + mem_info_obj = (struct mem_seg_info *)(psz_coff_buf + + sizeof(u32)); + for (i = 0; i < dload_segs; i++) { + rmm_segs[i].base = (mem_info_obj + i)->base; + rmm_segs[i].length = (mem_info_obj + i)->len; + rmm_segs[i].space = 0; + nldr_obj->seg_table[i] = + (mem_info_obj + i)->type; + dev_dbg(bridge, + "(proc) DLL MEMSEGMENT: %d, " + "Base: 0x%x, Length: 0x%x\n", i, + rmm_segs[i].base, rmm_segs[i].length); + } + } + } + /* Create Remote memory manager */ + if (DSP_SUCCEEDED(status)) + status = rmm_create(&nldr_obj->rmm, rmm_segs, dload_segs); + + if (DSP_SUCCEEDED(status)) { + /* set the alloc, free, write functions for loader */ + nldr_obj->ldr_fxns.get_attrs_fxn(nldr_obj->dbll, &save_attrs); + new_attrs = save_attrs; + new_attrs.alloc = (dbll_alloc_fxn) remote_alloc; + new_attrs.free = (dbll_free_fxn) remote_free; + new_attrs.sym_lookup = (dbll_sym_lookup) get_symbol_value; + new_attrs.sym_handle = nldr_obj; + new_attrs.write = (dbll_write_fxn) pattrs->pfn_write; + nldr_obj->ovly_fxn = pattrs->pfn_ovly; + nldr_obj->write_fxn = pattrs->pfn_write; + nldr_obj->ldr_attrs = new_attrs; + } + kfree(rmm_segs); + + kfree(psz_coff_buf); + + /* Get overlay nodes */ + if (DSP_SUCCEEDED(status)) { + status = + cod_get_base_name(cod_mgr, sz_zl_file, COD_MAXPATHLENGTH); + /* lazy check */ + DBC_ASSERT(DSP_SUCCEEDED(status)); + /* First count number of overlay nodes */ + status = + dcd_get_objects(nldr_obj->hdcd_mgr, sz_zl_file, + add_ovly_node, (void *)nldr_obj); + /* Now build table of overlay nodes */ + if (DSP_SUCCEEDED(status) && nldr_obj->ovly_nodes > 0) { + /* Allocate table for overlay nodes */ + nldr_obj->ovly_table = + kzalloc(sizeof(struct ovly_node) * + nldr_obj->ovly_nodes, GFP_KERNEL); + /* Put overlay nodes in the table */ + nldr_obj->ovly_nid = 0; + status = dcd_get_objects(nldr_obj->hdcd_mgr, sz_zl_file, + add_ovly_node, + (void *)nldr_obj); + } + } + /* Do a fake reload of the base image to get overlay section info */ + if (DSP_SUCCEEDED(status) && nldr_obj->ovly_nodes > 0) { + save_attrs.write = fake_ovly_write; + save_attrs.log_write = add_ovly_info; + save_attrs.log_write_handle = nldr_obj; + flags = DBLL_CODE | DBLL_DATA | DBLL_SYMB; + status = nldr_obj->ldr_fxns.load_fxn(nldr_obj->base_lib, flags, + &save_attrs, &ul_entry); + } + if (DSP_SUCCEEDED(status)) { + *phNldr = (struct nldr_object *)nldr_obj; + } else { + if (nldr_obj) + nldr_delete((struct nldr_object *)nldr_obj); + + *phNldr = NULL; + } + /* FIXME:Temp. Fix. Must be removed */ + DBC_ENSURE((DSP_SUCCEEDED(status) && *phNldr) + || (DSP_FAILED(status) && (*phNldr == NULL))); + return status; +} + +/* + * ======== nldr_delete ======== + */ +void nldr_delete(struct nldr_object *nldr_obj) +{ + struct ovly_sect *ovly_section; + struct ovly_sect *next; + u16 i; + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(nldr_obj); + + nldr_obj->ldr_fxns.exit_fxn(); + if (nldr_obj->rmm) + rmm_delete(nldr_obj->rmm); + + kfree(nldr_obj->seg_table); + + if (nldr_obj->hdcd_mgr) + dcd_destroy_manager(nldr_obj->hdcd_mgr); + + /* Free overlay node information */ + if (nldr_obj->ovly_table) { + for (i = 0; i < nldr_obj->ovly_nodes; i++) { + ovly_section = + nldr_obj->ovly_table[i].create_sects_list; + while (ovly_section) { + next = ovly_section->next_sect; + kfree(ovly_section); + ovly_section = next; + } + ovly_section = + nldr_obj->ovly_table[i].delete_sects_list; + while (ovly_section) { + next = ovly_section->next_sect; + kfree(ovly_section); + ovly_section = next; + } + ovly_section = + nldr_obj->ovly_table[i].execute_sects_list; + while (ovly_section) { + next = ovly_section->next_sect; + kfree(ovly_section); + ovly_section = next; + } + ovly_section = nldr_obj->ovly_table[i].other_sects_list; + while (ovly_section) { + next = ovly_section->next_sect; + kfree(ovly_section); + ovly_section = next; + } + } + kfree(nldr_obj->ovly_table); + } + kfree(nldr_obj); +} + +/* + * ======== nldr_exit ======== + * Discontinue usage of NLDR module. + */ +void nldr_exit(void) +{ + DBC_REQUIRE(refs > 0); + + refs--; + + if (refs == 0) + rmm_exit(); + + DBC_ENSURE(refs >= 0); +} + +/* + * ======== nldr_get_fxn_addr ======== + */ +int nldr_get_fxn_addr(struct nldr_nodeobject *nldr_node_obj, + char *pstrFxn, u32 * pulAddr) +{ + struct dbll_sym_val *dbll_sym; + struct nldr_object *nldr_obj; + int status = 0; + bool status1 = false; + s32 i = 0; + struct lib_node root = { NULL, 0, NULL }; + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(nldr_node_obj); + DBC_REQUIRE(pulAddr != NULL); + DBC_REQUIRE(pstrFxn != NULL); + + nldr_obj = nldr_node_obj->nldr_obj; + /* Called from node_create(), node_delete(), or node_run(). */ + if (nldr_node_obj->dynamic && *nldr_node_obj->pf_phase_split) { + switch (nldr_node_obj->phase) { + case NLDR_CREATE: + root = nldr_node_obj->create_lib; + break; + case NLDR_EXECUTE: + root = nldr_node_obj->execute_lib; + break; + case NLDR_DELETE: + root = nldr_node_obj->delete_lib; + break; + default: + DBC_ASSERT(false); + break; + } + } else { + /* for Overlay nodes or non-split Dynamic nodes */ + root = nldr_node_obj->root; + } + status1 = + nldr_obj->ldr_fxns.get_c_addr_fxn(root.lib, pstrFxn, &dbll_sym); + if (!status1) + status1 = + nldr_obj->ldr_fxns.get_addr_fxn(root.lib, pstrFxn, + &dbll_sym); + + /* If symbol not found, check dependent libraries */ + if (!status1) { + for (i = 0; i < root.dep_libs; i++) { + status1 = + nldr_obj->ldr_fxns.get_addr_fxn(root.dep_libs_tree + [i].lib, pstrFxn, + &dbll_sym); + if (!status1) { + status1 = + nldr_obj->ldr_fxns. + get_c_addr_fxn(root.dep_libs_tree[i].lib, + pstrFxn, &dbll_sym); + } + if (status1) { + /* Symbol found */ + break; + } + } + } + /* Check persistent libraries */ + if (!status1) { + for (i = 0; i < nldr_node_obj->pers_libs; i++) { + status1 = + nldr_obj->ldr_fxns. + get_addr_fxn(nldr_node_obj->pers_lib_table[i].lib, + pstrFxn, &dbll_sym); + if (!status1) { + status1 = + nldr_obj->ldr_fxns. + get_c_addr_fxn(nldr_node_obj->pers_lib_table + [i].lib, pstrFxn, &dbll_sym); + } + if (status1) { + /* Symbol found */ + break; + } + } + } + + if (status1) + *pulAddr = dbll_sym->value; + else + status = -ESPIPE; + + return status; +} + +/* + * ======== nldr_get_rmm_manager ======== + * Given a NLDR object, retrieve RMM Manager Handle + */ +int nldr_get_rmm_manager(struct nldr_object *hNldrObject, + OUT struct rmm_target_obj **phRmmMgr) +{ + int status = 0; + struct nldr_object *nldr_obj = hNldrObject; + DBC_REQUIRE(phRmmMgr != NULL); + + if (hNldrObject) { + *phRmmMgr = nldr_obj->rmm; + } else { + *phRmmMgr = NULL; + status = -EFAULT; + } + + DBC_ENSURE(DSP_SUCCEEDED(status) || ((phRmmMgr != NULL) && + (*phRmmMgr == NULL))); + + return status; +} + +/* + * ======== nldr_init ======== + * Initialize the NLDR module. + */ +bool nldr_init(void) +{ + DBC_REQUIRE(refs >= 0); + + if (refs == 0) + rmm_init(); + + refs++; + + DBC_ENSURE(refs > 0); + return true; +} + +/* + * ======== nldr_load ======== + */ +int nldr_load(struct nldr_nodeobject *nldr_node_obj, + enum nldr_phase phase) +{ + struct nldr_object *nldr_obj; + struct dsp_uuid lib_uuid; + int status = 0; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(nldr_node_obj); + + nldr_obj = nldr_node_obj->nldr_obj; + + if (nldr_node_obj->dynamic) { + nldr_node_obj->phase = phase; + + lib_uuid = nldr_node_obj->uuid; + + /* At this point, we may not know if node is split into + * different libraries. So we'll go ahead and load the + * library, and then save the pointer to the appropriate + * location after we know. */ + + status = + load_lib(nldr_node_obj, &nldr_node_obj->root, lib_uuid, + false, nldr_node_obj->lib_path, phase, 0); + + if (DSP_SUCCEEDED(status)) { + if (*nldr_node_obj->pf_phase_split) { + switch (phase) { + case NLDR_CREATE: + nldr_node_obj->create_lib = + nldr_node_obj->root; + break; + + case NLDR_EXECUTE: + nldr_node_obj->execute_lib = + nldr_node_obj->root; + break; + + case NLDR_DELETE: + nldr_node_obj->delete_lib = + nldr_node_obj->root; + break; + + default: + DBC_ASSERT(false); + break; + } + } + } + } else { + if (nldr_node_obj->overlay) + status = load_ovly(nldr_node_obj, phase); + + } + + return status; +} + +/* + * ======== nldr_unload ======== + */ +int nldr_unload(struct nldr_nodeobject *nldr_node_obj, + enum nldr_phase phase) +{ + int status = 0; + struct lib_node *root_lib = NULL; + s32 i = 0; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(nldr_node_obj); + + if (nldr_node_obj != NULL) { + if (nldr_node_obj->dynamic) { + if (*nldr_node_obj->pf_phase_split) { + switch (phase) { + case NLDR_CREATE: + root_lib = &nldr_node_obj->create_lib; + break; + case NLDR_EXECUTE: + root_lib = &nldr_node_obj->execute_lib; + break; + case NLDR_DELETE: + root_lib = &nldr_node_obj->delete_lib; + /* Unload persistent libraries */ + for (i = 0; + i < nldr_node_obj->pers_libs; + i++) { + unload_lib(nldr_node_obj, + &nldr_node_obj-> + pers_lib_table[i]); + } + nldr_node_obj->pers_libs = 0; + break; + default: + DBC_ASSERT(false); + break; + } + } else { + /* Unload main library */ + root_lib = &nldr_node_obj->root; + } + if (root_lib) + unload_lib(nldr_node_obj, root_lib); + } else { + if (nldr_node_obj->overlay) + unload_ovly(nldr_node_obj, phase); + + } + } + return status; +} + +/* + * ======== add_ovly_info ======== + */ +static int add_ovly_info(void *handle, struct dbll_sect_info *sect_info, + u32 addr, u32 bytes) +{ + char *node_name; + char *sect_name = (char *)sect_info->name; + bool sect_exists = false; + char seps = ':'; + char *pch; + u16 i; + struct nldr_object *nldr_obj = (struct nldr_object *)handle; + int status = 0; + + /* Is this an overlay section (load address != run address)? */ + if (sect_info->sect_load_addr == sect_info->sect_run_addr) + goto func_end; + + /* Find the node it belongs to */ + for (i = 0; i < nldr_obj->ovly_nodes; i++) { + node_name = nldr_obj->ovly_table[i].node_name; + DBC_REQUIRE(node_name); + if (strncmp(node_name, sect_name + 1, strlen(node_name)) == 0) { + /* Found the node */ + break; + } + } + if (!(i < nldr_obj->ovly_nodes)) + goto func_end; + + /* Determine which phase this section belongs to */ + for (pch = sect_name + 1; *pch && *pch != seps; pch++) + ;; + + if (*pch) { + pch++; /* Skip over the ':' */ + if (strncmp(pch, PCREATE, strlen(PCREATE)) == 0) { + status = + add_ovly_sect(nldr_obj, + &nldr_obj-> + ovly_table[i].create_sects_list, + sect_info, §_exists, addr, bytes); + if (DSP_SUCCEEDED(status) && !sect_exists) + nldr_obj->ovly_table[i].create_sects++; + + } else if (strncmp(pch, PDELETE, strlen(PDELETE)) == 0) { + status = + add_ovly_sect(nldr_obj, + &nldr_obj-> + ovly_table[i].delete_sects_list, + sect_info, §_exists, addr, bytes); + if (DSP_SUCCEEDED(status) && !sect_exists) + nldr_obj->ovly_table[i].delete_sects++; + + } else if (strncmp(pch, PEXECUTE, strlen(PEXECUTE)) == 0) { + status = + add_ovly_sect(nldr_obj, + &nldr_obj-> + ovly_table[i].execute_sects_list, + sect_info, §_exists, addr, bytes); + if (DSP_SUCCEEDED(status) && !sect_exists) + nldr_obj->ovly_table[i].execute_sects++; + + } else { + /* Put in "other" sectins */ + status = + add_ovly_sect(nldr_obj, + &nldr_obj-> + ovly_table[i].other_sects_list, + sect_info, §_exists, addr, bytes); + if (DSP_SUCCEEDED(status) && !sect_exists) + nldr_obj->ovly_table[i].other_sects++; + + } + } +func_end: + return status; +} + +/* + * ======== add_ovly_node ========= + * Callback function passed to dcd_get_objects. + */ +static int add_ovly_node(struct dsp_uuid *uuid_obj, + enum dsp_dcdobjtype obj_type, IN void *handle) +{ + struct nldr_object *nldr_obj = (struct nldr_object *)handle; + char *node_name = NULL; + char *pbuf = NULL; + u32 len; + struct dcd_genericobj obj_def; + int status = 0; + + if (obj_type != DSP_DCDNODETYPE) + goto func_end; + + status = + dcd_get_object_def(nldr_obj->hdcd_mgr, uuid_obj, obj_type, + &obj_def); + if (DSP_FAILED(status)) + goto func_end; + + /* If overlay node, add to the list */ + if (obj_def.obj_data.node_obj.us_load_type == NLDR_OVLYLOAD) { + if (nldr_obj->ovly_table == NULL) { + nldr_obj->ovly_nodes++; + } else { + /* Add node to table */ + nldr_obj->ovly_table[nldr_obj->ovly_nid].uuid = + *uuid_obj; + DBC_REQUIRE(obj_def.obj_data.node_obj.ndb_props. + ac_name); + len = + strlen(obj_def.obj_data.node_obj.ndb_props.ac_name); + node_name = obj_def.obj_data.node_obj.ndb_props.ac_name; + pbuf = kzalloc(len + 1, GFP_KERNEL); + if (pbuf == NULL) { + status = -ENOMEM; + } else { + strncpy(pbuf, node_name, len); + nldr_obj->ovly_table[nldr_obj->ovly_nid]. + node_name = pbuf; + nldr_obj->ovly_nid++; + } + } + } + /* These were allocated in dcd_get_object_def */ + kfree(obj_def.obj_data.node_obj.pstr_create_phase_fxn); + + kfree(obj_def.obj_data.node_obj.pstr_execute_phase_fxn); + + kfree(obj_def.obj_data.node_obj.pstr_delete_phase_fxn); + + kfree(obj_def.obj_data.node_obj.pstr_i_alg_name); + +func_end: + return status; +} + +/* + * ======== add_ovly_sect ======== + */ +static int add_ovly_sect(struct nldr_object *nldr_obj, + struct ovly_sect **pList, + struct dbll_sect_info *pSectInfo, + bool *pExists, u32 addr, u32 bytes) +{ + struct ovly_sect *new_sect = NULL; + struct ovly_sect *last_sect; + struct ovly_sect *ovly_section; + int status = 0; + + ovly_section = last_sect = *pList; + *pExists = false; + while (ovly_section) { + /* + * Make sure section has not already been added. Multiple + * 'write' calls may be made to load the section. + */ + if (ovly_section->sect_load_addr == addr) { + /* Already added */ + *pExists = true; + break; + } + last_sect = ovly_section; + ovly_section = ovly_section->next_sect; + } + + if (!ovly_section) { + /* New section */ + new_sect = kzalloc(sizeof(struct ovly_sect), GFP_KERNEL); + if (new_sect == NULL) { + status = -ENOMEM; + } else { + new_sect->sect_load_addr = addr; + new_sect->sect_run_addr = pSectInfo->sect_run_addr + + (addr - pSectInfo->sect_load_addr); + new_sect->size = bytes; + new_sect->page = pSectInfo->type; + } + + /* Add to the list */ + if (DSP_SUCCEEDED(status)) { + if (*pList == NULL) { + /* First in the list */ + *pList = new_sect; + } else { + last_sect->next_sect = new_sect; + } + } + } + + return status; +} + +/* + * ======== fake_ovly_write ======== + */ +static s32 fake_ovly_write(void *handle, u32 dspAddr, void *buf, u32 bytes, + s32 mtype) +{ + return (s32) bytes; +} + +/* + * ======== free_sects ======== + */ +static void free_sects(struct nldr_object *nldr_obj, + struct ovly_sect *phase_sects, u16 alloc_num) +{ + struct ovly_sect *ovly_section = phase_sects; + u16 i = 0; + bool ret; + + while (ovly_section && i < alloc_num) { + /* 'Deallocate' */ + /* segid - page not supported yet */ + /* Reserved memory */ + ret = + rmm_free(nldr_obj->rmm, 0, ovly_section->sect_run_addr, + ovly_section->size, true); + DBC_ASSERT(ret); + ovly_section = ovly_section->next_sect; + i++; + } +} + +/* + * ======== get_symbol_value ======== + * Find symbol in library's base image. If not there, check dependent + * libraries. + */ +static bool get_symbol_value(void *handle, void *parg, void *rmm_handle, + char *name, struct dbll_sym_val **sym) +{ + struct nldr_object *nldr_obj = (struct nldr_object *)handle; + struct nldr_nodeobject *nldr_node_obj = + (struct nldr_nodeobject *)rmm_handle; + struct lib_node *root = (struct lib_node *)parg; + u16 i; + bool status = false; + + /* check the base image */ + status = nldr_obj->ldr_fxns.get_addr_fxn(nldr_obj->base_lib, name, sym); + if (!status) + status = + nldr_obj->ldr_fxns.get_c_addr_fxn(nldr_obj->base_lib, name, + sym); + + /* + * Check in root lib itself. If the library consists of + * multiple object files linked together, some symbols in the + * library may need to be resolved. + */ + if (!status) { + status = nldr_obj->ldr_fxns.get_addr_fxn(root->lib, name, sym); + if (!status) { + status = + nldr_obj->ldr_fxns.get_c_addr_fxn(root->lib, name, + sym); + } + } + + /* + * Check in root lib's dependent libraries, but not dependent + * libraries' dependents. + */ + if (!status) { + for (i = 0; i < root->dep_libs; i++) { + status = + nldr_obj->ldr_fxns.get_addr_fxn(root->dep_libs_tree + [i].lib, name, sym); + if (!status) { + status = + nldr_obj->ldr_fxns. + get_c_addr_fxn(root->dep_libs_tree[i].lib, + name, sym); + } + if (status) { + /* Symbol found */ + break; + } + } + } + /* + * Check in persistent libraries + */ + if (!status) { + for (i = 0; i < nldr_node_obj->pers_libs; i++) { + status = + nldr_obj->ldr_fxns. + get_addr_fxn(nldr_node_obj->pers_lib_table[i].lib, + name, sym); + if (!status) { + status = nldr_obj->ldr_fxns.get_c_addr_fxn + (nldr_node_obj->pers_lib_table[i].lib, name, + sym); + } + if (status) { + /* Symbol found */ + break; + } + } + } + + return status; +} + +/* + * ======== load_lib ======== + * Recursively load library and all its dependent libraries. The library + * we're loading is specified by a uuid. + */ +static int load_lib(struct nldr_nodeobject *nldr_node_obj, + struct lib_node *root, struct dsp_uuid uuid, + bool rootPersistent, + struct dbll_library_obj **lib_path, + enum nldr_phase phase, u16 depth) +{ + struct nldr_object *nldr_obj = nldr_node_obj->nldr_obj; + u16 nd_libs = 0; /* Number of dependent libraries */ + u16 np_libs = 0; /* Number of persistent libraries */ + u16 nd_libs_loaded = 0; /* Number of dep. libraries loaded */ + u16 i; + u32 entry; + u32 dw_buf_size = NLDR_MAXPATHLENGTH; + dbll_flags flags = DBLL_SYMB | DBLL_CODE | DBLL_DATA | DBLL_DYNAMIC; + struct dbll_attrs new_attrs; + char *psz_file_name = NULL; + struct dsp_uuid *dep_lib_uui_ds = NULL; + bool *persistent_dep_libs = NULL; + int status = 0; + bool lib_status = false; + struct lib_node *dep_lib; + + if (depth > MAXDEPTH) { + /* Error */ + DBC_ASSERT(false); + } + root->lib = NULL; + /* Allocate a buffer for library file name of size DBL_MAXPATHLENGTH */ + psz_file_name = kzalloc(DBLL_MAXPATHLENGTH, GFP_KERNEL); + if (psz_file_name == NULL) + status = -ENOMEM; + + if (DSP_SUCCEEDED(status)) { + /* Get the name of the library */ + if (depth == 0) { + status = + dcd_get_library_name(nldr_node_obj->nldr_obj-> + hdcd_mgr, &uuid, psz_file_name, + &dw_buf_size, phase, + nldr_node_obj->pf_phase_split); + } else { + /* Dependent libraries are registered with a phase */ + status = + dcd_get_library_name(nldr_node_obj->nldr_obj-> + hdcd_mgr, &uuid, psz_file_name, + &dw_buf_size, NLDR_NOPHASE, + NULL); + } + } + if (DSP_SUCCEEDED(status)) { + /* Open the library, don't load symbols */ + status = + nldr_obj->ldr_fxns.open_fxn(nldr_obj->dbll, psz_file_name, + DBLL_NOLOAD, &root->lib); + } + /* Done with file name */ + kfree(psz_file_name); + + /* Check to see if library not already loaded */ + if (DSP_SUCCEEDED(status) && rootPersistent) { + lib_status = + find_in_persistent_lib_array(nldr_node_obj, root->lib); + /* Close library */ + if (lib_status) { + nldr_obj->ldr_fxns.close_fxn(root->lib); + return 0; + } + } + if (DSP_SUCCEEDED(status)) { + /* Check for circular dependencies. */ + for (i = 0; i < depth; i++) { + if (root->lib == lib_path[i]) { + /* This condition could be checked by a + * tool at build time. */ + status = -EILSEQ; + } + } + } + if (DSP_SUCCEEDED(status)) { + /* Add library to current path in dependency tree */ + lib_path[depth] = root->lib; + depth++; + /* Get number of dependent libraries */ + status = + dcd_get_num_dep_libs(nldr_node_obj->nldr_obj->hdcd_mgr, + &uuid, &nd_libs, &np_libs, phase); + } + DBC_ASSERT(nd_libs >= np_libs); + if (DSP_SUCCEEDED(status)) { + if (!(*nldr_node_obj->pf_phase_split)) + np_libs = 0; + + /* nd_libs = #of dependent libraries */ + root->dep_libs = nd_libs - np_libs; + if (nd_libs > 0) { + dep_lib_uui_ds = kzalloc(sizeof(struct dsp_uuid) * + nd_libs, GFP_KERNEL); + persistent_dep_libs = + kzalloc(sizeof(bool) * nd_libs, GFP_KERNEL); + if (!dep_lib_uui_ds || !persistent_dep_libs) + status = -ENOMEM; + + if (root->dep_libs > 0) { + /* Allocate arrays for dependent lib UUIDs, + * lib nodes */ + root->dep_libs_tree = kzalloc + (sizeof(struct lib_node) * + (root->dep_libs), GFP_KERNEL); + if (!(root->dep_libs_tree)) + status = -ENOMEM; + + } + + if (DSP_SUCCEEDED(status)) { + /* Get the dependent library UUIDs */ + status = + dcd_get_dep_libs(nldr_node_obj-> + nldr_obj->hdcd_mgr, &uuid, + nd_libs, dep_lib_uui_ds, + persistent_dep_libs, + phase); + } + } + } + + /* + * Recursively load dependent libraries. + */ + if (DSP_SUCCEEDED(status)) { + for (i = 0; i < nd_libs; i++) { + /* If root library is NOT persistent, and dep library + * is, then record it. If root library IS persistent, + * the deplib is already included */ + if (!rootPersistent && persistent_dep_libs[i] && + *nldr_node_obj->pf_phase_split) { + if ((nldr_node_obj->pers_libs) >= MAXLIBS) { + status = -EILSEQ; + break; + } + + /* Allocate library outside of phase */ + dep_lib = + &nldr_node_obj->pers_lib_table + [nldr_node_obj->pers_libs]; + } else { + if (rootPersistent) + persistent_dep_libs[i] = true; + + /* Allocate library within phase */ + dep_lib = &root->dep_libs_tree[nd_libs_loaded]; + } + + status = load_lib(nldr_node_obj, dep_lib, + dep_lib_uui_ds[i], + persistent_dep_libs[i], lib_path, + phase, depth); + + if (DSP_SUCCEEDED(status)) { + if ((status != 0) && + !rootPersistent && persistent_dep_libs[i] && + *nldr_node_obj->pf_phase_split) { + (nldr_node_obj->pers_libs)++; + } else { + if (!persistent_dep_libs[i] || + !(*nldr_node_obj->pf_phase_split)) { + nd_libs_loaded++; + } + } + } else { + break; + } + } + } + + /* Now we can load the root library */ + if (DSP_SUCCEEDED(status)) { + new_attrs = nldr_obj->ldr_attrs; + new_attrs.sym_arg = root; + new_attrs.rmm_handle = nldr_node_obj; + new_attrs.input_params = nldr_node_obj->priv_ref; + new_attrs.base_image = false; + + status = + nldr_obj->ldr_fxns.load_fxn(root->lib, flags, &new_attrs, + &entry); + } + + /* + * In case of failure, unload any dependent libraries that + * were loaded, and close the root library. + * (Persistent libraries are unloaded from the very top) + */ + if (DSP_FAILED(status)) { + if (phase != NLDR_EXECUTE) { + for (i = 0; i < nldr_node_obj->pers_libs; i++) + unload_lib(nldr_node_obj, + &nldr_node_obj->pers_lib_table[i]); + + nldr_node_obj->pers_libs = 0; + } + for (i = 0; i < nd_libs_loaded; i++) + unload_lib(nldr_node_obj, &root->dep_libs_tree[i]); + + if (root->lib) + nldr_obj->ldr_fxns.close_fxn(root->lib); + + } + + /* Going up one node in the dependency tree */ + depth--; + + kfree(dep_lib_uui_ds); + dep_lib_uui_ds = NULL; + + kfree(persistent_dep_libs); + persistent_dep_libs = NULL; + + return status; +} + +/* + * ======== load_ovly ======== + */ +static int load_ovly(struct nldr_nodeobject *nldr_node_obj, + enum nldr_phase phase) +{ + struct nldr_object *nldr_obj = nldr_node_obj->nldr_obj; + struct ovly_node *po_node = NULL; + struct ovly_sect *phase_sects = NULL; + struct ovly_sect *other_sects_list = NULL; + u16 i; + u16 alloc_num = 0; + u16 other_alloc = 0; + u16 *ref_count = NULL; + u16 *other_ref = NULL; + u32 bytes; + struct ovly_sect *ovly_section; + int status = 0; + + /* Find the node in the table */ + for (i = 0; i < nldr_obj->ovly_nodes; i++) { + if (IS_EQUAL_UUID + (nldr_node_obj->uuid, nldr_obj->ovly_table[i].uuid)) { + /* Found it */ + po_node = &(nldr_obj->ovly_table[i]); + break; + } + } + + DBC_ASSERT(i < nldr_obj->ovly_nodes); + + if (!po_node) { + status = -ENOENT; + goto func_end; + } + + switch (phase) { + case NLDR_CREATE: + ref_count = &(po_node->create_ref); + other_ref = &(po_node->other_ref); + phase_sects = po_node->create_sects_list; + other_sects_list = po_node->other_sects_list; + break; + + case NLDR_EXECUTE: + ref_count = &(po_node->execute_ref); + phase_sects = po_node->execute_sects_list; + break; + + case NLDR_DELETE: + ref_count = &(po_node->delete_ref); + phase_sects = po_node->delete_sects_list; + break; + + default: + DBC_ASSERT(false); + break; + } + + if (ref_count == NULL) + goto func_end; + + if (*ref_count != 0) + goto func_end; + + /* 'Allocate' memory for overlay sections of this phase */ + ovly_section = phase_sects; + while (ovly_section) { + /* allocate *//* page not supported yet */ + /* reserve *//* align */ + status = rmm_alloc(nldr_obj->rmm, 0, ovly_section->size, 0, + &(ovly_section->sect_run_addr), true); + if (DSP_SUCCEEDED(status)) { + ovly_section = ovly_section->next_sect; + alloc_num++; + } else { + break; + } + } + if (other_ref && *other_ref == 0) { + /* 'Allocate' memory for other overlay sections + * (create phase) */ + if (DSP_SUCCEEDED(status)) { + ovly_section = other_sects_list; + while (ovly_section) { + /* page not supported *//* align */ + /* reserve */ + status = + rmm_alloc(nldr_obj->rmm, 0, + ovly_section->size, 0, + &(ovly_section->sect_run_addr), + true); + if (DSP_SUCCEEDED(status)) { + ovly_section = ovly_section->next_sect; + other_alloc++; + } else { + break; + } + } + } + } + if (*ref_count == 0) { + if (DSP_SUCCEEDED(status)) { + /* Load sections for this phase */ + ovly_section = phase_sects; + while (ovly_section && DSP_SUCCEEDED(status)) { + bytes = + (*nldr_obj->ovly_fxn) (nldr_node_obj-> + priv_ref, + ovly_section-> + sect_run_addr, + ovly_section-> + sect_load_addr, + ovly_section->size, + ovly_section->page); + if (bytes != ovly_section->size) + status = -EPERM; + + ovly_section = ovly_section->next_sect; + } + } + } + if (other_ref && *other_ref == 0) { + if (DSP_SUCCEEDED(status)) { + /* Load other sections (create phase) */ + ovly_section = other_sects_list; + while (ovly_section && DSP_SUCCEEDED(status)) { + bytes = + (*nldr_obj->ovly_fxn) (nldr_node_obj-> + priv_ref, + ovly_section-> + sect_run_addr, + ovly_section-> + sect_load_addr, + ovly_section->size, + ovly_section->page); + if (bytes != ovly_section->size) + status = -EPERM; + + ovly_section = ovly_section->next_sect; + } + } + } + if (DSP_FAILED(status)) { + /* 'Deallocate' memory */ + free_sects(nldr_obj, phase_sects, alloc_num); + free_sects(nldr_obj, other_sects_list, other_alloc); + } +func_end: + if (DSP_SUCCEEDED(status) && (ref_count != NULL)) { + *ref_count += 1; + if (other_ref) + *other_ref += 1; + + } + + return status; +} + +/* + * ======== remote_alloc ======== + */ +static int remote_alloc(void **pRef, u16 space, u32 size, + u32 align, u32 *dspAddr, + OPTIONAL s32 segmentId, OPTIONAL s32 req, + bool reserve) +{ + struct nldr_nodeobject *hnode = (struct nldr_nodeobject *)pRef; + struct nldr_object *nldr_obj; + struct rmm_target_obj *rmm; + u16 mem_phase_bit = MAXFLAGS; + u16 segid = 0; + u16 i; + u16 mem_sect_type; + u32 word_size; + struct rmm_addr *rmm_addr_obj = (struct rmm_addr *)dspAddr; + bool mem_load_req = false; + int status = -ENOMEM; /* Set to fail */ + DBC_REQUIRE(hnode); + DBC_REQUIRE(space == DBLL_CODE || space == DBLL_DATA || + space == DBLL_BSS); + nldr_obj = hnode->nldr_obj; + rmm = nldr_obj->rmm; + /* Convert size to DSP words */ + word_size = + (size + nldr_obj->us_dsp_word_size - + 1) / nldr_obj->us_dsp_word_size; + /* Modify memory 'align' to account for DSP cache line size */ + align = find_lcm(GEM_CACHE_LINE_SIZE, align); + dev_dbg(bridge, "%s: memory align to 0x%x\n", __func__, align); + if (segmentId != -1) { + rmm_addr_obj->segid = segmentId; + segid = segmentId; + mem_load_req = req; + } else { + switch (hnode->phase) { + case NLDR_CREATE: + mem_phase_bit = CREATEDATAFLAGBIT; + break; + case NLDR_DELETE: + mem_phase_bit = DELETEDATAFLAGBIT; + break; + case NLDR_EXECUTE: + mem_phase_bit = EXECUTEDATAFLAGBIT; + break; + default: + DBC_ASSERT(false); + break; + } + if (space == DBLL_CODE) + mem_phase_bit++; + + if (mem_phase_bit < MAXFLAGS) + segid = hnode->seg_id[mem_phase_bit]; + + /* Determine if there is a memory loading requirement */ + if ((hnode->code_data_flag_mask >> mem_phase_bit) & 0x1) + mem_load_req = true; + + } + mem_sect_type = (space == DBLL_CODE) ? DYNM_CODE : DYNM_DATA; + + /* Find an appropriate segment based on space */ + if (segid == NULLID) { + /* No memory requirements of preferences */ + DBC_ASSERT(!mem_load_req); + goto func_cont; + } + if (segid <= MAXSEGID) { + DBC_ASSERT(segid < nldr_obj->dload_segs); + /* Attempt to allocate from segid first. */ + rmm_addr_obj->segid = segid; + status = + rmm_alloc(rmm, segid, word_size, align, dspAddr, false); + if (DSP_FAILED(status)) { + dev_dbg(bridge, "%s: Unable allocate from segment %d\n", + __func__, segid); + } + } else { + /* segid > MAXSEGID ==> Internal or external memory */ + DBC_ASSERT(segid == MEMINTERNALID || segid == MEMEXTERNALID); + /* Check for any internal or external memory segment, + * depending on segid. */ + mem_sect_type |= segid == MEMINTERNALID ? + DYNM_INTERNAL : DYNM_EXTERNAL; + for (i = 0; i < nldr_obj->dload_segs; i++) { + if ((nldr_obj->seg_table[i] & mem_sect_type) != + mem_sect_type) + continue; + + status = rmm_alloc(rmm, i, word_size, align, dspAddr, + false); + if (DSP_SUCCEEDED(status)) { + /* Save segid for freeing later */ + rmm_addr_obj->segid = i; + break; + } + } + } +func_cont: + /* Haven't found memory yet, attempt to find any segment that works */ + if (status == -ENOMEM && !mem_load_req) { + dev_dbg(bridge, "%s: Preferred segment unavailable, trying " + "another\n", __func__); + for (i = 0; i < nldr_obj->dload_segs; i++) { + /* All bits of mem_sect_type must be set */ + if ((nldr_obj->seg_table[i] & mem_sect_type) != + mem_sect_type) + continue; + + status = rmm_alloc(rmm, i, word_size, align, dspAddr, + false); + if (DSP_SUCCEEDED(status)) { + /* Save segid */ + rmm_addr_obj->segid = i; + break; + } + } + } + + return status; +} + +static int remote_free(void **pRef, u16 space, u32 dspAddr, + u32 size, bool reserve) +{ + struct nldr_object *nldr_obj = (struct nldr_object *)pRef; + struct rmm_target_obj *rmm; + u32 word_size; + int status = -ENOMEM; /* Set to fail */ + + DBC_REQUIRE(nldr_obj); + + rmm = nldr_obj->rmm; + + /* Convert size to DSP words */ + word_size = + (size + nldr_obj->us_dsp_word_size - + 1) / nldr_obj->us_dsp_word_size; + + if (rmm_free(rmm, space, dspAddr, word_size, reserve)) + status = 0; + + return status; +} + +/* + * ======== unload_lib ======== + */ +static void unload_lib(struct nldr_nodeobject *nldr_node_obj, + struct lib_node *root) +{ + struct dbll_attrs new_attrs; + struct nldr_object *nldr_obj = nldr_node_obj->nldr_obj; + u16 i; + + DBC_ASSERT(root != NULL); + + /* Unload dependent libraries */ + for (i = 0; i < root->dep_libs; i++) + unload_lib(nldr_node_obj, &root->dep_libs_tree[i]); + + root->dep_libs = 0; + + new_attrs = nldr_obj->ldr_attrs; + new_attrs.rmm_handle = nldr_obj->rmm; + new_attrs.input_params = nldr_node_obj->priv_ref; + new_attrs.base_image = false; + new_attrs.sym_arg = root; + + if (root->lib) { + /* Unload the root library */ + nldr_obj->ldr_fxns.unload_fxn(root->lib, &new_attrs); + nldr_obj->ldr_fxns.close_fxn(root->lib); + } + + /* Free dependent library list */ + kfree(root->dep_libs_tree); + root->dep_libs_tree = NULL; +} + +/* + * ======== unload_ovly ======== + */ +static void unload_ovly(struct nldr_nodeobject *nldr_node_obj, + enum nldr_phase phase) +{ + struct nldr_object *nldr_obj = nldr_node_obj->nldr_obj; + struct ovly_node *po_node = NULL; + struct ovly_sect *phase_sects = NULL; + struct ovly_sect *other_sects_list = NULL; + u16 i; + u16 alloc_num = 0; + u16 other_alloc = 0; + u16 *ref_count = NULL; + u16 *other_ref = NULL; + + /* Find the node in the table */ + for (i = 0; i < nldr_obj->ovly_nodes; i++) { + if (IS_EQUAL_UUID + (nldr_node_obj->uuid, nldr_obj->ovly_table[i].uuid)) { + /* Found it */ + po_node = &(nldr_obj->ovly_table[i]); + break; + } + } + + DBC_ASSERT(i < nldr_obj->ovly_nodes); + + if (!po_node) + /* TODO: Should we print warning here? */ + return; + + switch (phase) { + case NLDR_CREATE: + ref_count = &(po_node->create_ref); + phase_sects = po_node->create_sects_list; + alloc_num = po_node->create_sects; + break; + case NLDR_EXECUTE: + ref_count = &(po_node->execute_ref); + phase_sects = po_node->execute_sects_list; + alloc_num = po_node->execute_sects; + break; + case NLDR_DELETE: + ref_count = &(po_node->delete_ref); + other_ref = &(po_node->other_ref); + phase_sects = po_node->delete_sects_list; + /* 'Other' overlay sections are unloaded in the delete phase */ + other_sects_list = po_node->other_sects_list; + alloc_num = po_node->delete_sects; + other_alloc = po_node->other_sects; + break; + default: + DBC_ASSERT(false); + break; + } + DBC_ASSERT(ref_count && (*ref_count > 0)); + if (ref_count && (*ref_count > 0)) { + *ref_count -= 1; + if (other_ref) { + DBC_ASSERT(*other_ref > 0); + *other_ref -= 1; + } + } + + if (ref_count && *ref_count == 0) { + /* 'Deallocate' memory */ + free_sects(nldr_obj, phase_sects, alloc_num); + } + if (other_ref && *other_ref == 0) + free_sects(nldr_obj, other_sects_list, other_alloc); +} + +/* + * ======== find_in_persistent_lib_array ======== + */ +static bool find_in_persistent_lib_array(struct nldr_nodeobject *nldr_node_obj, + struct dbll_library_obj *lib) +{ + s32 i = 0; + + for (i = 0; i < nldr_node_obj->pers_libs; i++) { + if (lib == nldr_node_obj->pers_lib_table[i].lib) + return true; + + } + + return false; +} + +/* + * ================ Find LCM (Least Common Multiplier === + */ +static u32 find_lcm(u32 a, u32 b) +{ + u32 ret; + + ret = a * b / find_gcf(a, b); + + return ret; +} + +/* + * ================ Find GCF (Greatest Common Factor ) === + */ +static u32 find_gcf(u32 a, u32 b) +{ + u32 c; + + /* Get the GCF (Greatest common factor between the numbers, + * using Euclidian Algo */ + while ((c = (a % b))) { + a = b; + b = c; + } + return b; +} + +/** + * nldr_find_addr() - Find the closest symbol to the given address based on + * dynamic node object. + * + * @nldr_node: Dynamic node object + * @sym_addr: Given address to find the dsp symbol + * @offset_range: offset range to look for dsp symbol + * @offset_output: Symbol Output address + * @sym_name: String with the dsp symbol + * + * This function finds the node library for a given address and + * retrieves the dsp symbol by calling dbll_find_dsp_symbol. + */ +int nldr_find_addr(struct nldr_nodeobject *nldr_node, u32 sym_addr, + u32 offset_range, void *offset_output, char *sym_name) +{ + int status = 0; + bool status1 = false; + s32 i = 0; + struct lib_node root = { NULL, 0, NULL }; + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(offset_output != NULL); + DBC_REQUIRE(sym_name != NULL); + pr_debug("%s(0x%x, 0x%x, 0x%x, 0x%x, %s)\n", __func__, (u32) nldr_node, + sym_addr, offset_range, (u32) offset_output, sym_name); + + if (nldr_node->dynamic && *nldr_node->pf_phase_split) { + switch (nldr_node->phase) { + case NLDR_CREATE: + root = nldr_node->create_lib; + break; + case NLDR_EXECUTE: + root = nldr_node->execute_lib; + break; + case NLDR_DELETE: + root = nldr_node->delete_lib; + break; + default: + DBC_ASSERT(false); + break; + } + } else { + /* for Overlay nodes or non-split Dynamic nodes */ + root = nldr_node->root; + } + + status1 = dbll_find_dsp_symbol(root.lib, sym_addr, + offset_range, offset_output, sym_name); + + /* If symbol not found, check dependent libraries */ + if (!status1) + for (i = 0; i < root.dep_libs; i++) { + status1 = dbll_find_dsp_symbol( + root.dep_libs_tree[i].lib, sym_addr, + offset_range, offset_output, sym_name); + if (status1) + /* Symbol found */ + break; + } + /* Check persistent libraries */ + if (!status1) + for (i = 0; i < nldr_node->pers_libs; i++) { + status1 = dbll_find_dsp_symbol( + nldr_node->pers_lib_table[i].lib, sym_addr, + offset_range, offset_output, sym_name); + if (status1) + /* Symbol found */ + break; + } + + if (!status1) { + pr_debug("%s: Address 0x%x not found in range %d.\n", + __func__, sym_addr, offset_range); + status = -ESPIPE; + } + + return status; +} |