New tool fm10k-flash (#1)
All checks were successful
continuous-integration/drone/push Build is passing

Moved FM10K SPI functions to its own files
Added register of known devices
New tool: fm10k-flash
Added COPYING file
Moved code under src/
Add functions to enable/disable sector protection on flash
Add CS release and sleep to finish write(?)

Co-authored-by: WeebDataHoarder <57538841+weebdatahoarder@users.noreply.github.com>
Reviewed-on: #1
Co-Authored-By: DataHoarder <weebdatahoarder@noreply.gammaspectra.live>
Co-Committed-By: DataHoarder <weebdatahoarder@noreply.gammaspectra.live>
This commit is contained in:
DataHoarder 2020-12-21 05:11:23 +00:00
parent b7d7809437
commit c0d6478623
Signed by: GammaSpectra.live Git
GPG key ID: 8B02E6093E9CB7B3
8 changed files with 715 additions and 156 deletions

3
.gitignore vendored
View file

@ -1,3 +1,4 @@
/fm10k-dump
/*.o
/fm10k-flash
*.o
/.idea

25
COPYING Normal file
View file

@ -0,0 +1,25 @@
Copyright (c) 2014 - 2015, Intel Corporation
Copyright (c) 2020, FM10K-Documentation Contributors
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,14 +1,25 @@
CC=cc
CFLAGS=-Wall -Wno-unknown-warning-option -Wno-packed-bitfield-compat
CFLAGS=-ggdb -Og -Wall -Wno-unknown-warning-option -Wno-packed-bitfield-compat
default: fm10k-dump
default: all
fm10k-dump.o: fm10k-dump.c
$(CC) -c fm10k-dump.c -o fm10k-dump.o $(CFLAGS)
all: fm10k-dump fm10k-flash
fm10k-dump: fm10k-dump.o
$(CC) fm10k-dump.o -o fm10k-dump $(CFLAGS)
fm10k.o: src/fm10k.c
$(CC) -c src/fm10k.c -o src/fm10k.o $(CFLAGS)
fm10k-dump.o: src/fm10k-dump.c
$(CC) -c src/fm10k-dump.c -o src/fm10k-dump.o $(CFLAGS)
fm10k-dump: fm10k-dump.o fm10k.o
$(CC) src/fm10k-dump.o src/fm10k.o -o fm10k-dump $(CFLAGS)
fm10k-flash.o: src/fm10k-flash.c
$(CC) -c src/fm10k-flash.c -o src/fm10k-flash.o $(CFLAGS)
fm10k-flash: fm10k-flash.o fm10k.o
$(CC) src/fm10k-flash.o src/fm10k.o -o fm10k-flash $(CFLAGS)
clean:
-rm -f fm10k-dump
-rm -f fm10k-dump.o
-rm -f fm10k-flash
-rm -f src/*.o

View file

@ -1,6 +1,6 @@
# fm10k-dump
# fm10k-dump & fm10k-flash
Utility that allows dumping the SPI Flash Non-Volatile Memory of the FM10000 family of cards.
Utility that allows dumping/flashing the SPI Flash Non-Volatile Memory of the FM10000 family of cards.
## Requirements
* GCC >= 4.4.0 or clang
@ -13,13 +13,18 @@ Utility that allows dumping the SPI Flash Non-Volatile Memory of the FM10000 fam
## Compilation
* `$ make clean && make`
* You can specify whether to use gcc or clang this way: `$ make CC=gcc` / `$ make CC=clang`
* An executable named `./fm10k-dump` should now exist.
* Two executables named `./fm10k-dump` and `./fm10k-flash` should now exist.
## Usage example
## fm10k-dump usage example
* If the chip on the card is known to the tool, it'll set the image size to match. Alternatively it will use the platform minimum of 8Mbit.
* `# ./fm10k-dump /dev/uio0 outputImage.bin`
* You can also force the image/chip size: `# ./fm10k-dump /dev/uio0 outputImage.bin 32`
## fm10k-flash usage example
* _fm10k-flash_ requires a backup of the existing image before flashing a new one, for safety measures. Use _fm10k-dump_ to take a backup copy of the current state before flashing.
* `# ./fm10k-dump /dev/uio0 backupImage.bin`
* `# ./fm10k-flash /dev/uio0 inputImage.bin backupImage.bin`
## License
* BSD-3-Clause
* See [fm10k-dump.c](fm10k-dump.c) header for more information.
* See [COPYING](COPYING) for the full license.

131
src/fm10k-dump.c Normal file
View file

@ -0,0 +1,131 @@
/*****************************************************************************
* File: fm10k-dump.c
* Creation Date: December 16, 2020
* Description: Utility to dump Non Volatile Memory on SPI for FM10K devices. Requires FM10K kernel module running with UIO enabled.
* Compile: make fm10k-dump
* Usage: ./fm10k-dump </dev/uio0> <output.bin> [readSizeInMegabits=8]
*
* Copyright (c) 2014 - 2015, Intel Corporation
* Copyright (c) 2020, FM10K-Documentation Contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the copyright holders nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "fm10k.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char** argv){
int fdUIO;
FILE* fdOutput;
if(argc < 3){
printf("Usage: %s </dev/uio0> <output.bin> [forceSizeInMegabits, default 8]\n", argv[0]);
return 1;
}
fdOutput = fopen(argv[2], "wb");
if(fdOutput == NULL){
printf("Could not create %s\n", argv[2]);
return 1;
}
fdUIO = open(argv[1], O_RDWR);
if(fdUIO <= 0){
printf("Unable to open uio device %s to read NVM\n", argv[1]);
return 1;
}
void* memmapAddr;
memmapAddr = mmap(NULL, FM10K_UIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fdUIO, 0);
if (memmapAddr == MAP_FAILED) {
printf("Unable to map uio device %s to read NVM\n", argv[1]);
close(fdUIO);
return 1;
}
SPI_COMMAND_READ_MANUFACTURER_RESULT manufacturerInfo = fm10k_uio_spi_ReadManufacturerData((uintptr_t)memmapAddr);
if(manufacturerInfo.value == 0){
printf("Error getting manufacturer information\n");
return 1;
}
printf("Manufacturer Info 0x%08x :: JEDEC Manufacturer ID 0x%02x family 0x%02x density 0x%02x %02x:%02x\n", manufacturerInfo.value, manufacturerInfo.fields.manufacturerId, manufacturerInfo.fields.familyCode, manufacturerInfo.fields.densityCode, manufacturerInfo.fields.subCode, manufacturerInfo.fields.productVariant);
unsigned int mbitsToRead = 8;
const KNOWN_FLASH_DEVICE* knownFlashDevice = getKnownFlashFromManufacturerData(manufacturerInfo);
if(knownFlashDevice != NULL){
printf("Device: %s\n", knownFlashDevice->name);
mbitsToRead = knownFlashDevice->sizeInMbit;
}else{
printf("Device: Unknown, defaulting %uMbit size\n", mbitsToRead);
}
if(argc > 3){
if(mbitsToRead != 8){
printf("WARNING: %uMbit size was already auto-detected\n", mbitsToRead);
}
mbitsToRead = strtoul(argv[3], NULL, 10);
printf("Forcing %uMbit size\n", mbitsToRead);
}
if(mbitsToRead > 1024){
printf("Too many MBit: %u\n", mbitsToRead);
return 1;
}
if(mbitsToRead < 8){
printf("Too few MBit: %u\n", mbitsToRead);
return 1;
}
uint32_t bootImageSize = (mbitsToRead * 1024 * 1024) / 8;
uint8_t* value = malloc(bootImageSize);
uint32_t strideSize = 512;
for(uint32_t addr = 0; addr < bootImageSize; addr += strideSize){
printf("\rread @ 0x%08x / 0x%08x %d bytes", addr, bootImageSize, strideSize);
if(fm10k_uio_spi_ReadFlash((uintptr_t)memmapAddr, addr, value + addr, strideSize)){
return 2;
}
}
fwrite(value, 1, bootImageSize, fdOutput);
free(value);
fclose(fdOutput);
printf("\nRead %u bytes\n", bootImageSize);
return 0;
}

241
src/fm10k-flash.c Normal file
View file

@ -0,0 +1,241 @@
/*****************************************************************************
* File: fm10k-flash.c
* Creation Date: December 20, 2020
* Description: Utility to flash Non Volatile Memory on SPI for FM10K devices. Requires FM10K kernel module running with UIO enabled.
* Compile: make fm10k-flash
* Usage: ./fm10k-flash </dev/uio0> <input.bin> <backup.bin>
*
* Copyright (c) 2014 - 2015, Intel Corporation
* Copyright (c) 2020, FM10K-Documentation Contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the copyright holders nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "fm10k.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char** argv){
int fdUIO;
FILE* fdBackup;
FILE* fdInput;
if(argc < 4){
printf("Usage: %s </dev/uio0> <input.bin> <backup.bin>\n", argv[0]);
return 1;
}
fdInput = fopen(argv[2], "rb");
if(fdInput == NULL){
printf("Could not open %s\n", argv[2]);
return 1;
}
fdBackup = fopen(argv[3], "rb");
if(fdBackup == NULL){
printf("Could not open existing backup %s\n", argv[3]);
return 1;
}
fseek(fdInput , 0, SEEK_END);
uint32_t fileSize = ftell(fdInput);
rewind(fdInput);
fseek(fdBackup , 0, SEEK_END);
uint32_t backupFileSize = ftell(fdBackup);
rewind(fdBackup);
if(backupFileSize < fileSize){
printf("Backup %d is smaller than new image %d\n", backupFileSize, fileSize);
return 1;
}
fdUIO = open(argv[1], O_RDWR);
if(fdUIO <= 0){
printf("Unable to open uio device %s to write NVM\n", argv[1]);
return 1;
}
void* memmapAddr;
memmapAddr = mmap(NULL, FM10K_UIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fdUIO, 0);
if (memmapAddr == MAP_FAILED) {
printf("Unable to map uio device %s to write NVM\n", argv[1]);
close(fdUIO);
return 1;
}
SPI_COMMAND_READ_MANUFACTURER_RESULT manufacturerInfo = fm10k_uio_spi_ReadManufacturerData((uintptr_t)memmapAddr);
if(manufacturerInfo.value == 0){
printf("Error getting manufacturer information\n");
return 1;
}
printf("Manufacturer Info 0x%08x :: JEDEC Manufacturer ID 0x%02x family 0x%02x density 0x%02x %02x:%02x\n", manufacturerInfo.value, manufacturerInfo.fields.manufacturerId, manufacturerInfo.fields.familyCode, manufacturerInfo.fields.densityCode, manufacturerInfo.fields.subCode, manufacturerInfo.fields.productVariant);
unsigned int mbitsToReadMax = 8;
const KNOWN_FLASH_DEVICE* knownFlashDevice = getKnownFlashFromManufacturerData(manufacturerInfo);
if(knownFlashDevice != NULL){
printf("Device: %s\n", knownFlashDevice->name);
mbitsToReadMax = knownFlashDevice->sizeInMbit;
}else{
printf("Device: Unknown, defaulting max %uMbit size\n", mbitsToReadMax);
}
if(mbitsToReadMax > 1024){
printf("Too many MBit: %u\n", mbitsToReadMax);
return 1;
}
if(mbitsToReadMax == 0){
printf("Too few MBit: %u\n", mbitsToReadMax);
return 1;
}
uint32_t strideSize = 512;
uint32_t bootImageSizeMax = (mbitsToReadMax * 1024 * 1024) / 8;
if(fileSize > bootImageSizeMax){
printf("Image too large: have %d, can write max %d bytes.\n", fileSize, bootImageSizeMax);
return 1;
}else if(fileSize < bootImageSizeMax){
do{
bootImageSizeMax -= strideSize;
} while (fileSize < bootImageSizeMax);
}
uint8_t* value = calloc(bootImageSizeMax, sizeof(uint8_t));
uint8_t* valueBackup = calloc(bootImageSizeMax, sizeof(uint8_t));
uint8_t* valueCheck = calloc(bootImageSizeMax, sizeof(uint8_t));
if(fread(value, 1, bootImageSizeMax, fdInput) != bootImageSizeMax){
printf("Error reading image file.\n");
return 1;
}
if(fread(valueBackup, 1, bootImageSizeMax, fdBackup) != bootImageSizeMax){
printf("Error reading backup image file.\n");
return 1;
}
if(memcmp(value, valueBackup, bootImageSizeMax) == 0){
printf("NOTE: %s and %s have the same bytes.\n", argv[2], argv[3]);
}
for(uint32_t addr = 0; addr < bootImageSizeMax; addr += strideSize){
printf("\rbackup check @ 0x%08x / 0x%08x %d bytes", addr, bootImageSizeMax, strideSize);
if(fm10k_uio_spi_ReadFlash((uintptr_t)memmapAddr, addr, valueCheck + addr, strideSize)){
return 2;
}
}
printf("\n");
if(memcmp(valueBackup, valueCheck, bootImageSizeMax) != 0){
printf("Backup image mismatch! Dump image again.\n");
return 1;
}else{
printf("Backup matches.\n");
}
sleep(1);
printf("Disabling sector protection.\n");
fm10k_uio_spi_DisableSectorProtection((uintptr_t)memmapAddr);
sleep(1);
for(uint32_t addr = 0; addr < bootImageSizeMax; addr += strideSize){
if(memcmp(valueBackup + addr, value + addr, strideSize) != 0){
printf("write @ 0x%08x\n", addr);
if(fm10k_uio_spi_WriteFlash((uintptr_t)memmapAddr, addr, value + addr, strideSize)){
return 2;
}
sleep(1);
}
}
printf("\n");
sleep(1);
for(uint32_t addr = 0; addr < bootImageSizeMax; addr += strideSize){
printf("\rverify check @ 0x%08x / 0x%08x %d bytes", addr, bootImageSizeMax, strideSize);
if(fm10k_uio_spi_ReadFlash((uintptr_t)memmapAddr, addr, valueCheck + addr, strideSize)){
return 2;
}
}
printf("\n");
for(uint32_t addr = 0; addr < bootImageSizeMax; addr += 4){
if(*(uint32_t*)(value + addr) != *(uint32_t*)(valueCheck + addr)){
printf("@0x%08x: 0x%08x != 0x%08x\n", addr, *(uint32_t*)(value + addr), *(uint32_t*)(valueCheck + addr));
}
}
sleep(1);
if(memcmp(value, valueCheck, bootImageSizeMax) != 0){
printf("Data written mismatch! Replacing with backup.\n");
for(uint32_t addr = 0; addr < bootImageSizeMax; addr += strideSize){
if(memcmp(valueBackup + addr, valueCheck + addr, strideSize) != 0){
printf("write backup @ 0x%08x\n", addr);
if(fm10k_uio_spi_WriteFlash((uintptr_t)memmapAddr, addr, valueBackup + addr, strideSize)){
return 2;
}
sleep(1);
}
}
}else{
printf("Data written verified.\n");
}
sleep(1);
free(value);
free(valueBackup);
free(valueCheck);
fclose(fdInput);
fclose(fdBackup);
printf("\nWritten %u bytes\n", bootImageSizeMax);
return 0;
}

View file

@ -1,9 +1,6 @@
/*****************************************************************************
* File: fm10k-dump.c
* Creation Date: December 16, 2020
* Description: Utility to dump Non Volatile Memory on SPI for FM10K devices. Requires FM10K kernel module running with UIO enabled.
* Compile: gcc -o fm10k-dump fm10k-dump.c
* Usage: ./fm10k-dump </dev/uio0> <output.bin> [readSizeInMegabits=8]
* File: fm10k.c
* Creation Date: December 20, 2020
*
* Copyright (c) 2014 - 2015, Intel Corporation
* Copyright (c) 2020, FM10K-Documentation Contributors
@ -32,67 +29,14 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <inttypes.h>
#include "fm10k.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#define FM10K_UIO_SIZE 0x0000000004000000
#define FM10K_UIO_OFFSET 0
//From Datasheet, 11.25.1 MGMT Map, Table 11-37
#define FM10K_REGISTER_BASE 0
#define FM10K_REGISTER_SPI_TX_DATA ((0xC26) + (FM10K_REGISTER_BASE))
#define FM10K_REGISTER_SPI_RX_DATA ((0xC27) + (FM10K_REGISTER_BASE))
#define FM10K_REGISTER_SPI_HEADER ((0xC28) + (FM10K_REGISTER_BASE))
#define FM10K_REGISTER_SPI_CTRL ((0xC29) + (FM10K_REGISTER_BASE))
#define FM10K_SPI_FREQ_KHZ 50000
#define FM10K_SPI_HEADER_COMMAND_READ_BYTES 0x3
#define FM10K_SPI_HEADER_COMMAND_READ_MANUFACTURER_INFORMATION 0x9F
/* Test for GCC >= 4.4.0, see https://gcc.gnu.org/gcc-4.4/changes.html : Packed bit-fields of type char were not properly bit-packed on many targets prior to GCC 4.4 */
#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 4)) || __clang__
typedef union {
struct {
uint16_t Freq : 10; // Actual speed is PCIE_REFCLK/(2*(1+Freq))
uint8_t Enable : 1;
uint8_t Command : 4; // 0b00 = reset
uint8_t HeaderSize : 2; // 0b00 = 4 bytes
uint8_t DataSize : 2; // 0b00 = 4 bytes
uint8_t DataShiftMethod : 2; // 0b00 = SINGLE, 0b01 = DUAL, 0b10 = QUAD, 0b11 = Reserved
uint8_t Busy : 1; // volatile ReadOnly
uint8_t Selected : 1; // volatile ReadOnly
uint8_t DirIO2 : 1; // 0b0 = Input, 0b1 = Output
uint8_t DirIO3 : 1; // 0b0 = Input, 0b1 = Output
uint8_t PinIO2 : 1; // volatile ReadWrite
uint8_t PinIO3 : 1; // volatile ReadWrite
uint8_t Reserved : 5;
} __attribute__((packed)) fields;
uint32_t value;
} SPI_CTRL;
typedef union {
struct {
uint8_t RESERVED : 8;
uint8_t productVariant : 5;
uint8_t subCode : 3;
uint8_t densityCode : 5;
uint8_t familyCode : 3;
uint8_t manufacturerId : 8;
} __attribute__((packed)) fields;
uint32_t value;
} SPI_COMMAND_READ_MANUFACTURER_RESULT;
#else
#error "Packed bit-fields of type char were not properly bit-packed on many targets prior to GCC 4.4, upgrade to GCC >= 4.4."
#endif
KNOWN_FLASH_DEVICE KNOWN_FLASH_DEVICE_LIST[] = {
{{0x0101271f}, "Adesto AT45DB321E 32-Mbit", 32},
{{0}, "", 0}
};
void ReadRegister32(uintptr_t mem, uint32_t addr, uint32_t* value) {
*value = *(((uint32_t*)mem) + addr);
@ -220,8 +164,12 @@ SPI_COMMAND_READ_MANUFACTURER_RESULT fm10k_uio_spi_ReadManufacturerData(uintptr_
return result;
}
uint32_t rxData;
/* get data */
ReadRegister32(mem, FM10K_REGISTER_SPI_RX_DATA, &result.value);
ReadRegister32(mem, FM10K_REGISTER_SPI_RX_DATA, &rxData);
result.value = ((rxData>>24)&0xff) | ((rxData<<8)&0xff0000) | ((rxData>>8)&0xff00) | ((rxData<<24)&0xff000000);
/* release CS */
spiCtrl.fields.Command = 0b1000;
@ -236,8 +184,7 @@ SPI_COMMAND_READ_MANUFACTURER_RESULT fm10k_uio_spi_ReadManufacturerData(uintptr_
return result;
}
uint32_t fm10k_uio_spi_ReadFlash(uintptr_t mem, uint32_t address, uint8_t* data, int32_t len){
uint32_t fm10k_uio_spi_ReadFlash(uintptr_t mem, uint32_t address, uint8_t* data, uint32_t len){
SPI_CTRL currentSpiCtrl;
SPI_CTRL spiCtrl = {0};
@ -245,8 +192,8 @@ uint32_t fm10k_uio_spi_ReadFlash(uintptr_t mem, uint32_t address, uint8_t* data,
uint32_t rxData;
uint32_t header;
int32_t freq;
int32_t cnt;
int32_t numRead;
uint32_t cnt;
uint32_t numRead;
fm10k_uio_spi_Enable(mem);
@ -307,94 +254,170 @@ uint32_t fm10k_uio_spi_ReadFlash(uintptr_t mem, uint32_t address, uint8_t* data,
return 0;
}
int main(int argc, char** argv){
int fdUIO;
FILE* fdOutput;
uint32_t fm10k_uio_spi_EnableSectorProtection(uintptr_t mem){
SPI_CTRL currentSpiCtrl;
SPI_CTRL spiCtrl = {0};
if(argc < 3){
printf("Usage: %s </dev/uio0> <output.bin> [forceSizeInMegabits, default 8]\n", argv[0]);
return 1;
}
uint32_t header;
int32_t freq;
fdOutput = fopen(argv[2], "wb");
if(fdOutput == NULL){
printf("Could not create %s\n", argv[2]);
return 1;
}
fdUIO = open(argv[1], O_RDWR);
if(fdUIO <= 0){
printf("Unable to open uio device %s to read NVM\n", argv[1]);
return 1;
}
void* memmapAddr;
memmapAddr = mmap(NULL, FM10K_UIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fdUIO, 0);
if (memmapAddr == MAP_FAILED) {
printf("Unable to map uio device %s to read NVM\n", argv[1]);
close(fdUIO);
return 1;
}
SPI_COMMAND_READ_MANUFACTURER_RESULT manufacturerInfo = fm10k_uio_spi_ReadManufacturerData((uintptr_t)memmapAddr);
if(manufacturerInfo.value == 0){
printf("Error getting manufacturer information\n");
return 1;
}
printf("Manufacturer Info 0x%08x :: JEDEC Manufacturer ID 0x%02x family 0x%02x density 0x%02x %02x:%02x\n", manufacturerInfo.value, manufacturerInfo.fields.manufacturerId, manufacturerInfo.fields.familyCode, manufacturerInfo.fields.densityCode, manufacturerInfo.fields.subCode, manufacturerInfo.fields.productVariant);
unsigned int mbitsToRead = 8;
fm10k_uio_spi_Enable(mem);
switch (manufacturerInfo.value >> 8) {
case 0x1f2701:
printf("Device: Adesto AT45Dxxx Standard:1 32Mbit\n");
mbitsToRead = 32;
break;
default:
printf("Device: Unknown, defaulting %uMbit size\n", mbitsToRead);
break;
}
ReadRegister32(mem, FM10K_REGISTER_SPI_CTRL, &currentSpiCtrl.value);
if(argc > 3){
if(mbitsToRead != 8){
printf("WARNING: %uMbit size was already auto-detected\n", mbitsToRead);
if(FM10K_SPI_FREQ_KHZ > 0){
freq = ((100000 / (int)FM10K_SPI_FREQ_KHZ) / 2) - 1;
if (freq < 0){
freq = 0;
}
mbitsToRead = strtoul(argv[3], NULL, 10);
printf("Forcing %uMbit size\n", mbitsToRead);
spiCtrl.fields.Freq = freq;
}
if(mbitsToRead > 1024){
printf("Too many MBit: %u\n", mbitsToRead);
spiCtrl.fields.Enable = 1;
header = (0x3D << 24) | (0x2A << 16) | (0x7F) | (0xA9);
WriteRegister32(mem, FM10K_REGISTER_SPI_HEADER, header);
/* first loop only: set send header flag. Following loops: shift only data. */
spiCtrl.fields.Command |= 0b0001;
/* send command to the flash */
if(fm10k_uio_spi_SetCtrlReg(mem, spiCtrl)){
return 1;
}
if(mbitsToRead < 8){
printf("Too few MBit: %u\n", mbitsToRead);
/* release CS */
spiCtrl.fields.Command = 0b1000;
if(fm10k_uio_spi_SetCtrlReg(mem, spiCtrl)){
return 1;
}
uint32_t bootImageSize = (mbitsToRead * 1024 * 1024) / 8;
uint8_t* value = malloc(bootImageSize);
uint32_t strideSize = 1024;
for(uint32_t addr = 0; addr < bootImageSize; addr += strideSize){
printf("\rread @ 0x%08x / 0x%08x %d bytes", addr, bootImageSize, strideSize);
if(fm10k_uio_spi_ReadFlash((uintptr_t)memmapAddr, addr, value + addr, strideSize)){
return 2;
}
}
fwrite(value, 1, bootImageSize, fdOutput);
free(value);
fclose(fdOutput);
printf("\nRead %u bytes\n", bootImageSize);
fm10k_uio_spi_Disable(mem);
return 0;
}
uint32_t fm10k_uio_spi_DisableSectorProtection(uintptr_t mem){
SPI_CTRL currentSpiCtrl;
SPI_CTRL spiCtrl = {0};
uint32_t header;
int32_t freq;
fm10k_uio_spi_Enable(mem);
ReadRegister32(mem, FM10K_REGISTER_SPI_CTRL, &currentSpiCtrl.value);
if(FM10K_SPI_FREQ_KHZ > 0){
freq = ((100000 / (int)FM10K_SPI_FREQ_KHZ) / 2) - 1;
if (freq < 0){
freq = 0;
}
spiCtrl.fields.Freq = freq;
}
spiCtrl.fields.Enable = 1;
header = (0x3D << 24) | (0x2A << 16) | (0x7F) | (0x9A);
WriteRegister32(mem, FM10K_REGISTER_SPI_HEADER, header);
/* first loop only: set send header flag. Following loops: shift only data. */
spiCtrl.fields.Command |= 0b0001;
/* send command to the flash */
if(fm10k_uio_spi_SetCtrlReg(mem, spiCtrl)){
return 1;
}
/* release CS */
spiCtrl.fields.Command = 0b1000;
if(fm10k_uio_spi_SetCtrlReg(mem, spiCtrl)){
return 1;
}
fm10k_uio_spi_Disable(mem);
return 0;
}
uint32_t fm10k_uio_spi_WriteFlash(uintptr_t mem, uint32_t address, const uint8_t* data, uint32_t len){
SPI_CTRL currentSpiCtrl;
SPI_CTRL spiCtrl = {0};
uint32_t txData;
uint32_t header;
int32_t freq;
uint32_t cnt;
uint32_t numWrite;
fm10k_uio_spi_Enable(mem);
ReadRegister32(mem, FM10K_REGISTER_SPI_CTRL, &currentSpiCtrl.value);
if(FM10K_SPI_FREQ_KHZ > 0){
freq = ((100000 / (int)FM10K_SPI_FREQ_KHZ) / 2) - 1;
if (freq < 0){
freq = 0;
}
spiCtrl.fields.Freq = freq;
}
spiCtrl.fields.Enable = 1;
/* header: command (1 byte: READ_MODIFY_WRITE) + address (3 bytes) */
header = (FM10K_SPI_HEADER_COMMAND_READ_MODIFY_WRITE_512 << 24) | (address & 0xffffff);
WriteRegister32(mem, FM10K_REGISTER_SPI_HEADER, header);
/* first loop only: set send header flag. Following loops: shift only data. */
spiCtrl.fields.Command |= 0b0001;
cnt = 0;
while (cnt < len){
/* determine the number of data bytes to read [1..4] */
numWrite = (len - cnt) > 3 ? 4 : (len - cnt);
/* set 'shift data' flag and number of data bytes */
spiCtrl.fields.Command |= 0b0100;
spiCtrl.fields.DataSize = numWrite & 0b11;
txData = 0;
while (numWrite--) {
txData = (txData << 8) | data[cnt++];
}
/* set data to be written */
WriteRegister32(mem, FM10K_REGISTER_SPI_TX_DATA, txData);
/* send command to the flash */
if(fm10k_uio_spi_SetCtrlReg(mem, spiCtrl)){
return 1;
}
spiCtrl.fields.Command = 0b0000;
spiCtrl.fields.DataSize = 0b0000;
}
/* release CS */
spiCtrl.fields.Command = 0b1000;
if(fm10k_uio_spi_SetCtrlReg(mem, spiCtrl)){
return 1;
}
fm10k_uio_spi_Disable(mem);
return 0;
}
const KNOWN_FLASH_DEVICE* getKnownFlashFromManufacturerData(SPI_COMMAND_READ_MANUFACTURER_RESULT data){
uint32_t deviceCount = (sizeof(KNOWN_FLASH_DEVICE_LIST) / sizeof(KNOWN_FLASH_DEVICE)) - 1;
for(uint32_t i = 0; i < deviceCount; ++i){
if(KNOWN_FLASH_DEVICE_LIST[i].data.value == data.value){
return &KNOWN_FLASH_DEVICE_LIST[i];
}
}
return NULL;
}

122
src/fm10k.h Normal file
View file

@ -0,0 +1,122 @@
/*****************************************************************************
* File: fm10k.h
* Creation Date: December 20, 2020
*
* Copyright (c) 2014 - 2015, Intel Corporation
* Copyright (c) 2020, FM10K-Documentation Contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the copyright holders nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
#include <inttypes.h>
#include <sys/time.h>
#define FM10K_UIO_SIZE 0x0000000004000000
#define FM10K_UIO_OFFSET 0
//From Datasheet, 11.25.1 MGMT Map, Table 11-37
#define FM10K_REGISTER_BASE 0
#define FM10K_REGISTER_SPI_TX_DATA ((0xC26) + (FM10K_REGISTER_BASE))
#define FM10K_REGISTER_SPI_RX_DATA ((0xC27) + (FM10K_REGISTER_BASE))
#define FM10K_REGISTER_SPI_HEADER ((0xC28) + (FM10K_REGISTER_BASE))
#define FM10K_REGISTER_SPI_CTRL ((0xC29) + (FM10K_REGISTER_BASE))
#define FM10K_SPI_FREQ_KHZ 50000
#define FM10K_SPI_HEADER_COMMAND_PAGE_PROGRAM 0x02
#define FM10K_SPI_HEADER_COMMAND_READ_BYTES 0x03
#define FM10K_SPI_HEADER_COMMAND_READ_MODIFY_WRITE_512 0x58
#define FM10K_SPI_HEADER_COMMAND_READ_MANUFACTURER_INFORMATION 0x9F
/* Test for GCC >= 4.4.0, see https://gcc.gnu.org/gcc-4.4/changes.html : Packed bit-fields of type char were not properly bit-packed on many targets prior to GCC 4.4 */
#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 4)) || __clang__
typedef union {
uint32_t value;
struct {
uint16_t Freq : 10; // Actual speed is PCIE_REFCLK/(2*(1+Freq))
uint8_t Enable : 1;
uint8_t Command : 4; // 0b00 = reset
uint8_t HeaderSize : 2; // 0b00 = 4 bytes
uint8_t DataSize : 2; // 0b00 = 4 bytes
uint8_t DataShiftMethod : 2; // 0b00 = SINGLE, 0b01 = DUAL, 0b10 = QUAD, 0b11 = Reserved
uint8_t Busy : 1; // volatile ReadOnly
uint8_t Selected : 1; // volatile ReadOnly
uint8_t DirIO2 : 1; // 0b0 = Input, 0b1 = Output
uint8_t DirIO3 : 1; // 0b0 = Input, 0b1 = Output
uint8_t PinIO2 : 1; // volatile ReadWrite
uint8_t PinIO3 : 1; // volatile ReadWrite
uint8_t Reserved : 5;
} __attribute__((packed)) fields;
} SPI_CTRL;
typedef union {
uint32_t value;
struct {
uint8_t manufacturerId : 8;
uint8_t densityCode : 5;
uint8_t familyCode : 3;
uint8_t productVariant : 5;
uint8_t subCode : 3;
uint8_t RESERVED : 8;
} __attribute__((packed)) fields;
} SPI_COMMAND_READ_MANUFACTURER_RESULT;
typedef struct {
SPI_COMMAND_READ_MANUFACTURER_RESULT data;
char name[64];
uint32_t sizeInMbit;
} KNOWN_FLASH_DEVICE;
#else
#error "Packed bit-fields of type char were not properly bit-packed on many targets prior to GCC 4.4, upgrade to GCC >= 4.4."
#endif
void ReadRegister32(uintptr_t mem, uint32_t addr, uint32_t* value);
void WriteRegister32(uintptr_t mem, uint32_t addr, uint32_t value);
uint32_t get_interval_diff(struct timeval *begin, struct timeval *end);
uint32_t fm10k_uio_spi_SetCtrlReg(uintptr_t mem, SPI_CTRL value);
void fm10k_uio_spi_Enable(uintptr_t mem);
void fm10k_uio_spi_Disable(uintptr_t mem);
//Error if result.value is 0x00
SPI_COMMAND_READ_MANUFACTURER_RESULT fm10k_uio_spi_ReadManufacturerData(uintptr_t mem);
const KNOWN_FLASH_DEVICE* getKnownFlashFromManufacturerData(SPI_COMMAND_READ_MANUFACTURER_RESULT data);
uint32_t fm10k_uio_spi_EnableSectorProtection(uintptr_t mem);
uint32_t fm10k_uio_spi_DisableSectorProtection(uintptr_t mem);
uint32_t fm10k_uio_spi_ReadFlash(uintptr_t mem, uint32_t address, uint8_t* data, uint32_t len);
uint32_t fm10k_uio_spi_WriteFlash(uintptr_t mem, uint32_t address, const uint8_t* data, uint32_t len);