ibmasmfs.c 14 KB

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