Files
pcileech/pcileech_flash/linux/pcileech_flash.c
2016-12-03 14:42:26 +01:00

186 lines
6.7 KiB
C

// pcileech_flash.c : Linux kernel module to flash the USB3380 into a PCILeech device.
//
// (c) Ulf Frisk, 2016
// Author: Ulf Frisk, pcileech@frizk.net
//
// Compiling:
// - In order to compile the required flash kernel module please go to the folder with
// this file and type make. GCC and kernel headers are required. If successful read
// the usage section below.
// Usage:
// - Insert PCILeech card in computer via PCIe/mPCIe/ExpressCard/Thunderbolt (not USB)
// - run 'insmod pcileech_flash.ko'. If the module is successfully inserted then the
// flash operation was successful. If flashing the USB3380-EVB device the blue LED
// will be lit upon success. Run 'rmmod pcileech_flash' to clean up the module from
// the kernel. In order to enable the PCILeech functionality the device must be
// removed from (re-inserted into) the computer.
// - If flashing fails; please check 'dmesg' for logs. If you are flashing the PP3380
// please ensure that the J3 jumper is bridged. If it fails for unknown reasons try
// rebooting and try again.
// Warning:
// Flashing hardware may result in bricked hardware. The author of this module takes
// no responsiblity for this code. The code is provided as is. Use at your own risk.
//
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/delay.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ulf Frisk");
MODULE_DESCRIPTION("PCILeech Firmware Automatic Flasher");
// -----------------------------------------------------------------------------
// START SHARED CODE WITH WINDOWS/LINUX FLASH PCILEECH FIRMWARE
// -----------------------------------------------------------------------------
#define OFFSET_USBREG_GPIO 0x50
#define OFFSET_PCIREG_VEN_DEV 0x1000
#define OFFSET_PCIREG_SUBSYS 0x102c
#define OFFSET_PCIREG_EEPROM_CTL 0x1260
#define OFFSET_PCIREG_EEPROM_DATA 0x1264
#define DEVICE_WAIT_TIME 10
#define SET_LED(v) *(unsigned int*)(pbar0 + OFFSET_USBREG_GPIO) = (0x0000000f & v) | 0xf0
static const unsigned char g_firmware_pcileech[] = {
0x5a, 0x00, 0x2a, 0x00, 0x23, 0x10, 0x49, 0x38, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x14, 0xbc, 0x16,
0xc8, 0x10, 0x02, 0x06, 0x04, 0x00, 0xd0, 0x10, 0x84, 0x06, 0x04, 0x00, 0xd8, 0x10, 0x86, 0x06,
0x04, 0x00, 0xe0, 0x10, 0x88, 0x06, 0x04, 0x00, 0x21, 0x10, 0xd1, 0x18, 0x01, 0x90, 0x00, 0x00 };
static int _action_flash_verify(unsigned char *pbar0)
{
unsigned int dwdata, dwaddr = 0;
while(dwaddr < sizeof(g_firmware_pcileech)) {
// write to CTL register to start EEPROM read (and wait for device)
dwdata = *(unsigned int*)(pbar0 + OFFSET_PCIREG_EEPROM_CTL);
dwdata = (0x00ff0000 & dwdata) | 0x00006000 | (dwaddr >> 2);
*(unsigned int*)(pbar0 + OFFSET_PCIREG_EEPROM_CTL) = dwdata;
msleep(DEVICE_WAIT_TIME);
if(*(unsigned int*)(pbar0 + OFFSET_PCIREG_EEPROM_DATA) != *(unsigned int*)(g_firmware_pcileech + dwaddr)) {
return -1;
}
dwaddr += 4;
}
return 0;
}
static void _action_flash_write(unsigned char *pbar0)
{
unsigned int dwdata, dwaddr = 0;
while(dwaddr < sizeof(g_firmware_pcileech)) {
// write enable latch (and wait for device)
*(unsigned char*)(pbar0 + OFFSET_PCIREG_EEPROM_CTL + 1) = 0xc0;
msleep(DEVICE_WAIT_TIME);
*(unsigned char*)(pbar0 + OFFSET_PCIREG_EEPROM_CTL + 1) = 0x00;
msleep(DEVICE_WAIT_TIME);
// write EEPROM data
dwdata = *(unsigned int*)(g_firmware_pcileech + dwaddr);
*(unsigned int*)(pbar0 + OFFSET_PCIREG_EEPROM_DATA) = dwdata;
// write to CTL register to start EEPROM write (and wait for device)
dwdata = *(unsigned int*)(pbar0 + OFFSET_PCIREG_EEPROM_CTL);
dwdata = (0x00ff0000 & dwdata) | 0x03004000 | (dwaddr >> 2);
*(unsigned int*)(pbar0 + OFFSET_PCIREG_EEPROM_CTL) = dwdata;
msleep(DEVICE_WAIT_TIME);
// next DWORD
dwaddr += 4;
}
}
static int _action_flash_writeverify(unsigned char *pbar0)
{
// 1: check if this is a valid device / memory range.
if(*(unsigned int*)(pbar0 + OFFSET_PCIREG_SUBSYS) != 0x338010B5) {
return -1;
}
if(*(unsigned int*)(pbar0 + OFFSET_PCIREG_VEN_DEV) != 0x338010B5 && *(unsigned int*)(pbar0 + OFFSET_PCIREG_VEN_DEV) != 0x16BC14E4) {
return -1;
}
// 2: is firmware already flashed?
if(0 == _action_flash_verify(pbar0)) {
SET_LED(0xf); // success -> blue+red led
return 0;
}
// 3: flash firmware.
_action_flash_write(pbar0);
// 4: verify flashed firmware.
if(0 == _action_flash_verify(pbar0)) {
SET_LED(0x8); // success -> blue led
return 0;
}
SET_LED(0x7); // fail -> red led
return -1;
}
// -----------------------------------------------------------------------------
// END SHARED CODE WITH WINDOWS/LINUX FLASH PCILEECH FIRMWARE
// -----------------------------------------------------------------------------
static int _action_flash_2(struct pci_dev *pdev)
{
int ret;
unsigned char *pbar0;
// enable the device
if((ret = pci_enable_device(pdev))) {
printk(KERN_ERR "PCILEECH FLASH: ERROR: Failed to enable PCIe device.\n");
return ret;
}
// take ownership of pci related regions
if((ret = pci_request_regions(pdev, "expdev"))) {
printk(KERN_ERR "PCILEECH FLASH: ERROR: Cannot request regions.\n");
goto error;
}
// checking if PCI-device reachable by checking that BAR0 is defined and memory mapped
if(!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
printk(KERN_ERR "PCILEECH FLASH: ERROR: BAR0 configuration not found.\n");
ret = -ENODEV;
goto error;
}
// remap BAR0 avoiding the use of CPU cache
pbar0 = ioremap_nocache(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
ret = _action_flash_writeverify(pbar0);
if(ret) {
ret = _action_flash_writeverify(pbar0); // make two write attempts
}
iounmap(pbar0);
if(ret) {
printk(KERN_ERR "PCILEECH FLASH: ERROR: Firmware write/verify not successful.\n");
} else {
printk(KERN_ERR "PCILEECH FLASH: SUCCESSFUL: Please re-insert the device to use as a PCILeech device!\n");
}
error:
pci_release_regions(pdev);
pci_disable_device(pdev);
return ret;
}
static int _action_flash_1(void) {
int ret;
struct pci_dev *pdev;
// retrieve compatible device (max 1 device per system)
if((pdev = pci_get_device(0x14e4, 0x16bc, NULL))) {
printk(KERN_INFO "PCILEECH FLASH: Found USB3380 already flashed as PCILeech.\n");
} else if((pdev = pci_get_device(0x10b5, 0x3380, NULL))) {
printk(KERN_INFO "PCILEECH FLASH: Found USB3380 not flashed as PCILeech.\n");
} else {
printk(KERN_ERR "PCILEECH FLASH: ERROR: Device not found.\n");
return -ENODEV;
}
ret = _action_flash_2(pdev);
pci_dev_put(pdev);
return ret;
}
static int pcileech_flash_init(void) {
printk(KERN_INFO "PCILEECH FLASH: Module init called.\n");
return _action_flash_1();
}
static void pcileech_flash_exit(void) {
printk(KERN_INFO "PCILEECH FLASH: Module exit called.\n");
}
module_init(pcileech_flash_init);
module_exit(pcileech_flash_exit);