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