2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
/*
* CMOS / NV - RAM driver for Linux
*
* Copyright ( C ) 1997 Roman Hodek < Roman . Hodek @ informatik . uni - erlangen . de >
* idea by and with help from Richard Jelinek < rj @ suse . de >
* Portions copyright ( c ) 2001 , 2002 Sun Microsystems ( thockin @ sun . com )
*
* This driver allows you to access the contents of the non - volatile memory in
* the mc146818rtc . h real - time clock . This chip is built into all PCs and into
* many Atari machines . In the former it ' s called " CMOS-RAM " , in the latter
* " NVRAM " ( NV stands for non - volatile ) .
*
* The data are supplied as a ( seekable ) character device , / dev / nvram . The
* size of this file is dependent on the controller . The usual size is 114 ,
* the number of freely available bytes in the memory ( i . e . , not used by the
* RTC itself ) .
*
* Checksums over the NVRAM contents are managed by this driver . In case of a
* bad checksum , reads and writes return - EIO . The checksum can be initialized
* to a sane state either by ioctl ( NVRAM_INIT ) ( clear whole NVRAM ) or
* ioctl ( NVRAM_SETCKS ) ( doesn ' t change contents , just makes checksum valid
* again ; use with care ! )
*
* 1.1 Cesar Barros : SMP locking fixes
* added changelog
* 1.2 Erik Gilling : Cobalt Networks support
* Tim Hockin : general cleanup , Cobalt support
2008-11-11 12:56:00 +03:00
* 1.3 Wim Van Sebroeck : convert PRINT_PROC to seq_file
2005-04-17 02:20:36 +04:00
*/
2008-11-11 12:56:00 +03:00
# define NVRAM_VERSION "1.3"
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/nvram.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/miscdevice.h>
# include <linux/ioport.h>
# include <linux/fcntl.h>
# include <linux/mc146818rtc.h>
# include <linux/init.h>
# include <linux/proc_fs.h>
2008-11-11 12:56:00 +03:00
# include <linux/seq_file.h>
2019-01-15 07:18:56 +03:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# include <linux/spinlock.h>
2008-11-11 12:44:07 +03:00
# include <linux/io.h>
# include <linux/uaccess.h>
2010-06-02 16:28:52 +04:00
# include <linux/mutex.h>
2015-12-06 05:39:06 +03:00
# include <linux/pagemap.h>
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
# ifdef CONFIG_PPC
# include <asm/nvram.h>
# endif
2005-04-17 02:20:36 +04:00
2010-06-02 16:28:52 +04:00
static DEFINE_MUTEX ( nvram_mutex ) ;
2005-04-17 02:20:36 +04:00
static DEFINE_SPINLOCK ( nvram_state_lock ) ;
static int nvram_open_cnt ; /* #times opened */
static int nvram_open_mode ; /* special open modes */
2019-01-15 07:18:56 +03:00
static ssize_t nvram_size ;
2005-04-17 02:20:36 +04:00
# define NVRAM_WRITE 1 /* opened for writing (exclusive) */
# define NVRAM_EXCL 2 /* opened with O_EXCL */
2019-01-15 07:18:56 +03:00
# ifdef CONFIG_X86
2005-04-17 02:20:36 +04:00
/*
* These functions are provided to be called internally or by other parts of
* the kernel . It ' s up to the caller to ensure correct checksum before reading
* or after writing ( needs to be done only once ) .
*
* It is worth noting that these functions all access bytes of general
* purpose memory in the NVRAM - that is to say , they all add the
2008-11-11 12:44:07 +03:00
* NVRAM_FIRST_BYTE offset . Pass them offsets into NVRAM as if you did not
2005-04-17 02:20:36 +04:00
* know about the RTC cruft .
*/
2019-01-15 07:18:56 +03:00
# define NVRAM_BYTES (128 - NVRAM_FIRST_BYTE)
/* Note that *all* calls to CMOS_READ and CMOS_WRITE must be done with
* rtc_lock held . Due to the index - port / data - port design of the RTC , we
* don ' t want two different things trying to get to it at once . ( e . g . the
* periodic 11 min sync from kernel / time / ntp . c vs . this driver . )
*/
2019-01-15 07:18:56 +03:00
static unsigned char __nvram_read_byte ( int i )
2005-04-17 02:20:36 +04:00
{
return CMOS_READ ( NVRAM_FIRST_BYTE + i ) ;
}
2019-01-15 07:18:56 +03:00
static unsigned char pc_nvram_read_byte ( int i )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned char c ;
spin_lock_irqsave ( & rtc_lock , flags ) ;
c = __nvram_read_byte ( i ) ;
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
return c ;
}
/* This races nicely with trying to read with checksum checking (nvram_read) */
2019-01-15 07:18:56 +03:00
static void __nvram_write_byte ( unsigned char c , int i )
2005-04-17 02:20:36 +04:00
{
CMOS_WRITE ( c , NVRAM_FIRST_BYTE + i ) ;
}
2019-01-15 07:18:56 +03:00
static void pc_nvram_write_byte ( unsigned char c , int i )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
spin_lock_irqsave ( & rtc_lock , flags ) ;
__nvram_write_byte ( c , i ) ;
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
}
2019-01-15 07:18:56 +03:00
/* On PCs, the checksum is built only over bytes 2..31 */
# define PC_CKS_RANGE_START 2
# define PC_CKS_RANGE_END 31
# define PC_CKS_LOC 32
2019-01-15 07:18:56 +03:00
static int __nvram_check_checksum ( void )
2005-04-17 02:20:36 +04:00
{
2019-01-15 07:18:56 +03:00
int i ;
unsigned short sum = 0 ;
unsigned short expect ;
for ( i = PC_CKS_RANGE_START ; i < = PC_CKS_RANGE_END ; + + i )
sum + = __nvram_read_byte ( i ) ;
expect = __nvram_read_byte ( PC_CKS_LOC ) < < 8 |
__nvram_read_byte ( PC_CKS_LOC + 1 ) ;
return ( sum & 0xffff ) = = expect ;
2005-04-17 02:20:36 +04:00
}
2008-11-11 12:44:07 +03:00
static void __nvram_set_checksum ( void )
2005-04-17 02:20:36 +04:00
{
2019-01-15 07:18:56 +03:00
int i ;
unsigned short sum = 0 ;
for ( i = PC_CKS_RANGE_START ; i < = PC_CKS_RANGE_END ; + + i )
sum + = __nvram_read_byte ( i ) ;
__nvram_write_byte ( sum > > 8 , PC_CKS_LOC ) ;
__nvram_write_byte ( sum & 0xff , PC_CKS_LOC + 1 ) ;
2005-04-17 02:20:36 +04:00
}
2019-01-15 07:18:56 +03:00
static long pc_nvram_set_checksum ( void )
2005-04-17 02:20:36 +04:00
{
2019-01-15 07:18:56 +03:00
spin_lock_irq ( & rtc_lock ) ;
__nvram_set_checksum ( ) ;
spin_unlock_irq ( & rtc_lock ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
static long pc_nvram_initialize ( void )
{
ssize_t i ;
spin_lock_irq ( & rtc_lock ) ;
for ( i = 0 ; i < NVRAM_BYTES ; + + i )
__nvram_write_byte ( 0 , i ) ;
2005-04-17 02:20:36 +04:00
__nvram_set_checksum ( ) ;
2019-01-15 07:18:56 +03:00
spin_unlock_irq ( & rtc_lock ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2019-01-15 07:18:56 +03:00
static ssize_t pc_nvram_get_size ( void )
{
return NVRAM_BYTES ;
}
2019-01-15 07:18:56 +03:00
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 ;
}
2019-01-15 07:18:56 +03:00
const struct nvram_ops arch_nvram_ops = {
2019-01-15 07:18:56 +03:00
. read = pc_nvram_read ,
. write = pc_nvram_write ,
2019-01-15 07:18:56 +03:00
. read_byte = pc_nvram_read_byte ,
. write_byte = pc_nvram_write_byte ,
. get_size = pc_nvram_get_size ,
2019-01-15 07:18:56 +03:00
. set_checksum = pc_nvram_set_checksum ,
. initialize = pc_nvram_initialize ,
2019-01-15 07:18:56 +03:00
} ;
EXPORT_SYMBOL ( arch_nvram_ops ) ;
# endif /* CONFIG_X86 */
2005-04-17 02:20:36 +04:00
/*
* The are the file operation function for user access to / dev / nvram
*/
2019-01-15 07:18:56 +03:00
static loff_t nvram_misc_llseek ( struct file * file , loff_t offset , int origin )
2005-04-17 02:20:36 +04:00
{
2015-12-06 05:39:06 +03:00
return generic_file_llseek_size ( file , offset , origin , MAX_LFS_FILESIZE ,
2019-01-15 07:18:56 +03:00
nvram_size ) ;
2005-04-17 02:20:36 +04:00
}
2019-01-15 07:18:56 +03:00
static ssize_t nvram_misc_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2019-01-15 07:18:56 +03:00
char * tmp ;
ssize_t ret ;
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
if ( * ppos > = nvram_size )
return 0 ;
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
count = min_t ( size_t , count , nvram_size - * ppos ) ;
count = min_t ( size_t , count , PAGE_SIZE ) ;
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
tmp = kmalloc ( count , GFP_KERNEL ) ;
if ( ! tmp )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
ret = nvram_read ( tmp , count , ppos ) ;
if ( ret < = 0 )
goto out ;
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
if ( copy_to_user ( buf , tmp , ret ) ) {
* ppos - = ret ;
ret = - EFAULT ;
}
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
out :
kfree ( tmp ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2019-01-15 07:18:56 +03:00
static ssize_t nvram_misc_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2019-01-15 07:18:56 +03:00
char * tmp ;
ssize_t ret ;
2009-12-12 02:48:23 +03:00
2019-01-15 07:18:56 +03:00
if ( * ppos > = nvram_size )
return 0 ;
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
count = min_t ( size_t , count , nvram_size - * ppos ) ;
count = min_t ( size_t , count , PAGE_SIZE ) ;
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
tmp = memdup_user ( buf , count ) ;
if ( IS_ERR ( tmp ) )
return PTR_ERR ( tmp ) ;
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
ret = nvram_write ( tmp , count , ppos ) ;
kfree ( tmp ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2019-01-15 07:18:56 +03:00
static long nvram_misc_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2019-01-15 07:18:56 +03:00
long ret = - ENOTTY ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
2019-01-15 07:18:56 +03:00
# ifdef CONFIG_PPC
case OBSOLETE_PMAC_NVRAM_GET_OFFSET :
pr_warn ( " nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl \n " ) ;
2020-08-24 01:36:59 +03:00
fallthrough ;
2019-01-15 07:18:56 +03:00
case IOC_NVRAM_GET_OFFSET :
ret = - EINVAL ;
# ifdef CONFIG_PPC_PMAC
if ( machine_is ( powermac ) ) {
int part , offset ;
if ( copy_from_user ( & part , ( void __user * ) arg ,
sizeof ( part ) ) ! = 0 )
return - EFAULT ;
if ( part < pmac_nvram_OF | | part > pmac_nvram_NR )
return - EINVAL ;
offset = pmac_get_partition ( part ) ;
2019-01-15 07:18:56 +03:00
if ( offset < 0 )
return - EINVAL ;
2019-01-15 07:18:56 +03:00
if ( copy_to_user ( ( void __user * ) arg ,
& offset , sizeof ( offset ) ) ! = 0 )
return - EFAULT ;
ret = 0 ;
}
# endif
break ;
2019-01-15 07:18:56 +03:00
# ifdef CONFIG_PPC32
2019-01-15 07:18:56 +03:00
case IOC_NVRAM_SYNC :
if ( ppc_md . nvram_sync ! = NULL ) {
mutex_lock ( & nvram_mutex ) ;
ppc_md . nvram_sync ( ) ;
mutex_unlock ( & nvram_mutex ) ;
}
ret = 0 ;
break ;
2019-01-15 07:18:56 +03:00
# endif
2019-01-15 07:18:56 +03:00
# elif defined(CONFIG_X86) || defined(CONFIG_M68K)
2005-04-17 02:20:36 +04:00
case NVRAM_INIT :
/* initialize NVRAM contents and checksum */
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2019-01-15 07:18:56 +03:00
if ( arch_nvram_ops . initialize ! = NULL ) {
mutex_lock ( & nvram_mutex ) ;
ret = arch_nvram_ops . initialize ( ) ;
mutex_unlock ( & nvram_mutex ) ;
}
break ;
2005-04-17 02:20:36 +04:00
case NVRAM_SETCKS :
2008-11-11 12:44:07 +03:00
/* just set checksum, contents unchanged (maybe useful after
2005-04-17 02:20:36 +04:00
* checksum garbaged somehow . . . ) */
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2019-01-15 07:18:56 +03:00
if ( arch_nvram_ops . set_checksum ! = NULL ) {
mutex_lock ( & nvram_mutex ) ;
ret = arch_nvram_ops . set_checksum ( ) ;
mutex_unlock ( & nvram_mutex ) ;
}
break ;
2019-01-15 07:18:56 +03:00
# endif /* CONFIG_X86 || CONFIG_M68K */
2005-04-17 02:20:36 +04:00
}
2019-01-15 07:18:56 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2019-01-15 07:18:56 +03:00
static int nvram_misc_open ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
spin_lock ( & nvram_state_lock ) ;
2019-01-15 07:18:56 +03:00
/* Prevent multiple readers/writers if desired. */
2005-04-17 02:20:36 +04:00
if ( ( nvram_open_cnt & & ( file - > f_flags & O_EXCL ) ) | |
2019-01-15 07:18:56 +03:00
( nvram_open_mode & NVRAM_EXCL ) ) {
spin_unlock ( & nvram_state_lock ) ;
return - EBUSY ;
}
2019-01-15 07:18:56 +03:00
# if defined(CONFIG_X86) || defined(CONFIG_M68K)
2019-01-15 07:18:56 +03:00
/* Prevent multiple writers if the set_checksum ioctl is implemented. */
if ( ( arch_nvram_ops . set_checksum ! = NULL ) & &
( file - > f_mode & FMODE_WRITE ) & & ( nvram_open_mode & NVRAM_WRITE ) ) {
2005-04-17 02:20:36 +04:00
spin_unlock ( & nvram_state_lock ) ;
return - EBUSY ;
}
2019-01-15 07:18:56 +03:00
# endif
2005-04-17 02:20:36 +04:00
if ( file - > f_flags & O_EXCL )
nvram_open_mode | = NVRAM_EXCL ;
2008-09-02 23:28:45 +04:00
if ( file - > f_mode & FMODE_WRITE )
2005-04-17 02:20:36 +04:00
nvram_open_mode | = NVRAM_WRITE ;
nvram_open_cnt + + ;
spin_unlock ( & nvram_state_lock ) ;
return 0 ;
}
2019-01-15 07:18:56 +03:00
static int nvram_misc_release ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
spin_lock ( & nvram_state_lock ) ;
nvram_open_cnt - - ;
/* if only one instance is open, clear the EXCL bit */
if ( nvram_open_mode & NVRAM_EXCL )
nvram_open_mode & = ~ NVRAM_EXCL ;
2008-09-02 23:28:45 +04:00
if ( file - > f_mode & FMODE_WRITE )
2005-04-17 02:20:36 +04:00
nvram_open_mode & = ~ NVRAM_WRITE ;
spin_unlock ( & nvram_state_lock ) ;
return 0 ;
}
2019-01-15 07:18:56 +03:00
# if defined(CONFIG_X86) && defined(CONFIG_PROC_FS)
2015-11-05 12:39:00 +03:00
static const char * const floppy_types [ ] = {
2005-04-17 02:20:36 +04:00
" none " , " 5.25'' 360k " , " 5.25'' 1.2M " , " 3.5'' 720k " , " 3.5'' 1.44M " ,
" 3.5'' 2.88M " , " 3.5'' 2.88M "
} ;
2015-11-05 12:39:00 +03:00
static const char * const gfx_types [ ] = {
2005-04-17 02:20:36 +04:00
" EGA, VGA, ... (with BIOS) " ,
" CGA (40 cols) " ,
" CGA (80 cols) " ,
" monochrome " ,
} ;
2019-01-15 07:18:56 +03:00
static void pc_nvram_proc_read ( unsigned char * nvram , struct seq_file * seq ,
void * offset )
2005-04-17 02:20:36 +04:00
{
int checksum ;
int type ;
spin_lock_irq ( & rtc_lock ) ;
checksum = __nvram_check_checksum ( ) ;
spin_unlock_irq ( & rtc_lock ) ;
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " Checksum status: %svalid \n " , checksum ? " " : " not " ) ;
2005-04-17 02:20:36 +04:00
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " # floppies : %d \n " ,
2005-04-17 02:20:36 +04:00
( nvram [ 6 ] & 1 ) ? ( nvram [ 6 ] > > 6 ) + 1 : 0 ) ;
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " Floppy 0 type : " ) ;
2005-04-17 02:20:36 +04:00
type = nvram [ 2 ] > > 4 ;
2006-01-10 07:54:02 +03:00
if ( type < ARRAY_SIZE ( floppy_types ) )
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " %s \n " , floppy_types [ type ] ) ;
2005-04-17 02:20:36 +04:00
else
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " %d (unknown) \n " , type ) ;
seq_printf ( seq , " Floppy 1 type : " ) ;
2005-04-17 02:20:36 +04:00
type = nvram [ 2 ] & 0x0f ;
2006-01-10 07:54:02 +03:00
if ( type < ARRAY_SIZE ( floppy_types ) )
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " %s \n " , floppy_types [ type ] ) ;
2005-04-17 02:20:36 +04:00
else
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " %d (unknown) \n " , type ) ;
2005-04-17 02:20:36 +04:00
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " HD 0 type : " ) ;
2005-04-17 02:20:36 +04:00
type = nvram [ 4 ] > > 4 ;
if ( type )
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " %02x \n " , type = = 0x0f ? nvram [ 11 ] : type ) ;
2005-04-17 02:20:36 +04:00
else
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " none \n " ) ;
2005-04-17 02:20:36 +04:00
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " HD 1 type : " ) ;
2005-04-17 02:20:36 +04:00
type = nvram [ 4 ] & 0x0f ;
if ( type )
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " %02x \n " , type = = 0x0f ? nvram [ 12 ] : type ) ;
2005-04-17 02:20:36 +04:00
else
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " none \n " ) ;
2005-04-17 02:20:36 +04:00
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " HD type 48 data: %d/%d/%d C/H/S, precomp %d, lz %d \n " ,
2005-04-17 02:20:36 +04:00
nvram [ 18 ] | ( nvram [ 19 ] < < 8 ) ,
nvram [ 20 ] , nvram [ 25 ] ,
nvram [ 21 ] | ( nvram [ 22 ] < < 8 ) , nvram [ 23 ] | ( nvram [ 24 ] < < 8 ) ) ;
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " HD type 49 data: %d/%d/%d C/H/S, precomp %d, lz %d \n " ,
2005-04-17 02:20:36 +04:00
nvram [ 39 ] | ( nvram [ 40 ] < < 8 ) ,
nvram [ 41 ] , nvram [ 46 ] ,
nvram [ 42 ] | ( nvram [ 43 ] < < 8 ) , nvram [ 44 ] | ( nvram [ 45 ] < < 8 ) ) ;
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " DOS base memory: %d kB \n " , nvram [ 7 ] | ( nvram [ 8 ] < < 8 ) ) ;
seq_printf ( seq , " Extended memory: %d kB (configured), %d kB (tested) \n " ,
2005-04-17 02:20:36 +04:00
nvram [ 9 ] | ( nvram [ 10 ] < < 8 ) , nvram [ 34 ] | ( nvram [ 35 ] < < 8 ) ) ;
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " Gfx adapter : %s \n " ,
gfx_types [ ( nvram [ 6 ] > > 4 ) & 3 ] ) ;
2005-04-17 02:20:36 +04:00
2008-11-11 12:56:00 +03:00
seq_printf ( seq , " FPU : %sinstalled \n " ,
2005-04-17 02:20:36 +04:00
( nvram [ 6 ] & 2 ) ? " " : " not " ) ;
2008-11-11 12:56:00 +03:00
return ;
2005-04-17 02:20:36 +04:00
}
2019-01-15 07:18:56 +03:00
static int nvram_proc_read ( struct seq_file * seq , void * offset )
{
unsigned char contents [ NVRAM_BYTES ] ;
int i = 0 ;
spin_lock_irq ( & rtc_lock ) ;
for ( i = 0 ; i < NVRAM_BYTES ; + + i )
contents [ i ] = __nvram_read_byte ( i ) ;
spin_unlock_irq ( & rtc_lock ) ;
pc_nvram_proc_read ( contents , seq , offset ) ;
return 0 ;
}
2019-01-15 07:18:56 +03:00
# endif /* CONFIG_X86 && CONFIG_PROC_FS */
2005-04-17 02:20:36 +04:00
2019-01-15 07:18:56 +03:00
static const struct file_operations nvram_misc_fops = {
. owner = THIS_MODULE ,
. llseek = nvram_misc_llseek ,
. read = nvram_misc_read ,
. write = nvram_misc_write ,
. unlocked_ioctl = nvram_misc_ioctl ,
. open = nvram_misc_open ,
. release = nvram_misc_release ,
} ;
static struct miscdevice nvram_misc = {
NVRAM_MINOR ,
" nvram " ,
& nvram_misc_fops ,
} ;
static int __init nvram_module_init ( void )
{
int ret ;
2019-01-15 07:18:56 +03:00
nvram_size = nvram_get_size ( ) ;
if ( nvram_size < 0 )
return nvram_size ;
2019-01-15 07:18:56 +03:00
ret = misc_register ( & nvram_misc ) ;
if ( ret ) {
pr_err ( " nvram: can't misc_register on minor=%d \n " , NVRAM_MINOR ) ;
return ret ;
}
2019-01-15 07:18:56 +03:00
# if defined(CONFIG_X86) && defined(CONFIG_PROC_FS)
2019-01-15 07:18:56 +03:00
if ( ! proc_create_single ( " driver/nvram " , 0 , NULL , nvram_proc_read ) ) {
pr_err ( " nvram: can't create /proc/driver/nvram \n " ) ;
misc_deregister ( & nvram_misc ) ;
return - ENOMEM ;
}
# endif
pr_info ( " Non-volatile memory driver v " NVRAM_VERSION " \n " ) ;
return 0 ;
}
static void __exit nvram_module_exit ( void )
{
2019-01-15 07:18:56 +03:00
# if defined(CONFIG_X86) && defined(CONFIG_PROC_FS)
2019-01-15 07:18:56 +03:00
remove_proc_entry ( " driver/nvram " , NULL ) ;
# endif
misc_deregister ( & nvram_misc ) ;
}
module_init ( nvram_module_init ) ;
module_exit ( nvram_module_exit ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_MISCDEV ( NVRAM_MINOR ) ;
2019-01-15 07:18:56 +03:00
MODULE_ALIAS ( " devname:nvram " ) ;