/* * dbdcd.c * * DSP-BIOS Bridge driver support functions for TI OMAP processors. * * This file contains the implementation of the DSP/BIOS Bridge * Configuration Database (DCD). * * Notes: * The fxn dcd_get_objects can apply a callback fxn to each DCD object * that is located in a specified COFF file. At the moment, * dcd_auto_register, dcd_auto_unregister, and NLDR module all use * dcd_get_objects. * * Copyright (C) 2005-2006 Texas Instruments, Inc. * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include /* ----------------------------------- Host OS */ #include /* ----------------------------------- DSP/BIOS Bridge */ #include /* ----------------------------------- Trace & Debug */ #include /* ----------------------------------- Platform Manager */ #include /* ----------------------------------- Others */ #include /* ----------------------------------- This */ #include /* ----------------------------------- Global defines. */ #define MAX_INT2CHAR_LENGTH 16 /* Max int2char len of 32 bit int */ /* Name of section containing dependent libraries */ #define DEPLIBSECT ".dspbridge_deplibs" /* DCD specific structures. */ struct dcd_manager { struct cod_manager *cod_mgr; /* Handle to COD manager object. */ }; /* Pointer to the registry support key */ static struct list_head reg_key_list; static DEFINE_SPINLOCK(dbdcd_lock); /* Global reference variables. */ static u32 refs; static u32 enum_refs; /* Helper function prototypes. */ static s32 atoi(char *psz_buf); static int get_attrs_from_buf(char *psz_buf, u32 ul_buf_size, enum dsp_dcdobjtype obj_type, struct dcd_genericobj *gen_obj); static void compress_buf(char *psz_buf, u32 ul_buf_size, s32 char_size); static char dsp_char2_gpp_char(char *word, s32 dsp_char_size); static int get_dep_lib_info(struct dcd_manager *hdcd_mgr, struct dsp_uuid *uuid_obj, u16 *num_libs, u16 *num_pers_libs, struct dsp_uuid *dep_lib_uuids, bool *prstnt_dep_libs, enum nldr_phase phase); /* * ======== dcd_auto_register ======== * Purpose: * Parses the supplied image and resigsters with DCD. */ int dcd_auto_register(struct dcd_manager *hdcd_mgr, char *sz_coff_path) { int status = 0; DBC_REQUIRE(refs > 0); if (hdcd_mgr) status = dcd_get_objects(hdcd_mgr, sz_coff_path, (dcd_registerfxn) dcd_register_object, (void *)sz_coff_path); else status = -EFAULT; return status; } /* * ======== dcd_auto_unregister ======== * Purpose: * Parses the supplied DSP image and unresiters from DCD. */ int dcd_auto_unregister(struct dcd_manager *hdcd_mgr, char *sz_coff_path) { int status = 0; DBC_REQUIRE(refs > 0); if (hdcd_mgr) status = dcd_get_objects(hdcd_mgr, sz_coff_path, (dcd_registerfxn) dcd_register_object, NULL); else status = -EFAULT; return status; } /* * ======== dcd_create_manager ======== * Purpose: * Creates DCD manager. */ int dcd_create_manager(char *sz_zl_dll_name, struct dcd_manager **dcd_mgr) { struct cod_manager *cod_mgr; /* COD manager handle */ struct dcd_manager *dcd_mgr_obj = NULL; /* DCD Manager pointer */ int status = 0; DBC_REQUIRE(refs >= 0); DBC_REQUIRE(dcd_mgr); status = cod_create(&cod_mgr, sz_zl_dll_name); if (status) goto func_end; /* Create a DCD object. */ dcd_mgr_obj = kzalloc(sizeof(struct dcd_manager), GFP_KERNEL); if (dcd_mgr_obj != NULL) { /* Fill out the object. */ dcd_mgr_obj->cod_mgr = cod_mgr; /* Return handle to this DCD interface. */ *dcd_mgr = dcd_mgr_obj; } else { status = -ENOMEM; /* * If allocation of DcdManager object failed, delete the * COD manager. */ cod_delete(cod_mgr); } DBC_ENSURE((!status) || ((dcd_mgr_obj == NULL) && (status == -ENOMEM))); func_end: return status; } /* * ======== dcd_destroy_manager ======== * Purpose: * Frees DCD Manager object. */ int dcd_destroy_manager(struct dcd_manager *hdcd_mgr) { struct dcd_manager *dcd_mgr_obj = hdcd_mgr; int status = -EFAULT; DBC_REQUIRE(refs >= 0); if (hdcd_mgr) { /* Delete the COD manager. */ cod_delete(dcd_mgr_obj->cod_mgr); /* Deallocate a DCD manager object. */ kfree(dcd_mgr_obj); status = 0; } return status; } /* * ======== dcd_enumerate_object ======== * Purpose: * Enumerates objects in the DCD. */ int dcd_enumerate_object(s32 index, enum dsp_dcdobjtype obj_type, struct dsp_uuid *uuid_obj) { int status = 0; char sz_reg_key[DCD_MAXPATHLENGTH]; char sz_value[DCD_MAXPATHLENGTH]; struct dsp_uuid dsp_uuid_obj; char sz_obj_type[MAX_INT2CHAR_LENGTH]; /* str. rep. of obj_type. */ u32 dw_key_len = 0; struct dcd_key_elem *dcd_key; int len; DBC_REQUIRE(refs >= 0); DBC_REQUIRE(index >= 0); DBC_REQUIRE(uuid_obj != NULL); if ((index != 0) && (enum_refs == 0)) { /* * If an enumeration is being performed on an index greater * than zero, then the current enum_refs must have been * incremented to greater than zero. */ status = -EIDRM; } else { /* * Pre-determine final key length. It's length of DCD_REGKEY + * "_\0" + length of sz_obj_type string + terminating NULL. */ dw_key_len = strlen(DCD_REGKEY) + 1 + sizeof(sz_obj_type) + 1; DBC_ASSERT(dw_key_len < DCD_MAXPATHLENGTH); /* Create proper REG key; concatenate DCD_REGKEY with * obj_type. */ strncpy(sz_reg_key, DCD_REGKEY, strlen(DCD_REGKEY) + 1); if ((strlen(sz_reg_key) + strlen("_\0")) < DCD_MAXPATHLENGTH) { strncat(sz_reg_key, "_\0", 2); } else { status = -EPERM; } /* This snprintf is guaranteed not to exceed max size of an * integer. */ status = snprintf(sz_obj_type, MAX_INT2CHAR_LENGTH, "%d", obj_type); if (status == -1) { status = -EPERM; } else { status = 0; if ((strlen(sz_reg_key) + strlen(sz_obj_type)) < DCD_MAXPATHLENGTH) { strncat(sz_reg_key, sz_obj_type, strlen(sz_obj_type) + 1); } else { status = -EPERM; } } if (!status) { len = strlen(sz_reg_key); spin_lock(&dbdcd_lock); list_for_each_entry(dcd_key, ®_key_list, link) { if (!strncmp(dcd_key->name, sz_reg_key, len) && !index--) { strncpy(sz_value, &dcd_key->name[len], strlen(&dcd_key->name[len]) + 1); break; } } spin_unlock(&dbdcd_lock); if (&dcd_key->link == ®_key_list) status = -ENODATA; } if (!status) { /* Create UUID value using string retrieved from * registry. */ uuid_uuid_from_string(sz_value, &dsp_uuid_obj); *uuid_obj = dsp_uuid_obj; /* Increment enum_refs to update reference count. */ enum_refs++; status = 0; } else if (status == -ENODATA) { /* At the end of enumeration. Reset enum_refs. */ enum_refs = 0; /* * TODO: Revisit, this is not an error case but code * expects non-zero value. */ status = ENODATA; } else { status = -EPERM; } } DBC_ENSURE(uuid_obj || (status == -EPERM)); return status; } /* * ======== dcd_exit ======== * Purpose: * Discontinue usage of the DCD module. */ void dcd_exit(void) { struct dcd_key_elem *rv, *rv_tmp; DBC_REQUIRE(refs > 0); refs--; if (refs == 0) { cod_exit(); list_for_each_entry_safe(rv, rv_tmp, ®_key_list, link) { list_del(&rv->link); kfree(rv->path); kfree(rv); } } DBC_ENSURE(refs >= 0); } /* * ======== dcd_get_dep_libs ======== */ int dcd_get_dep_libs(struct dcd_manager *hdcd_mgr, struct dsp_uuid *uuid_obj, u16 num_libs, struct dsp_uuid *dep_lib_uuids, bool *prstnt_dep_libs, enum nldr_phase phase) { int status = 0; DBC_REQUIRE(refs > 0); DBC_REQUIRE(hdcd_mgr); DBC_REQUIRE(uuid_obj != NULL); DBC_REQUIRE(dep_lib_uuids != NULL); DBC_REQUIRE(prstnt_dep_libs != NULL); status = get_dep_lib_info(hdcd_mgr, uuid_obj, &num_libs, NULL, dep_lib_uuids, prstnt_dep_libs, phase); return status; } /* * ======== dcd_get_num_dep_libs ======== */ int dcd_get_num_dep_libs(struct dcd_manager *hdcd_mgr, struct dsp_uuid *uuid_obj, u16 *num_libs, u16 *num_pers_libs, enum nldr_phase phase) { int status = 0; DBC_REQUIRE(refs > 0); DBC_REQUIRE(hdcd_mgr); DBC_REQUIRE(num_libs != NULL); DBC_REQUIRE(num_pers_libs != NULL); DBC_REQUIRE(uuid_obj != NULL); status = get_dep_lib_info(hdcd_mgr, uuid_obj, num_libs, num_pers_libs, NULL, NULL, phase); return status; } /* * ======== dcd_get_object_def ======== * Purpose: * Retrieves the properties of a node or processor based on the UUID and * object type. */ int dcd_get_object_def(struct dcd_manager *hdcd_mgr, struct dsp_uuid *obj_uuid, enum dsp_dcdobjtype obj_type, struct dcd_genericobj *obj_def) { struct dcd_manager *dcd_mgr_obj = hdcd_mgr; /* ptr to DCD mgr */ struct cod_libraryobj *lib = NULL; int status = 0; u32 ul_addr = 0; /* Used by cod_get_section */ u32 ul_len = 0; /* Used by cod_get_section */ u32 dw_buf_size; /* Used by REG functions */ char sz_reg_key[DCD_MAXPATHLENGTH]; char *sz_uuid; /*[MAXUUIDLEN]; */ struct dcd_key_elem *dcd_key = NULL; char sz_sect_name[MAXUUIDLEN + 2]; /* ".[UUID]\0" */ char *psz_coff_buf; u32 dw_key_len; /* Len of REG key. */ char sz_obj_type[MAX_INT2CHAR_LENGTH]; /* str. rep. of obj_type. */ DBC_REQUIRE(refs > 0); DBC_REQUIRE(obj_def != NULL); DBC_REQUIRE(obj_uuid != NULL); sz_uuid = kzalloc(MAXUUIDLEN, GFP_KERNEL); if (!sz_uuid) { status = -ENOMEM; goto func_end; } if (!hdcd_mgr) { status = -EFAULT; goto func_end; } /* Pre-determine final key length. It's length of DCD_REGKEY + * "_\0" + length of sz_obj_type string + terminating NULL */ dw_key_len = strlen(DCD_REGKEY) + 1 + sizeof(sz_obj_type) + 1; DBC_ASSERT(dw_key_len < DCD_MAXPATHLENGTH); /* Create proper REG key; concatenate DCD_REGKEY with obj_type. */ strncpy(sz_reg_key, DCD_REGKEY, strlen(DCD_REGKEY) + 1); if ((strlen(sz_reg_key) + strlen("_\0")) < DCD_MAXPATHLENGTH) strncat(sz_reg_key, "_\0", 2); else status = -EPERM; status = snprintf(sz_obj_type, MAX_INT2CHAR_LENGTH, "%d", obj_type); if (status == -1) { status = -EPERM; } else { status = 0; if ((strlen(sz_reg_key) + strlen(sz_obj_type)) < DCD_MAXPATHLENGTH) { strncat(sz_reg_key, sz_obj_type, strlen(sz_obj_type) + 1); } else { status = -EPERM; } /* Create UUID value to set in registry. */ uuid_uuid_to_string(obj_uuid, sz_uuid, MAXUUIDLEN); if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH) strncat(sz_reg_key, sz_uuid, MAXUUIDLEN); else status = -EPERM; /* Retrieve paths from the registry based on struct dsp_uuid */ dw_buf_size = DCD_MAXPATHLENGTH; } if (!status) { spin_lock(&dbdcd_lock); list_for_each_entry(dcd_key, ®_key_list, link) { if (!strncmp(dcd_key->name, sz_reg_key, strlen(sz_reg_key) + 1)) break; } spin_unlock(&dbdcd_lock); if (&dcd_key->link == ®_key_list) { status = -ENOKEY; goto func_end; } } /* Open COFF file. */ status = cod_open(dcd_mgr_obj->cod_mgr, dcd_key->path, COD_NOLOAD, &lib); if (status) { status = -EACCES; goto func_end; } /* Ensure sz_uuid + 1 is not greater than sizeof sz_sect_name. */ DBC_ASSERT((strlen(sz_uuid) + 1) < sizeof(sz_sect_name)); /* Create section name based on node UUID. A period is * pre-pended to the UUID string to form the section name. * I.e. ".24BC8D90_BB45_11d4_B756_006008BDB66F" */ strncpy(sz_sect_name, ".", 2); strncat(sz_sect_name, sz_uuid, strlen(sz_uuid)); /* Get section information. */ status = cod_get_section(lib, sz_sect_name, &ul_addr, &ul_len); if (status) { status = -EACCES; goto func_end; } /* Allocate zeroed buffer. */ psz_coff_buf = kzalloc(ul_len + 4, GFP_KERNEL); if (psz_coff_buf == NULL) { status = -ENOMEM; goto func_end; } #ifdef _DB_TIOMAP if (strstr(dcd_key->path, "iva") == NULL) { /* Locate section by objectID and read its content. */ status = cod_read_section(lib, sz_sect_name, psz_coff_buf, ul_len); } else { status = cod_read_section(lib, sz_sect_name, psz_coff_buf, ul_len); dev_dbg(bridge, "%s: Skipped Byte swap for IVA!!\n", __func__); } #else status = cod_read_section(lib, sz_sect_name, psz_coff_buf, ul_len); #endif if (!status) { /* Compres DSP buffer to conform to PC format. */ if (strstr(dcd_key->path, "iva") == NULL) { compress_buf(psz_coff_buf, ul_len, DSPWORDSIZE); } else { compress_buf(psz_coff_buf, ul_len, 1); dev_dbg(bridge, "%s: Compressing IVA COFF buffer by 1 " "for IVA!!\n", __func__); } /* Parse the content of the COFF buffer. */ status = get_attrs_from_buf(psz_coff_buf, ul_len, obj_type, obj_def); if (status) status = -EACCES; } else { status = -EACCES; } /* Free the previously allocated dynamic buffer. */ kfree(psz_coff_buf); func_end: if (lib) cod_close(lib); kfree(sz_uuid); return status; } /* * ======== dcd_get_objects ======== */ int dcd_get_objects(struct dcd_manager *hdcd_mgr, char *sz_coff_path, dcd_registerfxn register_fxn, void *handle) { struct dcd_manager *dcd_mgr_obj = hdcd_mgr; int status = 0; char *psz_coff_buf; char *psz_cur; struct cod_libraryobj *lib = NULL; u32 ul_addr = 0; /* Used by cod_get_section */ u32 ul_len = 0; /* Used by cod_get_section */ char seps[] = ":, "; char *token = NULL; struct dsp_uuid dsp_uuid_obj; s32 object_type; DBC_REQUIRE(refs > 0); if (!hdcd_mgr) { status = -EFAULT; goto func_end; } /* Open DSP coff file, don't load symbols. */ status = cod_open(dcd_mgr_obj->cod_mgr, sz_coff_path, COD_NOLOAD, &lib); if (status) { status = -EACCES; goto func_cont; } /* Get DCD_RESIGER_SECTION section information. */ status = cod_get_section(lib, DCD_REGISTER_SECTION, &ul_addr, &ul_len); if (status || !(ul_len > 0)) { status = -EACCES; goto func_cont; } /* Allocate zeroed buffer. */ psz_coff_buf = kzalloc(ul_len + 4, GFP_KERNEL); if (psz_coff_buf == NULL) { status = -ENOMEM; goto func_cont; } #ifdef _DB_TIOMAP if (strstr(sz_coff_path, "iva") == NULL) { /* Locate section by objectID and read its content. */ status = cod_read_section(lib, DCD_REGISTER_SECTION, psz_coff_buf, ul_len); } else { dev_dbg(bridge, "%s: Skipped Byte swap for IVA!!\n", __func__); status = cod_read_section(lib, DCD_REGISTER_SECTION, psz_coff_buf, ul_len); } #else status = cod_read_section(lib, DCD_REGISTER_SECTION, psz_coff_buf, ul_len); #endif if (!status) { /* Compress DSP buffer to conform to PC format. */ if (strstr(sz_coff_path, "iva") == NULL) { compress_buf(psz_coff_buf, ul_len, DSPWORDSIZE); } else { compress_buf(psz_coff_buf, ul_len, 1); dev_dbg(bridge, "%s: Compress COFF buffer with 1 word " "for IVA!!\n", __func__); } /* Read from buffer and register object in buffer. */ psz_cur = psz_coff_buf; while ((token = strsep(&psz_cur, seps)) && *token != '\0') { /* Retrieve UUID string. */ uuid_uuid_from_string(token, &dsp_uuid_obj); /* Retrieve object type */ token = strsep(&psz_cur, seps); /* Retrieve object type */ object_type = atoi(token); /* * Apply register_fxn to the found DCD object. * Possible actions include: * * 1) Register found DCD object. * 2) Unregister found DCD object (when handle == NULL) * 3) Add overlay node. */ status = register_fxn(&dsp_uuid_obj, object_type, handle); if (status) { /* if error occurs, break from while loop. */ break; } } } else { status = -EACCES; } /* Free the previously allocated dynamic buffer. */ kfree(psz_coff_buf); func_cont: if (lib) cod_close(lib); func_end: return status; } /* * ======== dcd_get_library_name ======== * Purpose: * Retrieves the library name for the given UUID. * */ int dcd_get_library_name(struct dcd_manager *hdcd_mgr, struct dsp_uuid *uuid_obj, char *str_lib_name, u32 *buff_size, enum nldr_phase phase, bool *phase_split) { char sz_reg_key[DCD_MAXPATHLENGTH]; char sz_uuid[MAXUUIDLEN]; u32 dw_key_len; /* Len of REG key. */ char sz_obj_type[MAX_INT2CHAR_LENGTH]; /* str. rep. of obj_type. */ int status = 0; struct dcd_key_elem *dcd_key = NULL; DBC_REQUIRE(uuid_obj != NULL); DBC_REQUIRE(str_lib_name != NULL); DBC_REQUIRE(buff_size != NULL); DBC_REQUIRE(hdcd_mgr); dev_dbg(bridge, "%s: hdcd_mgr %p, uuid_obj %p, str_lib_name %p," " buff_size %p\n", __func__, hdcd_mgr, uuid_obj, str_lib_name, buff_size); /* * Pre-determine final key length. It's length of DCD_REGKEY + * "_\0" + length of sz_obj_type string + terminating NULL. */ dw_key_len = strlen(DCD_REGKEY) + 1 + sizeof(sz_obj_type) + 1; DBC_ASSERT(dw_key_len < DCD_MAXPATHLENGTH); /* Create proper REG key; concatenate DCD_REGKEY with obj_type. */ strncpy(sz_reg_key, DCD_REGKEY, strlen(DCD_REGKEY) + 1); if ((strlen(sz_reg_key) + strlen("_\0")) < DCD_MAXPATHLENGTH) strncat(sz_reg_key, "_\0", 2); else status = -EPERM; switch (phase) { case NLDR_CREATE: /* create phase type */ sprintf(sz_obj_type, "%d", DSP_DCDCREATELIBTYPE); break; case NLDR_EXECUTE: /* execute phase type */ sprintf(sz_obj_type, "%d", DSP_DCDEXECUTELIBTYPE); break; case NLDR_DELETE: /* delete phase type */ sprintf(sz_obj_type, "%d", DSP_DCDDELETELIBTYPE); break; case NLDR_NOPHASE: /* known to be a dependent library */ sprintf(sz_obj_type, "%d", DSP_DCDLIBRARYTYPE); break; default: status = -EINVAL; DBC_ASSERT(false); } if (!status) { if ((strlen(sz_reg_key) + strlen(sz_obj_type)) < DCD_MAXPATHLENGTH) { strncat(sz_reg_key, sz_obj_type, strlen(sz_obj_type) + 1); } else { status = -EPERM; } /* Create UUID value to find match in registry. */ uuid_uuid_to_string(uuid_obj, sz_uuid, MAXUUIDLEN); if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH) strncat(sz_reg_key, sz_uuid, MAXUUIDLEN); else status = -EPERM; } if (!status) { spin_lock(&dbdcd_lock); list_for_each_entry(dcd_key, ®_key_list, link) { /* See if the name matches. */ if (!strncmp(dcd_key->name, sz_reg_key, strlen(sz_reg_key) + 1)) break; } spin_unlock(&dbdcd_lock); } if (&dcd_key->link == ®_key_list) status = -ENOKEY; /* If can't find, phases might be registered as generic LIBRARYTYPE */ if (status && phase != NLDR_NOPHASE) { if (phase_split) *phase_split = false; strncpy(sz_reg_key, DCD_REGKEY, strlen(DCD_REGKEY) + 1); if ((strlen(sz_reg_key) + strlen("_\0")) < DCD_MAXPATHLENGTH) { strncat(sz_reg_key, "_\0", 2); } else { status = -EPERM; } sprintf(sz_obj_type, "%d", DSP_DCDLIBRARYTYPE); if ((strlen(sz_reg_key) + strlen(sz_obj_type)) < DCD_MAXPATHLENGTH) { strncat(sz_reg_key, sz_obj_type, strlen(sz_obj_type) + 1); } else { status = -EPERM; } uuid_uuid_to_string(uuid_obj, sz_uuid, MAXUUIDLEN); if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH) strncat(sz_reg_key, sz_uuid, MAXUUIDLEN); else status = -EPERM; spin_lock(&dbdcd_lock); list_for_each_entry(dcd_key, ®_key_list, link) { /* See if the name matches. */ if (!strncmp(dcd_key->name, sz_reg_key, strlen(sz_reg_key) + 1)) break; } spin_unlock(&dbdcd_lock); status = (&dcd_key->link != ®_key_list) ? 0 : -ENOKEY; } if (!status) memcpy(str_lib_name, dcd_key->path, strlen(dcd_key->path) + 1); return status; } /* * ======== dcd_init ======== * Purpose: * Initialize the DCD module. */ bool dcd_init(void) { bool init_cod; bool ret = true; DBC_REQUIRE(refs >= 0); if (refs == 0) { /* Initialize required modules. */ init_cod = cod_init(); if (!init_cod) { ret = false; /* Exit initialized modules. */ if (init_cod) cod_exit(); } INIT_LIST_HEAD(®_key_list); } if (ret) refs++; DBC_ENSURE((ret && (refs > 0)) || (!ret && (refs == 0))); return ret; } /* * ======== dcd_register_object ======== * Purpose: * Registers a node or a processor with the DCD. * If psz_path_name == NULL, unregister the specified DCD object. */ int dcd_register_object(struct dsp_uuid *uuid_obj, enum dsp_dcdobjtype obj_type, char *psz_path_name) { int status = 0; char sz_reg_key[DCD_MAXPATHLENGTH]; char sz_uuid[MAXUUIDLEN + 1]; u32 dw_path_size = 0; u32 dw_key_len; /* Len of REG key. */ char sz_obj_type[MAX_INT2CHAR_LENGTH]; /* str. rep. of obj_type. */ struct dcd_key_elem *dcd_key = NULL; DBC_REQUIRE(refs > 0); DBC_REQUIRE(uuid_obj != NULL); DBC_REQUIRE((obj_type == DSP_DCDNODETYPE) || (obj_type == DSP_DCDPROCESSORTYPE) || (obj_type == DSP_DCDLIBRARYTYPE) || (obj_type == DSP_DCDCREATELIBTYPE) || (obj_type == DSP_DCDEXECUTELIBTYPE) || (obj_type == DSP_DCDDELETELIBTYPE)); dev_dbg(bridge, "%s: object UUID %p, obj_type %d, szPathName %s\n", __func__, uuid_obj, obj_type, psz_path_name); /* * Pre-determine final key length. It's length of DCD_REGKEY + * "_\0" + length of sz_obj_type string + terminating NULL. */ dw_key_len = strlen(DCD_REGKEY) + 1 + sizeof(sz_obj_type) + 1; DBC_ASSERT(dw_key_len < DCD_MAXPATHLENGTH); /* Create proper REG key; concatenate DCD_REGKEY with obj_type. */ strncpy(sz_reg_key, DCD_REGKEY, strlen(DCD_REGKEY) + 1); if ((strlen(sz_reg_key) + strlen("_\0")) < DCD_MAXPATHLENGTH) strncat(sz_reg_key, "_\0", 2); else { status = -EPERM; goto func_end; } status = snprintf(sz_obj_type, MAX_INT2CHAR_LENGTH, "%d", obj_type); if (status == -1) { status = -EPERM; } else { status = 0; if ((strlen(sz_reg_key) + strlen(sz_obj_type)) < DCD_MAXPATHLENGTH) { strncat(sz_reg_key, sz_obj_type, strlen(sz_obj_type) + 1); } else status = -EPERM; /* Create UUID value to set in registry. */ uuid_uuid_to_string(uuid_obj, sz_uuid, MAXUUIDLEN); if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH) strncat(sz_reg_key, sz_uuid, MAXUUIDLEN); else status = -EPERM; } if (status) goto func_end; /* * If psz_path_name != NULL, perform registration, otherwise, * perform unregistration. */ if (psz_path_name) { dw_path_size = strlen(psz_path_name) + 1; spin_lock(&dbdcd_lock); list_for_each_entry(dcd_key, ®_key_list, link) { /* See if the name matches. */ if (!strncmp(dcd_key->name, sz_reg_key, strlen(sz_reg_key) + 1)) break; } spin_unlock(&dbdcd_lock); if (&dcd_key->link == ®_key_list) { /* * Add new reg value (UUID+obj_type) * with COFF path info */ dcd_key = kmalloc(sizeof(struct dcd_key_elem), GFP_KERNEL); if (!dcd_key) { status = -ENOMEM; goto func_end; } dcd_key->path = kmalloc(strlen(sz_reg_key) + 1, GFP_KERNEL); if (!dcd_key->path) { kfree(dcd_key); status = -ENOMEM; goto func_end; } strncpy(dcd_key->name, sz_reg_key, strlen(sz_reg_key) + 1); strncpy(dcd_key->path, psz_path_name , dw_path_size); spin_lock(&dbdcd_lock); list_add_tail(&dcd_key->link, ®_key_list); spin_unlock(&dbdcd_lock); } else { /* Make sure the new data is the same. */ if (strncmp(dcd_key->path, psz_path_name, dw_path_size)) { /* The caller needs a different data size! */ kfree(dcd_key->path); dcd_key->path = kmalloc(dw_path_size, GFP_KERNEL); if (dcd_key->path == NULL) { status = -ENOMEM; goto func_end; } } /* We have a match! Copy out the data. */ memcpy(dcd_key->path, psz_path_name, dw_path_size); } dev_dbg(bridge, "%s: psz_path_name=%s, dw_path_size=%d\n", __func__, psz_path_name, dw_path_size); } else { /* Deregister an existing object */ spin_lock(&dbdcd_lock); list_for_each_entry(dcd_key, ®_key_list, link) { if (!strncmp(dcd_key->name, sz_reg_key, strlen(sz_reg_key) + 1)) { list_del(&dcd_key->link); kfree(dcd_key->path); kfree(dcd_key); break; } } spin_unlock(&dbdcd_lock); if (&dcd_key->link == ®_key_list) status = -EPERM; } if (!status) { /* * Because the node database has been updated through a * successful object registration/de-registration operation, * we need to reset the object enumeration counter to allow * current enumerations to reflect this update in the node * database. */ enum_refs = 0; } func_end: return status; } /* * ======== dcd_unregister_object ======== * Call DCD_Register object with psz_path_name set to NULL to * perform actual object de-registration. */ int dcd_unregister_object(struct dsp_uuid *uuid_obj, enum dsp_dcdobjtype obj_type) { int status = 0; DBC_REQUIRE(refs > 0); DBC_REQUIRE(uuid_obj != NULL); DBC_REQUIRE((obj_type == DSP_DCDNODETYPE) || (obj_type == DSP_DCDPROCESSORTYPE) || (obj_type == DSP_DCDLIBRARYTYPE) || (obj_type == DSP_DCDCREATELIBTYPE) || (obj_type == DSP_DCDEXECUTELIBTYPE) || (obj_type == DSP_DCDDELETELIBTYPE)); /* * When dcd_register_object is called with NULL as pathname, * it indicates an unregister object operation. */ status = dcd_register_object(uuid_obj, obj_type, NULL); return status; } /* ********************************************************************** * DCD Helper Functions ********************************************************************** */ /* * ======== atoi ======== * Purpose: * This function converts strings in decimal or hex format to integers. */ static s32 atoi(char *psz_buf) { char *pch = psz_buf; s32 base = 0; while (isspace(*pch)) pch++; if (*pch == '-' || *pch == '+') { base = 10; pch++; } else if (*pch && tolower(pch[strlen(pch) - 1]) == 'h') { base = 16; } return simple_strtoul(pch, NULL, base); } /* * ======== get_attrs_from_buf ======== * Purpose: * Parse the content of a buffer filled with DSP-side data and * retrieve an object's attributes from it. IMPORTANT: Assume the * buffer has been converted from DSP format to GPP format. */ static int get_attrs_from_buf(char *psz_buf, u32 ul_buf_size, enum dsp_dcdobjtype obj_type, struct dcd_genericobj *gen_obj) { int status = 0; char seps[] = ", "; char *psz_cur; char *token; s32 token_len = 0; u32 i = 0; #ifdef _DB_TIOMAP s32 entry_id; #endif DBC_REQUIRE(psz_buf != NULL); DBC_REQUIRE(ul_buf_size != 0); DBC_REQUIRE((obj_type == DSP_DCDNODETYPE) || (obj_type == DSP_DCDPROCESSORTYPE)); DBC_REQUIRE(gen_obj != NULL); switch (obj_type) { case DSP_DCDNODETYPE: /* * Parse COFF sect buffer to retrieve individual tokens used * to fill in object attrs. */ psz_cur = psz_buf; token = strsep(&psz_cur, seps); /* u32 cb_struct */ gen_obj->obj_data.node_obj.ndb_props.cb_struct = (u32) atoi(token); token = strsep(&psz_cur, seps); /* dsp_uuid ui_node_id */ uuid_uuid_from_string(token, &gen_obj->obj_data.node_obj.ndb_props. ui_node_id); token = strsep(&psz_cur, seps); /* ac_name */ DBC_REQUIRE(token); token_len = strlen(token); if (token_len > DSP_MAXNAMELEN - 1) token_len = DSP_MAXNAMELEN - 1; strncpy(gen_obj->obj_data.node_obj.ndb_props.ac_name, token, token_len); gen_obj->obj_data.node_obj.ndb_props.ac_name[token_len] = '\0'; token = strsep(&psz_cur, seps); /* u32 ntype */ gen_obj->obj_data.node_obj.ndb_props.ntype = atoi(token); token = strsep(&psz_cur, seps); /* u32 cache_on_gpp */ gen_obj->obj_data.node_obj.ndb_props.cache_on_gpp = atoi(token); token = strsep(&psz_cur, seps); /* dsp_resourcereqmts dsp_resource_reqmts */ gen_obj->obj_data.node_obj.ndb_props.dsp_resource_reqmts. cb_struct = (u32) atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.node_obj.ndb_props. dsp_resource_reqmts.static_data_size = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.node_obj.ndb_props. dsp_resource_reqmts.global_data_size = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.node_obj.ndb_props. dsp_resource_reqmts.program_mem_size = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.node_obj.ndb_props. dsp_resource_reqmts.wc_execution_time = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.node_obj.ndb_props. dsp_resource_reqmts.wc_period = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.node_obj.ndb_props. dsp_resource_reqmts.wc_deadline = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.node_obj.ndb_props. dsp_resource_reqmts.avg_exection_time = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.node_obj.ndb_props. dsp_resource_reqmts.minimum_period = atoi(token); token = strsep(&psz_cur, seps); /* s32 prio */ gen_obj->obj_data.node_obj.ndb_props.prio = atoi(token); token = strsep(&psz_cur, seps); /* u32 stack_size */ gen_obj->obj_data.node_obj.ndb_props.stack_size = atoi(token); token = strsep(&psz_cur, seps); /* u32 sys_stack_size */ gen_obj->obj_data.node_obj.ndb_props.sys_stack_size = atoi(token); token = strsep(&psz_cur, seps); /* u32 stack_seg */ gen_obj->obj_data.node_obj.ndb_props.stack_seg = atoi(token); token = strsep(&psz_cur, seps); /* u32 message_depth */ gen_obj->obj_data.node_obj.ndb_props.message_depth = atoi(token); token = strsep(&psz_cur, seps); /* u32 num_input_streams */ gen_obj->obj_data.node_obj.ndb_props.num_input_streams = atoi(token); token = strsep(&psz_cur, seps); /* u32 num_output_streams */ gen_obj->obj_data.node_obj.ndb_props.num_output_streams = atoi(token); token = strsep(&psz_cur, seps); /* u32 timeout */ gen_obj->obj_data.node_obj.ndb_props.timeout = atoi(token); token = strsep(&psz_cur, seps); /* char *str_create_phase_fxn */ DBC_REQUIRE(token); token_len = strlen(token); gen_obj->obj_data.node_obj.str_create_phase_fxn = kzalloc(token_len + 1, GFP_KERNEL); strncpy(gen_obj->obj_data.node_obj.str_create_phase_fxn, token, token_len); gen_obj->obj_data.node_obj.str_create_phase_fxn[token_len] = '\0'; token = strsep(&psz_cur, seps); /* char *str_execute_phase_fxn */ DBC_REQUIRE(token); token_len = strlen(token); gen_obj->obj_data.node_obj.str_execute_phase_fxn = kzalloc(token_len + 1, GFP_KERNEL); strncpy(gen_obj->obj_data.node_obj.str_execute_phase_fxn, token, token_len); gen_obj->obj_data.node_obj.str_execute_phase_fxn[token_len] = '\0'; token = strsep(&psz_cur, seps); /* char *str_delete_phase_fxn */ DBC_REQUIRE(token); token_len = strlen(token); gen_obj->obj_data.node_obj.str_delete_phase_fxn = kzalloc(token_len + 1, GFP_KERNEL); strncpy(gen_obj->obj_data.node_obj.str_delete_phase_fxn, token, token_len); gen_obj->obj_data.node_obj.str_delete_phase_fxn[token_len] = '\0'; token = strsep(&psz_cur, seps); /* Segment id for message buffers */ gen_obj->obj_data.node_obj.msg_segid = atoi(token); token = strsep(&psz_cur, seps); /* Message notification type */ gen_obj->obj_data.node_obj.msg_notify_type = atoi(token); token = strsep(&psz_cur, seps); /* char *str_i_alg_name */ if (token) { token_len = strlen(token); gen_obj->obj_data.node_obj.str_i_alg_name = kzalloc(token_len + 1, GFP_KERNEL); strncpy(gen_obj->obj_data.node_obj.str_i_alg_name, token, token_len); gen_obj->obj_data.node_obj.str_i_alg_name[token_len] = '\0'; token = strsep(&psz_cur, seps); } /* Load type (static, dynamic, or overlay) */ if (token) { gen_obj->obj_data.node_obj.load_type = atoi(token); token = strsep(&psz_cur, seps); } /* Dynamic load data requirements */ if (token) { gen_obj->obj_data.node_obj.data_mem_seg_mask = atoi(token); token = strsep(&psz_cur, seps); } /* Dynamic load code requirements */ if (token) { gen_obj->obj_data.node_obj.code_mem_seg_mask = atoi(token); token = strsep(&psz_cur, seps); } /* Extract node profiles into node properties */ if (token) { gen_obj->obj_data.node_obj.ndb_props.count_profiles = atoi(token); for (i = 0; i < gen_obj->obj_data.node_obj. ndb_props.count_profiles; i++) { token = strsep(&psz_cur, seps); if (token) { /* Heap Size for the node */ gen_obj->obj_data.node_obj. ndb_props.node_profiles[i]. heap_size = atoi(token); } } } token = strsep(&psz_cur, seps); if (token) { gen_obj->obj_data.node_obj.ndb_props.stack_seg_name = (u32) (token); } break; case DSP_DCDPROCESSORTYPE: /* * Parse COFF sect buffer to retrieve individual tokens used * to fill in object attrs. */ psz_cur = psz_buf; token = strsep(&psz_cur, seps); gen_obj->obj_data.proc_info.cb_struct = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.proc_info.processor_family = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.proc_info.processor_type = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.proc_info.clock_rate = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.proc_info.internal_mem_size = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.proc_info.external_mem_size = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.proc_info.processor_id = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.proc_info.ty_running_rtos = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.proc_info.node_min_priority = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.proc_info.node_max_priority = atoi(token); #ifdef _DB_TIOMAP /* Proc object may contain additional(extended) attributes. */ /* attr must match proc.hxx */ for (entry_id = 0; entry_id < 7; entry_id++) { token = strsep(&psz_cur, seps); gen_obj->obj_data.ext_proc_obj.ty_tlb[entry_id]. gpp_phys = atoi(token); token = strsep(&psz_cur, seps); gen_obj->obj_data.ext_proc_obj.ty_tlb[entry_id]. dsp_virt = atoi(token); } #endif break; default: status = -EPERM; break; } return status; } /* * ======== CompressBuffer ======== * Purpose: * Compress the DSP buffer, if necessary, to conform to PC format. */ static void compress_buf(char *psz_buf, u32 ul_buf_size, s32 char_size) { char *p; char ch; char *q; p = psz_buf; if (p == NULL) return; for (q = psz_buf; q < (psz_buf + ul_buf_size);) { ch = dsp_char2_gpp_char(q, char_size); if (ch == '\\') { q += char_size; ch = dsp_char2_gpp_char(q, char_size); switch (ch) { case 't': *p = '\t'; break; case 'n': *p = '\n'; break; case 'r': *p = '\r'; break; case '0': *p = '\0'; break; default: *p = ch; break; } } else { *p = ch; } p++; q += char_size; } /* NULL out remainder of buffer. */ while (p < q) *p++ = '\0'; } /* * ======== dsp_char2_gpp_char ======== * Purpose: * Convert DSP char to host GPP char in a portable manner */ static char dsp_char2_gpp_char(char *word, s32 dsp_char_size) { char ch = '\0'; char *ch_src; s32 i; for (ch_src = word, i = dsp_char_size; i > 0; i--) ch |= *ch_src++; return ch; } /* * ======== get_dep_lib_info ======== */ static int get_dep_lib_info(struct dcd_manager *hdcd_mgr, struct dsp_uuid *uuid_obj, u16 *num_libs, u16 *num_pers_libs, struct dsp_uuid *dep_lib_uuids, bool *prstnt_dep_libs, enum nldr_phase phase) { struct dcd_manager *dcd_mgr_obj = hdcd_mgr; char *psz_coff_buf = NULL; char *psz_cur; char *psz_file_name = NULL; struct cod_libraryobj *lib = NULL; u32 ul_addr = 0; /* Used by cod_get_section */ u32 ul_len = 0; /* Used by cod_get_section */ u32 dw_data_size = COD_MAXPATHLENGTH; char seps[] = ", "; char *token = NULL; bool get_uuids = (dep_lib_uuids != NULL); u16 dep_libs = 0; int status = 0; DBC_REQUIRE(refs > 0); DBC_REQUIRE(hdcd_mgr); DBC_REQUIRE(num_libs != NULL); DBC_REQUIRE(uuid_obj != NULL); /* Initialize to 0 dependent libraries, if only counting number of * dependent libraries */ if (!get_uuids) { *num_libs = 0; *num_pers_libs = 0; } /* Allocate a buffer for file name */ psz_file_name = kzalloc(dw_data_size, GFP_KERNEL); if (psz_file_name == NULL) { status = -ENOMEM; } else { /* Get the name of the library */ status = dcd_get_library_name(hdcd_mgr, uuid_obj, psz_file_name, &dw_data_size, phase, NULL); } /* Open the library */ if (!status) { status = cod_open(dcd_mgr_obj->cod_mgr, psz_file_name, COD_NOLOAD, &lib); } if (!status) { /* Get dependent library section information. */ status = cod_get_section(lib, DEPLIBSECT, &ul_addr, &ul_len); if (status) { /* Ok, no dependent libraries */ ul_len = 0; status = 0; } } if (status || !(ul_len > 0)) goto func_cont; /* Allocate zeroed buffer. */ psz_coff_buf = kzalloc(ul_len + 4, GFP_KERNEL); if (psz_coff_buf == NULL) status = -ENOMEM; /* Read section contents. */ status = cod_read_section(lib, DEPLIBSECT, psz_coff_buf, ul_len); if (status) goto func_cont; /* Compress and format DSP buffer to conform to PC format. */ compress_buf(psz_coff_buf, ul_len, DSPWORDSIZE); /* Read from buffer */ psz_cur = psz_coff_buf; while ((token = strsep(&psz_cur, seps)) && *token != '\0') { if (get_uuids) { if (dep_libs >= *num_libs) { /* Gone beyond the limit */ break; } else { /* Retrieve UUID string. */ uuid_uuid_from_string(token, &(dep_lib_uuids [dep_libs])); /* Is this library persistent? */ token = strsep(&psz_cur, seps); prstnt_dep_libs[dep_libs] = atoi(token); dep_libs++; } } else { /* Advanc to next token */ token = strsep(&psz_cur, seps); if (atoi(token)) (*num_pers_libs)++; /* Just counting number of dependent libraries */ (*num_libs)++; } } func_cont: if (lib) cod_close(lib); /* Free previously allocated dynamic buffers. */ kfree(psz_file_name); kfree(psz_coff_buf); return status; }