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

ibmasmfs.c (14356B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * IBM ASM Service Processor Device Driver
      4 *
      5 * Copyright (C) IBM Corporation, 2004
      6 *
      7 * Author: Max Asböck <amax@us.ibm.com>
      8 */
      9
     10/*
     11 * Parts of this code are based on an article by Jonathan Corbet
     12 * that appeared in Linux Weekly News.
     13 */
     14
     15
     16/*
     17 * The IBMASM file virtual filesystem. It creates the following hierarchy
     18 * dynamically when mounted from user space:
     19 *
     20 *    /ibmasm
     21 *    |-- 0
     22 *    |   |-- command
     23 *    |   |-- event
     24 *    |   |-- reverse_heartbeat
     25 *    |   `-- remote_video
     26 *    |       |-- depth
     27 *    |       |-- height
     28 *    |       `-- width
     29 *    .
     30 *    .
     31 *    .
     32 *    `-- n
     33 *        |-- command
     34 *        |-- event
     35 *        |-- reverse_heartbeat
     36 *        `-- remote_video
     37 *            |-- depth
     38 *            |-- height
     39 *            `-- width
     40 *
     41 * For each service processor the following files are created:
     42 *
     43 * command: execute dot commands
     44 *	write: execute a dot command on the service processor
     45 *	read: return the result of a previously executed dot command
     46 *
     47 * events: listen for service processor events
     48 *	read: sleep (interruptible) until an event occurs
     49 *      write: wakeup sleeping event listener
     50 *
     51 * reverse_heartbeat: send a heartbeat to the service processor
     52 *	read: sleep (interruptible) until the reverse heartbeat fails
     53 *      write: wakeup sleeping heartbeat listener
     54 *
     55 * remote_video/width
     56 * remote_video/height
     57 * remote_video/width: control remote display settings
     58 *	write: set value
     59 *	read: read value
     60 */
     61
     62#include <linux/fs.h>
     63#include <linux/fs_context.h>
     64#include <linux/pagemap.h>
     65#include <linux/slab.h>
     66#include <linux/uaccess.h>
     67#include <asm/io.h>
     68#include "ibmasm.h"
     69#include "remote.h"
     70#include "dot_command.h"
     71
     72#define IBMASMFS_MAGIC 0x66726f67
     73
     74static LIST_HEAD(service_processors);
     75
     76static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode);
     77static void ibmasmfs_create_files (struct super_block *sb);
     78static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc);
     79
     80static int ibmasmfs_get_tree(struct fs_context *fc)
     81{
     82	return get_tree_single(fc, ibmasmfs_fill_super);
     83}
     84
     85static const struct fs_context_operations ibmasmfs_context_ops = {
     86	.get_tree	= ibmasmfs_get_tree,
     87};
     88
     89static int ibmasmfs_init_fs_context(struct fs_context *fc)
     90{
     91	fc->ops = &ibmasmfs_context_ops;
     92	return 0;
     93}
     94
     95static const struct super_operations ibmasmfs_s_ops = {
     96	.statfs		= simple_statfs,
     97	.drop_inode	= generic_delete_inode,
     98};
     99
    100static const struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations;
    101
    102static struct file_system_type ibmasmfs_type = {
    103	.owner          = THIS_MODULE,
    104	.name           = "ibmasmfs",
    105	.init_fs_context = ibmasmfs_init_fs_context,
    106	.kill_sb        = kill_litter_super,
    107};
    108MODULE_ALIAS_FS("ibmasmfs");
    109
    110static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc)
    111{
    112	struct inode *root;
    113
    114	sb->s_blocksize = PAGE_SIZE;
    115	sb->s_blocksize_bits = PAGE_SHIFT;
    116	sb->s_magic = IBMASMFS_MAGIC;
    117	sb->s_op = &ibmasmfs_s_ops;
    118	sb->s_time_gran = 1;
    119
    120	root = ibmasmfs_make_inode (sb, S_IFDIR | 0500);
    121	if (!root)
    122		return -ENOMEM;
    123
    124	root->i_op = &simple_dir_inode_operations;
    125	root->i_fop = ibmasmfs_dir_ops;
    126
    127	sb->s_root = d_make_root(root);
    128	if (!sb->s_root)
    129		return -ENOMEM;
    130
    131	ibmasmfs_create_files(sb);
    132	return 0;
    133}
    134
    135static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
    136{
    137	struct inode *ret = new_inode(sb);
    138
    139	if (ret) {
    140		ret->i_ino = get_next_ino();
    141		ret->i_mode = mode;
    142		ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret);
    143	}
    144	return ret;
    145}
    146
    147static struct dentry *ibmasmfs_create_file(struct dentry *parent,
    148			const char *name,
    149			const struct file_operations *fops,
    150			void *data,
    151			int mode)
    152{
    153	struct dentry *dentry;
    154	struct inode *inode;
    155
    156	dentry = d_alloc_name(parent, name);
    157	if (!dentry)
    158		return NULL;
    159
    160	inode = ibmasmfs_make_inode(parent->d_sb, S_IFREG | mode);
    161	if (!inode) {
    162		dput(dentry);
    163		return NULL;
    164	}
    165
    166	inode->i_fop = fops;
    167	inode->i_private = data;
    168
    169	d_add(dentry, inode);
    170	return dentry;
    171}
    172
    173static struct dentry *ibmasmfs_create_dir(struct dentry *parent,
    174				const char *name)
    175{
    176	struct dentry *dentry;
    177	struct inode *inode;
    178
    179	dentry = d_alloc_name(parent, name);
    180	if (!dentry)
    181		return NULL;
    182
    183	inode = ibmasmfs_make_inode(parent->d_sb, S_IFDIR | 0500);
    184	if (!inode) {
    185		dput(dentry);
    186		return NULL;
    187	}
    188
    189	inode->i_op = &simple_dir_inode_operations;
    190	inode->i_fop = ibmasmfs_dir_ops;
    191
    192	d_add(dentry, inode);
    193	return dentry;
    194}
    195
    196int ibmasmfs_register(void)
    197{
    198	return register_filesystem(&ibmasmfs_type);
    199}
    200
    201void ibmasmfs_unregister(void)
    202{
    203	unregister_filesystem(&ibmasmfs_type);
    204}
    205
    206void ibmasmfs_add_sp(struct service_processor *sp)
    207{
    208	list_add(&sp->node, &service_processors);
    209}
    210
    211/* struct to save state between command file operations */
    212struct ibmasmfs_command_data {
    213	struct service_processor	*sp;
    214	struct command			*command;
    215};
    216
    217/* struct to save state between event file operations */
    218struct ibmasmfs_event_data {
    219	struct service_processor	*sp;
    220	struct event_reader		reader;
    221	int				active;
    222};
    223
    224/* struct to save state between reverse heartbeat file operations */
    225struct ibmasmfs_heartbeat_data {
    226	struct service_processor	*sp;
    227	struct reverse_heartbeat	heartbeat;
    228	int				active;
    229};
    230
    231static int command_file_open(struct inode *inode, struct file *file)
    232{
    233	struct ibmasmfs_command_data *command_data;
    234
    235	if (!inode->i_private)
    236		return -ENODEV;
    237
    238	command_data = kmalloc(sizeof(struct ibmasmfs_command_data), GFP_KERNEL);
    239	if (!command_data)
    240		return -ENOMEM;
    241
    242	command_data->command = NULL;
    243	command_data->sp = inode->i_private;
    244	file->private_data = command_data;
    245	return 0;
    246}
    247
    248static int command_file_close(struct inode *inode, struct file *file)
    249{
    250	struct ibmasmfs_command_data *command_data = file->private_data;
    251
    252	if (command_data->command)
    253		command_put(command_data->command);
    254
    255	kfree(command_data);
    256	return 0;
    257}
    258
    259static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
    260{
    261	struct ibmasmfs_command_data *command_data = file->private_data;
    262	struct command *cmd;
    263	int len;
    264	unsigned long flags;
    265
    266	if (*offset < 0)
    267		return -EINVAL;
    268	if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
    269		return 0;
    270	if (*offset != 0)
    271		return 0;
    272
    273	spin_lock_irqsave(&command_data->sp->lock, flags);
    274	cmd = command_data->command;
    275	if (cmd == NULL) {
    276		spin_unlock_irqrestore(&command_data->sp->lock, flags);
    277		return 0;
    278	}
    279	command_data->command = NULL;
    280	spin_unlock_irqrestore(&command_data->sp->lock, flags);
    281
    282	if (cmd->status != IBMASM_CMD_COMPLETE) {
    283		command_put(cmd);
    284		return -EIO;
    285	}
    286	len = min(count, cmd->buffer_size);
    287	if (copy_to_user(buf, cmd->buffer, len)) {
    288		command_put(cmd);
    289		return -EFAULT;
    290	}
    291	command_put(cmd);
    292
    293	return len;
    294}
    295
    296static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
    297{
    298	struct ibmasmfs_command_data *command_data = file->private_data;
    299	struct command *cmd;
    300	unsigned long flags;
    301
    302	if (*offset < 0)
    303		return -EINVAL;
    304	if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
    305		return 0;
    306	if (*offset != 0)
    307		return 0;
    308
    309	/* commands are executed sequentially, only one command at a time */
    310	if (command_data->command)
    311		return -EAGAIN;
    312
    313	cmd = ibmasm_new_command(command_data->sp, count);
    314	if (!cmd)
    315		return -ENOMEM;
    316
    317	if (copy_from_user(cmd->buffer, ubuff, count)) {
    318		command_put(cmd);
    319		return -EFAULT;
    320	}
    321
    322	spin_lock_irqsave(&command_data->sp->lock, flags);
    323	if (command_data->command) {
    324		spin_unlock_irqrestore(&command_data->sp->lock, flags);
    325		command_put(cmd);
    326		return -EAGAIN;
    327	}
    328	command_data->command = cmd;
    329	spin_unlock_irqrestore(&command_data->sp->lock, flags);
    330
    331	ibmasm_exec_command(command_data->sp, cmd);
    332	ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer));
    333
    334	return count;
    335}
    336
    337static int event_file_open(struct inode *inode, struct file *file)
    338{
    339	struct ibmasmfs_event_data *event_data;
    340	struct service_processor *sp;
    341
    342	if (!inode->i_private)
    343		return -ENODEV;
    344
    345	sp = inode->i_private;
    346
    347	event_data = kmalloc(sizeof(struct ibmasmfs_event_data), GFP_KERNEL);
    348	if (!event_data)
    349		return -ENOMEM;
    350
    351	ibmasm_event_reader_register(sp, &event_data->reader);
    352
    353	event_data->sp = sp;
    354	event_data->active = 0;
    355	file->private_data = event_data;
    356	return 0;
    357}
    358
    359static int event_file_close(struct inode *inode, struct file *file)
    360{
    361	struct ibmasmfs_event_data *event_data = file->private_data;
    362
    363	ibmasm_event_reader_unregister(event_data->sp, &event_data->reader);
    364	kfree(event_data);
    365	return 0;
    366}
    367
    368static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
    369{
    370	struct ibmasmfs_event_data *event_data = file->private_data;
    371	struct event_reader *reader = &event_data->reader;
    372	struct service_processor *sp = event_data->sp;
    373	int ret;
    374	unsigned long flags;
    375
    376	if (*offset < 0)
    377		return -EINVAL;
    378	if (count == 0 || count > IBMASM_EVENT_MAX_SIZE)
    379		return 0;
    380	if (*offset != 0)
    381		return 0;
    382
    383	spin_lock_irqsave(&sp->lock, flags);
    384	if (event_data->active) {
    385		spin_unlock_irqrestore(&sp->lock, flags);
    386		return -EBUSY;
    387	}
    388	event_data->active = 1;
    389	spin_unlock_irqrestore(&sp->lock, flags);
    390
    391	ret = ibmasm_get_next_event(sp, reader);
    392	if (ret <= 0)
    393		goto out;
    394
    395	if (count < reader->data_size) {
    396		ret = -EINVAL;
    397		goto out;
    398	}
    399
    400        if (copy_to_user(buf, reader->data, reader->data_size)) {
    401		ret = -EFAULT;
    402		goto out;
    403	}
    404	ret = reader->data_size;
    405
    406out:
    407	event_data->active = 0;
    408	return ret;
    409}
    410
    411static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
    412{
    413	struct ibmasmfs_event_data *event_data = file->private_data;
    414
    415	if (*offset < 0)
    416		return -EINVAL;
    417	if (count != 1)
    418		return 0;
    419	if (*offset != 0)
    420		return 0;
    421
    422	ibmasm_cancel_next_event(&event_data->reader);
    423	return 0;
    424}
    425
    426static int r_heartbeat_file_open(struct inode *inode, struct file *file)
    427{
    428	struct ibmasmfs_heartbeat_data *rhbeat;
    429
    430	if (!inode->i_private)
    431		return -ENODEV;
    432
    433	rhbeat = kmalloc(sizeof(struct ibmasmfs_heartbeat_data), GFP_KERNEL);
    434	if (!rhbeat)
    435		return -ENOMEM;
    436
    437	rhbeat->sp = inode->i_private;
    438	rhbeat->active = 0;
    439	ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
    440	file->private_data = rhbeat;
    441	return 0;
    442}
    443
    444static int r_heartbeat_file_close(struct inode *inode, struct file *file)
    445{
    446	struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
    447
    448	kfree(rhbeat);
    449	return 0;
    450}
    451
    452static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
    453{
    454	struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
    455	unsigned long flags;
    456	int result;
    457
    458	if (*offset < 0)
    459		return -EINVAL;
    460	if (count == 0 || count > 1024)
    461		return 0;
    462	if (*offset != 0)
    463		return 0;
    464
    465	/* allow only one reverse heartbeat per process */
    466	spin_lock_irqsave(&rhbeat->sp->lock, flags);
    467	if (rhbeat->active) {
    468		spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
    469		return -EBUSY;
    470	}
    471	rhbeat->active = 1;
    472	spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
    473
    474	result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
    475	rhbeat->active = 0;
    476
    477	return result;
    478}
    479
    480static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
    481{
    482	struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
    483
    484	if (*offset < 0)
    485		return -EINVAL;
    486	if (count != 1)
    487		return 0;
    488	if (*offset != 0)
    489		return 0;
    490
    491	if (rhbeat->active)
    492		ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat);
    493
    494	return 1;
    495}
    496
    497static int remote_settings_file_close(struct inode *inode, struct file *file)
    498{
    499	return 0;
    500}
    501
    502static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
    503{
    504	void __iomem *address = (void __iomem *)file->private_data;
    505	int len = 0;
    506	unsigned int value;
    507	char lbuf[20];
    508
    509	value = readl(address);
    510	len = snprintf(lbuf, sizeof(lbuf), "%d\n", value);
    511
    512	return simple_read_from_buffer(buf, count, offset, lbuf, len);
    513}
    514
    515static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
    516{
    517	void __iomem *address = (void __iomem *)file->private_data;
    518	char *buff;
    519	unsigned int value;
    520
    521	if (*offset < 0)
    522		return -EINVAL;
    523	if (count == 0 || count > 1024)
    524		return 0;
    525	if (*offset != 0)
    526		return 0;
    527
    528	buff = kzalloc (count + 1, GFP_KERNEL);
    529	if (!buff)
    530		return -ENOMEM;
    531
    532
    533	if (copy_from_user(buff, ubuff, count)) {
    534		kfree(buff);
    535		return -EFAULT;
    536	}
    537
    538	value = simple_strtoul(buff, NULL, 10);
    539	writel(value, address);
    540	kfree(buff);
    541
    542	return count;
    543}
    544
    545static const struct file_operations command_fops = {
    546	.open =		command_file_open,
    547	.release =	command_file_close,
    548	.read =		command_file_read,
    549	.write =	command_file_write,
    550	.llseek =	generic_file_llseek,
    551};
    552
    553static const struct file_operations event_fops = {
    554	.open =		event_file_open,
    555	.release =	event_file_close,
    556	.read =		event_file_read,
    557	.write =	event_file_write,
    558	.llseek =	generic_file_llseek,
    559};
    560
    561static const struct file_operations r_heartbeat_fops = {
    562	.open =		r_heartbeat_file_open,
    563	.release =	r_heartbeat_file_close,
    564	.read =		r_heartbeat_file_read,
    565	.write =	r_heartbeat_file_write,
    566	.llseek =	generic_file_llseek,
    567};
    568
    569static const struct file_operations remote_settings_fops = {
    570	.open =		simple_open,
    571	.release =	remote_settings_file_close,
    572	.read =		remote_settings_file_read,
    573	.write =	remote_settings_file_write,
    574	.llseek =	generic_file_llseek,
    575};
    576
    577
    578static void ibmasmfs_create_files (struct super_block *sb)
    579{
    580	struct list_head *entry;
    581	struct service_processor *sp;
    582
    583	list_for_each(entry, &service_processors) {
    584		struct dentry *dir;
    585		struct dentry *remote_dir;
    586		sp = list_entry(entry, struct service_processor, node);
    587		dir = ibmasmfs_create_dir(sb->s_root, sp->dirname);
    588		if (!dir)
    589			continue;
    590
    591		ibmasmfs_create_file(dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR);
    592		ibmasmfs_create_file(dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR);
    593		ibmasmfs_create_file(dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR);
    594
    595		remote_dir = ibmasmfs_create_dir(dir, "remote_video");
    596		if (!remote_dir)
    597			continue;
    598
    599		ibmasmfs_create_file(remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR);
    600		ibmasmfs_create_file(remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR);
    601		ibmasmfs_create_file(remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR);
    602	}
    603}