|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [XEN PATCH v7 20/20] xen/arm: ffa: support sharing large memory ranges
Hi Jens,
> On 15 Mar 2023, at 12:47, Jens Wiklander <jens.wiklander@xxxxxxxxxx> wrote:
>
> Hi Bertrand,
>
> On Wed, Mar 15, 2023 at 11:13 AM Bertrand Marquis
> <Bertrand.Marquis@xxxxxxx> wrote:
>>
>> Hi Jens,
>>
>>> On 22 Feb 2023, at 16:33, Jens Wiklander <jens.wiklander@xxxxxxxxxx> wrote:
>>>
>>> Adds support for sharing large memory ranges transmitted in fragments
>>> using FFA_MEM_FRAG_TX.
>>>
>>> The implementation is the bare minimum to be able to communicate with
>>> OP-TEE running as an SPMC at S-EL1.
>>>
>>> Adds a check that the SP supports the needed FF-A feature
>>> FFA_MEM_FRAG_TX.
>>>
>>> Signed-off-by: Jens Wiklander <jens.wiklander@xxxxxxxxxx>
>>> ---
>>> xen/arch/arm/tee/ffa.c | 254 ++++++++++++++++++++++++++++++++++++++---
>>> 1 file changed, 240 insertions(+), 14 deletions(-)
>>>
>>> diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c
>>> index 3557edc455d0..72c0249d8cad 100644
>>> --- a/xen/arch/arm/tee/ffa.c
>>> +++ b/xen/arch/arm/tee/ffa.c
>>> @@ -326,6 +326,7 @@ struct ffa_ctx {
>>> uint32_t guest_vers;
>>> bool tx_is_mine;
>>> bool interrupted;
>>> + struct list_head frag_list;
>>> struct list_head shm_list;
>>> unsigned int shm_count;
>>> spinlock_t lock;
>>> @@ -340,6 +341,18 @@ struct ffa_shm_mem {
>>> struct page_info *pages[];
>>> };
>>
>> We start to have a lot of fields here.
>> It might be useful to have some quick documentation
>> in comment here as some names are a bit generic.
>> For example "frag_list" does not say much.
>
> I'll add some comments.
>
>>
>>>
>>> +struct mem_frag_state {
>>> + struct list_head list;
>>> + struct ffa_shm_mem *shm;
>>> + uint32_t range_count;
>>> + unsigned int current_page_idx;
>>> + unsigned int frag_offset;
>>> + unsigned int range_offset;
>>> + const uint8_t *buf;
>>> + unsigned int buf_size;
>>> + struct ffa_address_range range;
>>> +};
>>
>> same here, at a first glance it is not quite clear why
>> a fragment needs that much info. Some seem to only
>> be needed during the transaction but do not need to be saved.
>
> This struct is only used during the transaction, so this is freed once
> the entire memory transaction descriptor has been processed.
>
>>
>>> +
>>> /* Negotiated FF-A version to use with the SPMC */
>>> static uint32_t ffa_version __ro_after_init;
>>>
>>> @@ -512,6 +525,36 @@ static int32_t ffa_mem_share(uint32_t tot_len,
>>> uint32_t frag_len,
>>> }
>>> }
>>>
>>> +static int32_t ffa_mem_frag_tx(uint64_t handle, uint32_t frag_len,
>>> + uint16_t sender_id)
>>> +{
>>> + struct arm_smccc_1_2_regs arg = {
>>> + .a0 = FFA_MEM_FRAG_TX,
>>> + .a1 = handle & UINT32_MAX,
>>> + .a2 = handle >> 32,
>>> + .a3 = frag_len,
>>> + .a4 = (uint32_t)sender_id << 16,
>>> + };
>>> + struct arm_smccc_1_2_regs resp;
>>> +
>>> + arm_smccc_1_2_smc(&arg, &resp);
>>> +
>>> + switch ( resp.a0 )
>>> + {
>>> + case FFA_ERROR:
>>> + if ( resp.a2 )
>>> + return resp.a2;
>>> + else
>>> + return FFA_RET_NOT_SUPPORTED;
>>> + case FFA_SUCCESS_32:
>>> + return FFA_RET_OK;
>>> + case FFA_MEM_FRAG_RX:
>>> + return resp.a3;
>>> + default:
>>> + return FFA_RET_NOT_SUPPORTED;
>>> + }
>>> +}
>>> +
>>> static int32_t ffa_mem_reclaim(uint32_t handle_lo, uint32_t handle_hi,
>>> uint32_t flags)
>>> {
>>> @@ -586,6 +629,14 @@ static void set_regs_success(struct cpu_user_regs
>>> *regs, uint32_t w2,
>>> set_regs(regs, FFA_SUCCESS_32, 0, w2, w3, 0, 0, 0, 0);
>>> }
>>>
>>> +static void set_regs_frag_rx(struct cpu_user_regs *regs, uint32_t
>>> handle_lo,
>>> + uint32_t handle_hi, uint32_t frag_offset,
>>> + uint16_t sender_id)
>>> +{
>>> + set_regs(regs, FFA_MEM_FRAG_RX, handle_lo, handle_hi, frag_offset,
>>> + (uint32_t)sender_id << 16, 0, 0, 0);
>>> +}
>>> +
>>> static void handle_version(struct cpu_user_regs *regs)
>>> {
>>> struct domain *d = current->domain;
>>> @@ -955,6 +1006,8 @@ static int share_shm(struct ffa_shm_mem *shm)
>>> paddr_t last_pa;
>>> unsigned int n;
>>> paddr_t pa;
>>> + bool first;
>>> + int ret;
>>>
>>> ASSERT(spin_is_locked(&ffa_tx_buffer_lock));
>>> if ( !shm->page_count )
>>> @@ -994,13 +1047,23 @@ static int share_shm(struct ffa_shm_mem *shm)
>>>
>>> tot_len = ADDR_RANGE_OFFSET(descr->mem_access_count, region_count,
>>> region_descr->address_range_count);
>>> - if ( tot_len > max_frag_len )
>>> - return FFA_RET_NOT_SUPPORTED;
>>>
>>> + /*
>>> + * Sharing memory with secure world may have to be done with multiple
>>> + * calls depending on how many address ranges will be needed. If we're
>>> + * sharing physically contiguous memory we will only need one range but
>>> + * we will also need to deal with the worst case where all physical
>>> + * pages are non-contiguous. For the first batch of address ranges we
>>> + * call ffa_mem_share() and for all that follows ffa_mem_frag_tx().
>>> + *
>>> + * We use frag_len to keep track of how far into the transmit buffer we
>>> + * have gone.
>>> + */
>>> addr_range = region_descr->address_range_array;
>>> frag_len = ADDR_RANGE_OFFSET(descr->mem_access_count, region_count, 1);
>>> last_pa = page_to_maddr(shm->pages[0]);
>>> init_range(addr_range, last_pa);
>>> + first = true;
>>> for ( n = 1; n < shm->page_count; last_pa = pa, n++ )
>>> {
>>> pa = page_to_maddr(shm->pages[n]);
>>> @@ -1010,12 +1073,34 @@ static int share_shm(struct ffa_shm_mem *shm)
>>> continue;
>>> }
>>>
>>> - frag_len += sizeof(*addr_range);
>>> - addr_range++;
>>> + if ( frag_len == max_frag_len )
>>> + {
>>> + if ( first )
>>> + {
>>> + ret = ffa_mem_share(tot_len, frag_len, 0, 0, &shm->handle);
>>> + first = false;
>>> + }
>>> + else
>>> + {
>>> + ret = ffa_mem_frag_tx(shm->handle, frag_len,
>>> shm->sender_id);
>>> + }
>>> + if ( ret <= 0 )
>>> + return ret;
>>> + frag_len = sizeof(*addr_range);
>>> + addr_range = buf;
>>> + }
>>> + else
>>> + {
>>> + frag_len += sizeof(*addr_range);
>>> + addr_range++;
>>> + }
>>> init_range(addr_range, pa);
>>> }
>>>
>>> - return ffa_mem_share(tot_len, frag_len, 0, 0, &shm->handle);
>>> + if ( first )
>>> + return ffa_mem_share(tot_len, frag_len, 0, 0, &shm->handle);
>>> + else
>>> + return ffa_mem_frag_tx(shm->handle, frag_len, shm->sender_id);
>>> }
>>>
>>> static int read_mem_transaction(uint32_t ffa_vers, const void *buf, size_t
>>> blen,
>>> @@ -1092,8 +1177,53 @@ static int read_mem_transaction(uint32_t ffa_vers,
>>> const void *buf, size_t blen,
>>> return 0;
>>> }
>>>
>>> +static int add_mem_share_frag(struct mem_frag_state *s, unsigned int offs,
>>> + unsigned int frag_len)
>>> +{
>>> + struct domain *d = current->domain;
>>> + unsigned int o = offs;
>>> + unsigned int l;
>>> + int ret;
>>> +
>>> + if ( frag_len < o )
>>> + return FFA_RET_INVALID_PARAMETERS;
>>> +
>>> + /* Fill up the first struct ffa_address_range */
>>> + l = min_t(unsigned int, frag_len - o, sizeof(s->range) -
>>> s->range_offset);
>>> + memcpy((uint8_t *)&s->range + s->range_offset, s->buf + o, l);
>>> + s->range_offset += l;
>>> + o += l;
>>> + if ( s->range_offset != sizeof(s->range) )
>>> + goto out;
>>> + s->range_offset = 0;
>>> +
>>> + while ( true )
>>> + {
>>> + ret = get_shm_pages(d, s->shm, &s->range, 1, s->current_page_idx,
>>> + &s->current_page_idx);
>>> + if ( ret )
>>> + return ret;
>>> + if ( s->range_count == 1 )
>>> + return 0;
>>> + s->range_count--;
>>> + if ( frag_len - o < sizeof(s->range) )
>>> + break;
>>> + memcpy(&s->range, s->buf + o, sizeof(s->range));
>>> + o += sizeof(s->range);
>>> + }
>>> +
>>> + /* Collect any remaining bytes for the next struct ffa_address_range */
>>> + s->range_offset = frag_len - o;
>>> + memcpy(&s->range, s->buf + o, frag_len - o);
>>> +out:
>>> + s->frag_offset += frag_len;
>>> +
>>> + return s->frag_offset;
>>> +}
>>> +
>>> static void handle_mem_share(struct cpu_user_regs *regs)
>>> {
>>> + static uint64_t next_handle = FFA_HANDLE_HYP_FLAG;
>>> uint32_t tot_len = get_user_reg(regs, 1);
>>> uint32_t frag_len = get_user_reg(regs, 2);
>>> uint64_t addr = get_user_reg(regs, 3);
>>> @@ -1128,13 +1258,6 @@ static void handle_mem_share(struct cpu_user_regs
>>> *regs)
>>> goto out_set_ret;
>>> }
>>>
>>> - /* We currently only support a single fragment */
>>> - if ( frag_len != tot_len )
>>> - {
>>> - ret = FFA_RET_NOT_SUPPORTED;
>>> - goto out_set_ret;
>>> - }
>>> -
>>> spin_lock(&ctx->lock);
>>>
>>> if ( frag_len > ctx->page_count * FFA_PAGE_SIZE )
>>> @@ -1195,11 +1318,41 @@ static void handle_mem_share(struct cpu_user_regs
>>> *regs)
>>> if ( !shm )
>>> {
>>> ret = FFA_RET_NO_MEMORY;
>>> - goto out_unlock;
>>> + goto out;
>>
>> Why is this changed ?
>> You still have no shm here so call free_sha_shm does not make sense
>
> Good catch, I'll fix it.
>
>>
>>> }
>>> shm->sender_id = trans.sender_id;
>>> shm->ep_id = read_atomic(&mem_access->access_perm.endpoint_id);
>>>
>>> + if ( frag_len != tot_len )
>>> + {
>>> + struct mem_frag_state *s = xzalloc(struct mem_frag_state);
>>> +
>>> + if ( !s )
>>> + {
>>> + ret = FFA_RET_NO_MEMORY;
>>> + goto out;
>>> + }
>>> + s->shm = shm;
>>> + s->range_count = range_count;
>>> + s->buf = ctx->tx;
>>> + s->buf_size = ffa_page_count * FFA_PAGE_SIZE;
>>> + ret = add_mem_share_frag(s, sizeof(*region_descr) + region_offs,
>>> + frag_len);
>>> + if ( ret <= 0 )
>>> + {
>>> + xfree(s);
>>> + if ( ret < 0 )
>>> + goto out;
>>> + }
>>> + else
>>> + {
>>> + shm->handle = next_handle++;
>>> + uint64_to_regpair(&handle_hi, &handle_lo, shm->handle);
>>> + list_add_tail(&s->list, &ctx->frag_list);
>>> + }
>>> + goto out_unlock;
>>> + }
>>> +
>>> /*
>>> * Check that the Composite memory region descriptor fits.
>>> */
>>> @@ -1238,7 +1391,75 @@ out_unlock:
>>> spin_unlock(&ctx->lock);
>>>
>>> out_set_ret:
>>> - if ( ret == 0)
>>> + if ( ret > 0 )
>>> + set_regs_frag_rx(regs, handle_lo, handle_hi, ret,
>>> trans.sender_id);
>>> + else if ( ret == 0)
>>> + set_regs_success(regs, handle_lo, handle_hi);
>>> + else
>>> + set_regs_error(regs, ret);
>>> +}
>>> +
>>> +static struct mem_frag_state *find_frag_state(struct ffa_ctx *ctx,
>>> + uint64_t handle)
>>> +{
>>> + struct mem_frag_state *s;
>>> +
>>> + list_for_each_entry(s, &ctx->frag_list, list)
>>> + if ( s->shm->handle == handle )
>>> + return s;
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +static void handle_mem_frag_tx(struct cpu_user_regs *regs)
>>> +{
>>> + struct domain *d = current->domain;
>>> + struct ffa_ctx *ctx = d->arch.tee;
>>> + uint32_t frag_len = get_user_reg(regs, 3);
>>> + uint32_t handle_lo = get_user_reg(regs, 1);
>>> + uint32_t handle_hi = get_user_reg(regs, 2);
>>> + uint64_t handle = regpair_to_uint64(handle_hi, handle_lo);
>>> + struct mem_frag_state *s;
>>> + uint16_t sender_id = 0;
>>> + int ret;
>>> +
>>> + spin_lock(&ctx->lock);
>>> + s = find_frag_state(ctx, handle);
>>> + if ( !s )
>>> + {
>>> + ret = FFA_RET_INVALID_PARAMETERS;
>>> + goto out;
>>> + }
>>> + sender_id = s->shm->sender_id;
>>> +
>>> + if ( frag_len > s->buf_size )
>>> + {
>>> + ret = FFA_RET_INVALID_PARAMETERS;
>>> + goto out;
>>> + }
>>> +
>>> + ret = add_mem_share_frag(s, 0, frag_len);
>>> + if ( ret == 0 )
>>> + {
>>> + /* Note that share_shm() uses our tx buffer */
>>> + spin_lock(&ffa_tx_buffer_lock);
>>> + ret = share_shm(s->shm);
>>> + spin_unlock(&ffa_tx_buffer_lock);
>>> + if ( ret == 0 )
>>> + list_add_tail(&s->shm->list, &ctx->shm_list);
>>> + else
>>> + free_ffa_shm_mem(ctx, s->shm);
>>> + }
>>> + else if ( ret < 0 )
>>> + free_ffa_shm_mem(ctx, s->shm);
>>
>>
>> If there is not error the stuff allocated are kept but i do not see
>> where/when they would be freed or used.
>> Could you explain why we need to save all those ?
>
> s->shm is the final shared memory object which is added to the list of
> shared memory objects when the transaction is completed successfully.
> The fragment state, s, is kept as long as the transaction is ongoing.
> Once the transaction is completed successfully or due to a failure
> it's freed.
>
> The specification doesn't say what to do if an invalid frag_len is
> detected, except that we should return FFA_RET_INVALID_PARAMETERS.
> Currently, we just return an error, but keep the state. Another option
> is to free the state instead since the caller might have lost track of
> the state.
There is no solution for the client to "continue" anyway so I think we should
properly cleanup in all exit conditions.
Cheers
Bertrand
>
> Thanks,
> Jens
>
>>
>> Cheers
>> Bertrand
>>
>>> + list_del(&s->list);
>>> + xfree(s);
>>> +out:
>>> + spin_unlock(&ctx->lock);
>>> +
>>> + if ( ret > 0 )
>>> + set_regs_frag_rx(regs, handle_lo, handle_hi, ret, sender_id);
>>> + else if ( ret == 0)
>>> set_regs_success(regs, handle_lo, handle_hi);
>>> else
>>> set_regs_error(regs, ret);
>>> @@ -1357,6 +1578,9 @@ static bool ffa_handle_call(struct cpu_user_regs
>>> *regs)
>>> else
>>> set_regs_success(regs, 0, 0);
>>> return true;
>>> + case FFA_MEM_FRAG_TX:
>>> + handle_mem_frag_tx(regs);
>>> + return true;
>>>
>>> default:
>>> gprintk(XENLOG_ERR, "ffa: unhandled fid 0x%x\n", fid);
>>> @@ -1396,6 +1620,7 @@ static int ffa_domain_init(struct domain *d)
>>> }
>>> }
>>>
>>> + INIT_LIST_HEAD(&ctx->frag_list);
>>> INIT_LIST_HEAD(&ctx->shm_list);
>>>
>>> d->arch.tee = ctx;
>>> @@ -1560,6 +1785,7 @@ static bool ffa_probe(void)
>>> #endif
>>> !check_mandatory_feature(FFA_RXTX_UNMAP) ||
>>> !check_mandatory_feature(FFA_MEM_SHARE_32) ||
>>> + !check_mandatory_feature(FFA_MEM_FRAG_TX) ||
>>> !check_mandatory_feature(FFA_MEM_RECLAIM) ||
>>> !check_mandatory_feature(FFA_MSG_SEND_DIRECT_REQ_32) )
>>> return false;
>>> --
>>> 2.34.1
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |