| /** @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, ¶ms); |
| 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, ¶m); |
| 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, ¶ms); |
| 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; |
| } |
| |
| /** @} */ |