162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2014 Google, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/cdev.h> 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci#include <linux/uaccess.h> 1062306a36Sopenharmony_ci#include "internal.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic DEFINE_MUTEX(pmsg_lock); 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic ssize_t write_pmsg(struct file *file, const char __user *buf, 1562306a36Sopenharmony_ci size_t count, loff_t *ppos) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct pstore_record record; 1862306a36Sopenharmony_ci int ret; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci if (!count) 2162306a36Sopenharmony_ci return 0; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci pstore_record_init(&record, psinfo); 2462306a36Sopenharmony_ci record.type = PSTORE_TYPE_PMSG; 2562306a36Sopenharmony_ci record.size = count; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci /* check outside lock, page in any data. write_user also checks */ 2862306a36Sopenharmony_ci if (!access_ok(buf, count)) 2962306a36Sopenharmony_ci return -EFAULT; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci mutex_lock(&pmsg_lock); 3262306a36Sopenharmony_ci ret = psinfo->write_user(&record, buf); 3362306a36Sopenharmony_ci mutex_unlock(&pmsg_lock); 3462306a36Sopenharmony_ci return ret ? ret : count; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic const struct file_operations pmsg_fops = { 3862306a36Sopenharmony_ci .owner = THIS_MODULE, 3962306a36Sopenharmony_ci .llseek = noop_llseek, 4062306a36Sopenharmony_ci .write = write_pmsg, 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct class *pmsg_class; 4462306a36Sopenharmony_cistatic int pmsg_major; 4562306a36Sopenharmony_ci#define PMSG_NAME "pmsg" 4662306a36Sopenharmony_ci#undef pr_fmt 4762306a36Sopenharmony_ci#define pr_fmt(fmt) PMSG_NAME ": " fmt 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic char *pmsg_devnode(const struct device *dev, umode_t *mode) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci if (mode) 5262306a36Sopenharmony_ci *mode = 0220; 5362306a36Sopenharmony_ci return NULL; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_civoid pstore_register_pmsg(void) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct device *pmsg_device; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops); 6162306a36Sopenharmony_ci if (pmsg_major < 0) { 6262306a36Sopenharmony_ci pr_err("register_chrdev failed\n"); 6362306a36Sopenharmony_ci goto err; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci pmsg_class = class_create(PMSG_NAME); 6762306a36Sopenharmony_ci if (IS_ERR(pmsg_class)) { 6862306a36Sopenharmony_ci pr_err("device class file already in use\n"); 6962306a36Sopenharmony_ci goto err_class; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci pmsg_class->devnode = pmsg_devnode; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0), 7462306a36Sopenharmony_ci NULL, "%s%d", PMSG_NAME, 0); 7562306a36Sopenharmony_ci if (IS_ERR(pmsg_device)) { 7662306a36Sopenharmony_ci pr_err("failed to create device\n"); 7762306a36Sopenharmony_ci goto err_device; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci return; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cierr_device: 8262306a36Sopenharmony_ci class_destroy(pmsg_class); 8362306a36Sopenharmony_cierr_class: 8462306a36Sopenharmony_ci unregister_chrdev(pmsg_major, PMSG_NAME); 8562306a36Sopenharmony_cierr: 8662306a36Sopenharmony_ci return; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_civoid pstore_unregister_pmsg(void) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci device_destroy(pmsg_class, MKDEV(pmsg_major, 0)); 9262306a36Sopenharmony_ci class_destroy(pmsg_class); 9362306a36Sopenharmony_ci unregister_chrdev(pmsg_major, PMSG_NAME); 9462306a36Sopenharmony_ci} 95