300 lines
7.3 KiB
C
300 lines
7.3 KiB
C
/*
|
|
* OpenBIOS - free your system!
|
|
* ( firmware/flash device driver for Linux )
|
|
*
|
|
* filesystem.c - vfs character device interface
|
|
*
|
|
* This program is part of a free implementation of the IEEE 1275-1994
|
|
* Standard for Boot (Initialization Configuration) Firmware.
|
|
*
|
|
* Copyright (C) 1998-2004 Stefan Reinauer
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/version.h>
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) && defined(MODVERSIONS)
|
|
#include <linux/modversions.h>
|
|
#endif
|
|
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/types.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
#include "bios.h"
|
|
#include "flashchips.h"
|
|
#include "pcisets.h"
|
|
#include "programming.h"
|
|
|
|
#ifdef MODULE
|
|
void inc_mod(void);
|
|
void dec_mod(void);
|
|
#endif
|
|
|
|
/*
|
|
* ******************************************
|
|
*
|
|
* /dev/bios filesystem operations
|
|
*
|
|
* ******************************************
|
|
*/
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
|
|
#define FDEV (MINOR(file->f_dentry->d_inode->i_rdev))
|
|
#else
|
|
#define FDEV (iminor(file->f_dentry->d_inode))
|
|
#endif
|
|
#define CFLASH flashdevices[FDEV]
|
|
// #define BIOS_SIZE ((flashchips[CFLASH.flashnum].size)*1024)
|
|
#define BIOS_SIZE (CFLASH.size)
|
|
|
|
static loff_t bios_llseek(struct file *file, loff_t offset, int origin )
|
|
{
|
|
currflash=FDEV;
|
|
switch(origin) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
offset += file->f_pos;
|
|
break;
|
|
case 2:
|
|
offset += BIOS_SIZE;
|
|
break;
|
|
}
|
|
return((offset >= 0)?(file->f_pos = offset):-EINVAL);
|
|
}
|
|
|
|
static ssize_t bios_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
|
|
{
|
|
signed int size=((BIOS_SIZE-*ppos>count) ? count : BIOS_SIZE-*ppos);
|
|
unsigned char *addr = (unsigned char*)CFLASH.mapped + CFLASH.offset;
|
|
int i;
|
|
|
|
currflash = FDEV;
|
|
|
|
devices[flashdevices[currflash].idx].activate();
|
|
|
|
for (i=0;i<size;i++)
|
|
buffer[i]=flash_readb(addr,*ppos+i);
|
|
|
|
devices[flashdevices[currflash].idx].deactivate();
|
|
|
|
*ppos+=size;
|
|
return size;
|
|
}
|
|
|
|
static ssize_t bios_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
|
|
{
|
|
unsigned long flags;
|
|
unsigned int offset=0, startsec=0, endsec=0;
|
|
unsigned int secnum=0, size=0, writeoffs=0;
|
|
unsigned int i, fn;
|
|
unsigned char *clipboard;
|
|
unsigned char *addr = (unsigned char*)CFLASH.mapped + CFLASH.offset;
|
|
|
|
currflash=FDEV;
|
|
fn=CFLASH.flashnum;
|
|
|
|
/* Some security checks. */
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
|
if (!suser())
|
|
return -EACCES;
|
|
#endif
|
|
|
|
if (!write) {
|
|
printk (KERN_WARNING "Writing is disabled for security reasons. RTFM.\n");
|
|
return -EACCES;
|
|
}
|
|
|
|
if (!flashchips[fn].supported) {
|
|
printk (KERN_ERR "BIOS: Flash device not supported.\n");
|
|
return -EMEDIUMTYPE;
|
|
}
|
|
|
|
if ( count > BIOS_SIZE-*ppos )
|
|
return -EFBIG;
|
|
|
|
/* FIXME: Autoselect(AMD) BC-90
|
|
* -> 00/MID;
|
|
* 01/PID;
|
|
* 02/Protected (1=yes/0=no)
|
|
*/
|
|
|
|
/* Determine size of data to be written */
|
|
|
|
if (!(flashchips[fn].flags & f_needs_erase) ) {
|
|
offset=(unsigned int)*ppos&~(flashchips[fn].pagesize-1);
|
|
size=(((unsigned int)*ppos+count+(flashchips[fn].pagesize-1))&
|
|
~(flashchips[CFLASH.flashnum].pagesize-1))-offset;
|
|
} else {
|
|
while (flashchips[fn].sectors[secnum] <= flashchips[fn].size ) {
|
|
if ((unsigned int)*ppos >= flashchips[fn].sectors[secnum]*1024) {
|
|
offset=flashchips[fn].sectors[secnum]*1024;
|
|
startsec=secnum;
|
|
}
|
|
if ((unsigned int)*ppos+count-1 <= flashchips[fn].sectors[secnum]*1024) {
|
|
size=(flashchips[fn].sectors[secnum]*1024)-offset;
|
|
endsec=secnum-1;
|
|
break;
|
|
}
|
|
secnum++;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printk (KERN_DEBUG "BIOS: Write [0x%06x..0x%06x] [0x%06x..0x%06x]\n",
|
|
(unsigned int)(*ppos),(unsigned int)(*ppos+count-1),offset,offset+size-1);
|
|
#endif
|
|
|
|
/* prepare data for writing */
|
|
|
|
clipboard=vmalloc(size);
|
|
|
|
spin_lock_irqsave(&bios_lock, flags);
|
|
|
|
devices[flashdevices[currflash].idx].activate();
|
|
|
|
for (i=0; i < size; i++)
|
|
clipboard[i] = flash_readb(addr,offset+i);
|
|
|
|
copy_from_user(clipboard+(*ppos-offset), buffer, count);
|
|
|
|
/* start write access */
|
|
|
|
if (flashchips[fn].flags & f_intel_compl) {
|
|
iflash_erase_sectors(addr,fn,startsec,endsec);
|
|
|
|
for (i=0;i<size;i++)
|
|
iflash_program_byte(addr, offset+i, clipboard[i]);
|
|
|
|
flash_command(addr, 0xff);
|
|
|
|
} else {
|
|
|
|
if (flashchips[fn].flags & f_needs_erase) {
|
|
if (size == flashchips[fn].size*1024) { /* whole chip erase */
|
|
printk (KERN_DEBUG "BIOS: Erasing via whole chip method\n");
|
|
flash_erase(addr, fn);
|
|
} else {
|
|
printk (KERN_DEBUG "BIOS: Erasing via sector method\n");
|
|
flash_erase_sectors(addr, fn,startsec,endsec);
|
|
}
|
|
}
|
|
|
|
while (size>0) {
|
|
if ((flashchips[fn].flags & f_manuf_compl) != f_atmel_compl) {
|
|
flash_program(addr);
|
|
} else {
|
|
flash_program_atmel(addr);
|
|
}
|
|
for (i=0;i<flashchips[fn].pagesize;i++) {
|
|
flash_writeb(addr,offset+writeoffs+i,clipboard[writeoffs+i]);
|
|
}
|
|
if ((flashchips[fn].flags & f_manuf_compl) == f_atmel_compl) {
|
|
udelay(750);
|
|
} else {
|
|
if (flashchips[fn].pagesize==1)
|
|
udelay(30);
|
|
else
|
|
udelay(300);
|
|
}
|
|
|
|
if (flash_ready_poll(addr,offset+writeoffs+flashchips[fn].pagesize-1,
|
|
clipboard[writeoffs+flashchips[fn].pagesize-1])) {
|
|
printk (KERN_ERR "BIOS: Error occurred, please repeat write operation.\n");
|
|
}
|
|
flash_command(addr, 0xf0);
|
|
|
|
writeoffs += flashchips[fn].pagesize;
|
|
size -= flashchips[fn].pagesize;
|
|
}
|
|
}
|
|
|
|
devices[flashdevices[currflash].idx].deactivate();
|
|
|
|
spin_unlock_irqrestore(&bios_lock, flags);
|
|
|
|
vfree(clipboard);
|
|
|
|
*ppos+=count;
|
|
return count;
|
|
}
|
|
|
|
static int bios_open(struct inode *inode, struct file *file)
|
|
{
|
|
currflash=FDEV;
|
|
|
|
if (flashcount<=FDEV) {
|
|
printk (KERN_ERR "BIOS: There is no device (%d).\n",FDEV);
|
|
return -ENODEV;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printk(KERN_DEBUG "BIOS: Opening device %d\n",FDEV);
|
|
#endif
|
|
/* Only one shall open for writing */
|
|
|
|
if ((CFLASH.open_cnt && (file->f_flags & O_EXCL)) ||
|
|
(CFLASH.open_mode & O_EXCL) ||
|
|
((file->f_mode & 2) && (CFLASH.open_mode & O_RDWR)))
|
|
return -EBUSY;
|
|
|
|
if (file->f_flags & O_EXCL)
|
|
CFLASH.open_mode |= O_EXCL;
|
|
|
|
if (file->f_mode & 2)
|
|
CFLASH.open_mode |= O_RDWR;
|
|
|
|
CFLASH.open_cnt++;
|
|
|
|
|
|
#ifdef MODULE
|
|
inc_mod();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int bios_release(struct inode *inode, struct file *file)
|
|
{
|
|
currflash=FDEV;
|
|
if (file->f_flags & O_EXCL)
|
|
CFLASH.open_mode &= ~O_EXCL;
|
|
|
|
if (file->f_mode & 2)
|
|
CFLASH.open_mode &= ~O_RDWR;
|
|
|
|
CFLASH.open_cnt--;
|
|
|
|
#ifdef MODULE
|
|
dec_mod();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
struct file_operations bios_fops = {
|
|
.owner = THIS_MODULE,
|
|
.llseek = bios_llseek,
|
|
.read = bios_read,
|
|
.write = bios_write,
|
|
.open = bios_open,
|
|
.release = bios_release,
|
|
};
|
|
|