51d5e099cc
Since status succeeded is 0, DSP_FAILED macro is not necessary anymore. Signed-off-by: Ernesto Ramos <ernesto@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
653 lines
14 KiB
C
653 lines
14 KiB
C
/*
|
|
* 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>
|
|
|
|
/* ----------------------------------- Trace & Debug */
|
|
#include <dspbridge/dbc.h>
|
|
|
|
/* ----------------------------------- OS Adaptation Layer */
|
|
#include <dspbridge/ldr.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 ul_entry;
|
|
struct ldr_module *dll_obj;
|
|
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 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 *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;
|
|
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(lib != NULL);
|
|
DBC_REQUIRE(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(struct cod_manager **mgr, char *str_zl_file,
|
|
const struct cod_attrs *attrs)
|
|
{
|
|
struct cod_manager *mgr_new;
|
|
struct dbll_attrs zl_attrs;
|
|
int status = 0;
|
|
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(mgr != NULL);
|
|
|
|
/* assume failure */
|
|
*mgr = 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;
|
|
|
|
/* 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)
|
|
{
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(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_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(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 *sz_name,
|
|
u32 usize)
|
|
{
|
|
int status = 0;
|
|
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(cod_mgr_obj);
|
|
DBC_REQUIRE(sz_name != NULL);
|
|
|
|
if (usize <= COD_MAXPATHLENGTH)
|
|
strncpy(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)
|
|
{
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(cod_mgr_obj);
|
|
DBC_REQUIRE(entry_pt != NULL);
|
|
|
|
*entry_pt = 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 **loader)
|
|
{
|
|
int status = 0;
|
|
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(cod_mgr_obj);
|
|
DBC_REQUIRE(loader != NULL);
|
|
|
|
*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;
|
|
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(lib != NULL);
|
|
DBC_REQUIRE(lib->cod_mgr);
|
|
DBC_REQUIRE(str_sect != NULL);
|
|
DBC_REQUIRE(addr != NULL);
|
|
DBC_REQUIRE(len != NULL);
|
|
|
|
*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;
|
|
}
|
|
|
|
DBC_ENSURE(!status || ((*addr == 0) && (*len == 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 *cod_mgr_obj, char *str_sym,
|
|
u32 *pul_value)
|
|
{
|
|
struct dbll_sym_val *dbll_sym;
|
|
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(cod_mgr_obj);
|
|
DBC_REQUIRE(str_sym != NULL);
|
|
DBC_REQUIRE(pul_value != NULL);
|
|
|
|
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_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 *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;
|
|
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(cod_mgr_obj);
|
|
DBC_REQUIRE(num_argc > 0);
|
|
DBC_REQUIRE(args != NULL);
|
|
DBC_REQUIRE(args[0] != NULL);
|
|
DBC_REQUIRE(pfn_write != NULL);
|
|
DBC_REQUIRE(cod_mgr_obj->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 */
|
|
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->ul_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;
|
|
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(hmgr);
|
|
DBC_REQUIRE(sz_coff_path != NULL);
|
|
DBC_REQUIRE(flags == COD_NOLOAD || flags == COD_SYMB);
|
|
DBC_REQUIRE(lib_obj != 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;
|
|
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(hmgr);
|
|
DBC_REQUIRE(sz_coff_path != 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, 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;
|
|
|
|
DBC_REQUIRE(refs > 0);
|
|
DBC_REQUIRE(lib != NULL);
|
|
DBC_REQUIRE(lib->cod_mgr);
|
|
DBC_REQUIRE(str_sect != NULL);
|
|
DBC_REQUIRE(str_content != NULL);
|
|
|
|
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;
|
|
}
|