19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29d5438f4SMark Salyzyn /*
39d5438f4SMark Salyzyn * Copyright 2014 Google, Inc.
49d5438f4SMark Salyzyn */
59d5438f4SMark Salyzyn
69d5438f4SMark Salyzyn #include <linux/cdev.h>
79d5438f4SMark Salyzyn #include <linux/device.h>
89d5438f4SMark Salyzyn #include <linux/fs.h>
99d5438f4SMark Salyzyn #include <linux/uaccess.h>
109d5438f4SMark Salyzyn #include "internal.h"
119d5438f4SMark Salyzyn
125239a89bSJohn Stultz static DEFINE_MUTEX(pmsg_lock);
139d5438f4SMark Salyzyn
write_pmsg(struct file * file,const char __user * buf,size_t count,loff_t * ppos)149d5438f4SMark Salyzyn static ssize_t write_pmsg(struct file *file, const char __user *buf,
159d5438f4SMark Salyzyn size_t count, loff_t *ppos)
169d5438f4SMark Salyzyn {
17e581ca81SKees Cook struct pstore_record record;
185bf6d1b9SMark Salyzyn int ret;
199d5438f4SMark Salyzyn
209d5438f4SMark Salyzyn if (!count)
219d5438f4SMark Salyzyn return 0;
229d5438f4SMark Salyzyn
23e581ca81SKees Cook pstore_record_init(&record, psinfo);
24e581ca81SKees Cook record.type = PSTORE_TYPE_PMSG;
25e581ca81SKees Cook record.size = count;
26e581ca81SKees Cook
274c9ec219SKees Cook /* check outside lock, page in any data. write_user also checks */
2896d4f267SLinus Torvalds if (!access_ok(buf, count))
299d5438f4SMark Salyzyn return -EFAULT;
309d5438f4SMark Salyzyn
315239a89bSJohn Stultz mutex_lock(&pmsg_lock);
324c9ec219SKees Cook ret = psinfo->write_user(&record, buf);
335239a89bSJohn Stultz mutex_unlock(&pmsg_lock);
345bf6d1b9SMark Salyzyn return ret ? ret : count;
359d5438f4SMark Salyzyn }
369d5438f4SMark Salyzyn
379d5438f4SMark Salyzyn static const struct file_operations pmsg_fops = {
389d5438f4SMark Salyzyn .owner = THIS_MODULE,
399d5438f4SMark Salyzyn .llseek = noop_llseek,
409d5438f4SMark Salyzyn .write = write_pmsg,
419d5438f4SMark Salyzyn };
429d5438f4SMark Salyzyn
439d5438f4SMark Salyzyn static struct class *pmsg_class;
449d5438f4SMark Salyzyn static int pmsg_major;
459d5438f4SMark Salyzyn #define PMSG_NAME "pmsg"
469d5438f4SMark Salyzyn #undef pr_fmt
479d5438f4SMark Salyzyn #define pr_fmt(fmt) PMSG_NAME ": " fmt
489d5438f4SMark Salyzyn
pmsg_devnode(const struct device * dev,umode_t * mode)49ff62b8e6SGreg Kroah-Hartman static char *pmsg_devnode(const struct device *dev, umode_t *mode)
509d5438f4SMark Salyzyn {
519d5438f4SMark Salyzyn if (mode)
529d5438f4SMark Salyzyn *mode = 0220;
539d5438f4SMark Salyzyn return NULL;
549d5438f4SMark Salyzyn }
559d5438f4SMark Salyzyn
pstore_register_pmsg(void)569d5438f4SMark Salyzyn void pstore_register_pmsg(void)
579d5438f4SMark Salyzyn {
589d5438f4SMark Salyzyn struct device *pmsg_device;
599d5438f4SMark Salyzyn
609d5438f4SMark Salyzyn pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops);
619d5438f4SMark Salyzyn if (pmsg_major < 0) {
629d5438f4SMark Salyzyn pr_err("register_chrdev failed\n");
639d5438f4SMark Salyzyn goto err;
649d5438f4SMark Salyzyn }
659d5438f4SMark Salyzyn
66*1aaba11dSGreg Kroah-Hartman pmsg_class = class_create(PMSG_NAME);
679d5438f4SMark Salyzyn if (IS_ERR(pmsg_class)) {
689d5438f4SMark Salyzyn pr_err("device class file already in use\n");
699d5438f4SMark Salyzyn goto err_class;
709d5438f4SMark Salyzyn }
719d5438f4SMark Salyzyn pmsg_class->devnode = pmsg_devnode;
729d5438f4SMark Salyzyn
739d5438f4SMark Salyzyn pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0),
749d5438f4SMark Salyzyn NULL, "%s%d", PMSG_NAME, 0);
759d5438f4SMark Salyzyn if (IS_ERR(pmsg_device)) {
769d5438f4SMark Salyzyn pr_err("failed to create device\n");
779d5438f4SMark Salyzyn goto err_device;
789d5438f4SMark Salyzyn }
799d5438f4SMark Salyzyn return;
809d5438f4SMark Salyzyn
819d5438f4SMark Salyzyn err_device:
829d5438f4SMark Salyzyn class_destroy(pmsg_class);
839d5438f4SMark Salyzyn err_class:
849d5438f4SMark Salyzyn unregister_chrdev(pmsg_major, PMSG_NAME);
859d5438f4SMark Salyzyn err:
869d5438f4SMark Salyzyn return;
879d5438f4SMark Salyzyn }
88ee1d2674SGeliang Tang
pstore_unregister_pmsg(void)89ee1d2674SGeliang Tang void pstore_unregister_pmsg(void)
90ee1d2674SGeliang Tang {
91ee1d2674SGeliang Tang device_destroy(pmsg_class, MKDEV(pmsg_major, 0));
92ee1d2674SGeliang Tang class_destroy(pmsg_class);
93ee1d2674SGeliang Tang unregister_chrdev(pmsg_major, PMSG_NAME);
94ee1d2674SGeliang Tang }
95