M7350/kernel/drivers/gud/MobiCoreDriver/api.c
2024-09-09 08:57:42 +00:00

420 lines
8.8 KiB
C

/*
* Copyright (c) 2013-2015 TRUSTONIC LIMITED
* All Rights Reserved.
*
* 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.
*
* 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.
*/
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/cred.h>
#include <linux/sched.h>
#include <public/mc_linux.h> /* MC_MAP_MAX */
#include "main.h"
#include "debug.h"
#include "mcp.h"
#include "admin.h"
#include "session.h"
#include "client.h"
#include "api.h"
static struct api_ctx {
struct mutex clients_lock; /* Clients list + temp notifs */
struct list_head clients; /* List of user-space clients */
} api_ctx;
/*
* Initialize a new tbase client object
* @return client pointer or NULL if no allocation was possible.
*/
struct tbase_client *api_open_device(bool is_from_kernel)
{
struct tbase_client *client;
/* Allocate and init client object */
client = client_create(is_from_kernel);
if (!client) {
MCDRV_ERROR("Could not create client");
return NULL;
}
/* Add client to list of clients */
mutex_lock(&api_ctx.clients_lock);
list_add_tail(&client->list, &api_ctx.clients);
mutex_unlock(&api_ctx.clients_lock);
MCDRV_DBG("created client %p", client);
return client;
}
/*
* Try and mark client as "closing"
* @return tbase driver error code
*/
int api_freeze_device(struct tbase_client *client)
{
int err = 0;
if (!client_set_closing(client))
err = -ENOTEMPTY;
MCDRV_DBG("client %p, exit with %d\n", client, err);
return err;
}
/*
* Release a client and the session+cbuf objects it contains.
* @param client_t client
* @return tbase driver error code
*/
void api_close_device(struct tbase_client *client)
{
/* Remove client from list of active clients */
mutex_lock(&api_ctx.clients_lock);
list_del(&client->list);
mutex_unlock(&api_ctx.clients_lock);
/* Close all remaining sessions */
client_close_sessions(client);
client_put(client);
MCDRV_DBG("client %p closed\n", client);
}
/*
* Open TA for given client. TA binary is provided by the daemon.
* @param
* @return tbase driver error code
*/
int api_open_session(struct tbase_client *client,
uint32_t *p_session_id,
const struct mc_uuid_t *uuid,
uintptr_t tci,
size_t tci_len,
bool is_gp_uuid,
struct mc_identity *identity)
{
int err = 0;
uint32_t sid = 0;
struct tbase_object *obj;
/* Check parameters */
if (!p_session_id)
return -EINVAL;
if (!uuid)
return -EINVAL;
/* Get secure object */
obj = tbase_object_get(uuid, is_gp_uuid);
if (IS_ERR(obj)) {
/* Try to select secure object inside the SWd if not found */
if ((PTR_ERR(obj) == -ENOENT) && g_ctx.f_ta_auth)
obj = tbase_object_select(uuid);
if (IS_ERR(obj)) {
err = PTR_ERR(obj);
goto end;
}
}
/* Open session */
err = client_add_session(client, obj, tci, tci_len, &sid, is_gp_uuid,
identity);
/* Fill in return parameter */
if (!err)
*p_session_id = sid;
/* Delete secure object */
tbase_object_free(obj);
end:
MCDRV_DBG("session %x, exit with %d\n", sid, err);
return err;
}
/*
* Open TA for given client. TA binary is provided by the client.
* @param
* @return tbase driver error code
*/
int api_open_trustlet(struct tbase_client *client,
uint32_t *p_session_id,
uint32_t spid,
uintptr_t trustlet,
size_t trustlet_len,
uintptr_t tci,
size_t tci_len)
{
struct tbase_object *obj;
struct mc_identity identity = {
.login_type = TEEC_LOGIN_PUBLIC,
};
uint32_t sid = 0;
int err = 0;
/* Check parameters */
if (!p_session_id)
return -EINVAL;
/* Create secure object from user-space trustlet binary */
obj = tbase_object_read(spid, trustlet, trustlet_len);
if (IS_ERR(obj)) {
err = PTR_ERR(obj);
goto end;
}
/* Open session */
err = client_add_session(client, obj, tci, tci_len, &sid, false,
&identity);
/* Fill in return parameter */
if (!err)
*p_session_id = sid;
/* Delete secure object */
tbase_object_free(obj);
end:
MCDRV_DBG("session %x, exit with %d\n", sid, err);
return err;
}
/*
* Close a TA
* @param
* @return tbase driver error code
*/
int api_close_session(struct tbase_client *client, uint32_t session_id)
{
int ret = client_remove_session(client, session_id);
MCDRV_DBG("session %x, exit with %d\n", session_id, ret);
return ret;
}
/*
* Send a notification to TA
* @return tbase driver error code
*/
int api_notify(struct tbase_client *client, uint32_t session_id)
{
int err = 0;
struct tbase_session *session = NULL;
/* Acquire session */
session = client_ref_session(client, session_id);
/* Send command to SWd */
if (!session) {
err = -ENXIO;
} else {
err = session_notify_swd(session);
/* Release session */
client_unref_session(session);
}
MCDRV_DBG("session %x, exit with %d\n", session_id, err);
return err;
}
/*
* Wait for a notification from TA
* @return tbase driver error code
*/
int api_wait_notification(struct tbase_client *client,
uint32_t session_id,
int32_t timeout)
{
int err = 0;
struct tbase_session *session = NULL;
/* Acquire session */
session = client_ref_session(client, session_id);
/* Wait for notification */
if (!session) {
err = -ENXIO;
} else {
err = session_waitnotif(session, timeout);
/* Release session */
client_unref_session(session);
}
MCDRV_DBG("session %x, exit with %d\n", session_id, err);
return err;
}
/*
* Allocate a contiguous buffer (cbuf) for given client
*
* @param client client
* @param len size of the cbuf
* @param **p_addr pointer to the cbuf kva
* @return tbase driver error code
*/
int api_malloc_cbuf(struct tbase_client *client, uint32_t len,
uintptr_t *addr, struct vm_area_struct *vmarea)
{
int err = tbase_cbuf_alloc(client, len, addr, vmarea);
MCDRV_DBG("exit with %d\n", err);
return err;
}
/*
* Free a contiguous buffer from given client
* @param client
* @param addr kernel virtual address of the buffer
*
* @return tbase driver error code
*/
int api_free_cbuf(struct tbase_client *client, uintptr_t addr)
{
int err = tbase_cbuf_free(client, addr);
MCDRV_DBG("@ 0x%lx, exit with %d\n", addr, err);
return err;
}
/* Share a buffer with given TA in SWd */
int api_map_wsms(struct tbase_client *client, uint32_t session_id,
struct mc_ioctl_buffer *bufs)
{
struct tbase_session *session = NULL;
int err = 0;
if (!client)
return -EINVAL;
if (!bufs)
return -EINVAL;
/* Acquire session */
session = client_ref_session(client, session_id);
if (session) {
/* Add buffer to the session */
err = session_wsms_add(session, bufs);
/* Release session */
client_unref_session(session);
} else {
err = -ENXIO;
}
MCDRV_DBG("exit with %d\n", err);
return err;
}
/* Stop sharing a buffer with SWd */
int api_unmap_wsms(struct tbase_client *client, uint32_t session_id,
const struct mc_ioctl_buffer *bufs)
{
struct tbase_session *session = NULL;
int err = 0;
if (!client)
return -EINVAL;
if (!bufs)
return -EINVAL;
/* Acquire session */
session = client_ref_session(client, session_id);
if (!session) {
err = -ENXIO;
} else {
/* Remove buffer from session */
err = session_wsms_remove(session, bufs);
/* Release session */
client_unref_session(session);
}
MCDRV_DBG("exit with %d\n", err);
return err;
}
/*
* Read session exit/termination code
*/
int api_get_session_exitcode(struct tbase_client *client, uint32_t session_id,
int32_t *exit_code)
{
int err = 0;
struct tbase_session *session;
/* Acquire session */
session = client_ref_session(client, session_id);
if (!session) {
err = -ENXIO;
} else {
/* Retrieve error */
*exit_code = session_exitcode(session);
/* Release session */
client_unref_session(session);
err = 0;
}
MCDRV_DBG("session %x, exit with %d\n", session_id, err);
return err;
}
void api_init(void)
{
INIT_LIST_HEAD(&api_ctx.clients);
mutex_init(&api_ctx.clients_lock);
INIT_LIST_HEAD(&g_ctx.closing_sess);
mutex_init(&g_ctx.closing_lock);
}
int api_info(struct kasnprintf_buf *buf)
{
struct tbase_client *client;
struct tbase_session *session;
ssize_t ret = 0;
mutex_lock(&api_ctx.clients_lock);
if (list_empty(&api_ctx.clients))
goto done;
list_for_each_entry(client, &api_ctx.clients, list) {
ret = client_info(client, buf);
if (ret < 0)
break;
}
done:
mutex_unlock(&api_ctx.clients_lock);
if (ret >= 0) {
mutex_lock(&g_ctx.closing_lock);
if (!list_empty(&g_ctx.closing_sess))
ret = kasnprintf(buf, "closing sessions:\n");
list_for_each_entry(session, &g_ctx.closing_sess, list) {
ret = session_info(session, buf);
if (ret < 0)
break;
}
mutex_unlock(&g_ctx.closing_lock);
}
return ret;
}