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

virtual_ncidev.c (4626B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Virtual NCI device simulation driver
      4 *
      5 * Copyright (C) 2020 Samsung Electrnoics
      6 * Bongsu Jeon <bongsu.jeon@samsung.com>
      7 */
      8
      9#include <linux/kernel.h>
     10#include <linux/module.h>
     11#include <linux/miscdevice.h>
     12#include <linux/mutex.h>
     13#include <linux/wait.h>
     14#include <net/nfc/nci_core.h>
     15
     16enum virtual_ncidev_mode {
     17	virtual_ncidev_enabled,
     18	virtual_ncidev_disabled,
     19	virtual_ncidev_disabling,
     20};
     21
     22#define IOCTL_GET_NCIDEV_IDX    0
     23#define VIRTUAL_NFC_PROTOCOLS	(NFC_PROTO_JEWEL_MASK | \
     24				 NFC_PROTO_MIFARE_MASK | \
     25				 NFC_PROTO_FELICA_MASK | \
     26				 NFC_PROTO_ISO14443_MASK | \
     27				 NFC_PROTO_ISO14443_B_MASK | \
     28				 NFC_PROTO_ISO15693_MASK)
     29
     30static enum virtual_ncidev_mode state;
     31static DECLARE_WAIT_QUEUE_HEAD(wq);
     32static struct miscdevice miscdev;
     33static struct sk_buff *send_buff;
     34static struct nci_dev *ndev;
     35static DEFINE_MUTEX(nci_mutex);
     36
     37static int virtual_nci_open(struct nci_dev *ndev)
     38{
     39	return 0;
     40}
     41
     42static int virtual_nci_close(struct nci_dev *ndev)
     43{
     44	mutex_lock(&nci_mutex);
     45	kfree_skb(send_buff);
     46	send_buff = NULL;
     47	mutex_unlock(&nci_mutex);
     48
     49	return 0;
     50}
     51
     52static int virtual_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
     53{
     54	mutex_lock(&nci_mutex);
     55	if (state != virtual_ncidev_enabled) {
     56		mutex_unlock(&nci_mutex);
     57		return 0;
     58	}
     59
     60	if (send_buff) {
     61		mutex_unlock(&nci_mutex);
     62		return -1;
     63	}
     64	send_buff = skb_copy(skb, GFP_KERNEL);
     65	mutex_unlock(&nci_mutex);
     66	wake_up_interruptible(&wq);
     67
     68	return 0;
     69}
     70
     71static const struct nci_ops virtual_nci_ops = {
     72	.open = virtual_nci_open,
     73	.close = virtual_nci_close,
     74	.send = virtual_nci_send
     75};
     76
     77static ssize_t virtual_ncidev_read(struct file *file, char __user *buf,
     78				   size_t count, loff_t *ppos)
     79{
     80	size_t actual_len;
     81
     82	mutex_lock(&nci_mutex);
     83	while (!send_buff) {
     84		mutex_unlock(&nci_mutex);
     85		if (wait_event_interruptible(wq, send_buff))
     86			return -EFAULT;
     87		mutex_lock(&nci_mutex);
     88	}
     89
     90	actual_len = min_t(size_t, count, send_buff->len);
     91
     92	if (copy_to_user(buf, send_buff->data, actual_len)) {
     93		mutex_unlock(&nci_mutex);
     94		return -EFAULT;
     95	}
     96
     97	skb_pull(send_buff, actual_len);
     98	if (send_buff->len == 0) {
     99		consume_skb(send_buff);
    100		send_buff = NULL;
    101	}
    102	mutex_unlock(&nci_mutex);
    103
    104	return actual_len;
    105}
    106
    107static ssize_t virtual_ncidev_write(struct file *file,
    108				    const char __user *buf,
    109				    size_t count, loff_t *ppos)
    110{
    111	struct sk_buff *skb;
    112
    113	skb = alloc_skb(count, GFP_KERNEL);
    114	if (!skb)
    115		return -ENOMEM;
    116
    117	if (copy_from_user(skb_put(skb, count), buf, count)) {
    118		kfree_skb(skb);
    119		return -EFAULT;
    120	}
    121
    122	nci_recv_frame(ndev, skb);
    123	return count;
    124}
    125
    126static int virtual_ncidev_open(struct inode *inode, struct file *file)
    127{
    128	int ret = 0;
    129
    130	mutex_lock(&nci_mutex);
    131	if (state != virtual_ncidev_disabled) {
    132		mutex_unlock(&nci_mutex);
    133		return -EBUSY;
    134	}
    135
    136	ndev = nci_allocate_device(&virtual_nci_ops, VIRTUAL_NFC_PROTOCOLS,
    137				   0, 0);
    138	if (!ndev) {
    139		mutex_unlock(&nci_mutex);
    140		return -ENOMEM;
    141	}
    142
    143	ret = nci_register_device(ndev);
    144	if (ret < 0) {
    145		nci_free_device(ndev);
    146		mutex_unlock(&nci_mutex);
    147		return ret;
    148	}
    149	state = virtual_ncidev_enabled;
    150	mutex_unlock(&nci_mutex);
    151
    152	return 0;
    153}
    154
    155static int virtual_ncidev_close(struct inode *inode, struct file *file)
    156{
    157	mutex_lock(&nci_mutex);
    158
    159	if (state == virtual_ncidev_enabled) {
    160		state = virtual_ncidev_disabling;
    161		mutex_unlock(&nci_mutex);
    162
    163		nci_unregister_device(ndev);
    164		nci_free_device(ndev);
    165
    166		mutex_lock(&nci_mutex);
    167	}
    168
    169	state = virtual_ncidev_disabled;
    170	mutex_unlock(&nci_mutex);
    171
    172	return 0;
    173}
    174
    175static long virtual_ncidev_ioctl(struct file *flip, unsigned int cmd,
    176				 unsigned long arg)
    177{
    178	const struct nfc_dev *nfc_dev = ndev->nfc_dev;
    179	void __user *p = (void __user *)arg;
    180
    181	if (cmd != IOCTL_GET_NCIDEV_IDX)
    182		return -ENOTTY;
    183
    184	if (copy_to_user(p, &nfc_dev->idx, sizeof(nfc_dev->idx)))
    185		return -EFAULT;
    186
    187	return 0;
    188}
    189
    190static const struct file_operations virtual_ncidev_fops = {
    191	.owner = THIS_MODULE,
    192	.read = virtual_ncidev_read,
    193	.write = virtual_ncidev_write,
    194	.open = virtual_ncidev_open,
    195	.release = virtual_ncidev_close,
    196	.unlocked_ioctl = virtual_ncidev_ioctl
    197};
    198
    199static int __init virtual_ncidev_init(void)
    200{
    201	state = virtual_ncidev_disabled;
    202	miscdev.minor = MISC_DYNAMIC_MINOR;
    203	miscdev.name = "virtual_nci";
    204	miscdev.fops = &virtual_ncidev_fops;
    205	miscdev.mode = 0600;
    206
    207	return misc_register(&miscdev);
    208}
    209
    210static void __exit virtual_ncidev_exit(void)
    211{
    212	misc_deregister(&miscdev);
    213}
    214
    215module_init(virtual_ncidev_init);
    216module_exit(virtual_ncidev_exit);
    217
    218MODULE_LICENSE("GPL");
    219MODULE_DESCRIPTION("Virtual NCI device simulation driver");
    220MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");