Arm FF-A updates for v6.10

1. Support for handling notification pending interrupt(NPI)
 
    The FF-A uses the notification pending interrupt to inform the receiver
    that it has a pending notification. This is a virtual interrupt and is
    used by the following type of receivers:
    - A guest/VM running under a hypervisor(normal world usecase)
    - An S-EL1 SP running under a S-EL2 SPMC(secure world only usecase)
 
    Also, when the FF-A driver is running inside a guest VM under an
    hypervisor, the driver/guest VM doesn't have the permission/capability
    to request the creation of notification bitmaps. For a VM, the hypervisor
    reserves memory for its VM and hypervisor framework notification bitmaps
    and the SPMC reserves memory for its SP and SPMC framework notification
    bitmaps before the hypervisor initializes it.
 
    These changes include skipping of creation of notification bitmaps, some
    refactoring around schedule receiver interrupt(SRI) handling and addition
    of support for NPI.
 
 2. Support for FF-A indirect messaging
 
    The FFA_MSG_SEND2 can be used to transmit a partition message from
    the Tx buffer of the sender(the driver in this case) endpoint to the Rx
    buffer of the receiver endpoint and inform the scheduler that the
    receiver endpoint must be run.
 
 Apart from these two main features, there is an optimisation to avoid
 queuing of a work when already running on the worker queue.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEunHlEgbzHrJD3ZPhAEG6vDF+4pgFAmYqYlMACgkQAEG6vDF+
 4piZdg//bXofMTqAzCgCJzzgCHnmPmAvoCk0cDzogorQ4rlZpabYOGMkVGeWIh+x
 p3fy/tD5HNl+AgAiglWDmchphxiqhxrMyb893oSsUUr7WHH2xeb8cLUlMrirGCMD
 CDUQ2mAv1h09pSvHfCLcJD4MVHUZ8maHCo5Bqg5zqMYV7poOSoUJJ/nHCWSHG2il
 S5h4ScEgtJNd7FmsnieSnTXwUKa7TXbVDWbUAhxukymAfJfQcwJ9XP8X537W/NkJ
 1UH4asPadU/ScRi6egNpaledEWiVM2Z1emsItfd3oO399lCHIBb9o5XDZg3TWaEi
 JEbKaBt6EP0g2zt4Rdjk/vPWIAwZKmnOLi1m+Cn+urZbsNBXke/7e5C1TvdXrFLA
 9D0vDmO6CMnIZ+wPt/xElzsnWKGNJ3tRu5kGPAL5XpnsbsY6NLbkQ//veS2GCeyW
 XYQBmd2V6nU9IB1tmRQmrtM01DbT4V02tDROaviRWgt9GrZFdBSV+uYNZ9wOVXej
 LhGuSW0ouaNt6PwjxdoocFZh4cfVHGwZdtNVJNOeRRtMok2LweKM16CMtN8RexSl
 QBTsHifZ0hCg+QZ9Cp2ka7nenpuVhll1dB/MmKENJWLfnn5+UMEbGoIGj/5cRn91
 aYuYC3f6qnZsgWCn/hfglP4jrl0pP6/OrblFBZPZRl0IoCjgfHE=
 =A2PF
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmYwCOEACgkQYKtH/8kJ
 Uifhkw/9FJUVel9Q84ZoovQH+L6D18pB6tOSy4BtrX/YujQ6tSlKhhQAhbl/d02s
 E4fUbkQDRLJIMDmfiqLJS7oFjV5ao6ws8ERwTtiaWQN30cMkTmmGVVTjPB2hXMyY
 Iez1CKT4UBncJQZdFj3JsSu0OPqWAtmi1RviRpyW/7/4ezpnvgawkmGKNDDJFgAI
 uXgO0nsZFkbFSSZTJyeWNvb4fwQdflgJgUZ3d3jozFTQDB+g/N+89yYHKFYWZ5Yf
 fJ/u3e4y/3Ap4oBhQ6D7WIcMX4flQNyQzlubOjO3Zro0/rWY3/LetcvtwaulzCNd
 28hDe7RxulflRgrNuRERzuM2uT0Wuy33NCX3CNbCTw3lc1PPk4g0WEXlIPFtv/q2
 5RcAiGeHP++u4qfFgxr2eutxPrWPBbVUxRpqL1jxawKSLDjxj0YMnDO+2A6QUyei
 cDGtn0jjTFbUaeprw8Kn7AqsOpQoCW4nhRYA7FIH8xn4HyP4ibGB3PAEZZ8WQ6ny
 cGG+sVPK82njXQW7PmIqkoAEmZLwp8yqt6esZuQ8ZY7LmTXiPgYaZ5L6l4dQEY9C
 13cfiEyCKH0qsCGiTuDD3FTnDH8NmIzcCuhL6BaYicvc1tlgfakDr+ib8u03tEhI
 iSQq9hLnF51tcEVlbKKvKvZ+kVEtQy5RyyNVRsvIRINEM/Y9uro=
 =Mf+D
 -----END PGP SIGNATURE-----

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

Arm FF-A updates for v6.10

1. Support for handling notification pending interrupt(NPI)

   The FF-A uses the notification pending interrupt to inform the receiver
   that it has a pending notification. This is a virtual interrupt and is
   used by the following type of receivers:
   - A guest/VM running under a hypervisor(normal world usecase)
   - An S-EL1 SP running under a S-EL2 SPMC(secure world only usecase)

   Also, when the FF-A driver is running inside a guest VM under an
   hypervisor, the driver/guest VM doesn't have the permission/capability
   to request the creation of notification bitmaps. For a VM, the hypervisor
   reserves memory for its VM and hypervisor framework notification bitmaps
   and the SPMC reserves memory for its SP and SPMC framework notification
   bitmaps before the hypervisor initializes it.

   These changes include skipping of creation of notification bitmaps, some
   refactoring around schedule receiver interrupt(SRI) handling and addition
   of support for NPI.

2. Support for FF-A indirect messaging

   The FFA_MSG_SEND2 can be used to transmit a partition message from
   the Tx buffer of the sender(the driver in this case) endpoint to the Rx
   buffer of the receiver endpoint and inform the scheduler that the
   receiver endpoint must be run.

Apart from these two main features, there is an optimisation to avoid
queuing of a work when already running on the worker queue.

* tag 'ffa-updates-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  firmware: arm_ffa: Avoid queuing work when running on the worker queue
  firmware: arm_ffa: Fix memory corruption in ffa_msg_send2()
  firmware: arm_ffa: Add support for FFA_MSG_SEND2
  firmware: arm_ffa: Stash the partition properties for query purposes
  firmware: arm_ffa: Fix kernel warning about incorrect SRI/NPI
  firmware: arm_ffa: Add support for handling notification pending interrupt(NPI)
  firmware: arm_ffa: Refactor SRI handling in prepartion to add NPI support
  firmware: arm_ffa: Skip creation of the notification bitmaps

Link: https://lore.kernel.org/r/20240426105051.1527016-1-sudeep.holla@arm.com
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2024-04-29 22:53:53 +02:00
commit 93f9f5a489
2 changed files with 169 additions and 45 deletions

View File

@ -101,11 +101,12 @@ struct ffa_drv_info {
bool bitmap_created;
bool notif_enabled;
unsigned int sched_recv_irq;
unsigned int notif_pend_irq;
unsigned int cpuhp_state;
struct ffa_pcpu_irq __percpu *irq_pcpu;
struct workqueue_struct *notif_pcpu_wq;
struct work_struct notif_pcpu_work;
struct work_struct irq_work;
struct work_struct sched_recv_irq_work;
struct xarray partition_info;
DECLARE_HASHTABLE(notifier_hash, ilog2(FFA_MAX_NOTIFICATIONS));
struct mutex notify_lock; /* lock to protect notifier hashtable */
@ -344,6 +345,38 @@ 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)
{
u32 src_dst_ids = PACK_TARGET_INFO(src_id, dst_id);
struct ffa_indirect_msg_hdr *msg;
ffa_value_t ret;
int retval = 0;
if (sz > (RXTX_BUFFER_SIZE - sizeof(*msg)))
return -ERANGE;
mutex_lock(&drv_info->tx_lock);
msg = drv_info->tx_buffer;
msg->flags = 0;
msg->res0 = 0;
msg->offset = sizeof(*msg);
msg->send_recv_id = src_dst_ids;
msg->size = sz;
memcpy((u8 *)msg + msg->offset, buf, sz);
/* flags = 0, sender VMID = 0 works for both physical/virtual NS */
invoke_ffa_fn((ffa_value_t){
.a0 = FFA_MSG_SEND2, .a1 = 0, .a2 = 0
}, &ret);
if (ret.a0 == FFA_ERROR)
retval = ffa_to_linux_errno((int)ret.a2);
mutex_unlock(&drv_info->tx_lock);
return retval;
}
static int ffa_mem_first_frag(u32 func_id, phys_addr_t buf, u32 buf_sz,
u32 frag_len, u32 len, u64 *handle)
{
@ -870,6 +903,11 @@ static int ffa_sync_send_receive(struct ffa_device *dev,
dev->mode_32bit, data);
}
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);
}
static int ffa_memory_share(struct ffa_mem_ops_args *args)
{
if (drv_info->mem_ops_native)
@ -1108,7 +1146,7 @@ static void handle_notif_callbacks(u64 bitmap, enum notify_type type)
}
}
static void notif_pcpu_irq_work_fn(struct work_struct *work)
static void notif_get_and_handle(void *unused)
{
int rc;
struct ffa_notify_bitmaps bitmaps;
@ -1131,10 +1169,17 @@ ffa_self_notif_handle(u16 vcpu, bool is_per_vcpu, void *cb_data)
struct ffa_drv_info *info = cb_data;
if (!is_per_vcpu)
notif_pcpu_irq_work_fn(&info->notif_pcpu_work);
notif_get_and_handle(info);
else
queue_work_on(vcpu, info->notif_pcpu_wq,
&info->notif_pcpu_work);
smp_call_function_single(vcpu, notif_get_and_handle, info, 0);
}
static void notif_pcpu_irq_work_fn(struct work_struct *work)
{
struct ffa_drv_info *info = container_of(work, struct ffa_drv_info,
notif_pcpu_work);
ffa_self_notif_handle(smp_processor_id(), true, info);
}
static const struct ffa_info_ops ffa_drv_info_ops = {
@ -1145,6 +1190,7 @@ static const struct ffa_info_ops ffa_drv_info_ops = {
static const struct ffa_msg_ops ffa_drv_msg_ops = {
.mode_32bit_set = ffa_mode_32bit_set,
.sync_send_receive = ffa_sync_send_receive,
.indirect_send = ffa_indirect_msg_send,
};
static const struct ffa_mem_ops ffa_drv_mem_ops = {
@ -1227,6 +1273,8 @@ static int ffa_setup_partitions(void)
continue;
}
ffa_dev->properties = tpbuf->properties;
if (drv_info->version > FFA_VERSION_1_0 &&
!(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC))
ffa_mode_32bit_set(ffa_dev);
@ -1291,12 +1339,23 @@ static void ffa_partitions_cleanup(void)
#define FFA_FEAT_SCHEDULE_RECEIVER_INT (2)
#define FFA_FEAT_MANAGED_EXIT_INT (3)
static irqreturn_t irq_handler(int irq, void *irq_data)
static irqreturn_t ffa_sched_recv_irq_handler(int irq, void *irq_data)
{
struct ffa_pcpu_irq *pcpu = irq_data;
struct ffa_drv_info *info = pcpu->info;
queue_work(info->notif_pcpu_wq, &info->irq_work);
queue_work(info->notif_pcpu_wq, &info->sched_recv_irq_work);
return IRQ_HANDLED;
}
static irqreturn_t notif_pend_irq_handler(int irq, void *irq_data)
{
struct ffa_pcpu_irq *pcpu = irq_data;
struct ffa_drv_info *info = pcpu->info;
queue_work_on(smp_processor_id(), info->notif_pcpu_wq,
&info->notif_pcpu_work);
return IRQ_HANDLED;
}
@ -1306,15 +1365,23 @@ static void ffa_sched_recv_irq_work_fn(struct work_struct *work)
ffa_notification_info_get();
}
static int ffa_sched_recv_irq_map(void)
static int ffa_irq_map(u32 id)
{
int ret, irq, sr_intid;
char *err_str;
int ret, irq, intid;
/* The returned sr_intid is assumed to be SGI donated to NS world */
ret = ffa_features(FFA_FEAT_SCHEDULE_RECEIVER_INT, 0, &sr_intid, NULL);
if (id == FFA_FEAT_NOTIFICATION_PENDING_INT)
err_str = "Notification Pending Interrupt";
else if (id == FFA_FEAT_SCHEDULE_RECEIVER_INT)
err_str = "Schedule Receiver Interrupt";
else
err_str = "Unknown ID";
/* The returned intid is assumed to be SGI donated to NS world */
ret = ffa_features(id, 0, &intid, NULL);
if (ret < 0) {
if (ret != -EOPNOTSUPP)
pr_err("Failed to retrieve scheduler Rx interrupt\n");
pr_err("Failed to retrieve FF-A %s %u\n", err_str, id);
return ret;
}
@ -1329,12 +1396,12 @@ static int ffa_sched_recv_irq_map(void)
oirq.np = gic;
oirq.args_count = 1;
oirq.args[0] = sr_intid;
oirq.args[0] = intid;
irq = irq_create_of_mapping(&oirq);
of_node_put(gic);
#ifdef CONFIG_ACPI
} else {
irq = acpi_register_gsi(NULL, sr_intid, ACPI_EDGE_SENSITIVE,
irq = acpi_register_gsi(NULL, intid, ACPI_EDGE_SENSITIVE,
ACPI_ACTIVE_HIGH);
#endif
}
@ -1347,23 +1414,28 @@ static int ffa_sched_recv_irq_map(void)
return irq;
}
static void ffa_sched_recv_irq_unmap(void)
static void ffa_irq_unmap(unsigned int irq)
{
if (drv_info->sched_recv_irq) {
irq_dispose_mapping(drv_info->sched_recv_irq);
drv_info->sched_recv_irq = 0;
}
if (!irq)
return;
irq_dispose_mapping(irq);
}
static int ffa_cpuhp_pcpu_irq_enable(unsigned int cpu)
{
enable_percpu_irq(drv_info->sched_recv_irq, IRQ_TYPE_NONE);
if (drv_info->sched_recv_irq)
enable_percpu_irq(drv_info->sched_recv_irq, IRQ_TYPE_NONE);
if (drv_info->notif_pend_irq)
enable_percpu_irq(drv_info->notif_pend_irq, IRQ_TYPE_NONE);
return 0;
}
static int ffa_cpuhp_pcpu_irq_disable(unsigned int cpu)
{
disable_percpu_irq(drv_info->sched_recv_irq);
if (drv_info->sched_recv_irq)
disable_percpu_irq(drv_info->sched_recv_irq);
if (drv_info->notif_pend_irq)
disable_percpu_irq(drv_info->notif_pend_irq);
return 0;
}
@ -1382,13 +1454,16 @@ static void ffa_uninit_pcpu_irq(void)
if (drv_info->sched_recv_irq)
free_percpu_irq(drv_info->sched_recv_irq, drv_info->irq_pcpu);
if (drv_info->notif_pend_irq)
free_percpu_irq(drv_info->notif_pend_irq, drv_info->irq_pcpu);
if (drv_info->irq_pcpu) {
free_percpu(drv_info->irq_pcpu);
drv_info->irq_pcpu = NULL;
}
}
static int ffa_init_pcpu_irq(unsigned int irq)
static int ffa_init_pcpu_irq(void)
{
struct ffa_pcpu_irq __percpu *irq_pcpu;
int ret, cpu;
@ -1402,13 +1477,31 @@ static int ffa_init_pcpu_irq(unsigned int irq)
drv_info->irq_pcpu = irq_pcpu;
ret = request_percpu_irq(irq, irq_handler, "ARM-FFA", irq_pcpu);
if (ret) {
pr_err("Error registering notification IRQ %d: %d\n", irq, ret);
return ret;
if (drv_info->sched_recv_irq) {
ret = request_percpu_irq(drv_info->sched_recv_irq,
ffa_sched_recv_irq_handler,
"ARM-FFA-SRI", irq_pcpu);
if (ret) {
pr_err("Error registering percpu SRI nIRQ %d : %d\n",
drv_info->sched_recv_irq, ret);
drv_info->sched_recv_irq = 0;
return ret;
}
}
INIT_WORK(&drv_info->irq_work, ffa_sched_recv_irq_work_fn);
if (drv_info->notif_pend_irq) {
ret = request_percpu_irq(drv_info->notif_pend_irq,
notif_pend_irq_handler,
"ARM-FFA-NPI", irq_pcpu);
if (ret) {
pr_err("Error registering percpu NPI nIRQ %d : %d\n",
drv_info->notif_pend_irq, ret);
drv_info->notif_pend_irq = 0;
return ret;
}
}
INIT_WORK(&drv_info->sched_recv_irq_work, ffa_sched_recv_irq_work_fn);
INIT_WORK(&drv_info->notif_pcpu_work, notif_pcpu_irq_work_fn);
drv_info->notif_pcpu_wq = create_workqueue("ffa_pcpu_irq_notification");
if (!drv_info->notif_pcpu_wq)
@ -1428,7 +1521,10 @@ static int ffa_init_pcpu_irq(unsigned int irq)
static void ffa_notifications_cleanup(void)
{
ffa_uninit_pcpu_irq();
ffa_sched_recv_irq_unmap();
ffa_irq_unmap(drv_info->sched_recv_irq);
drv_info->sched_recv_irq = 0;
ffa_irq_unmap(drv_info->notif_pend_irq);
drv_info->notif_pend_irq = 0;
if (drv_info->bitmap_created) {
ffa_notification_bitmap_destroy();
@ -1439,30 +1535,31 @@ static void ffa_notifications_cleanup(void)
static void ffa_notifications_setup(void)
{
int ret, irq;
int ret;
ret = ffa_features(FFA_NOTIFICATION_BITMAP_CREATE, 0, NULL, NULL);
if (ret) {
pr_info("Notifications not supported, continuing with it ..\n");
return;
if (!ret) {
ret = ffa_notification_bitmap_create();
if (ret) {
pr_err("Notification bitmap create error %d\n", ret);
return;
}
drv_info->bitmap_created = true;
}
ret = ffa_notification_bitmap_create();
if (ret) {
pr_info("Notification bitmap create error %d\n", ret);
return;
}
drv_info->bitmap_created = true;
ret = ffa_irq_map(FFA_FEAT_SCHEDULE_RECEIVER_INT);
if (ret > 0)
drv_info->sched_recv_irq = ret;
irq = ffa_sched_recv_irq_map();
if (irq <= 0) {
ret = irq;
ret = ffa_irq_map(FFA_FEAT_NOTIFICATION_PENDING_INT);
if (ret > 0)
drv_info->notif_pend_irq = ret;
if (!drv_info->sched_recv_irq && !drv_info->notif_pend_irq)
goto cleanup;
}
drv_info->sched_recv_irq = irq;
ret = ffa_init_pcpu_irq(irq);
ret = ffa_init_pcpu_irq();
if (ret)
goto cleanup;

View File

@ -126,6 +126,7 @@
/* FFA Bus/Device/Driver related */
struct ffa_device {
u32 id;
u32 properties;
int vm_id;
bool mode_32bit;
uuid_t uuid;
@ -221,12 +222,29 @@ struct ffa_partition_info {
#define FFA_PARTITION_DIRECT_SEND BIT(1)
/* partition can send and receive indirect messages. */
#define FFA_PARTITION_INDIRECT_MSG BIT(2)
/* partition can receive notifications */
#define FFA_PARTITION_NOTIFICATION_RECV BIT(3)
/* partition runs in the AArch64 execution state. */
#define FFA_PARTITION_AARCH64_EXEC BIT(8)
u32 properties;
u32 uuid[4];
};
static inline
bool ffa_partition_check_property(struct ffa_device *dev, u32 property)
{
return dev->properties & property;
}
#define ffa_partition_supports_notify_recv(dev) \
ffa_partition_check_property(dev, FFA_PARTITION_NOTIFICATION_RECV)
#define ffa_partition_supports_indirect_msg(dev) \
ffa_partition_check_property(dev, FFA_PARTITION_INDIRECT_MSG)
#define ffa_partition_supports_direct_recv(dev) \
ffa_partition_check_property(dev, FFA_PARTITION_DIRECT_RECV)
/* For use with FFA_MSG_SEND_DIRECT_{REQ,RESP} which pass data via registers */
struct ffa_send_direct_data {
unsigned long data0; /* w3/x3 */
@ -236,6 +254,14 @@ struct ffa_send_direct_data {
unsigned long data4; /* w7/x7 */
};
struct ffa_indirect_msg_hdr {
u32 flags;
u32 res0;
u32 offset;
u32 send_recv_id;
u32 size;
};
struct ffa_mem_region_addr_range {
/* The base IPA of the constituent memory region, aligned to 4 kiB */
u64 address;
@ -396,6 +422,7 @@ struct ffa_msg_ops {
void (*mode_32bit_set)(struct ffa_device *dev);
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);
};
struct ffa_mem_ops {