Arm FF-A updates for v6.15

This update primarily focuses on FF-A framework notification support
 along with other improvements, including UUID handling enhancements
 and various fixes.
 
 1. FF-A framework notification upport
 
     - Adds support for multiple UUIDs per partition to register individual
       SRI callbacks.
     - Handles Rx buffer full framework notifications and provides a general
       interface for future extensions.
 
 2. Improved multiple UUID/services per-partition handling
 
     - Adds support for UUID passing in FFA_MSG_SEND2, improving multiple
       UUID/service support in the driver.
     - Introduces a helper function to check whether a partition can
       receive REQUEST2 messages.
 
 3. Partition handling generic improvements
 
     - Implements device unregistration for better partition cleanup.
     - Improves handling of the host partition presence in partition info.
 
 4. FF-A version updates
 
     - Upgrades the driver version to FF-A v1.2.
     - Rejects major versions higher than the driver version as incompatible.
 
 5. Big-Endian support fixes
 
     - Fixes big-endian issues in:
         __ffa_partition_info_regs_get()
         __ffa_partition_info_get()
     - Big-endian support is still incomplete, and only these changes can
       be verified without additional application/testing updates at the
       moment. We can discover all the partitions correctly with big-endian
       kernel now.
 
 6. Miscellaneous fixes
 
     - Fixes function prototype misalignments in: sync_send_receive{,2}
     - Adds explicit type casting for return values from: FFA_VERSION
       and NOTIFICATION_INFO_GET
     - Corrects vCPU list parsing in ffa_notification_info_get().
 
 7. UUID management in the driver and DMA mask updates
 
     - Replaces UUID buffer with the standard UUID format in ffa_partition_info
       structure.
     - Fixes a typo in some FF-A bus macros.
     - Sets dma_mask for FF-A devices.
 
 In short, this update enhances notification handling, UUID support, and
 overall robustness of the FF-A driver while addressing multiple fixes
 and cleanups.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEunHlEgbzHrJD3ZPhAEG6vDF+4pgFAmfG2gkACgkQAEG6vDF+
 4pgCsA//UN0T8cX8BxgAYjEjGYxrwzvJJsjFN4gxIeiXa0aLUWRObSNbr8qkDWd/
 MnWYgDdmy5DdzBFc5yGnpTlgS7hQFPCywfnxU2sErJmFthG9FsrcZ4C3xss+aLP9
 QTqn8QpOMIAMegg+35FkzLaIcPjkJbAJNMvvBmC0BJUhU4eJFrtSXTnc4ijJDk0y
 jNxW/il0gSv6d/GJOiHDop+0W+XrxuCvbAtOzshiIZ6eZEew5rY1EJdHPsEAuh7h
 5tP1mIQZbXD8iwgKQmVqh+gSAUCRvK1l9WfsOTw6gK7+IL3kpuSOP0UjR/mB6F0Y
 TivwuFQtDlbpLD/TbiJqkTzJVF+aWouq5onv/hFUVSapcWlXlb8QO2mHXhCblgTV
 rTAi61WVlU95RB7ZmKG0pBig1LRsCw/tc9u2+zv8jOmVc9zDDYG7YHguOtx8d8uY
 z9yNXooNzes9F1EZyxQDhe/f/q7QKs9XUnG6WBmd6FXF0F3qDAfi+yySOY1fUUdh
 cO7Vy4bNNywg+/U+ePqMm11DnjLIjIny9z9s3ruyyQ4LuCTGq/oJc0nLaniH3ukb
 iynZVUXbXu1PXm5bGW+CpoA/ivlLo+FuFPIFgwXFVlgACvnNDfJXKG5a/5SxHbpD
 b4VCRkMz2wmamwYD6veZBq4AOHkrnWlnF6eZs08z1PBIzd2kfLo=
 =qX1J
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmfJyysACgkQYKtH/8kJ
 UifldBAAmSmOhxSHMSW9D5vtJW33XPsHf3zjcDjHHVNoF4AHZDWpvN2xTbBRhe1c
 iPf4R1A+Otfa0BfxTFyG5gMIMfyRGjSmkveDVlnnrn+dLafc2AyiHZsOAaz6PvSN
 zG3FFbMSPKBqNVy2CEbDDb/HfOtodzIpiqcHDO/kBSlrMz7hNn81TsOQ+BqvMjNE
 d7yddFvdEZZYGphHD6Q/cOcrFfuWNhfYkn130sORg8eolIhp+SsXXBKlOxgjBtRX
 TbRW6FUCE+aEZtyrzOpsopI9zqXsgPy7h6BgnlDc3INB+1N1re7zQlvh4Jsqn4wh
 DmhAhrPLf8NzkUuIIRUDPBoNRwIP+1MvGAvKWq8FyMh2hvZ+ARW87FxiABEiwCbc
 /N1waQrapdLB695cKAEDCaBrgN+opxWOx7P6X3tCRAirIRpEtNaLJ/Awd4lfDoyK
 qacIjsw+KQy9TGi1k/TPslVZ5E2wiVqmlEJjyC3KchVK7AIDDFQ480C7IC9gnJPZ
 TVlyUGWN0iHK8Aer0AvO0lV4z5E0fL6ovJhPX/8/3uLaPNFKXs5+m5+0i0RJPYw9
 0phlX7CyXSlm9IMmPQv2BgPI/+dKifRA0mtoDvtfQe6gbpihnXYcm0+90jVaa/bE
 DxaLozWLoQqWc22nQCDFJ6XUK5WC11bRr5KYFg07FqSYFEE/JA0=
 =Phyc
 -----END PGP SIGNATURE-----

Merge tag 'ffa-updates-6.15' of https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into soc/drivers

Arm FF-A updates for v6.15

This update primarily focuses on FF-A framework notification support
along with other improvements, including UUID handling enhancements
and various fixes.

1. FF-A framework notification upport

    - Adds support for multiple UUIDs per partition to register individual
      SRI callbacks.
    - Handles Rx buffer full framework notifications and provides a general
      interface for future extensions.

2. Improved multiple UUID/services per-partition handling

    - Adds support for UUID passing in FFA_MSG_SEND2, improving multiple
      UUID/service support in the driver.
    - Introduces a helper function to check whether a partition can
      receive REQUEST2 messages.

3. Partition handling generic improvements

    - Implements device unregistration for better partition cleanup.
    - Improves handling of the host partition presence in partition info.

4. FF-A version updates

    - Upgrades the driver version to FF-A v1.2.
    - Rejects major versions higher than the driver version as incompatible.

5. Big-Endian support fixes

    - Fixes big-endian issues in:
        __ffa_partition_info_regs_get()
        __ffa_partition_info_get()
    - Big-endian support is still incomplete, and only these changes can
      be verified without additional application/testing updates at the
      moment. We can discover all the partitions correctly with big-endian
      kernel now.

6. Miscellaneous fixes

    - Fixes function prototype misalignments in: sync_send_receive{,2}
    - Adds explicit type casting for return values from: FFA_VERSION
      and NOTIFICATION_INFO_GET
    - Corrects vCPU list parsing in ffa_notification_info_get().

7. UUID management in the driver and DMA mask updates

    - Replaces UUID buffer with the standard UUID format in ffa_partition_info
      structure.
    - Fixes a typo in some FF-A bus macros.
    - Sets dma_mask for FF-A devices.

In short, this update enhances notification handling, UUID support, and
overall robustness of the FF-A driver while addressing multiple fixes
and cleanups.

* tag 'ffa-updates-6.15' of https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux: (23 commits)
  firmware: arm_ffa: Set dma_mask for ffa devices
  firmware: arm_ffa: Skip the first/partition ID when parsing vCPU list
  firmware: arm_ffa: Explicitly cast return value from NOTIFICATION_INFO_GET
  firmware: arm_ffa: Explicitly cast return value from FFA_VERSION before comparison
  firmware: arm_ffa: Handle ffa_notification_get correctly at virtual FF-A instance
  firmware: arm_ffa: Allow multiple UUIDs per partition to register SRI callback
  firmware: arm_ffa: Add support for handling framework notifications
  firmware: arm_ffa: Add support for {un,}registration of framework notifications
  firmware: arm_ffa: Stash ffa_device instead of notify_type in notifier_cb_info
  firmware: arm_ffa: Refactoring to prepare for framework notification support
  firmware: arm_ffa: Remove unnecessary declaration of ffa_partitions_cleanup()
  firmware: arm_ffa: Reject higher major version as incompatible
  firmware: arm_ffa: Upgrade FF-A version to v1.2 in the driver
  firmware: arm_ffa: Add support for passing UUID in FFA_MSG_SEND2
  firmware: arm_ffa: Helper to check if a partition can receive REQUEST2 messages
  firmware: arm_ffa: Unregister the FF-A devices when cleaning up the partitions
  firmware: arm_ffa: Handle the presence of host partition in the partition info
  firmware: arm_ffa: Refactor addition of partition information into XArray
  firmware: arm_ffa: Fix big-endian support in __ffa_partition_info_regs_get()
  firmware: arm_ffa: Fix big-endian support in __ffa_partition_info_get()
  ...

Link: https://lore.kernel.org/r/20250304105928.432997-1-sudeep.holla@arm.com
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2025-03-06 17:19:45 +01:00
commit 4f1afeaa30
3 changed files with 440 additions and 164 deletions

View File

@ -15,7 +15,7 @@
#include "common.h"
#define SCMI_UEVENT_MODALIAS_FMT "arm_ffa:%04x:%pUb"
#define FFA_UEVENT_MODALIAS_FMT "arm_ffa:%04x:%pUb"
static DEFINE_IDA(ffa_bus_id);
@ -68,7 +68,7 @@ static int ffa_device_uevent(const struct device *dev, struct kobj_uevent_env *e
{
const struct ffa_device *ffa_dev = to_ffa_dev(dev);
return add_uevent_var(env, "MODALIAS=" SCMI_UEVENT_MODALIAS_FMT,
return add_uevent_var(env, "MODALIAS=" FFA_UEVENT_MODALIAS_FMT,
ffa_dev->vm_id, &ffa_dev->uuid);
}
@ -77,7 +77,7 @@ static ssize_t modalias_show(struct device *dev,
{
struct ffa_device *ffa_dev = to_ffa_dev(dev);
return sysfs_emit(buf, SCMI_UEVENT_MODALIAS_FMT, ffa_dev->vm_id,
return sysfs_emit(buf, FFA_UEVENT_MODALIAS_FMT, ffa_dev->vm_id,
&ffa_dev->uuid);
}
static DEVICE_ATTR_RO(modalias);
@ -160,11 +160,12 @@ static int __ffa_devices_unregister(struct device *dev, void *data)
return 0;
}
static void ffa_devices_unregister(void)
void ffa_devices_unregister(void)
{
bus_for_each_dev(&ffa_bus_type, NULL, NULL,
__ffa_devices_unregister);
}
EXPORT_SYMBOL_GPL(ffa_devices_unregister);
bool ffa_device_is_valid(struct ffa_device *ffa_dev)
{
@ -192,7 +193,6 @@ ffa_device_register(const struct ffa_partition_info *part_info,
const struct ffa_ops *ops)
{
int id, ret;
uuid_t uuid;
struct device *dev;
struct ffa_device *ffa_dev;
@ -212,14 +212,14 @@ ffa_device_register(const struct ffa_partition_info *part_info,
dev = &ffa_dev->dev;
dev->bus = &ffa_bus_type;
dev->release = ffa_release_device;
dev->dma_mask = &dev->coherent_dma_mask;
dev_set_name(&ffa_dev->dev, "arm-ffa-%d", id);
ffa_dev->id = id;
ffa_dev->vm_id = part_info->id;
ffa_dev->properties = part_info->properties;
ffa_dev->ops = ops;
import_uuid(&uuid, (u8 *)part_info->uuid);
uuid_copy(&ffa_dev->uuid, &uuid);
uuid_copy(&ffa_dev->uuid, &part_info->uuid);
ret = device_register(&ffa_dev->dev);
if (ret) {

View File

@ -44,7 +44,7 @@
#include "common.h"
#define FFA_DRIVER_VERSION FFA_VERSION_1_1
#define FFA_DRIVER_VERSION FFA_VERSION_1_2
#define FFA_MIN_VERSION FFA_VERSION_1_0
#define SENDER_ID_MASK GENMASK(31, 16)
@ -114,7 +114,6 @@ struct ffa_drv_info {
};
static struct ffa_drv_info *drv_info;
static void ffa_partitions_cleanup(void);
/*
* The driver must be able to support all the versions from the earliest
@ -145,11 +144,19 @@ static int ffa_version_check(u32 *version)
.a0 = FFA_VERSION, .a1 = FFA_DRIVER_VERSION,
}, &ver);
if (ver.a0 == FFA_RET_NOT_SUPPORTED) {
if ((s32)ver.a0 == FFA_RET_NOT_SUPPORTED) {
pr_info("FFA_VERSION returned not supported\n");
return -EOPNOTSUPP;
}
if (FFA_MAJOR_VERSION(ver.a0) > FFA_MAJOR_VERSION(FFA_DRIVER_VERSION)) {
pr_err("Incompatible v%d.%d! Latest supported v%d.%d\n",
FFA_MAJOR_VERSION(ver.a0), FFA_MINOR_VERSION(ver.a0),
FFA_MAJOR_VERSION(FFA_DRIVER_VERSION),
FFA_MINOR_VERSION(FFA_DRIVER_VERSION));
return -EINVAL;
}
if (ver.a0 < FFA_MIN_VERSION) {
pr_err("Incompatible v%d.%d! Earliest supported v%d.%d\n",
FFA_MAJOR_VERSION(ver.a0), FFA_MINOR_VERSION(ver.a0),
@ -276,9 +283,21 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
}
if (buffer && count <= num_partitions)
for (idx = 0; idx < count; idx++)
memcpy(buffer + idx, drv_info->rx_buffer + idx * sz,
buf_sz);
for (idx = 0; idx < count; idx++) {
struct ffa_partition_info_le {
__le16 id;
__le16 exec_ctxt;
__le32 properties;
uuid_t uuid;
} *rx_buf = drv_info->rx_buffer + idx * sz;
struct ffa_partition_info *buf = buffer + idx;
buf->id = le16_to_cpu(rx_buf->id);
buf->exec_ctxt = le16_to_cpu(rx_buf->exec_ctxt);
buf->properties = le32_to_cpu(rx_buf->properties);
if (buf_sz > 8)
import_uuid(&buf->uuid, (u8 *)&rx_buf->uuid);
}
ffa_rx_release();
@ -295,14 +314,24 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
#define CURRENT_INDEX(x) ((u16)(FIELD_GET(CURRENT_INDEX_MASK, (x))))
#define UUID_INFO_TAG(x) ((u16)(FIELD_GET(UUID_INFO_TAG_MASK, (x))))
#define PARTITION_INFO_SZ(x) ((u16)(FIELD_GET(PARTITION_INFO_SZ_MASK, (x))))
#define PART_INFO_ID_MASK GENMASK(15, 0)
#define PART_INFO_EXEC_CXT_MASK GENMASK(31, 16)
#define PART_INFO_PROPS_MASK GENMASK(63, 32)
#define PART_INFO_ID(x) ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x))))
#define PART_INFO_EXEC_CXT(x) ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x))))
#define PART_INFO_PROPERTIES(x) ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x))))
static int
__ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
struct ffa_partition_info *buffer, int num_parts)
{
u16 buf_sz, start_idx, cur_idx, count = 0, prev_idx = 0, tag = 0;
struct ffa_partition_info *buf = buffer;
ffa_value_t partition_info;
do {
__le64 *regs;
int idx;
start_idx = prev_idx ? prev_idx + 1 : 0;
invoke_ffa_fn((ffa_value_t){
@ -326,8 +355,25 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
if (buf_sz > sizeof(*buffer))
buf_sz = sizeof(*buffer);
memcpy(buffer + prev_idx * buf_sz, &partition_info.a3,
(cur_idx - start_idx + 1) * buf_sz);
regs = (void *)&partition_info.a3;
for (idx = 0; idx < cur_idx - start_idx + 1; idx++, buf++) {
union {
uuid_t uuid;
u64 regs[2];
} uuid_regs = {
.regs = {
le64_to_cpu(*(regs + 1)),
le64_to_cpu(*(regs + 2)),
}
};
u64 val = *(u64 *)regs;
buf->id = PART_INFO_ID(val);
buf->exec_ctxt = PART_INFO_EXEC_CXT(val);
buf->properties = PART_INFO_PROPERTIES(val);
uuid_copy(&buf->uuid, &uuid_regs.uuid);
regs += 3;
}
prev_idx = cur_idx;
} while (cur_idx < (count - 1));
@ -445,9 +491,9 @@ static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit,
return -EINVAL;
}
static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
static int ffa_msg_send2(struct ffa_device *dev, u16 src_id, void *buf, size_t sz)
{
u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
u32 src_dst_ids = PACK_TARGET_INFO(src_id, dev->vm_id);
struct ffa_indirect_msg_hdr *msg;
ffa_value_t ret;
int retval = 0;
@ -463,6 +509,7 @@ static int ffa_msg_send2(u16 src_id, u16 dst_id, void *buf, size_t sz)
msg->offset = sizeof(*msg);
msg->send_recv_id = src_dst_ids;
msg->size = sz;
uuid_copy(&msg->uuid, &dev->uuid);
memcpy((u8 *)msg + msg->offset, buf, sz);
/* flags = 0, sender VMID = 0 works for both physical/virtual NS */
@ -760,6 +807,13 @@ static int ffa_notification_bitmap_destroy(void)
return 0;
}
enum notify_type {
SECURE_PARTITION,
NON_SECURE_VM,
SPM_FRAMEWORK,
NS_HYP_FRAMEWORK,
};
#define NOTIFICATION_LOW_MASK GENMASK(31, 0)
#define NOTIFICATION_HIGH_MASK GENMASK(63, 32)
#define NOTIFICATION_BITMAP_HIGH(x) \
@ -783,10 +837,22 @@ static int ffa_notification_bitmap_destroy(void)
#define MAX_IDS_32 10
#define PER_VCPU_NOTIFICATION_FLAG BIT(0)
#define SECURE_PARTITION_BITMAP BIT(0)
#define NON_SECURE_VM_BITMAP BIT(1)
#define SPM_FRAMEWORK_BITMAP BIT(2)
#define NS_HYP_FRAMEWORK_BITMAP BIT(3)
#define SECURE_PARTITION_BITMAP_ENABLE BIT(SECURE_PARTITION)
#define NON_SECURE_VM_BITMAP_ENABLE BIT(NON_SECURE_VM)
#define SPM_FRAMEWORK_BITMAP_ENABLE BIT(SPM_FRAMEWORK)
#define NS_HYP_FRAMEWORK_BITMAP_ENABLE BIT(NS_HYP_FRAMEWORK)
#define FFA_BITMAP_SECURE_ENABLE_MASK \
(SECURE_PARTITION_BITMAP_ENABLE | SPM_FRAMEWORK_BITMAP_ENABLE)
#define FFA_BITMAP_NS_ENABLE_MASK \
(NON_SECURE_VM_BITMAP_ENABLE | NS_HYP_FRAMEWORK_BITMAP_ENABLE)
#define FFA_BITMAP_ALL_ENABLE_MASK \
(FFA_BITMAP_SECURE_ENABLE_MASK | FFA_BITMAP_NS_ENABLE_MASK)
#define FFA_SECURE_PARTITION_ID_FLAG BIT(15)
#define SPM_FRAMEWORK_BITMAP(x) NOTIFICATION_BITMAP_LOW(x)
#define NS_HYP_FRAMEWORK_BITMAP(x) NOTIFICATION_BITMAP_HIGH(x)
#define FRAMEWORK_NOTIFY_RX_BUFFER_FULL BIT(0)
static int ffa_notification_bind_common(u16 dst_id, u64 bitmap,
u32 flags, bool is_bind)
@ -852,9 +918,15 @@ static int ffa_notification_get(u32 flags, struct ffa_notify_bitmaps *notify)
else if (ret.a0 != FFA_SUCCESS)
return -EINVAL; /* Something else went wrong. */
notify->sp_map = PACK_NOTIFICATION_BITMAP(ret.a2, ret.a3);
notify->vm_map = PACK_NOTIFICATION_BITMAP(ret.a4, ret.a5);
notify->arch_map = PACK_NOTIFICATION_BITMAP(ret.a6, ret.a7);
if (flags & SECURE_PARTITION_BITMAP_ENABLE)
notify->sp_map = PACK_NOTIFICATION_BITMAP(ret.a2, ret.a3);
if (flags & NON_SECURE_VM_BITMAP_ENABLE)
notify->vm_map = PACK_NOTIFICATION_BITMAP(ret.a4, ret.a5);
if (flags & SPM_FRAMEWORK_BITMAP_ENABLE)
notify->arch_map = SPM_FRAMEWORK_BITMAP(ret.a6);
if (flags & NS_HYP_FRAMEWORK_BITMAP_ENABLE)
notify->arch_map = PACK_NOTIFICATION_BITMAP(notify->arch_map,
ret.a7);
return 0;
}
@ -863,27 +935,32 @@ struct ffa_dev_part_info {
ffa_sched_recv_cb callback;
void *cb_data;
rwlock_t rw_lock;
struct ffa_device *dev;
struct list_head node;
};
static void __do_sched_recv_cb(u16 part_id, u16 vcpu, bool is_per_vcpu)
{
struct ffa_dev_part_info *partition;
struct ffa_dev_part_info *partition = NULL, *tmp;
ffa_sched_recv_cb callback;
struct list_head *phead;
void *cb_data;
partition = xa_load(&drv_info->partition_info, part_id);
if (!partition) {
phead = xa_load(&drv_info->partition_info, part_id);
if (!phead) {
pr_err("%s: Invalid partition ID 0x%x\n", __func__, part_id);
return;
}
read_lock(&partition->rw_lock);
callback = partition->callback;
cb_data = partition->cb_data;
read_unlock(&partition->rw_lock);
list_for_each_entry_safe(partition, tmp, phead, node) {
read_lock(&partition->rw_lock);
callback = partition->callback;
cb_data = partition->cb_data;
read_unlock(&partition->rw_lock);
if (callback)
callback(vcpu, is_per_vcpu, cb_data);
if (callback)
callback(vcpu, is_per_vcpu, cb_data);
}
}
static void ffa_notification_info_get(void)
@ -899,7 +976,7 @@ static void ffa_notification_info_get(void)
}, &ret);
if (ret.a0 != FFA_FN_NATIVE(SUCCESS) && ret.a0 != FFA_SUCCESS) {
if (ret.a2 != FFA_RET_NO_DATA)
if ((s32)ret.a2 != FFA_RET_NO_DATA)
pr_err("Notification Info fetch failed: 0x%lx (0x%lx)",
ret.a0, ret.a2);
return;
@ -935,7 +1012,7 @@ static void ffa_notification_info_get(void)
}
/* Per vCPU Notification */
for (idx = 0; idx < ids_count[list]; idx++) {
for (idx = 1; idx < ids_count[list]; idx++) {
if (ids_processed >= max_ids - 1)
break;
@ -1015,17 +1092,17 @@ static int ffa_sync_send_receive(struct ffa_device *dev,
static int ffa_indirect_msg_send(struct ffa_device *dev, void *buf, size_t sz)
{
return ffa_msg_send2(drv_info->vm_id, dev->vm_id, buf, sz);
return ffa_msg_send2(dev, drv_info->vm_id, buf, sz);
}
static int ffa_sync_send_receive2(struct ffa_device *dev, const uuid_t *uuid,
static int ffa_sync_send_receive2(struct ffa_device *dev,
struct ffa_send_direct_data2 *data)
{
if (!drv_info->msg_direct_req2_supp)
return -EOPNOTSUPP;
return ffa_msg_send_direct_req2(drv_info->vm_id, dev->vm_id,
uuid, data);
&dev->uuid, data);
}
static int ffa_memory_share(struct ffa_mem_ops_args *args)
@ -1051,35 +1128,39 @@ static int ffa_memory_lend(struct ffa_mem_ops_args *args)
return ffa_memory_ops(FFA_MEM_LEND, args);
}
#define FFA_SECURE_PARTITION_ID_FLAG BIT(15)
#define ffa_notifications_disabled() (!drv_info->notif_enabled)
enum notify_type {
NON_SECURE_VM,
SECURE_PARTITION,
FRAMEWORK,
};
struct notifier_cb_info {
struct hlist_node hnode;
struct ffa_device *dev;
ffa_fwk_notifier_cb fwk_cb;
ffa_notifier_cb cb;
void *cb_data;
enum notify_type type;
};
static int ffa_sched_recv_cb_update(u16 part_id, ffa_sched_recv_cb callback,
void *cb_data, bool is_registration)
static int
ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback,
void *cb_data, bool is_registration)
{
struct ffa_dev_part_info *partition;
struct ffa_dev_part_info *partition = NULL, *tmp;
struct list_head *phead;
bool cb_valid;
if (ffa_notifications_disabled())
return -EOPNOTSUPP;
partition = xa_load(&drv_info->partition_info, part_id);
phead = xa_load(&drv_info->partition_info, dev->vm_id);
if (!phead) {
pr_err("%s: Invalid partition ID 0x%x\n", __func__, dev->vm_id);
return -EINVAL;
}
list_for_each_entry_safe(partition, tmp, phead, node)
if (partition->dev == dev)
break;
if (!partition) {
pr_err("%s: Invalid partition ID 0x%x\n", __func__, part_id);
pr_err("%s: No such partition ID 0x%x\n", __func__, dev->vm_id);
return -EINVAL;
}
@ -1101,12 +1182,12 @@ static int ffa_sched_recv_cb_update(u16 part_id, ffa_sched_recv_cb callback,
static int ffa_sched_recv_cb_register(struct ffa_device *dev,
ffa_sched_recv_cb cb, void *cb_data)
{
return ffa_sched_recv_cb_update(dev->vm_id, cb, cb_data, true);
return ffa_sched_recv_cb_update(dev, cb, cb_data, true);
}
static int ffa_sched_recv_cb_unregister(struct ffa_device *dev)
{
return ffa_sched_recv_cb_update(dev->vm_id, NULL, NULL, false);
return ffa_sched_recv_cb_update(dev, NULL, NULL, false);
}
static int ffa_notification_bind(u16 dst_id, u64 bitmap, u32 flags)
@ -1119,27 +1200,69 @@ static int ffa_notification_unbind(u16 dst_id, u64 bitmap)
return ffa_notification_bind_common(dst_id, bitmap, 0, false);
}
/* Should be called while the notify_lock is taken */
static enum notify_type ffa_notify_type_get(u16 vm_id)
{
if (vm_id & FFA_SECURE_PARTITION_ID_FLAG)
return SECURE_PARTITION;
else
return NON_SECURE_VM;
}
/* notifier_hnode_get* should be called with notify_lock held */
static struct notifier_cb_info *
notifier_hash_node_get(u16 notify_id, enum notify_type type)
notifier_hnode_get_by_vmid(u16 notify_id, int vmid)
{
struct notifier_cb_info *node;
hash_for_each_possible(drv_info->notifier_hash, node, hnode, notify_id)
if (type == node->type)
if (node->fwk_cb && vmid == node->dev->vm_id)
return node;
return NULL;
}
static struct notifier_cb_info *
notifier_hnode_get_by_vmid_uuid(u16 notify_id, int vmid, const uuid_t *uuid)
{
struct notifier_cb_info *node;
if (uuid_is_null(uuid))
return notifier_hnode_get_by_vmid(notify_id, vmid);
hash_for_each_possible(drv_info->notifier_hash, node, hnode, notify_id)
if (node->fwk_cb && vmid == node->dev->vm_id &&
uuid_equal(&node->dev->uuid, uuid))
return node;
return NULL;
}
static struct notifier_cb_info *
notifier_hnode_get_by_type(u16 notify_id, enum notify_type type)
{
struct notifier_cb_info *node;
hash_for_each_possible(drv_info->notifier_hash, node, hnode, notify_id)
if (node->cb && type == ffa_notify_type_get(node->dev->vm_id))
return node;
return NULL;
}
static int
update_notifier_cb(int notify_id, enum notify_type type, ffa_notifier_cb cb,
void *cb_data, bool is_registration)
update_notifier_cb(struct ffa_device *dev, int notify_id, void *cb,
void *cb_data, bool is_registration, bool is_framework)
{
struct notifier_cb_info *cb_info = NULL;
enum notify_type type = ffa_notify_type_get(dev->vm_id);
bool cb_found;
cb_info = notifier_hash_node_get(notify_id, type);
if (is_framework)
cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, dev->vm_id,
&dev->uuid);
else
cb_info = notifier_hnode_get_by_type(notify_id, type);
cb_found = !!cb_info;
if (!(is_registration ^ cb_found))
@ -1150,9 +1273,12 @@ update_notifier_cb(int notify_id, enum notify_type type, ffa_notifier_cb cb,
if (!cb_info)
return -ENOMEM;
cb_info->type = type;
cb_info->cb = cb;
cb_info->dev = dev;
cb_info->cb_data = cb_data;
if (is_framework)
cb_info->fwk_cb = cb;
else
cb_info->cb = cb;
hash_add(drv_info->notifier_hash, &cb_info->hnode, notify_id);
} else {
@ -1162,18 +1288,10 @@ update_notifier_cb(int notify_id, enum notify_type type, ffa_notifier_cb cb,
return 0;
}
static enum notify_type ffa_notify_type_get(u16 vm_id)
{
if (vm_id & FFA_SECURE_PARTITION_ID_FLAG)
return SECURE_PARTITION;
else
return NON_SECURE_VM;
}
static int ffa_notify_relinquish(struct ffa_device *dev, int notify_id)
static int __ffa_notify_relinquish(struct ffa_device *dev, int notify_id,
bool is_framework)
{
int rc;
enum notify_type type = ffa_notify_type_get(dev->vm_id);
if (ffa_notifications_disabled())
return -EOPNOTSUPP;
@ -1183,53 +1301,83 @@ static int ffa_notify_relinquish(struct ffa_device *dev, int notify_id)
mutex_lock(&drv_info->notify_lock);
rc = update_notifier_cb(notify_id, type, NULL, NULL, false);
rc = update_notifier_cb(dev, notify_id, NULL, NULL, false,
is_framework);
if (rc) {
pr_err("Could not unregister notification callback\n");
mutex_unlock(&drv_info->notify_lock);
return rc;
}
rc = ffa_notification_unbind(dev->vm_id, BIT(notify_id));
if (!is_framework)
rc = ffa_notification_unbind(dev->vm_id, BIT(notify_id));
mutex_unlock(&drv_info->notify_lock);
return rc;
}
static int ffa_notify_relinquish(struct ffa_device *dev, int notify_id)
{
return __ffa_notify_relinquish(dev, notify_id, false);
}
static int ffa_fwk_notify_relinquish(struct ffa_device *dev, int notify_id)
{
return __ffa_notify_relinquish(dev, notify_id, true);
}
static int __ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu,
void *cb, void *cb_data,
int notify_id, bool is_framework)
{
int rc;
u32 flags = 0;
if (ffa_notifications_disabled())
return -EOPNOTSUPP;
if (notify_id >= FFA_MAX_NOTIFICATIONS)
return -EINVAL;
mutex_lock(&drv_info->notify_lock);
if (!is_framework) {
if (is_per_vcpu)
flags = PER_VCPU_NOTIFICATION_FLAG;
rc = ffa_notification_bind(dev->vm_id, BIT(notify_id), flags);
if (rc) {
mutex_unlock(&drv_info->notify_lock);
return rc;
}
}
rc = update_notifier_cb(dev, notify_id, cb, cb_data, true,
is_framework);
if (rc) {
pr_err("Failed to register callback for %d - %d\n",
notify_id, rc);
if (!is_framework)
ffa_notification_unbind(dev->vm_id, BIT(notify_id));
}
mutex_unlock(&drv_info->notify_lock);
return rc;
}
static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu,
ffa_notifier_cb cb, void *cb_data, int notify_id)
{
int rc;
u32 flags = 0;
enum notify_type type = ffa_notify_type_get(dev->vm_id);
return __ffa_notify_request(dev, is_per_vcpu, cb, cb_data, notify_id,
false);
}
if (ffa_notifications_disabled())
return -EOPNOTSUPP;
if (notify_id >= FFA_MAX_NOTIFICATIONS)
return -EINVAL;
mutex_lock(&drv_info->notify_lock);
if (is_per_vcpu)
flags = PER_VCPU_NOTIFICATION_FLAG;
rc = ffa_notification_bind(dev->vm_id, BIT(notify_id), flags);
if (rc) {
mutex_unlock(&drv_info->notify_lock);
return rc;
}
rc = update_notifier_cb(notify_id, type, cb, cb_data, true);
if (rc) {
pr_err("Failed to register callback for %d - %d\n",
notify_id, rc);
ffa_notification_unbind(dev->vm_id, BIT(notify_id));
}
mutex_unlock(&drv_info->notify_lock);
return rc;
static int
ffa_fwk_notify_request(struct ffa_device *dev, ffa_fwk_notifier_cb cb,
void *cb_data, int notify_id)
{
return __ffa_notify_request(dev, false, cb, cb_data, notify_id, true);
}
static int ffa_notify_send(struct ffa_device *dev, int notify_id,
@ -1258,7 +1406,7 @@ static void handle_notif_callbacks(u64 bitmap, enum notify_type type)
continue;
mutex_lock(&drv_info->notify_lock);
cb_info = notifier_hash_node_get(notify_id, type);
cb_info = notifier_hnode_get_by_type(notify_id, type);
mutex_unlock(&drv_info->notify_lock);
if (cb_info && cb_info->cb)
@ -1266,21 +1414,68 @@ static void handle_notif_callbacks(u64 bitmap, enum notify_type type)
}
}
static void notif_get_and_handle(void *unused)
static void handle_fwk_notif_callbacks(u32 bitmap)
{
void *buf;
uuid_t uuid;
int notify_id = 0, target;
struct ffa_indirect_msg_hdr *msg;
struct notifier_cb_info *cb_info = NULL;
/* Only one framework notification defined and supported for now */
if (!(bitmap & FRAMEWORK_NOTIFY_RX_BUFFER_FULL))
return;
mutex_lock(&drv_info->rx_lock);
msg = drv_info->rx_buffer;
buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL);
if (!buf) {
mutex_unlock(&drv_info->rx_lock);
return;
}
target = SENDER_ID(msg->send_recv_id);
if (msg->offset >= sizeof(*msg))
uuid_copy(&uuid, &msg->uuid);
else
uuid_copy(&uuid, &uuid_null);
mutex_unlock(&drv_info->rx_lock);
ffa_rx_release();
mutex_lock(&drv_info->notify_lock);
cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid);
mutex_unlock(&drv_info->notify_lock);
if (cb_info && cb_info->fwk_cb)
cb_info->fwk_cb(notify_id, cb_info->cb_data, buf);
kfree(buf);
}
static void notif_get_and_handle(void *cb_data)
{
int rc;
struct ffa_notify_bitmaps bitmaps;
u32 flags;
struct ffa_drv_info *info = cb_data;
struct ffa_notify_bitmaps bitmaps = { 0 };
rc = ffa_notification_get(SECURE_PARTITION_BITMAP |
SPM_FRAMEWORK_BITMAP, &bitmaps);
if (info->vm_id == 0) /* Non secure physical instance */
flags = FFA_BITMAP_SECURE_ENABLE_MASK;
else
flags = FFA_BITMAP_ALL_ENABLE_MASK;
rc = ffa_notification_get(flags, &bitmaps);
if (rc) {
pr_err("Failed to retrieve notifications with %d!\n", rc);
return;
}
handle_fwk_notif_callbacks(SPM_FRAMEWORK_BITMAP(bitmaps.arch_map));
handle_fwk_notif_callbacks(NS_HYP_FRAMEWORK_BITMAP(bitmaps.arch_map));
handle_notif_callbacks(bitmaps.vm_map, NON_SECURE_VM);
handle_notif_callbacks(bitmaps.sp_map, SECURE_PARTITION);
handle_notif_callbacks(bitmaps.arch_map, FRAMEWORK);
}
static void
@ -1329,6 +1524,8 @@ static const struct ffa_notifier_ops ffa_drv_notifier_ops = {
.sched_recv_cb_unregister = ffa_sched_recv_cb_unregister,
.notify_request = ffa_notify_request,
.notify_relinquish = ffa_notify_relinquish,
.fwk_notify_request = ffa_fwk_notify_request,
.fwk_notify_relinquish = ffa_fwk_notify_relinquish,
.notify_send = ffa_notify_send,
};
@ -1384,11 +1581,110 @@ static struct notifier_block ffa_bus_nb = {
.notifier_call = ffa_bus_notifier,
};
static int ffa_xa_add_partition_info(struct ffa_device *dev)
{
struct ffa_dev_part_info *info;
struct list_head *head, *phead;
int ret = -ENOMEM;
phead = xa_load(&drv_info->partition_info, dev->vm_id);
if (phead) {
head = phead;
list_for_each_entry(info, head, node) {
if (info->dev == dev) {
pr_err("%s: duplicate dev %p part ID 0x%x\n",
__func__, dev, dev->vm_id);
return -EEXIST;
}
}
}
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return ret;
rwlock_init(&info->rw_lock);
info->dev = dev;
if (!phead) {
phead = kzalloc(sizeof(*phead), GFP_KERNEL);
if (!phead)
goto free_out;
INIT_LIST_HEAD(phead);
ret = xa_insert(&drv_info->partition_info, dev->vm_id, phead,
GFP_KERNEL);
if (ret) {
pr_err("%s: failed to save part ID 0x%x Ret:%d\n",
__func__, dev->vm_id, ret);
goto free_out;
}
}
list_add(&info->node, phead);
return 0;
free_out:
kfree(phead);
kfree(info);
return ret;
}
static int ffa_setup_host_partition(int vm_id)
{
struct ffa_partition_info buf = { 0 };
struct ffa_device *ffa_dev;
int ret;
buf.id = vm_id;
ffa_dev = ffa_device_register(&buf, &ffa_drv_ops);
if (!ffa_dev) {
pr_err("%s: failed to register host partition ID 0x%x\n",
__func__, vm_id);
return -EINVAL;
}
ret = ffa_xa_add_partition_info(ffa_dev);
if (ret)
return ret;
if (ffa_notifications_disabled())
return 0;
ret = ffa_sched_recv_cb_update(ffa_dev, ffa_self_notif_handle,
drv_info, true);
if (ret)
pr_info("Failed to register driver sched callback %d\n", ret);
return ret;
}
static void ffa_partitions_cleanup(void)
{
struct list_head *phead;
unsigned long idx;
/* Clean up/free all registered devices */
ffa_devices_unregister();
xa_for_each(&drv_info->partition_info, idx, phead) {
struct ffa_dev_part_info *info, *tmp;
xa_erase(&drv_info->partition_info, idx);
list_for_each_entry_safe(info, tmp, phead, node) {
list_del(&info->node);
kfree(info);
}
kfree(phead);
}
xa_destroy(&drv_info->partition_info);
}
static int ffa_setup_partitions(void)
{
int count, idx, ret;
struct ffa_device *ffa_dev;
struct ffa_dev_part_info *info;
struct ffa_partition_info *pbuf, *tpbuf;
if (drv_info->version == FFA_VERSION_1_0) {
@ -1422,59 +1718,30 @@ static int ffa_setup_partitions(void)
!(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC))
ffa_mode_32bit_set(ffa_dev);
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
if (ffa_xa_add_partition_info(ffa_dev)) {
ffa_device_unregister(ffa_dev);
continue;
}
rwlock_init(&info->rw_lock);
ret = xa_insert(&drv_info->partition_info, tpbuf->id,
info, GFP_KERNEL);
if (ret) {
pr_err("%s: failed to save partition ID 0x%x - ret:%d\n",
__func__, tpbuf->id, ret);
ffa_device_unregister(ffa_dev);
kfree(info);
}
}
kfree(pbuf);
/* Allocate for the host */
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
/* Already registered devices are freed on bus_exit */
ffa_partitions_cleanup();
return -ENOMEM;
}
/*
* Check if the host is already added as part of partition info
* No multiple UUID possible for the host, so just checking if
* there is an entry will suffice
*/
if (xa_load(&drv_info->partition_info, drv_info->vm_id))
return 0;
rwlock_init(&info->rw_lock);
ret = xa_insert(&drv_info->partition_info, drv_info->vm_id,
info, GFP_KERNEL);
if (ret) {
pr_err("%s: failed to save Host partition ID 0x%x - ret:%d. Abort.\n",
__func__, drv_info->vm_id, ret);
kfree(info);
/* Already registered devices are freed on bus_exit */
/* Allocate for the host */
ret = ffa_setup_host_partition(drv_info->vm_id);
if (ret)
ffa_partitions_cleanup();
}
return ret;
}
static void ffa_partitions_cleanup(void)
{
struct ffa_dev_part_info *info;
unsigned long idx;
xa_for_each(&drv_info->partition_info, idx, info) {
xa_erase(&drv_info->partition_info, idx);
kfree(info);
}
xa_destroy(&drv_info->partition_info);
}
/* FFA FEATURE IDs */
#define FFA_FEAT_NOTIFICATION_PENDING_INT (1)
#define FFA_FEAT_SCHEDULE_RECEIVER_INT (2)
@ -1777,19 +2044,10 @@ static int __init ffa_init(void)
ffa_notifications_setup();
ret = ffa_setup_partitions();
if (ret) {
pr_err("failed to setup partitions\n");
goto cleanup_notifs;
}
if (!ret)
return ret;
ret = ffa_sched_recv_cb_update(drv_info->vm_id, ffa_self_notif_handle,
drv_info, true);
if (ret)
pr_info("Failed to register driver sched callback %d\n", ret);
return 0;
cleanup_notifs:
pr_err("failed to setup partitions\n");
ffa_notifications_cleanup();
free_pages:
if (drv_info->tx_buffer)

View File

@ -112,6 +112,7 @@
FIELD_PREP(FFA_MINOR_VERSION_MASK, (minor)))
#define FFA_VERSION_1_0 FFA_PACK_VERSION_INFO(1, 0)
#define FFA_VERSION_1_1 FFA_PACK_VERSION_INFO(1, 1)
#define FFA_VERSION_1_2 FFA_PACK_VERSION_INFO(1, 2)
/**
* FF-A specification mentions explicitly about '4K pages'. This should
@ -176,6 +177,7 @@ void ffa_device_unregister(struct ffa_device *ffa_dev);
int ffa_driver_register(struct ffa_driver *driver, struct module *owner,
const char *mod_name);
void ffa_driver_unregister(struct ffa_driver *driver);
void ffa_devices_unregister(void);
bool ffa_device_is_valid(struct ffa_device *ffa_dev);
#else
@ -188,6 +190,8 @@ ffa_device_register(const struct ffa_partition_info *part_info,
static inline void ffa_device_unregister(struct ffa_device *dev) {}
static inline void ffa_devices_unregister(void) {}
static inline int
ffa_driver_register(struct ffa_driver *driver, struct module *owner,
const char *mod_name)
@ -237,8 +241,12 @@ struct ffa_partition_info {
#define FFA_PARTITION_NOTIFICATION_RECV BIT(3)
/* partition runs in the AArch64 execution state. */
#define FFA_PARTITION_AARCH64_EXEC BIT(8)
/* partition supports receipt of direct request2 */
#define FFA_PARTITION_DIRECT_REQ2_RECV BIT(9)
/* partition can send direct request2. */
#define FFA_PARTITION_DIRECT_REQ2_SEND BIT(10)
u32 properties;
u32 uuid[4];
uuid_t uuid;
};
static inline
@ -256,6 +264,10 @@ bool ffa_partition_check_property(struct ffa_device *dev, u32 property)
#define ffa_partition_supports_direct_recv(dev) \
ffa_partition_check_property(dev, FFA_PARTITION_DIRECT_RECV)
#define ffa_partition_supports_direct_req2_recv(dev) \
(ffa_partition_check_property(dev, FFA_PARTITION_DIRECT_REQ2_RECV) && \
!dev->mode_32bit)
/* For use with FFA_MSG_SEND_DIRECT_{REQ,RESP} which pass data via registers */
struct ffa_send_direct_data {
unsigned long data0; /* w3/x3 */
@ -271,6 +283,7 @@ struct ffa_indirect_msg_hdr {
u32 offset;
u32 send_recv_id;
u32 size;
uuid_t uuid;
};
/* For use with FFA_MSG_SEND_DIRECT_{REQ,RESP}2 which pass data via registers */
@ -439,7 +452,7 @@ struct ffa_msg_ops {
int (*sync_send_receive)(struct ffa_device *dev,
struct ffa_send_direct_data *data);
int (*indirect_send)(struct ffa_device *dev, void *buf, size_t sz);
int (*sync_send_receive2)(struct ffa_device *dev, const uuid_t *uuid,
int (*sync_send_receive2)(struct ffa_device *dev,
struct ffa_send_direct_data2 *data);
};
@ -455,6 +468,7 @@ struct ffa_cpu_ops {
typedef void (*ffa_sched_recv_cb)(u16 vcpu, bool is_per_vcpu, void *cb_data);
typedef void (*ffa_notifier_cb)(int notify_id, void *cb_data);
typedef void (*ffa_fwk_notifier_cb)(int notify_id, void *cb_data, void *buf);
struct ffa_notifier_ops {
int (*sched_recv_cb_register)(struct ffa_device *dev,
@ -463,6 +477,10 @@ struct ffa_notifier_ops {
int (*notify_request)(struct ffa_device *dev, bool per_vcpu,
ffa_notifier_cb cb, void *cb_data, int notify_id);
int (*notify_relinquish)(struct ffa_device *dev, int notify_id);
int (*fwk_notify_request)(struct ffa_device *dev,
ffa_fwk_notifier_cb cb, void *cb_data,
int notify_id);
int (*fwk_notify_relinquish)(struct ffa_device *dev, int notify_id);
int (*notify_send)(struct ffa_device *dev, int notify_id, bool per_vcpu,
u16 vcpu);
};