Hi Jens,

On 02/05/2024 15:56, Jens Wiklander wrote:
-static bool ffa_probe(void)
+static int __init ffa_init(void)
  {
      uint32_t vers;
      unsigned int major_vers;
@@ -460,16 +511,16 @@ static bool ffa_probe(void)
          printk(XENLOG_ERR
                 "ffa: unsupported SMCCC version %#x (need at least %#x)\n",
                 smccc_ver, ARM_SMCCC_VERSION_1_2);
-        return false;
+        return 0;
      }
if ( !ffa_get_version(&vers) )
-        return false;
+        return 0;
if ( vers < FFA_MIN_SPMC_VERSION || vers > FFA_MY_VERSION )
      {
          printk(XENLOG_ERR "ffa: Incompatible version %#x found\n", vers);
-        return false;
+        return 0;
      }
major_vers = (vers >> FFA_VERSION_MAJOR_SHIFT) & FFA_VERSION_MAJOR_MASK;
@@ -492,26 +543,33 @@ static bool ffa_probe(void)
           !check_mandatory_feature(FFA_MEM_SHARE_32) ||
           !check_mandatory_feature(FFA_MEM_RECLAIM) ||
           !check_mandatory_feature(FFA_MSG_SEND_DIRECT_REQ_32) )
-        return false;
+        return 0;
if ( !ffa_rxtx_init() )
-        return false;
+        return 0;
ffa_version = vers; if ( !ffa_partinfo_init() )
          goto err_rxtx_destroy;
+ ffa_notif_init();
      INIT_LIST_HEAD(&ffa_teardown_head);
      init_timer(&ffa_teardown_timer, ffa_teardown_timer_callback, NULL, 0);
- return true;
+    return 0;
err_rxtx_destroy:
      ffa_rxtx_destroy();
      ffa_version = 0;
- return false;
+    return 0;
+}
+presmp_initcall(ffa_init);
I would rather prefer if tee_init() is called from presmp_initcall(). This would avoid FFA to have to try to initialize itself before all the others.

This is what I had in mind with my original request, but I guess I wasn't clear enough. Sorry for that.

[...]

+static void notif_irq_handler(int irq, void *data)
+{
+    const struct arm_smccc_1_2_regs arg = {
+        .a0 = FFA_NOTIFICATION_INFO_GET_64,
+    };
+    struct arm_smccc_1_2_regs resp;
+    unsigned int id_pos;
+    unsigned int list_count;
+    uint64_t ids_count;
+    unsigned int n;
+    int32_t res;
+
+    do {
+        arm_smccc_1_2_smc(&arg, &resp);
+        res = ffa_get_ret_code(&resp);
+        if ( res )
+        {
+            if ( res != FFA_RET_NO_DATA )
+                printk(XENLOG_ERR "ffa: notification info get failed: error 
%d\n",
+                       res);
+            return;
+        }
+
+        ids_count = resp.a2 >> FFA_NOTIF_INFO_GET_ID_LIST_SHIFT;
+        list_count = ( resp.a2 >> FFA_NOTIF_INFO_GET_ID_COUNT_SHIFT ) &
+                     FFA_NOTIF_INFO_GET_ID_COUNT_MASK;
+
+        id_pos = 0;
+        for ( n = 0; n < list_count; n++ )
+        {
+            unsigned int count = ((ids_count >> 2 * n) & 0x3) + 1;
+            uint16_t vm_id = get_id_from_resp(&resp, id_pos);
+            struct domain *d;
+
+            /*
+             * vm_id == 0 means a notifications pending for Xen itself, but
+             * we don't support that yet.
+             */
+            if (vm_id)
+                d = ffa_rcu_lock_domain_by_vm_id(vm_id);

I am still unconvinced that this is sufficient. This will prevent "struct domain *" to be freed. But ...

+            else
+                d = NULL;
+
+            if ( d )
+            {
+                struct ffa_ctx *ctx = d->arch.tee;

... I don't think it will protect d->arch.tee to be freed as this is happening during teardorwn. You mostly need some other sort of locking to avoid d->arch.tee been freed behind your back.

+                struct vcpu *v;
+
+                ACCESS_ONCE(ctx->notif.secure_pending) = true;
+
+                /*
+                 * Since we're only delivering global notification, always
+                 * deliver to the first online vCPU. It doesn't matter
+                 * which we chose, as long as it's available.
+                 */
+                for_each_vcpu(d, v)
+                {
+                    if ( is_vcpu_online(v) )
+                    {
+                        vgic_inject_irq(d, v, GUEST_FFA_NOTIF_PEND_INTR_ID,
+                                        true);
+                        break;
+                    }
+                }
+                if ( !v )
+                    printk(XENLOG_ERR "ffa: can't inject NPI, all vCPUs 
offline\n");
+
+                rcu_unlock_domain(d);
+            }
+
+            id_pos += count;
+        }
+
+    } while (resp.a2 & FFA_NOTIF_INFO_GET_MORE_FLAG);
+}

[...]

+static int ffa_setup_irq_callback(struct notifier_block *nfb,
+                                  unsigned long action, void *hcpu)
+{
+    unsigned int cpu = (unsigned long)hcpu;
+    struct notif_irq_info irq_info = { };
+
+    switch ( action )
+    {
+    case CPU_ONLINE:

Can't you execute the notifier in CPU_STARTING? This will be called on the CPU directly, so you should be able to use request_irq(...).

+        /*
+         * The notifier call is done on the primary or initiating CPU when
+         * the target CPU have come online, but the SGI must be setup on
+         * the target CPU.
+         *
+         * We make an IPI call to the target CPU to setup the SGI. The call
+         * is executed in interrupt context on the target CPU, so we can't
+         * call request_irq() directly since it allocates memory.
+         *
+         * We preallocate the needed irqaction here and pass it via the
+         * temporary struct notif_irq_info. The call is synchronous in the
+         * sense that when on_selected_cpus() returns the callback
+         * notif_irq_enable() has done the same on the target CPU.
+         *
+         * We deal with two errors, one where notif_irq_enable() hasn't
+         * been called for some reason, that error is logged below. The
+         * other where setup_irq() fails is logged in the callback. We must
+         * free the irqaction in both cases since it has failed to become
+         * registered.
+         *
+         * Failures leads to a problem notifications, the CPUs with failure
+         * will trigger on the SGI indicating that there are notifications
+         * pending, while the SPMC in the secure world will not notice that
+         * the interrupt was lost.
+         */
+        irq_info.action = xmalloc(struct irqaction);
+        if ( !irq_info.action )
+        {
+            printk(XENLOG_ERR "ffa: setup irq %u failed, out of memory\n",
+                   notif_sri_irq);
+            return -ENOMEM;
+        }
+
+        *irq_info.action = (struct irqaction){
+            .handler = notif_irq_handler,
+            .name = "FF-A notif",
+            .free_on_release = 1,
+        };
+
+        on_selected_cpus(cpumask_of(cpu), notif_irq_enable, &irq_info, 1);
+        if (!irq_info.called)
+            printk(XENLOG_ERR "ffa: on_selected_cpus(cpumask_of(%u)) failed\n",
+                   cpu);
+        /*
+         * The irqaction is unused and must be freed if irq_info.action is
+         * non-NULL at this stage.
+         */
+        XFREE(irq_info.action);
+        break;
+    default:
+        break;
+    }
+
+    return 0;
+}

Cheers,

--
Julien Grall

Reply via email to