diff --git a/ia32/Makefile b/ia32/Makefile new file mode 100644 index 0000000..5e8e43f --- /dev/null +++ b/ia32/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for InsydeFlash tool for Linux (build your main kernel) +# +MACHINE = $(shell uname -m) + +obj-m+=isfl_drv.o + + +all: isfl_drv + +isfl_drv: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean + diff --git a/ia32/isfl.h b/ia32/isfl.h new file mode 100644 index 0000000..8b44ca0 --- /dev/null +++ b/ia32/isfl.h @@ -0,0 +1,57 @@ +/* + * InsydeFlash Linux Driver + * File: isfl.h + * + * Copyright (C) 2005 - 2015 Insyde Software Corp. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#define DEVICE_NAME "isfl" //InsydeFlash buffer +#define DEVICE_NAME_PATH "/dev/"DEVICE_NAME // Open device node on user-space +#define DEVICE_MAJOR_NUM 100 +#define ISFB_FLASH_SIZE ( 128 * 1024 ) // 128KB +#define MEMORY_BLOCK_SIZE ( 64 * 1024 ) // 64KB +#define MAX_MEMORY_BLOCK_COUNT 512 + +struct _isfl +{ + unsigned long smi_io; // SMI Port + unsigned long mbuf_size; // memory buffer size + unsigned long mbuf_paddr; // memory buffer physical address + unsigned long smi_status; // SW SMI Status Return + unsigned long enable_kbc; + unsigned long disable_kbc; +}; + +struct _mblock +{ + unsigned long mbuf_PhyAddr; // memory buffer index + unsigned long mbuf_size; // memory buffer size + unsigned char pbyBuffer[MEMORY_BLOCK_SIZE]; +}; + +#define IOCTL_ALLOCATE_MEMORY _IOWR(DEVICE_MAJOR_NUM, 0, struct _isfl *) +#define IOCTL_FREE_MEMORY _IOW(DEVICE_MAJOR_NUM, 1, struct _isfl *) +#define IOCTL_WRITE_MEMORY _IOWR(DEVICE_MAJOR_NUM, 2, struct _isfl *) +#define IOCTL_READ_MEMORY _IOWR(DEVICE_MAJOR_NUM, 3, struct _isfl *) +#define IOCTL_GENERATE_SMI _IOW(DEVICE_MAJOR_NUM, 4, struct _isfl *) +#define IOCTL_GET_SMI_STATUS _IOR(DEVICE_MAJOR_NUM, 5, struct _isfl *) +#define IOCTL_DISABLE_KBC _IOR(DEVICE_MAJOR_NUM, 6, struct _isfl *) +#define IOCTL_ENABLE_KBC _IOR(DEVICE_MAJOR_NUM, 7, struct _isfl *) +#define IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY _IOWR(DEVICE_MAJOR_NUM, 8, struct _isfl *) +#define IOCTL_FREE_MULTI_BLOCK_MEMORY _IOW(DEVICE_MAJOR_NUM, 9, struct _isfl *) +#define IOCTL_WRITE_TO_PHYSICAL _IOWR(DEVICE_MAJOR_NUM, 10, struct _mblock *) +#define IOCTL_READ_FROME_PHYSICAL _IOWR(DEVICE_MAJOR_NUM, 11, struct _mblock *) + +#endif diff --git a/ia32/isfl_drv.c b/ia32/isfl_drv.c new file mode 100644 index 0000000..ffaefbd --- /dev/null +++ b/ia32/isfl_drv.c @@ -0,0 +1,558 @@ +/* + * InsydeFlash Linux Driver + * File: isfl_drv.c + * + * Copyright (C) 2005 - 2015 Insyde Software Corp. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _PageAlloc { + unsigned long addr; + unsigned long PhyAddr; + unsigned int order; +}PageAlloc_t; + +#include "isfl.h" + +#define MAX_8042_LOOPS 100000 + +#define ISFL_VERSION "0.0.05" +#define DRIVER_AUTHOR "Howard Ho " +#define DRIVER_DESC "InsydeFlash tool for Linux" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) +static dev_t first; +static struct cdev c_dev; +static struct class *isfl_class; +#endif + +static int Device_Open = 0; +static unsigned char *isfl_mem_buff; +static unsigned char *g_ppbyMemoryBuffer[MAX_MEMORY_BLOCK_COUNT] = {0}; +static PageAlloc_t g_ppbyMemoryBuffer2[MAX_MEMORY_BLOCK_COUNT] = {{0,0}}; +static int isfl_open(struct inode *, struct file *); +static int isfl_release(struct inode *, struct file *); +static ssize_t isfl_read(struct file *, char *, size_t, loff_t *); +static ssize_t isfl_write(struct file *file, const char *buff, size_t length, loff_t *offset); +//Flash will close kbc except show dialog +static void enable_a20_kbc(void); +static void disable_a20_kbc(void); +//static int empty_8042(void); +static inline void io_delay(void); + +//Use this function to get the message from AP +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) + static long isfl_ioctl(struct file *file, unsigned int num, unsigned long arg) +#else + static int isfl_ioctl(struct inode *inode, struct file *file, unsigned int num, unsigned long arg) +#endif +{ + unsigned long isfl_phys_addr; // Assgin to SW SMI + struct _isfl *isfl; + int isfl_size = sizeof( struct _isfl ); + unsigned char buf[isfl_size]; + struct _mblock *pBlock; + int iBlockStructureSize = sizeof( struct _mblock ); + unsigned char *pbyBlockBuffer; + unsigned char *ch; + int i; + unsigned long mbuf_index; + unsigned char *vir_addr = 0; + + switch ( num ) + { + case IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY: + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + get_user( buf[i], ( ch + i ) ); + } + isfl = (struct _isfl *)buf; + + mbuf_index = isfl->mbuf_paddr; // use mbuf_paddr to store mbuf_index to backward compatible + + // Allocate memory buffer + g_ppbyMemoryBuffer2[mbuf_index].order = get_order( isfl->mbuf_size ); + g_ppbyMemoryBuffer2[mbuf_index].addr = __get_free_pages( GFP_DMA32, g_ppbyMemoryBuffer2[mbuf_index].order ); + g_ppbyMemoryBuffer[mbuf_index] = (unsigned char *)g_ppbyMemoryBuffer2[mbuf_index].addr; + if ( g_ppbyMemoryBuffer[mbuf_index] == NULL ) + { + //printk( KERN_INFO "isfl ioctl IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY allocate physical memory failed\n" ); + return -1; + } + memset( g_ppbyMemoryBuffer[mbuf_index], 0, isfl->mbuf_size ); + + g_ppbyMemoryBuffer2[mbuf_index].PhyAddr = virt_to_phys( g_ppbyMemoryBuffer[mbuf_index] ); + isfl->mbuf_paddr = g_ppbyMemoryBuffer2[mbuf_index].PhyAddr; + //printk( KERN_INFO "isfl alloc id=%d, PhyAddr=%x\n", mbuf_index, g_ppbyMemoryBuffer2[mbuf_index].PhyAddr ); + + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + put_user( buf[i], ( ch + i ) ); + } + break; + + case IOCTL_FREE_MULTI_BLOCK_MEMORY: + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + get_user( buf[i], ( ch + i ) ); + } + isfl = (struct _isfl *)buf; + + mbuf_index = -1; + isfl_phys_addr = isfl->mbuf_paddr; + for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++) + if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr) + mbuf_index = i; + if (-1 == mbuf_index) + return -1; + + // Free Memory + if ( g_ppbyMemoryBuffer[mbuf_index] != NULL ) + { + free_pages( g_ppbyMemoryBuffer2[mbuf_index].addr, g_ppbyMemoryBuffer2[mbuf_index].order ); + g_ppbyMemoryBuffer2[mbuf_index].addr = 0; + g_ppbyMemoryBuffer2[mbuf_index].order = 0; + g_ppbyMemoryBuffer[mbuf_index] = NULL; + } + break; + + case IOCTL_WRITE_TO_PHYSICAL: + ch = (unsigned char *)arg; + pbyBlockBuffer = kmalloc( iBlockStructureSize, GFP_DMA ); + if ( pbyBlockBuffer == NULL ) + { + //printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with NULL parameter\n" ); + return -1; + } + for ( i = 0; i < iBlockStructureSize; i++ ) + { + get_user( pbyBlockBuffer[i], ( ch + i ) ); + } + pBlock = (struct _mblock *)pbyBlockBuffer; + isfl_phys_addr = pBlock->mbuf_PhyAddr; + //printk( KERN_INFO "isfl write PhyAddr=%x\n", pBlock->mbuf_PhyAddr); + //printk( KERN_INFO "isfl write len=%x\n", pBlock->mbuf_size); + //printk( KERN_INFO "isfl write PhyAddr=%x\n", isfl_phys_addr ); + + mbuf_index = -1; + for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++) + if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr) + mbuf_index = i; + if (-1 == mbuf_index) + { + //printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with wrong physcial address\n" ); + return -1; + } + + if ( g_ppbyMemoryBuffer[mbuf_index] == NULL ) + { + //printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with physcial address didn't allocate\n" ); + return -1; + } + + for ( i = 0; i < pBlock->mbuf_size && i < ISFB_FLASH_SIZE; i++ ) + { + g_ppbyMemoryBuffer[mbuf_index][i] = pBlock->pbyBuffer[i]; + } + + kfree( pbyBlockBuffer ); + break; + + case IOCTL_READ_FROME_PHYSICAL: + ch = (unsigned char *)arg; + pbyBlockBuffer = kmalloc( iBlockStructureSize, GFP_DMA ); + if ( pbyBlockBuffer == NULL ) + { + return -1; + } + for ( i = 0; i < iBlockStructureSize; i++ ) + { + get_user( pbyBlockBuffer[i], ( ch + i ) ); + } + pBlock = (struct _mblock *)pbyBlockBuffer; + isfl_phys_addr = pBlock->mbuf_PhyAddr; + //printk( KERN_INFO "isfl read PhyAddr=%x\n", pBlock->mbuf_PhyAddr); + //printk( KERN_INFO "isfl read len=%x\n", pBlock->mbuf_size); + //printk( KERN_INFO "isfl read PhyAddr=%x\n", isfl_phys_addr ); + + mbuf_index = -1; + for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++) + if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr) + mbuf_index = i; + if (-1 == mbuf_index) + return -1; + + if ( g_ppbyMemoryBuffer[mbuf_index] == NULL ) + { + return -1; + } + + for ( i = 0; i < pBlock->mbuf_size && i < ISFB_FLASH_SIZE; i++ ) + { + pBlock->pbyBuffer[i] = g_ppbyMemoryBuffer[mbuf_index][i]; + } + + for ( i = 0; i < iBlockStructureSize; i++ ) + { + put_user( pbyBlockBuffer[i], ( ch + i ) ); + } + kfree( pbyBlockBuffer ); + break; + + case IOCTL_ALLOCATE_MEMORY: + //printk( KERN_INFO "IOCTL_ALLOCATE_MEMORY\n" ); + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + get_user( buf[i], ( ch + i ) ); + //printk( KERN_INFO "%02X ", buf[i] ); + } + //printk( KERN_INFO "\n" ); + isfl = (struct _isfl *)buf; + + /* Allocate a memory buffer */ + //printk( KERN_INFO "Allocate Memory size: %ld\n", isfl->mbuf_size ); + + isfl_mem_buff = kmalloc( isfl->mbuf_size, GFP_DMA ); //20090605 - Keep OS allocate under 16M + if ( isfl_mem_buff == NULL ) + { + return -1; + } + memset( isfl_mem_buff, 0, isfl->mbuf_size ); + + isfl_phys_addr = virt_to_phys( isfl_mem_buff ); + //printk( KERN_INFO "isfl: virt: 0x%p phys: 0x%08lx\n", isfl_mem_buff, isfl_phys_addr ); + isfl->mbuf_paddr = isfl_phys_addr; + //printk( KERN_INFO "Memory physical addr: 0x%08lx\n", isfl->mbuf_paddr ); + + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + //printk( KERN_INFO "%02X ", buf[i] ); + put_user( buf[i], ( ch + i ) ); + } + //printk( KERN_INFO "\n" ); + break; + + case IOCTL_FREE_MEMORY: + //printk( KERN_INFO "IOCTL_FREE_MEMORY\n" ); + //kfree( isfl_mem_buff ); + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + get_user( buf[i], ( ch + i ) ); + //printk( KERN_INFO "%02X ", buf[i] ); + } + + isfl = (struct _isfl *)buf; + isfl_phys_addr = isfl->mbuf_paddr; + + vir_addr = (unsigned char*)phys_to_virt (isfl_phys_addr); + if ( vir_addr == NULL ) + { + //printk( KERN_INFO "isfl ioctl IOCTL_READ_FROME_PHYSICAL with failure from phys_to_virt\n" ); + return -1; + } + + kfree (vir_addr); + break; + + case IOCTL_GENERATE_SMI: + //printk( "IOCTL_GENERATE_SMI\n" ); + + //printk( "size of (struct _isfl): %d\n", isfl_size ); + + /* Get SMI initial data */ + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + get_user( buf[i], ( ch + i ) ); + //printk( "%02X ", buf[i] ); + } + //printk( "\n" ); + isfl = (struct _isfl *)buf; + //printk( "SMI IO port: 0x%lX\n", isfl->smi_io ); + //printk( "Bufer Size: %ld\n", isfl->mbuf_size ); + break; + + case IOCTL_GET_SMI_STATUS: + //printk( "IOCTL_GET_SMI_STATUS\n" ); + + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + //printk( "%02X ", buf[i] ); + put_user( buf[i], ( ch + i ) ); + } + //printk( "\n" ); + break; + + case IOCTL_ENABLE_KBC: + enable_a20_kbc(); + break; + + case IOCTL_DISABLE_KBC: + disable_a20_kbc(); + break; + } + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) + static DEFINE_MUTEX( isflash_mutex ); + static long isfl_ioctl_unlock(struct file *fp, unsigned int cmd, unsigned long arg) + { + long ret; + mutex_lock( &isflash_mutex ); + ret = isfl_ioctl( fp, cmd, arg ); + mutex_unlock( &isflash_mutex ); + return ret; + } +#endif + +static struct file_operations fops = { + .read = isfl_read, + .write = isfl_write, + .open = isfl_open, +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) + .unlocked_ioctl = isfl_ioctl_unlock, + .compat_ioctl = isfl_ioctl_unlock, +#else + .ioctl = isfl_ioctl, +#endif + .release = isfl_release +}; + +//Driver initialization +static int __init init_isfl(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) + first = MKDEV(DEVICE_MAJOR_NUM, 0); + if (register_chrdev_region(first, 1, DEVICE_NAME) < 0) + { + return -1; + } + if ((isfl_class = class_create(THIS_MODULE, DEVICE_NAME)) == NULL) + { + unregister_chrdev_region(first, 1); + return -1; + } + if (device_create(isfl_class, NULL, first, NULL, "isfl") == NULL) + { + class_destroy(isfl_class); + unregister_chrdev_region(first, 1); + return -1; + } + cdev_init(&c_dev, &fops); + if (cdev_add(&c_dev, first, 1) == -1) + { + device_destroy(isfl_class, first); + class_destroy(isfl_class); + unregister_chrdev_region(first, 1); + return -1; + } +#else + int ret = register_chrdev(DEVICE_MAJOR_NUM, DEVICE_NAME, &fops); + if ( ret < 0 ) + { + //printk( KERN_ALERT "Registering char device %s failed with %d\n", DEVICE_NAME, ret ); + return ret; + } +#endif + //printk ("<0> IOCTL_ALLOCATE_MEMORY = %lx\n", IOCTL_ALLOCATE_MEMORY ); + //printk ("<0> IOCTL_FREE_MEMORY = %lx\n", IOCTL_FREE_MEMORY ); + //printk ("<0> IOCTL_WRITE_MEMORY = %lx\n", IOCTL_WRITE_MEMORY ); + //printk ("<0> IOCTL_READ_MEMORY = %lx\n", IOCTL_READ_MEMORY ); + //printk ("<0> IOCTL_GENERATE_SMI = %lx\n", IOCTL_GENERATE_SMI ); + //printk ("<0> IOCTL_GET_SMI_STATUS = %lx\n", IOCTL_GET_SMI_STATUS ); + //printk ("<0> IOCTL_DISABLE_KBC = %lx\n", IOCTL_DISABLE_KBC ); + //printk ("<0> IOCTL_ENABLE_KBC = %lx\n", IOCTL_ENABLE_KBC ); + //printk ("<0> IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY = %lx\n", IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY); + //printk ("<0> IOCTL_FREE_MULTI_BLOCK_MEMORY = %lx\n", IOCTL_FREE_MULTI_BLOCK_MEMORY ); + //printk ("<0> IOCTL_COPY_BUFFER_TO_MEMORY = %lx\n", IOCTL_COPY_BUFFER_TO_MEMORY ); + //printk ("<0> = %d\n", MAJOR(first)); + return 0; +} + +static void __exit cleanup_isfl(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) + cdev_del(&c_dev); + device_destroy(isfl_class, first); + class_destroy(isfl_class); + unregister_chrdev_region(first, 1); +#else + unregister_chrdev( DEVICE_MAJOR_NUM, DEVICE_NAME ); +#endif +} + +static int isfl_open(struct inode *inode, struct file *file) +{ + if ( Device_Open ) + { + return -EBUSY; + } + + Device_Open++; + try_module_get( THIS_MODULE ); + + return 0; +} + +static int isfl_release(struct inode *inode, struct file *file) +{ + Device_Open--; + + module_put( THIS_MODULE ); + + return 0; +} + +static ssize_t isfl_read(struct file *filp, char *buff, size_t len, loff_t *offset) +{ + int bytes_read = 0; + + //printk( KERN_INFO "isfl_read\n" ); + + if ( isfl_mem_buff == 0 ) + { + //printk( KERN_ERR "Memory buffer in kernel space that maybe not be allocated!\n" ); + return -EFAULT; + } + + while ( len ) + { + put_user( isfl_mem_buff[bytes_read++], buff++ ); + len--; + } + + //printk( KERN_INFO "isfl_read: read %d bytes, left %d\n", bytes_read, len ); + return bytes_read; +} + +static ssize_t isfl_write(struct file *file, const char *buff, size_t len, loff_t *offset) +{ + int i; + + //printk( KERN_INFO "isfl_write\n" ); + + if ( isfl_mem_buff == 0 ) + { + //printk( KERN_ERR "Memory buffer in kernel space that maybe not be allocated!\n" ); + return -EFAULT; + } + + //printk( KERN_INFO "isfl_write: len: %d\n", len ); + + for ( i = 0; i < len && i < ISFB_FLASH_SIZE; i++ ) + { + get_user( *( isfl_mem_buff + i ), buff + i ); + } +#if 0 + for ( i = 0; i < len; i++ ) + { + if ( ( i + 1 ) % 8 ) + { + //printk( "%02x ", isfl_mem_buff[i] ); + } + else if ( ( i + 1 ) %16 ) + { + //printk( "%02x ", isfl_mem_buff[i] ); + } + else + { + //printk( "%02x\n", isfl_mem_buff[i] ); + } + } + //printk( "\n" ); +#endif + return i; +} + +static int empty_8042(void) +{ + u8 status; + int loops = MAX_8042_LOOPS; + + while ( loops-- ) + { + io_delay(); + + status = inb( 0x64 ); + if ( status & 1 ) + { + /* Read and discard input data */ + io_delay(); + (void)inb( 0x60 ); + } + else if ( !( status & 2 ) ) + { + /* Buffers empty, finished! */ + return 0; + } + } + + return -1; +} + +static void enable_a20_kbc(void) +{ + empty_8042(); + + outb( 0x60, 0x64 ); // Command write + empty_8042(); + + outb( 0x47, 0x60 ); // A20 on + empty_8042(); +} + +static void disable_a20_kbc(void) +{ + empty_8042(); + + outb( 0x60, 0x64 ); // Command write + empty_8042(); + + outb( 0x74, 0x60 ); // A20 on + empty_8042(); +} + +static inline void io_delay(void) +{ + const u16 DELAY_PORT = 0x80; + asm volatile( "outb %%al,%0" : : "dN" (DELAY_PORT) ); +} + +module_init( init_isfl ); +module_exit( cleanup_isfl ); + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_VERSION( ISFL_VERSION ); +MODULE_SUPPORTED_DEVICE( "Insyde EFI BIOS Flash" ); +MODULE_LICENSE( "GPL" ); diff --git a/x64/Makefile b/x64/Makefile new file mode 100644 index 0000000..5e8e43f --- /dev/null +++ b/x64/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for InsydeFlash tool for Linux (build your main kernel) +# +MACHINE = $(shell uname -m) + +obj-m+=isfl_drv.o + + +all: isfl_drv + +isfl_drv: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean + diff --git a/x64/isfl.h b/x64/isfl.h new file mode 100644 index 0000000..8b44ca0 --- /dev/null +++ b/x64/isfl.h @@ -0,0 +1,57 @@ +/* + * InsydeFlash Linux Driver + * File: isfl.h + * + * Copyright (C) 2005 - 2015 Insyde Software Corp. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#define DEVICE_NAME "isfl" //InsydeFlash buffer +#define DEVICE_NAME_PATH "/dev/"DEVICE_NAME // Open device node on user-space +#define DEVICE_MAJOR_NUM 100 +#define ISFB_FLASH_SIZE ( 128 * 1024 ) // 128KB +#define MEMORY_BLOCK_SIZE ( 64 * 1024 ) // 64KB +#define MAX_MEMORY_BLOCK_COUNT 512 + +struct _isfl +{ + unsigned long smi_io; // SMI Port + unsigned long mbuf_size; // memory buffer size + unsigned long mbuf_paddr; // memory buffer physical address + unsigned long smi_status; // SW SMI Status Return + unsigned long enable_kbc; + unsigned long disable_kbc; +}; + +struct _mblock +{ + unsigned long mbuf_PhyAddr; // memory buffer index + unsigned long mbuf_size; // memory buffer size + unsigned char pbyBuffer[MEMORY_BLOCK_SIZE]; +}; + +#define IOCTL_ALLOCATE_MEMORY _IOWR(DEVICE_MAJOR_NUM, 0, struct _isfl *) +#define IOCTL_FREE_MEMORY _IOW(DEVICE_MAJOR_NUM, 1, struct _isfl *) +#define IOCTL_WRITE_MEMORY _IOWR(DEVICE_MAJOR_NUM, 2, struct _isfl *) +#define IOCTL_READ_MEMORY _IOWR(DEVICE_MAJOR_NUM, 3, struct _isfl *) +#define IOCTL_GENERATE_SMI _IOW(DEVICE_MAJOR_NUM, 4, struct _isfl *) +#define IOCTL_GET_SMI_STATUS _IOR(DEVICE_MAJOR_NUM, 5, struct _isfl *) +#define IOCTL_DISABLE_KBC _IOR(DEVICE_MAJOR_NUM, 6, struct _isfl *) +#define IOCTL_ENABLE_KBC _IOR(DEVICE_MAJOR_NUM, 7, struct _isfl *) +#define IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY _IOWR(DEVICE_MAJOR_NUM, 8, struct _isfl *) +#define IOCTL_FREE_MULTI_BLOCK_MEMORY _IOW(DEVICE_MAJOR_NUM, 9, struct _isfl *) +#define IOCTL_WRITE_TO_PHYSICAL _IOWR(DEVICE_MAJOR_NUM, 10, struct _mblock *) +#define IOCTL_READ_FROME_PHYSICAL _IOWR(DEVICE_MAJOR_NUM, 11, struct _mblock *) + +#endif diff --git a/x64/isfl_drv.c b/x64/isfl_drv.c new file mode 100644 index 0000000..ffaefbd --- /dev/null +++ b/x64/isfl_drv.c @@ -0,0 +1,558 @@ +/* + * InsydeFlash Linux Driver + * File: isfl_drv.c + * + * Copyright (C) 2005 - 2015 Insyde Software Corp. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _PageAlloc { + unsigned long addr; + unsigned long PhyAddr; + unsigned int order; +}PageAlloc_t; + +#include "isfl.h" + +#define MAX_8042_LOOPS 100000 + +#define ISFL_VERSION "0.0.05" +#define DRIVER_AUTHOR "Howard Ho " +#define DRIVER_DESC "InsydeFlash tool for Linux" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) +static dev_t first; +static struct cdev c_dev; +static struct class *isfl_class; +#endif + +static int Device_Open = 0; +static unsigned char *isfl_mem_buff; +static unsigned char *g_ppbyMemoryBuffer[MAX_MEMORY_BLOCK_COUNT] = {0}; +static PageAlloc_t g_ppbyMemoryBuffer2[MAX_MEMORY_BLOCK_COUNT] = {{0,0}}; +static int isfl_open(struct inode *, struct file *); +static int isfl_release(struct inode *, struct file *); +static ssize_t isfl_read(struct file *, char *, size_t, loff_t *); +static ssize_t isfl_write(struct file *file, const char *buff, size_t length, loff_t *offset); +//Flash will close kbc except show dialog +static void enable_a20_kbc(void); +static void disable_a20_kbc(void); +//static int empty_8042(void); +static inline void io_delay(void); + +//Use this function to get the message from AP +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) + static long isfl_ioctl(struct file *file, unsigned int num, unsigned long arg) +#else + static int isfl_ioctl(struct inode *inode, struct file *file, unsigned int num, unsigned long arg) +#endif +{ + unsigned long isfl_phys_addr; // Assgin to SW SMI + struct _isfl *isfl; + int isfl_size = sizeof( struct _isfl ); + unsigned char buf[isfl_size]; + struct _mblock *pBlock; + int iBlockStructureSize = sizeof( struct _mblock ); + unsigned char *pbyBlockBuffer; + unsigned char *ch; + int i; + unsigned long mbuf_index; + unsigned char *vir_addr = 0; + + switch ( num ) + { + case IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY: + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + get_user( buf[i], ( ch + i ) ); + } + isfl = (struct _isfl *)buf; + + mbuf_index = isfl->mbuf_paddr; // use mbuf_paddr to store mbuf_index to backward compatible + + // Allocate memory buffer + g_ppbyMemoryBuffer2[mbuf_index].order = get_order( isfl->mbuf_size ); + g_ppbyMemoryBuffer2[mbuf_index].addr = __get_free_pages( GFP_DMA32, g_ppbyMemoryBuffer2[mbuf_index].order ); + g_ppbyMemoryBuffer[mbuf_index] = (unsigned char *)g_ppbyMemoryBuffer2[mbuf_index].addr; + if ( g_ppbyMemoryBuffer[mbuf_index] == NULL ) + { + //printk( KERN_INFO "isfl ioctl IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY allocate physical memory failed\n" ); + return -1; + } + memset( g_ppbyMemoryBuffer[mbuf_index], 0, isfl->mbuf_size ); + + g_ppbyMemoryBuffer2[mbuf_index].PhyAddr = virt_to_phys( g_ppbyMemoryBuffer[mbuf_index] ); + isfl->mbuf_paddr = g_ppbyMemoryBuffer2[mbuf_index].PhyAddr; + //printk( KERN_INFO "isfl alloc id=%d, PhyAddr=%x\n", mbuf_index, g_ppbyMemoryBuffer2[mbuf_index].PhyAddr ); + + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + put_user( buf[i], ( ch + i ) ); + } + break; + + case IOCTL_FREE_MULTI_BLOCK_MEMORY: + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + get_user( buf[i], ( ch + i ) ); + } + isfl = (struct _isfl *)buf; + + mbuf_index = -1; + isfl_phys_addr = isfl->mbuf_paddr; + for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++) + if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr) + mbuf_index = i; + if (-1 == mbuf_index) + return -1; + + // Free Memory + if ( g_ppbyMemoryBuffer[mbuf_index] != NULL ) + { + free_pages( g_ppbyMemoryBuffer2[mbuf_index].addr, g_ppbyMemoryBuffer2[mbuf_index].order ); + g_ppbyMemoryBuffer2[mbuf_index].addr = 0; + g_ppbyMemoryBuffer2[mbuf_index].order = 0; + g_ppbyMemoryBuffer[mbuf_index] = NULL; + } + break; + + case IOCTL_WRITE_TO_PHYSICAL: + ch = (unsigned char *)arg; + pbyBlockBuffer = kmalloc( iBlockStructureSize, GFP_DMA ); + if ( pbyBlockBuffer == NULL ) + { + //printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with NULL parameter\n" ); + return -1; + } + for ( i = 0; i < iBlockStructureSize; i++ ) + { + get_user( pbyBlockBuffer[i], ( ch + i ) ); + } + pBlock = (struct _mblock *)pbyBlockBuffer; + isfl_phys_addr = pBlock->mbuf_PhyAddr; + //printk( KERN_INFO "isfl write PhyAddr=%x\n", pBlock->mbuf_PhyAddr); + //printk( KERN_INFO "isfl write len=%x\n", pBlock->mbuf_size); + //printk( KERN_INFO "isfl write PhyAddr=%x\n", isfl_phys_addr ); + + mbuf_index = -1; + for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++) + if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr) + mbuf_index = i; + if (-1 == mbuf_index) + { + //printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with wrong physcial address\n" ); + return -1; + } + + if ( g_ppbyMemoryBuffer[mbuf_index] == NULL ) + { + //printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with physcial address didn't allocate\n" ); + return -1; + } + + for ( i = 0; i < pBlock->mbuf_size && i < ISFB_FLASH_SIZE; i++ ) + { + g_ppbyMemoryBuffer[mbuf_index][i] = pBlock->pbyBuffer[i]; + } + + kfree( pbyBlockBuffer ); + break; + + case IOCTL_READ_FROME_PHYSICAL: + ch = (unsigned char *)arg; + pbyBlockBuffer = kmalloc( iBlockStructureSize, GFP_DMA ); + if ( pbyBlockBuffer == NULL ) + { + return -1; + } + for ( i = 0; i < iBlockStructureSize; i++ ) + { + get_user( pbyBlockBuffer[i], ( ch + i ) ); + } + pBlock = (struct _mblock *)pbyBlockBuffer; + isfl_phys_addr = pBlock->mbuf_PhyAddr; + //printk( KERN_INFO "isfl read PhyAddr=%x\n", pBlock->mbuf_PhyAddr); + //printk( KERN_INFO "isfl read len=%x\n", pBlock->mbuf_size); + //printk( KERN_INFO "isfl read PhyAddr=%x\n", isfl_phys_addr ); + + mbuf_index = -1; + for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++) + if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr) + mbuf_index = i; + if (-1 == mbuf_index) + return -1; + + if ( g_ppbyMemoryBuffer[mbuf_index] == NULL ) + { + return -1; + } + + for ( i = 0; i < pBlock->mbuf_size && i < ISFB_FLASH_SIZE; i++ ) + { + pBlock->pbyBuffer[i] = g_ppbyMemoryBuffer[mbuf_index][i]; + } + + for ( i = 0; i < iBlockStructureSize; i++ ) + { + put_user( pbyBlockBuffer[i], ( ch + i ) ); + } + kfree( pbyBlockBuffer ); + break; + + case IOCTL_ALLOCATE_MEMORY: + //printk( KERN_INFO "IOCTL_ALLOCATE_MEMORY\n" ); + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + get_user( buf[i], ( ch + i ) ); + //printk( KERN_INFO "%02X ", buf[i] ); + } + //printk( KERN_INFO "\n" ); + isfl = (struct _isfl *)buf; + + /* Allocate a memory buffer */ + //printk( KERN_INFO "Allocate Memory size: %ld\n", isfl->mbuf_size ); + + isfl_mem_buff = kmalloc( isfl->mbuf_size, GFP_DMA ); //20090605 - Keep OS allocate under 16M + if ( isfl_mem_buff == NULL ) + { + return -1; + } + memset( isfl_mem_buff, 0, isfl->mbuf_size ); + + isfl_phys_addr = virt_to_phys( isfl_mem_buff ); + //printk( KERN_INFO "isfl: virt: 0x%p phys: 0x%08lx\n", isfl_mem_buff, isfl_phys_addr ); + isfl->mbuf_paddr = isfl_phys_addr; + //printk( KERN_INFO "Memory physical addr: 0x%08lx\n", isfl->mbuf_paddr ); + + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + //printk( KERN_INFO "%02X ", buf[i] ); + put_user( buf[i], ( ch + i ) ); + } + //printk( KERN_INFO "\n" ); + break; + + case IOCTL_FREE_MEMORY: + //printk( KERN_INFO "IOCTL_FREE_MEMORY\n" ); + //kfree( isfl_mem_buff ); + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + get_user( buf[i], ( ch + i ) ); + //printk( KERN_INFO "%02X ", buf[i] ); + } + + isfl = (struct _isfl *)buf; + isfl_phys_addr = isfl->mbuf_paddr; + + vir_addr = (unsigned char*)phys_to_virt (isfl_phys_addr); + if ( vir_addr == NULL ) + { + //printk( KERN_INFO "isfl ioctl IOCTL_READ_FROME_PHYSICAL with failure from phys_to_virt\n" ); + return -1; + } + + kfree (vir_addr); + break; + + case IOCTL_GENERATE_SMI: + //printk( "IOCTL_GENERATE_SMI\n" ); + + //printk( "size of (struct _isfl): %d\n", isfl_size ); + + /* Get SMI initial data */ + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + get_user( buf[i], ( ch + i ) ); + //printk( "%02X ", buf[i] ); + } + //printk( "\n" ); + isfl = (struct _isfl *)buf; + //printk( "SMI IO port: 0x%lX\n", isfl->smi_io ); + //printk( "Bufer Size: %ld\n", isfl->mbuf_size ); + break; + + case IOCTL_GET_SMI_STATUS: + //printk( "IOCTL_GET_SMI_STATUS\n" ); + + ch = (unsigned char *)arg; + for ( i = 0; i < isfl_size; i++ ) + { + //printk( "%02X ", buf[i] ); + put_user( buf[i], ( ch + i ) ); + } + //printk( "\n" ); + break; + + case IOCTL_ENABLE_KBC: + enable_a20_kbc(); + break; + + case IOCTL_DISABLE_KBC: + disable_a20_kbc(); + break; + } + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) + static DEFINE_MUTEX( isflash_mutex ); + static long isfl_ioctl_unlock(struct file *fp, unsigned int cmd, unsigned long arg) + { + long ret; + mutex_lock( &isflash_mutex ); + ret = isfl_ioctl( fp, cmd, arg ); + mutex_unlock( &isflash_mutex ); + return ret; + } +#endif + +static struct file_operations fops = { + .read = isfl_read, + .write = isfl_write, + .open = isfl_open, +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) + .unlocked_ioctl = isfl_ioctl_unlock, + .compat_ioctl = isfl_ioctl_unlock, +#else + .ioctl = isfl_ioctl, +#endif + .release = isfl_release +}; + +//Driver initialization +static int __init init_isfl(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) + first = MKDEV(DEVICE_MAJOR_NUM, 0); + if (register_chrdev_region(first, 1, DEVICE_NAME) < 0) + { + return -1; + } + if ((isfl_class = class_create(THIS_MODULE, DEVICE_NAME)) == NULL) + { + unregister_chrdev_region(first, 1); + return -1; + } + if (device_create(isfl_class, NULL, first, NULL, "isfl") == NULL) + { + class_destroy(isfl_class); + unregister_chrdev_region(first, 1); + return -1; + } + cdev_init(&c_dev, &fops); + if (cdev_add(&c_dev, first, 1) == -1) + { + device_destroy(isfl_class, first); + class_destroy(isfl_class); + unregister_chrdev_region(first, 1); + return -1; + } +#else + int ret = register_chrdev(DEVICE_MAJOR_NUM, DEVICE_NAME, &fops); + if ( ret < 0 ) + { + //printk( KERN_ALERT "Registering char device %s failed with %d\n", DEVICE_NAME, ret ); + return ret; + } +#endif + //printk ("<0> IOCTL_ALLOCATE_MEMORY = %lx\n", IOCTL_ALLOCATE_MEMORY ); + //printk ("<0> IOCTL_FREE_MEMORY = %lx\n", IOCTL_FREE_MEMORY ); + //printk ("<0> IOCTL_WRITE_MEMORY = %lx\n", IOCTL_WRITE_MEMORY ); + //printk ("<0> IOCTL_READ_MEMORY = %lx\n", IOCTL_READ_MEMORY ); + //printk ("<0> IOCTL_GENERATE_SMI = %lx\n", IOCTL_GENERATE_SMI ); + //printk ("<0> IOCTL_GET_SMI_STATUS = %lx\n", IOCTL_GET_SMI_STATUS ); + //printk ("<0> IOCTL_DISABLE_KBC = %lx\n", IOCTL_DISABLE_KBC ); + //printk ("<0> IOCTL_ENABLE_KBC = %lx\n", IOCTL_ENABLE_KBC ); + //printk ("<0> IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY = %lx\n", IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY); + //printk ("<0> IOCTL_FREE_MULTI_BLOCK_MEMORY = %lx\n", IOCTL_FREE_MULTI_BLOCK_MEMORY ); + //printk ("<0> IOCTL_COPY_BUFFER_TO_MEMORY = %lx\n", IOCTL_COPY_BUFFER_TO_MEMORY ); + //printk ("<0> = %d\n", MAJOR(first)); + return 0; +} + +static void __exit cleanup_isfl(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 ) + cdev_del(&c_dev); + device_destroy(isfl_class, first); + class_destroy(isfl_class); + unregister_chrdev_region(first, 1); +#else + unregister_chrdev( DEVICE_MAJOR_NUM, DEVICE_NAME ); +#endif +} + +static int isfl_open(struct inode *inode, struct file *file) +{ + if ( Device_Open ) + { + return -EBUSY; + } + + Device_Open++; + try_module_get( THIS_MODULE ); + + return 0; +} + +static int isfl_release(struct inode *inode, struct file *file) +{ + Device_Open--; + + module_put( THIS_MODULE ); + + return 0; +} + +static ssize_t isfl_read(struct file *filp, char *buff, size_t len, loff_t *offset) +{ + int bytes_read = 0; + + //printk( KERN_INFO "isfl_read\n" ); + + if ( isfl_mem_buff == 0 ) + { + //printk( KERN_ERR "Memory buffer in kernel space that maybe not be allocated!\n" ); + return -EFAULT; + } + + while ( len ) + { + put_user( isfl_mem_buff[bytes_read++], buff++ ); + len--; + } + + //printk( KERN_INFO "isfl_read: read %d bytes, left %d\n", bytes_read, len ); + return bytes_read; +} + +static ssize_t isfl_write(struct file *file, const char *buff, size_t len, loff_t *offset) +{ + int i; + + //printk( KERN_INFO "isfl_write\n" ); + + if ( isfl_mem_buff == 0 ) + { + //printk( KERN_ERR "Memory buffer in kernel space that maybe not be allocated!\n" ); + return -EFAULT; + } + + //printk( KERN_INFO "isfl_write: len: %d\n", len ); + + for ( i = 0; i < len && i < ISFB_FLASH_SIZE; i++ ) + { + get_user( *( isfl_mem_buff + i ), buff + i ); + } +#if 0 + for ( i = 0; i < len; i++ ) + { + if ( ( i + 1 ) % 8 ) + { + //printk( "%02x ", isfl_mem_buff[i] ); + } + else if ( ( i + 1 ) %16 ) + { + //printk( "%02x ", isfl_mem_buff[i] ); + } + else + { + //printk( "%02x\n", isfl_mem_buff[i] ); + } + } + //printk( "\n" ); +#endif + return i; +} + +static int empty_8042(void) +{ + u8 status; + int loops = MAX_8042_LOOPS; + + while ( loops-- ) + { + io_delay(); + + status = inb( 0x64 ); + if ( status & 1 ) + { + /* Read and discard input data */ + io_delay(); + (void)inb( 0x60 ); + } + else if ( !( status & 2 ) ) + { + /* Buffers empty, finished! */ + return 0; + } + } + + return -1; +} + +static void enable_a20_kbc(void) +{ + empty_8042(); + + outb( 0x60, 0x64 ); // Command write + empty_8042(); + + outb( 0x47, 0x60 ); // A20 on + empty_8042(); +} + +static void disable_a20_kbc(void) +{ + empty_8042(); + + outb( 0x60, 0x64 ); // Command write + empty_8042(); + + outb( 0x74, 0x60 ); // A20 on + empty_8042(); +} + +static inline void io_delay(void) +{ + const u16 DELAY_PORT = 0x80; + asm volatile( "outb %%al,%0" : : "dN" (DELAY_PORT) ); +} + +module_init( init_isfl ); +module_exit( cleanup_isfl ); + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_VERSION( ISFL_VERSION ); +MODULE_SUPPORTED_DEVICE( "Insyde EFI BIOS Flash" ); +MODULE_LICENSE( "GPL" );