/* * cod.c * * DSP-BIOS Bridge driver support functions for TI OMAP processors. * * This module implements DSP code management for the DSP/BIOS Bridge * environment. It is mostly a thin wrapper. * * This module provides an interface for loading both static and * dynamic code objects onto DSP systems. * * 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. */ /* ----------------------------------- Host OS */ #include #include #include /* ----------------------------------- DSP/BIOS Bridge */ #include #include /* ----------------------------------- Trace & Debug */ #include /* ----------------------------------- OS Adaptation Layer */ #include /* ----------------------------------- Platform Manager */ /* Include appropriate loader header file */ #include /* ----------------------------------- This */ #include /* magic number for handle validation */ #define MAGIC 0xc001beef /* macro to validate COD manager handles */ #define IS_VALID(h) ((h) != NULL && (h)->ul_magic == MAGIC) /* * ======== cod_manager ======== */ struct cod_manager { struct dbll_tar_obj *target; struct dbll_library_obj *base_lib; bool loaded; /* Base library loaded? */ u32 ul_entry; struct ldr_module *dll_obj; struct dbll_fxns fxns; struct dbll_attrs attrs; char sz_zl_file[COD_MAXPATHLENGTH]; u32 ul_magic; }; /* * ======== cod_libraryobj ======== */ struct cod_libraryobj { struct dbll_library_obj *dbll_lib; struct cod_manager *cod_mgr; }; static u32 refs = 0L; 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 bool no_op(void); /* * File operations (originally were under kfile.c) */ static s32 cod_f_close(struct file *filp) { /* Check for valid handle */ if (!filp) return -EFAULT; filp_close(filp, NULL); /* we can't use 0 here */ return 0; } static struct file *cod_f_open(CONST char *psz_file_name, CONST char *pszMode) { mm_segment_t fs; struct file *filp; fs = get_fs(); set_fs(get_ds()); /* ignore given mode and open file as read-only */ filp = filp_open(psz_file_name, O_RDONLY, 0); if (IS_ERR(filp)) filp = NULL; set_fs(fs); return filp; } static s32 cod_f_read(void __user *pbuffer, s32 size, s32 count, struct file *filp) { /* check for valid file handle */ if (!filp) return -EFAULT; if ((size > 0) && (count > 0) && pbuffer) { u32 dw_bytes_read; mm_segment_t fs; /* read from file */ fs = get_fs(); set_fs(get_ds()); dw_bytes_read = filp->f_op->read(filp, pbuffer, size * count, &(filp->f_pos)); set_fs(fs); if (!dw_bytes_read) return -EBADF; return dw_bytes_read / size; } return -EINVAL; } static s32 cod_f_seek(struct file *filp, s32 offset, s32 origin) { loff_t dw_cur_pos; /* check for valid file handle */ if (!filp) return -EFAULT; /* based on the origin flag, move the internal pointer */ dw_cur_pos = filp->f_op->llseek(filp, offset, origin); if ((s32) dw_cur_pos < 0) return -EPERM; /* we can't use 0 here */ return 0; } static s32 cod_f_tell(struct file *filp) { loff_t dw_cur_pos; if (!filp) return -EFAULT; /* Get current position */ dw_cur_pos = filp->f_op->llseek(filp, 0, SEEK_CUR); if ((s32) dw_cur_pos < 0) return -EPERM; return dw_cur_pos; } /* * ======== cod_close ======== */ void cod_close(struct cod_libraryobj *lib) { struct cod_manager *hmgr; DBC_REQUIRE(refs > 0); DBC_REQUIRE(lib != NULL); DBC_REQUIRE(IS_VALID(((struct cod_libraryobj *)lib)->cod_mgr)); hmgr = lib->cod_mgr; hmgr->fxns.close_fxn(lib->dbll_lib); kfree(lib); } /* * ======== cod_create ======== * Purpose: * Create an object to manage code on a DSP system. * This object can be used to load an initial program image with * arguments that can later be expanded with * dynamically loaded object files. * */ int cod_create(OUT struct cod_manager **phMgr, char *pstrDummyFile, IN OPTIONAL CONST struct cod_attrs *attrs) { struct cod_manager *mgr_new; struct dbll_attrs zl_attrs; int status = 0; DBC_REQUIRE(refs > 0); DBC_REQUIRE(phMgr != NULL); /* assume failure */ *phMgr = NULL; /* we don't support non-default attrs yet */ if (attrs != NULL) return -ENOSYS; mgr_new = kzalloc(sizeof(struct cod_manager), GFP_KERNEL); if (mgr_new == NULL) return -ENOMEM; mgr_new->ul_magic = MAGIC; /* Set up loader functions */ mgr_new->fxns = ldr_fxns; /* initialize the ZL module */ mgr_new->fxns.init_fxn(); zl_attrs.alloc = (dbll_alloc_fxn) no_op; zl_attrs.free = (dbll_free_fxn) no_op; zl_attrs.fread = (dbll_read_fxn) cod_f_read; zl_attrs.fseek = (dbll_seek_fxn) cod_f_seek; zl_attrs.ftell = (dbll_tell_fxn) cod_f_tell; zl_attrs.fclose = (dbll_f_close_fxn) cod_f_close; zl_attrs.fopen = (dbll_f_open_fxn) cod_f_open; zl_attrs.sym_lookup = NULL; zl_attrs.base_image = true; zl_attrs.log_write = NULL; zl_attrs.log_write_handle = NULL; zl_attrs.write = NULL; zl_attrs.rmm_handle = NULL; zl_attrs.input_params = NULL; zl_attrs.sym_handle = NULL; zl_attrs.sym_arg = NULL; mgr_new->attrs = zl_attrs; status = mgr_new->fxns.create_fxn(&mgr_new->target, &zl_attrs); if (DSP_FAILED(status)) { cod_delete(mgr_new); return -ESPIPE; } /* return the new manager */ *phMgr = mgr_new; return 0; } /* * ======== cod_delete ======== * Purpose: * Delete a code manager object. */ void cod_delete(struct cod_manager *hmgr) { DBC_REQUIRE(refs > 0); DBC_REQUIRE(IS_VALID(hmgr)); if (hmgr->base_lib) { if (hmgr->loaded) hmgr->fxns.unload_fxn(hmgr->base_lib, &hmgr->attrs); hmgr->fxns.close_fxn(hmgr->base_lib); } if (hmgr->target) { hmgr->fxns.delete_fxn(hmgr->target); hmgr->fxns.exit_fxn(); } hmgr->ul_magic = ~MAGIC; kfree(hmgr); } /* * ======== cod_exit ======== * Purpose: * Discontinue usage of the COD module. * */ void cod_exit(void) { DBC_REQUIRE(refs > 0); refs--; DBC_ENSURE(refs >= 0); } /* * ======== cod_get_base_lib ======== * Purpose: * Get handle to the base image DBL library. */ int cod_get_base_lib(struct cod_manager *cod_mgr_obj, struct dbll_library_obj **plib) { int status = 0; DBC_REQUIRE(refs > 0); DBC_REQUIRE(IS_VALID(cod_mgr_obj)); DBC_REQUIRE(plib != NULL); *plib = (struct dbll_library_obj *)cod_mgr_obj->base_lib; return status; } /* * ======== cod_get_base_name ======== */ int cod_get_base_name(struct cod_manager *cod_mgr_obj, char *pszName, u32 usize) { int status = 0; DBC_REQUIRE(refs > 0); DBC_REQUIRE(IS_VALID(cod_mgr_obj)); DBC_REQUIRE(pszName != NULL); if (usize <= COD_MAXPATHLENGTH) strncpy(pszName, cod_mgr_obj->sz_zl_file, usize); else status = -EPERM; return status; } /* * ======== cod_get_entry ======== * Purpose: * Retrieve the entry point of a loaded DSP program image * */ int cod_get_entry(struct cod_manager *cod_mgr_obj, u32 *pulEntry) { DBC_REQUIRE(refs > 0); DBC_REQUIRE(IS_VALID(cod_mgr_obj)); DBC_REQUIRE(pulEntry != NULL); *pulEntry = cod_mgr_obj->ul_entry; return 0; } /* * ======== cod_get_loader ======== * Purpose: * Get handle to the DBLL loader. */ int cod_get_loader(struct cod_manager *cod_mgr_obj, struct dbll_tar_obj **phLoader) { int status = 0; DBC_REQUIRE(refs > 0); DBC_REQUIRE(IS_VALID(cod_mgr_obj)); DBC_REQUIRE(phLoader != NULL); *phLoader = (struct dbll_tar_obj *)cod_mgr_obj->target; return status; } /* * ======== cod_get_section ======== * Purpose: * Retrieve the starting address and length of a section in the COFF file * given the section name. */ int cod_get_section(struct cod_libraryobj *lib, IN char *pstrSect, OUT u32 *puAddr, OUT u32 *puLen) { struct cod_manager *cod_mgr_obj; int status = 0; DBC_REQUIRE(refs > 0); DBC_REQUIRE(lib != NULL); DBC_REQUIRE(IS_VALID(lib->cod_mgr)); DBC_REQUIRE(pstrSect != NULL); DBC_REQUIRE(puAddr != NULL); DBC_REQUIRE(puLen != NULL); *puAddr = 0; *puLen = 0; if (lib != NULL) { cod_mgr_obj = lib->cod_mgr; status = cod_mgr_obj->fxns.get_sect_fxn(lib->dbll_lib, pstrSect, puAddr, puLen); } else { status = -ESPIPE; } DBC_ENSURE(DSP_SUCCEEDED(status) || ((*puAddr == 0) && (*puLen == 0))); return status; } /* * ======== cod_get_sym_value ======== * Purpose: * Retrieve the value for the specified symbol. The symbol is first * searched for literally and then, if not found, searched for as a * C symbol. * */ int cod_get_sym_value(struct cod_manager *hmgr, char *pstrSym, u32 *pul_value) { struct dbll_sym_val *dbll_sym; DBC_REQUIRE(refs > 0); DBC_REQUIRE(IS_VALID(hmgr)); DBC_REQUIRE(pstrSym != NULL); DBC_REQUIRE(pul_value != NULL); dev_dbg(bridge, "%s: hmgr: %p pstrSym: %s pul_value: %p\n", __func__, hmgr, pstrSym, pul_value); if (hmgr->base_lib) { if (!hmgr->fxns. get_addr_fxn(hmgr->base_lib, pstrSym, &dbll_sym)) { if (!hmgr->fxns. get_c_addr_fxn(hmgr->base_lib, pstrSym, &dbll_sym)) return -ESPIPE; } } else { return -ESPIPE; } *pul_value = dbll_sym->value; return 0; } /* * ======== cod_init ======== * Purpose: * Initialize the COD module's private state. * */ bool cod_init(void) { bool ret = true; DBC_REQUIRE(refs >= 0); if (ret) refs++; DBC_ENSURE((ret && refs > 0) || (!ret && refs >= 0)); return ret; } /* * ======== cod_load_base ======== * Purpose: * Load the initial program image, optionally with command-line arguments, * on the DSP system managed by the supplied handle. The program to be * loaded must be the first element of the args array and must be a fully * qualified pathname. * Details: * if num_argc doesn't match the number of arguments in the args array, the * args array is searched for a NULL terminating entry, and argc is * recalculated to reflect this. In this way, we can support NULL * terminating args arrays, if num_argc is very large. */ int cod_load_base(struct cod_manager *hmgr, u32 num_argc, char *args[], cod_writefxn pfn_write, void *arb, char *envp[]) { dbll_flags flags; struct dbll_attrs save_attrs; struct dbll_attrs new_attrs; int status; u32 i; DBC_REQUIRE(refs > 0); DBC_REQUIRE(IS_VALID(hmgr)); DBC_REQUIRE(num_argc > 0); DBC_REQUIRE(args != NULL); DBC_REQUIRE(args[0] != NULL); DBC_REQUIRE(pfn_write != NULL); DBC_REQUIRE(hmgr->base_lib != NULL); /* * Make sure every argv[] stated in argc has a value, or change argc to * reflect true number in NULL terminated argv array. */ for (i = 0; i < num_argc; i++) { if (args[i] == NULL) { num_argc = i; break; } } /* set the write function for this operation */ hmgr->fxns.get_attrs_fxn(hmgr->target, &save_attrs); new_attrs = save_attrs; new_attrs.write = (dbll_write_fxn) pfn_write; new_attrs.input_params = arb; new_attrs.alloc = (dbll_alloc_fxn) no_op; new_attrs.free = (dbll_free_fxn) no_op; new_attrs.log_write = NULL; new_attrs.log_write_handle = NULL; /* Load the image */ flags = DBLL_CODE | DBLL_DATA | DBLL_SYMB; status = hmgr->fxns.load_fxn(hmgr->base_lib, flags, &new_attrs, &hmgr->ul_entry); if (DSP_FAILED(status)) hmgr->fxns.close_fxn(hmgr->base_lib); if (DSP_SUCCEEDED(status)) hmgr->loaded = true; else hmgr->base_lib = NULL; return status; } /* * ======== cod_open ======== * Open library for reading sections. */ int cod_open(struct cod_manager *hmgr, IN char *pszCoffPath, u32 flags, struct cod_libraryobj **pLib) { int status = 0; struct cod_libraryobj *lib = NULL; DBC_REQUIRE(refs > 0); DBC_REQUIRE(IS_VALID(hmgr)); DBC_REQUIRE(pszCoffPath != NULL); DBC_REQUIRE(flags == COD_NOLOAD || flags == COD_SYMB); DBC_REQUIRE(pLib != NULL); *pLib = NULL; lib = kzalloc(sizeof(struct cod_libraryobj), GFP_KERNEL); if (lib == NULL) status = -ENOMEM; if (DSP_SUCCEEDED(status)) { lib->cod_mgr = hmgr; status = hmgr->fxns.open_fxn(hmgr->target, pszCoffPath, flags, &lib->dbll_lib); if (DSP_SUCCEEDED(status)) *pLib = lib; } if (DSP_FAILED(status)) pr_err("%s: error status 0x%x, pszCoffPath: %s flags: 0x%x\n", __func__, status, pszCoffPath, flags); return status; } /* * ======== cod_open_base ======== * Purpose: * Open base image for reading sections. */ int cod_open_base(struct cod_manager *hmgr, IN char *pszCoffPath, dbll_flags flags) { int status = 0; struct dbll_library_obj *lib; DBC_REQUIRE(refs > 0); DBC_REQUIRE(IS_VALID(hmgr)); DBC_REQUIRE(pszCoffPath != NULL); /* if we previously opened a base image, close it now */ if (hmgr->base_lib) { if (hmgr->loaded) { hmgr->fxns.unload_fxn(hmgr->base_lib, &hmgr->attrs); hmgr->loaded = false; } hmgr->fxns.close_fxn(hmgr->base_lib); hmgr->base_lib = NULL; } status = hmgr->fxns.open_fxn(hmgr->target, pszCoffPath, flags, &lib); if (DSP_SUCCEEDED(status)) { /* hang onto the library for subsequent sym table usage */ hmgr->base_lib = lib; strncpy(hmgr->sz_zl_file, pszCoffPath, COD_MAXPATHLENGTH - 1); hmgr->sz_zl_file[COD_MAXPATHLENGTH - 1] = '\0'; } if (DSP_FAILED(status)) pr_err("%s: error status 0x%x pszCoffPath: %s\n", __func__, status, pszCoffPath); return status; } /* * ======== cod_read_section ======== * Purpose: * Retrieve the content of a code section given the section name. */ int cod_read_section(struct cod_libraryobj *lib, IN char *pstrSect, OUT char *pstrContent, IN u32 content_size) { int status = 0; DBC_REQUIRE(refs > 0); DBC_REQUIRE(lib != NULL); DBC_REQUIRE(IS_VALID(lib->cod_mgr)); DBC_REQUIRE(pstrSect != NULL); DBC_REQUIRE(pstrContent != NULL); if (lib != NULL) status = lib->cod_mgr->fxns.read_sect_fxn(lib->dbll_lib, pstrSect, pstrContent, content_size); else status = -ESPIPE; return status; } /* * ======== no_op ======== * Purpose: * No Operation. * */ static bool no_op(void) { return true; }