| /* |
| * 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. |
| */ |
| |
| #include <linux/types.h> |
| |
| /* ----------------------------------- Host OS */ |
| #include <dspbridge/host_os.h> |
| #include <linux/fs.h> |
| #include <linux/uaccess.h> |
| |
| /* ----------------------------------- DSP/BIOS Bridge */ |
| #include <dspbridge/dbdefs.h> |
| |
| /* ----------------------------------- Platform Manager */ |
| /* Include appropriate loader header file */ |
| #include <dspbridge/dbll.h> |
| |
| /* ----------------------------------- This */ |
| #include <dspbridge/cod.h> |
| |
| /* |
| * ======== cod_manager ======== |
| */ |
| struct cod_manager { |
| struct dbll_tar_obj *target; |
| struct dbll_library_obj *base_lib; |
| bool loaded; /* Base library loaded? */ |
| u32 entry; |
| struct dbll_fxns fxns; |
| struct dbll_attrs attrs; |
| char sz_zl_file[COD_MAXPATHLENGTH]; |
| }; |
| |
| /* |
| * ======== cod_libraryobj ======== |
| */ |
| struct cod_libraryobj { |
| struct dbll_library_obj *dbll_lib; |
| struct cod_manager *cod_mgr; |
| }; |
| |
| 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 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 *sz_mode) |
| { |
| 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; |
| |
| 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(struct cod_manager **mgr, char *str_zl_file) |
| { |
| struct cod_manager *mgr_new; |
| struct dbll_attrs zl_attrs; |
| int status = 0; |
| |
| /* assume failure */ |
| *mgr = NULL; |
| |
| mgr_new = kzalloc(sizeof(struct cod_manager), GFP_KERNEL); |
| if (mgr_new == NULL) |
| return -ENOMEM; |
| |
| /* 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 (status) { |
| cod_delete(mgr_new); |
| return -ESPIPE; |
| } |
| |
| /* return the new manager */ |
| *mgr = mgr_new; |
| |
| return 0; |
| } |
| |
| /* |
| * ======== cod_delete ======== |
| * Purpose: |
| * Delete a code manager object. |
| */ |
| void cod_delete(struct cod_manager *cod_mgr_obj) |
| { |
| if (cod_mgr_obj->base_lib) { |
| if (cod_mgr_obj->loaded) |
| cod_mgr_obj->fxns.unload_fxn(cod_mgr_obj->base_lib, |
| &cod_mgr_obj->attrs); |
| |
| cod_mgr_obj->fxns.close_fxn(cod_mgr_obj->base_lib); |
| } |
| if (cod_mgr_obj->target) { |
| cod_mgr_obj->fxns.delete_fxn(cod_mgr_obj->target); |
| cod_mgr_obj->fxns.exit_fxn(); |
| } |
| kfree(cod_mgr_obj); |
| } |
| |
| /* |
| * ======== 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; |
| |
| *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 *sz_name, |
| u32 usize) |
| { |
| int status = 0; |
| |
| if (usize <= COD_MAXPATHLENGTH) |
| strlcpy(sz_name, 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 *entry_pt) |
| { |
| *entry_pt = cod_mgr_obj->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 **loader) |
| { |
| int status = 0; |
| |
| *loader = (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, char *str_sect, |
| u32 *addr, u32 *len) |
| { |
| struct cod_manager *cod_mgr_obj; |
| int status = 0; |
| |
| *addr = 0; |
| *len = 0; |
| if (lib != NULL) { |
| cod_mgr_obj = lib->cod_mgr; |
| status = cod_mgr_obj->fxns.get_sect_fxn(lib->dbll_lib, str_sect, |
| addr, len); |
| } else { |
| status = -ESPIPE; |
| } |
| |
| 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 *cod_mgr_obj, char *str_sym, |
| u32 *pul_value) |
| { |
| struct dbll_sym_val *dbll_sym; |
| |
| dev_dbg(bridge, "%s: cod_mgr_obj: %p str_sym: %s pul_value: %p\n", |
| __func__, cod_mgr_obj, str_sym, pul_value); |
| if (cod_mgr_obj->base_lib) { |
| if (!cod_mgr_obj->fxns. |
| get_addr_fxn(cod_mgr_obj->base_lib, str_sym, &dbll_sym)) { |
| if (!cod_mgr_obj->fxns. |
| get_c_addr_fxn(cod_mgr_obj->base_lib, str_sym, |
| &dbll_sym)) |
| return -ESPIPE; |
| } |
| } else { |
| return -ESPIPE; |
| } |
| |
| *pul_value = dbll_sym->value; |
| |
| return 0; |
| } |
| |
| /* |
| * ======== 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 *cod_mgr_obj, 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; |
| |
| /* |
| * 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 */ |
| cod_mgr_obj->fxns.get_attrs_fxn(cod_mgr_obj->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 = cod_mgr_obj->fxns.load_fxn(cod_mgr_obj->base_lib, flags, |
| &new_attrs, |
| &cod_mgr_obj->entry); |
| if (status) |
| cod_mgr_obj->fxns.close_fxn(cod_mgr_obj->base_lib); |
| |
| if (!status) |
| cod_mgr_obj->loaded = true; |
| else |
| cod_mgr_obj->base_lib = NULL; |
| |
| return status; |
| } |
| |
| /* |
| * ======== cod_open ======== |
| * Open library for reading sections. |
| */ |
| int cod_open(struct cod_manager *hmgr, char *sz_coff_path, |
| u32 flags, struct cod_libraryobj **lib_obj) |
| { |
| int status = 0; |
| struct cod_libraryobj *lib = NULL; |
| |
| *lib_obj = NULL; |
| |
| lib = kzalloc(sizeof(struct cod_libraryobj), GFP_KERNEL); |
| if (lib == NULL) |
| status = -ENOMEM; |
| |
| if (!status) { |
| lib->cod_mgr = hmgr; |
| status = hmgr->fxns.open_fxn(hmgr->target, sz_coff_path, flags, |
| &lib->dbll_lib); |
| if (!status) |
| *lib_obj = lib; |
| } |
| |
| if (status) |
| pr_err("%s: error status 0x%x, sz_coff_path: %s flags: 0x%x\n", |
| __func__, status, sz_coff_path, flags); |
| return status; |
| } |
| |
| /* |
| * ======== cod_open_base ======== |
| * Purpose: |
| * Open base image for reading sections. |
| */ |
| int cod_open_base(struct cod_manager *hmgr, char *sz_coff_path, |
| dbll_flags flags) |
| { |
| int status = 0; |
| struct dbll_library_obj *lib; |
| |
| /* 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, sz_coff_path, flags, &lib); |
| if (!status) { |
| /* hang onto the library for subsequent sym table usage */ |
| hmgr->base_lib = lib; |
| strncpy(hmgr->sz_zl_file, sz_coff_path, COD_MAXPATHLENGTH - 1); |
| hmgr->sz_zl_file[COD_MAXPATHLENGTH - 1] = '\0'; |
| } |
| |
| if (status) |
| pr_err("%s: error status 0x%x sz_coff_path: %s\n", __func__, |
| status, sz_coff_path); |
| 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, char *str_sect, |
| char *str_content, u32 content_size) |
| { |
| int status = 0; |
| |
| if (lib != NULL) |
| status = |
| lib->cod_mgr->fxns.read_sect_fxn(lib->dbll_lib, str_sect, |
| str_content, content_size); |
| else |
| status = -ESPIPE; |
| |
| return status; |
| } |
| |
| /* |
| * ======== no_op ======== |
| * Purpose: |
| * No Operation. |
| * |
| */ |
| static bool no_op(void) |
| { |
| return true; |
| } |