summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/char/nvram.c120
-rw-r--r--include/linux/nvram.h32
2 files changed, 104 insertions, 48 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,
diff --git a/include/linux/nvram.h b/include/linux/nvram.h
index 31c763087746..9df85703735c 100644
--- a/include/linux/nvram.h
+++ b/include/linux/nvram.h
@@ -66,18 +66,46 @@ static inline void nvram_write_byte(unsigned char val, int addr)
#endif
}
+static inline ssize_t nvram_read_bytes(char *buf, size_t count, loff_t *ppos)
+{
+ ssize_t nvram_size = nvram_get_size();
+ loff_t i;
+ char *p = buf;
+
+ if (nvram_size < 0)
+ return nvram_size;
+ for (i = *ppos; count > 0 && i < nvram_size; ++i, ++p, --count)
+ *p = nvram_read_byte(i);
+ *ppos = i;
+ return p - buf;
+}
+
+static inline ssize_t nvram_write_bytes(char *buf, size_t count, loff_t *ppos)
+{
+ ssize_t nvram_size = nvram_get_size();
+ loff_t i;
+ char *p = buf;
+
+ if (nvram_size < 0)
+ return nvram_size;
+ for (i = *ppos; count > 0 && i < nvram_size; ++i, ++p, --count)
+ nvram_write_byte(*p, i);
+ *ppos = i;
+ return p - buf;
+}
+
static inline ssize_t nvram_read(char *buf, size_t count, loff_t *ppos)
{
if (arch_nvram_ops.read)
return arch_nvram_ops.read(buf, count, ppos);
- return -ENODEV;
+ return nvram_read_bytes(buf, count, ppos);
}
static inline ssize_t nvram_write(char *buf, size_t count, loff_t *ppos)
{
if (arch_nvram_ops.write)
return arch_nvram_ops.write(buf, count, ppos);
- return -ENODEV;
+ return nvram_write_bytes(buf, count, ppos);
}
#endif /* _LINUX_NVRAM_H */