1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2014 Google, Inc. 4 */ 5 6 #include <linux/cdev.h> 7 #include <linux/device.h> 8 #include <linux/fs.h> 9 #include <linux/uaccess.h> 10 #include "internal.h" 11 12 static DEFINE_MUTEX(pmsg_lock); 13 14 static ssize_t write_pmsg(struct file *file, const char __user *buf, 15 size_t count, loff_t *ppos) 16 { 17 struct pstore_record record; 18 int ret; 19 20 if (!count) 21 return 0; 22 23 pstore_record_init(&record, psinfo); 24 record.type = PSTORE_TYPE_PMSG; 25 record.size = count; 26 27 /* check outside lock, page in any data. write_user also checks */ 28 if (!access_ok(buf, count)) 29 return -EFAULT; 30 31 mutex_lock(&pmsg_lock); 32 ret = psinfo->write_user(&record, buf); 33 mutex_unlock(&pmsg_lock); 34 return ret ? ret : count; 35 } 36 37 static const struct file_operations pmsg_fops = { 38 .owner = THIS_MODULE, 39 .llseek = noop_llseek, 40 .write = write_pmsg, 41 }; 42 43 static struct class *pmsg_class; 44 static int pmsg_major; 45 #define PMSG_NAME "pmsg" 46 #undef pr_fmt 47 #define pr_fmt(fmt) PMSG_NAME ": " fmt 48 49 static char *pmsg_devnode(const struct device *dev, umode_t *mode) 50 { 51 if (mode) 52 *mode = 0220; 53 return NULL; 54 } 55 56 void pstore_register_pmsg(void) 57 { 58 struct device *pmsg_device; 59 60 pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops); 61 if (pmsg_major < 0) { 62 pr_err("register_chrdev failed\n"); 63 goto err; 64 } 65 66 pmsg_class = class_create(PMSG_NAME); 67 if (IS_ERR(pmsg_class)) { 68 pr_err("device class file already in use\n"); 69 goto err_class; 70 } 71 pmsg_class->devnode = pmsg_devnode; 72 73 pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0), 74 NULL, "%s%d", PMSG_NAME, 0); 75 if (IS_ERR(pmsg_device)) { 76 pr_err("failed to create device\n"); 77 goto err_device; 78 } 79 return; 80 81 err_device: 82 class_destroy(pmsg_class); 83 err_class: 84 unregister_chrdev(pmsg_major, PMSG_NAME); 85 err: 86 return; 87 } 88 89 void pstore_unregister_pmsg(void) 90 { 91 device_destroy(pmsg_class, MKDEV(pmsg_major, 0)); 92 class_destroy(pmsg_class); 93 unregister_chrdev(pmsg_major, PMSG_NAME); 94 } 95