blob: cddda519b8064af1e8d3b51d4cc10a069f3ce3dc [file] [log] [blame]
/** @addtogroup MCD_MCDIMPL_DAEMON_KERNEL
* @{
* @file
*
* MobiCore Driver Kernel Module Interface.
*
* <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
#include <cstdlib>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <inttypes.h>
#include <cstring>
#include "McTypes.h"
#include "mc_linux.h"
#include "mcVersionHelper.h"
#include "CMcKMod.h"
#include "log.h"
//------------------------------------------------------------------------------
MC_CHECK_VERSION(MCDRVMODULEAPI, 1, 1);
//------------------------------------------------------------------------------
mcResult_t CMcKMod::mapWsm(
uint32_t len,
uint32_t *pHandle,
addr_t *pVirtAddr,
addr_t *pPhysAddr)
{
int ret = 0;
LOG_V(" mapWsm(): len=%d", len);
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
// mapping response data is in the buffer
struct mc_ioctl_map mapParams = { len:
len
};
ret = ioctl(fdKMod, MC_IO_MAP_WSM, &mapParams);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_MAP_WSM");
return MAKE_MC_DRV_KMOD_WITH_ERRNO(errno);
}
addr_t virtAddr = ::mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED,
fdKMod, mapParams.phys_addr);
if (virtAddr == MAP_FAILED) {
LOG_ERRNO("mmap");
return MAKE_MC_DRV_KMOD_WITH_ERRNO(errno);
}
LOG_V(" mapped to %p, handle=%d, phys=%p ", virtAddr,
mapParams.handle, (addr_t) (mapParams.phys_addr));
if (pVirtAddr != NULL) {
*pVirtAddr = virtAddr;
}
if (pHandle != NULL) {
*pHandle = mapParams.handle;
}
if (pPhysAddr != NULL) {
*pPhysAddr = (addr_t) (mapParams.phys_addr);
}
return 0;
}
//------------------------------------------------------------------------------
mcResult_t CMcKMod::mapMCI(
uint32_t len,
uint32_t *pHandle,
addr_t *pVirtAddr,
addr_t *pPhysAddr,
bool *pReuse)
{
LOG_I("Mapping MCI: len=%d", len);
// mapping response data is in the buffer
struct mc_ioctl_map mapParams = { len:
len
};
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
int ret = ioctl(fdKMod, MC_IO_MAP_MCI, &mapParams);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_MAP_MCI");
return MAKE_MC_DRV_KMOD_WITH_ERRNO(errno);
}
addr_t virtAddr = ::mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED,
fdKMod, 0);
if (virtAddr == MAP_FAILED) {
LOG_ERRNO("mmap");
return MAKE_MC_DRV_KMOD_WITH_ERRNO(errno);
}
mapParams.addr = (unsigned long)virtAddr;
*pReuse = mapParams.reused;
LOG_V(" MCI mapped to %p, handle=%d, phys=%p, reused=%s",
(void *)mapParams.addr, mapParams.handle, (addr_t) (mapParams.phys_addr),
mapParams.reused ? "true" : "false");
if (pVirtAddr != NULL) {
*pVirtAddr = (void *)mapParams.addr;
}
if (pHandle != NULL) {
*pHandle = mapParams.handle;
}
if (pPhysAddr != NULL) {
*pPhysAddr = (addr_t) (mapParams.phys_addr);
}
// clean memory
//memset(pMmapResp, 0, sizeof(*pMmapResp));
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t CMcKMod::mapPersistent(
uint32_t len,
uint32_t *pHandle,
addr_t *pVirtAddr,
addr_t *pPhysAddr)
{
// Not currently supported by the driver
LOG_E("MobiCore Driver does't support persistent buffers");
return MC_DRV_ERR_NOT_IMPLEMENTED;
}
//------------------------------------------------------------------------------
int CMcKMod::read(addr_t buffer, uint32_t len)
{
int ret = 0;
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
ret = ::read(fdKMod, buffer, len);
if (ret == -1) {
LOG_ERRNO("read");
}
return ret;
}
//------------------------------------------------------------------------------
bool CMcKMod::waitSSIQ(uint32_t *pCnt)
{
uint32_t cnt;
if (read(&cnt, sizeof(cnt)) != sizeof(cnt)) {
return false;
}
if (pCnt != NULL) {
*pCnt = cnt;
}
return true;
}
//------------------------------------------------------------------------------
int CMcKMod::fcInit(uint32_t nqOffset, uint32_t nqLength, uint32_t mcpOffset,
uint32_t mcpLength)
{
int ret = 0;
if (!isOpen()) {
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
// Init MC with NQ and MCP buffer addresses
struct mc_ioctl_init fcInitParams = {
nq_offset :
nqOffset,
nq_length :
nqLength,
mcp_offset :
mcpOffset,
mcp_length :
mcpLength
};
ret = ioctl(fdKMod, MC_IO_INIT, &fcInitParams);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_INIT");
}
return ret;
}
//------------------------------------------------------------------------------
int CMcKMod::fcInfo(uint32_t extInfoId, uint32_t *pState, uint32_t *pExtInfo)
{
int ret = 0;
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
// Init MC with NQ and MCP buffer addresses
struct mc_ioctl_info fcInfoParams = {ext_info_id :
extInfoId
};
ret = ioctl(fdKMod, MC_IO_INFO, &fcInfoParams);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_INFO");
return ret;
}
if (pState != NULL) {
*pState = fcInfoParams.state;
}
if (pExtInfo != NULL) {
*pExtInfo = fcInfoParams.ext_info;
}
return ret;
}
//------------------------------------------------------------------------------
int CMcKMod::fcYield(void)
{
int ret = 0;
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
ret = ioctl(fdKMod, MC_IO_YIELD, NULL);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_YIELD");
LOG_E("ret = %d", ret);
}
return ret;
}
//------------------------------------------------------------------------------
int CMcKMod::fcNSIQ(void)
{
int ret = 0;
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
ret = ioctl(fdKMod, MC_IO_NSIQ, NULL);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_NSIQ");
LOG_E("ret = %d", ret);
}
return ret;
}
//------------------------------------------------------------------------------
mcResult_t CMcKMod::free(uint32_t handle, addr_t buffer, uint32_t len)
{
LOG_V("free(): handle=%d", handle);
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
// Even if unmap fails we still go on with our request
if (::munmap(buffer, len)) {
LOG_I("buffer = %p, len = %d", buffer, len);
LOG_ERRNO("mmap failed");
}
int ret = ioctl(fdKMod, MC_IO_FREE, handle);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_FREE");
return MAKE_MC_DRV_KMOD_WITH_ERRNO(errno);
}
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t CMcKMod::registerWsmL2(
addr_t buffer,
uint32_t len,
uint32_t pid,
uint32_t *pHandle,
addr_t *pPhysWsmL2)
{
LOG_I(" Registering virtual buffer at %p, len=%d as World Shared Memory", buffer, len);
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
struct mc_ioctl_reg_wsm params = {
buffer :
(uint32_t) buffer,
len :
len,
pid :
pid
};
int ret = ioctl(fdKMod, MC_IO_REG_WSM, &params);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_UNREG_WSM");
return MAKE_MC_DRV_KMOD_WITH_ERRNO(errno);
}
LOG_I(" Registered, handle=%d, L2 phys=0x%x ", params.handle, params.table_phys);
if (pHandle != NULL) {
*pHandle = params.handle;
}
if (pPhysWsmL2 != NULL) {
*pPhysWsmL2 = (addr_t) params.table_phys;
}
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t CMcKMod::unregisterWsmL2(uint32_t handle)
{
LOG_I(" Unregistering World Shared Memory with handle %d", handle);
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
int ret = ioctl(fdKMod, MC_IO_UNREG_WSM, handle);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_UNREG_WSM");
return MAKE_MC_DRV_KMOD_WITH_ERRNO(errno);
}
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t CMcKMod::lockWsmL2(uint32_t handle)
{
int ret = 0;
LOG_I(" Locking World Shared Memory with handle %d", handle);
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
ret = ioctl(fdKMod, MC_IO_LOCK_WSM, handle);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_UNREG_WSM");
LOG_E("ret = %d", ret);
}
return ret;
}
//------------------------------------------------------------------------------
mcResult_t CMcKMod::unlockWsmL2(uint32_t handle)
{
int ret = 0;
LOG_I(" Unlocking World Shared Memory with handle %d", handle);
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
ret = ioctl(fdKMod, MC_IO_UNLOCK_WSM, handle);
// Failure here is not really important
if (ret != 0) {
LOG_I("ret = %d", ret);
}
return ret;
}
//------------------------------------------------------------------------------
addr_t CMcKMod::findWsmL2(uint32_t handle)
{
int ret = 0;
uint32_t param = handle;
LOG_I(" Resolving the WSM l2 for handle=%u", handle);
if (!isOpen()) {
LOG_E("no connection to kmod");
return NULL;
}
ret = ioctl(fdKMod, MC_IO_RESOLVE_WSM, &param);
if (ret != 0 || param == 0) {
LOG_ERRNO("ioctl MC_IO_RESOLVE_WSM");
LOG_E("param %u, ret = %d", param, ret);
}
return (addr_t)param;
}
//------------------------------------------------------------------------------
mcResult_t CMcKMod::findContiguousWsm(uint32_t handle, addr_t *phys, uint32_t *len)
{
mcResult_t ret = MC_DRV_OK;
struct mc_ioctl_resolv_cont_wsm wsm;
wsm.handle = handle;
LOG_I(" Resolving the contiguous WSM l2 for handle=%u", handle);
if (!isOpen()) {
LOG_E("no connection to kmod");
return NULL;
}
ret = ioctl(fdKMod, MC_IO_RESOLVE_CONT_WSM, &wsm);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_RESOLVE_CONT_WSM");
} else {
*phys = (addr_t)wsm.phys;
*len = wsm.length;
}
return ret;
}
//------------------------------------------------------------------------------
mcResult_t CMcKMod::cleanupWsmL2(void)
{
int ret = 0;
LOG_I(" Cleaning up the orphaned bulk buffers");
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
ret = ioctl(fdKMod, MC_IO_CLEAN_WSM, 0);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_UNREG_WSM");
LOG_E("ret = %d", ret);
}
return ret;
}
//------------------------------------------------------------------------------
int CMcKMod::fcExecute(addr_t startAddr, uint32_t areaLength)
{
int ret = 0;
struct mc_ioctl_execute params = {
phys_start_addr :
(uint32_t)startAddr,
length :
areaLength
};
if (!isOpen()) {
LOG_E("no connection to kmod");
return MC_DRV_ERR_KMOD_NOT_OPEN;
}
ret = ioctl(fdKMod, MC_IO_EXECUTE, &params);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_EXECUTE");
}
return ret;
}
//------------------------------------------------------------------------------
bool CMcKMod::checkVersion(void)
{
uint32_t version;
if (!isOpen()) {
LOG_E("no connection to kmod");
return false;
}
int ret = ioctl(fdKMod, MC_IO_VERSION, &version);
if (ret != 0) {
LOG_ERRNO("ioctl MC_IO_VERSION");
LOG_E("ret = %d", ret);
return false;
}
// Run-time check.
char *errmsg;
if (!checkVersionOkMCDRVMODULEAPI(version, &errmsg)) {
LOG_E("%s", errmsg);
return false;
}
LOG_I("%s", errmsg);
return true;
}
/** @} */