18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CMOS/NV-RAM driver for Linux 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1997 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> 68c2ecf20Sopenharmony_ci * idea by and with help from Richard Jelinek <rj@suse.de> 78c2ecf20Sopenharmony_ci * Portions copyright (c) 2001,2002 Sun Microsystems (thockin@sun.com) 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This driver allows you to access the contents of the non-volatile memory in 108c2ecf20Sopenharmony_ci * the mc146818rtc.h real-time clock. This chip is built into all PCs and into 118c2ecf20Sopenharmony_ci * many Atari machines. In the former it's called "CMOS-RAM", in the latter 128c2ecf20Sopenharmony_ci * "NVRAM" (NV stands for non-volatile). 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * The data are supplied as a (seekable) character device, /dev/nvram. The 158c2ecf20Sopenharmony_ci * size of this file is dependent on the controller. The usual size is 114, 168c2ecf20Sopenharmony_ci * the number of freely available bytes in the memory (i.e., not used by the 178c2ecf20Sopenharmony_ci * RTC itself). 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Checksums over the NVRAM contents are managed by this driver. In case of a 208c2ecf20Sopenharmony_ci * bad checksum, reads and writes return -EIO. The checksum can be initialized 218c2ecf20Sopenharmony_ci * to a sane state either by ioctl(NVRAM_INIT) (clear whole NVRAM) or 228c2ecf20Sopenharmony_ci * ioctl(NVRAM_SETCKS) (doesn't change contents, just makes checksum valid 238c2ecf20Sopenharmony_ci * again; use with care!) 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * 1.1 Cesar Barros: SMP locking fixes 268c2ecf20Sopenharmony_ci * added changelog 278c2ecf20Sopenharmony_ci * 1.2 Erik Gilling: Cobalt Networks support 288c2ecf20Sopenharmony_ci * Tim Hockin: general cleanup, Cobalt support 298c2ecf20Sopenharmony_ci * 1.3 Wim Van Sebroeck: convert PRINT_PROC to seq_file 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define NVRAM_VERSION "1.3" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/module.h> 358c2ecf20Sopenharmony_ci#include <linux/nvram.h> 368c2ecf20Sopenharmony_ci#include <linux/types.h> 378c2ecf20Sopenharmony_ci#include <linux/errno.h> 388c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 398c2ecf20Sopenharmony_ci#include <linux/ioport.h> 408c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 418c2ecf20Sopenharmony_ci#include <linux/mc146818rtc.h> 428c2ecf20Sopenharmony_ci#include <linux/init.h> 438c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 448c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 458c2ecf20Sopenharmony_ci#include <linux/slab.h> 468c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 478c2ecf20Sopenharmony_ci#include <linux/io.h> 488c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 498c2ecf20Sopenharmony_ci#include <linux/mutex.h> 508c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC 538c2ecf20Sopenharmony_ci#include <asm/nvram.h> 548c2ecf20Sopenharmony_ci#endif 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(nvram_mutex); 578c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(nvram_state_lock); 588c2ecf20Sopenharmony_cistatic int nvram_open_cnt; /* #times opened */ 598c2ecf20Sopenharmony_cistatic int nvram_open_mode; /* special open modes */ 608c2ecf20Sopenharmony_cistatic ssize_t nvram_size; 618c2ecf20Sopenharmony_ci#define NVRAM_WRITE 1 /* opened for writing (exclusive) */ 628c2ecf20Sopenharmony_ci#define NVRAM_EXCL 2 /* opened with O_EXCL */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * These functions are provided to be called internally or by other parts of 678c2ecf20Sopenharmony_ci * the kernel. It's up to the caller to ensure correct checksum before reading 688c2ecf20Sopenharmony_ci * or after writing (needs to be done only once). 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * It is worth noting that these functions all access bytes of general 718c2ecf20Sopenharmony_ci * purpose memory in the NVRAM - that is to say, they all add the 728c2ecf20Sopenharmony_ci * NVRAM_FIRST_BYTE offset. Pass them offsets into NVRAM as if you did not 738c2ecf20Sopenharmony_ci * know about the RTC cruft. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define NVRAM_BYTES (128 - NVRAM_FIRST_BYTE) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Note that *all* calls to CMOS_READ and CMOS_WRITE must be done with 798c2ecf20Sopenharmony_ci * rtc_lock held. Due to the index-port/data-port design of the RTC, we 808c2ecf20Sopenharmony_ci * don't want two different things trying to get to it at once. (e.g. the 818c2ecf20Sopenharmony_ci * periodic 11 min sync from kernel/time/ntp.c vs. this driver.) 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic unsigned char __nvram_read_byte(int i) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci return CMOS_READ(NVRAM_FIRST_BYTE + i); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic unsigned char pc_nvram_read_byte(int i) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci unsigned long flags; 928c2ecf20Sopenharmony_ci unsigned char c; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci spin_lock_irqsave(&rtc_lock, flags); 958c2ecf20Sopenharmony_ci c = __nvram_read_byte(i); 968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rtc_lock, flags); 978c2ecf20Sopenharmony_ci return c; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* This races nicely with trying to read with checksum checking (nvram_read) */ 1018c2ecf20Sopenharmony_cistatic void __nvram_write_byte(unsigned char c, int i) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci CMOS_WRITE(c, NVRAM_FIRST_BYTE + i); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void pc_nvram_write_byte(unsigned char c, int i) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci unsigned long flags; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci spin_lock_irqsave(&rtc_lock, flags); 1118c2ecf20Sopenharmony_ci __nvram_write_byte(c, i); 1128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rtc_lock, flags); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* On PCs, the checksum is built only over bytes 2..31 */ 1168c2ecf20Sopenharmony_ci#define PC_CKS_RANGE_START 2 1178c2ecf20Sopenharmony_ci#define PC_CKS_RANGE_END 31 1188c2ecf20Sopenharmony_ci#define PC_CKS_LOC 32 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int __nvram_check_checksum(void) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int i; 1238c2ecf20Sopenharmony_ci unsigned short sum = 0; 1248c2ecf20Sopenharmony_ci unsigned short expect; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci for (i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i) 1278c2ecf20Sopenharmony_ci sum += __nvram_read_byte(i); 1288c2ecf20Sopenharmony_ci expect = __nvram_read_byte(PC_CKS_LOC)<<8 | 1298c2ecf20Sopenharmony_ci __nvram_read_byte(PC_CKS_LOC+1); 1308c2ecf20Sopenharmony_ci return (sum & 0xffff) == expect; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void __nvram_set_checksum(void) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int i; 1368c2ecf20Sopenharmony_ci unsigned short sum = 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci for (i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i) 1398c2ecf20Sopenharmony_ci sum += __nvram_read_byte(i); 1408c2ecf20Sopenharmony_ci __nvram_write_byte(sum >> 8, PC_CKS_LOC); 1418c2ecf20Sopenharmony_ci __nvram_write_byte(sum & 0xff, PC_CKS_LOC + 1); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic long pc_nvram_set_checksum(void) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci spin_lock_irq(&rtc_lock); 1478c2ecf20Sopenharmony_ci __nvram_set_checksum(); 1488c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc_lock); 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic long pc_nvram_initialize(void) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci ssize_t i; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci spin_lock_irq(&rtc_lock); 1578c2ecf20Sopenharmony_ci for (i = 0; i < NVRAM_BYTES; ++i) 1588c2ecf20Sopenharmony_ci __nvram_write_byte(0, i); 1598c2ecf20Sopenharmony_ci __nvram_set_checksum(); 1608c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc_lock); 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic ssize_t pc_nvram_get_size(void) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci return NVRAM_BYTES; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic ssize_t pc_nvram_read(char *buf, size_t count, loff_t *ppos) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci char *p = buf; 1728c2ecf20Sopenharmony_ci loff_t i; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci spin_lock_irq(&rtc_lock); 1758c2ecf20Sopenharmony_ci if (!__nvram_check_checksum()) { 1768c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc_lock); 1778c2ecf20Sopenharmony_ci return -EIO; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p) 1808c2ecf20Sopenharmony_ci *p = __nvram_read_byte(i); 1818c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc_lock); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci *ppos = i; 1848c2ecf20Sopenharmony_ci return p - buf; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic ssize_t pc_nvram_write(char *buf, size_t count, loff_t *ppos) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci char *p = buf; 1908c2ecf20Sopenharmony_ci loff_t i; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci spin_lock_irq(&rtc_lock); 1938c2ecf20Sopenharmony_ci if (!__nvram_check_checksum()) { 1948c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc_lock); 1958c2ecf20Sopenharmony_ci return -EIO; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p) 1988c2ecf20Sopenharmony_ci __nvram_write_byte(*p, i); 1998c2ecf20Sopenharmony_ci __nvram_set_checksum(); 2008c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc_lock); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci *ppos = i; 2038c2ecf20Sopenharmony_ci return p - buf; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciconst struct nvram_ops arch_nvram_ops = { 2078c2ecf20Sopenharmony_ci .read = pc_nvram_read, 2088c2ecf20Sopenharmony_ci .write = pc_nvram_write, 2098c2ecf20Sopenharmony_ci .read_byte = pc_nvram_read_byte, 2108c2ecf20Sopenharmony_ci .write_byte = pc_nvram_write_byte, 2118c2ecf20Sopenharmony_ci .get_size = pc_nvram_get_size, 2128c2ecf20Sopenharmony_ci .set_checksum = pc_nvram_set_checksum, 2138c2ecf20Sopenharmony_ci .initialize = pc_nvram_initialize, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(arch_nvram_ops); 2168c2ecf20Sopenharmony_ci#endif /* CONFIG_X86 */ 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci * The are the file operation function for user access to /dev/nvram 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic loff_t nvram_misc_llseek(struct file *file, loff_t offset, int origin) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE, 2258c2ecf20Sopenharmony_ci nvram_size); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic ssize_t nvram_misc_read(struct file *file, char __user *buf, 2298c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci char *tmp; 2328c2ecf20Sopenharmony_ci ssize_t ret; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (*ppos >= nvram_size) 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci count = min_t(size_t, count, nvram_size - *ppos); 2398c2ecf20Sopenharmony_ci count = min_t(size_t, count, PAGE_SIZE); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci tmp = kmalloc(count, GFP_KERNEL); 2428c2ecf20Sopenharmony_ci if (!tmp) 2438c2ecf20Sopenharmony_ci return -ENOMEM; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ret = nvram_read(tmp, count, ppos); 2468c2ecf20Sopenharmony_ci if (ret <= 0) 2478c2ecf20Sopenharmony_ci goto out; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (copy_to_user(buf, tmp, ret)) { 2508c2ecf20Sopenharmony_ci *ppos -= ret; 2518c2ecf20Sopenharmony_ci ret = -EFAULT; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ciout: 2558c2ecf20Sopenharmony_ci kfree(tmp); 2568c2ecf20Sopenharmony_ci return ret; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic ssize_t nvram_misc_write(struct file *file, const char __user *buf, 2608c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci char *tmp; 2638c2ecf20Sopenharmony_ci ssize_t ret; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (*ppos >= nvram_size) 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci count = min_t(size_t, count, nvram_size - *ppos); 2698c2ecf20Sopenharmony_ci count = min_t(size_t, count, PAGE_SIZE); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci tmp = memdup_user(buf, count); 2728c2ecf20Sopenharmony_ci if (IS_ERR(tmp)) 2738c2ecf20Sopenharmony_ci return PTR_ERR(tmp); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci ret = nvram_write(tmp, count, ppos); 2768c2ecf20Sopenharmony_ci kfree(tmp); 2778c2ecf20Sopenharmony_ci return ret; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic long nvram_misc_ioctl(struct file *file, unsigned int cmd, 2818c2ecf20Sopenharmony_ci unsigned long arg) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci long ret = -ENOTTY; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci switch (cmd) { 2868c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC 2878c2ecf20Sopenharmony_ci case OBSOLETE_PMAC_NVRAM_GET_OFFSET: 2888c2ecf20Sopenharmony_ci pr_warn("nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl\n"); 2898c2ecf20Sopenharmony_ci fallthrough; 2908c2ecf20Sopenharmony_ci case IOC_NVRAM_GET_OFFSET: 2918c2ecf20Sopenharmony_ci ret = -EINVAL; 2928c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 2938c2ecf20Sopenharmony_ci if (machine_is(powermac)) { 2948c2ecf20Sopenharmony_ci int part, offset; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (copy_from_user(&part, (void __user *)arg, 2978c2ecf20Sopenharmony_ci sizeof(part)) != 0) 2988c2ecf20Sopenharmony_ci return -EFAULT; 2998c2ecf20Sopenharmony_ci if (part < pmac_nvram_OF || part > pmac_nvram_NR) 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci offset = pmac_get_partition(part); 3028c2ecf20Sopenharmony_ci if (offset < 0) 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, 3058c2ecf20Sopenharmony_ci &offset, sizeof(offset)) != 0) 3068c2ecf20Sopenharmony_ci return -EFAULT; 3078c2ecf20Sopenharmony_ci ret = 0; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci#endif 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32 3128c2ecf20Sopenharmony_ci case IOC_NVRAM_SYNC: 3138c2ecf20Sopenharmony_ci if (ppc_md.nvram_sync != NULL) { 3148c2ecf20Sopenharmony_ci mutex_lock(&nvram_mutex); 3158c2ecf20Sopenharmony_ci ppc_md.nvram_sync(); 3168c2ecf20Sopenharmony_ci mutex_unlock(&nvram_mutex); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci ret = 0; 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci#endif 3218c2ecf20Sopenharmony_ci#elif defined(CONFIG_X86) || defined(CONFIG_M68K) 3228c2ecf20Sopenharmony_ci case NVRAM_INIT: 3238c2ecf20Sopenharmony_ci /* initialize NVRAM contents and checksum */ 3248c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 3258c2ecf20Sopenharmony_ci return -EACCES; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (arch_nvram_ops.initialize != NULL) { 3288c2ecf20Sopenharmony_ci mutex_lock(&nvram_mutex); 3298c2ecf20Sopenharmony_ci ret = arch_nvram_ops.initialize(); 3308c2ecf20Sopenharmony_ci mutex_unlock(&nvram_mutex); 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case NVRAM_SETCKS: 3348c2ecf20Sopenharmony_ci /* just set checksum, contents unchanged (maybe useful after 3358c2ecf20Sopenharmony_ci * checksum garbaged somehow...) */ 3368c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 3378c2ecf20Sopenharmony_ci return -EACCES; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (arch_nvram_ops.set_checksum != NULL) { 3408c2ecf20Sopenharmony_ci mutex_lock(&nvram_mutex); 3418c2ecf20Sopenharmony_ci ret = arch_nvram_ops.set_checksum(); 3428c2ecf20Sopenharmony_ci mutex_unlock(&nvram_mutex); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci#endif /* CONFIG_X86 || CONFIG_M68K */ 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int nvram_misc_open(struct inode *inode, struct file *file) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci spin_lock(&nvram_state_lock); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Prevent multiple readers/writers if desired. */ 3558c2ecf20Sopenharmony_ci if ((nvram_open_cnt && (file->f_flags & O_EXCL)) || 3568c2ecf20Sopenharmony_ci (nvram_open_mode & NVRAM_EXCL)) { 3578c2ecf20Sopenharmony_ci spin_unlock(&nvram_state_lock); 3588c2ecf20Sopenharmony_ci return -EBUSY; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci#if defined(CONFIG_X86) || defined(CONFIG_M68K) 3628c2ecf20Sopenharmony_ci /* Prevent multiple writers if the set_checksum ioctl is implemented. */ 3638c2ecf20Sopenharmony_ci if ((arch_nvram_ops.set_checksum != NULL) && 3648c2ecf20Sopenharmony_ci (file->f_mode & FMODE_WRITE) && (nvram_open_mode & NVRAM_WRITE)) { 3658c2ecf20Sopenharmony_ci spin_unlock(&nvram_state_lock); 3668c2ecf20Sopenharmony_ci return -EBUSY; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci#endif 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (file->f_flags & O_EXCL) 3718c2ecf20Sopenharmony_ci nvram_open_mode |= NVRAM_EXCL; 3728c2ecf20Sopenharmony_ci if (file->f_mode & FMODE_WRITE) 3738c2ecf20Sopenharmony_ci nvram_open_mode |= NVRAM_WRITE; 3748c2ecf20Sopenharmony_ci nvram_open_cnt++; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci spin_unlock(&nvram_state_lock); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int nvram_misc_release(struct inode *inode, struct file *file) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci spin_lock(&nvram_state_lock); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci nvram_open_cnt--; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* if only one instance is open, clear the EXCL bit */ 3888c2ecf20Sopenharmony_ci if (nvram_open_mode & NVRAM_EXCL) 3898c2ecf20Sopenharmony_ci nvram_open_mode &= ~NVRAM_EXCL; 3908c2ecf20Sopenharmony_ci if (file->f_mode & FMODE_WRITE) 3918c2ecf20Sopenharmony_ci nvram_open_mode &= ~NVRAM_WRITE; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci spin_unlock(&nvram_state_lock); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return 0; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci#if defined(CONFIG_X86) && defined(CONFIG_PROC_FS) 3998c2ecf20Sopenharmony_cistatic const char * const floppy_types[] = { 4008c2ecf20Sopenharmony_ci "none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M", 4018c2ecf20Sopenharmony_ci "3.5'' 2.88M", "3.5'' 2.88M" 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic const char * const gfx_types[] = { 4058c2ecf20Sopenharmony_ci "EGA, VGA, ... (with BIOS)", 4068c2ecf20Sopenharmony_ci "CGA (40 cols)", 4078c2ecf20Sopenharmony_ci "CGA (80 cols)", 4088c2ecf20Sopenharmony_ci "monochrome", 4098c2ecf20Sopenharmony_ci}; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic void pc_nvram_proc_read(unsigned char *nvram, struct seq_file *seq, 4128c2ecf20Sopenharmony_ci void *offset) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci int checksum; 4158c2ecf20Sopenharmony_ci int type; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci spin_lock_irq(&rtc_lock); 4188c2ecf20Sopenharmony_ci checksum = __nvram_check_checksum(); 4198c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc_lock); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci seq_printf(seq, "Checksum status: %svalid\n", checksum ? "" : "not "); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci seq_printf(seq, "# floppies : %d\n", 4248c2ecf20Sopenharmony_ci (nvram[6] & 1) ? (nvram[6] >> 6) + 1 : 0); 4258c2ecf20Sopenharmony_ci seq_printf(seq, "Floppy 0 type : "); 4268c2ecf20Sopenharmony_ci type = nvram[2] >> 4; 4278c2ecf20Sopenharmony_ci if (type < ARRAY_SIZE(floppy_types)) 4288c2ecf20Sopenharmony_ci seq_printf(seq, "%s\n", floppy_types[type]); 4298c2ecf20Sopenharmony_ci else 4308c2ecf20Sopenharmony_ci seq_printf(seq, "%d (unknown)\n", type); 4318c2ecf20Sopenharmony_ci seq_printf(seq, "Floppy 1 type : "); 4328c2ecf20Sopenharmony_ci type = nvram[2] & 0x0f; 4338c2ecf20Sopenharmony_ci if (type < ARRAY_SIZE(floppy_types)) 4348c2ecf20Sopenharmony_ci seq_printf(seq, "%s\n", floppy_types[type]); 4358c2ecf20Sopenharmony_ci else 4368c2ecf20Sopenharmony_ci seq_printf(seq, "%d (unknown)\n", type); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci seq_printf(seq, "HD 0 type : "); 4398c2ecf20Sopenharmony_ci type = nvram[4] >> 4; 4408c2ecf20Sopenharmony_ci if (type) 4418c2ecf20Sopenharmony_ci seq_printf(seq, "%02x\n", type == 0x0f ? nvram[11] : type); 4428c2ecf20Sopenharmony_ci else 4438c2ecf20Sopenharmony_ci seq_printf(seq, "none\n"); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci seq_printf(seq, "HD 1 type : "); 4468c2ecf20Sopenharmony_ci type = nvram[4] & 0x0f; 4478c2ecf20Sopenharmony_ci if (type) 4488c2ecf20Sopenharmony_ci seq_printf(seq, "%02x\n", type == 0x0f ? nvram[12] : type); 4498c2ecf20Sopenharmony_ci else 4508c2ecf20Sopenharmony_ci seq_printf(seq, "none\n"); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci seq_printf(seq, "HD type 48 data: %d/%d/%d C/H/S, precomp %d, lz %d\n", 4538c2ecf20Sopenharmony_ci nvram[18] | (nvram[19] << 8), 4548c2ecf20Sopenharmony_ci nvram[20], nvram[25], 4558c2ecf20Sopenharmony_ci nvram[21] | (nvram[22] << 8), nvram[23] | (nvram[24] << 8)); 4568c2ecf20Sopenharmony_ci seq_printf(seq, "HD type 49 data: %d/%d/%d C/H/S, precomp %d, lz %d\n", 4578c2ecf20Sopenharmony_ci nvram[39] | (nvram[40] << 8), 4588c2ecf20Sopenharmony_ci nvram[41], nvram[46], 4598c2ecf20Sopenharmony_ci nvram[42] | (nvram[43] << 8), nvram[44] | (nvram[45] << 8)); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci seq_printf(seq, "DOS base memory: %d kB\n", nvram[7] | (nvram[8] << 8)); 4628c2ecf20Sopenharmony_ci seq_printf(seq, "Extended memory: %d kB (configured), %d kB (tested)\n", 4638c2ecf20Sopenharmony_ci nvram[9] | (nvram[10] << 8), nvram[34] | (nvram[35] << 8)); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci seq_printf(seq, "Gfx adapter : %s\n", 4668c2ecf20Sopenharmony_ci gfx_types[(nvram[6] >> 4) & 3]); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci seq_printf(seq, "FPU : %sinstalled\n", 4698c2ecf20Sopenharmony_ci (nvram[6] & 2) ? "" : "not "); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int nvram_proc_read(struct seq_file *seq, void *offset) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci unsigned char contents[NVRAM_BYTES]; 4778c2ecf20Sopenharmony_ci int i = 0; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci spin_lock_irq(&rtc_lock); 4808c2ecf20Sopenharmony_ci for (i = 0; i < NVRAM_BYTES; ++i) 4818c2ecf20Sopenharmony_ci contents[i] = __nvram_read_byte(i); 4828c2ecf20Sopenharmony_ci spin_unlock_irq(&rtc_lock); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci pc_nvram_proc_read(contents, seq, offset); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci#endif /* CONFIG_X86 && CONFIG_PROC_FS */ 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic const struct file_operations nvram_misc_fops = { 4918c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4928c2ecf20Sopenharmony_ci .llseek = nvram_misc_llseek, 4938c2ecf20Sopenharmony_ci .read = nvram_misc_read, 4948c2ecf20Sopenharmony_ci .write = nvram_misc_write, 4958c2ecf20Sopenharmony_ci .unlocked_ioctl = nvram_misc_ioctl, 4968c2ecf20Sopenharmony_ci .open = nvram_misc_open, 4978c2ecf20Sopenharmony_ci .release = nvram_misc_release, 4988c2ecf20Sopenharmony_ci}; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic struct miscdevice nvram_misc = { 5018c2ecf20Sopenharmony_ci NVRAM_MINOR, 5028c2ecf20Sopenharmony_ci "nvram", 5038c2ecf20Sopenharmony_ci &nvram_misc_fops, 5048c2ecf20Sopenharmony_ci}; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic int __init nvram_module_init(void) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci int ret; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci nvram_size = nvram_get_size(); 5118c2ecf20Sopenharmony_ci if (nvram_size < 0) 5128c2ecf20Sopenharmony_ci return nvram_size; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci ret = misc_register(&nvram_misc); 5158c2ecf20Sopenharmony_ci if (ret) { 5168c2ecf20Sopenharmony_ci pr_err("nvram: can't misc_register on minor=%d\n", NVRAM_MINOR); 5178c2ecf20Sopenharmony_ci return ret; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci#if defined(CONFIG_X86) && defined(CONFIG_PROC_FS) 5218c2ecf20Sopenharmony_ci if (!proc_create_single("driver/nvram", 0, NULL, nvram_proc_read)) { 5228c2ecf20Sopenharmony_ci pr_err("nvram: can't create /proc/driver/nvram\n"); 5238c2ecf20Sopenharmony_ci misc_deregister(&nvram_misc); 5248c2ecf20Sopenharmony_ci return -ENOMEM; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci#endif 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci pr_info("Non-volatile memory driver v" NVRAM_VERSION "\n"); 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic void __exit nvram_module_exit(void) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci#if defined(CONFIG_X86) && defined(CONFIG_PROC_FS) 5358c2ecf20Sopenharmony_ci remove_proc_entry("driver/nvram", NULL); 5368c2ecf20Sopenharmony_ci#endif 5378c2ecf20Sopenharmony_ci misc_deregister(&nvram_misc); 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cimodule_init(nvram_module_init); 5418c2ecf20Sopenharmony_cimodule_exit(nvram_module_exit); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5448c2ecf20Sopenharmony_ciMODULE_ALIAS_MISCDEV(NVRAM_MINOR); 5458c2ecf20Sopenharmony_ciMODULE_ALIAS("devname:nvram"); 546