// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, 2023 Linaro Limited
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/arm_ffa.h>
#include <linux/errno.h>
#include <linux/rpmb.h>
#include <linux/scatterlist.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/tee_core.h>
#include <linux/types.h>
#include "optee_private.h"
#include "optee_ffa.h"
#include "optee_rpc_cmd.h"
/*
* This file implement the FF-A ABI used when communicating with secure world
* OP-TEE OS via FF-A.
* This file is divided into the following sections:
* 1. Maintain a hash table for lookup of a global FF-A memory handle
* 2. Convert between struct tee_param and struct optee_msg_param
* 3. Low level support functions to register shared memory in secure world
* 4. Dynamic shared memory pool based on alloc_pages()
* 5. Do a normal scheduled call into secure world
* 6. Driver initialization.
*/
/*
* 1. Maintain a hash table for lookup of a global FF-A memory handle
*
* FF-A assigns a global memory handle for each piece shared memory.
* This handle is then used when communicating with secure world.
*
* Main functions are optee_shm_add_ffa_handle() and optee_shm_rem_ffa_handle()
*/
struct shm_rhash {
struct tee_shm *shm;
u64 global_id;
struct rhash_head linkage;
};
static void rh_free_fn(void *ptr, void *arg)
{
kfree(ptr);
}
static const struct rhashtable_params shm_rhash_params = {
.head_offset = offsetof(struct shm_rhash, linkage),
.key_len = sizeof(u64),
.key_offset = offsetof(struct shm_rhash, global_id),
.automatic_shrinking = true,
};
static struct tee_shm *optee_shm_from_ffa_handle(struct optee *optee,
u64 global_id)
{
struct tee_shm *shm = NULL;
struct shm_rhash *r;
mutex_lock(&optee->ffa.mutex);
r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id,
shm_rhash_params);
if (r)
shm = r->shm;
mutex_unlock(&optee->ffa.mutex);
return shm;
}
static int optee_shm_add_ffa_handle(struct optee *optee, struct tee_shm *shm,
u64 global_id)
{
struct shm_rhash *r;
int rc;
r = kmalloc(sizeof(*r), GFP_KERNEL);
if (!r)
return -ENOMEM;
r->shm = shm;
r->global_id = global_id;
mutex_lock(&optee->ffa.mutex);
rc = rhashtable_lookup_insert_fast(&optee->ffa.global_ids, &r->linkage,
shm_rhash_params);
mutex_unlock(&optee->ffa.mutex);
if (rc)
kfree(r);
return rc;
}
static int optee_shm_rem_ffa_handle(struct optee *optee, u64 global_id)
{
struct shm_rhash *r;
int rc = -ENOENT;
mutex_lock(&optee->ffa.mutex);
r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id,
shm_rhash_params);
if (r)
rc = rhashtable_remove_fast(&optee->ffa.global_ids,
&r->linkage, shm_rhash_params);
mutex_unlock(&