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