11b302ac0SJens Wiklander // SPDX-License-Identifier: BSD-2-Clause
21b302ac0SJens Wiklander /*
3bef959c8SJens Wiklander * Copyright (c) 2020-2025, Linaro Limited.
419ad526cSBalint Dobszay * Copyright (c) 2019-2024, Arm Limited. All rights reserved.
51b302ac0SJens Wiklander */
61b302ac0SJens Wiklander
71b302ac0SJens Wiklander #include <assert.h>
81b302ac0SJens Wiklander #include <ffa.h>
9fb19e98eSJens Wiklander #include <initcall.h>
107e399f9bSJens Wiklander #include <io.h>
11bef959c8SJens Wiklander #include <kernel/dt.h>
121b302ac0SJens Wiklander #include <kernel/interrupt.h>
132e02a737SJens Wiklander #include <kernel/notif.h>
141b302ac0SJens Wiklander #include <kernel/panic.h>
15ae6b3380SJelle Sels #include <kernel/secure_partition.h>
161b302ac0SJens Wiklander #include <kernel/spinlock.h>
17ae6b3380SJelle Sels #include <kernel/spmc_sp_handler.h>
181b302ac0SJens Wiklander #include <kernel/tee_misc.h>
191b302ac0SJens Wiklander #include <kernel/thread.h>
207e399f9bSJens Wiklander #include <kernel/thread_private.h>
21c185655eSJelle Sels #include <kernel/thread_spmc.h>
22a65dd3a6SJens Wiklander #include <kernel/virtualization.h>
23bef959c8SJens Wiklander #include <libfdt.h>
241b302ac0SJens Wiklander #include <mm/core_mmu.h>
251b302ac0SJens Wiklander #include <mm/mobj.h>
261b302ac0SJens Wiklander #include <optee_ffa.h>
271b302ac0SJens Wiklander #include <optee_msg.h>
281b302ac0SJens Wiklander #include <optee_rpc_cmd.h>
291d184480SJens Wiklander #include <sm/optee_smc.h>
301b302ac0SJens Wiklander #include <string.h>
311b302ac0SJens Wiklander #include <sys/queue.h>
321b302ac0SJens Wiklander #include <tee/entry_std.h>
333a7bfc34SJelle Sels #include <tee/uuid.h>
34ecf08061SJens Wiklander #include <tee_api_types.h>
35ecf08061SJens Wiklander #include <types_ext.h>
361b302ac0SJens Wiklander #include <util.h>
371b302ac0SJens Wiklander
38fb19e98eSJens Wiklander #if defined(CFG_CORE_SEL1_SPMC)
3900338334SJens Wiklander struct mem_op_state {
4000338334SJens Wiklander bool mem_share;
411b302ac0SJens Wiklander struct mobj_ffa *mf;
421b302ac0SJens Wiklander unsigned int page_count;
431b302ac0SJens Wiklander unsigned int region_count;
441b302ac0SJens Wiklander unsigned int current_page_idx;
451b302ac0SJens Wiklander };
461b302ac0SJens Wiklander
471b302ac0SJens Wiklander struct mem_frag_state {
4800338334SJens Wiklander struct mem_op_state op;
491b302ac0SJens Wiklander tee_mm_entry_t *mm;
501b302ac0SJens Wiklander unsigned int frag_offset;
511b302ac0SJens Wiklander SLIST_ENTRY(mem_frag_state) link;
521b302ac0SJens Wiklander };
53fb19e98eSJens Wiklander #endif
54fb19e98eSJens Wiklander
55f6dcf234SJens Wiklander struct notif_vm_bitmap {
56f6dcf234SJens Wiklander bool initialized;
57f6dcf234SJens Wiklander int do_bottom_half_value;
58f6dcf234SJens Wiklander uint64_t pending;
59f6dcf234SJens Wiklander uint64_t bound;
60f6dcf234SJens Wiklander };
61f6dcf234SJens Wiklander
62ecf08061SJens Wiklander STAILQ_HEAD(spmc_lsp_desc_head, spmc_lsp_desc);
63ecf08061SJens Wiklander
64ecf08061SJens Wiklander static struct spmc_lsp_desc_head lsp_head __nex_data =
65ecf08061SJens Wiklander STAILQ_HEAD_INITIALIZER(lsp_head);
66ecf08061SJens Wiklander
67f6dcf234SJens Wiklander static unsigned int spmc_notif_lock __nex_data = SPINLOCK_UNLOCK;
68f6dcf234SJens Wiklander static bool spmc_notif_is_ready __nex_bss;
69f6dcf234SJens Wiklander static int notif_intid __nex_data __maybe_unused = -1;
70f6dcf234SJens Wiklander
71f6dcf234SJens Wiklander /* Id used to look up the guest specific struct notif_vm_bitmap */
72f6dcf234SJens Wiklander static unsigned int notif_vm_bitmap_id __nex_bss;
73f6dcf234SJens Wiklander /* Notification state when ns-virtualization isn't enabled */
74f6dcf234SJens Wiklander static struct notif_vm_bitmap default_notif_vm_bitmap;
752e02a737SJens Wiklander
76fb19e98eSJens Wiklander /* Initialized in spmc_init() below */
77ecf08061SJens Wiklander static struct spmc_lsp_desc optee_core_lsp;
78a1c53023SJens Wiklander #ifdef CFG_CORE_SEL1_SPMC
79a1c53023SJens Wiklander /*
80ecf08061SJens Wiklander * Representation of the internal SPMC when OP-TEE is the S-EL1 SPMC.
81ecf08061SJens Wiklander * Initialized in spmc_init() below.
82a1c53023SJens Wiklander */
83ecf08061SJens Wiklander static struct spmc_lsp_desc optee_spmc_lsp;
84ecf08061SJens Wiklander /* FF-A ID of the SPMD. This is only valid when OP-TEE is the S-EL1 SPMC. */
85ecf08061SJens Wiklander static uint16_t spmd_id __nex_bss;
861b302ac0SJens Wiklander
871b302ac0SJens Wiklander /*
88cf133f37SJelle Sels * If struct ffa_rxtx::size is 0 RX/TX buffers are not mapped or initialized.
891b302ac0SJens Wiklander *
90cf133f37SJelle Sels * struct ffa_rxtx::spin_lock protects the variables below from concurrent
91cf133f37SJelle Sels * access this includes the use of content of struct ffa_rxtx::rx and
92cf133f37SJelle Sels * @frag_state_head.
931b302ac0SJens Wiklander *
94cf133f37SJelle Sels * struct ffa_rxtx::tx_buf_is_mine is true when we may write to struct
95cf133f37SJelle Sels * ffa_rxtx::tx and false when it is owned by normal world.
961b302ac0SJens Wiklander *
971b302ac0SJens Wiklander * Note that we can't prevent normal world from updating the content of
981b302ac0SJens Wiklander * these buffers so we must always be careful when reading. while we hold
991b302ac0SJens Wiklander * the lock.
1001b302ac0SJens Wiklander */
101cf133f37SJelle Sels
102f49f23f7SJens Wiklander static struct ffa_rxtx my_rxtx __nex_bss;
103cf133f37SJelle Sels
is_nw_buf(struct ffa_rxtx * rxtx)104cf133f37SJelle Sels static bool is_nw_buf(struct ffa_rxtx *rxtx)
105cf133f37SJelle Sels {
106f49f23f7SJens Wiklander return rxtx == &my_rxtx;
107cf133f37SJelle Sels }
1081b302ac0SJens Wiklander
1091b302ac0SJens Wiklander static SLIST_HEAD(mem_frag_state_head, mem_frag_state) frag_state_head =
1101b302ac0SJens Wiklander SLIST_HEAD_INITIALIZER(&frag_state_head);
1112e02a737SJens Wiklander
112e26b8354SJens Wiklander #else
113ecf08061SJens Wiklander /* FF-A ID of the external SPMC */
114ecf08061SJens Wiklander static uint16_t spmc_id __nex_bss;
1152b17b9bfSJens Wiklander static uint8_t __rx_buf[SMALL_PAGE_SIZE] __aligned(SMALL_PAGE_SIZE) __nex_bss;
1162b17b9bfSJens Wiklander static uint8_t __tx_buf[SMALL_PAGE_SIZE] __aligned(SMALL_PAGE_SIZE) __nex_bss;
1172b17b9bfSJens Wiklander static struct ffa_rxtx my_rxtx __nex_data = {
118ffa93873SJens Wiklander .rx = __rx_buf,
119ffa93873SJens Wiklander .tx = __tx_buf,
120ffa93873SJens Wiklander .size = sizeof(__rx_buf),
121ffa93873SJens Wiklander };
122fb19e98eSJens Wiklander #endif
1231b302ac0SJens Wiklander
spmc_is_reserved_id(uint16_t id)124fc6415c4SJens Wiklander bool spmc_is_reserved_id(uint16_t id)
125fc6415c4SJens Wiklander {
126fc6415c4SJens Wiklander #ifdef CFG_CORE_SEL1_SPMC
127ecf08061SJens Wiklander return id == spmd_id;
128ecf08061SJens Wiklander #else
129fc6415c4SJens Wiklander return id == spmc_id;
130ecf08061SJens Wiklander #endif
131ecf08061SJens Wiklander }
132ecf08061SJens Wiklander
spmc_find_lsp_by_sp_id(uint16_t sp_id)133ecf08061SJens Wiklander struct spmc_lsp_desc *spmc_find_lsp_by_sp_id(uint16_t sp_id)
134ecf08061SJens Wiklander {
135ecf08061SJens Wiklander struct spmc_lsp_desc *desc = NULL;
136ecf08061SJens Wiklander
137ecf08061SJens Wiklander STAILQ_FOREACH(desc, &lsp_head, link)
138ecf08061SJens Wiklander if (desc->sp_id == sp_id)
139ecf08061SJens Wiklander return desc;
140ecf08061SJens Wiklander
141ecf08061SJens Wiklander return NULL;
142fc6415c4SJens Wiklander }
143fc6415c4SJens Wiklander
swap_src_dst(uint32_t src_dst)1441b302ac0SJens Wiklander static uint32_t swap_src_dst(uint32_t src_dst)
1451b302ac0SJens Wiklander {
1461b302ac0SJens Wiklander return (src_dst >> 16) | (src_dst << 16);
1471b302ac0SJens Wiklander }
1481b302ac0SJens Wiklander
get_sender_id(uint32_t src_dst)149a65dd3a6SJens Wiklander static uint16_t get_sender_id(uint32_t src_dst)
150a65dd3a6SJens Wiklander {
151a65dd3a6SJens Wiklander return src_dst >> 16;
152a65dd3a6SJens Wiklander }
153a65dd3a6SJens Wiklander
spmc_set_args(struct thread_smc_1_2_regs * args,uint32_t fid,uint32_t src_dst,uint32_t w2,uint32_t w3,uint32_t w4,uint32_t w5)154d17db2afSJens Wiklander void spmc_set_args(struct thread_smc_1_2_regs *args, uint32_t fid,
155d17db2afSJens Wiklander uint32_t src_dst, uint32_t w2, uint32_t w3, uint32_t w4,
156d17db2afSJens Wiklander uint32_t w5)
1571b302ac0SJens Wiklander {
158d17db2afSJens Wiklander *args = (struct thread_smc_1_2_regs){
159d17db2afSJens Wiklander .a0 = fid,
1601b302ac0SJens Wiklander .a1 = src_dst,
1611b302ac0SJens Wiklander .a2 = w2,
1621b302ac0SJens Wiklander .a3 = w3,
1631b302ac0SJens Wiklander .a4 = w4,
164d17db2afSJens Wiklander .a5 = w5,
165d17db2afSJens Wiklander };
1661b302ac0SJens Wiklander }
1671b302ac0SJens Wiklander
set_simple_ret_val(struct thread_smc_1_2_regs * args,int ffa_ret)168d17db2afSJens Wiklander static void set_simple_ret_val(struct thread_smc_1_2_regs *args, int ffa_ret)
16905c6a763SJens Wiklander {
17005c6a763SJens Wiklander if (ffa_ret)
17105c6a763SJens Wiklander spmc_set_args(args, FFA_ERROR, 0, ffa_ret, 0, 0, 0);
17205c6a763SJens Wiklander else
17305c6a763SJens Wiklander spmc_set_args(args, FFA_SUCCESS_32, 0, 0, 0, 0, 0);
17405c6a763SJens Wiklander }
17505c6a763SJens Wiklander
spmc_exchange_version(uint32_t vers,struct ffa_rxtx * rxtx)176923f61cdSJens Wiklander uint32_t spmc_exchange_version(uint32_t vers, struct ffa_rxtx *rxtx)
1771b302ac0SJens Wiklander {
178bef959c8SJens Wiklander uint32_t major_vers = FFA_GET_MAJOR_VERSION(vers);
179bef959c8SJens Wiklander uint32_t minor_vers = FFA_GET_MINOR_VERSION(vers);
180bef959c8SJens Wiklander uint32_t my_vers = FFA_VERSION_1_2;
181bef959c8SJens Wiklander uint32_t my_major_vers = 0;
182bef959c8SJens Wiklander uint32_t my_minor_vers = 0;
183bef959c8SJens Wiklander
184bef959c8SJens Wiklander my_major_vers = FFA_GET_MAJOR_VERSION(my_vers);
185bef959c8SJens Wiklander my_minor_vers = FFA_GET_MINOR_VERSION(my_vers);
186bef959c8SJens Wiklander
1871b302ac0SJens Wiklander /*
188a1c53023SJens Wiklander * No locking, if the caller does concurrent calls to this it's
189a1c53023SJens Wiklander * only making a mess for itself. We must be able to renegotiate
190a1c53023SJens Wiklander * the FF-A version in order to support differing versions between
191a1c53023SJens Wiklander * the loader and the driver.
192bef959c8SJens Wiklander *
193bef959c8SJens Wiklander * Callers should use the version requested if we return a matching
194bef959c8SJens Wiklander * major version and a matching or larger minor version. The caller
195bef959c8SJens Wiklander * should downgrade to our minor version if our minor version is
196bef959c8SJens Wiklander * smaller. Regardless, always return our version as recommended by
197bef959c8SJens Wiklander * the specification.
1981b302ac0SJens Wiklander */
199bef959c8SJens Wiklander if (major_vers == my_major_vers) {
200bef959c8SJens Wiklander if (minor_vers > my_minor_vers)
201bef959c8SJens Wiklander rxtx->ffa_vers = my_vers;
202a1c53023SJens Wiklander else
203bef959c8SJens Wiklander rxtx->ffa_vers = vers;
204bef959c8SJens Wiklander }
205a1c53023SJens Wiklander
206bef959c8SJens Wiklander return my_vers;
207a1c53023SJens Wiklander }
208a1c53023SJens Wiklander
is_ffa_success(uint32_t fid)20955cd94d1SJens Wiklander static bool is_ffa_success(uint32_t fid)
21055cd94d1SJens Wiklander {
21155cd94d1SJens Wiklander #ifdef ARM64
21255cd94d1SJens Wiklander if (fid == FFA_SUCCESS_64)
21355cd94d1SJens Wiklander return true;
21455cd94d1SJens Wiklander #endif
21555cd94d1SJens Wiklander return fid == FFA_SUCCESS_32;
21655cd94d1SJens Wiklander }
21755cd94d1SJens Wiklander
get_ffa_ret_code(const struct thread_smc_args * args)21855cd94d1SJens Wiklander static int32_t get_ffa_ret_code(const struct thread_smc_args *args)
21955cd94d1SJens Wiklander {
22055cd94d1SJens Wiklander if (is_ffa_success(args->a0))
22155cd94d1SJens Wiklander return FFA_OK;
22255cd94d1SJens Wiklander if (args->a0 == FFA_ERROR && args->a2)
22355cd94d1SJens Wiklander return args->a2;
22455cd94d1SJens Wiklander return FFA_NOT_SUPPORTED;
22555cd94d1SJens Wiklander }
22655cd94d1SJens Wiklander
ffa_simple_call(uint32_t fid,unsigned long a1,unsigned long a2,unsigned long a3,unsigned long a4)22755cd94d1SJens Wiklander static int ffa_simple_call(uint32_t fid, unsigned long a1, unsigned long a2,
22855cd94d1SJens Wiklander unsigned long a3, unsigned long a4)
22955cd94d1SJens Wiklander {
23055cd94d1SJens Wiklander struct thread_smc_args args = {
23155cd94d1SJens Wiklander .a0 = fid,
23255cd94d1SJens Wiklander .a1 = a1,
23355cd94d1SJens Wiklander .a2 = a2,
23455cd94d1SJens Wiklander .a3 = a3,
23555cd94d1SJens Wiklander .a4 = a4,
23655cd94d1SJens Wiklander };
23755cd94d1SJens Wiklander
23855cd94d1SJens Wiklander thread_smccc(&args);
23955cd94d1SJens Wiklander
24055cd94d1SJens Wiklander return get_ffa_ret_code(&args);
24155cd94d1SJens Wiklander }
24255cd94d1SJens Wiklander
ffa_features(uint32_t id)24355cd94d1SJens Wiklander static int __maybe_unused ffa_features(uint32_t id)
24455cd94d1SJens Wiklander {
24555cd94d1SJens Wiklander return ffa_simple_call(FFA_FEATURES, id, 0, 0, 0);
24655cd94d1SJens Wiklander }
24755cd94d1SJens Wiklander
ffa_set_notification(uint16_t dst,uint16_t src,uint32_t flags,uint64_t bitmap)24855cd94d1SJens Wiklander static int __maybe_unused ffa_set_notification(uint16_t dst, uint16_t src,
24955cd94d1SJens Wiklander uint32_t flags, uint64_t bitmap)
25055cd94d1SJens Wiklander {
25155cd94d1SJens Wiklander return ffa_simple_call(FFA_NOTIFICATION_SET,
25255cd94d1SJens Wiklander SHIFT_U32(src, 16) | dst, flags,
25355cd94d1SJens Wiklander low32_from_64(bitmap), high32_from_64(bitmap));
25455cd94d1SJens Wiklander }
25555cd94d1SJens Wiklander
256a1c53023SJens Wiklander #if defined(CFG_CORE_SEL1_SPMC)
handle_features(struct thread_smc_1_2_regs * args)257d17db2afSJens Wiklander static void handle_features(struct thread_smc_1_2_regs *args)
2581b302ac0SJens Wiklander {
2592e02a737SJens Wiklander uint32_t ret_fid = FFA_ERROR;
2602e02a737SJens Wiklander uint32_t ret_w2 = FFA_NOT_SUPPORTED;
2611b302ac0SJens Wiklander
2621b302ac0SJens Wiklander switch (args->a1) {
2632e02a737SJens Wiklander case FFA_FEATURE_SCHEDULE_RECV_INTR:
2642e02a737SJens Wiklander if (spmc_notif_is_ready) {
2652e02a737SJens Wiklander ret_fid = FFA_SUCCESS_32;
2662e02a737SJens Wiklander ret_w2 = notif_intid;
2672e02a737SJens Wiklander }
2682e02a737SJens Wiklander break;
2692e02a737SJens Wiklander
2701b302ac0SJens Wiklander #ifdef ARM64
2711b302ac0SJens Wiklander case FFA_RXTX_MAP_64:
2721b302ac0SJens Wiklander #endif
2731b302ac0SJens Wiklander case FFA_RXTX_MAP_32:
2741b302ac0SJens Wiklander ret_fid = FFA_SUCCESS_32;
2751b302ac0SJens Wiklander ret_w2 = 0; /* 4kB Minimum buffer size and alignment boundary */
2761b302ac0SJens Wiklander break;
2771b302ac0SJens Wiklander #ifdef ARM64
2781b302ac0SJens Wiklander case FFA_MEM_SHARE_64:
2791b302ac0SJens Wiklander #endif
2801b302ac0SJens Wiklander case FFA_MEM_SHARE_32:
2811b302ac0SJens Wiklander ret_fid = FFA_SUCCESS_32;
2821b302ac0SJens Wiklander /*
2831b302ac0SJens Wiklander * Partition manager supports transmission of a memory
2841b302ac0SJens Wiklander * transaction descriptor in a buffer dynamically allocated
2851b302ac0SJens Wiklander * by the endpoint.
2861b302ac0SJens Wiklander */
2871b302ac0SJens Wiklander ret_w2 = BIT(0);
2881b302ac0SJens Wiklander break;
2891b302ac0SJens Wiklander
2901b302ac0SJens Wiklander case FFA_ERROR:
2911b302ac0SJens Wiklander case FFA_VERSION:
2921b302ac0SJens Wiklander case FFA_SUCCESS_32:
2931b302ac0SJens Wiklander #ifdef ARM64
2941b302ac0SJens Wiklander case FFA_SUCCESS_64:
2951b302ac0SJens Wiklander #endif
296a1c53023SJens Wiklander case FFA_FEATURES:
297412d46f6SJens Wiklander case FFA_SPM_ID_GET:
2981b302ac0SJens Wiklander case FFA_MEM_FRAG_TX:
2991b302ac0SJens Wiklander case FFA_MEM_RECLAIM:
3001d184480SJens Wiklander case FFA_MSG_SEND_DIRECT_REQ_64:
3011b302ac0SJens Wiklander case FFA_MSG_SEND_DIRECT_REQ_32:
3021b302ac0SJens Wiklander case FFA_INTERRUPT:
3031b302ac0SJens Wiklander case FFA_PARTITION_INFO_GET:
304a1c53023SJens Wiklander case FFA_RXTX_UNMAP:
3051b302ac0SJens Wiklander case FFA_RX_RELEASE:
306a1c53023SJens Wiklander case FFA_FEATURE_MANAGED_EXIT_INTR:
3072e02a737SJens Wiklander case FFA_NOTIFICATION_BITMAP_CREATE:
3082e02a737SJens Wiklander case FFA_NOTIFICATION_BITMAP_DESTROY:
3092e02a737SJens Wiklander case FFA_NOTIFICATION_BIND:
3102e02a737SJens Wiklander case FFA_NOTIFICATION_UNBIND:
3112e02a737SJens Wiklander case FFA_NOTIFICATION_SET:
3122e02a737SJens Wiklander case FFA_NOTIFICATION_GET:
3132e02a737SJens Wiklander case FFA_NOTIFICATION_INFO_GET_32:
3142e02a737SJens Wiklander #ifdef ARM64
3152e02a737SJens Wiklander case FFA_NOTIFICATION_INFO_GET_64:
3162e02a737SJens Wiklander #endif
3171b302ac0SJens Wiklander ret_fid = FFA_SUCCESS_32;
3182e02a737SJens Wiklander ret_w2 = FFA_PARAM_MBZ;
3191b302ac0SJens Wiklander break;
3201b302ac0SJens Wiklander default:
3211b302ac0SJens Wiklander break;
3221b302ac0SJens Wiklander }
3231b302ac0SJens Wiklander
324fe513722SJelle Sels spmc_set_args(args, ret_fid, FFA_PARAM_MBZ, ret_w2, FFA_PARAM_MBZ,
325fe513722SJelle Sels FFA_PARAM_MBZ, FFA_PARAM_MBZ);
3261b302ac0SJens Wiklander }
3271b302ac0SJens Wiklander
map_buf(paddr_t pa,unsigned int sz,void ** va_ret)3281b302ac0SJens Wiklander static int map_buf(paddr_t pa, unsigned int sz, void **va_ret)
3291b302ac0SJens Wiklander {
3301b302ac0SJens Wiklander tee_mm_entry_t *mm = NULL;
3311b302ac0SJens Wiklander
3321b302ac0SJens Wiklander if (!core_pbuf_is(CORE_MEM_NON_SEC, pa, sz))
3331b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
3341b302ac0SJens Wiklander
335fdf696b7SJens Wiklander mm = tee_mm_alloc(&core_virt_shm_pool, sz);
3361b302ac0SJens Wiklander if (!mm)
3371b302ac0SJens Wiklander return FFA_NO_MEMORY;
3381b302ac0SJens Wiklander
3391b302ac0SJens Wiklander if (core_mmu_map_contiguous_pages(tee_mm_get_smem(mm), pa,
3401b302ac0SJens Wiklander sz / SMALL_PAGE_SIZE,
3411b302ac0SJens Wiklander MEM_AREA_NSEC_SHM)) {
3421b302ac0SJens Wiklander tee_mm_free(mm);
3431b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
3441b302ac0SJens Wiklander }
3451b302ac0SJens Wiklander
3461b302ac0SJens Wiklander *va_ret = (void *)tee_mm_get_smem(mm);
3471b302ac0SJens Wiklander return 0;
3481b302ac0SJens Wiklander }
3491b302ac0SJens Wiklander
spmc_handle_spm_id_get(struct thread_smc_1_2_regs * args)350d17db2afSJens Wiklander void spmc_handle_spm_id_get(struct thread_smc_1_2_regs *args)
351412d46f6SJens Wiklander {
352ecf08061SJens Wiklander spmc_set_args(args, FFA_SUCCESS_32, FFA_PARAM_MBZ, optee_spmc_lsp.sp_id,
353412d46f6SJens Wiklander FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ);
354412d46f6SJens Wiklander }
355412d46f6SJens Wiklander
unmap_buf(void * va,size_t sz)3561b302ac0SJens Wiklander static void unmap_buf(void *va, size_t sz)
3571b302ac0SJens Wiklander {
358fdf696b7SJens Wiklander tee_mm_entry_t *mm = tee_mm_find(&core_virt_shm_pool, (vaddr_t)va);
3591b302ac0SJens Wiklander
3601b302ac0SJens Wiklander assert(mm);
3611b302ac0SJens Wiklander core_mmu_unmap_pages(tee_mm_get_smem(mm), sz / SMALL_PAGE_SIZE);
3621b302ac0SJens Wiklander tee_mm_free(mm);
3631b302ac0SJens Wiklander }
3641b302ac0SJens Wiklander
spmc_handle_rxtx_map(struct thread_smc_1_2_regs * args,struct ffa_rxtx * rxtx)365d17db2afSJens Wiklander void spmc_handle_rxtx_map(struct thread_smc_1_2_regs *args,
366d17db2afSJens Wiklander struct ffa_rxtx *rxtx)
3671b302ac0SJens Wiklander {
3681b302ac0SJens Wiklander int rc = 0;
3691b302ac0SJens Wiklander unsigned int sz = 0;
3701b302ac0SJens Wiklander paddr_t rx_pa = 0;
3711b302ac0SJens Wiklander paddr_t tx_pa = 0;
3721b302ac0SJens Wiklander void *rx = NULL;
3731b302ac0SJens Wiklander void *tx = NULL;
3741b302ac0SJens Wiklander
375cf133f37SJelle Sels cpu_spin_lock(&rxtx->spinlock);
3761b302ac0SJens Wiklander
3771b302ac0SJens Wiklander if (args->a3 & GENMASK_64(63, 6)) {
3781b302ac0SJens Wiklander rc = FFA_INVALID_PARAMETERS;
3791b302ac0SJens Wiklander goto out;
3801b302ac0SJens Wiklander }
3811b302ac0SJens Wiklander
3821b302ac0SJens Wiklander sz = args->a3 * SMALL_PAGE_SIZE;
3831b302ac0SJens Wiklander if (!sz) {
3841b302ac0SJens Wiklander rc = FFA_INVALID_PARAMETERS;
3851b302ac0SJens Wiklander goto out;
3861b302ac0SJens Wiklander }
3871b302ac0SJens Wiklander /* TX/RX are swapped compared to the caller */
3881b302ac0SJens Wiklander tx_pa = args->a2;
3891b302ac0SJens Wiklander rx_pa = args->a1;
3901b302ac0SJens Wiklander
391cf133f37SJelle Sels if (rxtx->size) {
3921b302ac0SJens Wiklander rc = FFA_DENIED;
3931b302ac0SJens Wiklander goto out;
3941b302ac0SJens Wiklander }
3951b302ac0SJens Wiklander
396cf133f37SJelle Sels /*
397cf133f37SJelle Sels * If the buffer comes from a SP the address is virtual and already
398cf133f37SJelle Sels * mapped.
399cf133f37SJelle Sels */
400cf133f37SJelle Sels if (is_nw_buf(rxtx)) {
401a65dd3a6SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
402a65dd3a6SJens Wiklander enum teecore_memtypes mt = MEM_AREA_NEX_NSEC_SHM;
403a65dd3a6SJens Wiklander bool tx_alloced = false;
404a65dd3a6SJens Wiklander
405a65dd3a6SJens Wiklander /*
406a65dd3a6SJens Wiklander * With virtualization we establish this mapping in
407a65dd3a6SJens Wiklander * the nexus mapping which then is replicated to
408a65dd3a6SJens Wiklander * each partition.
409a65dd3a6SJens Wiklander *
410a65dd3a6SJens Wiklander * This means that this mapping must be done before
411a65dd3a6SJens Wiklander * any partition is created and then must not be
412a65dd3a6SJens Wiklander * changed.
413a65dd3a6SJens Wiklander */
414a65dd3a6SJens Wiklander
415a65dd3a6SJens Wiklander /*
416a65dd3a6SJens Wiklander * core_mmu_add_mapping() may reuse previous
417a65dd3a6SJens Wiklander * mappings. First check if there's any mappings to
418a65dd3a6SJens Wiklander * reuse so we know how to clean up in case of
419a65dd3a6SJens Wiklander * failure.
420a65dd3a6SJens Wiklander */
421a65dd3a6SJens Wiklander tx = phys_to_virt(tx_pa, mt, sz);
422a65dd3a6SJens Wiklander rx = phys_to_virt(rx_pa, mt, sz);
423a65dd3a6SJens Wiklander if (!tx) {
424a65dd3a6SJens Wiklander tx = core_mmu_add_mapping(mt, tx_pa, sz);
425a65dd3a6SJens Wiklander if (!tx) {
426a65dd3a6SJens Wiklander rc = FFA_NO_MEMORY;
427a65dd3a6SJens Wiklander goto out;
428a65dd3a6SJens Wiklander }
429a65dd3a6SJens Wiklander tx_alloced = true;
430a65dd3a6SJens Wiklander }
431a65dd3a6SJens Wiklander if (!rx)
432a65dd3a6SJens Wiklander rx = core_mmu_add_mapping(mt, rx_pa, sz);
433a65dd3a6SJens Wiklander
434a65dd3a6SJens Wiklander if (!rx) {
435a65dd3a6SJens Wiklander if (tx_alloced && tx)
436a65dd3a6SJens Wiklander core_mmu_remove_mapping(mt, tx, sz);
437a65dd3a6SJens Wiklander rc = FFA_NO_MEMORY;
438a65dd3a6SJens Wiklander goto out;
439a65dd3a6SJens Wiklander }
440a65dd3a6SJens Wiklander } else {
4411b302ac0SJens Wiklander rc = map_buf(tx_pa, sz, &tx);
4421b302ac0SJens Wiklander if (rc)
4431b302ac0SJens Wiklander goto out;
4441b302ac0SJens Wiklander rc = map_buf(rx_pa, sz, &rx);
4451b302ac0SJens Wiklander if (rc) {
4461b302ac0SJens Wiklander unmap_buf(tx, sz);
4471b302ac0SJens Wiklander goto out;
4481b302ac0SJens Wiklander }
449a65dd3a6SJens Wiklander }
450cf133f37SJelle Sels rxtx->tx = tx;
451cf133f37SJelle Sels rxtx->rx = rx;
452cf133f37SJelle Sels } else {
453cf133f37SJelle Sels if ((tx_pa & SMALL_PAGE_MASK) || (rx_pa & SMALL_PAGE_MASK)) {
454cf133f37SJelle Sels rc = FFA_INVALID_PARAMETERS;
455cf133f37SJelle Sels goto out;
456cf133f37SJelle Sels }
4571b302ac0SJens Wiklander
458cf133f37SJelle Sels if (!virt_to_phys((void *)tx_pa) ||
459cf133f37SJelle Sels !virt_to_phys((void *)rx_pa)) {
460cf133f37SJelle Sels rc = FFA_INVALID_PARAMETERS;
461cf133f37SJelle Sels goto out;
462cf133f37SJelle Sels }
463cf133f37SJelle Sels
464cf133f37SJelle Sels rxtx->tx = (void *)tx_pa;
465cf133f37SJelle Sels rxtx->rx = (void *)rx_pa;
466cf133f37SJelle Sels }
467cf133f37SJelle Sels
468cf133f37SJelle Sels rxtx->size = sz;
469cf133f37SJelle Sels rxtx->tx_is_mine = true;
4701b302ac0SJens Wiklander DMSG("Mapped tx %#"PRIxPA" size %#x @ %p", tx_pa, sz, tx);
4711b302ac0SJens Wiklander DMSG("Mapped rx %#"PRIxPA" size %#x @ %p", rx_pa, sz, rx);
4721b302ac0SJens Wiklander out:
473cf133f37SJelle Sels cpu_spin_unlock(&rxtx->spinlock);
47405c6a763SJens Wiklander set_simple_ret_val(args, rc);
4751b302ac0SJens Wiklander }
4761b302ac0SJens Wiklander
spmc_handle_rxtx_unmap(struct thread_smc_1_2_regs * args,struct ffa_rxtx * rxtx)477d17db2afSJens Wiklander void spmc_handle_rxtx_unmap(struct thread_smc_1_2_regs *args,
478d17db2afSJens Wiklander struct ffa_rxtx *rxtx)
4791b302ac0SJens Wiklander {
4801b302ac0SJens Wiklander int rc = FFA_INVALID_PARAMETERS;
4811b302ac0SJens Wiklander
482cf133f37SJelle Sels cpu_spin_lock(&rxtx->spinlock);
4831b302ac0SJens Wiklander
484cf133f37SJelle Sels if (!rxtx->size)
4851b302ac0SJens Wiklander goto out;
486cf133f37SJelle Sels
4879502204bSJens Wiklander /*
4889502204bSJens Wiklander * We don't unmap the SP memory as the SP might still use it.
4899502204bSJens Wiklander * We avoid to make changes to nexus mappings at this stage since
4909502204bSJens Wiklander * there currently isn't a way to replicate those changes to all
4919502204bSJens Wiklander * partitions.
4929502204bSJens Wiklander */
4939502204bSJens Wiklander if (is_nw_buf(rxtx) && !IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
494cf133f37SJelle Sels unmap_buf(rxtx->rx, rxtx->size);
495cf133f37SJelle Sels unmap_buf(rxtx->tx, rxtx->size);
496cf133f37SJelle Sels }
497cf133f37SJelle Sels rxtx->size = 0;
498cf133f37SJelle Sels rxtx->rx = NULL;
499cf133f37SJelle Sels rxtx->tx = NULL;
5001b302ac0SJens Wiklander rc = 0;
5011b302ac0SJens Wiklander out:
502cf133f37SJelle Sels cpu_spin_unlock(&rxtx->spinlock);
50305c6a763SJens Wiklander set_simple_ret_val(args, rc);
5041b302ac0SJens Wiklander }
5051b302ac0SJens Wiklander
spmc_handle_rx_release(struct thread_smc_1_2_regs * args,struct ffa_rxtx * rxtx)506d17db2afSJens Wiklander void spmc_handle_rx_release(struct thread_smc_1_2_regs *args,
507d17db2afSJens Wiklander struct ffa_rxtx *rxtx)
5081b302ac0SJens Wiklander {
5091b302ac0SJens Wiklander int rc = 0;
5101b302ac0SJens Wiklander
511cf133f37SJelle Sels cpu_spin_lock(&rxtx->spinlock);
5121b302ac0SJens Wiklander /* The senders RX is our TX */
513cf133f37SJelle Sels if (!rxtx->size || rxtx->tx_is_mine) {
5141b302ac0SJens Wiklander rc = FFA_DENIED;
5151b302ac0SJens Wiklander } else {
5161b302ac0SJens Wiklander rc = 0;
517cf133f37SJelle Sels rxtx->tx_is_mine = true;
5181b302ac0SJens Wiklander }
519cf133f37SJelle Sels cpu_spin_unlock(&rxtx->spinlock);
5201b302ac0SJens Wiklander
52105c6a763SJens Wiklander set_simple_ret_val(args, rc);
5221b302ac0SJens Wiklander }
5231b302ac0SJens Wiklander
is_nil_uuid(uint32_t w0,uint32_t w1,uint32_t w2,uint32_t w3)5241b302ac0SJens Wiklander static bool is_nil_uuid(uint32_t w0, uint32_t w1, uint32_t w2, uint32_t w3)
5251b302ac0SJens Wiklander {
5261b302ac0SJens Wiklander return !w0 && !w1 && !w2 && !w3;
5271b302ac0SJens Wiklander }
5281b302ac0SJens Wiklander
spmc_fill_partition_entry(uint32_t ffa_vers,void * buf,size_t blen,size_t idx,uint16_t endpoint_id,uint16_t execution_context,uint32_t part_props,const uint32_t uuid_words[4])529a1c53023SJens Wiklander TEE_Result spmc_fill_partition_entry(uint32_t ffa_vers, void *buf, size_t blen,
530a1c53023SJens Wiklander size_t idx, uint16_t endpoint_id,
531a1c53023SJens Wiklander uint16_t execution_context,
532a1c53023SJens Wiklander uint32_t part_props,
533a1c53023SJens Wiklander const uint32_t uuid_words[4])
5343a7bfc34SJelle Sels {
535a1c53023SJens Wiklander struct ffa_partition_info_x *fpi = NULL;
536a1c53023SJens Wiklander size_t fpi_size = sizeof(*fpi);
537a1c53023SJens Wiklander
538a1c53023SJens Wiklander if (ffa_vers >= FFA_VERSION_1_1)
539a1c53023SJens Wiklander fpi_size += FFA_UUID_SIZE;
540a1c53023SJens Wiklander
541a1c53023SJens Wiklander if ((idx + 1) * fpi_size > blen)
542a1c53023SJens Wiklander return TEE_ERROR_OUT_OF_MEMORY;
543a1c53023SJens Wiklander
544a1c53023SJens Wiklander fpi = (void *)((vaddr_t)buf + idx * fpi_size);
5453a7bfc34SJelle Sels fpi->id = endpoint_id;
5463a7bfc34SJelle Sels /* Number of execution contexts implemented by this partition */
5473a7bfc34SJelle Sels fpi->execution_context = execution_context;
5483a7bfc34SJelle Sels
549a1c53023SJens Wiklander fpi->partition_properties = part_props;
550a1c53023SJens Wiklander
551cc04f76fSBalint Dobszay /* In FF-A 1.0 only bits [2:0] are defined, let's mask others */
552cc04f76fSBalint Dobszay if (ffa_vers < FFA_VERSION_1_1)
553cc04f76fSBalint Dobszay fpi->partition_properties &= FFA_PART_PROP_DIRECT_REQ_RECV |
554cc04f76fSBalint Dobszay FFA_PART_PROP_DIRECT_REQ_SEND |
555cc04f76fSBalint Dobszay FFA_PART_PROP_INDIRECT_MSGS;
556cc04f76fSBalint Dobszay
557a1c53023SJens Wiklander if (ffa_vers >= FFA_VERSION_1_1) {
558a1c53023SJens Wiklander if (uuid_words)
559a1c53023SJens Wiklander memcpy(fpi->uuid, uuid_words, FFA_UUID_SIZE);
560a1c53023SJens Wiklander else
561a1c53023SJens Wiklander memset(fpi->uuid, 0, FFA_UUID_SIZE);
5623a7bfc34SJelle Sels }
5633a7bfc34SJelle Sels
564a1c53023SJens Wiklander return TEE_SUCCESS;
565a1c53023SJens Wiklander }
5661b302ac0SJens Wiklander
lsp_partition_info_get(uint32_t ffa_vers,void * buf,size_t buf_size,size_t * elem_count,const uint32_t uuid_words[4],bool count_only)567ecf08061SJens Wiklander static TEE_Result lsp_partition_info_get(uint32_t ffa_vers, void *buf,
568ecf08061SJens Wiklander size_t buf_size, size_t *elem_count,
569ecf08061SJens Wiklander const uint32_t uuid_words[4],
570ecf08061SJens Wiklander bool count_only)
571a1c53023SJens Wiklander {
572ecf08061SJens Wiklander struct spmc_lsp_desc *desc = NULL;
573ecf08061SJens Wiklander TEE_Result res = TEE_SUCCESS;
574ecf08061SJens Wiklander size_t c = *elem_count;
575ecf08061SJens Wiklander
576ecf08061SJens Wiklander STAILQ_FOREACH(desc, &lsp_head, link) {
577ecf08061SJens Wiklander /*
578ecf08061SJens Wiklander * LSPs (OP-TEE SPMC) without an assigned UUID are not
579ecf08061SJens Wiklander * proper LSPs and shouldn't be reported here.
580ecf08061SJens Wiklander */
581ecf08061SJens Wiklander if (is_nil_uuid(desc->uuid_words[0], desc->uuid_words[1],
582ecf08061SJens Wiklander desc->uuid_words[2], desc->uuid_words[3]))
583ecf08061SJens Wiklander continue;
584ecf08061SJens Wiklander
585ecf08061SJens Wiklander if (uuid_words && memcmp(uuid_words, desc->uuid_words,
586ecf08061SJens Wiklander sizeof(desc->uuid_words)))
587ecf08061SJens Wiklander continue;
588ecf08061SJens Wiklander
589ecf08061SJens Wiklander if (!count_only && !res)
590ecf08061SJens Wiklander res = spmc_fill_partition_entry(ffa_vers, buf, buf_size,
591ecf08061SJens Wiklander c, desc->sp_id,
592a1c53023SJens Wiklander CFG_TEE_CORE_NB_CORE,
593ecf08061SJens Wiklander desc->properties,
594ecf08061SJens Wiklander desc->uuid_words);
595ecf08061SJens Wiklander c++;
5963a7bfc34SJelle Sels }
5973a7bfc34SJelle Sels
598ecf08061SJens Wiklander *elem_count = c;
599ecf08061SJens Wiklander
600ecf08061SJens Wiklander return res;
6013a7bfc34SJelle Sels }
6023a7bfc34SJelle Sels
spmc_handle_partition_info_get(struct thread_smc_1_2_regs * args,struct ffa_rxtx * rxtx)603d17db2afSJens Wiklander void spmc_handle_partition_info_get(struct thread_smc_1_2_regs *args,
6043a7bfc34SJelle Sels struct ffa_rxtx *rxtx)
6053a7bfc34SJelle Sels {
606a1c53023SJens Wiklander TEE_Result res = TEE_SUCCESS;
6073a7bfc34SJelle Sels uint32_t ret_fid = FFA_ERROR;
60804f7f019SJens Wiklander uint32_t fpi_size = 0;
6093a7bfc34SJelle Sels uint32_t rc = 0;
610a1c53023SJens Wiklander bool count_only = args->a5 & FFA_PARTITION_INFO_GET_COUNT_FLAG;
611ecf08061SJens Wiklander uint32_t uuid_words[4] = { args->a1, args->a2, args->a3, args->a4, };
612ecf08061SJens Wiklander uint32_t *uuid = uuid_words;
613ecf08061SJens Wiklander size_t count = 0;
6143a7bfc34SJelle Sels
615a1c53023SJens Wiklander if (!count_only) {
6163a7bfc34SJelle Sels cpu_spin_lock(&rxtx->spinlock);
6173a7bfc34SJelle Sels
6183a7bfc34SJelle Sels if (!rxtx->size || !rxtx->tx_is_mine) {
6193a7bfc34SJelle Sels rc = FFA_BUSY;
6203a7bfc34SJelle Sels goto out;
6213a7bfc34SJelle Sels }
6223a7bfc34SJelle Sels }
6233a7bfc34SJelle Sels
624ecf08061SJens Wiklander if (is_nil_uuid(uuid[0], uuid[1], uuid[2], uuid[3]))
625ecf08061SJens Wiklander uuid = NULL;
6263a7bfc34SJelle Sels
627ecf08061SJens Wiklander if (lsp_partition_info_get(rxtx->ffa_vers, rxtx->tx, rxtx->size,
628ecf08061SJens Wiklander &count, uuid, count_only)) {
629a1c53023SJens Wiklander ret_fid = FFA_ERROR;
630a1c53023SJens Wiklander rc = FFA_INVALID_PARAMETERS;
631a1c53023SJens Wiklander goto out;
632a1c53023SJens Wiklander }
633ecf08061SJens Wiklander if (IS_ENABLED(CFG_SECURE_PARTITION)) {
634a1c53023SJens Wiklander res = sp_partition_info_get(rxtx->ffa_vers, rxtx->tx,
635ecf08061SJens Wiklander rxtx->size, uuid, &count,
636a1c53023SJens Wiklander count_only);
6373a7bfc34SJelle Sels if (res != TEE_SUCCESS) {
6383a7bfc34SJelle Sels ret_fid = FFA_ERROR;
6393a7bfc34SJelle Sels rc = FFA_INVALID_PARAMETERS;
6403a7bfc34SJelle Sels goto out;
6413a7bfc34SJelle Sels }
6421b302ac0SJens Wiklander }
6431b302ac0SJens Wiklander
644ecf08061SJens Wiklander rc = count;
6451b302ac0SJens Wiklander ret_fid = FFA_SUCCESS_32;
6461b302ac0SJens Wiklander out:
64704f7f019SJens Wiklander if (ret_fid == FFA_SUCCESS_32 && !count_only &&
64804f7f019SJens Wiklander rxtx->ffa_vers >= FFA_VERSION_1_1)
64904f7f019SJens Wiklander fpi_size = sizeof(struct ffa_partition_info_x) + FFA_UUID_SIZE;
65004f7f019SJens Wiklander
65104f7f019SJens Wiklander spmc_set_args(args, ret_fid, FFA_PARAM_MBZ, rc, fpi_size,
652fe513722SJelle Sels FFA_PARAM_MBZ, FFA_PARAM_MBZ);
653a1c53023SJens Wiklander if (!count_only) {
654a1c53023SJens Wiklander rxtx->tx_is_mine = false;
6553a7bfc34SJelle Sels cpu_spin_unlock(&rxtx->spinlock);
6561b302ac0SJens Wiklander }
657a1c53023SJens Wiklander }
6584d028847SImre Kis
spmc_handle_run(struct thread_smc_1_2_regs * args)659d17db2afSJens Wiklander static void spmc_handle_run(struct thread_smc_1_2_regs *args)
6604d028847SImre Kis {
6614d028847SImre Kis uint16_t endpoint = FFA_TARGET_INFO_GET_SP_ID(args->a1);
6624d028847SImre Kis uint16_t thread_id = FFA_TARGET_INFO_GET_VCPU_ID(args->a1);
66340f03182SJens Wiklander uint32_t rc = FFA_INVALID_PARAMETERS;
6644d028847SImre Kis
6654d028847SImre Kis /*
66640f03182SJens Wiklander * OP-TEE core threads are only preemted using controlled exit so
66740f03182SJens Wiklander * FFA_RUN mustn't be used to resume such threads.
668ecf08061SJens Wiklander *
669ecf08061SJens Wiklander * The OP-TEE SPMC is not preemted at all, it's an error to try to
670ecf08061SJens Wiklander * resume that ID.
67140f03182SJens Wiklander */
672ecf08061SJens Wiklander if (spmc_find_lsp_by_sp_id(endpoint))
67340f03182SJens Wiklander goto out;
67440f03182SJens Wiklander
67540f03182SJens Wiklander /*
67640f03182SJens Wiklander * The endpoint should be a S-EL0 SP, try to resume the SP from
6774d028847SImre Kis * preempted into busy state.
6784d028847SImre Kis */
679*8c8f3baeSJens Wiklander rc = spmc_sp_resume_from_preempted(endpoint, thread_id);
6804d028847SImre Kis out:
68105c6a763SJens Wiklander set_simple_ret_val(args, rc);
6824d028847SImre Kis }
683fb19e98eSJens Wiklander #endif /*CFG_CORE_SEL1_SPMC*/
6841b302ac0SJens Wiklander
get_notif_vm_bitmap(struct guest_partition * prtn,uint16_t vm_id)685f6dcf234SJens Wiklander static struct notif_vm_bitmap *get_notif_vm_bitmap(struct guest_partition *prtn,
686f6dcf234SJens Wiklander uint16_t vm_id)
687f6dcf234SJens Wiklander {
688f6dcf234SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
689f6dcf234SJens Wiklander if (!prtn)
690f6dcf234SJens Wiklander return NULL;
691f6dcf234SJens Wiklander assert(vm_id == virt_get_guest_id(prtn));
692f6dcf234SJens Wiklander return virt_get_guest_spec_data(prtn, notif_vm_bitmap_id);
693f6dcf234SJens Wiklander }
694f6dcf234SJens Wiklander if (vm_id)
695f6dcf234SJens Wiklander return NULL;
696f6dcf234SJens Wiklander return &default_notif_vm_bitmap;
697f6dcf234SJens Wiklander }
698f6dcf234SJens Wiklander
spmc_enable_async_notif(uint32_t bottom_half_value,uint16_t vm_id)6992e02a737SJens Wiklander static uint32_t spmc_enable_async_notif(uint32_t bottom_half_value,
7002e02a737SJens Wiklander uint16_t vm_id)
7012e02a737SJens Wiklander {
702f6dcf234SJens Wiklander struct guest_partition *prtn = NULL;
703f6dcf234SJens Wiklander struct notif_vm_bitmap *nvb = NULL;
7042e02a737SJens Wiklander uint32_t old_itr_status = 0;
705f6dcf234SJens Wiklander uint32_t res = 0;
7062e02a737SJens Wiklander
7072e02a737SJens Wiklander if (!spmc_notif_is_ready) {
7082e02a737SJens Wiklander /*
7092e02a737SJens Wiklander * This should never happen, not if normal world respects the
7102e02a737SJens Wiklander * exchanged capabilities.
7112e02a737SJens Wiklander */
7122e02a737SJens Wiklander EMSG("Asynchronous notifications are not ready");
7132e02a737SJens Wiklander return TEE_ERROR_NOT_IMPLEMENTED;
7142e02a737SJens Wiklander }
7152e02a737SJens Wiklander
71655cd94d1SJens Wiklander if (bottom_half_value >= OPTEE_FFA_MAX_ASYNC_NOTIF_VALUE) {
71755cd94d1SJens Wiklander EMSG("Invalid bottom half value %"PRIu32, bottom_half_value);
71855cd94d1SJens Wiklander return TEE_ERROR_BAD_PARAMETERS;
71955cd94d1SJens Wiklander }
72055cd94d1SJens Wiklander
721f6dcf234SJens Wiklander prtn = virt_get_guest(vm_id);
722f6dcf234SJens Wiklander nvb = get_notif_vm_bitmap(prtn, vm_id);
723f6dcf234SJens Wiklander if (!nvb) {
724f6dcf234SJens Wiklander res = TEE_ERROR_BAD_PARAMETERS;
725f6dcf234SJens Wiklander goto out;
726f6dcf234SJens Wiklander }
727f6dcf234SJens Wiklander
7282e02a737SJens Wiklander old_itr_status = cpu_spin_lock_xsave(&spmc_notif_lock);
729f6dcf234SJens Wiklander nvb->do_bottom_half_value = bottom_half_value;
7302e02a737SJens Wiklander cpu_spin_unlock_xrestore(&spmc_notif_lock, old_itr_status);
7312e02a737SJens Wiklander
732f6dcf234SJens Wiklander notif_deliver_atomic_event(NOTIF_EVENT_STARTED, vm_id);
733f6dcf234SJens Wiklander res = TEE_SUCCESS;
734f6dcf234SJens Wiklander out:
735f6dcf234SJens Wiklander virt_put_guest(prtn);
736f6dcf234SJens Wiklander return res;
7372e02a737SJens Wiklander }
7382e02a737SJens Wiklander
get_direct_resp_fid(uint32_t fid)739ecf08061SJens Wiklander static uint32_t get_direct_resp_fid(uint32_t fid)
7401b302ac0SJens Wiklander {
741ecf08061SJens Wiklander assert(fid == FFA_MSG_SEND_DIRECT_REQ_64 ||
742ecf08061SJens Wiklander fid == FFA_MSG_SEND_DIRECT_REQ_32);
743ecf08061SJens Wiklander
744ecf08061SJens Wiklander if (OPTEE_SMC_IS_64(fid))
745ecf08061SJens Wiklander return FFA_MSG_SEND_DIRECT_RESP_64;
746ecf08061SJens Wiklander return FFA_MSG_SEND_DIRECT_RESP_32;
747ecf08061SJens Wiklander }
748ecf08061SJens Wiklander
handle_yielding_call(struct thread_smc_1_2_regs * args)749ecf08061SJens Wiklander static void handle_yielding_call(struct thread_smc_1_2_regs *args)
750ecf08061SJens Wiklander {
751ecf08061SJens Wiklander uint32_t direct_resp_fid = get_direct_resp_fid(args->a0);
752ecf08061SJens Wiklander TEE_Result res = TEE_SUCCESS;
7531b302ac0SJens Wiklander
7541b302ac0SJens Wiklander thread_check_canaries();
7551b302ac0SJens Wiklander
7561d184480SJens Wiklander #ifdef ARM64
7571d184480SJens Wiklander /* Saving this for an eventual RPC */
7581d184480SJens Wiklander thread_get_core_local()->direct_resp_fid = direct_resp_fid;
7591d184480SJens Wiklander #endif
7601d184480SJens Wiklander
7611b302ac0SJens Wiklander if (args->a3 == OPTEE_FFA_YIELDING_CALL_RESUME) {
7621b302ac0SJens Wiklander /* Note connection to struct thread_rpc_arg::ret */
7631b302ac0SJens Wiklander thread_resume_from_rpc(args->a7, args->a4, args->a5, args->a6,
7641b302ac0SJens Wiklander 0);
765c1bdf4fcSJens Wiklander res = TEE_ERROR_BAD_PARAMETERS;
7661b302ac0SJens Wiklander } else {
7674107d2f9SJens Wiklander thread_alloc_and_run(args->a1, args->a3, args->a4, args->a5,
7684107d2f9SJens Wiklander args->a6, args->a7);
769c1bdf4fcSJens Wiklander res = TEE_ERROR_BUSY;
7701b302ac0SJens Wiklander }
7711d184480SJens Wiklander spmc_set_args(args, direct_resp_fid, swap_src_dst(args->a1),
7721d184480SJens Wiklander 0, res, 0, 0);
773c1bdf4fcSJens Wiklander }
774c1bdf4fcSJens Wiklander
handle_unregister_shm(uint32_t a4,uint32_t a5)775c1bdf4fcSJens Wiklander static uint32_t handle_unregister_shm(uint32_t a4, uint32_t a5)
776c1bdf4fcSJens Wiklander {
777c1bdf4fcSJens Wiklander uint64_t cookie = reg_pair_to_64(a5, a4);
778c1bdf4fcSJens Wiklander uint32_t res = 0;
779c1bdf4fcSJens Wiklander
780c1bdf4fcSJens Wiklander res = mobj_ffa_unregister_by_cookie(cookie);
781c1bdf4fcSJens Wiklander switch (res) {
782c1bdf4fcSJens Wiklander case TEE_SUCCESS:
783c1bdf4fcSJens Wiklander case TEE_ERROR_ITEM_NOT_FOUND:
784c1bdf4fcSJens Wiklander return 0;
785c1bdf4fcSJens Wiklander case TEE_ERROR_BUSY:
786c1bdf4fcSJens Wiklander EMSG("res %#"PRIx32, res);
787c1bdf4fcSJens Wiklander return FFA_BUSY;
788c1bdf4fcSJens Wiklander default:
789c1bdf4fcSJens Wiklander EMSG("res %#"PRIx32, res);
790c1bdf4fcSJens Wiklander return FFA_INVALID_PARAMETERS;
791c1bdf4fcSJens Wiklander }
7921b302ac0SJens Wiklander }
7931b302ac0SJens Wiklander
handle_blocking_call(struct thread_smc_1_2_regs * args)794ecf08061SJens Wiklander static void handle_blocking_call(struct thread_smc_1_2_regs *args)
7951b302ac0SJens Wiklander {
796ecf08061SJens Wiklander uint32_t direct_resp_fid = get_direct_resp_fid(args->a0);
7972e02a737SJens Wiklander uint32_t sec_caps = 0;
7982e02a737SJens Wiklander
7991b302ac0SJens Wiklander switch (args->a3) {
8001b302ac0SJens Wiklander case OPTEE_FFA_GET_API_VERSION:
8011d184480SJens Wiklander spmc_set_args(args, direct_resp_fid, swap_src_dst(args->a1), 0,
802fe513722SJelle Sels OPTEE_FFA_VERSION_MAJOR, OPTEE_FFA_VERSION_MINOR,
803fe513722SJelle Sels 0);
8041b302ac0SJens Wiklander break;
8051b302ac0SJens Wiklander case OPTEE_FFA_GET_OS_VERSION:
8061d184480SJens Wiklander spmc_set_args(args, direct_resp_fid, swap_src_dst(args->a1), 0,
807fe513722SJelle Sels CFG_OPTEE_REVISION_MAJOR,
808835688acSJerome Forissier CFG_OPTEE_REVISION_MINOR,
809835688acSJerome Forissier TEE_IMPL_GIT_SHA1 >> 32);
8101b302ac0SJens Wiklander break;
8111b302ac0SJens Wiklander case OPTEE_FFA_EXCHANGE_CAPABILITIES:
8122e02a737SJens Wiklander sec_caps = OPTEE_FFA_SEC_CAP_ARG_OFFSET;
8132e02a737SJens Wiklander if (spmc_notif_is_ready)
8142e02a737SJens Wiklander sec_caps |= OPTEE_FFA_SEC_CAP_ASYNC_NOTIF;
815b78dd3f2SJens Wiklander if (IS_ENABLED(CFG_RPMB_ANNOUNCE_PROBE_CAP))
8168dfdf392SJens Wiklander sec_caps |= OPTEE_FFA_SEC_CAP_RPMB_PROBE;
81700338334SJens Wiklander if (IS_ENABLED(CFG_CORE_DYN_PROTMEM))
81800338334SJens Wiklander sec_caps |= OPTEE_FFA_SEC_CAP_PROTMEM;
8192e02a737SJens Wiklander spmc_set_args(args, direct_resp_fid,
8202e02a737SJens Wiklander swap_src_dst(args->a1), 0, 0,
8212e02a737SJens Wiklander THREAD_RPC_MAX_NUM_PARAMS, sec_caps);
822c1bdf4fcSJens Wiklander break;
823c1bdf4fcSJens Wiklander case OPTEE_FFA_UNREGISTER_SHM:
8241d184480SJens Wiklander spmc_set_args(args, direct_resp_fid, swap_src_dst(args->a1), 0,
825c1bdf4fcSJens Wiklander handle_unregister_shm(args->a4, args->a5), 0, 0);
8261b302ac0SJens Wiklander break;
8272e02a737SJens Wiklander case OPTEE_FFA_ENABLE_ASYNC_NOTIF:
8282e02a737SJens Wiklander spmc_set_args(args, direct_resp_fid,
8292e02a737SJens Wiklander swap_src_dst(args->a1), 0,
8302e02a737SJens Wiklander spmc_enable_async_notif(args->a4,
8312e02a737SJens Wiklander FFA_SRC(args->a1)),
8322e02a737SJens Wiklander 0, 0);
8332e02a737SJens Wiklander break;
83400338334SJens Wiklander #ifdef CFG_CORE_DYN_PROTMEM
83500338334SJens Wiklander case OPTEE_FFA_RELEASE_PROTMEM:
83600338334SJens Wiklander spmc_set_args(args, direct_resp_fid, swap_src_dst(args->a1), 0,
83700338334SJens Wiklander handle_unregister_shm(args->a4, args->a5), 0, 0);
83800338334SJens Wiklander break;
83900338334SJens Wiklander #endif
8401b302ac0SJens Wiklander default:
8411b302ac0SJens Wiklander EMSG("Unhandled blocking service ID %#"PRIx32,
8421b302ac0SJens Wiklander (uint32_t)args->a3);
84387691a6fSJens Wiklander spmc_set_args(args, direct_resp_fid, swap_src_dst(args->a1), 0,
84487691a6fSJens Wiklander TEE_ERROR_BAD_PARAMETERS, 0, 0);
8451b302ac0SJens Wiklander }
8461b302ac0SJens Wiklander }
8471b302ac0SJens Wiklander
handle_framework_direct_request(struct thread_smc_1_2_regs * args)848ecf08061SJens Wiklander static void handle_framework_direct_request(struct thread_smc_1_2_regs *args)
849a1c53023SJens Wiklander {
850ecf08061SJens Wiklander uint32_t direct_resp_fid = get_direct_resp_fid(args->a0);
851a1c53023SJens Wiklander uint32_t w0 = FFA_ERROR;
852a1c53023SJens Wiklander uint32_t w1 = FFA_PARAM_MBZ;
853a1c53023SJens Wiklander uint32_t w2 = FFA_NOT_SUPPORTED;
854a1c53023SJens Wiklander uint32_t w3 = FFA_PARAM_MBZ;
855a1c53023SJens Wiklander
856a1c53023SJens Wiklander switch (args->a2 & FFA_MSG_TYPE_MASK) {
857a65dd3a6SJens Wiklander case FFA_MSG_SEND_VM_CREATED:
858a65dd3a6SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
859a65dd3a6SJens Wiklander uint16_t guest_id = args->a5;
860a65dd3a6SJens Wiklander TEE_Result res = virt_guest_created(guest_id);
861a65dd3a6SJens Wiklander
8621d184480SJens Wiklander w0 = direct_resp_fid;
863a65dd3a6SJens Wiklander w1 = swap_src_dst(args->a1);
864a65dd3a6SJens Wiklander w2 = FFA_MSG_FLAG_FRAMEWORK | FFA_MSG_RESP_VM_CREATED;
865a65dd3a6SJens Wiklander if (res == TEE_SUCCESS)
866a65dd3a6SJens Wiklander w3 = FFA_OK;
867a65dd3a6SJens Wiklander else if (res == TEE_ERROR_OUT_OF_MEMORY)
868a65dd3a6SJens Wiklander w3 = FFA_DENIED;
869a65dd3a6SJens Wiklander else
870a65dd3a6SJens Wiklander w3 = FFA_INVALID_PARAMETERS;
871a65dd3a6SJens Wiklander }
872a65dd3a6SJens Wiklander break;
87393a9647fSJens Wiklander case FFA_MSG_SEND_VM_DESTROYED:
87493a9647fSJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
87593a9647fSJens Wiklander uint16_t guest_id = args->a5;
87693a9647fSJens Wiklander TEE_Result res = virt_guest_destroyed(guest_id);
87793a9647fSJens Wiklander
8781d184480SJens Wiklander w0 = direct_resp_fid;
87993a9647fSJens Wiklander w1 = swap_src_dst(args->a1);
88093a9647fSJens Wiklander w2 = FFA_MSG_FLAG_FRAMEWORK | FFA_MSG_RESP_VM_DESTROYED;
88193a9647fSJens Wiklander if (res == TEE_SUCCESS)
88293a9647fSJens Wiklander w3 = FFA_OK;
88393a9647fSJens Wiklander else
88493a9647fSJens Wiklander w3 = FFA_INVALID_PARAMETERS;
88593a9647fSJens Wiklander }
88693a9647fSJens Wiklander break;
887a1c53023SJens Wiklander case FFA_MSG_VERSION_REQ:
8881d184480SJens Wiklander w0 = direct_resp_fid;
889a1c53023SJens Wiklander w1 = swap_src_dst(args->a1);
890a1c53023SJens Wiklander w2 = FFA_MSG_FLAG_FRAMEWORK | FFA_MSG_VERSION_RESP;
891ecf08061SJens Wiklander w3 = spmc_exchange_version(args->a3, &my_rxtx);
892a1c53023SJens Wiklander break;
893a1c53023SJens Wiklander default:
894a1c53023SJens Wiklander break;
895a1c53023SJens Wiklander }
896a1c53023SJens Wiklander spmc_set_args(args, w0, w1, w2, w3, FFA_PARAM_MBZ, FFA_PARAM_MBZ);
897a1c53023SJens Wiklander }
898a1c53023SJens Wiklander
optee_lsp_handle_direct_request(struct thread_smc_1_2_regs * args)899ecf08061SJens Wiklander static void optee_lsp_handle_direct_request(struct thread_smc_1_2_regs *args)
900a1c53023SJens Wiklander {
901a1c53023SJens Wiklander if (args->a2 & FFA_MSG_FLAG_FRAMEWORK) {
902ecf08061SJens Wiklander handle_framework_direct_request(args);
903a1c53023SJens Wiklander return;
904a1c53023SJens Wiklander }
905a1c53023SJens Wiklander
906a65dd3a6SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION) &&
907a65dd3a6SJens Wiklander virt_set_guest(get_sender_id(args->a1))) {
908ecf08061SJens Wiklander spmc_set_args(args, get_direct_resp_fid(args->a0),
909ecf08061SJens Wiklander swap_src_dst(args->a1), 0,
910a65dd3a6SJens Wiklander TEE_ERROR_ITEM_NOT_FOUND, 0, 0);
911a65dd3a6SJens Wiklander return;
912a65dd3a6SJens Wiklander }
913a65dd3a6SJens Wiklander
914a1c53023SJens Wiklander if (args->a3 & BIT32(OPTEE_FFA_YIELDING_CALL_BIT))
915ecf08061SJens Wiklander handle_yielding_call(args);
916a1c53023SJens Wiklander else
917ecf08061SJens Wiklander handle_blocking_call(args);
918a65dd3a6SJens Wiklander
919a65dd3a6SJens Wiklander /*
920a65dd3a6SJens Wiklander * Note that handle_yielding_call() typically only returns if a
921a65dd3a6SJens Wiklander * thread cannot be allocated or found. virt_unset_guest() is also
922a65dd3a6SJens Wiklander * called from thread_state_suspend() and thread_state_free().
923a65dd3a6SJens Wiklander */
924b65298cdSImre Kis if (IS_ENABLED(CFG_NS_VIRTUALIZATION))
925a65dd3a6SJens Wiklander virt_unset_guest();
926a1c53023SJens Wiklander }
927a1c53023SJens Wiklander
928ecf08061SJens Wiklander static void __maybe_unused
optee_spmc_lsp_handle_direct_request(struct thread_smc_1_2_regs * args)929ecf08061SJens Wiklander optee_spmc_lsp_handle_direct_request(struct thread_smc_1_2_regs *args)
930ecf08061SJens Wiklander {
931ecf08061SJens Wiklander if (args->a2 & FFA_MSG_FLAG_FRAMEWORK)
932ecf08061SJens Wiklander handle_framework_direct_request(args);
933ecf08061SJens Wiklander else
934ecf08061SJens Wiklander set_simple_ret_val(args, FFA_INVALID_PARAMETERS);
935ecf08061SJens Wiklander }
936ecf08061SJens Wiklander
handle_direct_request(struct thread_smc_1_2_regs * args)937ecf08061SJens Wiklander static void handle_direct_request(struct thread_smc_1_2_regs *args)
938ecf08061SJens Wiklander {
939ecf08061SJens Wiklander struct spmc_lsp_desc *lsp = spmc_find_lsp_by_sp_id(FFA_DST(args->a1));
940ecf08061SJens Wiklander
941ecf08061SJens Wiklander if (lsp) {
942ecf08061SJens Wiklander lsp->direct_req(args);
943ecf08061SJens Wiklander } else {
9446af74df5SJens Wiklander int rc = spmc_sp_start_thread(args);
9456af74df5SJens Wiklander
946ecf08061SJens Wiklander /*
9476af74df5SJens Wiklander * spmc_sp_start_thread() returns here if the SPs aren't
9486af74df5SJens Wiklander * supported or if all threads are busy.
949ecf08061SJens Wiklander */
9506af74df5SJens Wiklander set_simple_ret_val(args, rc);
951ecf08061SJens Wiklander }
952ecf08061SJens Wiklander }
953ecf08061SJens Wiklander
spmc_read_mem_transaction(uint32_t ffa_vers,void * buf,size_t blen,struct ffa_mem_transaction_x * trans)954a1c53023SJens Wiklander int spmc_read_mem_transaction(uint32_t ffa_vers, void *buf, size_t blen,
955a1c53023SJens Wiklander struct ffa_mem_transaction_x *trans)
956a1c53023SJens Wiklander {
957a1c53023SJens Wiklander uint16_t mem_reg_attr = 0;
958a1c53023SJens Wiklander uint32_t flags = 0;
959a1c53023SJens Wiklander uint32_t count = 0;
960a1c53023SJens Wiklander uint32_t offs = 0;
961a1c53023SJens Wiklander uint32_t size = 0;
962a1c53023SJens Wiklander size_t n = 0;
963a1c53023SJens Wiklander
964a1c53023SJens Wiklander if (!IS_ALIGNED_WITH_TYPE(buf, uint64_t))
965a1c53023SJens Wiklander return FFA_INVALID_PARAMETERS;
966a1c53023SJens Wiklander
967a1c53023SJens Wiklander if (ffa_vers >= FFA_VERSION_1_1) {
968a1c53023SJens Wiklander struct ffa_mem_transaction_1_1 *descr = NULL;
969a1c53023SJens Wiklander
970a1c53023SJens Wiklander if (blen < sizeof(*descr))
971a1c53023SJens Wiklander return FFA_INVALID_PARAMETERS;
972a1c53023SJens Wiklander
973a1c53023SJens Wiklander descr = buf;
974a1c53023SJens Wiklander trans->sender_id = READ_ONCE(descr->sender_id);
975a1c53023SJens Wiklander mem_reg_attr = READ_ONCE(descr->mem_reg_attr);
976a1c53023SJens Wiklander flags = READ_ONCE(descr->flags);
977a1c53023SJens Wiklander trans->global_handle = READ_ONCE(descr->global_handle);
978a1c53023SJens Wiklander trans->tag = READ_ONCE(descr->tag);
979a1c53023SJens Wiklander
980a1c53023SJens Wiklander count = READ_ONCE(descr->mem_access_count);
981a1c53023SJens Wiklander size = READ_ONCE(descr->mem_access_size);
982a1c53023SJens Wiklander offs = READ_ONCE(descr->mem_access_offs);
983a1c53023SJens Wiklander } else {
984a1c53023SJens Wiklander struct ffa_mem_transaction_1_0 *descr = NULL;
985a1c53023SJens Wiklander
986a1c53023SJens Wiklander if (blen < sizeof(*descr))
987a1c53023SJens Wiklander return FFA_INVALID_PARAMETERS;
988a1c53023SJens Wiklander
989a1c53023SJens Wiklander descr = buf;
990a1c53023SJens Wiklander trans->sender_id = READ_ONCE(descr->sender_id);
991a1c53023SJens Wiklander mem_reg_attr = READ_ONCE(descr->mem_reg_attr);
992a1c53023SJens Wiklander flags = READ_ONCE(descr->flags);
993a1c53023SJens Wiklander trans->global_handle = READ_ONCE(descr->global_handle);
994a1c53023SJens Wiklander trans->tag = READ_ONCE(descr->tag);
995a1c53023SJens Wiklander
996a1c53023SJens Wiklander count = READ_ONCE(descr->mem_access_count);
997a1c53023SJens Wiklander size = sizeof(struct ffa_mem_access);
998a1c53023SJens Wiklander offs = offsetof(struct ffa_mem_transaction_1_0,
999a1c53023SJens Wiklander mem_access_array);
1000a1c53023SJens Wiklander }
1001a1c53023SJens Wiklander
1002a1c53023SJens Wiklander if (mem_reg_attr > UINT8_MAX || flags > UINT8_MAX ||
1003a1c53023SJens Wiklander size > UINT8_MAX || count > UINT8_MAX || offs > UINT16_MAX)
1004a1c53023SJens Wiklander return FFA_INVALID_PARAMETERS;
1005a1c53023SJens Wiklander
1006a1c53023SJens Wiklander /* Check that the endpoint memory access descriptor array fits */
1007a1c53023SJens Wiklander if (MUL_OVERFLOW(size, count, &n) || ADD_OVERFLOW(offs, n, &n) ||
1008a1c53023SJens Wiklander n > blen)
1009a1c53023SJens Wiklander return FFA_INVALID_PARAMETERS;
1010a1c53023SJens Wiklander
1011a1c53023SJens Wiklander trans->mem_reg_attr = mem_reg_attr;
1012a1c53023SJens Wiklander trans->flags = flags;
1013a1c53023SJens Wiklander trans->mem_access_size = size;
1014a1c53023SJens Wiklander trans->mem_access_count = count;
1015a1c53023SJens Wiklander trans->mem_access_offs = offs;
1016a1c53023SJens Wiklander return 0;
1017a1c53023SJens Wiklander }
1018a1c53023SJens Wiklander
1019fb19e98eSJens Wiklander #if defined(CFG_CORE_SEL1_SPMC)
get_acc_perms(vaddr_t mem_acc_base,unsigned int mem_access_size,unsigned int mem_access_count,uint8_t * acc_perms,unsigned int * region_offs)1020a1c53023SJens Wiklander static int get_acc_perms(vaddr_t mem_acc_base, unsigned int mem_access_size,
1021a1c53023SJens Wiklander unsigned int mem_access_count, uint8_t *acc_perms,
10221b302ac0SJens Wiklander unsigned int *region_offs)
10231b302ac0SJens Wiklander {
1024a1c53023SJens Wiklander struct ffa_mem_access_perm *descr = NULL;
1025a1c53023SJens Wiklander struct ffa_mem_access *mem_acc = NULL;
10261b302ac0SJens Wiklander unsigned int n = 0;
10271b302ac0SJens Wiklander
1028a1c53023SJens Wiklander for (n = 0; n < mem_access_count; n++) {
1029a1c53023SJens Wiklander mem_acc = (void *)(mem_acc_base + mem_access_size * n);
1030a1c53023SJens Wiklander descr = &mem_acc->access_perm;
1031ecf08061SJens Wiklander if (READ_ONCE(descr->endpoint_id) == optee_core_lsp.sp_id) {
1032c1bdf4fcSJens Wiklander *acc_perms = READ_ONCE(descr->perm);
1033c1bdf4fcSJens Wiklander *region_offs = READ_ONCE(mem_acc[n].region_offs);
10341b302ac0SJens Wiklander return 0;
10351b302ac0SJens Wiklander }
10361b302ac0SJens Wiklander }
10371b302ac0SJens Wiklander
10381b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
10391b302ac0SJens Wiklander }
10401b302ac0SJens Wiklander
mem_op_init(bool mem_share,struct ffa_mem_transaction_x * mem_trans,void * buf,size_t blen,unsigned int * page_count,unsigned int * region_count,size_t * addr_range_offs)104100338334SJens Wiklander static int mem_op_init(bool mem_share, struct ffa_mem_transaction_x *mem_trans,
104200338334SJens Wiklander void *buf, size_t blen, unsigned int *page_count,
10431b302ac0SJens Wiklander unsigned int *region_count, size_t *addr_range_offs)
10441b302ac0SJens Wiklander {
1045c1bdf4fcSJens Wiklander const uint8_t exp_mem_acc_perm = FFA_MEM_ACC_RW;
1046c1bdf4fcSJens Wiklander struct ffa_mem_region *region_descr = NULL;
10471b302ac0SJens Wiklander unsigned int region_descr_offs = 0;
104800338334SJens Wiklander uint16_t exp_mem_reg_attr = 0;
1049a1c53023SJens Wiklander uint8_t mem_acc_perm = 0;
10501b302ac0SJens Wiklander size_t n = 0;
10511b302ac0SJens Wiklander
105200338334SJens Wiklander if (mem_share)
105300338334SJens Wiklander exp_mem_reg_attr = FFA_NORMAL_MEM_REG_ATTR;
1054a1c53023SJens Wiklander if (mem_trans->mem_reg_attr != exp_mem_reg_attr)
10551b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
10561b302ac0SJens Wiklander
10571b302ac0SJens Wiklander /* Check that the access permissions matches what's expected */
1058a1c53023SJens Wiklander if (get_acc_perms((vaddr_t)buf + mem_trans->mem_access_offs,
1059a1c53023SJens Wiklander mem_trans->mem_access_size,
1060a1c53023SJens Wiklander mem_trans->mem_access_count,
1061a1c53023SJens Wiklander &mem_acc_perm, ®ion_descr_offs) ||
10621b302ac0SJens Wiklander mem_acc_perm != exp_mem_acc_perm)
10631b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
10641b302ac0SJens Wiklander
10651b302ac0SJens Wiklander /* Check that the Composite memory region descriptor fits */
10661b302ac0SJens Wiklander if (ADD_OVERFLOW(region_descr_offs, sizeof(*region_descr), &n) ||
10671b302ac0SJens Wiklander n > blen)
10681b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
10691b302ac0SJens Wiklander
1070a1c53023SJens Wiklander if (!IS_ALIGNED_WITH_TYPE((vaddr_t)buf + region_descr_offs,
1071c1bdf4fcSJens Wiklander struct ffa_mem_region))
10721b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
10731b302ac0SJens Wiklander
1074a1c53023SJens Wiklander region_descr = (struct ffa_mem_region *)((vaddr_t)buf +
10751b302ac0SJens Wiklander region_descr_offs);
10761b302ac0SJens Wiklander *page_count = READ_ONCE(region_descr->total_page_count);
10771b302ac0SJens Wiklander *region_count = READ_ONCE(region_descr->address_range_count);
10781b302ac0SJens Wiklander *addr_range_offs = n;
10791b302ac0SJens Wiklander return 0;
10801b302ac0SJens Wiklander }
10811b302ac0SJens Wiklander
add_mem_op_helper(struct mem_op_state * s,void * buf,size_t flen)108200338334SJens Wiklander static int add_mem_op_helper(struct mem_op_state *s, void *buf, size_t flen)
10831b302ac0SJens Wiklander {
1084c1bdf4fcSJens Wiklander unsigned int region_count = flen / sizeof(struct ffa_address_range);
1085c1bdf4fcSJens Wiklander struct ffa_address_range *arange = NULL;
10861b302ac0SJens Wiklander unsigned int n = 0;
10871b302ac0SJens Wiklander
10881b302ac0SJens Wiklander if (region_count > s->region_count)
10891b302ac0SJens Wiklander region_count = s->region_count;
10901b302ac0SJens Wiklander
1091be501eb1SJorge Ramirez-Ortiz if (!IS_ALIGNED_WITH_TYPE(buf, struct ffa_address_range))
10921b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
10931b302ac0SJens Wiklander arange = buf;
10941b302ac0SJens Wiklander
10951b302ac0SJens Wiklander for (n = 0; n < region_count; n++) {
10961b302ac0SJens Wiklander unsigned int page_count = READ_ONCE(arange[n].page_count);
10971b302ac0SJens Wiklander uint64_t addr = READ_ONCE(arange[n].address);
10981b302ac0SJens Wiklander
10991b302ac0SJens Wiklander if (mobj_ffa_add_pages_at(s->mf, &s->current_page_idx,
11001b302ac0SJens Wiklander addr, page_count))
11011b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
11021b302ac0SJens Wiklander }
11031b302ac0SJens Wiklander
11041b302ac0SJens Wiklander s->region_count -= region_count;
11051b302ac0SJens Wiklander if (s->region_count)
11061b302ac0SJens Wiklander return region_count * sizeof(*arange);
11071b302ac0SJens Wiklander
11081b302ac0SJens Wiklander if (s->current_page_idx != s->page_count)
11091b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
11101b302ac0SJens Wiklander
11111b302ac0SJens Wiklander return 0;
11121b302ac0SJens Wiklander }
11131b302ac0SJens Wiklander
add_mem_op_frag(struct mem_frag_state * s,void * buf,size_t flen)111400338334SJens Wiklander static int add_mem_op_frag(struct mem_frag_state *s, void *buf, size_t flen)
11151b302ac0SJens Wiklander {
11161b302ac0SJens Wiklander int rc = 0;
11171b302ac0SJens Wiklander
111800338334SJens Wiklander rc = add_mem_op_helper(&s->op, buf, flen);
11191b302ac0SJens Wiklander if (rc >= 0) {
11201b302ac0SJens Wiklander if (!ADD_OVERFLOW(s->frag_offset, rc, &s->frag_offset)) {
11217f127d42SJens Wiklander /* We're not at the end of the descriptor yet */
112200338334SJens Wiklander if (s->op.region_count)
11231b302ac0SJens Wiklander return s->frag_offset;
11247f127d42SJens Wiklander
11257f127d42SJens Wiklander /* We're done */
11267f127d42SJens Wiklander rc = 0;
11271b302ac0SJens Wiklander } else {
11281b302ac0SJens Wiklander rc = FFA_INVALID_PARAMETERS;
11291b302ac0SJens Wiklander }
11301b302ac0SJens Wiklander }
11311b302ac0SJens Wiklander
11321b302ac0SJens Wiklander SLIST_REMOVE(&frag_state_head, s, mem_frag_state, link);
113300338334SJens Wiklander if (rc < 0) {
113400338334SJens Wiklander mobj_ffa_sel1_spmc_delete(s->op.mf);
113500338334SJens Wiklander } else {
113600338334SJens Wiklander if (mobj_ffa_push_to_inactive(s->op.mf)) {
113700338334SJens Wiklander rc = FFA_INVALID_PARAMETERS;
113800338334SJens Wiklander mobj_ffa_sel1_spmc_delete(s->op.mf);
113900338334SJens Wiklander }
114000338334SJens Wiklander }
11411b302ac0SJens Wiklander free(s);
11421b302ac0SJens Wiklander
11431b302ac0SJens Wiklander return rc;
11441b302ac0SJens Wiklander }
11451b302ac0SJens Wiklander
is_sp_op(struct ffa_mem_transaction_x * mem_trans,void * buf)114600338334SJens Wiklander static bool is_sp_op(struct ffa_mem_transaction_x *mem_trans, void *buf)
11476a1b230cSJelle Sels {
11486a1b230cSJelle Sels struct ffa_mem_access_perm *perm = NULL;
1149a1c53023SJens Wiklander struct ffa_mem_access *mem_acc = NULL;
11506a1b230cSJelle Sels
11516a1b230cSJelle Sels if (!IS_ENABLED(CFG_SECURE_PARTITION))
11526a1b230cSJelle Sels return false;
11536a1b230cSJelle Sels
1154a1c53023SJens Wiklander if (mem_trans->mem_access_count < 1)
1155a1c53023SJens Wiklander return false;
1156a1c53023SJens Wiklander
1157a1c53023SJens Wiklander mem_acc = (void *)((vaddr_t)buf + mem_trans->mem_access_offs);
1158a1c53023SJens Wiklander perm = &mem_acc->access_perm;
11596a1b230cSJelle Sels
11606a1b230cSJelle Sels /*
11616a1b230cSJelle Sels * perm->endpoint_id is read here only to check if the endpoint is
11626a1b230cSJelle Sels * OP-TEE. We do read it later on again, but there are some additional
11636a1b230cSJelle Sels * checks there to make sure that the data is correct.
11646a1b230cSJelle Sels */
1165ecf08061SJens Wiklander return READ_ONCE(perm->endpoint_id) != optee_core_lsp.sp_id;
11666a1b230cSJelle Sels }
11676a1b230cSJelle Sels
add_mem_op(bool mem_share,struct ffa_mem_transaction_x * mem_trans,tee_mm_entry_t * mm,void * buf,size_t blen,size_t flen,uint64_t * global_handle)116800338334SJens Wiklander static int add_mem_op(bool mem_share, struct ffa_mem_transaction_x *mem_trans,
116900338334SJens Wiklander tee_mm_entry_t *mm, void *buf, size_t blen, size_t flen,
117000338334SJens Wiklander uint64_t *global_handle)
11711b302ac0SJens Wiklander {
11721b302ac0SJens Wiklander int rc = 0;
117300338334SJens Wiklander struct mem_op_state op = { .mem_share = mem_share, };
11741b302ac0SJens Wiklander size_t addr_range_offs = 0;
1175a65dd3a6SJens Wiklander uint64_t cookie = OPTEE_MSG_FMEM_INVALID_GLOBAL_ID;
117600338334SJens Wiklander enum mobj_use_case use_case = MOBJ_USE_CASE_NS_SHM;
11771b302ac0SJens Wiklander size_t n = 0;
11781b302ac0SJens Wiklander
117900338334SJens Wiklander rc = mem_op_init(mem_share, mem_trans, buf, flen, &op.page_count,
118000338334SJens Wiklander &op.region_count, &addr_range_offs);
11811b302ac0SJens Wiklander if (rc)
11821b302ac0SJens Wiklander return rc;
11831b302ac0SJens Wiklander
118400338334SJens Wiklander if (!op.page_count || !op.region_count)
118592870f11SImre Kis return FFA_INVALID_PARAMETERS;
118692870f11SImre Kis
118700338334SJens Wiklander if (MUL_OVERFLOW(op.region_count,
1188c1bdf4fcSJens Wiklander sizeof(struct ffa_address_range), &n) ||
11891b302ac0SJens Wiklander ADD_OVERFLOW(n, addr_range_offs, &n) || n > blen)
11901b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
11911b302ac0SJens Wiklander
1192a65dd3a6SJens Wiklander if (mem_trans->global_handle)
1193a65dd3a6SJens Wiklander cookie = mem_trans->global_handle;
119400338334SJens Wiklander if (!mem_share)
119500338334SJens Wiklander use_case = mem_trans->tag;
119600338334SJens Wiklander op.mf = mobj_ffa_sel1_spmc_new(cookie, op.page_count, use_case);
119700338334SJens Wiklander if (!op.mf)
11981b302ac0SJens Wiklander return FFA_NO_MEMORY;
11991b302ac0SJens Wiklander
12001b302ac0SJens Wiklander if (flen != blen) {
1201b70970feSEtienne Carriere struct mem_frag_state *s = calloc(1, sizeof(*s));
12021b302ac0SJens Wiklander
12031b302ac0SJens Wiklander if (!s) {
12041b302ac0SJens Wiklander rc = FFA_NO_MEMORY;
12051b302ac0SJens Wiklander goto err;
12061b302ac0SJens Wiklander }
120700338334SJens Wiklander s->op = op;
12081b302ac0SJens Wiklander s->mm = mm;
12091b302ac0SJens Wiklander s->frag_offset = addr_range_offs;
12101b302ac0SJens Wiklander
12111b302ac0SJens Wiklander SLIST_INSERT_HEAD(&frag_state_head, s, link);
121200338334SJens Wiklander rc = add_mem_op_frag(s, (char *)buf + addr_range_offs,
12131b302ac0SJens Wiklander flen - addr_range_offs);
12141b302ac0SJens Wiklander
12151b302ac0SJens Wiklander if (rc >= 0)
121600338334SJens Wiklander *global_handle = mobj_ffa_get_cookie(op.mf);
12171b302ac0SJens Wiklander
12181b302ac0SJens Wiklander return rc;
12191b302ac0SJens Wiklander }
12201b302ac0SJens Wiklander
122100338334SJens Wiklander rc = add_mem_op_helper(&op, (char *)buf + addr_range_offs,
12221b302ac0SJens Wiklander flen - addr_range_offs);
12231b302ac0SJens Wiklander if (rc) {
12241b302ac0SJens Wiklander /*
12251b302ac0SJens Wiklander * Number of consumed bytes may be returned instead of 0 for
12261b302ac0SJens Wiklander * done.
12271b302ac0SJens Wiklander */
12281b302ac0SJens Wiklander rc = FFA_INVALID_PARAMETERS;
12291b302ac0SJens Wiklander goto err;
12301b302ac0SJens Wiklander }
12311b302ac0SJens Wiklander
123200338334SJens Wiklander if (mobj_ffa_push_to_inactive(op.mf)) {
123300338334SJens Wiklander rc = FFA_INVALID_PARAMETERS;
123400338334SJens Wiklander goto err;
123500338334SJens Wiklander }
123600338334SJens Wiklander *global_handle = mobj_ffa_get_cookie(op.mf);
12371b302ac0SJens Wiklander
12381b302ac0SJens Wiklander return 0;
12391b302ac0SJens Wiklander err:
124000338334SJens Wiklander mobj_ffa_sel1_spmc_delete(op.mf);
12411b302ac0SJens Wiklander return rc;
12421b302ac0SJens Wiklander }
12431b302ac0SJens Wiklander
handle_mem_op_tmem(bool share_mem,paddr_t pbuf,size_t blen,size_t flen,unsigned int page_count,uint64_t * global_handle,struct ffa_rxtx * rxtx)124400338334SJens Wiklander static int handle_mem_op_tmem(bool share_mem, paddr_t pbuf, size_t blen,
124500338334SJens Wiklander size_t flen, unsigned int page_count,
1246cf133f37SJelle Sels uint64_t *global_handle, struct ffa_rxtx *rxtx)
12471b302ac0SJens Wiklander {
1248a1c53023SJens Wiklander struct ffa_mem_transaction_x mem_trans = { };
12491b302ac0SJens Wiklander int rc = 0;
12501b302ac0SJens Wiklander size_t len = 0;
1251a1c53023SJens Wiklander void *buf = NULL;
12521b302ac0SJens Wiklander tee_mm_entry_t *mm = NULL;
12531b302ac0SJens Wiklander vaddr_t offs = pbuf & SMALL_PAGE_MASK;
12541b302ac0SJens Wiklander
12551b302ac0SJens Wiklander if (MUL_OVERFLOW(page_count, SMALL_PAGE_SIZE, &len))
12561b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
12571b302ac0SJens Wiklander if (!core_pbuf_is(CORE_MEM_NON_SEC, pbuf, len))
12581b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
12591b302ac0SJens Wiklander
12601b302ac0SJens Wiklander /*
1261fba7d2adSJens Wiklander * Check that the length reported in flen is covered by len even
12621b302ac0SJens Wiklander * if the offset is taken into account.
12631b302ac0SJens Wiklander */
1264fba7d2adSJens Wiklander if (len < flen || len - offs < flen)
12651b302ac0SJens Wiklander return FFA_INVALID_PARAMETERS;
12661b302ac0SJens Wiklander
1267fdf696b7SJens Wiklander mm = tee_mm_alloc(&core_virt_shm_pool, len);
12681b302ac0SJens Wiklander if (!mm)
12691b302ac0SJens Wiklander return FFA_NO_MEMORY;
12701b302ac0SJens Wiklander
12711b302ac0SJens Wiklander if (core_mmu_map_contiguous_pages(tee_mm_get_smem(mm), pbuf,
12721b302ac0SJens Wiklander page_count, MEM_AREA_NSEC_SHM)) {
12731b302ac0SJens Wiklander rc = FFA_INVALID_PARAMETERS;
12741b302ac0SJens Wiklander goto out;
12751b302ac0SJens Wiklander }
1276a1c53023SJens Wiklander buf = (void *)(tee_mm_get_smem(mm) + offs);
12771b302ac0SJens Wiklander
1278cf133f37SJelle Sels cpu_spin_lock(&rxtx->spinlock);
1279a1c53023SJens Wiklander rc = spmc_read_mem_transaction(rxtx->ffa_vers, buf, flen, &mem_trans);
1280b65298cdSImre Kis if (rc)
1281b65298cdSImre Kis goto unlock;
1282b65298cdSImre Kis
128300338334SJens Wiklander if (is_sp_op(&mem_trans, buf)) {
128400338334SJens Wiklander if (!share_mem) {
128500338334SJens Wiklander rc = FFA_DENIED;
128600338334SJens Wiklander goto unlock;
128700338334SJens Wiklander }
1288b65298cdSImre Kis rc = spmc_sp_add_share(&mem_trans, buf, blen, flen,
1289b65298cdSImre Kis global_handle, NULL);
1290b65298cdSImre Kis goto unlock;
1291b65298cdSImre Kis }
1292b65298cdSImre Kis
1293b65298cdSImre Kis if (IS_ENABLED(CFG_NS_VIRTUALIZATION) &&
1294b65298cdSImre Kis virt_set_guest(mem_trans.sender_id)) {
1295a65dd3a6SJens Wiklander rc = FFA_DENIED;
1296b65298cdSImre Kis goto unlock;
1297b65298cdSImre Kis }
1298b65298cdSImre Kis
129900338334SJens Wiklander rc = add_mem_op(share_mem, &mem_trans, mm, buf, blen, flen,
130000338334SJens Wiklander global_handle);
1301b65298cdSImre Kis
1302b65298cdSImre Kis if (IS_ENABLED(CFG_NS_VIRTUALIZATION))
1303a65dd3a6SJens Wiklander virt_unset_guest();
1304b65298cdSImre Kis
1305b65298cdSImre Kis unlock:
1306cf133f37SJelle Sels cpu_spin_unlock(&rxtx->spinlock);
13071b302ac0SJens Wiklander if (rc > 0)
13081b302ac0SJens Wiklander return rc;
13091b302ac0SJens Wiklander
13101b302ac0SJens Wiklander core_mmu_unmap_pages(tee_mm_get_smem(mm), page_count);
13111b302ac0SJens Wiklander out:
13121b302ac0SJens Wiklander tee_mm_free(mm);
13131b302ac0SJens Wiklander return rc;
13141b302ac0SJens Wiklander }
13151b302ac0SJens Wiklander
handle_mem_op_rxbuf(bool share_mem,size_t blen,size_t flen,uint64_t * global_handle,struct ffa_rxtx * rxtx)131600338334SJens Wiklander static int handle_mem_op_rxbuf(bool share_mem, size_t blen, size_t flen,
131700338334SJens Wiklander uint64_t *global_handle, struct ffa_rxtx *rxtx)
13181b302ac0SJens Wiklander {
1319a1c53023SJens Wiklander struct ffa_mem_transaction_x mem_trans = { };
13201b302ac0SJens Wiklander int rc = FFA_DENIED;
13211b302ac0SJens Wiklander
1322cf133f37SJelle Sels cpu_spin_lock(&rxtx->spinlock);
13231b302ac0SJens Wiklander
1324a1c53023SJens Wiklander if (!rxtx->rx || flen > rxtx->size)
1325a1c53023SJens Wiklander goto out;
1326a1c53023SJens Wiklander
1327a1c53023SJens Wiklander rc = spmc_read_mem_transaction(rxtx->ffa_vers, rxtx->rx, flen,
1328a1c53023SJens Wiklander &mem_trans);
1329a1c53023SJens Wiklander if (rc)
1330a1c53023SJens Wiklander goto out;
133100338334SJens Wiklander if (is_sp_op(&mem_trans, rxtx->rx)) {
133200338334SJens Wiklander if (!share_mem) {
133300338334SJens Wiklander rc = FFA_DENIED;
133400338334SJens Wiklander goto out;
133500338334SJens Wiklander }
1336b65298cdSImre Kis rc = spmc_sp_add_share(&mem_trans, rxtx, blen, flen,
13376a1b230cSJelle Sels global_handle, NULL);
1338a1c53023SJens Wiklander goto out;
13396a1b230cSJelle Sels }
13401b302ac0SJens Wiklander
1341a65dd3a6SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION) &&
1342a65dd3a6SJens Wiklander virt_set_guest(mem_trans.sender_id))
1343a65dd3a6SJens Wiklander goto out;
1344a65dd3a6SJens Wiklander
134500338334SJens Wiklander rc = add_mem_op(share_mem, &mem_trans, NULL, rxtx->rx, blen, flen,
1346a1c53023SJens Wiklander global_handle);
1347a1c53023SJens Wiklander
1348b65298cdSImre Kis if (IS_ENABLED(CFG_NS_VIRTUALIZATION))
1349a65dd3a6SJens Wiklander virt_unset_guest();
1350a65dd3a6SJens Wiklander
1351a1c53023SJens Wiklander out:
1352cf133f37SJelle Sels cpu_spin_unlock(&rxtx->spinlock);
13531b302ac0SJens Wiklander
13541b302ac0SJens Wiklander return rc;
13551b302ac0SJens Wiklander }
13561b302ac0SJens Wiklander
handle_mem_op(struct thread_smc_1_2_regs * args,struct ffa_rxtx * rxtx)135700338334SJens Wiklander static void handle_mem_op(struct thread_smc_1_2_regs *args,
1358cf133f37SJelle Sels struct ffa_rxtx *rxtx)
13591b302ac0SJens Wiklander {
1360fba7d2adSJens Wiklander uint32_t tot_len = args->a1;
1361fba7d2adSJens Wiklander uint32_t frag_len = args->a2;
1362fba7d2adSJens Wiklander uint64_t addr = args->a3;
1363fba7d2adSJens Wiklander uint32_t page_count = args->a4;
13641b302ac0SJens Wiklander uint32_t ret_w1 = 0;
13651b302ac0SJens Wiklander uint32_t ret_w2 = FFA_INVALID_PARAMETERS;
13661b302ac0SJens Wiklander uint32_t ret_w3 = 0;
13671b302ac0SJens Wiklander uint32_t ret_fid = FFA_ERROR;
13681b302ac0SJens Wiklander uint64_t global_handle = 0;
136900338334SJens Wiklander bool share_mem = false;
13701b302ac0SJens Wiklander int rc = 0;
13711b302ac0SJens Wiklander
13721b302ac0SJens Wiklander /* Check that the MBZs are indeed 0 */
13731b302ac0SJens Wiklander if (args->a5 || args->a6 || args->a7)
13741b302ac0SJens Wiklander goto out;
13751b302ac0SJens Wiklander
1376fba7d2adSJens Wiklander /* Check that fragment length doesn't exceed total length */
1377fba7d2adSJens Wiklander if (frag_len > tot_len)
1378fba7d2adSJens Wiklander goto out;
1379fba7d2adSJens Wiklander
1380fba7d2adSJens Wiklander /* Check for 32-bit calling convention */
138100338334SJens Wiklander if (!OPTEE_SMC_IS_64(args->a0))
1382fba7d2adSJens Wiklander addr &= UINT32_MAX;
1383fba7d2adSJens Wiklander
138400338334SJens Wiklander if (args->a0 == FFA_MEM_SHARE_32 || args->a0 == FFA_MEM_SHARE_64)
138500338334SJens Wiklander share_mem = true;
138600338334SJens Wiklander else
138700338334SJens Wiklander share_mem = false;
138800338334SJens Wiklander
1389fba7d2adSJens Wiklander if (!addr) {
13901b302ac0SJens Wiklander /*
13911b302ac0SJens Wiklander * The memory transaction descriptor is passed via our rx
13921b302ac0SJens Wiklander * buffer.
13931b302ac0SJens Wiklander */
1394fba7d2adSJens Wiklander if (page_count)
13951b302ac0SJens Wiklander goto out;
139600338334SJens Wiklander rc = handle_mem_op_rxbuf(share_mem, tot_len, frag_len,
1397fba7d2adSJens Wiklander &global_handle, rxtx);
139800338334SJens Wiklander } else {
139900338334SJens Wiklander rc = handle_mem_op_tmem(share_mem, addr, tot_len, frag_len,
140000338334SJens Wiklander page_count, &global_handle, rxtx);
14011b302ac0SJens Wiklander }
14021b302ac0SJens Wiklander if (rc < 0) {
14031b302ac0SJens Wiklander ret_w2 = rc;
14047f127d42SJens Wiklander } else if (rc > 0) {
14051b302ac0SJens Wiklander ret_fid = FFA_MEM_FRAG_RX;
14061b302ac0SJens Wiklander ret_w3 = rc;
14071b302ac0SJens Wiklander reg_pair_from_64(global_handle, &ret_w2, &ret_w1);
14087f127d42SJens Wiklander } else {
14091b302ac0SJens Wiklander ret_fid = FFA_SUCCESS_32;
14101b302ac0SJens Wiklander reg_pair_from_64(global_handle, &ret_w3, &ret_w2);
14117f127d42SJens Wiklander }
14121b302ac0SJens Wiklander out:
1413fe513722SJelle Sels spmc_set_args(args, ret_fid, ret_w1, ret_w2, ret_w3, 0, 0);
14141b302ac0SJens Wiklander }
14151b302ac0SJens Wiklander
get_frag_state(uint64_t global_handle)14161b302ac0SJens Wiklander static struct mem_frag_state *get_frag_state(uint64_t global_handle)
14171b302ac0SJens Wiklander {
14181b302ac0SJens Wiklander struct mem_frag_state *s = NULL;
14191b302ac0SJens Wiklander
14201b302ac0SJens Wiklander SLIST_FOREACH(s, &frag_state_head, link)
142100338334SJens Wiklander if (mobj_ffa_get_cookie(s->op.mf) == global_handle)
14221b302ac0SJens Wiklander return s;
14231b302ac0SJens Wiklander
14241b302ac0SJens Wiklander return NULL;
14251b302ac0SJens Wiklander }
14261b302ac0SJens Wiklander
handle_mem_frag_tx(struct thread_smc_1_2_regs * args,struct ffa_rxtx * rxtx)1427d17db2afSJens Wiklander static void handle_mem_frag_tx(struct thread_smc_1_2_regs *args,
1428cf133f37SJelle Sels struct ffa_rxtx *rxtx)
14291b302ac0SJens Wiklander {
1430a65dd3a6SJens Wiklander uint64_t global_handle = reg_pair_to_64(args->a2, args->a1);
1431a65dd3a6SJens Wiklander size_t flen = args->a3;
1432a65dd3a6SJens Wiklander uint32_t endpoint_id = args->a4;
14331b302ac0SJens Wiklander struct mem_frag_state *s = NULL;
14341b302ac0SJens Wiklander tee_mm_entry_t *mm = NULL;
14351b302ac0SJens Wiklander unsigned int page_count = 0;
14361b302ac0SJens Wiklander void *buf = NULL;
14371b302ac0SJens Wiklander uint32_t ret_w1 = 0;
14381b302ac0SJens Wiklander uint32_t ret_w2 = 0;
14391b302ac0SJens Wiklander uint32_t ret_w3 = 0;
14401b302ac0SJens Wiklander uint32_t ret_fid = 0;
1441a65dd3a6SJens Wiklander int rc = 0;
1442a65dd3a6SJens Wiklander
1443a65dd3a6SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
1444a65dd3a6SJens Wiklander uint16_t guest_id = endpoint_id >> 16;
1445a65dd3a6SJens Wiklander
1446a65dd3a6SJens Wiklander if (!guest_id || virt_set_guest(guest_id)) {
1447a65dd3a6SJens Wiklander rc = FFA_INVALID_PARAMETERS;
1448a65dd3a6SJens Wiklander goto out_set_rc;
1449a65dd3a6SJens Wiklander }
1450a65dd3a6SJens Wiklander }
14511b302ac0SJens Wiklander
14521b302ac0SJens Wiklander /*
14531b302ac0SJens Wiklander * Currently we're only doing this for fragmented FFA_MEM_SHARE_*
14541b302ac0SJens Wiklander * requests.
14551b302ac0SJens Wiklander */
14561b302ac0SJens Wiklander
1457cf133f37SJelle Sels cpu_spin_lock(&rxtx->spinlock);
14581b302ac0SJens Wiklander
14591b302ac0SJens Wiklander s = get_frag_state(global_handle);
14601b302ac0SJens Wiklander if (!s) {
14611b302ac0SJens Wiklander rc = FFA_INVALID_PARAMETERS;
14621b302ac0SJens Wiklander goto out;
14631b302ac0SJens Wiklander }
14641b302ac0SJens Wiklander
14651b302ac0SJens Wiklander mm = s->mm;
14661b302ac0SJens Wiklander if (mm) {
14671b302ac0SJens Wiklander if (flen > tee_mm_get_bytes(mm)) {
14681b302ac0SJens Wiklander rc = FFA_INVALID_PARAMETERS;
14691b302ac0SJens Wiklander goto out;
14701b302ac0SJens Wiklander }
147100338334SJens Wiklander page_count = s->op.page_count;
14721b302ac0SJens Wiklander buf = (void *)tee_mm_get_smem(mm);
14731b302ac0SJens Wiklander } else {
1474cf133f37SJelle Sels if (flen > rxtx->size) {
14751b302ac0SJens Wiklander rc = FFA_INVALID_PARAMETERS;
14761b302ac0SJens Wiklander goto out;
14771b302ac0SJens Wiklander }
1478cf133f37SJelle Sels buf = rxtx->rx;
14791b302ac0SJens Wiklander }
14801b302ac0SJens Wiklander
148100338334SJens Wiklander rc = add_mem_op_frag(s, buf, flen);
14821b302ac0SJens Wiklander out:
1483b65298cdSImre Kis if (IS_ENABLED(CFG_NS_VIRTUALIZATION))
1484a65dd3a6SJens Wiklander virt_unset_guest();
1485b65298cdSImre Kis
1486cf133f37SJelle Sels cpu_spin_unlock(&rxtx->spinlock);
14871b302ac0SJens Wiklander
14881b302ac0SJens Wiklander if (rc <= 0 && mm) {
14891b302ac0SJens Wiklander core_mmu_unmap_pages(tee_mm_get_smem(mm), page_count);
14901b302ac0SJens Wiklander tee_mm_free(mm);
14911b302ac0SJens Wiklander }
14921b302ac0SJens Wiklander
1493a65dd3a6SJens Wiklander out_set_rc:
14941b302ac0SJens Wiklander if (rc < 0) {
14951b302ac0SJens Wiklander ret_fid = FFA_ERROR;
14961b302ac0SJens Wiklander ret_w2 = rc;
14971b302ac0SJens Wiklander } else if (rc > 0) {
14981b302ac0SJens Wiklander ret_fid = FFA_MEM_FRAG_RX;
14991b302ac0SJens Wiklander ret_w3 = rc;
15001b302ac0SJens Wiklander reg_pair_from_64(global_handle, &ret_w2, &ret_w1);
15011b302ac0SJens Wiklander } else {
15021b302ac0SJens Wiklander ret_fid = FFA_SUCCESS_32;
15031b302ac0SJens Wiklander reg_pair_from_64(global_handle, &ret_w3, &ret_w2);
15041b302ac0SJens Wiklander }
15051b302ac0SJens Wiklander
1506fe513722SJelle Sels spmc_set_args(args, ret_fid, ret_w1, ret_w2, ret_w3, 0, 0);
15071b302ac0SJens Wiklander }
15081b302ac0SJens Wiklander
handle_mem_reclaim(struct thread_smc_1_2_regs * args)1509d17db2afSJens Wiklander static void handle_mem_reclaim(struct thread_smc_1_2_regs *args)
15101b302ac0SJens Wiklander {
151105c6a763SJens Wiklander int rc = FFA_INVALID_PARAMETERS;
15121b302ac0SJens Wiklander uint64_t cookie = 0;
15131b302ac0SJens Wiklander
15141b302ac0SJens Wiklander if (args->a3 || args->a4 || args->a5 || args->a6 || args->a7)
15151b302ac0SJens Wiklander goto out;
15161b302ac0SJens Wiklander
15171b302ac0SJens Wiklander cookie = reg_pair_to_64(args->a2, args->a1);
1518a65dd3a6SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
1519a65dd3a6SJens Wiklander uint16_t guest_id = 0;
1520a65dd3a6SJens Wiklander
1521a65dd3a6SJens Wiklander if (cookie & FFA_MEMORY_HANDLE_HYPERVISOR_BIT) {
1522a65dd3a6SJens Wiklander guest_id = virt_find_guest_by_cookie(cookie);
1523a65dd3a6SJens Wiklander } else {
1524a65dd3a6SJens Wiklander guest_id = (cookie >> FFA_MEMORY_HANDLE_PRTN_SHIFT) &
1525a65dd3a6SJens Wiklander FFA_MEMORY_HANDLE_PRTN_MASK;
1526a65dd3a6SJens Wiklander }
15274078bcdeSJens Wiklander if (!guest_id)
1528a65dd3a6SJens Wiklander goto out;
15294078bcdeSJens Wiklander if (virt_set_guest(guest_id)) {
15304078bcdeSJens Wiklander if (!virt_reclaim_cookie_from_destroyed_guest(guest_id,
15314078bcdeSJens Wiklander cookie))
15324078bcdeSJens Wiklander rc = FFA_OK;
15334078bcdeSJens Wiklander goto out;
15344078bcdeSJens Wiklander }
1535a65dd3a6SJens Wiklander }
1536a65dd3a6SJens Wiklander
15371b302ac0SJens Wiklander switch (mobj_ffa_sel1_spmc_reclaim(cookie)) {
15381b302ac0SJens Wiklander case TEE_SUCCESS:
153905c6a763SJens Wiklander rc = FFA_OK;
15401b302ac0SJens Wiklander break;
15411b302ac0SJens Wiklander case TEE_ERROR_ITEM_NOT_FOUND:
15421b302ac0SJens Wiklander DMSG("cookie %#"PRIx64" not found", cookie);
154305c6a763SJens Wiklander rc = FFA_INVALID_PARAMETERS;
15441b302ac0SJens Wiklander break;
15451b302ac0SJens Wiklander default:
15461b302ac0SJens Wiklander DMSG("cookie %#"PRIx64" busy", cookie);
154705c6a763SJens Wiklander rc = FFA_DENIED;
15481b302ac0SJens Wiklander break;
15491b302ac0SJens Wiklander }
1550a65dd3a6SJens Wiklander
1551b65298cdSImre Kis if (IS_ENABLED(CFG_NS_VIRTUALIZATION))
1552a65dd3a6SJens Wiklander virt_unset_guest();
1553a65dd3a6SJens Wiklander
15541b302ac0SJens Wiklander out:
155505c6a763SJens Wiklander set_simple_ret_val(args, rc);
15561b302ac0SJens Wiklander }
15572e02a737SJens Wiklander
handle_notification_bitmap_create(struct thread_smc_1_2_regs * args)1558d17db2afSJens Wiklander static void handle_notification_bitmap_create(struct thread_smc_1_2_regs *args)
15592e02a737SJens Wiklander {
15602e02a737SJens Wiklander uint32_t ret_val = FFA_INVALID_PARAMETERS;
15612e02a737SJens Wiklander uint32_t ret_fid = FFA_ERROR;
15622e02a737SJens Wiklander uint32_t old_itr_status = 0;
15632e02a737SJens Wiklander
15642e02a737SJens Wiklander if (!FFA_TARGET_INFO_GET_SP_ID(args->a1) && !args->a3 && !args->a4 &&
15652e02a737SJens Wiklander !args->a5 && !args->a6 && !args->a7) {
1566f6dcf234SJens Wiklander struct guest_partition *prtn = NULL;
1567f6dcf234SJens Wiklander struct notif_vm_bitmap *nvb = NULL;
15682e02a737SJens Wiklander uint16_t vm_id = args->a1;
15692e02a737SJens Wiklander
1570f6dcf234SJens Wiklander prtn = virt_get_guest(vm_id);
1571f6dcf234SJens Wiklander nvb = get_notif_vm_bitmap(prtn, vm_id);
1572f6dcf234SJens Wiklander if (!nvb) {
1573f6dcf234SJens Wiklander ret_val = FFA_INVALID_PARAMETERS;
1574f6dcf234SJens Wiklander goto out_virt_put;
1575f6dcf234SJens Wiklander }
1576f6dcf234SJens Wiklander
15772e02a737SJens Wiklander old_itr_status = cpu_spin_lock_xsave(&spmc_notif_lock);
15782e02a737SJens Wiklander
1579f6dcf234SJens Wiklander if (nvb->initialized) {
15802e02a737SJens Wiklander ret_val = FFA_DENIED;
1581f6dcf234SJens Wiklander goto out_unlock;
15822e02a737SJens Wiklander }
15832e02a737SJens Wiklander
1584f6dcf234SJens Wiklander nvb->initialized = true;
1585f6dcf234SJens Wiklander nvb->do_bottom_half_value = -1;
1586f6dcf234SJens Wiklander ret_val = FFA_OK;
1587f6dcf234SJens Wiklander ret_fid = FFA_SUCCESS_32;
1588f6dcf234SJens Wiklander out_unlock:
15892e02a737SJens Wiklander cpu_spin_unlock_xrestore(&spmc_notif_lock, old_itr_status);
1590f6dcf234SJens Wiklander out_virt_put:
1591f6dcf234SJens Wiklander virt_put_guest(prtn);
15922e02a737SJens Wiklander }
15932e02a737SJens Wiklander
15942e02a737SJens Wiklander spmc_set_args(args, ret_fid, 0, ret_val, 0, 0, 0);
15952e02a737SJens Wiklander }
15962e02a737SJens Wiklander
handle_notification_bitmap_destroy(struct thread_smc_1_2_regs * args)1597d17db2afSJens Wiklander static void handle_notification_bitmap_destroy(struct thread_smc_1_2_regs *args)
15982e02a737SJens Wiklander {
15992e02a737SJens Wiklander uint32_t ret_val = FFA_INVALID_PARAMETERS;
16002e02a737SJens Wiklander uint32_t ret_fid = FFA_ERROR;
16012e02a737SJens Wiklander uint32_t old_itr_status = 0;
16022e02a737SJens Wiklander
16032e02a737SJens Wiklander if (!FFA_TARGET_INFO_GET_SP_ID(args->a1) && !args->a3 && !args->a4 &&
16042e02a737SJens Wiklander !args->a5 && !args->a6 && !args->a7) {
1605f6dcf234SJens Wiklander struct guest_partition *prtn = NULL;
1606f6dcf234SJens Wiklander struct notif_vm_bitmap *nvb = NULL;
16072e02a737SJens Wiklander uint16_t vm_id = args->a1;
16082e02a737SJens Wiklander
1609f6dcf234SJens Wiklander prtn = virt_get_guest(vm_id);
1610f6dcf234SJens Wiklander nvb = get_notif_vm_bitmap(prtn, vm_id);
1611f6dcf234SJens Wiklander if (!nvb) {
1612f6dcf234SJens Wiklander ret_val = FFA_INVALID_PARAMETERS;
1613f6dcf234SJens Wiklander goto out_virt_put;
1614f6dcf234SJens Wiklander }
1615f6dcf234SJens Wiklander
16162e02a737SJens Wiklander old_itr_status = cpu_spin_lock_xsave(&spmc_notif_lock);
16172e02a737SJens Wiklander
1618f6dcf234SJens Wiklander if (nvb->pending || nvb->bound) {
16192e02a737SJens Wiklander ret_val = FFA_DENIED;
1620f6dcf234SJens Wiklander goto out_unlock;
16212e02a737SJens Wiklander }
16222e02a737SJens Wiklander
1623f6dcf234SJens Wiklander memset(nvb, 0, sizeof(*nvb));
1624f6dcf234SJens Wiklander ret_val = FFA_OK;
1625f6dcf234SJens Wiklander ret_fid = FFA_SUCCESS_32;
1626f6dcf234SJens Wiklander out_unlock:
16272e02a737SJens Wiklander cpu_spin_unlock_xrestore(&spmc_notif_lock, old_itr_status);
1628f6dcf234SJens Wiklander out_virt_put:
1629f6dcf234SJens Wiklander virt_put_guest(prtn);
16302e02a737SJens Wiklander }
16312e02a737SJens Wiklander
16322e02a737SJens Wiklander spmc_set_args(args, ret_fid, 0, ret_val, 0, 0, 0);
16332e02a737SJens Wiklander }
16342e02a737SJens Wiklander
handle_notification_bind(struct thread_smc_1_2_regs * args)1635d17db2afSJens Wiklander static void handle_notification_bind(struct thread_smc_1_2_regs *args)
16362e02a737SJens Wiklander {
16372e02a737SJens Wiklander uint32_t ret_val = FFA_INVALID_PARAMETERS;
1638f6dcf234SJens Wiklander struct guest_partition *prtn = NULL;
1639f6dcf234SJens Wiklander struct notif_vm_bitmap *nvb = NULL;
16402e02a737SJens Wiklander uint32_t ret_fid = FFA_ERROR;
16412e02a737SJens Wiklander uint32_t old_itr_status = 0;
16422e02a737SJens Wiklander uint64_t bitmap = 0;
16432e02a737SJens Wiklander uint16_t vm_id = 0;
16442e02a737SJens Wiklander
16452e02a737SJens Wiklander if (args->a5 || args->a6 || args->a7)
16462e02a737SJens Wiklander goto out;
16472e02a737SJens Wiklander if (args->a2) {
1648f6dcf234SJens Wiklander /* We only deal with global notifications */
1649f6dcf234SJens Wiklander ret_val = FFA_DENIED;
16502e02a737SJens Wiklander goto out;
16512e02a737SJens Wiklander }
16522e02a737SJens Wiklander
16532e02a737SJens Wiklander /* The destination of the eventual notification */
16542e02a737SJens Wiklander vm_id = FFA_DST(args->a1);
16552e02a737SJens Wiklander bitmap = reg_pair_to_64(args->a4, args->a3);
16562e02a737SJens Wiklander
1657f6dcf234SJens Wiklander prtn = virt_get_guest(vm_id);
1658f6dcf234SJens Wiklander nvb = get_notif_vm_bitmap(prtn, vm_id);
1659f6dcf234SJens Wiklander if (!nvb) {
1660f6dcf234SJens Wiklander ret_val = FFA_INVALID_PARAMETERS;
1661f6dcf234SJens Wiklander goto out_virt_put;
1662f6dcf234SJens Wiklander }
1663f6dcf234SJens Wiklander
16642e02a737SJens Wiklander old_itr_status = cpu_spin_lock_xsave(&spmc_notif_lock);
16652e02a737SJens Wiklander
1666f6dcf234SJens Wiklander if ((bitmap & nvb->bound)) {
16672e02a737SJens Wiklander ret_val = FFA_DENIED;
16682e02a737SJens Wiklander } else {
1669f6dcf234SJens Wiklander nvb->bound |= bitmap;
16702e02a737SJens Wiklander ret_val = FFA_OK;
16712e02a737SJens Wiklander ret_fid = FFA_SUCCESS_32;
16722e02a737SJens Wiklander }
16732e02a737SJens Wiklander
16742e02a737SJens Wiklander cpu_spin_unlock_xrestore(&spmc_notif_lock, old_itr_status);
1675f6dcf234SJens Wiklander out_virt_put:
1676f6dcf234SJens Wiklander virt_put_guest(prtn);
16772e02a737SJens Wiklander out:
16782e02a737SJens Wiklander spmc_set_args(args, ret_fid, 0, ret_val, 0, 0, 0);
16792e02a737SJens Wiklander }
16802e02a737SJens Wiklander
handle_notification_unbind(struct thread_smc_1_2_regs * args)1681d17db2afSJens Wiklander static void handle_notification_unbind(struct thread_smc_1_2_regs *args)
16822e02a737SJens Wiklander {
16832e02a737SJens Wiklander uint32_t ret_val = FFA_INVALID_PARAMETERS;
1684f6dcf234SJens Wiklander struct guest_partition *prtn = NULL;
1685f6dcf234SJens Wiklander struct notif_vm_bitmap *nvb = NULL;
16862e02a737SJens Wiklander uint32_t ret_fid = FFA_ERROR;
16872e02a737SJens Wiklander uint32_t old_itr_status = 0;
16882e02a737SJens Wiklander uint64_t bitmap = 0;
16892e02a737SJens Wiklander uint16_t vm_id = 0;
16902e02a737SJens Wiklander
16912e02a737SJens Wiklander if (args->a2 || args->a5 || args->a6 || args->a7)
16922e02a737SJens Wiklander goto out;
16932e02a737SJens Wiklander
16942e02a737SJens Wiklander /* The destination of the eventual notification */
16952e02a737SJens Wiklander vm_id = FFA_DST(args->a1);
16962e02a737SJens Wiklander bitmap = reg_pair_to_64(args->a4, args->a3);
16972e02a737SJens Wiklander
1698f6dcf234SJens Wiklander prtn = virt_get_guest(vm_id);
1699f6dcf234SJens Wiklander nvb = get_notif_vm_bitmap(prtn, vm_id);
1700f6dcf234SJens Wiklander if (!nvb) {
1701f6dcf234SJens Wiklander ret_val = FFA_INVALID_PARAMETERS;
1702f6dcf234SJens Wiklander goto out_virt_put;
1703f6dcf234SJens Wiklander }
1704f6dcf234SJens Wiklander
17052e02a737SJens Wiklander old_itr_status = cpu_spin_lock_xsave(&spmc_notif_lock);
17062e02a737SJens Wiklander
1707f6dcf234SJens Wiklander if (bitmap & nvb->pending) {
17082e02a737SJens Wiklander ret_val = FFA_DENIED;
17092e02a737SJens Wiklander } else {
1710f6dcf234SJens Wiklander nvb->bound &= ~bitmap;
17112e02a737SJens Wiklander ret_val = FFA_OK;
17122e02a737SJens Wiklander ret_fid = FFA_SUCCESS_32;
17132e02a737SJens Wiklander }
17142e02a737SJens Wiklander
17152e02a737SJens Wiklander cpu_spin_unlock_xrestore(&spmc_notif_lock, old_itr_status);
1716f6dcf234SJens Wiklander out_virt_put:
1717f6dcf234SJens Wiklander virt_put_guest(prtn);
17182e02a737SJens Wiklander out:
17192e02a737SJens Wiklander spmc_set_args(args, ret_fid, 0, ret_val, 0, 0, 0);
17202e02a737SJens Wiklander }
17212e02a737SJens Wiklander
handle_notification_get(struct thread_smc_1_2_regs * args)1722d17db2afSJens Wiklander static void handle_notification_get(struct thread_smc_1_2_regs *args)
17232e02a737SJens Wiklander {
17242e02a737SJens Wiklander uint32_t w2 = FFA_INVALID_PARAMETERS;
1725f6dcf234SJens Wiklander struct guest_partition *prtn = NULL;
1726f6dcf234SJens Wiklander struct notif_vm_bitmap *nvb = NULL;
17272e02a737SJens Wiklander uint32_t ret_fid = FFA_ERROR;
17282e02a737SJens Wiklander uint32_t old_itr_status = 0;
17292e02a737SJens Wiklander uint16_t vm_id = 0;
17302e02a737SJens Wiklander uint32_t w3 = 0;
17312e02a737SJens Wiklander
17322e02a737SJens Wiklander if (args->a5 || args->a6 || args->a7)
17332e02a737SJens Wiklander goto out;
17342e02a737SJens Wiklander if (!(args->a2 & 0x1)) {
17352e02a737SJens Wiklander ret_fid = FFA_SUCCESS_32;
17362e02a737SJens Wiklander w2 = 0;
17372e02a737SJens Wiklander goto out;
17382e02a737SJens Wiklander }
1739b59e43feSJens Wiklander vm_id = FFA_DST(args->a1);
17402e02a737SJens Wiklander
1741f6dcf234SJens Wiklander prtn = virt_get_guest(vm_id);
1742f6dcf234SJens Wiklander nvb = get_notif_vm_bitmap(prtn, vm_id);
1743f6dcf234SJens Wiklander if (!nvb)
1744f6dcf234SJens Wiklander goto out_virt_put;
1745f6dcf234SJens Wiklander
17462e02a737SJens Wiklander old_itr_status = cpu_spin_lock_xsave(&spmc_notif_lock);
17472e02a737SJens Wiklander
1748f6dcf234SJens Wiklander reg_pair_from_64(nvb->pending, &w3, &w2);
1749f6dcf234SJens Wiklander nvb->pending = 0;
17502e02a737SJens Wiklander ret_fid = FFA_SUCCESS_32;
17512e02a737SJens Wiklander
17522e02a737SJens Wiklander cpu_spin_unlock_xrestore(&spmc_notif_lock, old_itr_status);
1753f6dcf234SJens Wiklander out_virt_put:
1754f6dcf234SJens Wiklander virt_put_guest(prtn);
17552e02a737SJens Wiklander out:
17562e02a737SJens Wiklander spmc_set_args(args, ret_fid, 0, w2, w3, 0, 0);
17572e02a737SJens Wiklander }
17582e02a737SJens Wiklander
1759f6dcf234SJens Wiklander struct notif_info_get_state {
1760d17db2afSJens Wiklander struct thread_smc_1_2_regs *args;
1761f6dcf234SJens Wiklander unsigned int ids_per_reg;
1762f6dcf234SJens Wiklander unsigned int ids_count;
1763f6dcf234SJens Wiklander unsigned int id_pos;
1764f6dcf234SJens Wiklander unsigned int count;
1765f6dcf234SJens Wiklander unsigned int max_list_count;
1766f6dcf234SJens Wiklander unsigned int list_count;
1767f6dcf234SJens Wiklander };
1768f6dcf234SJens Wiklander
add_id_in_regs(struct notif_info_get_state * state,uint16_t id)1769f6dcf234SJens Wiklander static bool add_id_in_regs(struct notif_info_get_state *state,
1770f6dcf234SJens Wiklander uint16_t id)
1771f6dcf234SJens Wiklander {
1772f6dcf234SJens Wiklander unsigned int reg_idx = state->id_pos / state->ids_per_reg + 3;
1773f6dcf234SJens Wiklander unsigned int reg_shift = (state->id_pos % state->ids_per_reg) * 16;
1774f6dcf234SJens Wiklander
1775f6dcf234SJens Wiklander if (reg_idx > 7)
1776f6dcf234SJens Wiklander return false;
1777f6dcf234SJens Wiklander
1778d17db2afSJens Wiklander state->args->a[reg_idx] &= ~SHIFT_U64(0xffff, reg_shift);
1779d17db2afSJens Wiklander state->args->a[reg_idx] |= (unsigned long)id << reg_shift;
1780f6dcf234SJens Wiklander
1781f6dcf234SJens Wiklander state->id_pos++;
1782f6dcf234SJens Wiklander state->count++;
1783f6dcf234SJens Wiklander return true;
1784f6dcf234SJens Wiklander }
1785f6dcf234SJens Wiklander
add_id_count(struct notif_info_get_state * state)1786f6dcf234SJens Wiklander static bool add_id_count(struct notif_info_get_state *state)
1787f6dcf234SJens Wiklander {
1788f6dcf234SJens Wiklander assert(state->list_count < state->max_list_count &&
1789f6dcf234SJens Wiklander state->count >= 1 && state->count <= 4);
1790f6dcf234SJens Wiklander
1791f6dcf234SJens Wiklander state->ids_count |= (state->count - 1) << (state->list_count * 2 + 12);
1792f6dcf234SJens Wiklander state->list_count++;
1793f6dcf234SJens Wiklander state->count = 0;
1794f6dcf234SJens Wiklander
1795f6dcf234SJens Wiklander return state->list_count < state->max_list_count;
1796f6dcf234SJens Wiklander }
1797f6dcf234SJens Wiklander
add_nvb_to_state(struct notif_info_get_state * state,uint16_t guest_id,struct notif_vm_bitmap * nvb)1798f6dcf234SJens Wiklander static bool add_nvb_to_state(struct notif_info_get_state *state,
1799f6dcf234SJens Wiklander uint16_t guest_id, struct notif_vm_bitmap *nvb)
1800f6dcf234SJens Wiklander {
1801f6dcf234SJens Wiklander if (!nvb->pending)
1802f6dcf234SJens Wiklander return true;
1803f6dcf234SJens Wiklander /*
1804f6dcf234SJens Wiklander * Add only the guest_id, meaning a global notification for this
1805f6dcf234SJens Wiklander * guest.
1806f6dcf234SJens Wiklander *
1807f6dcf234SJens Wiklander * If notifications for one or more specific vCPUs we'd add those
1808f6dcf234SJens Wiklander * before calling add_id_count(), but that's not supported.
1809f6dcf234SJens Wiklander */
1810f6dcf234SJens Wiklander return add_id_in_regs(state, guest_id) && add_id_count(state);
1811f6dcf234SJens Wiklander }
1812f6dcf234SJens Wiklander
handle_notification_info_get(struct thread_smc_1_2_regs * args)1813d17db2afSJens Wiklander static void handle_notification_info_get(struct thread_smc_1_2_regs *args)
18142e02a737SJens Wiklander {
1815f6dcf234SJens Wiklander struct notif_info_get_state state = { .args = args };
1816f6dcf234SJens Wiklander uint32_t ffa_res = FFA_INVALID_PARAMETERS;
1817f6dcf234SJens Wiklander struct guest_partition *prtn = NULL;
1818f6dcf234SJens Wiklander struct notif_vm_bitmap *nvb = NULL;
1819f6dcf234SJens Wiklander uint32_t more_pending_flag = 0;
1820f6dcf234SJens Wiklander uint32_t itr_state = 0;
1821f6dcf234SJens Wiklander uint16_t guest_id = 0;
18222e02a737SJens Wiklander
18232e02a737SJens Wiklander if (args->a1 || args->a2 || args->a3 || args->a4 || args->a5 ||
18242e02a737SJens Wiklander args->a6 || args->a7)
1825f6dcf234SJens Wiklander goto err;
18262e02a737SJens Wiklander
1827f6dcf234SJens Wiklander if (OPTEE_SMC_IS_64(args->a0)) {
1828f6dcf234SJens Wiklander spmc_set_args(args, FFA_SUCCESS_64, 0, 0, 0, 0, 0);
1829f6dcf234SJens Wiklander state.ids_per_reg = 4;
1830f6dcf234SJens Wiklander state.max_list_count = 31;
1831f6dcf234SJens Wiklander } else {
1832f6dcf234SJens Wiklander spmc_set_args(args, FFA_SUCCESS_32, 0, 0, 0, 0, 0);
1833f6dcf234SJens Wiklander state.ids_per_reg = 2;
1834f6dcf234SJens Wiklander state.max_list_count = 15;
1835f6dcf234SJens Wiklander }
18362e02a737SJens Wiklander
1837f6dcf234SJens Wiklander while (true) {
18382e02a737SJens Wiklander /*
1839f6dcf234SJens Wiklander * With NS-Virtualization we need to go through all
1840f6dcf234SJens Wiklander * partitions to collect the notification bitmaps, without
1841f6dcf234SJens Wiklander * we just check the only notification bitmap we have.
18422e02a737SJens Wiklander */
1843f6dcf234SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
1844f6dcf234SJens Wiklander prtn = virt_next_guest(prtn);
1845f6dcf234SJens Wiklander if (!prtn)
1846f6dcf234SJens Wiklander break;
1847f6dcf234SJens Wiklander guest_id = virt_get_guest_id(prtn);
1848f6dcf234SJens Wiklander }
1849f6dcf234SJens Wiklander nvb = get_notif_vm_bitmap(prtn, guest_id);
1850f6dcf234SJens Wiklander
1851f6dcf234SJens Wiklander itr_state = cpu_spin_lock_xsave(&spmc_notif_lock);
1852f6dcf234SJens Wiklander if (!add_nvb_to_state(&state, guest_id, nvb))
1853f6dcf234SJens Wiklander more_pending_flag = BIT(0);
1854f6dcf234SJens Wiklander cpu_spin_unlock_xrestore(&spmc_notif_lock, itr_state);
1855f6dcf234SJens Wiklander
1856f6dcf234SJens Wiklander if (!IS_ENABLED(CFG_NS_VIRTUALIZATION) || more_pending_flag)
1857f6dcf234SJens Wiklander break;
1858f6dcf234SJens Wiklander }
1859f6dcf234SJens Wiklander virt_put_guest(prtn);
1860f6dcf234SJens Wiklander
1861f6dcf234SJens Wiklander if (!state.id_pos) {
1862f6dcf234SJens Wiklander ffa_res = FFA_NO_DATA;
1863f6dcf234SJens Wiklander goto err;
1864f6dcf234SJens Wiklander }
1865f6dcf234SJens Wiklander args->a2 = (state.list_count << FFA_NOTIF_INFO_GET_ID_COUNT_SHIFT) |
1866f6dcf234SJens Wiklander (state.ids_count << FFA_NOTIF_INFO_GET_ID_LIST_SHIFT) |
1867f6dcf234SJens Wiklander more_pending_flag;
1868f6dcf234SJens Wiklander return;
1869f6dcf234SJens Wiklander err:
1870f6dcf234SJens Wiklander spmc_set_args(args, FFA_ERROR, 0, ffa_res, 0, 0, 0);
18712e02a737SJens Wiklander }
18722e02a737SJens Wiklander
thread_spmc_set_async_notif_intid(int intid)18732e02a737SJens Wiklander void thread_spmc_set_async_notif_intid(int intid)
18742e02a737SJens Wiklander {
18752e02a737SJens Wiklander assert(interrupt_can_raise_sgi(interrupt_get_main_chip()));
18762e02a737SJens Wiklander notif_intid = intid;
18772e02a737SJens Wiklander spmc_notif_is_ready = true;
18782e02a737SJens Wiklander DMSG("Asynchronous notifications are ready");
18792e02a737SJens Wiklander }
18802e02a737SJens Wiklander
notif_send_async(uint32_t value,uint16_t guest_id)1881f6dcf234SJens Wiklander void notif_send_async(uint32_t value, uint16_t guest_id)
18822e02a737SJens Wiklander {
1883f6dcf234SJens Wiklander struct guest_partition *prtn = NULL;
1884f6dcf234SJens Wiklander struct notif_vm_bitmap *nvb = NULL;
18852e02a737SJens Wiklander uint32_t old_itr_status = 0;
18862e02a737SJens Wiklander
1887f6dcf234SJens Wiklander prtn = virt_get_guest(guest_id);
1888f6dcf234SJens Wiklander nvb = get_notif_vm_bitmap(prtn, guest_id);
1889f6dcf234SJens Wiklander
1890f6dcf234SJens Wiklander if (nvb) {
18912e02a737SJens Wiklander old_itr_status = cpu_spin_lock_xsave(&spmc_notif_lock);
1892f6dcf234SJens Wiklander assert(value == NOTIF_VALUE_DO_BOTTOM_HALF &&
1893f6dcf234SJens Wiklander spmc_notif_is_ready && nvb->do_bottom_half_value >= 0 &&
1894f6dcf234SJens Wiklander notif_intid >= 0);
1895f6dcf234SJens Wiklander nvb->pending |= BIT64(nvb->do_bottom_half_value);
18962e02a737SJens Wiklander interrupt_raise_sgi(interrupt_get_main_chip(), notif_intid,
18972e02a737SJens Wiklander ITR_CPU_MASK_TO_THIS_CPU);
18982e02a737SJens Wiklander cpu_spin_unlock_xrestore(&spmc_notif_lock, old_itr_status);
18992e02a737SJens Wiklander }
1900f6dcf234SJens Wiklander
1901f6dcf234SJens Wiklander virt_put_guest(prtn);
1902f6dcf234SJens Wiklander }
19032e02a737SJens Wiklander #else
notif_send_async(uint32_t value,uint16_t guest_id)1904f6dcf234SJens Wiklander void notif_send_async(uint32_t value, uint16_t guest_id)
19052e02a737SJens Wiklander {
1906f6dcf234SJens Wiklander struct guest_partition *prtn = NULL;
1907f6dcf234SJens Wiklander struct notif_vm_bitmap *nvb = NULL;
190855cd94d1SJens Wiklander /* global notification, delay notification interrupt */
190955cd94d1SJens Wiklander uint32_t flags = BIT32(1);
191055cd94d1SJens Wiklander int res = 0;
191155cd94d1SJens Wiklander
1912f6dcf234SJens Wiklander prtn = virt_get_guest(guest_id);
1913f6dcf234SJens Wiklander nvb = get_notif_vm_bitmap(prtn, guest_id);
1914f6dcf234SJens Wiklander
1915f6dcf234SJens Wiklander if (nvb) {
1916f6dcf234SJens Wiklander assert(value == NOTIF_VALUE_DO_BOTTOM_HALF &&
1917f6dcf234SJens Wiklander spmc_notif_is_ready && nvb->do_bottom_half_value >= 0);
1918ecf08061SJens Wiklander res = ffa_set_notification(guest_id, optee_core_lsp.sp_id,
1919ecf08061SJens Wiklander flags,
1920f6dcf234SJens Wiklander BIT64(nvb->do_bottom_half_value));
192155cd94d1SJens Wiklander if (res) {
192255cd94d1SJens Wiklander EMSG("notification set failed with error %d", res);
19232e02a737SJens Wiklander panic();
19242e02a737SJens Wiklander }
192555cd94d1SJens Wiklander }
1926f6dcf234SJens Wiklander
1927f6dcf234SJens Wiklander virt_put_guest(prtn);
1928f6dcf234SJens Wiklander }
1929fb19e98eSJens Wiklander #endif
19301b302ac0SJens Wiklander
19311b302ac0SJens Wiklander /* Only called from assembly */
1932d17db2afSJens Wiklander void thread_spmc_msg_recv(struct thread_smc_1_2_regs *args);
thread_spmc_msg_recv(struct thread_smc_1_2_regs * args)1933d17db2afSJens Wiklander void thread_spmc_msg_recv(struct thread_smc_1_2_regs *args)
19341b302ac0SJens Wiklander {
19351b302ac0SJens Wiklander assert((thread_get_exceptions() & THREAD_EXCP_ALL) == THREAD_EXCP_ALL);
19361b302ac0SJens Wiklander switch (args->a0) {
1937fb19e98eSJens Wiklander #if defined(CFG_CORE_SEL1_SPMC)
19381b302ac0SJens Wiklander case FFA_FEATURES:
19391b302ac0SJens Wiklander handle_features(args);
19401b302ac0SJens Wiklander break;
1941412d46f6SJens Wiklander case FFA_SPM_ID_GET:
194219ad526cSBalint Dobszay spmc_handle_spm_id_get(args);
1943412d46f6SJens Wiklander break;
19441b302ac0SJens Wiklander #ifdef ARM64
19451b302ac0SJens Wiklander case FFA_RXTX_MAP_64:
19461b302ac0SJens Wiklander #endif
19471b302ac0SJens Wiklander case FFA_RXTX_MAP_32:
1948f49f23f7SJens Wiklander spmc_handle_rxtx_map(args, &my_rxtx);
19491b302ac0SJens Wiklander break;
19501b302ac0SJens Wiklander case FFA_RXTX_UNMAP:
1951f49f23f7SJens Wiklander spmc_handle_rxtx_unmap(args, &my_rxtx);
19521b302ac0SJens Wiklander break;
19531b302ac0SJens Wiklander case FFA_RX_RELEASE:
1954f49f23f7SJens Wiklander spmc_handle_rx_release(args, &my_rxtx);
19551b302ac0SJens Wiklander break;
19561b302ac0SJens Wiklander case FFA_PARTITION_INFO_GET:
1957f49f23f7SJens Wiklander spmc_handle_partition_info_get(args, &my_rxtx);
19581b302ac0SJens Wiklander break;
19594d028847SImre Kis case FFA_RUN:
19604d028847SImre Kis spmc_handle_run(args);
19614d028847SImre Kis break;
1962fb19e98eSJens Wiklander #endif /*CFG_CORE_SEL1_SPMC*/
19631b302ac0SJens Wiklander case FFA_INTERRUPT:
1964af06edb5SJens Wiklander if (IS_ENABLED(CFG_CORE_SEL1_SPMC))
1965af06edb5SJens Wiklander spmc_set_args(args, FFA_NORMAL_WORLD_RESUME, 0, 0, 0,
1966af06edb5SJens Wiklander 0, 0);
1967af06edb5SJens Wiklander else
196867fec989SJens Wiklander spmc_set_args(args, FFA_MSG_WAIT, 0, 0, 0, 0, 0);
19691b302ac0SJens Wiklander break;
197015da69cfSJelle Sels #ifdef ARM64
197115da69cfSJelle Sels case FFA_MSG_SEND_DIRECT_REQ_64:
197215da69cfSJelle Sels #endif
19731b302ac0SJens Wiklander case FFA_MSG_SEND_DIRECT_REQ_32:
1974ecf08061SJens Wiklander handle_direct_request(args);
19751b302ac0SJens Wiklander break;
1976fb19e98eSJens Wiklander #if defined(CFG_CORE_SEL1_SPMC)
19771b302ac0SJens Wiklander #ifdef ARM64
19781b302ac0SJens Wiklander case FFA_MEM_SHARE_64:
19791b302ac0SJens Wiklander #endif
19801b302ac0SJens Wiklander case FFA_MEM_SHARE_32:
198100338334SJens Wiklander #ifdef ARM64
198200338334SJens Wiklander case FFA_MEM_LEND_64:
198300338334SJens Wiklander #endif
198400338334SJens Wiklander case FFA_MEM_LEND_32:
198500338334SJens Wiklander handle_mem_op(args, &my_rxtx);
19861b302ac0SJens Wiklander break;
19871b302ac0SJens Wiklander case FFA_MEM_RECLAIM:
19887736c4e4SJelle Sels if (!IS_ENABLED(CFG_SECURE_PARTITION) ||
19897736c4e4SJelle Sels !ffa_mem_reclaim(args, NULL))
19901b302ac0SJens Wiklander handle_mem_reclaim(args);
19911b302ac0SJens Wiklander break;
19921b302ac0SJens Wiklander case FFA_MEM_FRAG_TX:
1993f49f23f7SJens Wiklander handle_mem_frag_tx(args, &my_rxtx);
19941b302ac0SJens Wiklander break;
19952e02a737SJens Wiklander case FFA_NOTIFICATION_BITMAP_CREATE:
19962e02a737SJens Wiklander handle_notification_bitmap_create(args);
19972e02a737SJens Wiklander break;
19982e02a737SJens Wiklander case FFA_NOTIFICATION_BITMAP_DESTROY:
19992e02a737SJens Wiklander handle_notification_bitmap_destroy(args);
20002e02a737SJens Wiklander break;
20012e02a737SJens Wiklander case FFA_NOTIFICATION_BIND:
20022e02a737SJens Wiklander handle_notification_bind(args);
20032e02a737SJens Wiklander break;
20042e02a737SJens Wiklander case FFA_NOTIFICATION_UNBIND:
20052e02a737SJens Wiklander handle_notification_unbind(args);
20062e02a737SJens Wiklander break;
20072e02a737SJens Wiklander case FFA_NOTIFICATION_GET:
20082e02a737SJens Wiklander handle_notification_get(args);
20092e02a737SJens Wiklander break;
20102e02a737SJens Wiklander #ifdef ARM64
20112e02a737SJens Wiklander case FFA_NOTIFICATION_INFO_GET_64:
20122e02a737SJens Wiklander #endif
20132e02a737SJens Wiklander case FFA_NOTIFICATION_INFO_GET_32:
20142e02a737SJens Wiklander handle_notification_info_get(args);
20152e02a737SJens Wiklander break;
2016fb19e98eSJens Wiklander #endif /*CFG_CORE_SEL1_SPMC*/
201717c54670SJens Wiklander case FFA_ERROR:
201817c54670SJens Wiklander EMSG("Cannot handle FFA_ERROR(%d)", (int)args->a2);
201917c54670SJens Wiklander if (!IS_ENABLED(CFG_CORE_SEL1_SPMC)) {
202017c54670SJens Wiklander /*
202117c54670SJens Wiklander * The SPMC will return an FFA_ERROR back so better
202217c54670SJens Wiklander * panic() now than flooding the log.
202317c54670SJens Wiklander */
202417c54670SJens Wiklander panic("FFA_ERROR from SPMC is fatal");
202517c54670SJens Wiklander }
202617c54670SJens Wiklander spmc_set_args(args, FFA_ERROR, FFA_PARAM_MBZ, FFA_NOT_SUPPORTED,
202717c54670SJens Wiklander FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ);
202817c54670SJens Wiklander break;
20291b302ac0SJens Wiklander default:
20301b302ac0SJens Wiklander EMSG("Unhandled FFA function ID %#"PRIx32, (uint32_t)args->a0);
203105c6a763SJens Wiklander set_simple_ret_val(args, FFA_NOT_SUPPORTED);
20321b302ac0SJens Wiklander }
20331b302ac0SJens Wiklander }
20341b302ac0SJens Wiklander
yielding_call_with_arg(uint64_t cookie,uint32_t offset)2035453d8327SJens Wiklander static TEE_Result yielding_call_with_arg(uint64_t cookie, uint32_t offset)
20361b302ac0SJens Wiklander {
2037c1bdf4fcSJens Wiklander size_t sz_rpc = OPTEE_MSG_GET_ARG_SIZE(THREAD_RPC_MAX_NUM_PARAMS);
2038c1bdf4fcSJens Wiklander struct thread_ctx *thr = threads + thread_get_id();
2039453d8327SJens Wiklander TEE_Result res = TEE_ERROR_BAD_PARAMETERS;
20401b302ac0SJens Wiklander struct optee_msg_arg *arg = NULL;
20411b302ac0SJens Wiklander struct mobj *mobj = NULL;
20421b302ac0SJens Wiklander uint32_t num_params = 0;
2043c1bdf4fcSJens Wiklander size_t sz = 0;
20441b302ac0SJens Wiklander
20451b302ac0SJens Wiklander mobj = mobj_ffa_get_by_cookie(cookie, 0);
20461b302ac0SJens Wiklander if (!mobj) {
20471b302ac0SJens Wiklander EMSG("Can't find cookie %#"PRIx64, cookie);
20481b302ac0SJens Wiklander return TEE_ERROR_BAD_PARAMETERS;
20491b302ac0SJens Wiklander }
20501b302ac0SJens Wiklander
2051453d8327SJens Wiklander res = mobj_inc_map(mobj);
2052453d8327SJens Wiklander if (res)
20531b302ac0SJens Wiklander goto out_put_mobj;
20541b302ac0SJens Wiklander
2055453d8327SJens Wiklander res = TEE_ERROR_BAD_PARAMETERS;
20569c4aaf67SJens Wiklander arg = mobj_get_va(mobj, offset, sizeof(*arg));
20571b302ac0SJens Wiklander if (!arg)
20581b302ac0SJens Wiklander goto out_dec_map;
20591b302ac0SJens Wiklander
20601b302ac0SJens Wiklander num_params = READ_ONCE(arg->num_params);
20611b302ac0SJens Wiklander if (num_params > OPTEE_MSG_MAX_NUM_PARAMS)
20621b302ac0SJens Wiklander goto out_dec_map;
20631b302ac0SJens Wiklander
2064c1bdf4fcSJens Wiklander sz = OPTEE_MSG_GET_ARG_SIZE(num_params);
20657267624eSJens Wiklander
20669c4aaf67SJens Wiklander thr->rpc_arg = mobj_get_va(mobj, offset + sz, sz_rpc);
20677267624eSJens Wiklander if (!thr->rpc_arg)
20681b302ac0SJens Wiklander goto out_dec_map;
20691b302ac0SJens Wiklander
2070a65dd3a6SJens Wiklander virt_on_stdcall();
2071453d8327SJens Wiklander res = tee_entry_std(arg, num_params);
20721b302ac0SJens Wiklander
2073c1bdf4fcSJens Wiklander thread_rpc_shm_cache_clear(&thr->shm_cache);
2074c1bdf4fcSJens Wiklander thr->rpc_arg = NULL;
2075897adff4SJens Wiklander
20761b302ac0SJens Wiklander out_dec_map:
20771b302ac0SJens Wiklander mobj_dec_map(mobj);
20781b302ac0SJens Wiklander out_put_mobj:
20791b302ac0SJens Wiklander mobj_put(mobj);
2080453d8327SJens Wiklander return res;
20811b302ac0SJens Wiklander }
20821b302ac0SJens Wiklander
20831b302ac0SJens Wiklander /*
20841b302ac0SJens Wiklander * Helper routine for the assembly function thread_std_smc_entry()
20851b302ac0SJens Wiklander *
2086593b94eeSJens Wiklander * Note: this function is weak just to make link_dummies_paged.c happy.
20871b302ac0SJens Wiklander */
__thread_std_smc_entry(uint32_t a0,uint32_t a1,uint32_t a2,uint32_t a3,uint32_t a4,uint32_t a5 __unused)20881b302ac0SJens Wiklander uint32_t __weak __thread_std_smc_entry(uint32_t a0, uint32_t a1,
20894107d2f9SJens Wiklander uint32_t a2, uint32_t a3,
2090c1bdf4fcSJens Wiklander uint32_t a4, uint32_t a5 __unused)
20911b302ac0SJens Wiklander {
20921b302ac0SJens Wiklander /*
20931b302ac0SJens Wiklander * Arguments are supplied from handle_yielding_call() as:
20941b302ac0SJens Wiklander * a0 <- w1
20951b302ac0SJens Wiklander * a1 <- w3
20961b302ac0SJens Wiklander * a2 <- w4
20971b302ac0SJens Wiklander * a3 <- w5
20984107d2f9SJens Wiklander * a4 <- w6
20994107d2f9SJens Wiklander * a5 <- w7
21001b302ac0SJens Wiklander */
21011b302ac0SJens Wiklander thread_get_tsd()->rpc_target_info = swap_src_dst(a0);
2102c1bdf4fcSJens Wiklander if (a1 == OPTEE_FFA_YIELDING_CALL_WITH_ARG)
2103c1bdf4fcSJens Wiklander return yielding_call_with_arg(reg_pair_to_64(a3, a2), a4);
21041b302ac0SJens Wiklander return FFA_DENIED;
21051b302ac0SJens Wiklander }
21061b302ac0SJens Wiklander
set_fmem(struct optee_msg_param * param,struct thread_param * tpm)21071b302ac0SJens Wiklander static bool set_fmem(struct optee_msg_param *param, struct thread_param *tpm)
21081b302ac0SJens Wiklander {
21091b302ac0SJens Wiklander uint64_t offs = tpm->u.memref.offs;
21101b302ac0SJens Wiklander
21111b302ac0SJens Wiklander param->attr = tpm->attr - THREAD_PARAM_ATTR_MEMREF_IN +
21121b302ac0SJens Wiklander OPTEE_MSG_ATTR_TYPE_FMEM_INPUT;
21131b302ac0SJens Wiklander
21141b302ac0SJens Wiklander param->u.fmem.offs_low = offs;
21151b302ac0SJens Wiklander param->u.fmem.offs_high = offs >> 32;
21161b302ac0SJens Wiklander if (param->u.fmem.offs_high != offs >> 32)
21171b302ac0SJens Wiklander return false;
21181b302ac0SJens Wiklander
21191b302ac0SJens Wiklander param->u.fmem.size = tpm->u.memref.size;
21201b302ac0SJens Wiklander if (tpm->u.memref.mobj) {
2121c1bdf4fcSJens Wiklander uint64_t cookie = mobj_get_cookie(tpm->u.memref.mobj);
2122c1bdf4fcSJens Wiklander
2123c1bdf4fcSJens Wiklander /* If a mobj is passed it better be one with a valid cookie. */
2124c1bdf4fcSJens Wiklander if (cookie == OPTEE_MSG_FMEM_INVALID_GLOBAL_ID)
21251b302ac0SJens Wiklander return false;
2126c1bdf4fcSJens Wiklander param->u.fmem.global_id = cookie;
21271b302ac0SJens Wiklander } else {
2128c1bdf4fcSJens Wiklander param->u.fmem.global_id = OPTEE_MSG_FMEM_INVALID_GLOBAL_ID;
21291b302ac0SJens Wiklander }
21301b302ac0SJens Wiklander
21311b302ac0SJens Wiklander return true;
21321b302ac0SJens Wiklander }
21331b302ac0SJens Wiklander
get_rpc_arg(uint32_t cmd,size_t num_params,struct thread_param * params,struct optee_msg_arg ** arg_ret)21341b302ac0SJens Wiklander static uint32_t get_rpc_arg(uint32_t cmd, size_t num_params,
21351b302ac0SJens Wiklander struct thread_param *params,
2136c1bdf4fcSJens Wiklander struct optee_msg_arg **arg_ret)
21371b302ac0SJens Wiklander {
21381b302ac0SJens Wiklander size_t sz = OPTEE_MSG_GET_ARG_SIZE(THREAD_RPC_MAX_NUM_PARAMS);
21391b302ac0SJens Wiklander struct thread_ctx *thr = threads + thread_get_id();
21401b302ac0SJens Wiklander struct optee_msg_arg *arg = thr->rpc_arg;
21411b302ac0SJens Wiklander
21421b302ac0SJens Wiklander if (num_params > THREAD_RPC_MAX_NUM_PARAMS)
21431b302ac0SJens Wiklander return TEE_ERROR_BAD_PARAMETERS;
21441b302ac0SJens Wiklander
21451b302ac0SJens Wiklander if (!arg) {
2146c1bdf4fcSJens Wiklander EMSG("rpc_arg not set");
2147c1bdf4fcSJens Wiklander return TEE_ERROR_GENERIC;
21481b302ac0SJens Wiklander }
21491b302ac0SJens Wiklander
21501b302ac0SJens Wiklander memset(arg, 0, sz);
21511b302ac0SJens Wiklander arg->cmd = cmd;
21521b302ac0SJens Wiklander arg->num_params = num_params;
21531b302ac0SJens Wiklander arg->ret = TEE_ERROR_GENERIC; /* in case value isn't updated */
21541b302ac0SJens Wiklander
21551b302ac0SJens Wiklander for (size_t n = 0; n < num_params; n++) {
21561b302ac0SJens Wiklander switch (params[n].attr) {
21571b302ac0SJens Wiklander case THREAD_PARAM_ATTR_NONE:
21581b302ac0SJens Wiklander arg->params[n].attr = OPTEE_MSG_ATTR_TYPE_NONE;
21591b302ac0SJens Wiklander break;
21601b302ac0SJens Wiklander case THREAD_PARAM_ATTR_VALUE_IN:
21611b302ac0SJens Wiklander case THREAD_PARAM_ATTR_VALUE_OUT:
21621b302ac0SJens Wiklander case THREAD_PARAM_ATTR_VALUE_INOUT:
21631b302ac0SJens Wiklander arg->params[n].attr = params[n].attr -
21641b302ac0SJens Wiklander THREAD_PARAM_ATTR_VALUE_IN +
21651b302ac0SJens Wiklander OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
21661b302ac0SJens Wiklander arg->params[n].u.value.a = params[n].u.value.a;
21671b302ac0SJens Wiklander arg->params[n].u.value.b = params[n].u.value.b;
21681b302ac0SJens Wiklander arg->params[n].u.value.c = params[n].u.value.c;
21691b302ac0SJens Wiklander break;
21701b302ac0SJens Wiklander case THREAD_PARAM_ATTR_MEMREF_IN:
21711b302ac0SJens Wiklander case THREAD_PARAM_ATTR_MEMREF_OUT:
21721b302ac0SJens Wiklander case THREAD_PARAM_ATTR_MEMREF_INOUT:
21731b302ac0SJens Wiklander if (!set_fmem(arg->params + n, params + n))
21741b302ac0SJens Wiklander return TEE_ERROR_BAD_PARAMETERS;
21751b302ac0SJens Wiklander break;
21761b302ac0SJens Wiklander default:
21771b302ac0SJens Wiklander return TEE_ERROR_BAD_PARAMETERS;
21781b302ac0SJens Wiklander }
21791b302ac0SJens Wiklander }
21801b302ac0SJens Wiklander
2181c1bdf4fcSJens Wiklander if (arg_ret)
21821b302ac0SJens Wiklander *arg_ret = arg;
21831b302ac0SJens Wiklander
21841b302ac0SJens Wiklander return TEE_SUCCESS;
21851b302ac0SJens Wiklander }
21861b302ac0SJens Wiklander
get_rpc_arg_res(struct optee_msg_arg * arg,size_t num_params,struct thread_param * params)21871b302ac0SJens Wiklander static uint32_t get_rpc_arg_res(struct optee_msg_arg *arg, size_t num_params,
21881b302ac0SJens Wiklander struct thread_param *params)
21891b302ac0SJens Wiklander {
21901b302ac0SJens Wiklander for (size_t n = 0; n < num_params; n++) {
21911b302ac0SJens Wiklander switch (params[n].attr) {
21921b302ac0SJens Wiklander case THREAD_PARAM_ATTR_VALUE_OUT:
21931b302ac0SJens Wiklander case THREAD_PARAM_ATTR_VALUE_INOUT:
21941b302ac0SJens Wiklander params[n].u.value.a = arg->params[n].u.value.a;
21951b302ac0SJens Wiklander params[n].u.value.b = arg->params[n].u.value.b;
21961b302ac0SJens Wiklander params[n].u.value.c = arg->params[n].u.value.c;
21971b302ac0SJens Wiklander break;
21981b302ac0SJens Wiklander case THREAD_PARAM_ATTR_MEMREF_OUT:
21991b302ac0SJens Wiklander case THREAD_PARAM_ATTR_MEMREF_INOUT:
22001b302ac0SJens Wiklander params[n].u.memref.size = arg->params[n].u.fmem.size;
22011b302ac0SJens Wiklander break;
22021b302ac0SJens Wiklander default:
22031b302ac0SJens Wiklander break;
22041b302ac0SJens Wiklander }
22051b302ac0SJens Wiklander }
22061b302ac0SJens Wiklander
22071b302ac0SJens Wiklander return arg->ret;
22081b302ac0SJens Wiklander }
22091b302ac0SJens Wiklander
thread_rpc_cmd(uint32_t cmd,size_t num_params,struct thread_param * params)22101b302ac0SJens Wiklander uint32_t thread_rpc_cmd(uint32_t cmd, size_t num_params,
22111b302ac0SJens Wiklander struct thread_param *params)
22121b302ac0SJens Wiklander {
22131b302ac0SJens Wiklander struct thread_rpc_arg rpc_arg = { .call = {
22141b302ac0SJens Wiklander .w1 = thread_get_tsd()->rpc_target_info,
22151b302ac0SJens Wiklander .w4 = OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD,
22161b302ac0SJens Wiklander },
22171b302ac0SJens Wiklander };
22181b302ac0SJens Wiklander struct optee_msg_arg *arg = NULL;
22191b302ac0SJens Wiklander uint32_t ret = 0;
22201b302ac0SJens Wiklander
2221c1bdf4fcSJens Wiklander ret = get_rpc_arg(cmd, num_params, params, &arg);
22221b302ac0SJens Wiklander if (ret)
22231b302ac0SJens Wiklander return ret;
22241b302ac0SJens Wiklander
22251b302ac0SJens Wiklander thread_rpc(&rpc_arg);
22261b302ac0SJens Wiklander
22271b302ac0SJens Wiklander return get_rpc_arg_res(arg, num_params, params);
22281b302ac0SJens Wiklander }
2229a9d0e06fSJens Wiklander
thread_rpc_free(unsigned int bt,uint64_t cookie,struct mobj * mobj)2230c1bdf4fcSJens Wiklander static void thread_rpc_free(unsigned int bt, uint64_t cookie, struct mobj *mobj)
2231a9d0e06fSJens Wiklander {
2232c1bdf4fcSJens Wiklander struct thread_rpc_arg rpc_arg = { .call = {
2233c1bdf4fcSJens Wiklander .w1 = thread_get_tsd()->rpc_target_info,
2234c1bdf4fcSJens Wiklander .w4 = OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD,
2235c1bdf4fcSJens Wiklander },
2236c1bdf4fcSJens Wiklander };
2237c1bdf4fcSJens Wiklander struct thread_param param = THREAD_PARAM_VALUE(IN, bt, cookie, 0);
2238c1bdf4fcSJens Wiklander uint32_t res2 = 0;
2239c1bdf4fcSJens Wiklander uint32_t res = 0;
2240c1bdf4fcSJens Wiklander
2241c1bdf4fcSJens Wiklander DMSG("freeing cookie %#"PRIx64, cookie);
2242c1bdf4fcSJens Wiklander
2243c1bdf4fcSJens Wiklander res = get_rpc_arg(OPTEE_RPC_CMD_SHM_FREE, 1, ¶m, NULL);
2244c1bdf4fcSJens Wiklander
2245c1bdf4fcSJens Wiklander mobj_put(mobj);
2246c1bdf4fcSJens Wiklander res2 = mobj_ffa_unregister_by_cookie(cookie);
2247c1bdf4fcSJens Wiklander if (res2)
2248c1bdf4fcSJens Wiklander DMSG("mobj_ffa_unregister_by_cookie(%#"PRIx64"): %#"PRIx32,
2249c1bdf4fcSJens Wiklander cookie, res2);
2250c1bdf4fcSJens Wiklander if (!res)
2251c1bdf4fcSJens Wiklander thread_rpc(&rpc_arg);
2252c1bdf4fcSJens Wiklander }
2253c1bdf4fcSJens Wiklander
thread_rpc_alloc(size_t size,size_t align,unsigned int bt)2254c1bdf4fcSJens Wiklander static struct mobj *thread_rpc_alloc(size_t size, size_t align, unsigned int bt)
2255c1bdf4fcSJens Wiklander {
2256c1bdf4fcSJens Wiklander struct thread_rpc_arg rpc_arg = { .call = {
2257c1bdf4fcSJens Wiklander .w1 = thread_get_tsd()->rpc_target_info,
2258c1bdf4fcSJens Wiklander .w4 = OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD,
2259c1bdf4fcSJens Wiklander },
2260c1bdf4fcSJens Wiklander };
2261c1bdf4fcSJens Wiklander struct thread_param param = THREAD_PARAM_VALUE(IN, bt, size, align);
2262c1bdf4fcSJens Wiklander struct optee_msg_arg *arg = NULL;
2263c1bdf4fcSJens Wiklander unsigned int internal_offset = 0;
2264c1bdf4fcSJens Wiklander struct mobj *mobj = NULL;
2265c1bdf4fcSJens Wiklander uint64_t cookie = 0;
2266c1bdf4fcSJens Wiklander
2267c1bdf4fcSJens Wiklander if (get_rpc_arg(OPTEE_RPC_CMD_SHM_ALLOC, 1, ¶m, &arg))
2268c1bdf4fcSJens Wiklander return NULL;
2269c1bdf4fcSJens Wiklander
2270c1bdf4fcSJens Wiklander thread_rpc(&rpc_arg);
2271c1bdf4fcSJens Wiklander
2272c1bdf4fcSJens Wiklander if (arg->num_params != 1 ||
2273c1bdf4fcSJens Wiklander arg->params->attr != OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT)
2274c1bdf4fcSJens Wiklander return NULL;
2275c1bdf4fcSJens Wiklander
22762ac13956SJens Wiklander internal_offset = READ_ONCE(arg->params->u.fmem.internal_offs);
22772ac13956SJens Wiklander cookie = READ_ONCE(arg->params->u.fmem.global_id);
2278c1bdf4fcSJens Wiklander mobj = mobj_ffa_get_by_cookie(cookie, internal_offset);
2279c1bdf4fcSJens Wiklander if (!mobj) {
2280c1bdf4fcSJens Wiklander DMSG("mobj_ffa_get_by_cookie(%#"PRIx64", %#x): failed",
2281c1bdf4fcSJens Wiklander cookie, internal_offset);
2282a9d0e06fSJens Wiklander return NULL;
2283a9d0e06fSJens Wiklander }
2284a9d0e06fSJens Wiklander
2285c1bdf4fcSJens Wiklander assert(mobj_is_nonsec(mobj));
2286a9d0e06fSJens Wiklander
22874ed45027SJens Wiklander if (mobj->size < size) {
22884ed45027SJens Wiklander DMSG("Mobj %#"PRIx64": wrong size", cookie);
22894ed45027SJens Wiklander mobj_put(mobj);
22904ed45027SJens Wiklander return NULL;
22914ed45027SJens Wiklander }
22924ed45027SJens Wiklander
2293c1bdf4fcSJens Wiklander if (mobj_inc_map(mobj)) {
2294c1bdf4fcSJens Wiklander DMSG("mobj_inc_map(%#"PRIx64"): failed", cookie);
2295c1bdf4fcSJens Wiklander mobj_put(mobj);
2296c1bdf4fcSJens Wiklander return NULL;
2297c1bdf4fcSJens Wiklander }
2298c1bdf4fcSJens Wiklander
2299c1bdf4fcSJens Wiklander return mobj;
2300c1bdf4fcSJens Wiklander }
2301c1bdf4fcSJens Wiklander
thread_rpc_alloc_payload(size_t size)2302c1bdf4fcSJens Wiklander struct mobj *thread_rpc_alloc_payload(size_t size)
2303c1bdf4fcSJens Wiklander {
2304c1bdf4fcSJens Wiklander return thread_rpc_alloc(size, 8, OPTEE_RPC_SHM_TYPE_APPL);
2305c1bdf4fcSJens Wiklander }
2306c1bdf4fcSJens Wiklander
thread_rpc_alloc_kernel_payload(size_t size)2307c1bdf4fcSJens Wiklander struct mobj *thread_rpc_alloc_kernel_payload(size_t size)
2308c1bdf4fcSJens Wiklander {
2309c1bdf4fcSJens Wiklander return thread_rpc_alloc(size, 8, OPTEE_RPC_SHM_TYPE_KERNEL);
2310c1bdf4fcSJens Wiklander }
2311c1bdf4fcSJens Wiklander
thread_rpc_free_kernel_payload(struct mobj * mobj)2312c1bdf4fcSJens Wiklander void thread_rpc_free_kernel_payload(struct mobj *mobj)
2313c1bdf4fcSJens Wiklander {
23144989730fSJens Wiklander if (mobj)
23154989730fSJens Wiklander thread_rpc_free(OPTEE_RPC_SHM_TYPE_KERNEL,
23164989730fSJens Wiklander mobj_get_cookie(mobj), mobj);
2317c1bdf4fcSJens Wiklander }
2318c1bdf4fcSJens Wiklander
thread_rpc_free_payload(struct mobj * mobj)2319c1bdf4fcSJens Wiklander void thread_rpc_free_payload(struct mobj *mobj)
2320c1bdf4fcSJens Wiklander {
23214989730fSJens Wiklander if (mobj)
2322c1bdf4fcSJens Wiklander thread_rpc_free(OPTEE_RPC_SHM_TYPE_APPL, mobj_get_cookie(mobj),
2323c1bdf4fcSJens Wiklander mobj);
2324c1bdf4fcSJens Wiklander }
2325c1bdf4fcSJens Wiklander
thread_rpc_alloc_global_payload(size_t size)2326c1bdf4fcSJens Wiklander struct mobj *thread_rpc_alloc_global_payload(size_t size)
2327c1bdf4fcSJens Wiklander {
2328c1bdf4fcSJens Wiklander return thread_rpc_alloc(size, 8, OPTEE_RPC_SHM_TYPE_GLOBAL);
2329c1bdf4fcSJens Wiklander }
2330c1bdf4fcSJens Wiklander
thread_rpc_free_global_payload(struct mobj * mobj)2331c1bdf4fcSJens Wiklander void thread_rpc_free_global_payload(struct mobj *mobj)
2332c1bdf4fcSJens Wiklander {
23334989730fSJens Wiklander if (mobj)
23344989730fSJens Wiklander thread_rpc_free(OPTEE_RPC_SHM_TYPE_GLOBAL,
23354989730fSJens Wiklander mobj_get_cookie(mobj), mobj);
2336a9d0e06fSJens Wiklander }
2337fb19e98eSJens Wiklander
thread_spmc_register_secondary_ep(vaddr_t ep)2338c64fa9c5SJens Wiklander void thread_spmc_register_secondary_ep(vaddr_t ep)
2339c64fa9c5SJens Wiklander {
2340c64fa9c5SJens Wiklander unsigned long ret = 0;
2341c64fa9c5SJens Wiklander
2342c64fa9c5SJens Wiklander /* Let the SPM know the entry point for secondary CPUs */
2343c64fa9c5SJens Wiklander ret = thread_smc(FFA_SECONDARY_EP_REGISTER_64, ep, 0, 0);
2344c64fa9c5SJens Wiklander
2345c64fa9c5SJens Wiklander if (ret != FFA_SUCCESS_32 && ret != FFA_SUCCESS_64)
2346c64fa9c5SJens Wiklander EMSG("FFA_SECONDARY_EP_REGISTER_64 ret %#lx", ret);
2347c64fa9c5SJens Wiklander }
2348c64fa9c5SJens Wiklander
ffa_id_get(void)234919ad526cSBalint Dobszay static uint16_t ffa_id_get(void)
235019ad526cSBalint Dobszay {
235119ad526cSBalint Dobszay /*
235219ad526cSBalint Dobszay * Ask the SPM component running at a higher EL to return our FF-A ID.
235319ad526cSBalint Dobszay * This can either be the SPMC ID (if the SPMC is enabled in OP-TEE) or
235419ad526cSBalint Dobszay * the partition ID (if not).
235519ad526cSBalint Dobszay */
235619ad526cSBalint Dobszay struct thread_smc_args args = {
235719ad526cSBalint Dobszay .a0 = FFA_ID_GET,
235819ad526cSBalint Dobszay };
235919ad526cSBalint Dobszay
236019ad526cSBalint Dobszay thread_smccc(&args);
236119ad526cSBalint Dobszay if (!is_ffa_success(args.a0)) {
236219ad526cSBalint Dobszay if (args.a0 == FFA_ERROR)
236319ad526cSBalint Dobszay EMSG("Get id failed with error %ld", args.a2);
236419ad526cSBalint Dobszay else
236519ad526cSBalint Dobszay EMSG("Get id failed");
236619ad526cSBalint Dobszay panic();
236719ad526cSBalint Dobszay }
236819ad526cSBalint Dobszay
236919ad526cSBalint Dobszay return args.a2;
237019ad526cSBalint Dobszay }
237119ad526cSBalint Dobszay
ffa_spm_id_get(void)237219ad526cSBalint Dobszay static uint16_t ffa_spm_id_get(void)
237319ad526cSBalint Dobszay {
237419ad526cSBalint Dobszay /*
237519ad526cSBalint Dobszay * Ask the SPM component running at a higher EL to return its ID.
237619ad526cSBalint Dobszay * If OP-TEE implements the S-EL1 SPMC, this will get the SPMD ID.
237719ad526cSBalint Dobszay * If not, the ID of the SPMC will be returned.
237819ad526cSBalint Dobszay */
237919ad526cSBalint Dobszay struct thread_smc_args args = {
238019ad526cSBalint Dobszay .a0 = FFA_SPM_ID_GET,
238119ad526cSBalint Dobszay };
238219ad526cSBalint Dobszay
238319ad526cSBalint Dobszay thread_smccc(&args);
238419ad526cSBalint Dobszay if (!is_ffa_success(args.a0)) {
238519ad526cSBalint Dobszay if (args.a0 == FFA_ERROR)
238619ad526cSBalint Dobszay EMSG("Get spm id failed with error %ld", args.a2);
238719ad526cSBalint Dobszay else
238819ad526cSBalint Dobszay EMSG("Get spm id failed");
238919ad526cSBalint Dobszay panic();
239019ad526cSBalint Dobszay }
239119ad526cSBalint Dobszay
239219ad526cSBalint Dobszay return args.a2;
239319ad526cSBalint Dobszay }
239419ad526cSBalint Dobszay
239500338334SJens Wiklander #ifdef CFG_CORE_DYN_PROTMEM
thread_spmc_get_protmem_config(enum mobj_use_case use_case,void * buf,size_t * buf_sz,size_t * min_mem_sz,size_t * min_mem_align)239600338334SJens Wiklander TEE_Result thread_spmc_get_protmem_config(enum mobj_use_case use_case,
239700338334SJens Wiklander void *buf, size_t *buf_sz,
239800338334SJens Wiklander size_t *min_mem_sz,
239900338334SJens Wiklander size_t *min_mem_align)
240000338334SJens Wiklander {
240100338334SJens Wiklander TEE_Result res = TEE_SUCCESS;
240200338334SJens Wiklander struct ffa_mem_access_perm mem_acc_list[] = {
240300338334SJens Wiklander {
240400338334SJens Wiklander .endpoint_id = optee_core_lsp.sp_id,
240500338334SJens Wiklander .perm = FFA_MEM_ACC_RW,
240600338334SJens Wiklander },
240700338334SJens Wiklander };
240800338334SJens Wiklander
240900338334SJens Wiklander res = plat_get_protmem_config(use_case, min_mem_sz, min_mem_align);
241000338334SJens Wiklander if (res)
241100338334SJens Wiklander return res;
241200338334SJens Wiklander
241300338334SJens Wiklander if (!buf || *buf_sz < sizeof(mem_acc_list)) {
241400338334SJens Wiklander *buf_sz = sizeof(mem_acc_list);
241500338334SJens Wiklander return TEE_ERROR_SHORT_BUFFER;
241600338334SJens Wiklander }
241700338334SJens Wiklander
241800338334SJens Wiklander memcpy(buf, mem_acc_list, sizeof(mem_acc_list));
241900338334SJens Wiklander *buf_sz = sizeof(mem_acc_list);
242000338334SJens Wiklander
242100338334SJens Wiklander return TEE_SUCCESS;
242200338334SJens Wiklander }
242300338334SJens Wiklander #endif /*CFG_CORE_DYN_PROTMEM*/
242400338334SJens Wiklander
check_desc(struct spmc_lsp_desc * d)2425ecf08061SJens Wiklander static TEE_Result check_desc(struct spmc_lsp_desc *d)
2426ecf08061SJens Wiklander {
2427ecf08061SJens Wiklander uint32_t accept_props = FFA_PART_PROP_DIRECT_REQ_RECV |
2428ecf08061SJens Wiklander FFA_PART_PROP_DIRECT_REQ_SEND |
2429ecf08061SJens Wiklander FFA_PART_PROP_NOTIF_CREATED |
2430ecf08061SJens Wiklander FFA_PART_PROP_NOTIF_DESTROYED |
2431ecf08061SJens Wiklander FFA_PART_PROP_AARCH64_STATE;
2432ecf08061SJens Wiklander uint32_t id = d->sp_id;
2433ecf08061SJens Wiklander
2434ecf08061SJens Wiklander if (id && (spmc_is_reserved_id(id) || spmc_find_lsp_by_sp_id(id) ||
2435ecf08061SJens Wiklander id < FFA_SWD_ID_MIN || id > FFA_SWD_ID_MAX)) {
2436ecf08061SJens Wiklander EMSG("Conflicting SP id for SP \"%s\" id %#"PRIx32,
2437ecf08061SJens Wiklander d->name, id);
2438ecf08061SJens Wiklander if (!IS_ENABLED(CFG_SP_SKIP_FAILED))
2439ecf08061SJens Wiklander panic();
2440ecf08061SJens Wiklander return TEE_ERROR_BAD_FORMAT;
2441ecf08061SJens Wiklander }
2442ecf08061SJens Wiklander
2443ecf08061SJens Wiklander if (d->properties & ~accept_props) {
2444ecf08061SJens Wiklander EMSG("Unexpected properties in %#"PRIx32" for LSP \"%s\" %#"PRIx16,
2445ecf08061SJens Wiklander d->properties, d->name, d->sp_id);
2446ecf08061SJens Wiklander if (!IS_ENABLED(CFG_SP_SKIP_FAILED))
2447ecf08061SJens Wiklander panic();
2448ecf08061SJens Wiklander d->properties &= accept_props;
2449ecf08061SJens Wiklander }
2450ecf08061SJens Wiklander
2451ecf08061SJens Wiklander if (!d->direct_req) {
2452ecf08061SJens Wiklander EMSG("Missing direct request callback for LSP \"%s\" %#"PRIx16,
2453ecf08061SJens Wiklander d->name, d->sp_id);
2454ecf08061SJens Wiklander if (!IS_ENABLED(CFG_SP_SKIP_FAILED))
2455ecf08061SJens Wiklander panic();
2456ecf08061SJens Wiklander return TEE_ERROR_BAD_FORMAT;
2457ecf08061SJens Wiklander }
2458ecf08061SJens Wiklander
2459ecf08061SJens Wiklander if (!d->uuid_words[0] && !d->uuid_words[1] &&
2460ecf08061SJens Wiklander !d->uuid_words[2] && !d->uuid_words[3]) {
2461ecf08061SJens Wiklander EMSG("Found NULL UUID for LSP \"%s\" %#"PRIx16,
2462ecf08061SJens Wiklander d->name, d->sp_id);
2463ecf08061SJens Wiklander if (!IS_ENABLED(CFG_SP_SKIP_FAILED))
2464ecf08061SJens Wiklander panic();
2465ecf08061SJens Wiklander return TEE_ERROR_BAD_FORMAT;
2466ecf08061SJens Wiklander }
2467ecf08061SJens Wiklander
2468ecf08061SJens Wiklander return TEE_SUCCESS;
2469ecf08061SJens Wiklander }
2470ecf08061SJens Wiklander
find_unused_sp_id(void)2471ecf08061SJens Wiklander static uint16_t find_unused_sp_id(void)
2472ecf08061SJens Wiklander {
2473ecf08061SJens Wiklander uint32_t id = FFA_SWD_ID_MIN;
2474ecf08061SJens Wiklander
2475ecf08061SJens Wiklander while (spmc_is_reserved_id(id) || spmc_find_lsp_by_sp_id(id)) {
2476ecf08061SJens Wiklander id++;
2477ecf08061SJens Wiklander assert(id <= FFA_SWD_ID_MAX);
2478ecf08061SJens Wiklander }
2479ecf08061SJens Wiklander
2480ecf08061SJens Wiklander return id;
2481ecf08061SJens Wiklander }
2482ecf08061SJens Wiklander
spmc_register_lsp(struct spmc_lsp_desc * desc)2483ecf08061SJens Wiklander TEE_Result spmc_register_lsp(struct spmc_lsp_desc *desc)
2484ecf08061SJens Wiklander {
2485ecf08061SJens Wiklander TEE_Result res = TEE_SUCCESS;
2486ecf08061SJens Wiklander
2487ecf08061SJens Wiklander res = check_desc(desc);
2488ecf08061SJens Wiklander if (res)
2489ecf08061SJens Wiklander return res;
2490ecf08061SJens Wiklander
2491ecf08061SJens Wiklander if (STAILQ_EMPTY(&lsp_head)) {
2492ecf08061SJens Wiklander DMSG("Cannot add Logical SP \"%s\": LSP framework not initialized yet",
2493ecf08061SJens Wiklander desc->name);
2494ecf08061SJens Wiklander return TEE_ERROR_ITEM_NOT_FOUND;
2495ecf08061SJens Wiklander }
2496ecf08061SJens Wiklander
2497ecf08061SJens Wiklander if (!desc->sp_id)
2498ecf08061SJens Wiklander desc->sp_id = find_unused_sp_id();
2499ecf08061SJens Wiklander
2500ecf08061SJens Wiklander DMSG("Adding Logical SP \"%s\" with id %#"PRIx16,
2501ecf08061SJens Wiklander desc->name, desc->sp_id);
2502ecf08061SJens Wiklander
2503ecf08061SJens Wiklander STAILQ_INSERT_TAIL(&lsp_head, desc, link);
2504ecf08061SJens Wiklander
2505ecf08061SJens Wiklander return TEE_SUCCESS;
2506ecf08061SJens Wiklander }
2507ecf08061SJens Wiklander
2508ecf08061SJens Wiklander static struct spmc_lsp_desc optee_core_lsp __nex_data = {
2509ecf08061SJens Wiklander .name = "OP-TEE",
2510ecf08061SJens Wiklander .direct_req = optee_lsp_handle_direct_request,
2511ecf08061SJens Wiklander .properties = FFA_PART_PROP_DIRECT_REQ_RECV |
2512ecf08061SJens Wiklander FFA_PART_PROP_DIRECT_REQ_SEND |
2513ecf08061SJens Wiklander #ifdef CFG_NS_VIRTUALIZATION
2514ecf08061SJens Wiklander FFA_PART_PROP_NOTIF_CREATED |
2515ecf08061SJens Wiklander FFA_PART_PROP_NOTIF_DESTROYED |
2516ecf08061SJens Wiklander #endif
2517ecf08061SJens Wiklander FFA_PART_PROP_AARCH64_STATE |
2518ecf08061SJens Wiklander FFA_PART_PROP_IS_PE_ID,
2519ecf08061SJens Wiklander /*
2520ecf08061SJens Wiklander * - if the SPMC is in S-EL2 this UUID describes OP-TEE as a S-EL1
2521ecf08061SJens Wiklander * SP, or
2522ecf08061SJens Wiklander * - if the SPMC is in S-EL1 then this UUID is for OP-TEE as a
2523ecf08061SJens Wiklander * logical partition, residing in the same exception level as the
2524ecf08061SJens Wiklander * SPMC
2525ecf08061SJens Wiklander * UUID 486178e0-e7f8-11e3-bc5e-0002a5d5c51b
2526ecf08061SJens Wiklander */
2527ecf08061SJens Wiklander .uuid_words = { 0xe0786148, 0xe311f8e7, 0x02005ebc, 0x1bc5d5a5, },
2528ecf08061SJens Wiklander };
2529ecf08061SJens Wiklander
2530e26b8354SJens Wiklander #if defined(CFG_CORE_SEL1_SPMC)
2531ecf08061SJens Wiklander static struct spmc_lsp_desc optee_spmc_lsp __nex_data = {
2532ecf08061SJens Wiklander .name = "OP-TEE SPMC",
2533ecf08061SJens Wiklander .direct_req = optee_spmc_lsp_handle_direct_request,
2534ecf08061SJens Wiklander };
2535ecf08061SJens Wiklander
spmc_init(void)2536e26b8354SJens Wiklander static TEE_Result spmc_init(void)
2537e26b8354SJens Wiklander {
2538f6dcf234SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION) &&
2539f6dcf234SJens Wiklander virt_add_guest_spec_data(¬if_vm_bitmap_id,
2540f6dcf234SJens Wiklander sizeof(struct notif_vm_bitmap), NULL))
2541f6dcf234SJens Wiklander panic("virt_add_guest_spec_data");
254219ad526cSBalint Dobszay spmd_id = ffa_spm_id_get();
254319ad526cSBalint Dobszay DMSG("SPMD ID %#"PRIx16, spmd_id);
254419ad526cSBalint Dobszay
2545ecf08061SJens Wiklander optee_spmc_lsp.sp_id = ffa_id_get();
2546ecf08061SJens Wiklander DMSG("SPMC ID %#"PRIx16, optee_spmc_lsp.sp_id);
2547ecf08061SJens Wiklander STAILQ_INSERT_HEAD(&lsp_head, &optee_spmc_lsp, link);
254819ad526cSBalint Dobszay
2549ecf08061SJens Wiklander optee_core_lsp.sp_id = find_unused_sp_id();
2550ecf08061SJens Wiklander DMSG("OP-TEE endpoint ID %#"PRIx16, optee_core_lsp.sp_id);
2551ecf08061SJens Wiklander STAILQ_INSERT_HEAD(&lsp_head, &optee_core_lsp, link);
2552e26b8354SJens Wiklander
2553a1c53023SJens Wiklander /*
2554a1c53023SJens Wiklander * If SPMD think we are version 1.0 it will report version 1.0 to
2555a1c53023SJens Wiklander * normal world regardless of what version we query the SPM with.
2556a1c53023SJens Wiklander * However, if SPMD think we are version 1.1 it will forward
2557a1c53023SJens Wiklander * queries from normal world to let us negotiate version. So by
2558a1c53023SJens Wiklander * setting version 1.0 here we should be compatible.
2559a1c53023SJens Wiklander *
2560a1c53023SJens Wiklander * Note that disagreement on negotiated version means that we'll
2561a1c53023SJens Wiklander * have communication problems with normal world.
2562a1c53023SJens Wiklander */
2563f49f23f7SJens Wiklander my_rxtx.ffa_vers = FFA_VERSION_1_0;
2564a1c53023SJens Wiklander
2565e26b8354SJens Wiklander return TEE_SUCCESS;
2566e26b8354SJens Wiklander }
2567e26b8354SJens Wiklander #else /* !defined(CFG_CORE_SEL1_SPMC) */
spmc_rxtx_map(struct ffa_rxtx * rxtx)2568fb19e98eSJens Wiklander static void spmc_rxtx_map(struct ffa_rxtx *rxtx)
2569fb19e98eSJens Wiklander {
2570fb19e98eSJens Wiklander struct thread_smc_args args = {
2571fb19e98eSJens Wiklander #ifdef ARM64
2572fb19e98eSJens Wiklander .a0 = FFA_RXTX_MAP_64,
2573fb19e98eSJens Wiklander #else
2574fb19e98eSJens Wiklander .a0 = FFA_RXTX_MAP_32,
2575fb19e98eSJens Wiklander #endif
25762d35f6b6SJens Wiklander .a1 = virt_to_phys(rxtx->tx),
25772d35f6b6SJens Wiklander .a2 = virt_to_phys(rxtx->rx),
2578fb19e98eSJens Wiklander .a3 = 1,
2579fb19e98eSJens Wiklander };
2580fb19e98eSJens Wiklander
2581fb19e98eSJens Wiklander thread_smccc(&args);
2582fb19e98eSJens Wiklander if (!is_ffa_success(args.a0)) {
2583fb19e98eSJens Wiklander if (args.a0 == FFA_ERROR)
2584fb19e98eSJens Wiklander EMSG("rxtx map failed with error %ld", args.a2);
2585fb19e98eSJens Wiklander else
2586fb19e98eSJens Wiklander EMSG("rxtx map failed");
2587fb19e98eSJens Wiklander panic();
2588fb19e98eSJens Wiklander }
2589fb19e98eSJens Wiklander }
2590fb19e98eSJens Wiklander
get_ffa_version(uint32_t my_version)2591a1c53023SJens Wiklander static uint32_t get_ffa_version(uint32_t my_version)
2592fb19e98eSJens Wiklander {
2593a1c53023SJens Wiklander struct thread_smc_args args = {
2594a1c53023SJens Wiklander .a0 = FFA_VERSION,
2595a1c53023SJens Wiklander .a1 = my_version,
2596a1c53023SJens Wiklander };
2597a1c53023SJens Wiklander
2598a1c53023SJens Wiklander thread_smccc(&args);
2599a1c53023SJens Wiklander if (args.a0 & BIT(31)) {
2600a1c53023SJens Wiklander EMSG("FF-A version failed with error %ld", args.a0);
2601a1c53023SJens Wiklander panic();
2602a1c53023SJens Wiklander }
2603a1c53023SJens Wiklander
2604a1c53023SJens Wiklander return args.a0;
2605a1c53023SJens Wiklander }
2606a1c53023SJens Wiklander
spmc_retrieve_req(struct ffa_mem_transaction_x * trans)260700338334SJens Wiklander static void *spmc_retrieve_req(struct ffa_mem_transaction_x *trans)
2608a1c53023SJens Wiklander {
260900338334SJens Wiklander uint64_t cookie __maybe_unused = trans->global_handle;
2610fb19e98eSJens Wiklander struct ffa_mem_access *acc_descr_array = NULL;
2611fb19e98eSJens Wiklander struct ffa_mem_access_perm *perm_descr = NULL;
2612fb19e98eSJens Wiklander struct thread_smc_args args = {
2613fb19e98eSJens Wiklander .a0 = FFA_MEM_RETRIEVE_REQ_32,
2614fb19e98eSJens Wiklander .a3 = 0, /* Address, Using TX -> MBZ */
2615fb19e98eSJens Wiklander .a4 = 0, /* Using TX -> MBZ */
2616fb19e98eSJens Wiklander };
2617a1c53023SJens Wiklander size_t size = 0;
2618a1c53023SJens Wiklander int rc = 0;
2619fb19e98eSJens Wiklander
2620f49f23f7SJens Wiklander if (my_rxtx.ffa_vers == FFA_VERSION_1_0) {
2621f49f23f7SJens Wiklander struct ffa_mem_transaction_1_0 *trans_descr = my_rxtx.tx;
2622a1c53023SJens Wiklander
2623a1c53023SJens Wiklander size = sizeof(*trans_descr) + 1 * sizeof(struct ffa_mem_access);
2624fb19e98eSJens Wiklander memset(trans_descr, 0, size);
262500338334SJens Wiklander trans_descr->sender_id = trans->sender_id;
262600338334SJens Wiklander trans_descr->mem_reg_attr = trans->mem_reg_attr;
262700338334SJens Wiklander trans_descr->global_handle = trans->global_handle;
262800338334SJens Wiklander trans_descr->tag = trans->tag;
262900338334SJens Wiklander trans_descr->flags = trans->flags;
2630fb19e98eSJens Wiklander trans_descr->mem_access_count = 1;
2631fb19e98eSJens Wiklander acc_descr_array = trans_descr->mem_access_array;
2632a1c53023SJens Wiklander } else {
2633f49f23f7SJens Wiklander struct ffa_mem_transaction_1_1 *trans_descr = my_rxtx.tx;
2634a1c53023SJens Wiklander
2635a1c53023SJens Wiklander size = sizeof(*trans_descr) + 1 * sizeof(struct ffa_mem_access);
2636a1c53023SJens Wiklander memset(trans_descr, 0, size);
263700338334SJens Wiklander trans_descr->sender_id = trans->sender_id;
263800338334SJens Wiklander trans_descr->mem_reg_attr = trans->mem_reg_attr;
263900338334SJens Wiklander trans_descr->global_handle = trans->global_handle;
264000338334SJens Wiklander trans_descr->tag = trans->tag;
264100338334SJens Wiklander trans_descr->flags = trans->flags;
2642a1c53023SJens Wiklander trans_descr->mem_access_count = 1;
2643a1c53023SJens Wiklander trans_descr->mem_access_offs = sizeof(*trans_descr);
2644a1c53023SJens Wiklander trans_descr->mem_access_size = sizeof(struct ffa_mem_access);
2645f49f23f7SJens Wiklander acc_descr_array = (void *)((vaddr_t)my_rxtx.tx +
2646a1c53023SJens Wiklander sizeof(*trans_descr));
2647a1c53023SJens Wiklander }
2648fb19e98eSJens Wiklander acc_descr_array->region_offs = 0;
2649fb19e98eSJens Wiklander acc_descr_array->reserved = 0;
2650fb19e98eSJens Wiklander perm_descr = &acc_descr_array->access_perm;
2651ecf08061SJens Wiklander perm_descr->endpoint_id = optee_core_lsp.sp_id;
2652fb19e98eSJens Wiklander perm_descr->perm = FFA_MEM_ACC_RW;
26535f01dc49SJens Wiklander perm_descr->flags = 0;
2654fb19e98eSJens Wiklander
2655a1c53023SJens Wiklander args.a1 = size; /* Total Length */
2656a1c53023SJens Wiklander args.a2 = size; /* Frag Length == Total length */
2657fb19e98eSJens Wiklander thread_smccc(&args);
2658fb19e98eSJens Wiklander if (args.a0 != FFA_MEM_RETRIEVE_RESP) {
2659fb19e98eSJens Wiklander if (args.a0 == FFA_ERROR)
2660fb19e98eSJens Wiklander EMSG("Failed to fetch cookie %#"PRIx64" error code %d",
2661fb19e98eSJens Wiklander cookie, (int)args.a2);
2662fb19e98eSJens Wiklander else
2663fb19e98eSJens Wiklander EMSG("Failed to fetch cookie %#"PRIx64" a0 %#"PRIx64,
2664fb19e98eSJens Wiklander cookie, args.a0);
2665fb19e98eSJens Wiklander return NULL;
2666fb19e98eSJens Wiklander }
2667c354668aSJens Wiklander rc = spmc_read_mem_transaction(my_rxtx.ffa_vers, my_rxtx.rx,
2668f49f23f7SJens Wiklander my_rxtx.size, trans);
2669a1c53023SJens Wiklander if (rc) {
2670a1c53023SJens Wiklander EMSG("Memory transaction failure for cookie %#"PRIx64" rc %d",
2671a1c53023SJens Wiklander cookie, rc);
2672a1c53023SJens Wiklander return NULL;
2673a1c53023SJens Wiklander }
2674fb19e98eSJens Wiklander
2675f49f23f7SJens Wiklander return my_rxtx.rx;
2676fb19e98eSJens Wiklander }
2677fb19e98eSJens Wiklander
thread_spmc_relinquish(uint64_t cookie)2678fb19e98eSJens Wiklander void thread_spmc_relinquish(uint64_t cookie)
2679fb19e98eSJens Wiklander {
2680f49f23f7SJens Wiklander struct ffa_mem_relinquish *relinquish_desc = my_rxtx.tx;
2681fb19e98eSJens Wiklander struct thread_smc_args args = {
2682fb19e98eSJens Wiklander .a0 = FFA_MEM_RELINQUISH,
2683fb19e98eSJens Wiklander };
2684fb19e98eSJens Wiklander
2685fb19e98eSJens Wiklander memset(relinquish_desc, 0, sizeof(*relinquish_desc));
2686fb19e98eSJens Wiklander relinquish_desc->handle = cookie;
2687fb19e98eSJens Wiklander relinquish_desc->flags = 0;
2688fb19e98eSJens Wiklander relinquish_desc->endpoint_count = 1;
2689ecf08061SJens Wiklander relinquish_desc->endpoint_id_array[0] = optee_core_lsp.sp_id;
2690fb19e98eSJens Wiklander thread_smccc(&args);
2691fb19e98eSJens Wiklander if (!is_ffa_success(args.a0))
2692fb19e98eSJens Wiklander EMSG("Failed to relinquish cookie %#"PRIx64, cookie);
2693fb19e98eSJens Wiklander }
2694fb19e98eSJens Wiklander
set_pages(struct ffa_address_range * regions,unsigned int num_regions,unsigned int num_pages,struct mobj_ffa * mf)2695fb19e98eSJens Wiklander static int set_pages(struct ffa_address_range *regions,
2696fb19e98eSJens Wiklander unsigned int num_regions, unsigned int num_pages,
2697fb19e98eSJens Wiklander struct mobj_ffa *mf)
2698fb19e98eSJens Wiklander {
2699fb19e98eSJens Wiklander unsigned int n = 0;
2700fb19e98eSJens Wiklander unsigned int idx = 0;
2701fb19e98eSJens Wiklander
2702fb19e98eSJens Wiklander for (n = 0; n < num_regions; n++) {
2703fb19e98eSJens Wiklander unsigned int page_count = READ_ONCE(regions[n].page_count);
2704fb19e98eSJens Wiklander uint64_t addr = READ_ONCE(regions[n].address);
2705fb19e98eSJens Wiklander
2706fb19e98eSJens Wiklander if (mobj_ffa_add_pages_at(mf, &idx, addr, page_count))
2707fb19e98eSJens Wiklander return FFA_INVALID_PARAMETERS;
2708fb19e98eSJens Wiklander }
2709fb19e98eSJens Wiklander
2710fb19e98eSJens Wiklander if (idx != num_pages)
2711fb19e98eSJens Wiklander return FFA_INVALID_PARAMETERS;
2712fb19e98eSJens Wiklander
2713fb19e98eSJens Wiklander return 0;
2714fb19e98eSJens Wiklander }
2715fb19e98eSJens Wiklander
thread_spmc_populate_mobj_from_rx(uint64_t cookie,enum mobj_use_case use_case)271600338334SJens Wiklander struct mobj_ffa *thread_spmc_populate_mobj_from_rx(uint64_t cookie,
271700338334SJens Wiklander enum mobj_use_case use_case)
2718fb19e98eSJens Wiklander {
2719fb19e98eSJens Wiklander struct mobj_ffa *ret = NULL;
272000338334SJens Wiklander struct ffa_mem_transaction_x retrieve_desc = { .tag = use_case};
2721fb19e98eSJens Wiklander struct ffa_mem_access *descr_array = NULL;
2722fb19e98eSJens Wiklander struct ffa_mem_region *descr = NULL;
2723fb19e98eSJens Wiklander struct mobj_ffa *mf = NULL;
2724fb19e98eSJens Wiklander unsigned int num_pages = 0;
2725fb19e98eSJens Wiklander unsigned int offs = 0;
2726a1c53023SJens Wiklander void *buf = NULL;
2727fb19e98eSJens Wiklander struct thread_smc_args ffa_rx_release_args = {
2728fb19e98eSJens Wiklander .a0 = FFA_RX_RELEASE
2729fb19e98eSJens Wiklander };
2730fb19e98eSJens Wiklander
273100338334SJens Wiklander if (use_case == MOBJ_USE_CASE_NS_SHM)
273200338334SJens Wiklander retrieve_desc.flags = FFA_MEMORY_REGION_TRANSACTION_TYPE_SHARE;
273300338334SJens Wiklander else
273400338334SJens Wiklander retrieve_desc.flags = FFA_MEMORY_REGION_TRANSACTION_TYPE_LEND;
273500338334SJens Wiklander retrieve_desc.flags |= FFA_MEMORY_REGION_FLAG_ANY_ALIGNMENT;
273600338334SJens Wiklander retrieve_desc.global_handle = cookie;
273700338334SJens Wiklander retrieve_desc.sender_id = thread_get_tsd()->rpc_target_info;
273800338334SJens Wiklander retrieve_desc.mem_reg_attr = FFA_NORMAL_MEM_REG_ATTR;
273900338334SJens Wiklander
2740fb19e98eSJens Wiklander /*
2741fb19e98eSJens Wiklander * OP-TEE is only supporting a single mem_region while the
2742fb19e98eSJens Wiklander * specification allows for more than one.
2743fb19e98eSJens Wiklander */
274400338334SJens Wiklander buf = spmc_retrieve_req(&retrieve_desc);
2745a1c53023SJens Wiklander if (!buf) {
2746fb19e98eSJens Wiklander EMSG("Failed to retrieve cookie from rx buffer %#"PRIx64,
2747fb19e98eSJens Wiklander cookie);
2748fb19e98eSJens Wiklander return NULL;
2749fb19e98eSJens Wiklander }
2750fb19e98eSJens Wiklander
2751a1c53023SJens Wiklander descr_array = (void *)((vaddr_t)buf + retrieve_desc.mem_access_offs);
2752fb19e98eSJens Wiklander offs = READ_ONCE(descr_array->region_offs);
2753a1c53023SJens Wiklander descr = (struct ffa_mem_region *)((vaddr_t)buf + offs);
2754fb19e98eSJens Wiklander
2755fb19e98eSJens Wiklander num_pages = READ_ONCE(descr->total_page_count);
275600338334SJens Wiklander mf = mobj_ffa_spmc_new(cookie, num_pages, use_case);
2757fb19e98eSJens Wiklander if (!mf)
2758fb19e98eSJens Wiklander goto out;
2759fb19e98eSJens Wiklander
2760fb19e98eSJens Wiklander if (set_pages(descr->address_range_array,
2761fb19e98eSJens Wiklander READ_ONCE(descr->address_range_count), num_pages, mf)) {
2762e26b8354SJens Wiklander mobj_ffa_spmc_delete(mf);
2763fb19e98eSJens Wiklander goto out;
2764fb19e98eSJens Wiklander }
2765fb19e98eSJens Wiklander
2766fb19e98eSJens Wiklander ret = mf;
2767fb19e98eSJens Wiklander
2768fb19e98eSJens Wiklander out:
2769fb19e98eSJens Wiklander /* Release RX buffer after the mem retrieve request. */
2770fb19e98eSJens Wiklander thread_smccc(&ffa_rx_release_args);
2771fb19e98eSJens Wiklander
2772fb19e98eSJens Wiklander return ret;
2773fb19e98eSJens Wiklander }
2774fb19e98eSJens Wiklander
get_ffa_version_from_manifest(void * fdt)2775bef959c8SJens Wiklander static uint32_t get_ffa_version_from_manifest(void *fdt)
2776bef959c8SJens Wiklander {
2777bef959c8SJens Wiklander int ret = 0;
2778bef959c8SJens Wiklander uint32_t vers = 0;
2779bef959c8SJens Wiklander
2780bef959c8SJens Wiklander ret = fdt_node_check_compatible(fdt, 0, "arm,ffa-manifest-1.0");
2781bef959c8SJens Wiklander if (ret < 0) {
2782bef959c8SJens Wiklander EMSG("Invalid FF-A manifest at %p: error %d", fdt, ret);
2783bef959c8SJens Wiklander panic();
2784bef959c8SJens Wiklander }
2785bef959c8SJens Wiklander
2786bef959c8SJens Wiklander ret = fdt_read_uint32(fdt, 0, "ffa-version", &vers);
2787bef959c8SJens Wiklander if (ret < 0) {
2788bef959c8SJens Wiklander EMSG("Can't read \"ffa-version\" from FF-A manifest at %p: error %d",
2789bef959c8SJens Wiklander fdt, ret);
2790bef959c8SJens Wiklander panic();
2791bef959c8SJens Wiklander }
2792bef959c8SJens Wiklander
2793bef959c8SJens Wiklander return vers;
2794bef959c8SJens Wiklander }
2795bef959c8SJens Wiklander
spmc_init(void)2796fb19e98eSJens Wiklander static TEE_Result spmc_init(void)
2797fb19e98eSJens Wiklander {
2798a1c53023SJens Wiklander uint32_t my_vers = 0;
2799a1c53023SJens Wiklander uint32_t vers = 0;
2800a1c53023SJens Wiklander
2801101b9d4dSJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION) &&
2802101b9d4dSJens Wiklander virt_add_guest_spec_data(¬if_vm_bitmap_id,
2803101b9d4dSJens Wiklander sizeof(struct notif_vm_bitmap), NULL))
2804101b9d4dSJens Wiklander panic("virt_add_guest_spec_data");
2805101b9d4dSJens Wiklander
2806bef959c8SJens Wiklander my_vers = get_ffa_version_from_manifest(get_manifest_dt());
2807bef959c8SJens Wiklander if (my_vers < FFA_VERSION_1_0 || my_vers > FFA_VERSION_1_2) {
2808bef959c8SJens Wiklander EMSG("Unsupported version %"PRIu32".%"PRIu32" from manifest",
2809bef959c8SJens Wiklander FFA_GET_MAJOR_VERSION(my_vers),
2810bef959c8SJens Wiklander FFA_GET_MINOR_VERSION(my_vers));
2811bef959c8SJens Wiklander panic();
2812bef959c8SJens Wiklander }
2813a1c53023SJens Wiklander vers = get_ffa_version(my_vers);
2814bef959c8SJens Wiklander DMSG("SPMC reported version %"PRIu32".%"PRIu32,
2815bef959c8SJens Wiklander FFA_GET_MAJOR_VERSION(vers), FFA_GET_MINOR_VERSION(vers));
2816bef959c8SJens Wiklander if (FFA_GET_MAJOR_VERSION(vers) != FFA_GET_MAJOR_VERSION(my_vers)) {
2817bef959c8SJens Wiklander EMSG("Incompatible major version %"PRIu32", expected %"PRIu32"",
2818bef959c8SJens Wiklander FFA_GET_MAJOR_VERSION(vers),
2819bef959c8SJens Wiklander FFA_GET_MAJOR_VERSION(my_vers));
2820a1c53023SJens Wiklander panic();
2821a1c53023SJens Wiklander }
2822a1c53023SJens Wiklander if (vers < my_vers)
2823a1c53023SJens Wiklander my_vers = vers;
2824bef959c8SJens Wiklander DMSG("Using version %"PRIu32".%"PRIu32"",
2825bef959c8SJens Wiklander FFA_GET_MAJOR_VERSION(my_vers), FFA_GET_MINOR_VERSION(my_vers));
2826f49f23f7SJens Wiklander my_rxtx.ffa_vers = my_vers;
2827a1c53023SJens Wiklander
2828f49f23f7SJens Wiklander spmc_rxtx_map(&my_rxtx);
282919ad526cSBalint Dobszay
283019ad526cSBalint Dobszay spmc_id = ffa_spm_id_get();
283119ad526cSBalint Dobszay DMSG("SPMC ID %#"PRIx16, spmc_id);
283219ad526cSBalint Dobszay
2833ecf08061SJens Wiklander optee_core_lsp.sp_id = ffa_id_get();
2834ecf08061SJens Wiklander DMSG("OP-TEE endpoint ID %#"PRIx16, optee_core_lsp.sp_id);
2835ecf08061SJens Wiklander STAILQ_INSERT_HEAD(&lsp_head, &optee_core_lsp, link);
2836fb19e98eSJens Wiklander
283755cd94d1SJens Wiklander if (!ffa_features(FFA_NOTIFICATION_SET)) {
283855cd94d1SJens Wiklander spmc_notif_is_ready = true;
283955cd94d1SJens Wiklander DMSG("Asynchronous notifications are ready");
284055cd94d1SJens Wiklander }
284155cd94d1SJens Wiklander
2842fb19e98eSJens Wiklander return TEE_SUCCESS;
2843fb19e98eSJens Wiklander }
2844e26b8354SJens Wiklander #endif /* !defined(CFG_CORE_SEL1_SPMC) */
2845fb19e98eSJens Wiklander
2846f6dcf234SJens Wiklander nex_service_init(spmc_init);
2847