162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015-2021, Linaro Limited 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/arm-smccc.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/spinlock.h> 1262306a36Sopenharmony_ci#include <linux/tee_drv.h> 1362306a36Sopenharmony_ci#include "optee_private.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct notif_entry { 1662306a36Sopenharmony_ci struct list_head link; 1762306a36Sopenharmony_ci struct completion c; 1862306a36Sopenharmony_ci u_int key; 1962306a36Sopenharmony_ci}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic bool have_key(struct optee *optee, u_int key) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct notif_entry *entry; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci list_for_each_entry(entry, &optee->notif.db, link) 2662306a36Sopenharmony_ci if (entry->key == key) 2762306a36Sopenharmony_ci return true; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci return false; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciint optee_notif_wait(struct optee *optee, u_int key) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci unsigned long flags; 3562306a36Sopenharmony_ci struct notif_entry *entry; 3662306a36Sopenharmony_ci int rc = 0; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (key > optee->notif.max_key) 3962306a36Sopenharmony_ci return -EINVAL; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 4262306a36Sopenharmony_ci if (!entry) 4362306a36Sopenharmony_ci return -ENOMEM; 4462306a36Sopenharmony_ci init_completion(&entry->c); 4562306a36Sopenharmony_ci entry->key = key; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci spin_lock_irqsave(&optee->notif.lock, flags); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* 5062306a36Sopenharmony_ci * If the bit is already set it means that the key has already 5162306a36Sopenharmony_ci * been posted and we must not wait. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci if (test_bit(key, optee->notif.bitmap)) { 5462306a36Sopenharmony_ci clear_bit(key, optee->notif.bitmap); 5562306a36Sopenharmony_ci goto out; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* 5962306a36Sopenharmony_ci * Check if someone is already waiting for this key. If there is 6062306a36Sopenharmony_ci * it's a programming error. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci if (have_key(optee, key)) { 6362306a36Sopenharmony_ci rc = -EBUSY; 6462306a36Sopenharmony_ci goto out; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci list_add_tail(&entry->link, &optee->notif.db); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * Unlock temporarily and wait for completion. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci spin_unlock_irqrestore(&optee->notif.lock, flags); 7362306a36Sopenharmony_ci wait_for_completion(&entry->c); 7462306a36Sopenharmony_ci spin_lock_irqsave(&optee->notif.lock, flags); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci list_del(&entry->link); 7762306a36Sopenharmony_ciout: 7862306a36Sopenharmony_ci spin_unlock_irqrestore(&optee->notif.lock, flags); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci kfree(entry); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return rc; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciint optee_notif_send(struct optee *optee, u_int key) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci unsigned long flags; 8862306a36Sopenharmony_ci struct notif_entry *entry; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (key > optee->notif.max_key) 9162306a36Sopenharmony_ci return -EINVAL; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci spin_lock_irqsave(&optee->notif.lock, flags); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci list_for_each_entry(entry, &optee->notif.db, link) 9662306a36Sopenharmony_ci if (entry->key == key) { 9762306a36Sopenharmony_ci complete(&entry->c); 9862306a36Sopenharmony_ci goto out; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Only set the bit in case there where nobody waiting */ 10262306a36Sopenharmony_ci set_bit(key, optee->notif.bitmap); 10362306a36Sopenharmony_ciout: 10462306a36Sopenharmony_ci spin_unlock_irqrestore(&optee->notif.lock, flags); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciint optee_notif_init(struct optee *optee, u_int max_key) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci spin_lock_init(&optee->notif.lock); 11262306a36Sopenharmony_ci INIT_LIST_HEAD(&optee->notif.db); 11362306a36Sopenharmony_ci optee->notif.bitmap = bitmap_zalloc(max_key, GFP_KERNEL); 11462306a36Sopenharmony_ci if (!optee->notif.bitmap) 11562306a36Sopenharmony_ci return -ENOMEM; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci optee->notif.max_key = max_key; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_civoid optee_notif_uninit(struct optee *optee) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci bitmap_free(optee->notif.bitmap); 12562306a36Sopenharmony_ci} 126