/* * 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 <linux/types.h> #include <dspbridge/host_os.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> #include <linux/lcm.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 = preferred/required dynamic mem segid for create phase data/code * dddddd = preferred/required dynamic mem segid for delete phase data/code * eeeeee = preferred/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 /* * 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" static inline bool is_equal_uuid(struct dsp_uuid *uuid1, struct dsp_uuid *uuid2) { return !memcmp(uuid1, uuid2, sizeof(struct dsp_uuid)); } /* * ======== 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 *dev_obj; /* Device object */ struct dcd_manager *dcd_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 dsp_mau_size; /* Size of DSP MAU */ u16 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 *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_open_fxn) dbll_open, (dbll_read_sect_fxn) dbll_read_sect, (dbll_unload_fxn) dbll_unload, }; 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, void *handle); static int add_ovly_sect(struct nldr_object *nldr_obj, struct ovly_sect **lst, struct dbll_sect_info *sect_inf, bool *exists, u32 addr, u32 bytes); static s32 fake_ovly_write(void *handle, u32 dsp_address, 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 *sym_name, struct dbll_sym_val **sym); static int load_lib(struct nldr_nodeobject *nldr_node_obj, struct lib_node *root, struct dsp_uuid uuid, bool root_prstnt, 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 **ref, u16 mem_sect, u32 size, u32 align, u32 *dsp_address, s32 segmnt_id, s32 req, bool reserve); static int remote_free(void **ref, u16 space, u32 dsp_address, 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); /* * ======== nldr_allocate ======== */ int nldr_allocate(struct nldr_object *nldr_obj, void *priv_ref, const struct dcd_nodeprops *node_props, struct nldr_nodeobject **nldr_nodeobj, 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(nldr_nodeobj != NULL); DBC_REQUIRE(nldr_obj); /* Initialize handle in case of failure */ *nldr_nodeobj = 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->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->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->data_mem_seg_mask >> CREATEBIT) & SEGMASK; nldr_node_obj->code_data_flag_mask |= ((node_props->data_mem_seg_mask >> (CREATEBIT + FLAGBIT)) & 1) << CREATEDATAFLAGBIT; nldr_node_obj->seg_id[CREATECODEFLAGBIT] = (u16) (node_props->code_mem_seg_mask >> CREATEBIT) & SEGMASK; nldr_node_obj->code_data_flag_mask |= ((node_props->code_mem_seg_mask >> (CREATEBIT + FLAGBIT)) & 1) << CREATECODEFLAGBIT; /* Execute phase */ nldr_node_obj->seg_id[EXECUTEDATAFLAGBIT] = (u16) (node_props->data_mem_seg_mask >> EXECUTEBIT) & SEGMASK; nldr_node_obj->code_data_flag_mask |= ((node_props->data_mem_seg_mask >> (EXECUTEBIT + FLAGBIT)) & 1) << EXECUTEDATAFLAGBIT; nldr_node_obj->seg_id[EXECUTECODEFLAGBIT] = (u16) (node_props->code_mem_seg_mask >> EXECUTEBIT) & SEGMASK; nldr_node_obj->code_data_flag_mask |= ((node_props->code_mem_seg_mask >> (EXECUTEBIT + FLAGBIT)) & 1) << EXECUTECODEFLAGBIT; /* Delete phase */ nldr_node_obj->seg_id[DELETEDATAFLAGBIT] = (u16) (node_props->data_mem_seg_mask >> DELETEBIT) & SEGMASK; nldr_node_obj->code_data_flag_mask |= ((node_props->data_mem_seg_mask >> (DELETEBIT + FLAGBIT)) & 1) << DELETEDATAFLAGBIT; nldr_node_obj->seg_id[DELETECODEFLAGBIT] = (u16) (node_props->code_mem_seg_mask >> DELETEBIT) & SEGMASK; nldr_node_obj->code_data_flag_mask |= ((node_props->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->load_type == NLDR_OVLYLOAD) nldr_node_obj->overlay = true; } *nldr_nodeobj = (struct nldr_nodeobject *)nldr_node_obj; } /* Cleanup on failure */ if (status && nldr_node_obj) kfree(nldr_node_obj); DBC_ENSURE((!status && *nldr_nodeobj) || (status && *nldr_nodeobj == NULL)); return status; } /* * ======== nldr_create ======== */ int nldr_create(struct nldr_object **nldr, struct dev_object *hdev_obj, 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(nldr != NULL); DBC_REQUIRE(hdev_obj != NULL); DBC_REQUIRE(pattrs != NULL); DBC_REQUIRE(pattrs->ovly != NULL); DBC_REQUIRE(pattrs->write != NULL); /* Allocate dynamic loader object */ nldr_obj = kzalloc(sizeof(struct nldr_object), GFP_KERNEL); if (nldr_obj) { nldr_obj->dev_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(!status); status = cod_get_base_lib(cod_mgr, &nldr_obj->base_lib); DBC_ASSERT(!status); status = cod_get_base_name(cod_mgr, sz_zl_file, COD_MAXPATHLENGTH); DBC_ASSERT(!status); } status = 0; /* end lazy status checking */ nldr_obj->dsp_mau_size = pattrs->dsp_mau_size; nldr_obj->dsp_word_size = pattrs->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 (!status) status = dcd_create_manager(NULL, &nldr_obj->dcd_mgr); /* Get dynamic loading memory sections from base lib */ if (!status) { status = nldr_obj->ldr_fxns.get_sect_fxn(nldr_obj->base_lib, DYNMEMSECT, &ul_addr, &ul_len); if (!status) { psz_coff_buf = kzalloc(ul_len * nldr_obj->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 (!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 (!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 (!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 (!status) status = rmm_create(&nldr_obj->rmm, rmm_segs, dload_segs); if (!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->write; nldr_obj->ovly_fxn = pattrs->ovly; nldr_obj->write_fxn = pattrs->write; nldr_obj->ldr_attrs = new_attrs; } kfree(rmm_segs); kfree(psz_coff_buf); /* Get overlay nodes */ if (!status) { status = cod_get_base_name(cod_mgr, sz_zl_file, COD_MAXPATHLENGTH); /* lazy check */ DBC_ASSERT(!status); /* First count number of overlay nodes */ status = dcd_get_objects(nldr_obj->dcd_mgr, sz_zl_file, add_ovly_node, (void *)nldr_obj); /* Now build table of overlay nodes */ if (!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->dcd_mgr, sz_zl_file, add_ovly_node, (void *)nldr_obj); } } /* Do a fake reload of the base image to get overlay section info */ if (!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 (!status) { *nldr = (struct nldr_object *)nldr_obj; } else { if (nldr_obj) nldr_delete((struct nldr_object *)nldr_obj); *nldr = NULL; } /* FIXME:Temp. Fix. Must be removed */ DBC_ENSURE((!status && *nldr) || (status && *nldr == 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->dcd_mgr) dcd_destroy_manager(nldr_obj->dcd_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 *str_fxn, u32 * addr) { 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(addr != NULL); DBC_REQUIRE(str_fxn != 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->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, str_fxn, &dbll_sym); if (!status1) status1 = nldr_obj->ldr_fxns.get_addr_fxn(root.lib, str_fxn, &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, str_fxn, &dbll_sym); if (!status1) { status1 = nldr_obj->ldr_fxns. get_c_addr_fxn(root.dep_libs_tree[i].lib, str_fxn, &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, str_fxn, &dbll_sym); if (!status1) { status1 = nldr_obj->ldr_fxns. get_c_addr_fxn(nldr_node_obj->pers_lib_table [i].lib, str_fxn, &dbll_sym); } if (status1) { /* Symbol found */ break; } } } if (status1) *addr = 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 *nldr, struct rmm_target_obj **rmm_mgr) { int status = 0; struct nldr_object *nldr_obj = nldr; DBC_REQUIRE(rmm_mgr != NULL); if (nldr) { *rmm_mgr = nldr_obj->rmm; } else { *rmm_mgr = NULL; status = -EFAULT; } DBC_ENSURE(!status || (rmm_mgr != NULL && *rmm_mgr == 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 (!status) { if (*nldr_node_obj->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->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 (!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 (!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 (!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 (!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, 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->dcd_mgr, uuid_obj, obj_type, &obj_def); if (status) goto func_end; /* If overlay node, add to the list */ if (obj_def.obj_data.node_obj.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.str_create_phase_fxn); kfree(obj_def.obj_data.node_obj.str_execute_phase_fxn); kfree(obj_def.obj_data.node_obj.str_delete_phase_fxn); kfree(obj_def.obj_data.node_obj.str_i_alg_name); func_end: return status; } /* * ======== add_ovly_sect ======== */ static int add_ovly_sect(struct nldr_object *nldr_obj, struct ovly_sect **lst, struct dbll_sect_info *sect_inf, bool *exists, 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 = *lst; *exists = 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 */ *exists = 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 = sect_inf->sect_run_addr + (addr - sect_inf->sect_load_addr); new_sect->size = bytes; new_sect->page = sect_inf->type; } /* Add to the list */ if (!status) { if (*lst == NULL) { /* First in the list */ *lst = new_sect; } else { last_sect->next_sect = new_sect; } } } return status; } /* * ======== fake_ovly_write ======== */ static s32 fake_ovly_write(void *handle, u32 dsp_address, 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 *sym_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, sym_name, sym); if (!status) status = nldr_obj->ldr_fxns.get_c_addr_fxn(nldr_obj->base_lib, sym_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, sym_name, sym); if (!status) { status = nldr_obj->ldr_fxns.get_c_addr_fxn(root->lib, sym_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, sym_name, sym); if (!status) { status = nldr_obj->ldr_fxns. get_c_addr_fxn(root->dep_libs_tree[i].lib, sym_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, sym_name, sym); if (!status) { status = nldr_obj->ldr_fxns.get_c_addr_fxn (nldr_node_obj->pers_lib_table[i].lib, sym_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 root_prstnt, 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 (!status) { /* Get the name of the library */ if (depth == 0) { status = dcd_get_library_name(nldr_node_obj->nldr_obj-> dcd_mgr, &uuid, psz_file_name, &dw_buf_size, phase, nldr_node_obj->phase_split); } else { /* Dependent libraries are registered with a phase */ status = dcd_get_library_name(nldr_node_obj->nldr_obj-> dcd_mgr, &uuid, psz_file_name, &dw_buf_size, NLDR_NOPHASE, NULL); } } if (!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 (!status && root_prstnt) { 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 (!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 (!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->dcd_mgr, &uuid, &nd_libs, &np_libs, phase); } DBC_ASSERT(nd_libs >= np_libs); if (!status) { if (!(*nldr_node_obj->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 (!status) { /* Get the dependent library UUIDs */ status = dcd_get_dep_libs(nldr_node_obj-> nldr_obj->dcd_mgr, &uuid, nd_libs, dep_lib_uui_ds, persistent_dep_libs, phase); } } } /* * Recursively load dependent libraries. */ if (!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 (!root_prstnt && persistent_dep_libs[i] && *nldr_node_obj->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 (root_prstnt) 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 (!status) { if ((status != 0) && !root_prstnt && persistent_dep_libs[i] && *nldr_node_obj->phase_split) { (nldr_node_obj->pers_libs)++; } else { if (!persistent_dep_libs[i] || !(*nldr_node_obj->phase_split)) { nd_libs_loaded++; } } } else { break; } } } /* Now we can load the root library */ if (!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 (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 (!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 (!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 (!status) { ovly_section = ovly_section->next_sect; other_alloc++; } else { break; } } } } if (*ref_count == 0) { if (!status) { /* Load sections for this phase */ ovly_section = phase_sects; while (ovly_section && !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 (!status) { /* Load other sections (create phase) */ ovly_section = other_sects_list; while (ovly_section && !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 (status) { /* 'Deallocate' memory */ free_sects(nldr_obj, phase_sects, alloc_num); free_sects(nldr_obj, other_sects_list, other_alloc); } func_end: if (!status && (ref_count != NULL)) { *ref_count += 1; if (other_ref) *other_ref += 1; } return status; } /* * ======== remote_alloc ======== */ static int remote_alloc(void **ref, u16 mem_sect, u32 size, u32 align, u32 *dsp_address, s32 segmnt_id, s32 req, bool reserve) { struct nldr_nodeobject *hnode = (struct nldr_nodeobject *)ref; 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 *)dsp_address; bool mem_load_req = false; int status = -ENOMEM; /* Set to fail */ DBC_REQUIRE(hnode); DBC_REQUIRE(mem_sect == DBLL_CODE || mem_sect == DBLL_DATA || mem_sect == DBLL_BSS); nldr_obj = hnode->nldr_obj; rmm = nldr_obj->rmm; /* Convert size to DSP words */ word_size = (size + nldr_obj->dsp_word_size - 1) / nldr_obj->dsp_word_size; /* Modify memory 'align' to account for DSP cache line size */ align = lcm(GEM_CACHE_LINE_SIZE, align); dev_dbg(bridge, "%s: memory align to 0x%x\n", __func__, align); if (segmnt_id != -1) { rmm_addr_obj->segid = segmnt_id; segid = segmnt_id; 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 (mem_sect == 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 = (mem_sect == DBLL_CODE) ? DYNM_CODE : DYNM_DATA; /* Find an appropriate segment based on mem_sect */ 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, dsp_address, false); if (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, dsp_address, false); if (!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, dsp_address, false); if (!status) { /* Save segid */ rmm_addr_obj->segid = i; break; } } } return status; } static int remote_free(void **ref, u16 space, u32 dsp_address, u32 size, bool reserve) { struct nldr_object *nldr_obj = (struct nldr_object *)ref; 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->dsp_word_size - 1) / nldr_obj->dsp_word_size; if (rmm_free(rmm, space, dsp_address, 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; } #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE /** * 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->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; } #endif