1041 lines
25 KiB
C
1041 lines
25 KiB
C
|
/*
|
||
|
* MobiCore KernelApi module
|
||
|
*
|
||
|
* <-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 as
|
||
|
* published by the Free Software Foundation.
|
||
|
*/
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/init.h>
|
||
|
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/netlink.h>
|
||
|
#include <net/sock.h>
|
||
|
#include <net/net_namespace.h>
|
||
|
#include <linux/list.h>
|
||
|
|
||
|
#include "public/mobicore_driver_api.h"
|
||
|
#include "public/mobicore_driver_cmd.h"
|
||
|
#include "include/mcinq.h"
|
||
|
#include "device.h"
|
||
|
#include "session.h"
|
||
|
|
||
|
/* device list */
|
||
|
LIST_HEAD(devices);
|
||
|
|
||
|
static struct mcore_device_t *resolve_device_id(uint32_t device_id)
|
||
|
{
|
||
|
struct mcore_device_t *tmp;
|
||
|
struct list_head *pos;
|
||
|
|
||
|
/* Get mcore_device_t for device_id */
|
||
|
list_for_each(pos, &devices) {
|
||
|
tmp = list_entry(pos, struct mcore_device_t, list);
|
||
|
if (tmp->device_id == device_id)
|
||
|
return tmp;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void add_device(struct mcore_device_t *device)
|
||
|
{
|
||
|
list_add_tail(&(device->list), &devices);
|
||
|
}
|
||
|
|
||
|
static bool remove_device(uint32_t device_id)
|
||
|
{
|
||
|
struct mcore_device_t *tmp;
|
||
|
struct list_head *pos, *q;
|
||
|
|
||
|
list_for_each_safe(pos, q, &devices) {
|
||
|
tmp = list_entry(pos, struct mcore_device_t, list);
|
||
|
if (tmp->device_id == device_id) {
|
||
|
list_del(pos);
|
||
|
mcore_device_cleanup(tmp);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
enum mc_result mc_open_device(uint32_t device_id)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_OK;
|
||
|
struct connection *dev_con = NULL;
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
struct mcore_device_t *device = resolve_device_id(device_id);
|
||
|
if (device != NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"Device %d already opened", device_id);
|
||
|
mc_result = MC_DRV_ERR_INVALID_OPERATION;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Open new connection to device */
|
||
|
dev_con = connection_new();
|
||
|
if (!connection_connect(dev_con, MC_DAEMON_PID)) {
|
||
|
MCDRV_DBG_ERROR(
|
||
|
mc_kapi,
|
||
|
"Could not setup netlink connection to PID %u",
|
||
|
MC_DAEMON_PID);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Forward device open to the daemon and read result */
|
||
|
struct mc_drv_cmd_open_device_t mc_drv_cmd_open_device = {
|
||
|
{
|
||
|
MC_DRV_CMD_OPEN_DEVICE
|
||
|
},
|
||
|
{
|
||
|
device_id
|
||
|
}
|
||
|
};
|
||
|
|
||
|
int len = connection_write_data(
|
||
|
dev_con,
|
||
|
&mc_drv_cmd_open_device,
|
||
|
sizeof(struct mc_drv_cmd_open_device_t));
|
||
|
if (len < 0) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_OPEN_DEVICE writeCmd failed %d",
|
||
|
len);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct mc_drv_response_header_t rsp_header;
|
||
|
len = connection_read_datablock(
|
||
|
dev_con,
|
||
|
&rsp_header,
|
||
|
sizeof(rsp_header));
|
||
|
if (len != sizeof(rsp_header)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_OPEN_DEVICE readRsp failed %d",
|
||
|
len);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
if (rsp_header.response_id != MC_DRV_RSP_OK) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_OPEN_DEVICE failed, respId=%d",
|
||
|
rsp_header.response_id);
|
||
|
switch (rsp_header.response_id) {
|
||
|
case MC_DRV_RSP_PAYLOAD_LENGTH_ERROR:
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
case MC_DRV_INVALID_DEVICE_NAME:
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
case MC_DRV_RSP_DEVICE_ALREADY_OPENED:
|
||
|
default:
|
||
|
mc_result = MC_DRV_ERR_INVALID_OPERATION;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* there is no payload to read */
|
||
|
|
||
|
device = mcore_device_create(device_id, dev_con);
|
||
|
if (!mcore_device_open(device, MC_DRV_MOD_DEVNODE_FULLPATH)) {
|
||
|
mcore_device_cleanup(device);
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"could not open device file: %s",
|
||
|
MC_DRV_MOD_DEVNODE_FULLPATH);
|
||
|
mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
add_device(device);
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
if (mc_result != MC_DRV_OK)
|
||
|
connection_cleanup(dev_con);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_open_device);
|
||
|
|
||
|
enum mc_result mc_close_device(uint32_t device_id)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_OK;
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
struct mcore_device_t *device = resolve_device_id(device_id);
|
||
|
if (device == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Device not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
struct connection *dev_con = device->connection;
|
||
|
|
||
|
/* Return if not all sessions have been closed */
|
||
|
if (mcore_device_has_sessions(device)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"cannot close with sessions pending");
|
||
|
mc_result = MC_DRV_ERR_SESSION_PENDING;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct mc_drv_cmd_close_device_t mc_drv_cmd_close_device = {
|
||
|
{
|
||
|
MC_DRV_CMD_CLOSE_DEVICE
|
||
|
}
|
||
|
};
|
||
|
int len = connection_write_data(
|
||
|
dev_con,
|
||
|
&mc_drv_cmd_close_device,
|
||
|
sizeof(struct mc_drv_cmd_close_device_t));
|
||
|
/* ignore error, but log details */
|
||
|
if (len < 0) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_CLOSE_DEVICE writeCmd failed %d",
|
||
|
len);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
}
|
||
|
|
||
|
struct mc_drv_response_header_t rsp_header;
|
||
|
len = connection_read_datablock(
|
||
|
dev_con,
|
||
|
&rsp_header,
|
||
|
sizeof(rsp_header));
|
||
|
if (len != sizeof(rsp_header)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_CLOSE_DEVICE readResp failed %d",
|
||
|
len);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (rsp_header.response_id != MC_DRV_RSP_OK) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_CLOSE_DEVICE failed, respId=%d",
|
||
|
rsp_header.response_id);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
remove_device(device_id);
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_close_device);
|
||
|
|
||
|
enum mc_result mc_open_session(struct mc_session_handle *session,
|
||
|
const struct mc_uuid_t *uuid,
|
||
|
uint8_t *tci, uint32_t len)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_OK;
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
if (session == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Session is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
if (uuid == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "UUID is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
if (tci == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "TCI is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
if (len > MC_MAX_TCI_LEN) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "TCI length is longer than %d",
|
||
|
MC_MAX_TCI_LEN);
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Get the device associated with the given session */
|
||
|
struct mcore_device_t *device =
|
||
|
resolve_device_id(session->device_id);
|
||
|
if (device == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Device not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
struct connection *dev_con = device->connection;
|
||
|
|
||
|
/* Get the physical address of the given TCI */
|
||
|
struct wsm *wsm =
|
||
|
mcore_device_find_contiguous_wsm(device, tci);
|
||
|
if (wsm == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"Could not resolve TCI phy address ");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (wsm->len < len) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"length is more than allocated TCI");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Prepare open session command */
|
||
|
struct mc_drv_cmd_open_session_t cmdOpenSession = {
|
||
|
{
|
||
|
MC_DRV_CMD_OPEN_SESSION
|
||
|
},
|
||
|
{
|
||
|
session->device_id,
|
||
|
*uuid,
|
||
|
(uint32_t)wsm->phys_addr,
|
||
|
len
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/* Transmit command data */
|
||
|
int len = connection_write_data(dev_con,
|
||
|
&cmdOpenSession,
|
||
|
sizeof(cmdOpenSession));
|
||
|
if (len != sizeof(cmdOpenSession)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_OPEN_SESSION writeData failed %d",
|
||
|
len);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Read command response */
|
||
|
|
||
|
/* read header first */
|
||
|
struct mc_drv_response_header_t rsp_header;
|
||
|
len = connection_read_datablock(dev_con,
|
||
|
&rsp_header,
|
||
|
sizeof(rsp_header));
|
||
|
if (len != sizeof(rsp_header)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_OPEN_SESSION readResp failed %d",
|
||
|
len);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (rsp_header.response_id != MC_DRV_RSP_OK) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_OPEN_SESSION failed, respId=%d",
|
||
|
rsp_header.response_id);
|
||
|
switch (rsp_header.response_id) {
|
||
|
case MC_DRV_RSP_TRUSTLET_NOT_FOUND:
|
||
|
mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
|
||
|
break;
|
||
|
case MC_DRV_RSP_PAYLOAD_LENGTH_ERROR:
|
||
|
case MC_DRV_RSP_DEVICE_NOT_OPENED:
|
||
|
case MC_DRV_RSP_FAILED:
|
||
|
default:
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* read payload */
|
||
|
struct mc_drv_rsp_open_session_payload_t
|
||
|
rsp_open_session_payload;
|
||
|
len = connection_read_datablock(
|
||
|
dev_con,
|
||
|
&rsp_open_session_payload,
|
||
|
sizeof(rsp_open_session_payload));
|
||
|
if (len != sizeof(rsp_open_session_payload)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_OPEN_SESSION readPayload fail %d",
|
||
|
len);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Register session with handle */
|
||
|
session->session_id = rsp_open_session_payload.session_id;
|
||
|
|
||
|
/* Set up second channel for notifications */
|
||
|
struct connection *session_connection = connection_new();
|
||
|
|
||
|
if (!connection_connect(session_connection, MC_DAEMON_PID)) {
|
||
|
MCDRV_DBG_ERROR(
|
||
|
mc_kapi,
|
||
|
"Could not setup netlink connection to PID %u",
|
||
|
MC_DAEMON_PID);
|
||
|
connection_cleanup(session_connection);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Write command to use channel for notifications */
|
||
|
struct mc_drv_cmd_nqconnect_t cmd_nqconnect = {
|
||
|
{
|
||
|
MC_DRV_CMD_NQ_CONNECT
|
||
|
},
|
||
|
{
|
||
|
session->device_id,
|
||
|
session->session_id,
|
||
|
rsp_open_session_payload.device_session_id,
|
||
|
rsp_open_session_payload.session_magic
|
||
|
}
|
||
|
};
|
||
|
connection_write_data(session_connection,
|
||
|
&cmd_nqconnect,
|
||
|
sizeof(cmd_nqconnect));
|
||
|
|
||
|
/* Read command response, header first */
|
||
|
len = connection_read_datablock(session_connection,
|
||
|
&rsp_header,
|
||
|
sizeof(rsp_header));
|
||
|
if (len != sizeof(rsp_header)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_NQ_CONNECT readRsp failed %d",
|
||
|
len);
|
||
|
connection_cleanup(session_connection);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (rsp_header.response_id != MC_DRV_RSP_OK) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_NQ_CONNECT failed, respId=%d",
|
||
|
rsp_header.response_id);
|
||
|
connection_cleanup(session_connection);
|
||
|
mc_result = MC_DRV_ERR_NQ_FAILED;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* there is no payload. */
|
||
|
|
||
|
/* Session established, new session object must be created */
|
||
|
mcore_device_create_new_session(device,
|
||
|
session->session_id,
|
||
|
session_connection);
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_open_session);
|
||
|
|
||
|
enum mc_result mc_close_session(struct mc_session_handle *session)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_OK;
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
if (session == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Session is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct mcore_device_t *device =
|
||
|
resolve_device_id(session->device_id);
|
||
|
if (device == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Device not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
struct connection *dev_con = device->connection;
|
||
|
|
||
|
struct session *nq_session =
|
||
|
mcore_device_resolve_session_id(device,
|
||
|
session->session_id);
|
||
|
|
||
|
if (nq_session == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Session not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Write close session command */
|
||
|
struct mc_drv_cmd_close_session_t cmd_close_session = {
|
||
|
{
|
||
|
MC_DRV_CMD_CLOSE_SESSION
|
||
|
},
|
||
|
{
|
||
|
session->session_id,
|
||
|
}
|
||
|
};
|
||
|
connection_write_data(dev_con,
|
||
|
&cmd_close_session,
|
||
|
sizeof(cmd_close_session));
|
||
|
|
||
|
/* Read command response */
|
||
|
struct mc_drv_response_header_t rsp_header;
|
||
|
int len = connection_read_datablock(dev_con,
|
||
|
&rsp_header,
|
||
|
sizeof(rsp_header));
|
||
|
if (len != sizeof(rsp_header)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_CLOSE_SESSION readRsp failed %d",
|
||
|
len);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (rsp_header.response_id != MC_DRV_RSP_OK) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_CLOSE_SESSION failed, respId=%d",
|
||
|
rsp_header.response_id);
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
mcore_device_remove_session(device, session->session_id);
|
||
|
mc_result = MC_DRV_OK;
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_close_session);
|
||
|
|
||
|
enum mc_result mc_notify(struct mc_session_handle *session)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_OK;
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
if (session == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Session is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct mcore_device_t *device =
|
||
|
resolve_device_id(session->device_id);
|
||
|
if (device == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Device not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
struct connection *dev_con = device->connection;
|
||
|
|
||
|
struct session *nqsession =
|
||
|
mcore_device_resolve_session_id(device, session->session_id);
|
||
|
if (nqsession == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Session not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct mc_drv_cmd_notify_t cmd_notify = {
|
||
|
{
|
||
|
MC_DRV_CMD_NOTIFY
|
||
|
},
|
||
|
{
|
||
|
session->session_id
|
||
|
}
|
||
|
};
|
||
|
|
||
|
connection_write_data(dev_con,
|
||
|
&cmd_notify,
|
||
|
sizeof(cmd_notify));
|
||
|
|
||
|
/* Daemon will not return a response */
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_notify);
|
||
|
|
||
|
enum mc_result mc_wait_notification(struct mc_session_handle *session,
|
||
|
int32_t timeout)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_OK;
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
if (session == NULL) {
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct mcore_device_t *device =
|
||
|
resolve_device_id(session->device_id);
|
||
|
if (device == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Device not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct session *nq_session =
|
||
|
mcore_device_resolve_session_id(device,
|
||
|
session->session_id);
|
||
|
if (nq_session == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Session not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct connection *nqconnection =
|
||
|
nq_session->notification_connection;
|
||
|
uint32_t count = 0;
|
||
|
|
||
|
/* Read notification queue till it's empty */
|
||
|
for (;;) {
|
||
|
struct notification notification;
|
||
|
ssize_t num_read =
|
||
|
connection_read_data(nqconnection,
|
||
|
¬ification,
|
||
|
sizeof(notification),
|
||
|
timeout);
|
||
|
/*
|
||
|
* Exit on timeout in first run. Later runs have
|
||
|
* timeout set to 0.
|
||
|
* -2 means, there is no more data.
|
||
|
*/
|
||
|
if (count == 0 && num_read == -2) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "read timeout");
|
||
|
mc_result = MC_DRV_ERR_TIMEOUT;
|
||
|
break;
|
||
|
}
|
||
|
/*
|
||
|
* After first notification the queue will be
|
||
|
* drained, Thus we set no timeout for the
|
||
|
* following reads
|
||
|
*/
|
||
|
timeout = 0;
|
||
|
|
||
|
if (num_read != sizeof(struct notification)) {
|
||
|
if (count == 0) {
|
||
|
/* failure in first read, notify it */
|
||
|
mc_result = MC_DRV_ERR_NOTIFICATION;
|
||
|
MCDRV_DBG_ERROR(
|
||
|
mc_kapi,
|
||
|
"read notification failed, "
|
||
|
"%i bytes received", (int)num_read);
|
||
|
break;
|
||
|
} else {
|
||
|
/*
|
||
|
* Read of the n-th notification
|
||
|
* failed/timeout. We don't tell the
|
||
|
* caller, as we got valid notifications
|
||
|
* before.
|
||
|
*/
|
||
|
mc_result = MC_DRV_OK;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
count++;
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi,
|
||
|
"count=%d, SessionID=%d, Payload=%d",
|
||
|
count,
|
||
|
notification.session_id,
|
||
|
notification.payload);
|
||
|
|
||
|
if (notification.payload != 0) {
|
||
|
/* Session end point died -> store exit code */
|
||
|
session_set_error_info(nq_session,
|
||
|
notification.payload);
|
||
|
|
||
|
mc_result = MC_DRV_INFO_NOTIFICATION;
|
||
|
break;
|
||
|
}
|
||
|
} /* for(;;) */
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_wait_notification);
|
||
|
|
||
|
enum mc_result mc_malloc_wsm(uint32_t device_id, uint32_t align, uint32_t len,
|
||
|
uint8_t **wsm, uint32_t wsm_flags)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
struct mcore_device_t *device = resolve_device_id(device_id);
|
||
|
if (device == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Device not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
if (wsm == NULL) {
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct wsm *wsm_stack =
|
||
|
mcore_device_allocate_contiguous_wsm(device, len);
|
||
|
if (wsm_stack == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Allocation of WSM failed");
|
||
|
mc_result = MC_DRV_ERR_NO_FREE_MEMORY;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
*wsm = (uint8_t *)wsm_stack->virt_addr;
|
||
|
mc_result = MC_DRV_OK;
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_malloc_wsm);
|
||
|
|
||
|
enum mc_result mc_free_wsm(uint32_t device_id, uint8_t *wsm)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
|
||
|
struct mcore_device_t *device;
|
||
|
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
|
||
|
/* Get the device associated wit the given session */
|
||
|
device = resolve_device_id(device_id);
|
||
|
if (device == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Device not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* find WSM object */
|
||
|
struct wsm *wsm_stack =
|
||
|
mcore_device_find_contiguous_wsm(device, wsm);
|
||
|
if (wsm_stack == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "unknown address");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Free the given virtual address */
|
||
|
if (!mcore_device_free_contiguous_wsm(device, wsm_stack)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"Free of virtual address failed");
|
||
|
mc_result = MC_DRV_ERR_FREE_MEMORY_FAILED;
|
||
|
break;
|
||
|
}
|
||
|
mc_result = MC_DRV_OK;
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_free_wsm);
|
||
|
|
||
|
enum mc_result mc_map(struct mc_session_handle *session_handle, void *buf,
|
||
|
uint32_t buf_len, struct mc_bulk_map *map_info)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
if (session_handle == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "session_handle is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
if (map_info == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "map_info is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
if (buf == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "buf is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Determine device the session belongs to */
|
||
|
struct mcore_device_t *device =
|
||
|
resolve_device_id(session_handle->device_id);
|
||
|
|
||
|
if (device == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Device not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
struct connection *dev_con = device->connection;
|
||
|
|
||
|
/* Get session */
|
||
|
uint32_t session_id = session_handle->session_id;
|
||
|
struct session *session =
|
||
|
mcore_device_resolve_session_id(device,
|
||
|
session_id);
|
||
|
if (session == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Session not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Register mapped bulk buffer to Kernel Module and keep mapped
|
||
|
* bulk buffer in mind
|
||
|
*/
|
||
|
struct bulk_buffer_descriptor *bulk_buf =
|
||
|
session_add_bulk_buf(session, buf, buf_len);
|
||
|
if (bulk_buf == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Error mapping bulk buffer");
|
||
|
mc_result = MC_DRV_ERR_BULK_MAPPING;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Prepare map command */
|
||
|
struct mc_drv_cmd_map_bulk_mem_t mc_drv_cmd_map_bulk_mem = {
|
||
|
{
|
||
|
MC_DRV_CMD_MAP_BULK_BUF
|
||
|
},
|
||
|
{
|
||
|
session->session_id,
|
||
|
bulk_buf->handle,
|
||
|
(uint32_t)bulk_buf->phys_addr_wsm_l2,
|
||
|
(uint32_t)(bulk_buf->virt_addr) & 0xFFF,
|
||
|
bulk_buf->len
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/* Transmit map command to MobiCore device */
|
||
|
connection_write_data(dev_con,
|
||
|
&mc_drv_cmd_map_bulk_mem,
|
||
|
sizeof(mc_drv_cmd_map_bulk_mem));
|
||
|
|
||
|
/* Read command response */
|
||
|
struct mc_drv_response_header_t rsp_header;
|
||
|
int len = connection_read_datablock(dev_con,
|
||
|
&rsp_header,
|
||
|
sizeof(rsp_header));
|
||
|
if (len != sizeof(rsp_header)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_MAP_BULK_BUF readRsp failed %d",
|
||
|
len);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (rsp_header.response_id != MC_DRV_RSP_OK) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_MAP_BULK_BUF failed, respId=%d",
|
||
|
rsp_header.response_id);
|
||
|
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
|
||
|
/*
|
||
|
* Unregister mapped bulk buffer from Kernel Module and
|
||
|
* remove mapped bulk buffer from session maintenance
|
||
|
*/
|
||
|
if (!session_remove_bulk_buf(session, buf)) {
|
||
|
/* Removing of bulk buffer not possible */
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"Unreg of bulk memory failed");
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct mc_drv_rsp_map_bulk_mem_payload_t
|
||
|
rsp_map_bulk_mem_payload;
|
||
|
connection_read_datablock(dev_con,
|
||
|
&rsp_map_bulk_mem_payload,
|
||
|
sizeof(rsp_map_bulk_mem_payload));
|
||
|
|
||
|
/* Set mapping info for Trustlet */
|
||
|
map_info->secure_virt_addr =
|
||
|
(void *)(rsp_map_bulk_mem_payload.secure_virtual_adr);
|
||
|
map_info->secure_virt_len = buf_len;
|
||
|
mc_result = MC_DRV_OK;
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_map);
|
||
|
|
||
|
enum mc_result mc_unmap(struct mc_session_handle *session_handle, void *buf,
|
||
|
struct mc_bulk_map *map_info)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
if (session_handle == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "session_handle is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
if (map_info == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "map_info is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
if (buf == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "buf is null");
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Determine device the session belongs to */
|
||
|
struct mcore_device_t *device =
|
||
|
resolve_device_id(session_handle->device_id);
|
||
|
if (device == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Device not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
struct connection *dev_con = device->connection;
|
||
|
|
||
|
/* Get session */
|
||
|
uint32_t session_id = session_handle->session_id;
|
||
|
struct session *session =
|
||
|
mcore_device_resolve_session_id(device,
|
||
|
session_id);
|
||
|
if (session == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Session not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
uint32_t handle = session_find_bulk_buf(session, buf);
|
||
|
if (handle == 0) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Buffer not found");
|
||
|
mc_result = MC_DRV_ERR_BULK_UNMAPPING;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Prepare unmap command */
|
||
|
struct mc_drv_cmd_unmap_bulk_mem_t cmd_unmap_bulk_mem = {
|
||
|
{
|
||
|
MC_DRV_CMD_UNMAP_BULK_BUF
|
||
|
},
|
||
|
{
|
||
|
session->session_id,
|
||
|
handle,
|
||
|
(uint32_t)(map_info->secure_virt_addr)
|
||
|
}
|
||
|
};
|
||
|
|
||
|
connection_write_data(dev_con,
|
||
|
&cmd_unmap_bulk_mem,
|
||
|
sizeof(cmd_unmap_bulk_mem));
|
||
|
|
||
|
/* Read command response */
|
||
|
struct mc_drv_response_header_t rsp_header;
|
||
|
int len = connection_read_datablock(dev_con,
|
||
|
&rsp_header,
|
||
|
sizeof(rsp_header));
|
||
|
if (len != sizeof(rsp_header)) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_UNMAP_BULK_BUF readRsp failed %d",
|
||
|
len);
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (rsp_header.response_id != MC_DRV_RSP_OK) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"CMD_UNMAP_BULK_BUF failed, respId=%d",
|
||
|
rsp_header.response_id);
|
||
|
|
||
|
mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
struct mc_drv_rsp_unmap_bulk_mem_payload_t
|
||
|
rsp_unmap_bulk_mem_payload;
|
||
|
connection_read_datablock(dev_con,
|
||
|
&rsp_unmap_bulk_mem_payload,
|
||
|
sizeof(rsp_unmap_bulk_mem_payload));
|
||
|
|
||
|
/*
|
||
|
* Unregister mapped bulk buffer from Kernel Module and
|
||
|
* remove mapped bulk buffer from session maintenance
|
||
|
*/
|
||
|
if (!session_remove_bulk_buf(session, buf)) {
|
||
|
/* Removing of bulk buffer not possible */
|
||
|
MCDRV_DBG_ERROR(mc_kapi,
|
||
|
"Unregistering of bulk memory failed");
|
||
|
mc_result = MC_DRV_ERR_BULK_UNMAPPING;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
mc_result = MC_DRV_OK;
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_unmap);
|
||
|
|
||
|
enum mc_result mc_get_session_error_code(struct mc_session_handle *session,
|
||
|
int32_t *last_error)
|
||
|
{
|
||
|
enum mc_result mc_result = MC_DRV_OK;
|
||
|
|
||
|
MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
|
||
|
|
||
|
do {
|
||
|
if (session == NULL || last_error == NULL) {
|
||
|
mc_result = MC_DRV_ERR_INVALID_PARAMETER;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Get device */
|
||
|
struct mcore_device_t *device =
|
||
|
resolve_device_id(session->device_id);
|
||
|
if (device == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Device not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Get session */
|
||
|
uint32_t session_id = session->session_id;
|
||
|
struct session *nqsession =
|
||
|
mcore_device_resolve_session_id(device,
|
||
|
session_id);
|
||
|
if (nqsession == NULL) {
|
||
|
MCDRV_DBG_ERROR(mc_kapi, "Session not found");
|
||
|
mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
*last_error = session_get_last_err(nqsession);
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
return mc_result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_get_session_error_code);
|
||
|
|
||
|
enum mc_result mc_driver_ctrl(enum mc_driver_ctrl param, uint8_t *data,
|
||
|
uint32_t len)
|
||
|
{
|
||
|
MCDRV_DBG_WARN(mc_kapi, "not implemented");
|
||
|
return MC_DRV_ERR_NOT_IMPLEMENTED;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_driver_ctrl);
|
||
|
|
||
|
enum mc_result mc_manage(uint32_t device_id, uint8_t *data, uint32_t len)
|
||
|
{
|
||
|
MCDRV_DBG_WARN(mc_kapi, "not implemented");
|
||
|
return MC_DRV_ERR_NOT_IMPLEMENTED;
|
||
|
}
|
||
|
EXPORT_SYMBOL(mc_manage);
|
||
|
|