fm10k-dump/src/fm10k.c

642 lines
19 KiB
C

/*****************************************************************************
* File: fm10k.c
* Creation Date: December 20, 2020
*
* Copyright (c) 2014 - 2015, Intel Corporation
* Copyright (c) 2021, 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 <dirent.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <sys/mman.h>
#include <fcntl.h>
KNOWN_FLASH_DEVICE KNOWN_FLASH_DEVICE_LIST[] = {
{{0x0101271f}, "Adesto AT45DB321E 32-Mbit", 32}, // Present on Silicom PE3100G2DQiRM-QX4
{{0x001540ef}, "Winbond W25Q16JV 16-Mbit", 16}, // Present on Intel FM10420-100GbE-QDA2
{{0}, "", 0}
};
void ReadRegister32(uintptr_t mem, uint32_t addr, uint32_t *value) {
*value = *(((volatile uint32_t *) mem) + addr);
}
void WriteRegister32(uintptr_t mem, uint32_t addr, uint32_t value) {
*(((volatile uint32_t *) mem) + addr) = value;
//If returning instantly this will caused missed writes. nanosleep wait of 1 nsec was too much. Busy wait works
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
volatile uint32_t dummy;
for (uint32_t i = 0; i < 100; ++i) {
dummy = i;
}
#pragma GCC diagnostic pop
}
uint32_t get_interval_diff(struct timeval *begin, struct timeval *end) {
struct timeval endT;
struct timeval diff;
if (end == NULL) {
gettimeofday(&endT, NULL);
} else {
endT.tv_sec = end->tv_sec;
endT.tv_usec = end->tv_usec;
}
diff.tv_sec = endT.tv_sec - begin->tv_sec;
diff.tv_usec = endT.tv_usec - begin->tv_usec;
return (diff.tv_sec * 1000 + diff.tv_usec / 1000);
}
uint32_t fm10k_mem_spi_SetCtrlReg(uintptr_t mem, SPI_CTRL value) {
struct timeval startTime;
uint8_t isTimeout = 0;
SPI_CTRL spiCtrl = {0};
WriteRegister32(mem, FM10K_REGISTER_SPI_CTRL, value.value);
gettimeofday(&startTime, NULL);
do {
if (isTimeout) {
printf("Timeout waiting for SPI_CTRL.Busy 0x%02x\n", spiCtrl.value);
return 1;
}
if (get_interval_diff(&startTime, NULL) > 50) {
isTimeout = 1;
}
ReadRegister32(mem, FM10K_REGISTER_SPI_CTRL, &spiCtrl.value);
} while (spiCtrl.fields.Busy);
/* write back SPI_CTRL with command = 0 */
spiCtrl.fields.Command = 0;
WriteRegister32(mem, FM10K_REGISTER_SPI_CTRL, spiCtrl.value);
return 0;
}
void fm10k_mem_spi_Enable(uintptr_t mem) {
//ENABLE SPI
SPI_CTRL spiCtrl;
ReadRegister32(mem, FM10K_REGISTER_SPI_CTRL, &spiCtrl.value);
/* keep current freq setting and set SPI Enable */
SPI_CTRL newSpiCtrl;
newSpiCtrl.value = 0;
newSpiCtrl.fields.Freq = spiCtrl.fields.Freq;
newSpiCtrl.fields.Enable = 1;
WriteRegister32(mem, FM10K_REGISTER_SPI_CTRL, newSpiCtrl.value);
}
void fm10k_mem_spi_Disable(uintptr_t mem) {
//DISABLE SPI
SPI_CTRL spiCtrl;
ReadRegister32(mem, FM10K_REGISTER_SPI_CTRL, &spiCtrl.value);
/* keep current freq setting and set SPI Enable = 0 */
SPI_CTRL newSpiCtrl;
newSpiCtrl.value = 0;
newSpiCtrl.fields.Freq = spiCtrl.fields.Freq;
WriteRegister32(mem, FM10K_REGISTER_SPI_CTRL, newSpiCtrl.value);
}
//Error if result.value is 0x00
SPI_COMMAND_READ_MANUFACTURER_RESULT fm10k_mem_spi_ReadManufacturerData(uintptr_t mem) {
SPI_COMMAND_READ_MANUFACTURER_RESULT result = {0};
SPI_CTRL currentSpiCtrl;
SPI_CTRL spiCtrl = {0};
uint32_t header;
int32_t freq;
fm10k_mem_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 = currentSpiCtrl.fields.Enable;
/* header: command (1 byte: READ_BYTES) */
header = FM10K_SPI_HEADER_COMMAND_READ_MANUFACTURER_INFORMATION;
WriteRegister32(mem, FM10K_REGISTER_SPI_HEADER, header);
/* first loop only: set send header flag. Following loops: shift only data. */
spiCtrl.fields.Command |= 0x1;
spiCtrl.fields.Command |= 0x4;
spiCtrl.fields.HeaderSize = 1;
spiCtrl.fields.DataSize = 0x4 & 3;
if (fm10k_mem_spi_SetCtrlReg(mem, spiCtrl)) {
return result;
}
uint32_t rxData;
/* get data */
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 = 0x8;
if (fm10k_mem_spi_SetCtrlReg(mem, spiCtrl)) {
result.value = 0;
return result;
}
fm10k_mem_spi_Disable(mem);
return result;
}
uint32_t fm10k_mem_spi_ReadFlash(uintptr_t mem, uint32_t address, uint8_t *data, uint32_t len) {
SPI_CTRL currentSpiCtrl;
SPI_CTRL spiCtrl = {0};
uint32_t rxData;
uint32_t header;
int32_t freq;
uint32_t cnt;
uint32_t numRead;
fm10k_mem_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 = currentSpiCtrl.fields.Enable;
/* header: command (1 byte: READ_BYTES) + address (3 bytes) */
header = (FM10K_SPI_HEADER_COMMAND_READ_BYTES << 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 |= 0x1;
cnt = 0;
while (cnt < len) {
/* determine the number of data bytes to read [1..4] */
numRead = (len - cnt) > 3 ? 4 : (len - cnt);
/* set 'shift data' flag and number of data bytes */
spiCtrl.fields.Command |= 0x4;
spiCtrl.fields.DataSize = numRead & 3;
if (fm10k_mem_spi_SetCtrlReg(mem, spiCtrl)) {
return 1;
}
/* get data */
ReadRegister32(mem, FM10K_REGISTER_SPI_RX_DATA, &rxData);
/* push the read data into the array */
while (numRead) {
numRead--;
data[cnt++] = (rxData >> (numRead * 8)) & 0xff;
}
spiCtrl.fields.Command = 0;
spiCtrl.fields.DataSize = 0;
}
/* release CS */
spiCtrl.fields.Command = 0x8;
if (fm10k_mem_spi_SetCtrlReg(mem, spiCtrl)) {
return 1;
}
fm10k_mem_spi_Disable(mem);
return 0;
}
uint32_t fm10k_mem_spi_EnableSectorProtection(uintptr_t mem) {
SPI_CTRL currentSpiCtrl;
SPI_CTRL spiCtrl = {0};
uint32_t header;
int32_t freq;
fm10k_mem_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) | (0xA9);
WriteRegister32(mem, FM10K_REGISTER_SPI_HEADER, header);
/* first loop only: set send header flag. Following loops: shift only data. */
spiCtrl.fields.Command |= 0x1;
/* send command to the flash */
if (fm10k_mem_spi_SetCtrlReg(mem, spiCtrl)) {
return 1;
}
/* release CS */
spiCtrl.fields.Command = 0x8;
if (fm10k_mem_spi_SetCtrlReg(mem, spiCtrl)) {
return 1;
}
fm10k_mem_spi_Disable(mem);
return 0;
}
uint32_t fm10k_mem_spi_DisableSectorProtection(uintptr_t mem) {
SPI_CTRL currentSpiCtrl;
SPI_CTRL spiCtrl = {0};
uint32_t header;
int32_t freq;
fm10k_mem_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 |= 0x1;
/* send command to the flash */
if (fm10k_mem_spi_SetCtrlReg(mem, spiCtrl)) {
return 1;
}
/* release CS */
spiCtrl.fields.Command = 0x8;
if (fm10k_mem_spi_SetCtrlReg(mem, spiCtrl)) {
return 1;
}
fm10k_mem_spi_Disable(mem);
return 0;
}
uint32_t fm10k_mem_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_mem_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 |= 0x1;
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 |= 0x4;
spiCtrl.fields.DataSize = numWrite & 3;
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_mem_spi_SetCtrlReg(mem, spiCtrl)) {
return 1;
}
spiCtrl.fields.Command = 0;
spiCtrl.fields.DataSize = 0;
}
/* release CS */
spiCtrl.fields.Command = 0x8;
if (fm10k_mem_spi_SetCtrlReg(mem, spiCtrl)) {
return 1;
}
fm10k_mem_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;
}
uint8_t fm10k_mem_spi_LockTake(uintptr_t mem){
uint32_t maxTries = 64;
API_SPI_LOCK_STATE spiLock;
do{
ReadRegister32(mem, FM10K_API_SPI_LOCK_STATE, &spiLock.value);
if(!spiLock.fields.LockTaken){
spiLock.fields.LockTaken = 1;
spiLock.fields.LockOwner = 0; //Switch API
WriteRegister32(mem, FM10K_API_SPI_LOCK_STATE, spiLock.value);
nanosleep(&(struct timespec){0, 50000}, NULL); //Delay to observe if lock was actually taken by us
ReadRegister32(mem, FM10K_API_SPI_LOCK_STATE, &spiLock.value);
if(spiLock.fields.LockTaken && spiLock.fields.LockOwner == 0){
printf("Taken SPI Lock\n");
return 0;
}
}else if(spiLock.fields.LockOwner == 0){ //Are we taking a lock again, with same owner?
printf("WARNING: SPI Lock was already taken by us, maybe other process was using it?\n");
return 0;
}
nanosleep(&(struct timespec){0, 1000}, NULL);
} while (--maxTries);
return 1;
}
void fm10k_mem_spi_LockRelease(uintptr_t mem){
API_SPI_LOCK_STATE spiLock;
ReadRegister32(mem, FM10K_API_SPI_LOCK_STATE, &spiLock.value);
if(!spiLock.fields.LockTaken) {
printf("WARNING: SPI Lock was already released\n");
return;
}
if(spiLock.fields.LockOwner != 0){
printf("WARNING: SPI Lock Owner unexpected: %u\n", spiLock.fields.LockOwner);
return;
}
spiLock.fields.LockTaken = 0;
WriteRegister32(mem, FM10K_API_SPI_LOCK_STATE, spiLock.value);
printf("Released SPI Lock\n");
}
uint32_t bsm_interruptMask[2] = {0, 0};
void fm10k_mem_bsm_DisableInterrupts(uintptr_t mem){
//Save old registers
ReadRegister32(mem, FM10K_REGISTER_INTERRUPT_MASK_BSM(0), bsm_interruptMask);
ReadRegister32(mem, FM10K_REGISTER_INTERRUPT_MASK_BSM(1), bsm_interruptMask + 1);
WriteRegister32(mem, FM10K_REGISTER_INTERRUPT_MASK_BSM(0), 0xFFFFFFFF);
WriteRegister32(mem, FM10K_REGISTER_INTERRUPT_MASK_BSM(1), 0xFFFFFFFF);
}
void fm10k_mem_bsm_RestoreInterrupts(uintptr_t mem){
WriteRegister32(mem, FM10K_REGISTER_INTERRUPT_MASK_BSM(0), bsm_interruptMask[0]);
WriteRegister32(mem, FM10K_REGISTER_INTERRUPT_MASK_BSM(1), bsm_interruptMask[1]);
}
uint8_t fm10k_mem_TakePlatformLock(uintptr_t mem){
if(fm10k_mem_spi_LockTake(mem)){
return 1;
}
fm10k_mem_bsm_DisableInterrupts(mem);
return 0;
}
void fm10k_mem_ReleasePlatformLock(uintptr_t mem){
fm10k_mem_spi_LockRelease(mem);
fm10k_mem_bsm_RestoreInterrupts(mem);
}
int fm10k_pci_unmapDevice(FM10K_PCI_MAPPED_DEVICE* map){
if(map->map != NULL && map->map != MAP_FAILED){
if(munmap(map->map, FM10K_BAR4_SIZE) != 0){
return 1;
}
map->map = NULL;
}
if (map->fd > 0) {
if(close(map->fd) != 0){
return 1;
}
map->fd = 0;
}
return 0;
}
int fm10k_pci_mapDevice(const FM10K_PCI_DEVICE* device, FM10K_PCI_MAPPED_DEVICE* map){
int fdBAR4;
fdBAR4 = open(device->bar4Path, O_RDWR);
if (fdBAR4 <= 0) {
printf("Unable to open BAR4 resource %s to read NVM\n", device->bar4Path);
return 1;
}
void *memmapAddr;
memmapAddr = mmap(NULL, FM10K_BAR4_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fdBAR4, 0);
if (memmapAddr == MAP_FAILED) {
printf("Unable to map BAR4 resource %s to read NVM\n", device->bar4Path);
close(fdBAR4);
return 1;
}
map->device = device;
map->fd = fdBAR4;
map->map = memmapAddr;
if(fm10k_mem_TakePlatformLock((uintptr_t) map->map)){
//Error taking lock
fm10k_pci_unmapDevice(map);
return 1;
}
return 0;
}
int fm10k_pci_findDevice(const FM10K_PCI_DEVICES* devices, FM10K_PCI_MAPPED_DEVICE* map) {
if(devices->count == 0){
return 1;
}
for(uint32_t i = 0; i < devices->count; ++i){
if(fm10k_pci_mapDevice(&devices->devices[i], map) == 0){
return 0;
}
}
return 1;
}
FM10K_PCI_DEVICES fm10k_pci_findDevices() {
DIR *folder;
char fileName[PATH_MAX];
FM10K_PCI_DEVICES devices;
devices.count = 0;
folder = opendir("/sys/bus/pci/devices");
if (folder == NULL) {
return devices;
}
struct dirent *entry;
while ((entry = readdir(folder))) {
if (entry->d_type == DT_DIR || entry->d_type == DT_LNK) {
sprintf(fileName, "/sys/bus/pci/devices/%s/vendor", entry->d_name);
FILE *vendorBytesPointer = fopen(fileName, "rb");
if (vendorBytesPointer != NULL) {
char *vendorBytesBuffer = NULL;
size_t vendorBytesLen;
size_t vendorBytesRead = getdelim(&vendorBytesBuffer, &vendorBytesLen, '\0', vendorBytesPointer);
if (vendorBytesRead != -1) {
unsigned int vendor = (unsigned int) strtoul(vendorBytesBuffer, NULL, 0);
if (vendor == 0x8086) { //Intel Corporation
sprintf(fileName, "/sys/bus/pci/devices/%s/device", entry->d_name);
FILE *classBytesPointer = fopen(fileName, "rb");
if (classBytesPointer != NULL) {
char *classBytesBuffer = NULL;
size_t classBytesLen;
size_t classBytesRead = getdelim(&classBytesBuffer, &classBytesLen, '\0',
classBytesPointer);
if (classBytesRead != -1) {
unsigned int class = (unsigned int) strtoul(classBytesBuffer, NULL, 0);
if (class == 0x15a4 || class == 0x15d0 || class == 0x15d5) { //FM10000 devices
sprintf(fileName, "/sys/bus/pci/devices/%s/resource4", entry->d_name);
if (access(fileName, F_OK) == 0) {
devices.devices[devices.count].vendor = vendor;
devices.devices[devices.count].class = class;
sprintf(devices.devices[devices.count].bar4Path, "/sys/bus/pci/devices/%s/resource4", entry->d_name);
++devices.count;
}
}
}
if (classBytesBuffer != NULL) {
free(classBytesBuffer);
}
fclose(classBytesPointer);
}
}
}
if (vendorBytesBuffer != NULL) {
free(vendorBytesBuffer);
}
fclose(vendorBytesPointer);
}
if (devices.count >= FM10K_PCI_DEVICE_MAX) {
break;
}
}
}
closedir(folder);
return devices;
}