cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

counter-chrdev.c (14805B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Generic Counter character device interface
      4 * Copyright (C) 2020 William Breathitt Gray
      5 */
      6#include <linux/cdev.h>
      7#include <linux/counter.h>
      8#include <linux/err.h>
      9#include <linux/errno.h>
     10#include <linux/export.h>
     11#include <linux/fs.h>
     12#include <linux/kfifo.h>
     13#include <linux/list.h>
     14#include <linux/mutex.h>
     15#include <linux/nospec.h>
     16#include <linux/poll.h>
     17#include <linux/slab.h>
     18#include <linux/spinlock.h>
     19#include <linux/timekeeping.h>
     20#include <linux/types.h>
     21#include <linux/uaccess.h>
     22#include <linux/wait.h>
     23
     24#include "counter-chrdev.h"
     25
     26struct counter_comp_node {
     27	struct list_head l;
     28	struct counter_component component;
     29	struct counter_comp comp;
     30	void *parent;
     31};
     32
     33#define counter_comp_read_is_equal(a, b) \
     34	(a.action_read == b.action_read || \
     35	a.device_u8_read == b.device_u8_read || \
     36	a.count_u8_read == b.count_u8_read || \
     37	a.signal_u8_read == b.signal_u8_read || \
     38	a.device_u32_read == b.device_u32_read || \
     39	a.count_u32_read == b.count_u32_read || \
     40	a.signal_u32_read == b.signal_u32_read || \
     41	a.device_u64_read == b.device_u64_read || \
     42	a.count_u64_read == b.count_u64_read || \
     43	a.signal_u64_read == b.signal_u64_read)
     44
     45#define counter_comp_read_is_set(comp) \
     46	(comp.action_read || \
     47	comp.device_u8_read || \
     48	comp.count_u8_read || \
     49	comp.signal_u8_read || \
     50	comp.device_u32_read || \
     51	comp.count_u32_read || \
     52	comp.signal_u32_read || \
     53	comp.device_u64_read || \
     54	comp.count_u64_read || \
     55	comp.signal_u64_read)
     56
     57static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
     58				   size_t len, loff_t *f_ps)
     59{
     60	struct counter_device *const counter = filp->private_data;
     61	int err;
     62	unsigned int copied;
     63
     64	if (!counter->ops)
     65		return -ENODEV;
     66
     67	if (len < sizeof(struct counter_event))
     68		return -EINVAL;
     69
     70	do {
     71		if (kfifo_is_empty(&counter->events)) {
     72			if (filp->f_flags & O_NONBLOCK)
     73				return -EAGAIN;
     74
     75			err = wait_event_interruptible(counter->events_wait,
     76					!kfifo_is_empty(&counter->events) ||
     77					!counter->ops);
     78			if (err < 0)
     79				return err;
     80			if (!counter->ops)
     81				return -ENODEV;
     82		}
     83
     84		if (mutex_lock_interruptible(&counter->events_out_lock))
     85			return -ERESTARTSYS;
     86		err = kfifo_to_user(&counter->events, buf, len, &copied);
     87		mutex_unlock(&counter->events_out_lock);
     88		if (err < 0)
     89			return err;
     90	} while (!copied);
     91
     92	return copied;
     93}
     94
     95static __poll_t counter_chrdev_poll(struct file *filp,
     96				    struct poll_table_struct *pollt)
     97{
     98	struct counter_device *const counter = filp->private_data;
     99	__poll_t events = 0;
    100
    101	if (!counter->ops)
    102		return events;
    103
    104	poll_wait(filp, &counter->events_wait, pollt);
    105
    106	if (!kfifo_is_empty(&counter->events))
    107		events = EPOLLIN | EPOLLRDNORM;
    108
    109	return events;
    110}
    111
    112static void counter_events_list_free(struct list_head *const events_list)
    113{
    114	struct counter_event_node *p, *n;
    115	struct counter_comp_node *q, *o;
    116
    117	list_for_each_entry_safe(p, n, events_list, l) {
    118		/* Free associated component nodes */
    119		list_for_each_entry_safe(q, o, &p->comp_list, l) {
    120			list_del(&q->l);
    121			kfree(q);
    122		}
    123
    124		/* Free event node */
    125		list_del(&p->l);
    126		kfree(p);
    127	}
    128}
    129
    130static int counter_set_event_node(struct counter_device *const counter,
    131				  struct counter_watch *const watch,
    132				  const struct counter_comp_node *const cfg)
    133{
    134	struct counter_event_node *event_node;
    135	int err = 0;
    136	struct counter_comp_node *comp_node;
    137
    138	/* Search for event in the list */
    139	list_for_each_entry(event_node, &counter->next_events_list, l)
    140		if (event_node->event == watch->event &&
    141		    event_node->channel == watch->channel)
    142			break;
    143
    144	/* If event is not already in the list */
    145	if (&event_node->l == &counter->next_events_list) {
    146		/* Allocate new event node */
    147		event_node = kmalloc(sizeof(*event_node), GFP_KERNEL);
    148		if (!event_node)
    149			return -ENOMEM;
    150
    151		/* Configure event node and add to the list */
    152		event_node->event = watch->event;
    153		event_node->channel = watch->channel;
    154		INIT_LIST_HEAD(&event_node->comp_list);
    155		list_add(&event_node->l, &counter->next_events_list);
    156	}
    157
    158	/* Check if component watch has already been set before */
    159	list_for_each_entry(comp_node, &event_node->comp_list, l)
    160		if (comp_node->parent == cfg->parent &&
    161		    counter_comp_read_is_equal(comp_node->comp, cfg->comp)) {
    162			err = -EINVAL;
    163			goto exit_free_event_node;
    164		}
    165
    166	/* Allocate component node */
    167	comp_node = kmalloc(sizeof(*comp_node), GFP_KERNEL);
    168	if (!comp_node) {
    169		err = -ENOMEM;
    170		goto exit_free_event_node;
    171	}
    172	*comp_node = *cfg;
    173
    174	/* Add component node to event node */
    175	list_add_tail(&comp_node->l, &event_node->comp_list);
    176
    177exit_free_event_node:
    178	/* Free event node if no one else is watching */
    179	if (list_empty(&event_node->comp_list)) {
    180		list_del(&event_node->l);
    181		kfree(event_node);
    182	}
    183
    184	return err;
    185}
    186
    187static int counter_enable_events(struct counter_device *const counter)
    188{
    189	unsigned long flags;
    190	int err = 0;
    191
    192	mutex_lock(&counter->n_events_list_lock);
    193	spin_lock_irqsave(&counter->events_list_lock, flags);
    194
    195	counter_events_list_free(&counter->events_list);
    196	list_replace_init(&counter->next_events_list,
    197			  &counter->events_list);
    198
    199	if (counter->ops->events_configure)
    200		err = counter->ops->events_configure(counter);
    201
    202	spin_unlock_irqrestore(&counter->events_list_lock, flags);
    203	mutex_unlock(&counter->n_events_list_lock);
    204
    205	return err;
    206}
    207
    208static int counter_disable_events(struct counter_device *const counter)
    209{
    210	unsigned long flags;
    211	int err = 0;
    212
    213	spin_lock_irqsave(&counter->events_list_lock, flags);
    214
    215	counter_events_list_free(&counter->events_list);
    216
    217	if (counter->ops->events_configure)
    218		err = counter->ops->events_configure(counter);
    219
    220	spin_unlock_irqrestore(&counter->events_list_lock, flags);
    221
    222	mutex_lock(&counter->n_events_list_lock);
    223
    224	counter_events_list_free(&counter->next_events_list);
    225
    226	mutex_unlock(&counter->n_events_list_lock);
    227
    228	return err;
    229}
    230
    231static int counter_add_watch(struct counter_device *const counter,
    232			     const unsigned long arg)
    233{
    234	void __user *const uwatch = (void __user *)arg;
    235	struct counter_watch watch;
    236	struct counter_comp_node comp_node = {};
    237	size_t parent, id;
    238	struct counter_comp *ext;
    239	size_t num_ext;
    240	int err = 0;
    241
    242	if (copy_from_user(&watch, uwatch, sizeof(watch)))
    243		return -EFAULT;
    244
    245	if (watch.component.type == COUNTER_COMPONENT_NONE)
    246		goto no_component;
    247
    248	parent = watch.component.parent;
    249
    250	/* Configure parent component info for comp node */
    251	switch (watch.component.scope) {
    252	case COUNTER_SCOPE_DEVICE:
    253		ext = counter->ext;
    254		num_ext = counter->num_ext;
    255		break;
    256	case COUNTER_SCOPE_SIGNAL:
    257		if (parent >= counter->num_signals)
    258			return -EINVAL;
    259		parent = array_index_nospec(parent, counter->num_signals);
    260
    261		comp_node.parent = counter->signals + parent;
    262
    263		ext = counter->signals[parent].ext;
    264		num_ext = counter->signals[parent].num_ext;
    265		break;
    266	case COUNTER_SCOPE_COUNT:
    267		if (parent >= counter->num_counts)
    268			return -EINVAL;
    269		parent = array_index_nospec(parent, counter->num_counts);
    270
    271		comp_node.parent = counter->counts + parent;
    272
    273		ext = counter->counts[parent].ext;
    274		num_ext = counter->counts[parent].num_ext;
    275		break;
    276	default:
    277		return -EINVAL;
    278	}
    279
    280	id = watch.component.id;
    281
    282	/* Configure component info for comp node */
    283	switch (watch.component.type) {
    284	case COUNTER_COMPONENT_SIGNAL:
    285		if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
    286			return -EINVAL;
    287
    288		comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
    289		comp_node.comp.signal_u32_read = counter->ops->signal_read;
    290		break;
    291	case COUNTER_COMPONENT_COUNT:
    292		if (watch.component.scope != COUNTER_SCOPE_COUNT)
    293			return -EINVAL;
    294
    295		comp_node.comp.type = COUNTER_COMP_U64;
    296		comp_node.comp.count_u64_read = counter->ops->count_read;
    297		break;
    298	case COUNTER_COMPONENT_FUNCTION:
    299		if (watch.component.scope != COUNTER_SCOPE_COUNT)
    300			return -EINVAL;
    301
    302		comp_node.comp.type = COUNTER_COMP_FUNCTION;
    303		comp_node.comp.count_u32_read = counter->ops->function_read;
    304		break;
    305	case COUNTER_COMPONENT_SYNAPSE_ACTION:
    306		if (watch.component.scope != COUNTER_SCOPE_COUNT)
    307			return -EINVAL;
    308		if (id >= counter->counts[parent].num_synapses)
    309			return -EINVAL;
    310		id = array_index_nospec(id, counter->counts[parent].num_synapses);
    311
    312		comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
    313		comp_node.comp.action_read = counter->ops->action_read;
    314		comp_node.comp.priv = counter->counts[parent].synapses + id;
    315		break;
    316	case COUNTER_COMPONENT_EXTENSION:
    317		if (id >= num_ext)
    318			return -EINVAL;
    319		id = array_index_nospec(id, num_ext);
    320
    321		comp_node.comp = ext[id];
    322		break;
    323	default:
    324		return -EINVAL;
    325	}
    326	if (!counter_comp_read_is_set(comp_node.comp))
    327		return -EOPNOTSUPP;
    328
    329no_component:
    330	mutex_lock(&counter->n_events_list_lock);
    331
    332	if (counter->ops->watch_validate) {
    333		err = counter->ops->watch_validate(counter, &watch);
    334		if (err < 0)
    335			goto err_exit;
    336	}
    337
    338	comp_node.component = watch.component;
    339
    340	err = counter_set_event_node(counter, &watch, &comp_node);
    341
    342err_exit:
    343	mutex_unlock(&counter->n_events_list_lock);
    344
    345	return err;
    346}
    347
    348static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
    349				 unsigned long arg)
    350{
    351	struct counter_device *const counter = filp->private_data;
    352	int ret = -ENODEV;
    353
    354	mutex_lock(&counter->ops_exist_lock);
    355
    356	if (!counter->ops)
    357		goto out_unlock;
    358
    359	switch (cmd) {
    360	case COUNTER_ADD_WATCH_IOCTL:
    361		ret = counter_add_watch(counter, arg);
    362		break;
    363	case COUNTER_ENABLE_EVENTS_IOCTL:
    364		ret = counter_enable_events(counter);
    365		break;
    366	case COUNTER_DISABLE_EVENTS_IOCTL:
    367		ret = counter_disable_events(counter);
    368		break;
    369	default:
    370		ret = -ENOIOCTLCMD;
    371		break;
    372	}
    373
    374out_unlock:
    375	mutex_unlock(&counter->ops_exist_lock);
    376
    377	return ret;
    378}
    379
    380static int counter_chrdev_open(struct inode *inode, struct file *filp)
    381{
    382	struct counter_device *const counter = container_of(inode->i_cdev,
    383							    typeof(*counter),
    384							    chrdev);
    385
    386	get_device(&counter->dev);
    387	filp->private_data = counter;
    388
    389	return nonseekable_open(inode, filp);
    390}
    391
    392static int counter_chrdev_release(struct inode *inode, struct file *filp)
    393{
    394	struct counter_device *const counter = filp->private_data;
    395	int ret = 0;
    396
    397	mutex_lock(&counter->ops_exist_lock);
    398
    399	if (!counter->ops) {
    400		/* Free any lingering held memory */
    401		counter_events_list_free(&counter->events_list);
    402		counter_events_list_free(&counter->next_events_list);
    403		ret = -ENODEV;
    404		goto out_unlock;
    405	}
    406
    407	ret = counter_disable_events(counter);
    408	if (ret < 0) {
    409		mutex_unlock(&counter->ops_exist_lock);
    410		return ret;
    411	}
    412
    413out_unlock:
    414	mutex_unlock(&counter->ops_exist_lock);
    415
    416	put_device(&counter->dev);
    417
    418	return ret;
    419}
    420
    421static const struct file_operations counter_fops = {
    422	.owner = THIS_MODULE,
    423	.llseek = no_llseek,
    424	.read = counter_chrdev_read,
    425	.poll = counter_chrdev_poll,
    426	.unlocked_ioctl = counter_chrdev_ioctl,
    427	.open = counter_chrdev_open,
    428	.release = counter_chrdev_release,
    429};
    430
    431int counter_chrdev_add(struct counter_device *const counter)
    432{
    433	/* Initialize Counter events lists */
    434	INIT_LIST_HEAD(&counter->events_list);
    435	INIT_LIST_HEAD(&counter->next_events_list);
    436	spin_lock_init(&counter->events_list_lock);
    437	mutex_init(&counter->n_events_list_lock);
    438	init_waitqueue_head(&counter->events_wait);
    439	spin_lock_init(&counter->events_in_lock);
    440	mutex_init(&counter->events_out_lock);
    441
    442	/* Initialize character device */
    443	cdev_init(&counter->chrdev, &counter_fops);
    444
    445	/* Allocate Counter events queue */
    446	return kfifo_alloc(&counter->events, 64, GFP_KERNEL);
    447}
    448
    449void counter_chrdev_remove(struct counter_device *const counter)
    450{
    451	kfifo_free(&counter->events);
    452}
    453
    454static int counter_get_data(struct counter_device *const counter,
    455			    const struct counter_comp_node *const comp_node,
    456			    u64 *const value)
    457{
    458	const struct counter_comp *const comp = &comp_node->comp;
    459	void *const parent = comp_node->parent;
    460	u8 value_u8 = 0;
    461	u32 value_u32 = 0;
    462	int ret;
    463
    464	if (comp_node->component.type == COUNTER_COMPONENT_NONE)
    465		return 0;
    466
    467	switch (comp->type) {
    468	case COUNTER_COMP_U8:
    469	case COUNTER_COMP_BOOL:
    470		switch (comp_node->component.scope) {
    471		case COUNTER_SCOPE_DEVICE:
    472			ret = comp->device_u8_read(counter, &value_u8);
    473			break;
    474		case COUNTER_SCOPE_SIGNAL:
    475			ret = comp->signal_u8_read(counter, parent, &value_u8);
    476			break;
    477		case COUNTER_SCOPE_COUNT:
    478			ret = comp->count_u8_read(counter, parent, &value_u8);
    479			break;
    480		default:
    481			return -EINVAL;
    482		}
    483		*value = value_u8;
    484		return ret;
    485	case COUNTER_COMP_SIGNAL_LEVEL:
    486	case COUNTER_COMP_FUNCTION:
    487	case COUNTER_COMP_ENUM:
    488	case COUNTER_COMP_COUNT_DIRECTION:
    489	case COUNTER_COMP_COUNT_MODE:
    490		switch (comp_node->component.scope) {
    491		case COUNTER_SCOPE_DEVICE:
    492			ret = comp->device_u32_read(counter, &value_u32);
    493			break;
    494		case COUNTER_SCOPE_SIGNAL:
    495			ret = comp->signal_u32_read(counter, parent,
    496						    &value_u32);
    497			break;
    498		case COUNTER_SCOPE_COUNT:
    499			ret = comp->count_u32_read(counter, parent, &value_u32);
    500			break;
    501		default:
    502			return -EINVAL;
    503		}
    504		*value = value_u32;
    505		return ret;
    506	case COUNTER_COMP_U64:
    507		switch (comp_node->component.scope) {
    508		case COUNTER_SCOPE_DEVICE:
    509			return comp->device_u64_read(counter, value);
    510		case COUNTER_SCOPE_SIGNAL:
    511			return comp->signal_u64_read(counter, parent, value);
    512		case COUNTER_SCOPE_COUNT:
    513			return comp->count_u64_read(counter, parent, value);
    514		default:
    515			return -EINVAL;
    516		}
    517	case COUNTER_COMP_SYNAPSE_ACTION:
    518		ret = comp->action_read(counter, parent, comp->priv,
    519					&value_u32);
    520		*value = value_u32;
    521		return ret;
    522	default:
    523		return -EINVAL;
    524	}
    525}
    526
    527/**
    528 * counter_push_event - queue event for userspace reading
    529 * @counter:	pointer to Counter structure
    530 * @event:	triggered event
    531 * @channel:	event channel
    532 *
    533 * Note: If no one is watching for the respective event, it is silently
    534 * discarded.
    535 */
    536void counter_push_event(struct counter_device *const counter, const u8 event,
    537			const u8 channel)
    538{
    539	struct counter_event ev;
    540	unsigned int copied = 0;
    541	unsigned long flags;
    542	struct counter_event_node *event_node;
    543	struct counter_comp_node *comp_node;
    544
    545	ev.timestamp = ktime_get_ns();
    546	ev.watch.event = event;
    547	ev.watch.channel = channel;
    548
    549	/* Could be in an interrupt context, so use a spin lock */
    550	spin_lock_irqsave(&counter->events_list_lock, flags);
    551
    552	/* Search for event in the list */
    553	list_for_each_entry(event_node, &counter->events_list, l)
    554		if (event_node->event == event &&
    555		    event_node->channel == channel)
    556			break;
    557
    558	/* If event is not in the list */
    559	if (&event_node->l == &counter->events_list)
    560		goto exit_early;
    561
    562	/* Read and queue relevant comp for userspace */
    563	list_for_each_entry(comp_node, &event_node->comp_list, l) {
    564		ev.watch.component = comp_node->component;
    565		ev.status = -counter_get_data(counter, comp_node, &ev.value);
    566
    567		copied += kfifo_in_spinlocked_noirqsave(&counter->events, &ev,
    568							1, &counter->events_in_lock);
    569	}
    570
    571exit_early:
    572	spin_unlock_irqrestore(&counter->events_list_lock, flags);
    573
    574	if (copied)
    575		wake_up_poll(&counter->events_wait, EPOLLIN);
    576}
    577EXPORT_SYMBOL_GPL(counter_push_event);