Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Arnd Bergmann <arnd@arndb.de>

+46 -12
+27 -10
drivers/firmware/arm_ffa/driver.c
··· 649 649 return FFA_MEM_NORMAL | FFA_MEM_WRITE_BACK | FFA_MEM_INNER_SHAREABLE; 650 650 } 651 651 652 + static void ffa_emad_impdef_value_init(u32 version, void *dst, void *src) 653 + { 654 + struct ffa_mem_region_attributes *ep_mem_access; 655 + 656 + if (FFA_EMAD_HAS_IMPDEF_FIELD(version)) 657 + memcpy(dst, src, sizeof(ep_mem_access->impdef_val)); 658 + } 659 + 660 + static void 661 + ffa_mem_region_additional_setup(u32 version, struct ffa_mem_region *mem_region) 662 + { 663 + if (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET(version)) { 664 + mem_region->ep_mem_size = 0; 665 + } else { 666 + mem_region->ep_mem_size = ffa_emad_size_get(version); 667 + mem_region->ep_mem_offset = sizeof(*mem_region); 668 + memset(mem_region->reserved, 0, 12); 669 + } 670 + } 671 + 652 672 static int 653 673 ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, 654 674 struct ffa_mem_ops_args *args) ··· 687 667 mem_region->flags = args->flags; 688 668 mem_region->sender_id = drv_info->vm_id; 689 669 mem_region->attributes = ffa_memory_attributes_get(func_id); 690 - ep_mem_access = buffer + 691 - ffa_mem_desc_offset(buffer, 0, drv_info->version); 692 670 composite_offset = ffa_mem_desc_offset(buffer, args->nattrs, 693 671 drv_info->version); 694 672 695 - for (idx = 0; idx < args->nattrs; idx++, ep_mem_access++) { 673 + for (idx = 0; idx < args->nattrs; idx++) { 674 + ep_mem_access = buffer + 675 + ffa_mem_desc_offset(buffer, idx, drv_info->version); 696 676 ep_mem_access->receiver = args->attrs[idx].receiver; 697 677 ep_mem_access->attrs = args->attrs[idx].attrs; 698 678 ep_mem_access->composite_off = composite_offset; 699 679 ep_mem_access->flag = 0; 700 680 ep_mem_access->reserved = 0; 681 + ffa_emad_impdef_value_init(drv_info->version, 682 + ep_mem_access->impdef_val, 683 + args->attrs[idx].impdef_val); 701 684 } 702 685 mem_region->handle = 0; 703 686 mem_region->ep_count = args->nattrs; 704 - if (drv_info->version <= FFA_VERSION_1_0) { 705 - mem_region->ep_mem_size = 0; 706 - } else { 707 - mem_region->ep_mem_size = sizeof(*ep_mem_access); 708 - mem_region->ep_mem_offset = sizeof(*mem_region); 709 - memset(mem_region->reserved, 0, 12); 710 - } 687 + ffa_mem_region_additional_setup(drv_info->version, mem_region); 711 688 712 689 composite = buffer + composite_offset; 713 690 composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg);
+19 -2
include/linux/arm_ffa.h
··· 338 338 * an `struct ffa_mem_region_addr_range`. 339 339 */ 340 340 u32 composite_off; 341 + u8 impdef_val[16]; 341 342 u64 reserved; 342 343 }; 343 344 ··· 418 417 #define CONSTITUENTS_OFFSET(x) \ 419 418 (offsetof(struct ffa_composite_mem_region, constituents[x])) 420 419 420 + #define FFA_EMAD_HAS_IMPDEF_FIELD(version) ((version) >= FFA_VERSION_1_2) 421 + #define FFA_MEM_REGION_HAS_EP_MEM_OFFSET(version) ((version) > FFA_VERSION_1_0) 422 + 423 + static inline u32 ffa_emad_size_get(u32 ffa_version) 424 + { 425 + u32 sz; 426 + struct ffa_mem_region_attributes *ep_mem_access; 427 + 428 + if (FFA_EMAD_HAS_IMPDEF_FIELD(ffa_version)) 429 + sz = sizeof(*ep_mem_access); 430 + else 431 + sz = sizeof(*ep_mem_access) - sizeof(ep_mem_access->impdef_val); 432 + 433 + return sz; 434 + } 435 + 421 436 static inline u32 422 437 ffa_mem_desc_offset(struct ffa_mem_region *buf, int count, u32 ffa_version) 423 438 { 424 - u32 offset = count * sizeof(struct ffa_mem_region_attributes); 439 + u32 offset = count * ffa_emad_size_get(ffa_version); 425 440 /* 426 441 * Earlier to v1.1, the endpoint memory descriptor array started at 427 442 * offset 32(i.e. offset of ep_mem_offset in the current structure) 428 443 */ 429 - if (ffa_version <= FFA_VERSION_1_0) 444 + if (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET(ffa_version)) 430 445 offset += offsetof(struct ffa_mem_region, ep_mem_offset); 431 446 else 432 447 offset += sizeof(struct ffa_mem_region);