18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2014 Google, Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/cdev.h> 78c2ecf20Sopenharmony_ci#include <linux/device.h> 88c2ecf20Sopenharmony_ci#include <linux/fs.h> 98c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 108c2ecf20Sopenharmony_ci#include "internal.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pmsg_lock); 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic ssize_t write_pmsg(struct file *file, const char __user *buf, 158c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci struct pstore_record record; 188c2ecf20Sopenharmony_ci int ret; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci if (!count) 218c2ecf20Sopenharmony_ci return 0; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci pstore_record_init(&record, psinfo); 248c2ecf20Sopenharmony_ci record.type = PSTORE_TYPE_PMSG; 258c2ecf20Sopenharmony_ci record.size = count; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci /* check outside lock, page in any data. write_user also checks */ 288c2ecf20Sopenharmony_ci if (!access_ok(buf, count)) 298c2ecf20Sopenharmony_ci return -EFAULT; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci mutex_lock(&pmsg_lock); 328c2ecf20Sopenharmony_ci ret = psinfo->write_user(&record, buf); 338c2ecf20Sopenharmony_ci mutex_unlock(&pmsg_lock); 348c2ecf20Sopenharmony_ci return ret ? ret : count; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic const struct file_operations pmsg_fops = { 388c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 398c2ecf20Sopenharmony_ci .llseek = noop_llseek, 408c2ecf20Sopenharmony_ci .write = write_pmsg, 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct class *pmsg_class; 448c2ecf20Sopenharmony_cistatic int pmsg_major; 458c2ecf20Sopenharmony_ci#define PMSG_NAME "pmsg" 468c2ecf20Sopenharmony_ci#undef pr_fmt 478c2ecf20Sopenharmony_ci#define pr_fmt(fmt) PMSG_NAME ": " fmt 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic char *pmsg_devnode(struct device *dev, umode_t *mode) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci if (mode) 528c2ecf20Sopenharmony_ci *mode = 0220; 538c2ecf20Sopenharmony_ci return NULL; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_civoid pstore_register_pmsg(void) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct device *pmsg_device; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops); 618c2ecf20Sopenharmony_ci if (pmsg_major < 0) { 628c2ecf20Sopenharmony_ci pr_err("register_chrdev failed\n"); 638c2ecf20Sopenharmony_ci goto err; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci pmsg_class = class_create(THIS_MODULE, PMSG_NAME); 678c2ecf20Sopenharmony_ci if (IS_ERR(pmsg_class)) { 688c2ecf20Sopenharmony_ci pr_err("device class file already in use\n"); 698c2ecf20Sopenharmony_ci goto err_class; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci pmsg_class->devnode = pmsg_devnode; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0), 748c2ecf20Sopenharmony_ci NULL, "%s%d", PMSG_NAME, 0); 758c2ecf20Sopenharmony_ci if (IS_ERR(pmsg_device)) { 768c2ecf20Sopenharmony_ci pr_err("failed to create device\n"); 778c2ecf20Sopenharmony_ci goto err_device; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci return; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cierr_device: 828c2ecf20Sopenharmony_ci class_destroy(pmsg_class); 838c2ecf20Sopenharmony_cierr_class: 848c2ecf20Sopenharmony_ci unregister_chrdev(pmsg_major, PMSG_NAME); 858c2ecf20Sopenharmony_cierr: 868c2ecf20Sopenharmony_ci return; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_civoid pstore_unregister_pmsg(void) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci device_destroy(pmsg_class, MKDEV(pmsg_major, 0)); 928c2ecf20Sopenharmony_ci class_destroy(pmsg_class); 938c2ecf20Sopenharmony_ci unregister_chrdev(pmsg_major, PMSG_NAME); 948c2ecf20Sopenharmony_ci} 95