Unverified Commit f4cb0283 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'ffa-fix-6.18' of...

Merge tag 'ffa-fix-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into arm/fixes

Arm FF-A fix for v6.18

The FF-A driver was updated to support specification version 1.2 but omitted
support for the 16-byte implementation-defined (IMPDEF) field introduced in
FF-A v1.2 within the Endpoint Memory Access Descriptor (EMAD). This omission
breaks all memory interfaces.

This change updates the EMAD sizing and offset logic to correctly handle the
FF-A v1.2 layout while preserving backward compatibility with older versions.

* tag 'ffa-fix-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux

:
  firmware: arm_ffa: Add support for IMPDEF value in the memory access descriptor

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 1d4e7d9f 11fb1a82
Loading
Loading
Loading
Loading
+27 −10
Original line number Diff line number Diff line
@@ -649,6 +649,26 @@ static u16 ffa_memory_attributes_get(u32 func_id)
	return FFA_MEM_NORMAL | FFA_MEM_WRITE_BACK | FFA_MEM_INNER_SHAREABLE;
}

static void ffa_emad_impdef_value_init(u32 version, void *dst, void *src)
{
	struct ffa_mem_region_attributes *ep_mem_access;

	if (FFA_EMAD_HAS_IMPDEF_FIELD(version))
		memcpy(dst, src, sizeof(ep_mem_access->impdef_val));
}

static void
ffa_mem_region_additional_setup(u32 version, struct ffa_mem_region *mem_region)
{
	if (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET(version)) {
		mem_region->ep_mem_size = 0;
	} else {
		mem_region->ep_mem_size = ffa_emad_size_get(version);
		mem_region->ep_mem_offset = sizeof(*mem_region);
		memset(mem_region->reserved, 0, 12);
	}
}

static int
ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
		       struct ffa_mem_ops_args *args)
@@ -667,27 +687,24 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
	mem_region->flags = args->flags;
	mem_region->sender_id = drv_info->vm_id;
	mem_region->attributes = ffa_memory_attributes_get(func_id);
	ep_mem_access = buffer +
			ffa_mem_desc_offset(buffer, 0, drv_info->version);
	composite_offset = ffa_mem_desc_offset(buffer, args->nattrs,
					       drv_info->version);

	for (idx = 0; idx < args->nattrs; idx++, ep_mem_access++) {
	for (idx = 0; idx < args->nattrs; idx++) {
		ep_mem_access = buffer +
			ffa_mem_desc_offset(buffer, idx, drv_info->version);
		ep_mem_access->receiver = args->attrs[idx].receiver;
		ep_mem_access->attrs = args->attrs[idx].attrs;
		ep_mem_access->composite_off = composite_offset;
		ep_mem_access->flag = 0;
		ep_mem_access->reserved = 0;
		ffa_emad_impdef_value_init(drv_info->version,
					   ep_mem_access->impdef_val,
					   args->attrs[idx].impdef_val);
	}
	mem_region->handle = 0;
	mem_region->ep_count = args->nattrs;
	if (drv_info->version <= FFA_VERSION_1_0) {
		mem_region->ep_mem_size = 0;
	} else {
		mem_region->ep_mem_size = sizeof(*ep_mem_access);
		mem_region->ep_mem_offset = sizeof(*mem_region);
		memset(mem_region->reserved, 0, 12);
	}
	ffa_mem_region_additional_setup(drv_info->version, mem_region);

	composite = buffer + composite_offset;
	composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg);
+19 −2
Original line number Diff line number Diff line
@@ -338,6 +338,7 @@ struct ffa_mem_region_attributes {
	 * an `struct ffa_mem_region_addr_range`.
	 */
	u32 composite_off;
	u8 impdef_val[16];
	u64 reserved;
};

@@ -417,15 +418,31 @@ struct ffa_mem_region {
#define CONSTITUENTS_OFFSET(x)	\
	(offsetof(struct ffa_composite_mem_region, constituents[x]))

#define FFA_EMAD_HAS_IMPDEF_FIELD(version)	((version) >= FFA_VERSION_1_2)
#define FFA_MEM_REGION_HAS_EP_MEM_OFFSET(version) ((version) > FFA_VERSION_1_0)

static inline u32 ffa_emad_size_get(u32 ffa_version)
{
	u32 sz;
	struct ffa_mem_region_attributes *ep_mem_access;

	if (FFA_EMAD_HAS_IMPDEF_FIELD(ffa_version))
		sz = sizeof(*ep_mem_access);
	else
		sz = sizeof(*ep_mem_access) - sizeof(ep_mem_access->impdef_val);

	return sz;
}

static inline u32
ffa_mem_desc_offset(struct ffa_mem_region *buf, int count, u32 ffa_version)
{
	u32 offset = count * sizeof(struct ffa_mem_region_attributes);
	u32 offset = count * ffa_emad_size_get(ffa_version);
	/*
	 * Earlier to v1.1, the endpoint memory descriptor array started at
	 * offset 32(i.e. offset of ep_mem_offset in the current structure)
	 */
	if (ffa_version <= FFA_VERSION_1_0)
	if (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET(ffa_version))
		offset += offsetof(struct ffa_mem_region, ep_mem_offset);
	else
		offset += sizeof(struct ffa_mem_region);