Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * IBM ASM Service Processor Device Driver
0004  *
0005  * Copyright (C) IBM Corporation, 2004
0006  *
0007  * Author: Max Asböck <amax@us.ibm.com>
0008  */
0009 
0010 /*
0011  * Parts of this code are based on an article by Jonathan Corbet
0012  * that appeared in Linux Weekly News.
0013  */
0014 
0015 
0016 /*
0017  * The IBMASM file virtual filesystem. It creates the following hierarchy
0018  * dynamically when mounted from user space:
0019  *
0020  *    /ibmasm
0021  *    |-- 0
0022  *    |   |-- command
0023  *    |   |-- event
0024  *    |   |-- reverse_heartbeat
0025  *    |   `-- remote_video
0026  *    |       |-- depth
0027  *    |       |-- height
0028  *    |       `-- width
0029  *    .
0030  *    .
0031  *    .
0032  *    `-- n
0033  *        |-- command
0034  *        |-- event
0035  *        |-- reverse_heartbeat
0036  *        `-- remote_video
0037  *            |-- depth
0038  *            |-- height
0039  *            `-- width
0040  *
0041  * For each service processor the following files are created:
0042  *
0043  * command: execute dot commands
0044  *  write: execute a dot command on the service processor
0045  *  read: return the result of a previously executed dot command
0046  *
0047  * events: listen for service processor events
0048  *  read: sleep (interruptible) until an event occurs
0049  *      write: wakeup sleeping event listener
0050  *
0051  * reverse_heartbeat: send a heartbeat to the service processor
0052  *  read: sleep (interruptible) until the reverse heartbeat fails
0053  *      write: wakeup sleeping heartbeat listener
0054  *
0055  * remote_video/width
0056  * remote_video/height
0057  * remote_video/width: control remote display settings
0058  *  write: set value
0059  *  read: read value
0060  */
0061 
0062 #include <linux/fs.h>
0063 #include <linux/fs_context.h>
0064 #include <linux/pagemap.h>
0065 #include <linux/slab.h>
0066 #include <linux/uaccess.h>
0067 #include <asm/io.h>
0068 #include "ibmasm.h"
0069 #include "remote.h"
0070 #include "dot_command.h"
0071 
0072 #define IBMASMFS_MAGIC 0x66726f67
0073 
0074 static LIST_HEAD(service_processors);
0075 
0076 static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode);
0077 static void ibmasmfs_create_files (struct super_block *sb);
0078 static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc);
0079 
0080 static int ibmasmfs_get_tree(struct fs_context *fc)
0081 {
0082     return get_tree_single(fc, ibmasmfs_fill_super);
0083 }
0084 
0085 static const struct fs_context_operations ibmasmfs_context_ops = {
0086     .get_tree   = ibmasmfs_get_tree,
0087 };
0088 
0089 static int ibmasmfs_init_fs_context(struct fs_context *fc)
0090 {
0091     fc->ops = &ibmasmfs_context_ops;
0092     return 0;
0093 }
0094 
0095 static const struct super_operations ibmasmfs_s_ops = {
0096     .statfs     = simple_statfs,
0097     .drop_inode = generic_delete_inode,
0098 };
0099 
0100 static const struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations;
0101 
0102 static struct file_system_type ibmasmfs_type = {
0103     .owner          = THIS_MODULE,
0104     .name           = "ibmasmfs",
0105     .init_fs_context = ibmasmfs_init_fs_context,
0106     .kill_sb        = kill_litter_super,
0107 };
0108 MODULE_ALIAS_FS("ibmasmfs");
0109 
0110 static int ibmasmfs_fill_super(struct super_block *sb, struct fs_context *fc)
0111 {
0112     struct inode *root;
0113 
0114     sb->s_blocksize = PAGE_SIZE;
0115     sb->s_blocksize_bits = PAGE_SHIFT;
0116     sb->s_magic = IBMASMFS_MAGIC;
0117     sb->s_op = &ibmasmfs_s_ops;
0118     sb->s_time_gran = 1;
0119 
0120     root = ibmasmfs_make_inode (sb, S_IFDIR | 0500);
0121     if (!root)
0122         return -ENOMEM;
0123 
0124     root->i_op = &simple_dir_inode_operations;
0125     root->i_fop = ibmasmfs_dir_ops;
0126 
0127     sb->s_root = d_make_root(root);
0128     if (!sb->s_root)
0129         return -ENOMEM;
0130 
0131     ibmasmfs_create_files(sb);
0132     return 0;
0133 }
0134 
0135 static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
0136 {
0137     struct inode *ret = new_inode(sb);
0138 
0139     if (ret) {
0140         ret->i_ino = get_next_ino();
0141         ret->i_mode = mode;
0142         ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret);
0143     }
0144     return ret;
0145 }
0146 
0147 static struct dentry *ibmasmfs_create_file(struct dentry *parent,
0148             const char *name,
0149             const struct file_operations *fops,
0150             void *data,
0151             int mode)
0152 {
0153     struct dentry *dentry;
0154     struct inode *inode;
0155 
0156     dentry = d_alloc_name(parent, name);
0157     if (!dentry)
0158         return NULL;
0159 
0160     inode = ibmasmfs_make_inode(parent->d_sb, S_IFREG | mode);
0161     if (!inode) {
0162         dput(dentry);
0163         return NULL;
0164     }
0165 
0166     inode->i_fop = fops;
0167     inode->i_private = data;
0168 
0169     d_add(dentry, inode);
0170     return dentry;
0171 }
0172 
0173 static struct dentry *ibmasmfs_create_dir(struct dentry *parent,
0174                 const char *name)
0175 {
0176     struct dentry *dentry;
0177     struct inode *inode;
0178 
0179     dentry = d_alloc_name(parent, name);
0180     if (!dentry)
0181         return NULL;
0182 
0183     inode = ibmasmfs_make_inode(parent->d_sb, S_IFDIR | 0500);
0184     if (!inode) {
0185         dput(dentry);
0186         return NULL;
0187     }
0188 
0189     inode->i_op = &simple_dir_inode_operations;
0190     inode->i_fop = ibmasmfs_dir_ops;
0191 
0192     d_add(dentry, inode);
0193     return dentry;
0194 }
0195 
0196 int ibmasmfs_register(void)
0197 {
0198     return register_filesystem(&ibmasmfs_type);
0199 }
0200 
0201 void ibmasmfs_unregister(void)
0202 {
0203     unregister_filesystem(&ibmasmfs_type);
0204 }
0205 
0206 void ibmasmfs_add_sp(struct service_processor *sp)
0207 {
0208     list_add(&sp->node, &service_processors);
0209 }
0210 
0211 /* struct to save state between command file operations */
0212 struct ibmasmfs_command_data {
0213     struct service_processor    *sp;
0214     struct command          *command;
0215 };
0216 
0217 /* struct to save state between event file operations */
0218 struct ibmasmfs_event_data {
0219     struct service_processor    *sp;
0220     struct event_reader     reader;
0221     int             active;
0222 };
0223 
0224 /* struct to save state between reverse heartbeat file operations */
0225 struct ibmasmfs_heartbeat_data {
0226     struct service_processor    *sp;
0227     struct reverse_heartbeat    heartbeat;
0228     int             active;
0229 };
0230 
0231 static int command_file_open(struct inode *inode, struct file *file)
0232 {
0233     struct ibmasmfs_command_data *command_data;
0234 
0235     if (!inode->i_private)
0236         return -ENODEV;
0237 
0238     command_data = kmalloc(sizeof(struct ibmasmfs_command_data), GFP_KERNEL);
0239     if (!command_data)
0240         return -ENOMEM;
0241 
0242     command_data->command = NULL;
0243     command_data->sp = inode->i_private;
0244     file->private_data = command_data;
0245     return 0;
0246 }
0247 
0248 static int command_file_close(struct inode *inode, struct file *file)
0249 {
0250     struct ibmasmfs_command_data *command_data = file->private_data;
0251 
0252     if (command_data->command)
0253         command_put(command_data->command);
0254 
0255     kfree(command_data);
0256     return 0;
0257 }
0258 
0259 static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
0260 {
0261     struct ibmasmfs_command_data *command_data = file->private_data;
0262     struct command *cmd;
0263     int len;
0264     unsigned long flags;
0265 
0266     if (*offset < 0)
0267         return -EINVAL;
0268     if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
0269         return 0;
0270     if (*offset != 0)
0271         return 0;
0272 
0273     spin_lock_irqsave(&command_data->sp->lock, flags);
0274     cmd = command_data->command;
0275     if (cmd == NULL) {
0276         spin_unlock_irqrestore(&command_data->sp->lock, flags);
0277         return 0;
0278     }
0279     command_data->command = NULL;
0280     spin_unlock_irqrestore(&command_data->sp->lock, flags);
0281 
0282     if (cmd->status != IBMASM_CMD_COMPLETE) {
0283         command_put(cmd);
0284         return -EIO;
0285     }
0286     len = min(count, cmd->buffer_size);
0287     if (copy_to_user(buf, cmd->buffer, len)) {
0288         command_put(cmd);
0289         return -EFAULT;
0290     }
0291     command_put(cmd);
0292 
0293     return len;
0294 }
0295 
0296 static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
0297 {
0298     struct ibmasmfs_command_data *command_data = file->private_data;
0299     struct command *cmd;
0300     unsigned long flags;
0301 
0302     if (*offset < 0)
0303         return -EINVAL;
0304     if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
0305         return 0;
0306     if (*offset != 0)
0307         return 0;
0308 
0309     /* commands are executed sequentially, only one command at a time */
0310     if (command_data->command)
0311         return -EAGAIN;
0312 
0313     cmd = ibmasm_new_command(command_data->sp, count);
0314     if (!cmd)
0315         return -ENOMEM;
0316 
0317     if (copy_from_user(cmd->buffer, ubuff, count)) {
0318         command_put(cmd);
0319         return -EFAULT;
0320     }
0321 
0322     spin_lock_irqsave(&command_data->sp->lock, flags);
0323     if (command_data->command) {
0324         spin_unlock_irqrestore(&command_data->sp->lock, flags);
0325         command_put(cmd);
0326         return -EAGAIN;
0327     }
0328     command_data->command = cmd;
0329     spin_unlock_irqrestore(&command_data->sp->lock, flags);
0330 
0331     ibmasm_exec_command(command_data->sp, cmd);
0332     ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer));
0333 
0334     return count;
0335 }
0336 
0337 static int event_file_open(struct inode *inode, struct file *file)
0338 {
0339     struct ibmasmfs_event_data *event_data;
0340     struct service_processor *sp;
0341 
0342     if (!inode->i_private)
0343         return -ENODEV;
0344 
0345     sp = inode->i_private;
0346 
0347     event_data = kmalloc(sizeof(struct ibmasmfs_event_data), GFP_KERNEL);
0348     if (!event_data)
0349         return -ENOMEM;
0350 
0351     ibmasm_event_reader_register(sp, &event_data->reader);
0352 
0353     event_data->sp = sp;
0354     event_data->active = 0;
0355     file->private_data = event_data;
0356     return 0;
0357 }
0358 
0359 static int event_file_close(struct inode *inode, struct file *file)
0360 {
0361     struct ibmasmfs_event_data *event_data = file->private_data;
0362 
0363     ibmasm_event_reader_unregister(event_data->sp, &event_data->reader);
0364     kfree(event_data);
0365     return 0;
0366 }
0367 
0368 static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
0369 {
0370     struct ibmasmfs_event_data *event_data = file->private_data;
0371     struct event_reader *reader = &event_data->reader;
0372     struct service_processor *sp = event_data->sp;
0373     int ret;
0374     unsigned long flags;
0375 
0376     if (*offset < 0)
0377         return -EINVAL;
0378     if (count == 0 || count > IBMASM_EVENT_MAX_SIZE)
0379         return 0;
0380     if (*offset != 0)
0381         return 0;
0382 
0383     spin_lock_irqsave(&sp->lock, flags);
0384     if (event_data->active) {
0385         spin_unlock_irqrestore(&sp->lock, flags);
0386         return -EBUSY;
0387     }
0388     event_data->active = 1;
0389     spin_unlock_irqrestore(&sp->lock, flags);
0390 
0391     ret = ibmasm_get_next_event(sp, reader);
0392     if (ret <= 0)
0393         goto out;
0394 
0395     if (count < reader->data_size) {
0396         ret = -EINVAL;
0397         goto out;
0398     }
0399 
0400         if (copy_to_user(buf, reader->data, reader->data_size)) {
0401         ret = -EFAULT;
0402         goto out;
0403     }
0404     ret = reader->data_size;
0405 
0406 out:
0407     event_data->active = 0;
0408     return ret;
0409 }
0410 
0411 static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
0412 {
0413     struct ibmasmfs_event_data *event_data = file->private_data;
0414 
0415     if (*offset < 0)
0416         return -EINVAL;
0417     if (count != 1)
0418         return 0;
0419     if (*offset != 0)
0420         return 0;
0421 
0422     ibmasm_cancel_next_event(&event_data->reader);
0423     return 0;
0424 }
0425 
0426 static int r_heartbeat_file_open(struct inode *inode, struct file *file)
0427 {
0428     struct ibmasmfs_heartbeat_data *rhbeat;
0429 
0430     if (!inode->i_private)
0431         return -ENODEV;
0432 
0433     rhbeat = kmalloc(sizeof(struct ibmasmfs_heartbeat_data), GFP_KERNEL);
0434     if (!rhbeat)
0435         return -ENOMEM;
0436 
0437     rhbeat->sp = inode->i_private;
0438     rhbeat->active = 0;
0439     ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
0440     file->private_data = rhbeat;
0441     return 0;
0442 }
0443 
0444 static int r_heartbeat_file_close(struct inode *inode, struct file *file)
0445 {
0446     struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
0447 
0448     kfree(rhbeat);
0449     return 0;
0450 }
0451 
0452 static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
0453 {
0454     struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
0455     unsigned long flags;
0456     int result;
0457 
0458     if (*offset < 0)
0459         return -EINVAL;
0460     if (count == 0 || count > 1024)
0461         return 0;
0462     if (*offset != 0)
0463         return 0;
0464 
0465     /* allow only one reverse heartbeat per process */
0466     spin_lock_irqsave(&rhbeat->sp->lock, flags);
0467     if (rhbeat->active) {
0468         spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
0469         return -EBUSY;
0470     }
0471     rhbeat->active = 1;
0472     spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
0473 
0474     result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
0475     rhbeat->active = 0;
0476 
0477     return result;
0478 }
0479 
0480 static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
0481 {
0482     struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
0483 
0484     if (*offset < 0)
0485         return -EINVAL;
0486     if (count != 1)
0487         return 0;
0488     if (*offset != 0)
0489         return 0;
0490 
0491     if (rhbeat->active)
0492         ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat);
0493 
0494     return 1;
0495 }
0496 
0497 static int remote_settings_file_close(struct inode *inode, struct file *file)
0498 {
0499     return 0;
0500 }
0501 
0502 static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
0503 {
0504     void __iomem *address = (void __iomem *)file->private_data;
0505     int len = 0;
0506     unsigned int value;
0507     char lbuf[20];
0508 
0509     value = readl(address);
0510     len = snprintf(lbuf, sizeof(lbuf), "%d\n", value);
0511 
0512     return simple_read_from_buffer(buf, count, offset, lbuf, len);
0513 }
0514 
0515 static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
0516 {
0517     void __iomem *address = (void __iomem *)file->private_data;
0518     char *buff;
0519     unsigned int value;
0520 
0521     if (*offset < 0)
0522         return -EINVAL;
0523     if (count == 0 || count > 1024)
0524         return 0;
0525     if (*offset != 0)
0526         return 0;
0527 
0528     buff = kzalloc (count + 1, GFP_KERNEL);
0529     if (!buff)
0530         return -ENOMEM;
0531 
0532 
0533     if (copy_from_user(buff, ubuff, count)) {
0534         kfree(buff);
0535         return -EFAULT;
0536     }
0537 
0538     value = simple_strtoul(buff, NULL, 10);
0539     writel(value, address);
0540     kfree(buff);
0541 
0542     return count;
0543 }
0544 
0545 static const struct file_operations command_fops = {
0546     .open =     command_file_open,
0547     .release =  command_file_close,
0548     .read =     command_file_read,
0549     .write =    command_file_write,
0550     .llseek =   generic_file_llseek,
0551 };
0552 
0553 static const struct file_operations event_fops = {
0554     .open =     event_file_open,
0555     .release =  event_file_close,
0556     .read =     event_file_read,
0557     .write =    event_file_write,
0558     .llseek =   generic_file_llseek,
0559 };
0560 
0561 static const struct file_operations r_heartbeat_fops = {
0562     .open =     r_heartbeat_file_open,
0563     .release =  r_heartbeat_file_close,
0564     .read =     r_heartbeat_file_read,
0565     .write =    r_heartbeat_file_write,
0566     .llseek =   generic_file_llseek,
0567 };
0568 
0569 static const struct file_operations remote_settings_fops = {
0570     .open =     simple_open,
0571     .release =  remote_settings_file_close,
0572     .read =     remote_settings_file_read,
0573     .write =    remote_settings_file_write,
0574     .llseek =   generic_file_llseek,
0575 };
0576 
0577 
0578 static void ibmasmfs_create_files (struct super_block *sb)
0579 {
0580     struct list_head *entry;
0581     struct service_processor *sp;
0582 
0583     list_for_each(entry, &service_processors) {
0584         struct dentry *dir;
0585         struct dentry *remote_dir;
0586         sp = list_entry(entry, struct service_processor, node);
0587         dir = ibmasmfs_create_dir(sb->s_root, sp->dirname);
0588         if (!dir)
0589             continue;
0590 
0591         ibmasmfs_create_file(dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR);
0592         ibmasmfs_create_file(dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR);
0593         ibmasmfs_create_file(dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR);
0594 
0595         remote_dir = ibmasmfs_create_dir(dir, "remote_video");
0596         if (!remote_dir)
0597             continue;
0598 
0599         ibmasmfs_create_file(remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR);
0600         ibmasmfs_create_file(remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR);
0601         ibmasmfs_create_file(remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR);
0602     }
0603 }