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