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}