Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2021, Linaro Limited */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/arm-smccc.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/tee_drv.h> #include "optee_private.h" struct notif_entry { struct list_head link; struct completion c; u_int key; }; static bool have_key(struct optee *optee, u_int key) { struct notif_entry *entry; list_for_each_entry(entry, &optee->notif.db, link) if (entry->key == key) return true; return false; } int optee_notif_wait(struct optee *optee, u_int key) { unsigned long flags; struct notif_entry *entry; int rc = 0; if (key > optee->notif.max_key) return -EINVAL; entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; init_completion(&entry->c); entry->key = key; spin_lock_irqsave(&optee->notif.lock, flags); /* * If the bit is already set it means that the key has already * been posted and we must not wait. */ if (test_bit(key, optee->notif.bitmap)) { clear_bit(key, optee->notif.bitmap); goto out; } /* * Check if someone is already waiting for this key. If there is * it's a programming error. */ if (have_key(optee, key)) { rc = -EBUSY; goto out; } list_add_tail(&entry->link, &optee->notif.db); /* * Unlock temporarily and wait for completion. */ spin_unlock_irqrestore(&optee->notif.lock, flags); wait_for_completion(&entry->c); spin_lock_irqsave(&optee->notif.lock, flags); list_del(&entry->link); out: spin_unlock_irqrestore(&optee->notif.lock, flags); kfree(entry); return rc; } int optee_notif_send(struct optee *optee, u_int key) { unsigned long flags; struct notif_entry *entry; if (key > optee->notif.max_key) return -EINVAL; spin_lock_irqsave(&optee->notif.lock, flags); list_for_each_entry(entry, &optee->notif.db, link) if (entry->key == key) { complete(&entry->c); goto out; } /* Only set the bit in case there where nobody waiting */ set_bit(key, optee->notif.bitmap); out: spin_unlock_irqrestore(&optee->notif.lock, flags); return 0; } int optee_notif_init(struct optee *optee, u_int max_key) { spin_lock_init(&optee->notif.lock); INIT_LIST_HEAD(&optee->notif.db); optee->notif.bitmap = bitmap_zalloc(max_key, GFP_KERNEL); if (!optee->notif.bitmap) return -ENOMEM; optee->notif.max_key = max_key; return 0; } void optee_notif_uninit(struct optee *optee) { bitmap_free(optee->notif.bitmap); } |