linux/drivers/misc/ibmasm/ibmasmfs.c
<<
>>
Prefs
   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}
 604