aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/nvram.c
diff options
context:
space:
mode:
authorFinn Thain <fthain@telegraphics.com.au>2019-01-15 15:18:56 +1100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-01-22 10:21:44 +0100
commit109b3a89a7c48405d61a05d7a1720581a4f1574c (patch)
tree00bfc8a815366b81e44fe00580cfcb593a6bd429 /drivers/char/nvram.c
parentchar/nvram: Allow the set_checksum and initialize ioctls to be omitted (diff)
downloadlinux-dev-109b3a89a7c48405d61a05d7a1720581a4f1574c.tar.xz
linux-dev-109b3a89a7c48405d61a05d7a1720581a4f1574c.zip
char/nvram: Implement NVRAM read/write methods
Refactor the RTC "CMOS" NVRAM functions so that they can be used as arch_nvram_ops methods. Checksumming logic is moved from the misc device operations to the nvram read/write operations. This makes the misc device implementation more generic. This preserves the locking mechanism such that "read if checksum valid" and "write and update checksum" remain atomic operations. Some platforms implement byte-range read/write methods which are similar to file_operations struct methods. Other platforms provide only byte-at-a-time methods. The former are more efficient but may be unavailable so fall back on the latter methods when necessary. Tested-by: Stan Johnson <userm57@yahoo.com> Signed-off-by: Finn Thain <fthain@telegraphics.com.au> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/char/nvram.c')
-rw-r--r--drivers/char/nvram.c120
1 files changed, 74 insertions, 46 deletions
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index f88ef41d0598..adcc213c331e 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -41,6 +41,7 @@
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/uaccess.h>
@@ -161,7 +162,46 @@ static ssize_t pc_nvram_get_size(void)
return NVRAM_BYTES;
}
+static ssize_t pc_nvram_read(char *buf, size_t count, loff_t *ppos)
+{
+ char *p = buf;
+ loff_t i;
+
+ spin_lock_irq(&rtc_lock);
+ if (!__nvram_check_checksum()) {
+ spin_unlock_irq(&rtc_lock);
+ return -EIO;
+ }
+ for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p)
+ *p = __nvram_read_byte(i);
+ spin_unlock_irq(&rtc_lock);
+
+ *ppos = i;
+ return p - buf;
+}
+
+static ssize_t pc_nvram_write(char *buf, size_t count, loff_t *ppos)
+{
+ char *p = buf;
+ loff_t i;
+
+ spin_lock_irq(&rtc_lock);
+ if (!__nvram_check_checksum()) {
+ spin_unlock_irq(&rtc_lock);
+ return -EIO;
+ }
+ for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p)
+ __nvram_write_byte(*p, i);
+ __nvram_set_checksum();
+ spin_unlock_irq(&rtc_lock);
+
+ *ppos = i;
+ return p - buf;
+}
+
const struct nvram_ops arch_nvram_ops = {
+ .read = pc_nvram_read,
+ .write = pc_nvram_write,
.read_byte = pc_nvram_read_byte,
.write_byte = pc_nvram_write_byte,
.get_size = pc_nvram_get_size,
@@ -184,69 +224,57 @@ static loff_t nvram_misc_llseek(struct file *file, loff_t offset, int origin)
static ssize_t nvram_misc_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- unsigned char contents[NVRAM_BYTES];
- unsigned i = *ppos;
- unsigned char *tmp;
-
- spin_lock_irq(&rtc_lock);
+ char *tmp;
+ ssize_t ret;
- if (!__nvram_check_checksum())
- goto checksum_err;
- for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp)
- *tmp = __nvram_read_byte(i);
+ if (!access_ok(buf, count))
+ return -EFAULT;
+ if (*ppos >= nvram_size)
+ return 0;
- spin_unlock_irq(&rtc_lock);
+ count = min_t(size_t, count, nvram_size - *ppos);
+ count = min_t(size_t, count, PAGE_SIZE);
- if (copy_to_user(buf, contents, tmp - contents))
- return -EFAULT;
+ tmp = kmalloc(count, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
- *ppos = i;
+ ret = nvram_read(tmp, count, ppos);
+ if (ret <= 0)
+ goto out;
- return tmp - contents;
+ if (copy_to_user(buf, tmp, ret)) {
+ *ppos -= ret;
+ ret = -EFAULT;
+ }
-checksum_err:
- spin_unlock_irq(&rtc_lock);
- return -EIO;
+out:
+ kfree(tmp);
+ return ret;
}
static ssize_t nvram_misc_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- unsigned char contents[NVRAM_BYTES];
- unsigned i = *ppos;
- unsigned char *tmp;
-
- if (i >= NVRAM_BYTES)
- return 0; /* Past EOF */
-
- if (count > NVRAM_BYTES - i)
- count = NVRAM_BYTES - i;
- if (count > NVRAM_BYTES)
- return -EFAULT; /* Can't happen, but prove it to gcc */
+ char *tmp;
+ ssize_t ret;
- if (copy_from_user(contents, buf, count))
+ if (!access_ok(buf, count))
return -EFAULT;
+ if (*ppos >= nvram_size)
+ return 0;
- spin_lock_irq(&rtc_lock);
-
- if (!__nvram_check_checksum())
- goto checksum_err;
-
- for (tmp = contents; count--; ++i, ++tmp)
- __nvram_write_byte(*tmp, i);
+ count = min_t(size_t, count, nvram_size - *ppos);
+ count = min_t(size_t, count, PAGE_SIZE);
- __nvram_set_checksum();
-
- spin_unlock_irq(&rtc_lock);
+ tmp = memdup_user(buf, count);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
- *ppos = i;
-
- return tmp - contents;
-
-checksum_err:
- spin_unlock_irq(&rtc_lock);
- return -EIO;
+ ret = nvram_write(tmp, count, ppos);
+ kfree(tmp);
+ return ret;
}
static long nvram_misc_ioctl(struct file *file, unsigned int cmd,