From 00c1159858283f23bc7e890668bb2c3c442aca79 Mon Sep 17 00:00:00 2001 From: Kaz Fukuoka Date: Fri, 30 Jul 2010 13:13:01 -0700 Subject: [PATCH] [ARM] tegra: stingray: CPU-AVP RPC in kernel - /dev/nvfw ioctl interface to load AVP firmware. - Use request_firmware() for AVP modules - /dev/nvrpc ioctl interface to call RPC on AVP. - Server thread to serve RPC from AVP. Change-Id: I1694dc49d69b677cd225f8b68a4f84edf9bf0a23 Signed-off-by: Dima Zavin --- arch/arm/mach-tegra/nv/include/mach/nvrpc.h | 102 + arch/arm/mach-tegra/nv/include/nvfw.h | 54 + .../mach-tegra/nv/include/nvrm_moduleloader.h | 179 ++ .../arm/mach-tegra/nv/nvrm/core/ap15/Makefile | 1 + .../nv/nvrm/core/ap15/ap15rm_avp_service.c | 319 ++++ .../mach-tegra/nv/nvrm/core/common/Makefile | 6 +- .../nv/nvrm/core/common/nvrm_avp_cpu_rpc.c | 381 ++++ .../nvrm/core/common/nvrm_avp_swi_registry.h | 171 ++ .../nvrm/core/common/nvrm_graphics_private.h | 77 + .../nv/nvrm/core/common/nvrm_init_stub.c | 34 + .../nv/nvrm/core/common/nvrm_message.h | 68 +- .../nv/nvrm/core/common/nvrm_moduleloader.c | 1651 +++++++++++++++++ .../core/common/nvrm_moduleloader_private.h | 184 ++ .../mach-tegra/nv/nvrm/core/common/nvrm_rpc.h | 198 ++ .../nv/nvrm/core/common/nvrm_transport.c | 29 +- .../nv/nvrm/dispatch/nvrm_power_dispatch.c | 2 +- arch/arm/mach-tegra/nv/nvrpc_user.c | 68 +- 17 files changed, 3457 insertions(+), 67 deletions(-) create mode 100755 arch/arm/mach-tegra/nv/include/mach/nvrpc.h create mode 100644 arch/arm/mach-tegra/nv/include/nvfw.h create mode 100644 arch/arm/mach-tegra/nv/include/nvrm_moduleloader.h create mode 100644 arch/arm/mach-tegra/nv/nvrm/core/ap15/ap15rm_avp_service.c create mode 100644 arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_avp_cpu_rpc.c create mode 100644 arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_avp_swi_registry.h create mode 100644 arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_graphics_private.h create mode 100644 arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_init_stub.c create mode 100644 arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_moduleloader.c create mode 100644 arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_moduleloader_private.h create mode 100644 arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_rpc.h diff --git a/arch/arm/mach-tegra/nv/include/mach/nvrpc.h b/arch/arm/mach-tegra/nv/include/mach/nvrpc.h new file mode 100755 index 000000000000..594e1f5d4e94 --- /dev/null +++ b/arch/arm/mach-tegra/nv/include/mach/nvrpc.h @@ -0,0 +1,102 @@ +/* + * arch/arm/mach-tegra/include/linux/nvrpc_ioctl.h + * + * structure declarations for nvrpc user-space ioctls + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#if !defined(__KERNEL__) +#define __user +#endif + +#ifndef _MACH_TEGRA_NVRPC_IOCTL_H_ +#define _MACH_TEGRA_NVRPC_IOCTL_H_ + +struct nvrpc_handle_param { + __u32 handle; + __u32 param; + __u32 ret_val; /* operation status */ +}; + +struct nvrpc_open_params { + __u32 rm_handle; /* rm device handle */ + __u32 port_name_size; /* port name buffer size */ + __u32 sem; /* receive semaphore handle */ + __u32 transport_handle; /* transport handle */ + __u32 ret_val; /* operation status */ + unsigned long port_name; /* port name */ +}; + +struct nvrpc_set_queue_depth_params { + __u32 transport_handle; /* transport handle */ + __u32 max_queue_depth; /* maximum number of message in Queue */ + __u32 max_message_size; /* maximum size of the message in bytes */ + __u32 ret_val; /* operation status */ +}; + +struct nvrpc_msg_params { + __u32 transport_handle; /* transport handle */ + __u32 max_message_size; /* maximum size of the message in bytes */ + __u32 params; /* timeout in ms */ + __u32 ret_val; /* operation status */ + unsigned long msg_buffer; +}; + +#define NVRPC_IOC_MAGIC 'N' + +#define NVRPC_IOCTL_INIT \ + _IOWR(NVRPC_IOC_MAGIC, 0x30, struct nvrpc_handle_param) +#define NVRPC_IOCTL_OPEN \ + _IOWR(NVRPC_IOC_MAGIC, 0x31, struct nvrpc_open_params) +#define NVRPC_IOCTL_GET_PORTNAME \ + _IOWR(NVRPC_IOC_MAGIC, 0x32, struct nvrpc_open_params) +#define NVRPC_IOCTL_CLOSE \ + _IOWR(NVRPC_IOC_MAGIC, 0x33, struct nvrpc_handle_param) +#define NVRPC_IOCTL_DEINIT \ + _IOWR(NVRPC_IOC_MAGIC, 0x34, struct nvrpc_handle_param) +#define NVRPC_IOCTL_WAIT_FOR_CONNECT \ + _IOWR(NVRPC_IOC_MAGIC, 0x35, struct nvrpc_handle_param) +#define NVRPC_IOCTL_CONNECT \ + _IOWR(NVRPC_IOC_MAGIC, 0x36, struct nvrpc_handle_param) +#define NVRPC_IOCTL_SET_QUEUE_DEPTH \ + _IOWR(NVRPC_IOC_MAGIC, 0x37, struct nvrpc_set_queue_depth_params) +#define NVRPC_IOCTL_SEND_MSG \ + _IOWR(NVRPC_IOC_MAGIC, 0x38, struct nvrpc_msg_params) +#define NVRPC_IOCTL_SEND_MSG_LP0 \ + _IOWR(NVRPC_IOC_MAGIC, 0x39, struct nvrpc_msg_params) +#define NVRPC_IOCTL_RECV_MSG \ + _IOWR(NVRPC_IOC_MAGIC, 0x3A, struct nvrpc_msg_params) +#define NVRPC_IOCTL_XPC_INIT \ + _IOWR(NVRPC_IOC_MAGIC, 0x3B, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_ACQUIRE \ + _IOWR(NVRPC_IOC_MAGIC, 0x3C, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_RELEASE \ + _IOWR(NVRPC_IOC_MAGIC, 0x3D, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_GET_MSG \ + _IOWR(NVRPC_IOC_MAGIC, 0x3E, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_SEND_MSG \ + _IOWR(NVRPC_IOC_MAGIC, 0x3F, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_DESTROY \ + _IOWR(NVRPC_IOC_MAGIC, 0x40, struct nvrpc_handle_param) +#define NVRPC_IOCTL_XPC_CREATE \ + _IOWR(NVRPC_IOC_MAGIC, 0x41, struct nvrpc_handle_param) + +#endif diff --git a/arch/arm/mach-tegra/nv/include/nvfw.h b/arch/arm/mach-tegra/nv/include/nvfw.h new file mode 100644 index 000000000000..e6b496b5d6e0 --- /dev/null +++ b/arch/arm/mach-tegra/nv/include/nvfw.h @@ -0,0 +1,54 @@ +/* + * arch/arm/mach-tegra/include/linux/nvfw_ioctl.h + * + * structure declarations for nvfw ioctls + * + * Copyright (c) 2009, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#if !defined(__KERNEL__) +#define __user +#endif + +#ifndef _MACH_TEGRA_NVFW_IOCTL_H_ +#define _MACH_TEGRA_NVFW_IOCTL_H_ + +struct nvfw_load_handle { + const char *filename; + int length; + void *args; + int argssize; + int greedy; + void *handle; +}; + +struct nvfw_get_proc_address_handle { + const char *symbolname; + int length; + void *address; + void *handle; +}; + +#define NVFW_IOC_MAGIC 'N' +#define NVFW_IOC_LOAD_LIBRARY _IOWR(NVFW_IOC_MAGIC, 0x50, struct nvfw_load_handle) +#define NVFW_IOC_LOAD_LIBRARY_EX _IOWR(NVFW_IOC_MAGIC, 0x51, struct nvfw_load_handle) +#define NVFW_IOC_FREE_LIBRARY _IOW (NVFW_IOC_MAGIC, 0x52, struct nvfw_load_handle) +#define NVFW_IOC_GET_PROC_ADDRESS _IOWR(NVFW_IOC_MAGIC, 0x53, struct nvfw_load_handle) + +#endif diff --git a/arch/arm/mach-tegra/nv/include/nvrm_moduleloader.h b/arch/arm/mach-tegra/nv/include/nvrm_moduleloader.h new file mode 100644 index 000000000000..ab8761c970c3 --- /dev/null +++ b/arch/arm/mach-tegra/nv/include/nvrm_moduleloader.h @@ -0,0 +1,179 @@ +/* + * arch/arm/mach-tegra/include/nvrm_moduleloader.h + * + * + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_nvrm_moduleloader_H +#define INCLUDED_nvrm_moduleloader_H + + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#include "nvrm_init.h" + +#include "nvcommon.h" +#include "nvos.h" + +/** + * NvRmLibraryHandle is an opaque handle to the Module Loader interface + * + * @ingroup nvrm_moduleloader + */ + +typedef struct NvRmLibraryRec *NvRmLibraryHandle; + +/** + * @brief Defines the pin state + */ + +typedef enum +{ + NvRmModuleLoaderReason_Attach = 0, + NvRmModuleLoaderReason_Detach, + NvRmModuleLoaderReason_Num, + NvRmModuleLoaderReason_Force32 = 0x7FFFFFFF +} NvRmModuleLoaderReason; + +/** + * Loads the segments of requested library name. + * This method will parse the ELF dynamic library, relocate the address, + * resolve the symbols and load the segments accordingly. + * A successful load should return a valid handle. + * + * If some of the parameters passed are not valid assert + * encountered in debug mode. + * + * @ingroup nvrm_moduleloader + * + * @param hDevice The handle to the RM device + * @param pLibName The library to be loaded. + * @param pArgs The arguments to be passed. + * @param sizeOfArgs The size of arguments passed. + * @param hLibHandle The handle to the loaded library + * + * @retval NvSuccess Load library operation completed successfully + * @retval NvError_FileReadFailed Indicates that the fileoffset read failed + * @retval NvError_LibraryNotFound Indicates the given library could not be found + * @retval NvError_InsufficientMemory Indicates memory allocation failed + * @retval NvError_InvalidElfFormat Indicates the ELF file is not valid + */ + + NvError NvRmLoadLibrary( + NvRmDeviceHandle hDevice, + const char * pLibName, + void* pArgs, + NvU32 sizeOfArgs, + NvRmLibraryHandle * hLibHandle ); + +/** + * Loads the segments of requested library name.This method will parse the ELF dynamic + * library, relocate the address, resolve the symbols and load the segments depending + * on the conservative or greedy approach. In both the approaches the the IRAM_MAND + * sections are loaded in IRAM and DRAM_MAND sections are loaded in DRAM. In conservative + * approach the IRAM_PREF sections are always loaded in SDRAM. In greedy approach + * the IRAM_PREF sections are first laoded in IRAM. If IRAM allocation fails for an IRAM_PREF + * section, it would fallback to DRAM. A successful load should return a valid handle. + * + * IRAM_MAND_ADDR = 0x40000000 + * DRAM_MAND_ADDR = 0x10000000 + * Then + * If (vaddr < DRAM_MAND_ADDR) + * IRAM_PREF Section + * Else (vaddr >= IRAM_MAND_ADDR) + * IRAM_MAND Section + * Else + * DRAM_MAND Section + * + * If some of the parameters passed are not valid assert + * encountered in debug mode. + * + * @ingroup nvrm_moduleloader + * + * @param hDevice The handle to the RM device + * @param pLibName The library to be loaded. + * @param pArgs The arguments to be passed. + * @param sizeOfArgs The size of arguments passed. + * @param IsApproachGreedy The approach used to load the segments. + * @param hLibHandle The handle to the loaded library + * + * @retval NvSuccess Load library operation completed successfully + * @retval NvError_FileReadFailed Indicates that the fileoffset read failed + * @retval NvError_LibraryNotFound Indicates the given library could not be found + * @retval NvError_InsufficientMemory Indicates memory allocation failed + * @retval NvError_InvalidElfFormat Indicates the ELF file is not valid + */ + + NvError NvRmLoadLibraryEx( + NvRmDeviceHandle hDevice, + const char * pLibName, + void* pArgs, + NvU32 sizeOfArgs, + NvBool IsApproachGreedy, + NvRmLibraryHandle * hLibHandle ); + +/** + * Get symbol address for a given symbol name and handle. + * + * Client will request for symbol address for a export function by + * sending down the symbol name and handle to the loaded library. + * + * Assert encountered if some of the parameters passed are not valid + * + * NOTE: This function is currently only used to obtain the entry + * point address (ie, the address of "main"). It should be noted + * that the entry point must ALWAYS be in THUMB mode! Using ARM + * mode will cause the module to crash. + * + * @ingroup nvrm_moduleloader + * + * @param hLibHandle Library handle which is returned by NvRmLoadLibrary(). + * @param pSymbolName pointer to a symbol name to be looked up + * @param pSymAddress pointer to a symbol address + * + * @retval NvSuccess Symbol address is obtained successfully. + * @retval NvError_SymbolNotFound Indicates the symbol requested is not found + */ + + NvError NvRmGetProcAddress( + NvRmLibraryHandle hLibHandle, + const char * pSymbolName, + void* * pSymAddress ); + +/** + * Free the losded memory of the corresponding library handle. + * + * This API will use the handle to get the base loaded address and free the memory + * + * @param hLibHandle The handle which is returned by NvRmLoadLibrary(). + * + * @retval NvSuccess Successfuly unloaded the library memory. + */ + + NvError NvRmFreeLibrary( + NvRmLibraryHandle hLibHandle ); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/arch/arm/mach-tegra/nv/nvrm/core/ap15/Makefile b/arch/arm/mach-tegra/nv/nvrm/core/ap15/Makefile index 5d94bc4ca846..b5e63bd493e1 100644 --- a/arch/arm/mach-tegra/nv/nvrm/core/ap15/Makefile +++ b/arch/arm/mach-tegra/nv/nvrm/core/ap15/Makefile @@ -11,5 +11,6 @@ ccflags-y += -Iarch/arm/mach-tegra/nv/nvrm/core/common ccflags-y += -Iarch/arm/mach-tegra/nv/nvrm/core #obj-y += ap15rm_init.o +obj-y += ap15rm_avp_service.o obj-y += ap15rm_xpc.o obj-y += ap15rm_xpc_hw_private.o diff --git a/arch/arm/mach-tegra/nv/nvrm/core/ap15/ap15rm_avp_service.c b/arch/arm/mach-tegra/nv/nvrm/core/ap15/ap15rm_avp_service.c new file mode 100644 index 000000000000..53f503210cb8 --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/ap15/ap15rm_avp_service.c @@ -0,0 +1,319 @@ +/* + * arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_avp_service.c + * + * AVP service to handle AVP messages. + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** @file + * @brief NVIDIA Driver Development Kit: + * Testcases for the xpc + * + * @b Description: This file implements the AVP service to handle AVP messages. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvcommon.h" +#include "nvassert.h" +#include "nvrm_drf.h" +#include "nvrm_init.h" +#include "nvrm_message.h" +#include "nvrm_rpc.h" +#include "nvrm_moduleloader_private.h" +#include "nvrm_graphics_private.h" +#include "ap15/arres_sema.h" +#include "ap15/arflow_ctlr.h" +#include "ap15/arapbpm.h" +#include "nvrm_avp_swi_registry.h" +#include "ap15/arevp.h" +#include "nvrm_hardware_access.h" +#include "mach/io.h" +#include "mach/iomap.h" + +static NvU32 s_AvpInitialized = NV_FALSE; +static NvRmLibraryHandle s_hAvpLibrary = NULL; + +#define NV_USE_AOS 1 + +#define AVP_EXECUTABLE_NAME "nvrm_avp.axf" + +void NvRmPrivProcessMessage(NvRmRPCHandle hRPCHandle, char *pRecvMessage, int messageLength) +{ + NvError Error = NvSuccess; + NvRmMemHandle hMem; + + switch ((NvRmMsg)*pRecvMessage) { + + case NvRmMsg_MemHandleCreate: + { + NvRmMessage_HandleCreat *msgHandleCreate = NULL; + NvRmMessage_HandleCreatResponse msgRHandleCreate; + + msgHandleCreate = (NvRmMessage_HandleCreat*)pRecvMessage; + + msgRHandleCreate.msg = NvRmMsg_MemHandleCreate; + Error = NvRmMemHandleCreate(hRPCHandle->hRmDevice,&hMem, msgHandleCreate->size); + if (!Error) { + msgRHandleCreate.hMem = hMem; + } + msgRHandleCreate.msg = NvRmMsg_MemHandleCreate_Response; + msgRHandleCreate.error = Error; + + NvRmPrivRPCSendMsg(hRPCHandle, &msgRHandleCreate, + sizeof(msgRHandleCreate)); + } + break; + case NvRmMsg_MemHandleOpen: + break; + case NvRmMsg_MemHandleFree: + { + NvRmMessage_HandleFree *msgHandleFree = NULL; + msgHandleFree = (NvRmMessage_HandleFree*)pRecvMessage; + NvRmMemHandleFree(msgHandleFree->hMem); + } + break; + case NvRmMsg_MemAlloc: + { + NvRmMessage_MemAlloc *msgMemAlloc = NULL; + NvRmMessage_Response msgResponse; + msgMemAlloc = (NvRmMessage_MemAlloc*)pRecvMessage; + + Error = NvRmMemAlloc(msgMemAlloc->hMem, + (msgMemAlloc->NumHeaps == 0) ? NULL : msgMemAlloc->Heaps, + msgMemAlloc->NumHeaps, + msgMemAlloc->Alignment, + msgMemAlloc->Coherency); + msgResponse.msg = NvRmMsg_MemAlloc_Response; + msgResponse.error = Error; + + NvRmPrivRPCSendMsg(hRPCHandle, &msgResponse, sizeof(msgResponse)); + } + break; + case NvRmMsg_MemPin: + { + NvRmMessage_Pin *msgHandleFree = NULL; + NvRmMessage_PinResponse msgResponse; + msgHandleFree = (NvRmMessage_Pin*)pRecvMessage; + + msgResponse.address = NvRmMemPin(msgHandleFree->hMem); + msgResponse.msg = NvRmMsg_MemPin_Response; + + NvRmPrivRPCSendMsg(hRPCHandle, &msgResponse, sizeof(msgResponse)); + } + break; + case NvRmMsg_MemUnpin: + { + NvRmMessage_HandleFree *msgHandleFree = NULL; + NvRmMessage_Response msgResponse; + msgHandleFree = (NvRmMessage_HandleFree*)pRecvMessage; + + NvRmMemUnpin(msgHandleFree->hMem); + + msgResponse.msg = NvRmMsg_MemUnpin_Response; + msgResponse.error = NvSuccess; + + NvRmPrivRPCSendMsg(hRPCHandle, &msgResponse, sizeof(msgResponse)); + } + break; + case NvRmMsg_MemGetAddress: + { + NvRmMessage_GetAddress *msgGetAddress = NULL; + NvRmMessage_GetAddressResponse msgGetAddrResponse; + + msgGetAddress = (NvRmMessage_GetAddress*)pRecvMessage; + + msgGetAddrResponse.msg = NvRmMsg_MemGetAddress_Response; + msgGetAddrResponse.address = NvRmMemGetAddress(msgGetAddress->hMem,msgGetAddress->Offset); + + NvRmPrivRPCSendMsg(hRPCHandle, &msgGetAddrResponse, sizeof(msgGetAddrResponse)); + } + break; + case NvRmMsg_HandleFromId : + { + NvRmMessage_HandleFromId *msgHandleFromId = NULL; + NvRmMessage_Response msgResponse; + NvRmMemHandle hMem; + + msgHandleFromId = (NvRmMessage_HandleFromId*)pRecvMessage; + + msgResponse.msg = NvRmMsg_HandleFromId_Response; + msgResponse.error = NvRmMemHandleFromId(msgHandleFromId->id, &hMem); + + NvRmPrivRPCSendMsg(hRPCHandle, &msgResponse, sizeof(NvRmMessage_Response)); + } + break; + case NvRmMsg_PowerModuleClockControl: + { + NvRmMessage_Module *msgPMCC; + NvRmMessage_Response msgPMCCR; + msgPMCC = (NvRmMessage_Module*)pRecvMessage; + + msgPMCCR.msg = NvRmMsg_PowerModuleClockControl_Response; + msgPMCCR.error = NvRmPowerModuleClockControl(hRPCHandle->hRmDevice, + msgPMCC->ModuleId, + msgPMCC->ClientId, + msgPMCC->Enable); + + NvRmPrivRPCSendMsg(hRPCHandle, &msgPMCCR, sizeof(msgPMCCR)); + } + break; + case NvRmMsg_ModuleReset: + { + NvRmMessage_Module *msgPMCC; + NvRmMessage_Response msgPMCCR; + msgPMCC = (NvRmMessage_Module*)pRecvMessage; + + msgPMCCR.msg = NvRmMsg_ModuleReset_Response; + + NvRmModuleReset(hRPCHandle->hRmDevice, msgPMCC->ModuleId); + /// Send response since clients to this call needs to wait + /// for some time before they can start using the HW module + NvRmPrivRPCSendMsg(hRPCHandle, &msgPMCCR, sizeof(msgPMCCR)); + } + break; + + case NvRmMsg_PowerRegister: + { + NvRmMessage_PowerRegister *msgPower; + NvRmMessage_PowerRegister_Response msgResponse; + + msgPower = (NvRmMessage_PowerRegister*)pRecvMessage; + + msgResponse.msg = NvRmMsg_PowerResponse; + msgResponse.error = NvSuccess; + msgResponse.clientId = msgPower->clientId; + + NvRmPrivRPCSendMsg(hRPCHandle, &msgResponse, sizeof(msgResponse)); + + } + break; + + case NvRmMsg_PowerUnRegister: + break; + case NvRmMsg_PowerStarvationHint: + case NvRmMsg_PowerBusyHint: + { + NvRmMessage_Response msgResponse; + msgResponse.msg = NvRmMsg_PowerResponse; + msgResponse.error = NvSuccess; + NvRmPrivRPCSendMsg(hRPCHandle, &msgResponse, sizeof(msgResponse)); + } + break; + case NvRmMsg_PowerBusyMultiHint: + break; + case NvRmMsg_PowerDfsGetState: + { + NvRmMessage_PowerDfsGetState_Response msgResponse; + msgResponse.msg = NvRmMsg_PowerDfsGetState_Response; + msgResponse.state = NvRmDfsRunState_Stopped; + NvRmPrivRPCSendMsg(hRPCHandle, &msgResponse, sizeof(msgResponse)); + } + break; + case NvRmMsg_PowerModuleGetMaxFreq: + { + NvRmMessage_PowerModuleGetMaxFreq_Response msgResponse; + msgResponse.msg = NvRmMsg_PowerModuleGetMaxFreq; + msgResponse.freqKHz = 0; + NvRmPrivRPCSendMsg(hRPCHandle, &msgResponse, sizeof(msgResponse)); + } + break; + case NvRmMsg_PowerDfsGetClockUtilization: + { + NvRmMessage_PowerDfsGetClockUtilization_Response msgResponse; + NvRmDfsClockUsage ClockUsage = { 0, 0, 0, 0, 0, 0 }; + + msgResponse.msg = NvRmMsg_PowerDfsGetClockUtilization_Response; + msgResponse.error = NvSuccess; + NvOsMemcpy(&msgResponse.clockUsage, &ClockUsage, sizeof(ClockUsage)); + NvRmPrivRPCSendMsg(hRPCHandle, &msgResponse, sizeof(msgResponse)); + } + break; + case NvRmMsg_InitiateLP0: + { + //Just for testing purposes. + } + break; + case NvRmMsg_RemotePrintf: + { + NvRmMessage_RemotePrintf *msg; + + msg = (NvRmMessage_RemotePrintf*)pRecvMessage; + NvOsDebugPrintf("AVP: %s", msg->string); + } + break; + case NvRmMsg_AVP_Reset: + NvOsDebugPrintf("AVP has been reset by WDT\n"); + break; + default: + NV_ASSERT( !"AVP Service::ProcessMessage: bad message" ); + break; + } +} + +NvError +NvRmPrivInitAvp(NvRmDeviceHandle hDevice) +{ + NvError err = NvSuccess; + void* avpExecutionJumpAddress; + NvU32 RegVal, resetVector; + + // Do this only once. + if (s_AvpInitialized) return NvSuccess; + + NvOsDebugPrintf("%s : called\n", __func__); + + err = NvRmPrivLoadKernelLibrary(hDevice, AVP_EXECUTABLE_NAME, &s_hAvpLibrary); + if (err != NvSuccess) { + NV_DEBUG_PRINTF(("AVP executable file not found\n")); + NV_ASSERT(!"AVP executable file not found"); + } + + err = NvRmGetProcAddress(s_hAvpLibrary, "main", &avpExecutionJumpAddress); + NV_ASSERT(err == NvSuccess); + NvOsDebugPrintf("%s : avpExecutionJumpAddress=%x\n", __func__, avpExecutionJumpAddress); + + resetVector = (NvU32)avpExecutionJumpAddress & 0xFFFFFFFE; + + NV_WRITE32(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + EVP_COP_RESET_VECTOR_0, resetVector); + RegVal = NV_READ32(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + EVP_COP_RESET_VECTOR_0); + NV_ASSERT( RegVal == resetVector ); + + NvRmModuleReset(hDevice, NvRmModuleID_Avp); + + /// Resume AVP + RegVal = NV_DRF_DEF(FLOW_CTLR, HALT_COP_EVENTS, MODE, FLOW_MODE_NONE); + NV_WRITE32(IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + FLOW_CTLR_HALT_COP_EVENTS_0, RegVal); + + s_AvpInitialized = NV_TRUE; + + err = NvRmPrivInitService(hDevice); + if (err) return err; + + err = NvRmPrivInitModuleLoaderRPC(hDevice); + if (err) return err; + + return err; +} diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/Makefile b/arch/arm/mach-tegra/nv/nvrm/core/common/Makefile index e57ea8d3dec9..eb84beed8bdd 100644 --- a/arch/arm/mach-tegra/nv/nvrm/core/common/Makefile +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/Makefile @@ -10,7 +10,11 @@ ccflags-y += -Iarch/arm/mach-tegra/nv/include ccflags-y += -Iarch/arm/mach-tegra/nv/nvrm/core/common ccflags-y += -Iarch/arm/mach-tegra/nv/nvrm/core +obj-y += nvrm_avp_cpu_rpc.o +obj-y += nvrm_moduleloader.o obj-y += nvrm_rmctrace.o obj-y += nvrm_transport.o obj-y += nvrm_module_stub.o -obj-y += nvrm_power.o \ No newline at end of file +obj-y += nvrm_power.o +obj-y += nvrm_init_stub.o + diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_avp_cpu_rpc.c b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_avp_cpu_rpc.c new file mode 100644 index 000000000000..60c259db00cd --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_avp_cpu_rpc.c @@ -0,0 +1,381 @@ +/* + * arch/arm/mach-tegra/nvrm/core/common/nvrm_avp_cpu_rpc.c + * + * Transport API + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** @file + * @brief NVIDIA Driver Development Kit: + * Transport API + * + * @b Description: This is the wrapper implementation of Transport API. + */ + +#include +#include + +#include "nvrm_transport.h" +#include "nvrm_xpc.h" +#include "nvrm_rpc.h" +#include "nvrm_interrupt.h" +#include "nvassert.h" +#include "nvrm_graphics_private.h" + +/* global variable passed from nvrpc_user.c */ +extern NvRmTransportHandle g_hTransportAvp; +extern NvRmTransportHandle g_hTransportCpu; +extern NvOsSemaphoreHandle g_hTransportAvpSem; +extern NvOsSemaphoreHandle g_hTransportCpuSem; +extern int g_hTransportAvpIsConnected; +extern int g_hTransportCpuIsConnected; + +/* local variables for handles */ +static NvOsThreadHandle s_RecvThreadId_Service; +static NvRmRPCHandle gs_hRPCHandle = NULL; +volatile static int s_ContinueProcessing = 1; + +#if !NV_IS_AVP +#define PORT_NAME "RPC_CPU_PORT" +#else +#define PORT_NAME "RPC_AVP_PORT" +#endif +/* Receive message port thread */ +static void ServiceThread( void *args ); +static void ServiceThread( void *args ) +{ + NvError Error = NvSuccess; + static NvU8 ReceiveMessage[MAX_MESSAGE_LENGTH]; + NvU32 MessageLength = 0; + + NvOsDebugPrintf("%s: started\n", __func__); + + Error = NvRmPrivRPCWaitForConnect(gs_hRPCHandle); + if (Error) + { + goto exit_gracefully; + } + while (s_ContinueProcessing) + { + Error = NvRmPrivRPCRecvMsg(gs_hRPCHandle, ReceiveMessage, + &MessageLength); + if (Error == NvError_InvalidState) + { + break; + } + if (!Error) + { + ReceiveMessage[MessageLength] = '\0'; + } + NvRmPrivProcessMessage(gs_hRPCHandle, (char*)ReceiveMessage, + MessageLength); + } + +exit_gracefully: + return; +} + +NvError NvRmPrivRPCInit(NvRmDeviceHandle hDeviceHandle, char* portName, + NvRmRPCHandle *hRPCHandle ) +{ + NvError Error = NvSuccess; + + NvOsDebugPrintf("%s: portName=%s\n", __func__, portName); + + *hRPCHandle = NvOsAlloc(sizeof(NvRmRPC)); + if (!*hRPCHandle) + { + Error = NvError_InsufficientMemory; + return Error; + } + + Error = NvOsMutexCreate(&(*hRPCHandle)->RecvLock); + if( Error != NvSuccess) + { + goto clean_up; + } + + if (! portName) { + panic("%s: No port name.\n", __func__); + } + if (! strcmp(portName, "RPC_AVP_PORT")) { + if (g_hTransportAvp) panic("%s: g_hTransportAvp is already set.\n", __func__); + NvOsDebugPrintf("%s: creating RPC_AVP_PORT.\n", __func__); + + Error = NvOsSemaphoreCreate(&g_hTransportAvpSem, 0); + if (Error != NvSuccess) panic(__func__); + + Error = NvRmTransportOpen(hDeviceHandle, portName, g_hTransportAvpSem, + &g_hTransportAvp); + if (Error != NvSuccess) panic(__func__); + + (*hRPCHandle)->svcTransportHandle = g_hTransportAvp; + (*hRPCHandle)->TransportRecvSemId = g_hTransportAvpSem; + (*hRPCHandle)->isConnected = g_hTransportAvpIsConnected; + NvOsDebugPrintf("%s: isConnected=%d\n", __func__, g_hTransportAvpIsConnected); + } + if (! strcmp(portName, "RPC_CPU_PORT")) { + if (g_hTransportCpu) panic("%s: g_hTransportCpu is already set.\n", __func__); + NvOsDebugPrintf("%s: creating RPC_CPU_PORT.\n", __func__); + + Error = NvOsSemaphoreCreate(&g_hTransportCpuSem, 0); + if (Error != NvSuccess) panic(__func__); + + Error = NvRmTransportOpen(hDeviceHandle, portName, g_hTransportCpuSem, + &g_hTransportCpu); + if (Error != NvSuccess) panic(__func__); + + (*hRPCHandle)->svcTransportHandle = g_hTransportCpu; + (*hRPCHandle)->TransportRecvSemId = g_hTransportCpuSem; + (*hRPCHandle)->isConnected = g_hTransportCpuIsConnected; + NvOsDebugPrintf("%s: isConnected=%d\n", __func__, g_hTransportCpuIsConnected); + } + (*hRPCHandle)->hRmDevice = hDeviceHandle; + +clean_up: + return Error; +} + +void NvRmPrivRPCDeInit( NvRmRPCHandle hRPCHandle ) +{ + if(hRPCHandle != NULL) + { + if(hRPCHandle->svcTransportHandle != NULL) + { + NvOsSemaphoreDestroy(hRPCHandle->TransportRecvSemId); + NvOsMutexDestroy(hRPCHandle->RecvLock); + NvRmTransportClose(hRPCHandle->svcTransportHandle); + hRPCHandle->svcTransportHandle = NULL; + hRPCHandle->isConnected = NV_FALSE; + } + NvOsFree(hRPCHandle); + } +} + +void NvRmPrivRPCSendMsg(NvRmRPCHandle hRPCHandle, + void* pMessageBuffer, + NvU32 MessageSize) +{ + NvError Error = NvSuccess; + NV_ASSERT(hRPCHandle->svcTransportHandle != NULL); + + NvOsMutexLock(hRPCHandle->RecvLock); + Error = NvRmTransportSendMsg(hRPCHandle->svcTransportHandle, + pMessageBuffer, MessageSize, NV_WAIT_INFINITE); + NvOsMutexUnlock(hRPCHandle->RecvLock); + if(Error) + NV_ASSERT(Error == NvSuccess); +} + +void NvRmPrivRPCSendMsgWithResponse( NvRmRPCHandle hRPCHandle, + void* pRecvMessageBuffer, + NvU32 MaxSize, + NvU32 * pMessageSize, + void* pSendMessageBuffer, + NvU32 MessageSize) +{ + NvError Error = NvSuccess; + NvOsDebugPrintf("%s: started\n", __func__); + NV_ASSERT(hRPCHandle->svcTransportHandle != NULL); + + NvOsMutexLock(hRPCHandle->RecvLock); + NvOsDebugPrintf("%s: calling NvRmTransportSendMsg\n", __func__); + Error = NvRmTransportSendMsg(hRPCHandle->svcTransportHandle, + pSendMessageBuffer, MessageSize, NV_WAIT_INFINITE); + if (Error) + { + // TODO: Determine cause of error and pass appropriate error to caller. + NvOsDebugPrintf("%s: error in NvRmTransportSendMsg\n", __func__); + goto clean_up; + } + NvOsDebugPrintf("%s: returned from NvRmTransportSendMsg\n", __func__); + + NvOsDebugPrintf("%s: NvOsSemaphoreWait(TransportRecvSemId=%x)\n", __func__,hRPCHandle->TransportRecvSemId); + NvOsSemaphoreWait(hRPCHandle->TransportRecvSemId); + + NvOsDebugPrintf("%s: calling NvRmTransportRecvMsg\n", __func__); + Error = NvRmTransportRecvMsg(hRPCHandle->svcTransportHandle, + pRecvMessageBuffer, MaxSize, pMessageSize); + if (Error) + { + NvOsDebugPrintf("%s: error in NvRmTransportRecvMsg\n", __func__); + goto clean_up; + } + NvOsDebugPrintf("%s: returned from NvRmTransportRecvMsg\n", __func__); + +clean_up: + NV_ASSERT(Error == NvSuccess); + NvOsMutexUnlock(hRPCHandle->RecvLock); +} + +NvError NvRmPrivRPCWaitForConnect( NvRmRPCHandle hRPCHandle ) +{ + NvError Error = NvSuccess; + + NV_ASSERT(hRPCHandle != NULL); + NV_ASSERT(hRPCHandle->svcTransportHandle != NULL); + + if (hRPCHandle->isConnected) panic("%s: line=%d\n", __func__, __LINE__); + if(hRPCHandle->isConnected == NV_FALSE) + { + Error = NvRmTransportSetQueueDepth(hRPCHandle->svcTransportHandle, + MAX_QUEUE_DEPTH, MAX_MESSAGE_LENGTH); + if (Error) + { + goto clean_up; + } + Error = NvError_InvalidState; + // Connect to the other end + while (s_ContinueProcessing) + { + /* NvOsDebugPrintf("%s: NvRmTransportWaitForConnect(handle=%x)\n", __func__, hRPCHandle->svcTransportHandle); */ + Error = NvRmTransportWaitForConnect( + hRPCHandle->svcTransportHandle, 100 ); + /* NvOsDebugPrintf("%s: NvRmTransportWaitForConnect=%d\n", __func__, Error); */ + if (Error == NvSuccess) + { + hRPCHandle->isConnected = NV_TRUE; + break; + } + // if there is some other issue than a timeout, then bail out. + if (Error != NvError_Timeout) + { + goto clean_up; + } + } + } + +clean_up: + return Error; +} + +NvError NvRmPrivRPCConnect( NvRmRPCHandle hRPCHandle ) +{ + NvError Error = NvSuccess; + + NV_ASSERT(hRPCHandle != NULL); + NV_ASSERT(hRPCHandle->svcTransportHandle != NULL); + + /* if (hRPCHandle->isConnected) panic("%s: line=%d\n", __func__, __LINE__); */ + NvOsMutexLock(hRPCHandle->RecvLock); + if(hRPCHandle->isConnected == NV_TRUE) + { + goto clean_up; + } + Error = NvRmTransportSetQueueDepth(hRPCHandle->svcTransportHandle, + MAX_QUEUE_DEPTH, MAX_MESSAGE_LENGTH); + if (Error) + { + goto clean_up; + } + Error = NvError_InvalidState; + +#define CONNECTION_TIMEOUT (20 * 1000) + + // Connect to the other end with a large timeout + // Timeout value has been increased to suit slow enviornments like + // emulation FPGAs + NvOsDebugPrintf("%s: NvRmTransportConnect(handle=%x)\n", __func__, hRPCHandle->svcTransportHandle); + Error = NvRmTransportConnect(hRPCHandle->svcTransportHandle, + CONNECTION_TIMEOUT ); + NvOsDebugPrintf("%s: NvRmTransportConnect=%d\n", __func__, Error); + if(Error == NvSuccess) + { + NvOsDebugPrintf("%s: Connected.\n", __func__); + hRPCHandle->isConnected = NV_TRUE; + } + else + { + NvOsDebugPrintf("%s: Not connected.\n", __func__); + } + +#undef CONNECTION_TIMEOUT + +clean_up: + NvOsMutexUnlock(hRPCHandle->RecvLock); + return Error; +} + +NvError NvRmPrivRPCRecvMsg( NvRmRPCHandle hRPCHandle, void* pMessageBuffer, + NvU32 * pMessageSize ) +{ + NvError Error = NvSuccess; + NV_ASSERT(hRPCHandle->svcTransportHandle != NULL); + + if (s_ContinueProcessing == 0) + { + Error = NvError_InvalidState; + goto clean_up; + } + + NvOsSemaphoreWait(hRPCHandle->TransportRecvSemId); + if(s_ContinueProcessing != 0) + { + + Error = NvRmTransportRecvMsg(hRPCHandle->svcTransportHandle, + pMessageBuffer, MAX_MESSAGE_LENGTH, pMessageSize); + } + else + { + Error = NvError_InvalidState; + } +clean_up: + return Error; +} + +void NvRmPrivRPCClose( NvRmRPCHandle hRPCHandle ) +{ + // signal the thread to exit + s_ContinueProcessing = 0; + if(hRPCHandle && hRPCHandle->svcTransportHandle != NULL) + { + if (hRPCHandle->TransportRecvSemId) + NvOsSemaphoreSignal(hRPCHandle->TransportRecvSemId); + } +} + +NvError NvRmPrivInitService(NvRmDeviceHandle hDeviceHandle) +{ + NvError Error = NvSuccess; + + NvOsDebugPrintf("%s : called\n", __func__); + Error = NvRmPrivRPCInit(hDeviceHandle, PORT_NAME, &gs_hRPCHandle); + if( Error != NvSuccess) + { + goto exit_gracefully; + } + NV_ASSERT(gs_hRPCHandle != NULL); + +#if !NV_IS_AVP + Error = NvOsInterruptPriorityThreadCreate(ServiceThread, NULL, + &s_RecvThreadId_Service); + NvOsDebugPrintf("%s: s_RecvThreadId_Service=%p\n", __func__, s_RecvThreadId_Service); +#else + Error = NvOsThreadCreate(ServiceThread, NULL, &s_RecvThreadId_Service); +#endif + +exit_gracefully: + return Error; +} + +void NvRmPrivServiceDeInit() +{ + NvRmPrivRPCClose(gs_hRPCHandle); + NvOsThreadJoin(s_RecvThreadId_Service); + NvRmPrivRPCDeInit(gs_hRPCHandle); +} diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_avp_swi_registry.h b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_avp_swi_registry.h new file mode 100644 index 000000000000..712bca98efde --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_avp_swi_registry.h @@ -0,0 +1,171 @@ +/* + * arch/arm/mach-tegra/nvrm/core/common/nvrm_avp_swi_registry.h + * + * + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_NVRM_AVP_SWI_REGISTRY_H +#define INCLUDED_NVRM_AVP_SWI_REGISTRY_H + +#include "nvcommon.h" +#include "nvos.h" +#include "nvrm_power.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +enum {MAX_CLIENTS = 5}; +enum {MAX_SWI_PER_CLIENT = 32}; +enum {CLIENT_SWI_NUM_START = 0xD0}; + +typedef NvError (*AvpClientSwiHandler) (int *pRegs); + +typedef enum +{ + AvpSwiClientType_None = 0, + AvpSwiClientType_NvMM, + AvpSwiClientType_Force32 = 0x7fffffff +} AvpSwiClientType; + +typedef enum { + AvpSwiClientSwiNum_NvMM = 0xD0, + AvpSwiClientSwiNum_Force32 = 0x7fffffff +} AvpSwiClientSwiNum; + +typedef struct AvpSwiClientRec +{ + AvpClientSwiHandler ClientSwiHandler[MAX_SWI_PER_CLIENT]; + AvpSwiClientType ClientId; + AvpSwiClientSwiNum SwiNum; +} AvpSwiClient; + +typedef struct AvpSwiClientRegistryRec +{ + AvpSwiClient SwiClient[MAX_CLIENTS]; + NvU32 RefCount; + NvOsMutexHandle Mutex; +}AvpSwiClientRegistry; + +NvError +NvRmAvpClientSwiHandlerRegister( + AvpSwiClientType ClientId, + AvpClientSwiHandler pClinetSwiFunc, + NvU32 *pClientIndex); + +NvError +NvRmAvpClientSwiHandlerUnRegister( + AvpSwiClientType ClientId, + NvU32 ClientIndex); + +NvError +NvRmAvpHandleClientSwi( + NvU32 SwiNum, + NvU32 ClientIndex, + int *pRegs); + +typedef struct{ + NvRmDfsClockId clockId; + NvU32 clientId; + NvU32 boostDurationMS; + NvRmFreqKHz boostKHz; +}NvRm_PowerBusyHint; + +/** NvRmRegisterLibraryCall - Register a library call with the AVP RM + * + * @param id The user id to associate with the function + * @param pEntry The function to be registered + * @param pOwnerKey A special unique key to use when unregistering. + * + * @returns InsufficientMemory or AlreadyAllocated on failure. + */ +NvError +NvRmRegisterLibraryCall(NvU32 Id, void *pEntry, NvU32 *pOwnerKey); + +/** NvRmUnregisterLibraryCall - Unregister a library call + * + * @param id The user id associated with the function + * @param pOwnerKey A special unique key. Used to ensure that the correct owner + * unregisters a function. + * + */ +void +NvRmUnregisterLibraryCall(NvU32 Id, NvU32 OwnerKey); + +/** NvRmGetLibraryCall - Obtain a registered function from the AVP RM + * + * @param id The user id associated with the desired library function + * + * @returns NULL on failure. The function pointer on success + */ +void *NvRmGetLibraryCall(NvU32 Id); + +/** NvRmRemoteDebugPrintf - Routes client prints to the CPU. + * + * NOTE: This does *not* route kernel prints. ONLY AVP client prints will + * be routed. + * @param string The debug string to print to console + * + */ +void *NvRmRemoteDebugPrintf(const char *string); + +/** NvOsAVPThreadCreate - Creates threads on the AVP with an optional stackPtr argument. + * + * AVP clients can use this function to allocate thread stacks wherever they desire (like in + * IRAM, for instance). It is the clients responsibility to allocate this pointer and free it. + * NOTE: The client must free this pointer only after the thread has been joined. + * + * @param function The thread entry point + * @param args The thread args + * @param thread The result thread id structure (out param) + * @param stackPtr The optional pointer to a user allocated stack (Can be NULL) + * @param stackSize The size of the associated stackPtr. + * + */ +NvError NvOsAVPThreadCreate(NvOsThreadFunction function, + void *args, + NvOsThreadHandle *thread, + void *stackPtr, + NvU32 stackSize); + +/** NvOsAVPSetIdle - This function is used to force the AVP kernel to save its state + * + * When the PMC_SCRATCH22 register has a non-zero value, the AVP has finished saving all its state. + * @param iramSourceAddress The address at which the IRAM aperture begins + * @param iramBufferAddress The address of the buffer into which the AVP will save all IRAM state. + * @param iramSize The size of the iram aperture. + * + */ +void NvOsAVPSetIdle(NvU32 iramSourceAddress, + NvU32 iramBufferAddress, + NvU32 iramSize); + +/** NvRmPowerBusyMultiHint - Provide hints to multiple modules. Saves on messaging overhead. + * + * @param multiHint The array of hints + * @param numHints The number of hints + */ +void NvRmPowerBusyMultiHint(NvRm_PowerBusyHint *multiHint, NvU32 numHints); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_graphics_private.h b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_graphics_private.h new file mode 100644 index 000000000000..3953b3665d3d --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_graphics_private.h @@ -0,0 +1,77 @@ +/* + * arch/arm/mach-tegra/nvrm/core/common/nvrm_graphics_private.h + * + * + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef NVRM_GRAPHICS_PRIVATE_H +#define NVRM_GRAPHICS_PRIVATE_H + +#define NVRM_TRANSPORT_IN_KERNEL 1 + +/** + * Initialize all graphics stuff + * + * @param hDevice The RM instance + */ +NvError +NvRmGraphicsOpen( NvRmDeviceHandle rm ); + +/** + * Deinitialize all graphics stuff + * + * @param hDevice The RM instance + */ +void +NvRmGraphicsClose( NvRmDeviceHandle rm ); + +/** + * Initialize the channels. + * + * @param hDevice The RM instance + */ +NvError +NvRmPrivChannelInit( NvRmDeviceHandle hDevice ); + +/** + * Deinitialize the channels. + * + * @param hDevice The RM instance + */ +void +NvRmPrivChannelDeinit( NvRmDeviceHandle hDevice ); + +/** + * Initialize the graphics host, including interrupts. + */ +void +NvRmPrivHostInit( NvRmDeviceHandle rm ); + +void +NvRmPrivHostShutdown( NvRmDeviceHandle rm ); + +#if (NVRM_TRANSPORT_IN_KERNEL == 0) +NvError +NvRmPrivTransportInit(NvRmDeviceHandle hRmDevice); + +void +NvRmPrivTransportDeInit(NvRmDeviceHandle hRmDevice); +#endif + +#endif diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_init_stub.c b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_init_stub.c new file mode 100644 index 000000000000..481ebd43260f --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_init_stub.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009 NVIDIA Corporation. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvassert.h" +#include "nvidlcmd.h" +#include "nvrm_init.h" + +void NvRmClose(NvRmDeviceHandle hDevice) +{ +} + +NvError NvRmOpenNew(NvRmDeviceHandle *pHandle) +{ + *pHandle = (void *)1; + return NvSuccess; +} + +void NvRmInit(NvRmDeviceHandle *pHandle) +{ +} + +NvError NvRmOpen(NvRmDeviceHandle *pHandle, NvU32 DeviceId) +{ + return NvRmOpenNew(pHandle); +} diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_message.h b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_message.h index e32033f6aa05..0fe59dbf293a 100644 --- a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_message.h +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_message.h @@ -1,33 +1,23 @@ /* - * Copyright (c) 2010 NVIDIA Corporation. - * All rights reserved. + * arch/arm/mach-tegra/nvrm/core/common/nvrm_message.h * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Copyright (c) 2010, NVIDIA Corporation. * - * Neither the name of the NVIDIA Corporation nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef INCLUDED_NVRM_MESSAGE_H @@ -43,16 +33,16 @@ extern "C" { #endif /* __cplusplus */ -// Maximum message queue depth +/* Maximum message queue depth */ enum {MAX_QUEUE_DEPTH = 5}; -// Maximum message length +/* Maximum message length */ enum {MAX_MESSAGE_LENGTH = 256}; -// Maximum argument size +/* Maximum argument size */ enum {MAX_ARGS_SIZE = 220}; -// Max String length +/* Max String length */ enum {MAX_STRING_LENGTH = 200}; -typedef struct NvRmRPCRec +typedef struct NvRmRPCRec { NvRmTransportHandle svcTransportHandle; NvOsSemaphoreHandle TransportRecvSemId; @@ -86,11 +76,11 @@ typedef enum NvRmMsg_ModuleReset, NvRmMsg_ModuleReset_Response, NvRmMsg_PowerRegister, - NvRmMsg_PowerUnRegister, + NvRmMsg_PowerUnRegister, NvRmMsg_PowerStarvationHint, NvRmMsg_PowerBusyHint, NvRmMsg_PowerBusyMultiHint, - NvRmMsg_PowerDfsGetState, + NvRmMsg_PowerDfsGetState, NvRmMsg_PowerDfsGetState_Response, NvRmMsg_PowerResponse, NvRmMsg_PowerModuleGetMaxFreq, @@ -102,6 +92,8 @@ typedef enum NvRmMsg_DetachModule, NvRmMsg_DetachModule_Response, NvRmMsg_AVP_Reset, + NvRmMsg_PowerDfsGetClockUtilization, + NvRmMsg_PowerDfsGetClockUtilization_Response, NvRmMsg_Force32 = 0x7FFFFFFF }NvRmMsg; @@ -171,11 +163,11 @@ typedef struct{ typedef struct{ NvRmMsg msg; NvU32 clientId; - NvOsSemaphoreHandle eventSema; + NvOsSemaphoreHandle eventSema; }NvRmMessage_PowerRegister; typedef struct{ - NvRmMsg msg; + NvRmMsg msg; NvU32 clientId; }NvRmMessage_PowerUnRegister; @@ -225,6 +217,18 @@ typedef struct{ NvRmFreqKHz freqKHz; }NvRmMessage_PowerModuleGetMaxFreq_Response; +typedef struct{ + NvRmMsg msg; + NvError error; + NvRmDfsClockId clockId; +}NvRmMessage_PowerDfsGetClockUtilization; + +typedef struct{ + NvRmMsg msg; + NvError error; + NvRmDfsClockUsage clockUsage; +}NvRmMessage_PowerDfsGetClockUtilization_Response; + typedef struct{ NvRmMsg msg; NvU32 sourceAddr; diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_moduleloader.c b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_moduleloader.c new file mode 100644 index 000000000000..6de25c61608e --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_moduleloader.c @@ -0,0 +1,1651 @@ +/* + * arch/arm/mach-tegra/nvrm/core/common/nvrm_moduleloader.c + * + * AVP firmware module loader + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#define NV_ENABLE_DEBUG_PRINTS 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvcommon.h" +#include "nvassert.h" +#include "nvos.h" +#include "nvutil.h" +#include "nvrm_hardware_access.h" +#include "nvrm_message.h" +#include "nvrm_rpc.h" +#include "nvrm_moduleloader.h" +#include "nvrm_moduleloader_private.h" +#include "nvrm_graphics_private.h" +#include "nvrm_structure.h" +#include "nvfw.h" + +#define DEVICE_NAME "nvfw" + +static const struct firmware *s_FwEntry; +static NvRmRPCHandle s_RPCHandle = NULL; +static NvError SendMsgDetachModule(NvRmLibraryHandle hLibHandle); +static NvError SendMsgAttachModule(NvRmLibraryHandle *hLibHandle, + void* pArgs, + NvU32 sizeOfArgs); +NvU32 NvRmModuleGetChipId(NvRmDeviceHandle hDevice); +NvError NvRmPrivInitModuleLoaderRPC(NvRmDeviceHandle hDevice); +void NvRmPrivDeInitModuleLoaderRPC(void); + +#define ADD_MASK 0x00000001 +#define SUB_MASK 0xFFFFFFFD +// For the elf to be relocatable, we need atleast 2 program segments +// Although even elfs with more than 1 program segment may not be relocatable. +#define MIN_SEGMENTS_FOR_DYNAMIC_LOADING 2 + +static int nvfw_open(struct inode *inode, struct file *file); +static int nvfw_close(struct inode *inode, struct file *file); +static long nvfw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +static ssize_t nvfw_write(struct file *, const char __user *, size_t, loff_t *); + +static const struct file_operations nvfw_fops = +{ + .owner = THIS_MODULE, + .open = nvfw_open, + .release = nvfw_close, + .write = nvfw_write, + .unlocked_ioctl = nvfw_ioctl, +}; + +static struct miscdevice nvfw_dev = +{ + .name = DEVICE_NAME, + .fops = &nvfw_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + +// FIXME: This function is just for debugging. +ssize_t nvfw_write(struct file *file, const char __user *buff, size_t count, loff_t *offp) +{ + NvRmDeviceHandle hRmDevice; + NvRmLibraryHandle hRmLibHandle; + char filename[100]; + int error; + + printk(KERN_INFO "%s: entry\n", __func__); + + error = copy_from_user(filename, buff, count); + if (error) + panic("%s: line=%d\n", __func__, __LINE__); + filename[count] = 0; + printk(KERN_INFO "%s: filename=%s\n", __func__, filename); + + error = NvRmOpen( &hRmDevice, 0 ); + if (error) + panic("%s: line=%d\n", __func__, __LINE__); + + error = NvRmLoadLibrary(hRmDevice, filename, NULL, 0, &hRmLibHandle); + if (error) + panic("%s: line=%d\n", __func__, __LINE__); + + printk(KERN_INFO "%s: return\n", __func__); + return count; +} + +int nvfw_open(struct inode *inode, struct file *file) +{ + return 0; +} + +int nvfw_close(struct inode *inode, struct file *file) +{ + return 0; +} + +static int nvfw_ioctl_load_library(struct file *filp, void __user *arg) +{ + struct nvfw_load_handle op; + NvRmDeviceHandle hRmDevice; + NvRmLibraryHandle hRmLibHandle; + char *filename = NULL; + void *args = NULL; + int error; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + filename = NvOsAlloc(op.length + 1); + error = copy_from_user(filename, op.filename, op.length + 1); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + printk(KERN_INFO "%s: filename=%s\n", __func__, filename); + + args = NvOsAlloc(op.argssize); + error = copy_from_user(args, op.args, op.argssize); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + error = NvRmOpen( &hRmDevice, 0 ); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + error = NvRmLoadLibrary(hRmDevice, filename, args, op.argssize, &hRmLibHandle); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + op.handle = hRmLibHandle; + error = copy_to_user(arg, &op, sizeof(op)); + +error_exit: + NvOsFree(filename); + NvOsFree(args); + return error; +} + +static int nvfw_ioctl_load_library_ex(struct file *filp, void __user *arg) +{ + struct nvfw_load_handle op; + NvRmDeviceHandle hRmDevice; + NvRmLibraryHandle hRmLibHandle; + char *filename = NULL; + void *args = NULL; + int error; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + filename = NvOsAlloc(op.length + 1); + error = copy_from_user(filename, op.filename, op.length + 1); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + printk(KERN_INFO "%s: filename=%s\n", __func__, filename); + + args = NvOsAlloc(op.argssize); + error = copy_from_user(args, op.args, op.argssize); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + error = NvRmOpen( &hRmDevice, 0 ); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + error = NvRmLoadLibraryEx(hRmDevice, filename, args, op.argssize, op.greedy, &hRmLibHandle); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + op.handle = hRmLibHandle; + error = copy_to_user(arg, &op, sizeof(op)); + +error_exit: + NvOsFree(filename); + NvOsFree(args); + return error; +} + +static int nvfw_ioctl_free_library(struct file *filp, void __user *arg) +{ + struct nvfw_load_handle op; + NvRmDeviceHandle hRmDevice; + int error; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + error = NvRmOpen( &hRmDevice, 0 ); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + error = NvRmFreeLibrary(op.handle); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + +error_exit: + return error; +} + +static int nvfw_ioctl_get_proc_address(struct file *filp, void __user *arg) +{ + struct nvfw_get_proc_address_handle op; + NvRmDeviceHandle hRmDevice; + char *symbolname; + int error; + + error = copy_from_user(&op, arg, sizeof(op)); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + symbolname = NvOsAlloc(op.length + 1); + error = copy_from_user(symbolname, op.symbolname, op.length + 1); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + printk(KERN_INFO "%s: symbolname=%s\n", __func__, symbolname); + + error = NvRmOpen( &hRmDevice, 0 ); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + error = NvRmGetProcAddress(op.handle, symbolname, &op.address); + if (error) panic("%s: line=%d\n", __func__, __LINE__); + if (error) goto error_exit; + + error = copy_to_user(arg, &op, sizeof(op)); + +error_exit: + NvOsFree(symbolname); + return error; +} + +static long nvfw_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + void __user *uarg = (void __user *)arg; + + switch (cmd) { + case NVFW_IOC_LOAD_LIBRARY: + err = nvfw_ioctl_load_library(filp, uarg); + break; + case NVFW_IOC_LOAD_LIBRARY_EX: + err = nvfw_ioctl_load_library_ex(filp, uarg); + break; + case NVFW_IOC_FREE_LIBRARY: + err = nvfw_ioctl_free_library(filp, uarg); + break; + case NVFW_IOC_GET_PROC_ADDRESS: + err = nvfw_ioctl_get_proc_address(filp, uarg); + break; + default: + return -ENOTTY; + } + return err; +} + +static NvError +PrivateOsFopen(const char *filename, NvU32 flags, PrivateOsFileHandle *file) +{ + PrivateOsFileHandle hFile; + + hFile = NvOsAlloc(sizeof(PrivateOsFile)); + if (hFile == NULL) { + return NvError_InsufficientMemory; + } + + NvOsDebugPrintf("%s : file=%s\n", __func__, filename); + NvOsDebugPrintf("%s : calling request_firmware()\n", __func__); + if (request_firmware(&s_FwEntry, filename, nvfw_dev.this_device) != 0) { + printk(KERN_ERR "%s: Cannot read firmware '%s'\n", __func__, filename); + return NvError_FileReadFailed; + } + NvOsDebugPrintf("%s : back from request_firmware()\n", __func__); + hFile->pstart = s_FwEntry->data; + hFile->pread = s_FwEntry->data; + hFile->pend = s_FwEntry->data + s_FwEntry->size; + + *file = hFile; + + return NvError_Success; +} + +static void +PrivateOsFclose(PrivateOsFileHandle hFile) +{ + release_firmware(s_FwEntry); + NV_ASSERT(hFile); + NvOsFree(hFile); +} + +static NvError +PrivateOsFread( + PrivateOsFileHandle hFile, + void *ptr, + size_t size, + size_t *bytes) +{ + size_t nBytesRead = size; + NvError err = NvError_Success; + + if (hFile->pread >= hFile->pend) { + nBytesRead = 0; + err = NvError_EndOfFile; + goto epilogue; + } + + else if (hFile->pread + size > hFile->pend) { + nBytesRead = hFile->pend - hFile->pread; + NvOsMemcpy(ptr, hFile->pread, nBytesRead); + err = NvError_EndOfFile; + goto epilogue; + } + + else { + NvOsMemcpy(ptr, hFile->pread, nBytesRead); + goto epilogue; + } + +epilogue: + hFile->pread += nBytesRead; + *bytes = nBytesRead; + return err; +} + +static NvError +PrivateOsFseek(PrivateOsFileHandle file, NvS64 offset, NvOsSeekEnum whence) +{ + NV_ASSERT(whence == NvOsSeek_Set); + file->pread = file->pstart + (NvU32)offset; + + return NvError_Success; +} + +NvError +NvRmPrivLoadKernelLibrary(NvRmDeviceHandle hDevice, + const char *pLibName, + NvRmLibraryHandle *hLibHandle) +{ + NvError Error = NvSuccess; + + NvOsDebugPrintf("%s : file=%s\n", __func__, pLibName); + if ((Error = NvRmPrivLoadLibrary(hDevice, pLibName, 0, NV_FALSE, + hLibHandle)) != NvSuccess) + { + return Error; + } + return Error; +} + +NvError +NvRmLoadLibrary(NvRmDeviceHandle hDevice, + const char *pLibName, + void* pArgs, + NvU32 sizeOfArgs, + NvRmLibraryHandle *hLibHandle) +{ + NvError Error = NvSuccess; + NV_ASSERT(sizeOfArgs <= MAX_ARGS_SIZE); + + NvOsDebugPrintf("%s : file=%s\n", __func__, pLibName); + Error = NvRmLoadLibraryEx(hDevice, pLibName, pArgs, sizeOfArgs, NV_FALSE, + hLibHandle); + return Error; +} + +NvError +NvRmLoadLibraryEx(NvRmDeviceHandle hDevice, + const char *pLibName, + void* pArgs, + NvU32 sizeOfArgs, + NvBool IsApproachGreedy, + NvRmLibraryHandle *hLibHandle) +{ + NvError Error = NvSuccess; + NV_ASSERT(sizeOfArgs <= MAX_ARGS_SIZE); + + NvOsDebugPrintf("%s : file=%s\n", __func__, pLibName); + + /* NvRmPrivInitModuleLoaderRPC(hDevice); */ + if ((Error = NvRmPrivInitAvp(hDevice)) != NvSuccess) + { + return Error; + } + + if ((Error = NvRmPrivLoadLibrary(hDevice, pLibName, 0, IsApproachGreedy, + hLibHandle)) != NvSuccess) + { + return Error; + } + + if ((Error = NvRmPrivRPCConnect(s_RPCHandle)) == NvSuccess) + { + Error = SendMsgAttachModule(hLibHandle, pArgs, sizeOfArgs); + } + else + { + NvOsDebugPrintf("RPCConnect timedout during NvRmLoadLibraryEx\r\n"); + } + if (Error) + { + NvRmPrivFreeLibrary(*hLibHandle); + } + return Error; +} + +NvError +NvRmGetProcAddress(NvRmLibraryHandle Handle, + const char *pSymbol, + void **pSymAddress) +{ + NvError Error = NvSuccess; + NV_ASSERT(Handle); + Error = NvRmPrivGetProcAddress(Handle, pSymbol, pSymAddress); + return Error; +} + +NvError NvRmFreeLibrary(NvRmLibraryHandle hLibHandle) +{ + NvError Error = NvSuccess; + NV_ASSERT(hLibHandle); + if((Error = NvRmPrivRPCConnect(s_RPCHandle)) == NvSuccess) + { + Error = SendMsgDetachModule(hLibHandle); + } + if (Error == NvSuccess) + { + Error = NvRmPrivFreeLibrary(hLibHandle); + } + + return Error; +} + +NvU32 NvRmModuleGetChipId(NvRmDeviceHandle hDevice) +{ + typedef struct + { + NvU32 Id; + }Capabilities; + + NvError Error = NvSuccess; + NvRmModuleCapability caps[3]; + Capabilities Cap15, Cap20,Cap16,*capabilities; + + // for AP20 + Cap20.Id = 0x20; + caps[0].MajorVersion = 1; + caps[0].MinorVersion = 1; + caps[0].EcoLevel = 0; + caps[0].Capability = (void *)&Cap20; + + //for AP15 A01 + Cap15.Id = 0x15; + caps[1].MajorVersion = 1; + caps[1].MinorVersion = 0; + caps[1].EcoLevel = 0; + caps[1].Capability = (void *)&Cap15; + + //for AP15 A02 + Cap16.Id = 0x15; + caps[2].MajorVersion = 1; + caps[2].MinorVersion = 1; + caps[2].EcoLevel = 0; + caps[2].Capability = (void *)&Cap16; + + Error = NvRmModuleGetCapabilities(hDevice, NvRmModuleID_BseA, caps, 3, (void **)&capabilities); + + return capabilities->Id; +} + +//before unloading loading send message to avp with args and entry point via transport +static NvError SendMsgDetachModule(NvRmLibraryHandle hLibHandle) +{ + NvError Error = NvSuccess; + NvU32 RecvMsgSize; + NvRmMessage_DetachModule Msg; + NvRmMessage_DetachModuleResponse MsgR; + void *address = NULL; + + Msg.msg = NvRmMsg_DetachModule; + + if ((Error = NvRmGetProcAddress(hLibHandle, "main", &address)) != NvSuccess) + { + goto exit_gracefully; + } + Msg.msg = NvRmMsg_DetachModule; + Msg.reason = NvRmModuleLoaderReason_Detach; + Msg.entryAddress = (NvU32)address; + RecvMsgSize = sizeof(NvRmMessage_DetachModuleResponse); + NvRmPrivRPCSendMsgWithResponse(s_RPCHandle, + &MsgR, + RecvMsgSize, + &RecvMsgSize, + &Msg, + sizeof(Msg)); + + Error = MsgR.error; + if (Error) + { + goto exit_gracefully; + } +exit_gracefully: + return Error; +} + +//after successful loading send message to avp with args and entry point via transport +static NvError SendMsgAttachModule(NvRmLibraryHandle *hLibHandle, + void* pArgs, + NvU32 sizeOfArgs) +{ + NvError Error = NvSuccess; + NvU32 RecvMsgSize; + NvRmMessage_AttachModule *MsgPtr=NULL; + NvRmMessage_AttachModuleResponse MsgR; + void *address = NULL; + + MsgPtr = NvOsAlloc(sizeof(*MsgPtr)); + if(MsgPtr==NULL) + { + Error = NvError_InsufficientMemory; + goto exit_gracefully; + } + MsgPtr->msg = NvRmMsg_AttachModule; + + if(pArgs) + { + NvOsMemcpy(MsgPtr->args, pArgs, sizeOfArgs); + } + + MsgPtr->size = sizeOfArgs; + if ((Error = NvRmGetProcAddress(*hLibHandle, "main", &address)) != NvSuccess) + { + goto exit_gracefully; + } + MsgPtr->entryAddress = (NvU32)address; + MsgPtr->reason = NvRmModuleLoaderReason_Attach; + RecvMsgSize = sizeof(NvRmMessage_AttachModuleResponse); + NvRmPrivRPCSendMsgWithResponse(s_RPCHandle, + &MsgR, + RecvMsgSize, + &RecvMsgSize, + MsgPtr, + sizeof(*MsgPtr)); + + Error = MsgR.error; + if (Error) + { + goto exit_gracefully; + } +exit_gracefully: + NvOsFree(MsgPtr); + return Error; +} + + +NvError NvRmPrivInitModuleLoaderRPC(NvRmDeviceHandle hDevice) +{ + NvError err = NvSuccess; + + // Run only once. + if (s_RPCHandle) return NvError_Success; + + NvOsDebugPrintf("%s : NvRmPrivRPCInit(RPC_AVP_PORT)\n", __func__); + err = NvRmPrivRPCInit(hDevice, "RPC_AVP_PORT", &s_RPCHandle); + if (err) panic("%s: NvRmPrivRPCInit FAILED\n", __func__); + + return err; +} + +void NvRmPrivDeInitModuleLoaderRPC() +{ + NvRmPrivRPCDeInit(s_RPCHandle); +} + +SegmentNode* AddToSegmentList(SegmentNode *pList, + NvRmMemHandle pRegion, + Elf32_Phdr Phdr, + NvU32 Idx, + NvU32 PhysAddr, + void* MapAddress) +{ + SegmentNode *TempRec = NULL; + SegmentNode *CurrentRec = NULL; + + TempRec = NvOsAlloc(sizeof(SegmentNode)); + if (TempRec != NULL) + { + TempRec->pLoadRegion = pRegion; + TempRec->Index = Idx; + TempRec->VirtualAddr = Phdr.p_vaddr; + TempRec->MemorySize = Phdr.p_memsz; + TempRec->FileOffset = Phdr.p_offset; + TempRec->FileSize = Phdr.p_filesz; + TempRec->LoadAddress = PhysAddr; + TempRec->MapAddr = MapAddress; + TempRec->Next = NULL; + + CurrentRec = pList; + if (CurrentRec == NULL) + { + pList = TempRec; + } + else + { + while (CurrentRec->Next != NULL) + { + CurrentRec = CurrentRec->Next; + } + CurrentRec->Next = TempRec; + } + } + return pList; +} +void RemoveRegion(SegmentNode *pList) +{ + if (pList != NULL) + { + SegmentNode *pCurrentRec; + SegmentNode *pTmpRec; + pCurrentRec = pList; + while (pCurrentRec != NULL) + { + NvRmMemUnpin(pCurrentRec->pLoadRegion); + NvRmMemHandleFree(pCurrentRec->pLoadRegion); + pCurrentRec->pLoadRegion = NULL; + pTmpRec = pCurrentRec; + pCurrentRec = pCurrentRec->Next; + NvOsFree( pTmpRec ); + } + pList = NULL; + } +} + +void UnMapRegion(SegmentNode *pList) +{ + if (pList != NULL) + { + SegmentNode *pCurrentRec; + pCurrentRec = pList; + while (pCurrentRec != NULL && pCurrentRec->MapAddr ) + { + NvRmMemUnmap(pCurrentRec->pLoadRegion, pCurrentRec->MapAddr, + pCurrentRec->MemorySize); + pCurrentRec = pCurrentRec->Next; + } + } +} + +NvError +ApplyRelocation(SegmentNode *pList, + NvU32 FileOffset, + NvU32 SegmentOffset, + NvRmMemHandle pRegion, + const Elf32_Rel *pRel) +{ + NvError Error = NvSuccess; + NvU8 Type = 0; + NvU32 SymIndex = 0; + Elf32_Word Word = 0; + SegmentNode *pCur; + NvU32 TargetVirtualAddr = 0; + NvU32 LoadAddress = 0; + NV_ASSERT(NULL != pRel); + + NvRmMemRead(pRegion, SegmentOffset,&Word, sizeof(Word)); + NV_DEBUG_PRINTF(("NvRmMemRead: SegmentOffset 0x%04x, word %p\r\n", + SegmentOffset, Word)); + Type = ELF32_R_TYPE(pRel->r_info); + + switch (Type) + { + case R_ARM_NONE: + break; + case R_ARM_CALL: + break; + case R_ARM_RABS32: + SymIndex = ELF32_R_SYM(pRel->r_info); + if (pList != NULL) + { + pCur = pList; + while (pCur != NULL) + { + if (pCur->Index == (SymIndex - 1)) + { + TargetVirtualAddr = pCur->VirtualAddr; + LoadAddress = pCur->LoadAddress; + } + pCur = pCur->Next; + } + if (LoadAddress > TargetVirtualAddr) + { + Word = Word + (LoadAddress - TargetVirtualAddr); + } + else //handle negative displacement + { + Word = Word - (TargetVirtualAddr - LoadAddress); + } + NV_DEBUG_PRINTF(("NvRmMemWrite: SegmentOffset 0x%04x, word %p\r\n", + SegmentOffset, Word)); + NvRmMemWrite(pRegion, SegmentOffset, &Word, sizeof(Word)); + } + break; + default: + Error = NvError_NotSupported; + NV_DEBUG_PRINTF(("This relocation type is not handled = %d\r\n", Type)); + break; + } + return Error; +} + +NvError +GetSpecialSectionName(Elf32_Word SectionType, + Elf32_Word SectionFlags, + const char** SpecialSectionName) +{ + const char *unknownSection = "Unknown\r\n"; + *SpecialSectionName = unknownSection; + /// Mask off the high 16 bits for now + switch (SectionFlags & 0xffff) + { + case SHF_ALLOC|SHF_WRITE: + if (SectionType == SHT_NOBITS) + *SpecialSectionName = ".bss\r\n"; + else if (SectionType == SHT_PROGBITS) + *SpecialSectionName = ".data\r\n"; + else if (SectionType == SHT_FINI_ARRAY) + *SpecialSectionName = ".fini_array\r\n"; + else if (SectionType == SHT_INIT_ARRAY) + *SpecialSectionName = ".init_array\r\n"; + break; + case SHF_ALLOC|SHF_EXECINSTR: + if (SectionType == SHT_PROGBITS) + *SpecialSectionName = ".init or fini \r\n"; + + break; + case SHF_ALLOC: + if (SectionType == SHT_STRTAB) + *SpecialSectionName = ".dynstr\r\n"; + else if (SectionType == SHT_DYNSYM) + *SpecialSectionName = ".dynsym\r\n"; + else if (SectionType == SHT_HASH) + *SpecialSectionName = ".hash\r\n"; + else if (SectionType == SHT_PROGBITS) + *SpecialSectionName = ".rodata\r\n"; + else + *SpecialSectionName = unknownSection; + break; + default: + if (SectionType == SHT_PROGBITS) + *SpecialSectionName = ".comment\r\n"; + else + *SpecialSectionName = unknownSection; + break; + } + return NvSuccess; +} + +NvError +ParseDynamicSegment(SegmentNode *pList, + const char* pSegmentData, + size_t SegmentSize, + NvU32 DynamicSegmentOffset) +{ + NvError Error = NvSuccess; + Elf32_Dyn* pDynSeg = NULL; + NvU32 Counter = 0; + NvU32 RelocationTableAddressOffset = 0; + NvU32 RelocationTableSize = 0; + NvU32 RelocationEntrySize = 0; + const Elf32_Rel* RelocationTablePtr = NULL; + NvU32 SymbolTableAddressOffset = 0; + NvU32 SymbolTableEntrySize = 0; + NvU32 SymbolTableSize = 0; + NvU32 SegmentOffset = 0; + NvU32 FileOffset = 0; + SegmentNode *node; +#if NV_ENABLE_DEBUG_PRINTS + // Strings for interpreting ELF header e_type field. + static const char * s_DynSecTypeText[] = + { + "DT_NULL", + "DT_NEEDED", + "DT_PLTRELSZ", + "DT_PLTGOT", + "DT_HASH", +// "DT_STRTAB", + "String Table Address", +// "DT_SYMTAB", + "Symbol Table Address", +// "DT_RELA", + "Relocation Table Address", +// "DT_RELASZ", + "Relocation Table Size", +// "DT_RELAENT", + "Relocation Entry Size", +// "DT_STRSZ", + "String Table Size", +// "DT_SYMENT", + "Symbol Table Entry Size", + "DT_INIT", + "DT_FINI", + "DT_SONAME", + "DT_RPATH", + "DT_SYMBOLIC", +// "DT_REL", + "Relocation Table Address", +// "DT_RELSZ", + "Relocation Table Size", +// "DT_RELENT", + "Relocation Entry Size", + "DT_PLTREL", + "DT_DEBUG", + "DT_TEXTREL", + "DT_JMPREL", + "DT_BIND_NOW", + "DT_INIT_ARRAY", + "DT_FINI_ARRAY", + "DT_INIT_ARRAYSZ", + "DT_FINI_ARRAYSZ", + "DT_RUNPATH", + "DT_FLAGS", + "DT_ENCODING", + "DT_PREINIT_ARRAY", + "DT_PREINIT_ARRAYSZ", + "DT_NUM", + "DT_OS-specific", + "DT_PROC-specific", + "" + }; +#else +#define s_DynSecTypeText ((char**)0) +#endif + + pDynSeg = (Elf32_Dyn*)pSegmentData; + do + { + if (pDynSeg->d_tag < DT_NUM) + { + NV_DEBUG_PRINTF(("Entry %d with Tag %s %d\r\n", + Counter++, s_DynSecTypeText[pDynSeg->d_tag], pDynSeg->d_val)); + } + else + { + NV_DEBUG_PRINTF(("Entry %d Special Compatibility Range %x %d\r\n", + Counter++, pDynSeg->d_tag, pDynSeg->d_val)); + } + if (pDynSeg->d_tag == DT_NULL) + break; + if ((pDynSeg->d_tag == DT_REL) || (pDynSeg->d_tag == DT_RELA)) + RelocationTableAddressOffset = pDynSeg->d_un.d_val; + if ((pDynSeg->d_tag == DT_RELENT) || (pDynSeg->d_tag == DT_RELAENT)) + RelocationEntrySize = pDynSeg->d_un.d_val; + if ((pDynSeg->d_tag == DT_RELSZ) || (pDynSeg->d_tag == DT_RELASZ)) + RelocationTableSize = pDynSeg->d_un.d_val; + if (pDynSeg->d_tag == DT_SYMTAB) + SymbolTableAddressOffset = pDynSeg->d_un.d_val; + if (pDynSeg->d_tag == DT_SYMENT) + SymbolTableEntrySize = pDynSeg->d_un.d_val; + if (pDynSeg->d_tag == DT_ARM_RESERVED1) + SymbolTableSize = pDynSeg->d_un.d_val; + pDynSeg++; + + }while ((Counter*sizeof(Elf32_Dyn)) < SegmentSize); + + if (RelocationTableAddressOffset && RelocationTableSize && RelocationEntrySize) + { + RelocationTablePtr = (const Elf32_Rel*)&pSegmentData[RelocationTableAddressOffset]; + + for (Counter = 0; Counter < (RelocationTableSize/RelocationEntrySize); Counter++) + { + //calculate the actual offset of the reloc entry + NV_DEBUG_PRINTF(("Reloc %d offset is %x RType %d SymIdx %d \r\n", + Counter, RelocationTablePtr->r_offset, + ELF32_R_TYPE(RelocationTablePtr->r_info), + ELF32_R_SYM(RelocationTablePtr->r_info))); + + node = pList; + while (node != NULL) + { + if ( (RelocationTablePtr->r_offset > node->VirtualAddr) && + (RelocationTablePtr->r_offset <= + (node->VirtualAddr + node->MemorySize))) + { + FileOffset = node->FileOffset + + (RelocationTablePtr->r_offset - node->VirtualAddr); + + NV_DEBUG_PRINTF(("File offset to be relocated %d \r\n", FileOffset)); + + SegmentOffset = (RelocationTablePtr->r_offset - node->VirtualAddr); + + NV_DEBUG_PRINTF(("Segment offset to be relocated %d \r\n", SegmentOffset)); + + Error = ApplyRelocation(pList, FileOffset, SegmentOffset, + node->pLoadRegion, RelocationTablePtr); + + } + node = node->Next; + } + RelocationTablePtr++; + } + + } + if (SymbolTableAddressOffset && SymbolTableSize && SymbolTableEntrySize) + { +#if 0 + const Elf32_Sym* SymbolTablePtr = NULL; + SymbolTablePtr = (const Elf32_Sym*)&pSegmentData[SymbolTableAddressOffset]; + for (Counter = 0; Counter st_name, SymbolTablePtr->st_value, + SymbolTablePtr->st_size, SymbolTablePtr->st_info, + SymbolTablePtr->st_other, SymbolTablePtr->st_shndx); + NV_DEBUG_PRINTF(("Symbol name %x, value %x, size %x, info %x, other %x, shndx %x\r\n", + SymbolTablePtr->st_name, SymbolTablePtr->st_value, + SymbolTablePtr->st_size, SymbolTablePtr->st_info, + SymbolTablePtr->st_other, SymbolTablePtr->st_shndx)); + + SymbolTablePtr++; + } +#endif + } + return Error; +} + +NvError +LoadLoadableProgramSegment(PrivateOsFileHandle elfSourceHandle, + NvRmDeviceHandle hDevice, + NvRmLibraryHandle hLibHandle, + Elf32_Phdr Phdr, + Elf32_Ehdr Ehdr, + const NvRmHeap * Heaps, + NvU32 NumHeaps, + NvU32 loop, + const char *Filename, + SegmentNode **segmentList) +{ + NvError Error = NvSuccess; + NvRmMemHandle pLoadRegion = NULL; + void* pLoadAddress = NULL; + NvU32 offset = 0; + NvU32 addr; // address of pLoadRegion + size_t bytesRead = 0; + + Error = NvRmMemHandleCreate(hDevice, + &pLoadRegion, + Phdr.p_memsz); + + if (Error != NvSuccess) + goto CleanUpExit; + + Error = NvRmMemAlloc(pLoadRegion, + Heaps, + NumHeaps, + NV_MAX(16, Phdr.p_align), + NvOsMemAttribute_Uncached); + + if (Error != NvSuccess) + { + NV_DEBUG_PRINTF(("Memory Allocation %d Failed\r\n",Error)); + NvRmMemHandleFree(pLoadRegion); + pLoadRegion = NULL; + goto CleanUpExit; + } + addr = NvRmMemPin(pLoadRegion); + + Error = NvRmMemMap(pLoadRegion, 0, Phdr.p_memsz, + NVOS_MEM_READ_WRITE, &pLoadAddress); + if (Error != NvSuccess) + { + pLoadAddress = NULL; + } + + // This will initialize ZI to zero + if( pLoadAddress ) + { + NvOsMemset(pLoadAddress, 0, Phdr.p_memsz); + } + else + { + NvU8 *tmp = NvOsAlloc( Phdr.p_memsz ); + if( !tmp ) + { + goto CleanUpExit; + } + NvOsMemset( tmp, 0, Phdr.p_memsz ); + NvRmMemWrite( pLoadRegion, 0, tmp, Phdr.p_memsz ); + NvOsFree( tmp ); + } + + if(Phdr.p_filesz) + { + if( pLoadAddress ) + { + if ((Error = PrivateOsFread(elfSourceHandle, pLoadAddress, + Phdr.p_filesz, &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Read failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + } + else + { + NvU8 *tmp = NvOsAlloc( Phdr.p_filesz ); + if( !tmp ) + { + goto CleanUpExit; + } + + Error = PrivateOsFread( elfSourceHandle, tmp, Phdr.p_filesz, + &bytesRead ); + if( Error != NvSuccess ) + { + NvOsFree( tmp ); + goto CleanUpExit; + } + + NvRmMemWrite( pLoadRegion, 0, tmp, Phdr.p_filesz ); + + NvOsFree( tmp ); + } + } + if ((Ehdr.e_entry >= Phdr.p_vaddr) + && (Ehdr.e_entry < (Phdr.p_vaddr + Phdr.p_memsz))) + { + // Odd address indicates entry point is Thumb code. + // The address needs to be masked with LSB before being invoked. + if (addr > Phdr.p_vaddr) + { + offset = (addr - Phdr.p_vaddr) | ADD_MASK; + hLibHandle->EntryAddress = (void*)(Ehdr.e_entry + offset); + } + else + { + offset = ((Phdr.p_vaddr - addr) | (ADD_MASK)) & (SUB_MASK); + hLibHandle->EntryAddress = (void*)(Ehdr.e_entry - offset); + } + NV_DEBUG_PRINTF(("Load Address for %s segment %d:%x\r\n", + Filename, loop, addr)); + NvOsDebugPrintf("Load Address for %s segment %d:%x\r\n", + Filename, loop, addr); + } + + *segmentList = AddToSegmentList((*segmentList), pLoadRegion, Phdr, loop, + addr, pLoadAddress); + +CleanUpExit: + if (Error != NvSuccess) + { + if(pLoadRegion != NULL) + { + if( pLoadAddress ) + { + NvRmMemUnmap(pLoadRegion, pLoadAddress, Phdr.p_memsz); + } + + NvRmMemUnpin(pLoadRegion); + NvRmMemHandleFree(pLoadRegion); + } + } + return Error; +} + +NvError +NvRmPrivLoadLibrary(NvRmDeviceHandle hDevice, + const char *Filename, + NvU32 Address, + NvBool IsApproachGreedy, + NvRmLibraryHandle *hLibHandle) +{ + NvError Error = NvSuccess; + PrivateOsFileHandle elfSourceHandle = 0; + size_t bytesRead = 0; + Elf32_Ehdr elf; + Elf32_Phdr progHeader; + NvU32 loop = 0; + char *dynamicSegementBuffer = NULL; + int dynamicSegmentOffset = 0; + int lastFileOffset = 0; + SegmentNode *segmentList = NULL; + NvRmHeap HeapProperty[2]; + NvU32 HeapSize = 0; + + NV_ASSERT(NULL != Filename); + *hLibHandle = NULL; + + NvOsDebugPrintf("%s : file=%s\n", __func__, Filename); + if ((Error = PrivateOsFopen(Filename, NVOS_OPEN_READ, + &elfSourceHandle)) != NvSuccess) + { + NV_DEBUG_PRINTF(("Elf source file Not found Error = %d\r\n", Error)); + NvOsDebugPrintf("Failed to load library %s, NvError=%d." + " Make sure it is present on the device\r\n", Filename, Error); + return Error; + } + if ((Error = PrivateOsFread(elfSourceHandle, &elf, + sizeof(elf), &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Read size mismatch %d\r\n", bytesRead)); + goto CleanUpExit; + } + // Parse the elf headers and display information + parseElfHeader(&elf); + /// Parse the Program Segment Headers and display information + if ((Error = parseProgramSegmentHeaders(elfSourceHandle, elf.e_phoff, elf.e_phnum)) != NvSuccess) + { + NV_DEBUG_PRINTF(("parseProgramSegmentHeaders failed %d\r\n", Error)); + goto CleanUpExit; + } + /// Parse the section Headers and display information + if ((Error = parseSectionHeaders(elfSourceHandle, &elf)) != NvSuccess) + { + NV_DEBUG_PRINTF(("parseSectionHeaders failed %d\r\n", Error)); + goto CleanUpExit; + } + // allocate memory for handle.... + *hLibHandle = NvOsAlloc(sizeof(NvRmLibHandle)); + if (!*hLibHandle) + { + Error = NvError_InsufficientMemory; + goto CleanUpExit; + } + + if (elf.e_phnum && elf.e_phnum < MIN_SEGMENTS_FOR_DYNAMIC_LOADING) + { + if ((Error = loadSegmentsInFixedMemory(elfSourceHandle, + &elf, 0, &(*hLibHandle)->pLibBaseAddress)) != NvSuccess) + { + NV_DEBUG_PRINTF(("LoadSegmentsInFixedMemory Failed %d\r\n", Error)); + goto CleanUpExit; + } + (*hLibHandle)->EntryAddress = (*hLibHandle)->pLibBaseAddress; + return Error; + } + else if (elf.e_phnum) + { + if ((Error = PrivateOsFseek(elfSourceHandle, + elf.e_phoff, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + lastFileOffset = elf.e_phoff; + // load the IRAM mandatory and DRAM mandatory sections first... + for (loop = 0; loop < elf.e_phnum; loop++) + { + if ((Error = PrivateOsFread(elfSourceHandle, &progHeader, + sizeof(Elf32_Phdr), &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Read failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + lastFileOffset += bytesRead; + if (progHeader.p_type == PT_LOAD) + { + NV_DEBUG_PRINTF(("Found load segment %d\r\n",loop)); + if ((Error = PrivateOsFseek(elfSourceHandle, + progHeader.p_offset, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + if (progHeader.p_vaddr >= DRAM_MAND_ADDRESS && progHeader.p_vaddr < IRAM_PREF_EXT_ADDRESS) + { + + + if (progHeader.p_vaddr >= DRAM_MAND_ADDRESS && progHeader.p_vaddr < IRAM_MAND_ADDRESS) + { + HeapProperty[0] = NvRmHeap_ExternalCarveOut; + } + else if (progHeader.p_vaddr >= IRAM_MAND_ADDRESS) + { + HeapProperty[0] = NvRmHeap_IRam; + } + Error = LoadLoadableProgramSegment(elfSourceHandle, hDevice, (*hLibHandle), + progHeader, elf, HeapProperty, 1, loop, + Filename, &segmentList); + if (Error != NvSuccess) + { + NV_DEBUG_PRINTF(("Unable to load segment %d \r\n", loop)); + goto CleanUpExit; + } + } + if ((Error = PrivateOsFseek(elfSourceHandle, + lastFileOffset, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + } + } + + // now load the preferred and dynamic sections + if ((Error = PrivateOsFseek(elfSourceHandle, + elf.e_phoff, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + lastFileOffset = elf.e_phoff; + for (loop = 0; loop < elf.e_phnum; loop++) + { + if ((Error = PrivateOsFread(elfSourceHandle, &progHeader, + sizeof(Elf32_Phdr), &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Read failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + lastFileOffset += bytesRead; + if (progHeader.p_type == PT_LOAD) + { + NV_DEBUG_PRINTF(("Found load segment %d\r\n",loop)); + if ((Error = PrivateOsFseek(elfSourceHandle, + progHeader.p_offset, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + if (progHeader.p_vaddr < DRAM_MAND_ADDRESS) + { + if (IsApproachGreedy == NV_FALSE) + { + HeapSize = 1; + //conservative allocation - IRAM_PREF sections in DRAM. + HeapProperty[0] = NvRmHeap_ExternalCarveOut; + } + else + { + // greedy allocation - IRAM_PREF sections in IRAM, otherwise fallback to DRAM + HeapSize = 2; + HeapProperty[0] = NvRmHeap_IRam; + HeapProperty[1] = NvRmHeap_ExternalCarveOut; + } + Error = LoadLoadableProgramSegment(elfSourceHandle, hDevice, + (*hLibHandle), progHeader, elf, + HeapProperty, HeapSize, loop, + Filename, &segmentList); + if (Error != NvSuccess) + { + NV_DEBUG_PRINTF(("Unable to load segment %d \r\n", loop)); + goto CleanUpExit; + } + } + else if (progHeader.p_vaddr >= IRAM_PREF_EXT_ADDRESS) + { + NvU32 Chipid = NvRmModuleGetChipId(hDevice); + if(Chipid == 0x15 || Chipid == 0x16) + { + HeapSize = 1; + //conservative allocation - IRAM_PREF_EXT sections in DRAM for AP15. + HeapProperty[0] = NvRmHeap_ExternalCarveOut; + } + else if(Chipid >= 0x20) + { + if (IsApproachGreedy == NV_FALSE) + { + HeapSize = 1; + //conservative allocation - IRAM_PREF sections in DRAM. + HeapProperty[0] = NvRmHeap_ExternalCarveOut; + } + else + { + // greedy allocation - IRAM_PREF sections in IRAM, otherwise fallback to DRAM + HeapSize = 2; + HeapProperty[0] = NvRmHeap_IRam; + HeapProperty[1] = NvRmHeap_ExternalCarveOut; + } + } + Error = LoadLoadableProgramSegment(elfSourceHandle, hDevice, + (*hLibHandle), progHeader, elf, + HeapProperty, HeapSize, loop, + Filename, &segmentList); + if (Error != NvSuccess) + { + NV_DEBUG_PRINTF(("Unable to load segment %d \r\n", loop)); + goto CleanUpExit; + } + } + if ((Error = PrivateOsFseek(elfSourceHandle, + lastFileOffset, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + } + if (progHeader.p_type != PT_DYNAMIC) + continue; + dynamicSegmentOffset = progHeader.p_offset; + if ((Error = PrivateOsFseek(elfSourceHandle, + dynamicSegmentOffset, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + dynamicSegementBuffer = NvOsAlloc(progHeader.p_filesz); + if (dynamicSegementBuffer == NULL) + { + NV_DEBUG_PRINTF(("Memory Allocation %d Failed\r\n", progHeader.p_filesz)); + goto CleanUpExit; + } + if ((Error = PrivateOsFread(elfSourceHandle, dynamicSegementBuffer, + progHeader.p_filesz, &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Read failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + if ((Error = ParseDynamicSegment( + segmentList, + dynamicSegementBuffer, + progHeader.p_filesz, + dynamicSegmentOffset)) != NvSuccess) + { + NV_DEBUG_PRINTF(("Parsing and relocation of segment failed \r\n")); + goto CleanUpExit; + } + (*hLibHandle)->pList = segmentList; + if ((Error = PrivateOsFseek(elfSourceHandle, + lastFileOffset, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + goto CleanUpExit; + } + } + } + +CleanUpExit: + { + if (Error == NvSuccess) + { + UnMapRegion(segmentList); + NvOsFree(dynamicSegementBuffer); + PrivateOsFclose(elfSourceHandle); + } + else + { + RemoveRegion(segmentList); + NvOsFree(dynamicSegementBuffer); + PrivateOsFclose(elfSourceHandle); + NvOsFree(*hLibHandle); + } + } + return Error; +} + +NvError NvRmPrivFreeLibrary(NvRmLibHandle *hLibHandle) +{ + NvError Error = NvSuccess; + RemoveRegion(hLibHandle->pList); + NvOsFree(hLibHandle); + return Error; +} + +void parseElfHeader(Elf32_Ehdr *elf) +{ + if (elf->e_ident[0] == ELF_MAG0) + { + NV_DEBUG_PRINTF(("File is elf Object File with Identification %c%c%c\r\n", + elf->e_ident[1], elf->e_ident[2], elf->e_ident[3])); + NV_DEBUG_PRINTF(("Type of ELF is %x\r\n", elf->e_type)); + //An object file conforming to this specification must have + //the value EM_ARM (40, 0x28). + NV_DEBUG_PRINTF(("Machine type of the file is %x\r\n", elf->e_machine)); + //Address of entry point for this file. bit 1:0 + //indicates if entry point is ARM or thum mode + NV_DEBUG_PRINTF(("Entry point for this axf is %x\r\n", elf->e_entry)); + NV_DEBUG_PRINTF(("Version of the ELF is %d\r\n", elf->e_version)); + NV_DEBUG_PRINTF(("Program Table Header Offset %d\r\n", elf->e_phoff)); + NV_DEBUG_PRINTF(("Section Table Header Offset %d\r\n", elf->e_shoff)); + NV_DEBUG_PRINTF(("Elf Header size %d\r\n", elf->e_ehsize)); + NV_DEBUG_PRINTF(("Section Header's Size %d\r\n", elf->e_shentsize)); + NV_DEBUG_PRINTF(("Number of Section Headers %d\r\n", elf->e_shnum)); + NV_DEBUG_PRINTF(("String Table Section Header Index %d\r\n", elf->e_shstrndx)); + NV_DEBUG_PRINTF(("\r\n")); + } +} + +NvError parseProgramSegmentHeaders(PrivateOsFileHandle elfSourceHandle, + NvU32 segmentHeaderOffset, + NvU32 segmentCount) +{ + Elf32_Phdr progHeader; + size_t bytesRead = 0; + NvU32 loop = 0; + NvError Error = NvSuccess; + if (segmentCount) + { + NV_DEBUG_PRINTF(("Program Headers Found %d\r\n", segmentCount)); + if ((Error = PrivateOsFseek(elfSourceHandle, + segmentHeaderOffset, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", Error)); + return Error; + } + + for (loop = 0; loop < segmentCount; loop++) + { + if ((Error = PrivateOsFread(elfSourceHandle, &progHeader, + sizeof(progHeader), &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Read failed %d\r\n", bytesRead)); + return Error; + } + + NV_DEBUG_PRINTF(("Program %d Header type %d\r\n", + loop, progHeader.p_type)); + NV_DEBUG_PRINTF(("Program %d Header offset %d\r\n", + loop, progHeader.p_offset)); + NV_DEBUG_PRINTF(("Program %d Header Virtual Address %x\r\n", + loop, progHeader.p_vaddr)); + NV_DEBUG_PRINTF(("Program %d Header Physical Address %x\r\n", + loop, progHeader.p_paddr)); + NV_DEBUG_PRINTF(("Program %d Header Filesize %d\r\n", + loop, progHeader.p_filesz)); + NV_DEBUG_PRINTF(("Program %d Header Memory Size %d\r\n", + loop, progHeader.p_memsz)); + NV_DEBUG_PRINTF(("Program %d Header Flags %x\r\n", + loop, progHeader.p_flags)); + NV_DEBUG_PRINTF(("Program %d Header alignment %d\r\n", + loop, progHeader.p_align)); + NV_DEBUG_PRINTF(("\r\n")); + } + } + return NvSuccess; +} + +NvError +parseSectionHeaders(PrivateOsFileHandle elfSourceHandle, Elf32_Ehdr *elf) +{ + NvError Error = NvSuccess; + NvU32 stringTableOffset = 0; + Elf32_Shdr sectionHeader; + size_t bytesRead = 0; + NvU32 loop = 0; + char* stringTable = NULL; + const char *specialNamePtr = NULL; + + // Try to get to the string table so that we can get section names + stringTableOffset = elf->e_shoff + (elf->e_shentsize * elf->e_shstrndx); + + NV_DEBUG_PRINTF(("String Table File Offset is %d\r\n", stringTableOffset)); + + if ((Error = PrivateOsFseek(elfSourceHandle, + stringTableOffset, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + return Error; + } + if ((Error = PrivateOsFread(elfSourceHandle, §ionHeader, + sizeof(sectionHeader), &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Read failed %d\r\n", bytesRead)); + return Error; + } + if (sectionHeader.sh_type == SHT_STRTAB) + { + NV_DEBUG_PRINTF(("Found Section is string Table\r\n")); + if (sectionHeader.sh_size) + { + stringTable = NvOsAlloc(sectionHeader.sh_size); + if (stringTable == NULL) + { + NV_DEBUG_PRINTF(("String table mem allocation failed for %d\r\n", + sectionHeader.sh_size)); + return NvError_InsufficientMemory; + } + if ((Error = PrivateOsFseek(elfSourceHandle, + sectionHeader.sh_offset, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + goto CleanUpExit_parseSectionHeaders; + } + if ((Error = PrivateOsFread(elfSourceHandle, stringTable, + sectionHeader.sh_size, &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Read failed %d\r\n", bytesRead)); + goto CleanUpExit_parseSectionHeaders; + } + } + } + if ((Error = PrivateOsFseek(elfSourceHandle, + elf->e_shoff, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Seek failed %d\r\n", bytesRead)); + goto CleanUpExit_parseSectionHeaders; + } + for (loop = 0; loop < elf->e_shnum; loop++) + { + if ((Error = PrivateOsFread(elfSourceHandle, §ionHeader, + sizeof(sectionHeader), &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF(("File Read failed %d\r\n", bytesRead)); + goto CleanUpExit_parseSectionHeaders; + } + + NV_DEBUG_PRINTF(("Section %d is named %s\r\n", + loop, &stringTable[sectionHeader.sh_name])); + NV_DEBUG_PRINTF(("Section %d Type %d\r\n", + loop, sectionHeader.sh_type)); + NV_DEBUG_PRINTF(("Section %d Flags %x\r\n", + loop, sectionHeader.sh_flags)); + + GetSpecialSectionName(sectionHeader.sh_type, + sectionHeader.sh_flags, &specialNamePtr); + + NV_DEBUG_PRINTF(("Section %d Special Name is %s", + loop, specialNamePtr)); + NV_DEBUG_PRINTF(("Section %d Address %x\r\n", + loop, sectionHeader.sh_addr)); + NV_DEBUG_PRINTF(("Section %d File Offset %d\r\n", + loop, sectionHeader.sh_offset)); + NV_DEBUG_PRINTF(("Section %d Size %d \r\n", + loop, sectionHeader.sh_size)); + NV_DEBUG_PRINTF(("Section %d Link %d \r\n", + loop, sectionHeader.sh_link)); + NV_DEBUG_PRINTF(("Section %d Info %d\r\n", + loop, sectionHeader.sh_info)); + NV_DEBUG_PRINTF(("Section %d alignment %d\r\n", + loop, sectionHeader.sh_addralign)); + NV_DEBUG_PRINTF(("Section %d Fixed Entry Size %d\r\n", + loop, sectionHeader.sh_entsize)); + NV_DEBUG_PRINTF(("\r\n")); + + } +CleanUpExit_parseSectionHeaders: + if (stringTable) + NvOsFree(stringTable); + return Error; +} + + +NvError +loadSegmentsInFixedMemory(PrivateOsFileHandle elfSourceHandle, + Elf32_Ehdr *elf, NvU32 segmentIndex, void **loadaddress) +{ + NvError Error = NvSuccess; + size_t bytesRead = 0; + Elf32_Phdr progHeader; + + if ((Error = PrivateOsFseek(elfSourceHandle, + elf->e_phoff + (segmentIndex * sizeof(progHeader)), NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("loadSegmentsInFixedMemory File Seek failed %d\r\n", bytesRead)); + return Error; + } + + if ((Error = PrivateOsFread(elfSourceHandle, &progHeader, + sizeof(Elf32_Phdr), &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF((" loadSegmentsInFixedMemory File Read failed %d\r\n", bytesRead)); + return Error; + } + NV_ASSERT(progHeader.p_type == PT_LOAD); + if ((Error = PrivateOsFseek(elfSourceHandle, + progHeader.p_offset, NvOsSeek_Set)) != NvSuccess) + { + NV_DEBUG_PRINTF(("loadSegmentsInFixedMemory File Seek failed %d\r\n", Error)); + return Error; + } + + /* if((Error = NvRmPhysicalMemMap(progHeader.p_vaddr, */ + /* progHeader.p_memsz, NVOS_MEM_READ_WRITE, */ + /* NvOsMemAttribute_Uncached, loadaddress)) != NvSuccess) */ + /* { */ + /* NV_DEBUG_PRINTF(("loadSegmentsInFixedMemory Failed trying to Mem Map %x\r\n", progHeader.p_vaddr)); */ + /* return Error; */ + /* } */ + // This will initialize ZI to zero + *loadaddress = ioremap_nocache(progHeader.p_vaddr, progHeader.p_memsz); + + NvOsMemset(*loadaddress, 0, progHeader.p_memsz); + if ((Error = PrivateOsFread(elfSourceHandle, *loadaddress, + progHeader.p_filesz, &bytesRead)) != NvSuccess) + { + NV_DEBUG_PRINTF(("loadSegmentsInFixedMemory File Read failed %d\r\n", bytesRead)); + return Error; + } + // Load address need to be reset to the physical address as this is passed to the AVP as the entry point. + *loadaddress = (void *)progHeader.p_vaddr; + + return Error; +} + +NvError NvRmPrivGetProcAddress(NvRmLibraryHandle Handle, + const char *pSymbol, + void **pSymAddress) +{ + NvError Error = NvSuccess; + // In phase 1, this API will just return the load address as entry address + NvRmLibHandle *hHandle = Handle; + + //NOTE: The EntryAddress is pointing to a THUMB function + //(LSB is set). The Entry Function must be in THUMB mode. + if (hHandle->EntryAddress != NULL) + { + *pSymAddress = hHandle->EntryAddress; + } + else + { + Error = NvError_SymbolNotFound; + } + return Error; +} + +static int __init nvfw_init(void) +{ + int ret = 0; + + NvOsDebugPrintf("%s: called\n", __func__); + ret = misc_register(&nvfw_dev); + if (ret) panic("%s: misc_register FAILED\n", __func__); + + return ret; +} + +static void __exit nvfw_deinit(void) +{ + misc_deregister(&nvfw_dev); +} + +module_init(nvfw_init); +module_exit(nvfw_deinit); diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_moduleloader_private.h b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_moduleloader_private.h new file mode 100644 index 000000000000..becb53c978f3 --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_moduleloader_private.h @@ -0,0 +1,184 @@ +/* + * arch/arm/mach-tegra/nvrm/core/common/nvrm_moduleloader_private.h + * + * AVP firmware module loader + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_NVRM_MODULELOADER_PRIVATE_H +#define INCLUDED_NVRM_MODULELOADER_PRIVATE_H + +#include "nvrm_moduleloader.h" +#include "nvrm_memmgr.h" + +typedef struct PrivateOsFileRec +{ + const NvU8 *pstart; + const NvU8 *pread; + const NvU8 *pend; +} PrivateOsFile; + +typedef struct PrivateOsFileRec *PrivateOsFileHandle; + +#define LOAD_ADDRESS 0x11001000 +#define IRAM_PREF_EXT_ADDRESS 0x50000000 +#define IRAM_MAND_ADDRESS 0x40000000 +#define DRAM_MAND_ADDRESS 0x10000000 +#define DT_ARM_SYMTABSZ 0x70000001 +#define DT_ARM_RESERVED1 0x70000000 + +/// ELF magic number +enum +{ + ELF_MAG0 = 0x7F +}; + +/// ELF section header entry types. +enum +{ + SHT_INIT_ARRAY = 12, ///< Code initialization array + SHT_FINI_ARRAY, ///< Code finalization array + SHT_PREINIT_ARRAY, ///< Code pre-inialization array + SHT_GROUP, ///< Group + SHT_SYMTAB_SHNDX, ///< Symbol table index +}; +#define SHT_LOPROC 0x70000000 ///< Start of processor-specific +#define SHT_HIPROC 0x7fffffff ///< End of processor-specific +#define SHT_LOUSER 0x80000000 ///< Start of application-specific +#define SHT_HIUSER 0xffffffff ///< End of application-specific + +/// ELF dynamic section type flags +enum +{ + DT_NUM = 34, ///< Number used +}; + +/// ARM specific relocation codes +enum +{ + R_ARM_RABS32 = 253, +}; + +/// A linked list of load segment records +typedef struct SegmentRec SegmentNode; + +struct SegmentRec +{ + NvRmMemHandle pLoadRegion; + NvU32 LoadAddress; + NvU32 Index; + NvU32 VirtualAddr; + NvU32 MemorySize; + NvU32 FileOffset; + NvU32 FileSize; + void* MapAddr; + SegmentNode *Next; +}; + +/// ModuleLoader handle structure +typedef struct NvRmLibraryRec +{ + void* pLibBaseAddress; + void* EntryAddress; + SegmentNode *pList; +} NvRmLibHandle; + +NvError +NvRmPrivLoadKernelLibrary(NvRmDeviceHandle hDevice, + const char *pLibName, + NvRmLibraryHandle *hLibHandle); + +/// Add a load region to the segment list +SegmentNode* AddToSegmentList(SegmentNode *pList, + NvRmMemHandle pRegion, + Elf32_Phdr Phdr, + NvU32 Idx, + NvU32 PhysAddr, + void* MapAddr); + +/// Apply the relocation code based on relocation info from relocation table +NvError +ApplyRelocation(SegmentNode *pList, + NvU32 FileOffset, + NvU32 SegmentOffset, + NvRmMemHandle pRegion, + const Elf32_Rel *pRel); + +/// Get the special section name for a given section type and flag +NvError +GetSpecialSectionName(Elf32_Word SectionType, + Elf32_Word SectionFlags, + const char** SpecialSectionName); + +/// Parse the dynamic segment of ELF to extract the relocation table + NvError +ParseDynamicSegment(SegmentNode *pList, + const char* pSegmentData, + size_t SegmentSize, + NvU32 DynamicSegmentOffset); + +/// Parse ELF library and load the relocated library segments for a given library name +NvError NvRmPrivLoadLibrary(NvRmDeviceHandle hDevice, + const char *Filename, + NvU32 Address, + NvBool IsApproachGreedy, + NvRmLibraryHandle *hLibHandle); + +/// Get the symbol address. In phase1, this api will return the entry point address of the module +NvError +NvRmPrivGetProcAddress(NvRmLibraryHandle Handle, + const char *pSymbol, + void **pSymAddress); +/// Free the ELF library by unloading the library from memory +NvError NvRmPrivFreeLibrary(NvRmLibHandle *hLibHandle); + +NvError NvRmPrivInitModuleLoaderRPC(NvRmDeviceHandle hDevice); +NvError NvRmPrivInitAvp(NvRmDeviceHandle hDevice); + +/// Unmap memory segments +void UnMapRegion(SegmentNode *pList); +/// Unload segments +void RemoveRegion(SegmentNode *pList); + +void parseElfHeader(Elf32_Ehdr *elf); + +NvError +LoadLoadableProgramSegment(PrivateOsFileHandle elfSourceHandle, + NvRmDeviceHandle hDevice, + NvRmLibraryHandle hLibHandle, + Elf32_Phdr Phdr, + Elf32_Ehdr Ehdr, + const NvRmHeap * Heaps, + NvU32 NumHeaps, + NvU32 loop, + const char *Filename, + SegmentNode **segmentList); + +NvError +parseProgramSegmentHeaders(PrivateOsFileHandle elfSourceHandle, + NvU32 segmentHeaderOffset, + NvU32 segmentCount); + + NvError +parseSectionHeaders(PrivateOsFileHandle elfSourceHandle, + Elf32_Ehdr *elf); + +NvError +loadSegmentsInFixedMemory(PrivateOsFileHandle elfSourceHandle, + Elf32_Ehdr *elf, NvU32 segmentIndex, void **loadaddress); +#endif diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_rpc.h b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_rpc.h new file mode 100644 index 000000000000..b38e8a1b1f3a --- /dev/null +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_rpc.h @@ -0,0 +1,198 @@ +/* + * arch/arm/mach-tegra/nvrm/core/common/nvrm_rpc.h + * + * communication between processors (cpu and avp) + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef NVRM_RPC_H +#define NVRM_RPC_H + +/* + * nvrm_cpu_avp_rpc_private.h defines the private implementation functions to facilitate + * communication between processors (cpu and avp). + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvrm_init.h" +#include "nvrm_message.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** + * Initialize RPC + * + * Init the RPC. Both the service and client + * to the service must call this API before calling to create each endpoint of the connection + * via NvRmPrivRPCConnect + * + * If PortName is too long or does not exist debug mode + * assert is encountered. + * + * @param hDeviceHandle rm device handle + * @param rpcPortName the port name + * @param hRPCHandle the RPC transport handle + * + * @retval NvError_SemaphoreCreateFailed Creaion of semaphore failed. + */ + NvError NvRmPrivRPCInit( NvRmDeviceHandle hDeviceHandle, char* rpcPortName, NvRmRPCHandle *hRPCHandle ); +/** + * De-intialize the RPC and other resources. + * @param hRPCHandle the RPC transport handle + * + */ +void NvRmPrivRPCDeInit( NvRmRPCHandle hRPCHandle ); + +/** + * Connect to RPC port + * + * Creates one end of a RPC connection. Both the service and client + * to the service must call this API to create each endpoint of the connection + * through a specified port + * + * If PortName is too long or does not exist debug mode + * assert is encountered. + * + * @param hRPCHandle the RPC transport handle + * + * @retval NvSuccess Transport endpoint successfully allocated + * @retval NvError_InsufficientMemory Not enough memory to allocate endpoint + * @retval NvError_MutexCreateFailed Creaion of mutex failed. + * @retval NvError_SemaphoreCreateFailed Creaion of semaphore failed. + * @retval NvError_SharedMemAllocFailed Creaion of shared memory allocation + * failed. + * @retval NvError_NotInitialized The transport is not able to initialzed the + * threads. + */ + NvError NvRmPrivRPCConnect( NvRmRPCHandle hRPCHandle ); + + /** + * Connect to RPC port + * + * Creates one end of a RPC connection. Both the service and client + * to the service must call this API to create each endpoint of the connection + * through a specified port + * + * If PortName is too long or does not exist debug mode + * assert is encountered. + * + * @param hRPCHandle the RPC transport handle + * + * @retval NvSuccess Transport endpoint successfully allocated + * @retval NvError_InsufficientMemory Not enough memory to allocate endpoint + * @retval NvError_MutexCreateFailed Creaion of mutex failed. + * @retval NvError_SemaphoreCreateFailed Creaion of semaphore failed. + * @retval NvError_SharedMemAllocFailed Creaion of shared memory allocation + * failed. + * @retval NvError_NotInitialized The transport is not able to initialzed the + * threads. + */ + NvError NvRmPrivRPCWaitForConnect( NvRmRPCHandle hRPCHandle ); + /** + * Receive the message from the port. This will read the message if it is + * available for this port otherwise it will return the + * NvError_TransportMessageBoxEmpty error. + * + * @param hRPCHandle the RPC transport handle + * @param pMessageBuffer The pointer to the receive message buffer where the + * received message will be copied. + * @param pMessageSize Pointer to the variable where the length of the message + * will be stored. + * + * @retval NvSuccess Message received successfully. + * @retval NvError_NotInitialized hTransport is not open. + * @retval NvError_InvalidState The port is not connection state. + * @retval NvError_TransportMessageBoxEmpty The message box empty and not able + * to receive the message. + * @retval NvError_TransportIncompleteMessage The received message for this + * port is longer than the configured message length for this port. It copied + * the maximm size of the configured length of the message for this port and + * return the incomplete message buffer. + * @retval NvError_TransportMessageOverflow The port receives the message more + * than the configured queue depth count for this port and hence message + * overflow has been ocuured. + */ + + NvError NvRmPrivRPCRecvMsg( NvRmRPCHandle hRPCHandle, void* pMessageBuffer, NvU32 * pMessageSize ); + + /** + * Send Message. + * + * Sends a message to the other port which is connected to this port. + * Its a wrapper to rm transport send message + * + * @param hRPCHandle the RPC transport handle + * @param pMessageBuffer The pointer to the message buffer where message which + * need to be send is available. + * @param MessageSize Specifies the size of the message. + * + */ +void +NvRmPrivRPCSendMsg(NvRmRPCHandle hRPCHandle, + void* pMessageBuffer, + NvU32 MessageSize); + +/** + * Send and Recieve message. + * + * Send and Recieve a message between port. + * Its a wrapper to rm transport send message with response + * + * @param hRPCHandle the RPC transport handle + * @param pRecvMessageBuffer The pointer to the receive message buffer where the + * received message will be copied. + * @param MaxSize The maximum size in bytes that may be copied to the buffer + * @param pMessageSize Pointer to the variable where the length of the message + * will be stored. + * @param pSendMessageBuffer The pointer to the message buffer where message which + * need to be send is available. + * @param MessageSize Specifies the size of the message. + * + */ +void +NvRmPrivRPCSendMsgWithResponse(NvRmRPCHandle hRPCHandle, + void* pRecvMessageBuffer, + NvU32 MaxSize, + NvU32 *pMessageSize, + void* pSendMessageBuffer, + NvU32 MessageSize); + + +/** + * Closes a transport connection. Proper closure of this connection requires + * that both the client and service call this API. Therefore, it is expected + * that the client and service message one another to coordinate the close. + * + */ +void NvRmPrivRPCClose(NvRmRPCHandle hRPCHandle); + +NvError NvRmPrivInitService(NvRmDeviceHandle hDeviceHandle); + +void NvRmPrivServiceDeInit(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif diff --git a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_transport.c b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_transport.c index 6522dffe55d1..8227b9f2ca45 100644 --- a/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_transport.c +++ b/arch/arm/mach-tegra/nv/nvrm/core/common/nvrm_transport.c @@ -578,32 +578,12 @@ static void InboxFullIsr(void *args) } } -static irqreturn_t transport_ist(int irq, void *data) +static irqreturn_t transport_isr(int irq, void *data) { InboxFullIsr(data); - enable_irq(irq); return IRQ_HANDLED; } -static irqreturn_t transport_isr(int irq, void *data) -{ - disable_irq_nosync(irq); - return IRQ_WAKE_THREAD; -} - - -/** - * Handle the outbox empty interrupt. - */ - -static void -NvRmPrivProcIdGetProcessorInfo( - NvRmDeviceHandle hDevice, - NvRmModuleID *pProcModuleId) -{ - *pProcModuleId = NvRmModuleID_Cpu; -} - /** * Register for the transport interrupts. */ @@ -620,15 +600,14 @@ RegisterTransportInterrupt(NvRmDeviceHandle hDevice) IrqList = INT_SHR_SEM_INBOX_IBF; - set_irq_flags(IrqList, IRQF_VALID | IRQF_NOAUTOEN); - ret = request_threaded_irq(IrqList, transport_isr, transport_ist, 0, - "nvrm_transport", hDevice); + set_irq_flags(IrqList, IRQF_VALID); + ret = request_irq(IrqList, transport_isr, 0, + "nvrm_transport", hDevice); if (ret) { printk("%s failed %d\n", __func__, ret); return NvError_BadParameter; } s_TransportInterruptHandle = IrqList; - enable_irq(IrqList); return NvSuccess; } diff --git a/arch/arm/mach-tegra/nv/nvrm/dispatch/nvrm_power_dispatch.c b/arch/arm/mach-tegra/nv/nvrm/dispatch/nvrm_power_dispatch.c index 952fcd77c05a..fd80efcb642f 100644 --- a/arch/arm/mach-tegra/nv/nvrm/dispatch/nvrm_power_dispatch.c +++ b/arch/arm/mach-tegra/nv/nvrm/dispatch/nvrm_power_dispatch.c @@ -225,7 +225,7 @@ NvError nvrm_power_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void switch( function ) { case 9: err_ = NvRmPowerVoltageControl_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); - break; + break; case 8: err_ = NvRmPowerModuleClockControl_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); break; diff --git a/arch/arm/mach-tegra/nv/nvrpc_user.c b/arch/arm/mach-tegra/nv/nvrpc_user.c index 874ebdc57d9a..19ac6c540759 100644 --- a/arch/arm/mach-tegra/nv/nvrpc_user.c +++ b/arch/arm/mach-tegra/nv/nvrpc_user.c @@ -27,7 +27,7 @@ #include #include #include -#include "linux/nvrpc_ioctl.h" +#include #include "nvcommon.h" #include "nvassert.h" #include "nvos.h" @@ -102,15 +102,17 @@ static struct miscdevice nvrpc_dev = static DEFINE_MUTEX(nvrpc_device_lock); +static NvBool s_init_done = NV_FALSE; +NvRmDeviceHandle s_hRmGlobal = NULL; + int nvrpc_open(struct inode *inode, struct file *file) { NvError e = NvSuccess; - static NvBool init_done = NV_FALSE; mutex_lock(&nvrpc_device_lock); - if (init_done == NV_FALSE) { - e = NvRmTransportInit(NULL); - init_done = NV_TRUE; + if (s_init_done == NV_FALSE) { + e = NvRmTransportInit(s_hRmGlobal); + s_init_done = NV_TRUE; } mutex_unlock(&nvrpc_device_lock); @@ -139,6 +141,13 @@ static int nvrpc_make_error_code(NvError e) return error; } +NvRmTransportHandle g_hTransportAvp = NULL; +NvRmTransportHandle g_hTransportCpu = NULL; +NvOsSemaphoreHandle g_hTransportAvpSem = NULL; +NvOsSemaphoreHandle g_hTransportCpuSem = NULL; +int g_hTransportAvpIsConnected = 0; +int g_hTransportCpuIsConnected = 0; + static int nvrpc_ioctl_open(struct file *filp, unsigned int cmd, void __user *arg) { @@ -175,12 +184,29 @@ static int nvrpc_ioctl_open(struct file *filp, if (e != NvSuccess) goto fail; } - op.ret_val = NvRmTransportOpen(NULL, p_name, recv_sem, + printk(KERN_ERR "%s: NvRmTransportOpen\n", __func__); + op.ret_val = NvRmTransportOpen(s_hRmGlobal, p_name, recv_sem, (void *)&op.transport_handle); error = copy_to_user(arg, &op, sizeof(op)); + if (p_name && ! strcmp(p_name, "RPC_CPU_PORT")) { + if (g_hTransportCpu) { + panic("%s: g_hTransportCpu=%p is already assigned.\n", __func__, g_hTransportCpu); + } + g_hTransportCpu = (NvRmTransportHandle)op.transport_handle; + g_hTransportCpuSem = (NvOsSemaphoreHandle) op.sem; + printk(KERN_ERR "%s: g_hTransportCpu=%p\n", __func__, g_hTransportCpu); + } + if (p_name && ! strcmp(p_name, "RPC_AVP_PORT")) { + if (g_hTransportAvp) { + panic("%s: g_hTransportAvp=%p is already assigned.\n", __func__, g_hTransportAvp); + } + g_hTransportAvp = (NvRmTransportHandle)op.transport_handle; + g_hTransportAvpSem = (NvOsSemaphoreHandle) op.sem; + printk(KERN_ERR "%s: g_hTransportAvp=%p\n", __func__, g_hTransportAvp); + } fail: - nvrpc_stack_kfree(port_name, p_name); + nvrpc_stack_kfree((char*)port_name, p_name); if (recv_sem) NvOsSemaphoreDestroy(recv_sem); if (e != NvSuccess) @@ -217,7 +243,7 @@ static int nvrpc_ioctl_get_port_name(struct file *filp, } fail: - nvrpc_stack_kfree(port_name, p_name); + nvrpc_stack_kfree((NvS8*)port_name, p_name); return error; } @@ -262,14 +288,28 @@ static int nvrpc_ioctl_connect(struct file *filp, NvError e = NvSuccess; int error; struct nvrpc_handle_param op; + NvU8 port_name[NVRPC_MAX_LOCAL_STACK]; error = copy_from_user(&op, arg, sizeof(op)); if (error) goto fail; + + NvRmTransportGetPortName((void *)op.handle, + port_name, sizeof(port_name)); + printk(KERN_INFO "%s: port_name=%s\n", __func__, port_name); + + op.ret_val = NvRmTransportConnect( (void *)op.handle, op.param); error = copy_to_user(arg, &op, sizeof(op)); + if (! strcmp(port_name, "RPC_AVP_PORT")) { + g_hTransportAvpIsConnected = 1; + } + if (! strcmp(port_name, "RPC_CPU_PORT")) { + g_hTransportCpuIsConnected = 1; + } + fail: if (e != NvSuccess) error = nvrpc_make_error_code(e); @@ -613,6 +653,18 @@ static int __init nvrpc_init(void) { int ret = 0; + NvRmDeviceHandle handle; + NvRmInit(&handle); + + if (s_init_done == NV_FALSE) { + NvError e; + + printk(KERN_INFO "%s: NvRmTransportInit\n", __func__); + e = NvRmOpen(&s_hRmGlobal, 0); + e = NvRmTransportInit(s_hRmGlobal); + s_init_done = NV_TRUE; + } + ret = misc_register(&nvrpc_dev); if (ret) { pr_err("%s misc register FAILED\n", __func__); -- 2.34.1