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 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2021-2024, Linaro Limited
*/
#include <assert.h>
#include <bitstring.h>
#include <config.h>
#include <initcall.h>
#include <kernel/interrupt.h>
#include <kernel/notif.h>
#include <kernel/spinlock.h>
#include <kernel/virtualization.h>
#include <trace.h>
#include <types_ext.h>
struct notif_vm_bitmap {
bool alloc_values_inited;
bitstr_t bit_decl(values, NOTIF_ASYNC_VALUE_MAX + 1);
bitstr_t bit_decl(alloc_values, NOTIF_ASYNC_VALUE_MAX + 1);
};
static unsigned int notif_default_lock = SPINLOCK_UNLOCK;
/* Id used to look up the guest specific struct notif_vm_bitmap */
static unsigned int notif_vm_bitmap_id __nex_bss;
/* Notification state when ns-virtualization isn't enabled */
static struct notif_vm_bitmap default_notif_vm_bitmap;
static struct notif_vm_bitmap *get_notif_vm_bitmap(struct guest_partition *prtn)
{
if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
if (!prtn)
return NULL;
return virt_get_guest_spec_data(prtn, notif_vm_bitmap_id);
}
return &default_notif_vm_bitmap;
}
TEE_Result notif_alloc_async_value(uint32_t *val)
{
struct guest_partition *prtn = NULL;
struct notif_vm_bitmap *nvb = NULL;
TEE_Result res = TEE_SUCCESS;
uint32_t old_itr_status = 0;
int bit = 0;
assert(interrupt_can_raise_pi(interrupt_get_main_chip()));
prtn = virt_get_current_guest();
nvb = get_notif_vm_bitmap(prtn);
if (!nvb) {
res = TEE_ERROR_BAD_PARAMETERS;
goto out;
}
old_itr_status = cpu_spin_lock_xsave(¬if_default_lock);
if (!nvb->alloc_values_inited) {
bit_set(nvb->alloc_values, NOTIF_VALUE_DO_BOTTOM_HALF);
nvb->alloc_values_inited = true;
}
bit_ffc(nvb->alloc_values, (int)NOTIF_ASYNC_VALUE_MAX + 1, &bit);
if (bit < 0) {
res = TEE_ERROR_OUT_OF_MEMORY;
goto out_unlock;
}
*val = bit;
bit_set(nvb->alloc_values, bit);
out_unlock:
cpu_spin_unlock_xrestore(¬if_default_lock, old_itr_status);
out:
virt_put_guest(prtn);
return res;
}
void notif_free_async_value(uint32_t val)
{
struct guest_partition *prtn = NULL;
struct notif_vm_bitmap *nvb = NULL;
uint32_t old_itr_status = 0;
prtn = virt_get_current_guest();
nvb = get_notif_vm_bitmap(prtn);
if (!nvb)
goto out;
old_itr_status = cpu_spin_lock_xsave(¬if_default_lock);
assert(val < NOTIF_ASYNC_VALUE_MAX);
assert(bit_test(nvb->alloc_values, val));
bit_clear(nvb->alloc_values, val);
cpu_spin_unlock_xrestore(¬if_default_lock, old_itr_status);
out:
virt_put_guest(prtn);
}
uint32_t notif_get_value(bool *value_valid, bool *value_pending)
{
struct guest_partition *prtn = NULL;
struct notif_vm_bitmap *nvb = NULL;
uint32_t old_itr_status = 0;
uint32_t res = 0;
int bit = -1;
prtn = virt_get_current_guest();
nvb = get_notif_vm_bitmap(prtn);
if (!nvb) {
*value_valid = false;
goto out;
}
old_itr_status = cpu_spin_lock_xsave(¬if_default_lock);
bit_ffs(nvb->values, (int)NOTIF_ASYNC_VALUE_MAX + 1, &bit);
*value_valid = (bit >= 0);
if (!*value_valid)
goto out_unlock;
res = bit;
bit_clear(nvb->values, res);
bit_ffs(nvb->values, (int)NOTIF_ASYNC_VALUE_MAX + 1, &bit);
out_unlock:
cpu_spin_unlock_xrestore(¬if_default_lock, old_itr_status);
out:
virt_put_guest(prtn);
*value_pending = (bit >= 0);
return res;
}
void notif_send_async(uint32_t value, uint16_t guest_id)
{
struct guest_partition *prtn = NULL;
struct notif_vm_bitmap *nvb = NULL;
uint32_t old_itr_status = 0;
struct itr_chip *itr_chip = interrupt_get_main_chip();
assert(value <= NOTIF_ASYNC_VALUE_MAX);
prtn = virt_get_guest(guest_id);
nvb = get_notif_vm_bitmap(prtn);
if (!nvb)
goto out;
old_itr_status = cpu_spin_lock_xsave(¬if_default_lock);
bit_set(nvb->values, value);
interrupt_raise_pi(itr_chip, CFG_CORE_ASYNC_NOTIF_GIC_INTID);
cpu_spin_unlock_xrestore(¬if_default_lock, old_itr_status);
out:
virt_put_guest(prtn);
}
static TEE_Result notif_init(void)
{
if (IS_ENABLED(CFG_NS_VIRTUALIZATION) &&
virt_add_guest_spec_data(¬if_vm_bitmap_id,
sizeof(struct notif_vm_bitmap), NULL))
panic("virt_add_guest_spec_data");
return TEE_SUCCESS;
}
nex_service_init(notif_init);
|