xref: /openbmc/linux/fs/pstore/pmsg.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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