Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp4320239imu; Mon, 14 Jan 2019 20:26:03 -0800 (PST) X-Google-Smtp-Source: ALg8bN6b+2pdXFA+uzKEKmkUkjv3QWWDDt0PE8x9QfHEJIJ3t8gXXSd8c/se6IZ7cRNoebfWLT60 X-Received: by 2002:a63:f844:: with SMTP id v4mr2017087pgj.82.1547526363096; Mon, 14 Jan 2019 20:26:03 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1547526363; cv=none; d=google.com; s=arc-20160816; b=mBV14rFuE9QRqB9moGlVVsD2h/yqhuNAP34RgUfPjSI/j7Z/W1WzJ/uO+3ROLXcLgC l+Ri786Ipg/WZKJdjo0RrO2x+pk9wPY43Q7KstRiQIyXo9Z31LY4pYAJrszSdTPIUw8t RJXN4GfmqFWmZwdImmNXkAClByqGN/BzWx3Lv1g3PO9xVKi1Be58I94oeladdp0cWf5P +ZNqFjokTwp+MqhU7b/aFuIffYr7M0kYSr7n8CJLDfd631wsCqrBlbCNttNnpP7Vrumz OjoV73lE+jJgkXI9l2Xp48AnASu7Gm3QuE6bkF6OjEyQnpP5PpF5sblw73mWK1NDmul2 ZYHg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:date:subject:from:references:in-reply-to :message-id:cc:to; bh=Y2EHBebNX8g3YgE4mNjhKIooyXaQb3KcUC/llqZOwkg=; b=jtsu+me/KyhWhrfeTVvzj2I0cIASu6MQRwHhunl2TPdaWwepZAvBaAKHL4t7lgiz/8 Zaie2wg7hWZkKoXmIlF2b6Cp3Qs3XNC/uC+WPc0XHAuQjuKIpR71FcjQwDCCNnvEaQ0z gCkV6bCazCxqjby/x49Nl+d+41GK/1deaoXMmJVHg9hvhogWQFEqHNjrAnu0HEAdX1ge oOc1zm8Pg4xovEjuLXegnqaPLsFHhOcgxhFVTkfQf2l8Czr7cpSX1hj9cXV0kfH2pQW8 deWdUtTHr+w9elrcodTK+MgRVfhf0475Ta8xEoBj9ts5h9ajaQ18bKoI5bpRoWGu4qqK Zuww== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id m39si2213509plg.315.2019.01.14.20.25.48; Mon, 14 Jan 2019 20:26:03 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728255AbfAOEWa (ORCPT + 99 others); Mon, 14 Jan 2019 23:22:30 -0500 Received: from kvm5.telegraphics.com.au ([98.124.60.144]:37480 "EHLO kvm5.telegraphics.com.au" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728160AbfAOEW1 (ORCPT ); Mon, 14 Jan 2019 23:22:27 -0500 Received: by kvm5.telegraphics.com.au (Postfix, from userid 502) id 4DB2F29E6B; Mon, 14 Jan 2019 23:22:24 -0500 (EST) To: Arnd Bergmann , Greg Kroah-Hartman Cc: linux-kernel@vger.kernel.org, linux-m68k@lists.linux-m68k.org, linuxppc-dev@lists.ozlabs.org Message-Id: <24c1282d42d7d996cdc5c1a6c4dfd979af5a3ff7.1547525936.git.fthain@telegraphics.com.au> In-Reply-To: References: From: Finn Thain Subject: [PATCH v9 09/22] char/nvram: Implement NVRAM read/write methods Date: Tue, 15 Jan 2019 15:18:56 +1100 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 Signed-off-by: Finn Thain --- Changed since v8: - Renamed nvram_* functions to avoid name collisions. - Added nvram_read_bytes() and nvram_write_bytes() helpers for use by those platforms which access NVRAM only one-byte-at-a-time. Changed since v7: - Adopted memdup_user(), like arch/powerpc/kernel/nvram_64.c. --- drivers/char/nvram.c | 120 ++++++++++++++++++++++++++---------------- include/linux/nvram.h | 32 ++++++++++- 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 #include #include +#include #include #include #include @@ -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 */ -- 2.19.2