Compare commits

...

28 commits
v0.1 ... master

Author SHA1 Message Date
DataHoarder 37b1644120 Apply consistent spaces and style across project
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2021-12-19 15:32:37 +01:00
DataHoarder 702209cbe6 Update copyright year 2021-12-19 15:30:44 +01:00
DataHoarder 88646eb2bb Added device found string with path / vendor details.
Some checks failed
continuous-integration/drone/push Build is failing
2021-12-19 15:29:45 +01:00
DataHoarder f13923f5da Refactor device handling to allow finding multiple devices and select appropiate one.
All checks were successful
continuous-integration/drone/push Build is passing
2021-12-19 15:25:56 +01:00
DataHoarder ab1bffe446 Mark values as volatile
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-28 07:27:22 +02:00
DataHoarder 3a434aa9c6 Build Debian bullseye
All checks were successful
continuous-integration/drone/push Build is passing
2021-08-04 07:24:59 +02:00
DataHoarder a361926b62 Remove old UIO comment on documentation header for flash/dump
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-07 22:27:51 +01:00
DataHoarder 9a2fc07ad8 Add extra newlines on SPI lock end
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-06 12:39:08 +01:00
DataHoarder 975f7df4d8 Remove TCC from debian jessie and fedora, fix alpine, fix void
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-06 11:17:07 +01:00
DataHoarder 60963aa01a Fix TCC on some architectures, -pedantic build, replaced binary literals with hex numbers
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-06 09:36:51 +01:00
DataHoarder f018396b1d Add specific #pragma pack(1) for TCC, fixes packed alignment issues
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-06 08:53:51 +01:00
DataHoarder 6223ca323c Take Platform / SPI lock before doing anything, and disable BSM interrupts (and set both back at the end)
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-06 08:50:15 +01:00
DataHoarder e8dcaa3af3 Support TCC building, added two packed struct alignment tests 2021-01-06 08:01:35 +01:00
DataHoarder 32123658e9 Compacted pipelines to build gcc & clang tests in two steps
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-31 05:15:50 +01:00
DataHoarder cf88d445e3 Test arm64 platform build
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-31 04:52:06 +01:00
DataHoarder 9e8623be7c Force drone to build on linux-amd64
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-31 01:24:49 +01:00
DataHoarder 233cdfcaa3 Some libraries on clang come from gcc, and it does not depend on it
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-29 21:20:27 +01:00
DataHoarder 070e3f3368 Added Void Linux test for XBPS
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-29 21:06:10 +01:00
DataHoarder fe20d93634 Added binutils to alpine
Some checks reported errors
continuous-integration/drone/push Build was killed
2020-12-29 21:02:47 +01:00
DataHoarder 1b57ca3f78 Added Fedora test, fixed alpine test
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-29 20:49:12 +01:00
DataHoarder 6ca1dd96c9 Added alpine linux test
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-29 20:38:53 +01:00
DataHoarder a68b1312f3 Added -Wno-unknown-pragmas to Makefile 2020-12-29 18:53:50 +01:00
DataHoarder 929f438a35 Change .drone.yml in favor of more compact .drone.jsonnet
All checks were successful
continuous-integration/drone/push Build is passing
Include debian & archlinux in tests
2020-12-29 18:20:12 +01:00
DataHoarder e9ad8ad656 General reformat commit to make Sora happy again
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-23 02:24:04 +01:00
DataHoarder c67ec7ffbe
Merge pull request 'Direct PCIe resource access without depending on fm10k driver' (#2) from direct-pci-resource-access into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #2
2020-12-23 01:02:44 +00:00
DataHoarder a351389c1e Direct PCIe resource access without depending on fm10k driver
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2020-12-23 02:01:57 +01:00
DataHoarder a566a323f9 Added Winbond W25Q16JV 16-Mbit to known devices
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-22 01:28:18 +01:00
DataHoarder c0d6478623
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>
2020-12-21 05:11:23 +00:00
12 changed files with 1442 additions and 448 deletions

83
.drone.jsonnet Normal file
View file

@ -0,0 +1,83 @@
local Pipeline(image, version, arch, depinstall, extra, gccExtra, clangExtra, tccExtra) = {
kind: "pipeline",
type: "docker",
name: image+"-"+version+"-"+arch,
platform: {
os: "linux",
arch: arch
},
steps: [
{
name: "build-gcc",
image: image+":"+version,
commands: [
depinstall+" make "+extra+" "+gccExtra,
"make clean",
"make CC=gcc all"
]
},
{
name: "build-clang",
image: image+":"+version,
commands: [
depinstall+" make "+extra+" "+clangExtra,
"make clean",
"make CC=clang all"
]
}
] + (if tccExtra != "" then [
{
name: "build-tcc",
image: image+":"+version,
commands: [
depinstall+" make "+extra+" "+tccExtra,
"make clean",
"make CC=tcc all"
]
}
] else []
)
};
local AptPipeline(image, version, arch, extra="", gccExtra="gcc", clangExtra="clang", tccExtra="tcc") = Pipeline(image, version, arch, "apt update && DEBIAN_FRONTEND=noninteractive apt install -y", extra, gccExtra, clangExtra, tccExtra);
local PacManPipeline(image, version, arch, extra="", gccExtra="gcc", clangExtra="clang", tccExtra="tcc") = Pipeline(image, version, arch, "pacman -Syy && pacman --noconfirm -S", extra, gccExtra, clangExtra, tccExtra);
local ApkPipeline(image, version, arch, extra="musl-dev linux-headers", gccExtra="gcc", clangExtra="gcc clang", tccExtra="") = Pipeline(image, version, arch, "apk add --update", extra, gccExtra, clangExtra, tccExtra);
local YumPipeline(image, version, arch, extra="", gccExtra="gcc", clangExtra="clang", tccExtra="") = Pipeline(image, version, arch, "yum install -y", extra, gccExtra, clangExtra, tccExtra);
local XbpsPipeline(image, version, arch, extra="", gccExtra="gcc", clangExtra="clang", tccExtra="tcc glibc-devel") = Pipeline(image, version, arch, "xbps-install -Sy", extra, gccExtra, clangExtra, tccExtra);
#
[
AptPipeline("ubuntu", "20.04", "amd64"),
AptPipeline("ubuntu", "20.04", "arm64"),
AptPipeline("ubuntu", "18.04", "amd64"),
AptPipeline("debian", "bullseye", "amd64"),
AptPipeline("debian", "bullseye", "arm64"),
AptPipeline("debian", "buster", "amd64"),
AptPipeline("debian", "stretch", "amd64"),
AptPipeline("debian", "jessie", "amd64", "", "gcc", "clang", ""),
YumPipeline("fedora", "latest", "amd64", "", "gcc", "clang", ""),
YumPipeline("fedora", "latest", "arm64"),
PacManPipeline("archlinux", "latest", "amd64"),
ApkPipeline("alpine", "latest", "amd64"),
ApkPipeline("alpine", "latest", "arm64"),
XbpsPipeline("voidlinux/voidlinux", "latest", "amd64"),
]

View file

@ -1,27 +0,0 @@
---
kind: pipeline
type: docker
name: build-gcc
steps:
- name: build
image: ubuntu:20.04
commands:
- apt update
- DEBIAN_FRONTEND=noninteractive apt install -y gcc make
- make clean
- make CC=gcc CFLAGS="-Wall -Werror -Wno-unknown-warning-option -Wno-packed-bitfield-compat"
---
kind: pipeline
type: docker
name: build-clang
steps:
- name: build
image: ubuntu:20.04
commands:
- apt update
- DEBIAN_FRONTEND=noninteractive apt install -y clang make
- make clean
- make CC=clang CFLAGS="-Wall -Werror -Wno-unknown-warning-option -Wno-packed-bitfield-compat"
...

4
.gitignore vendored
View file

@ -1,3 +1,5 @@
/fm10k-dump
/*.o
/fm10k-flash
/test-runner
*.o
/.idea

25
COPYING Normal file
View file

@ -0,0 +1,25 @@
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.

View file

@ -1,14 +1,41 @@
CC=cc
CFLAGS=-Wall -Wno-unknown-warning-option -Wno-packed-bitfield-compat
CFLAGS=-ggdb -Wall -Werror -pedantic -Wno-unknown-pragmas -Wno-packed-bitfield-compat
default: fm10k-dump
ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"), 1)
CFLAGS += -O0 -std=gnu99 -Wno-unknown-warning-option
else ifeq ($(shell $(CC) -v 2>&1 | grep -c "tcc version"), 1)
CFLAGS += -std=c99 -Wno-macro-redefined
else
CFLAGS += -Og -std=gnu99
endif
fm10k-dump.o: fm10k-dump.c
$(CC) -c fm10k-dump.c -o fm10k-dump.o $(CFLAGS)
default: fm10k-dump fm10k-flash
fm10k-dump: fm10k-dump.o
$(CC) fm10k-dump.o -o fm10k-dump $(CFLAGS)
all: fm10k-dump fm10k-flash test
test: run_test
src/%.o: src/%.c
@$(CC) $(CFLAGS) -c $< $(INC) -o $@
fm10k-dump: src/fm10k-dump.o src/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: src/fm10k-flash.o src/fm10k.o
$(CC) src/fm10k-flash.o src/fm10k.o -o fm10k-flash $(CFLAGS)
test-runner: src/test.o src/fm10k.o
$(CC) src/test.o src/fm10k.o -o test-runner $(CFLAGS)
run_test: test-runner
./test-runner
clean:
-rm -f fm10k-dump
-rm -f fm10k-dump.o
-rm -f fm10k-flash
-rm -f test-runner
-rm -f src/*.o

View file

@ -1,25 +1,29 @@
# 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
* GCC >= 4.4.0, TCC, or clang
* make
* [fm10k kernel module](https://git.gammaspectra.live/FM10K/fm10k) compiled with UIO.
* `# cd src && make clean && make -j $(nproc) CONFIG_UIO=1 && make install CONFIG_UIO=1`
* `# rmmod fm10k && modprobe uio && modprobe fm10k`
* If this works an UIO device (`/dev/uio0`) will exist. If not, check your bifurcation settings and/or whether the slot supports 16x PCIe 3.0
* Only one FM10K device installed on hardware.
* A management resource (BAR4) must be exposed, check bifurcation settings if needed.
* Usually FM10K cards use two 8x PCIe groups per 16x slot.
## 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.
* You can specify whether to use gcc/clang/tcc this way: `$ make CC=gcc` / `$ make CC=clang` / `$ make CC=tcc`
* 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-dump outputImage.bin`
* You can also force the image/chip size: `# ./fm10k-dump 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 backupImage.bin`
* `# ./fm10k-flash 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.

View file

@ -1,400 +0,0 @@
/*****************************************************************************
* 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]
*
* 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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.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
void ReadRegister32(uintptr_t mem, uint32_t addr, uint32_t* value) {
*value = *(((uint32_t*)mem) + addr);
}
void WriteRegister32(uintptr_t mem, uint32_t addr, uint32_t value) {
*(uint32_t 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_uio_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_uio_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_uio_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_uio_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_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 = 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 |= 0b0001;
spiCtrl.fields.Command |= 0b0100;
spiCtrl.fields.HeaderSize = 1;
spiCtrl.fields.DataSize = 4 & 0b11;
if(fm10k_uio_spi_SetCtrlReg(mem, spiCtrl)){
return result;
}
/* get data */
ReadRegister32(mem, FM10K_REGISTER_SPI_RX_DATA, &result.value);
/* release CS */
spiCtrl.fields.Command = 0b1000;
if(fm10k_uio_spi_SetCtrlReg(mem, spiCtrl)){
result.value = 0;
return result;
}
fm10k_uio_spi_Disable(mem);
return result;
}
uint32_t fm10k_uio_spi_ReadFlash(uintptr_t mem, uint32_t address, uint8_t* data, int32_t len){
SPI_CTRL currentSpiCtrl;
SPI_CTRL spiCtrl = {0};
uint32_t rxData;
uint32_t header;
int32_t freq;
int32_t cnt;
int32_t numRead;
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 = 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 |= 0b0001;
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 |= 0b0100;
spiCtrl.fields.DataSize = numRead & 0b11;
if(fm10k_uio_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 = 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;
}
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;
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;
}
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 = 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);
return 0;
}

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

@ -0,0 +1,137 @@
/*****************************************************************************
* 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 <output.bin> [readSizeInMegabits=8]
*
* 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 <stdlib.h>
int main(int argc, char **argv) {
FILE *fdOutput;
if (argc < 2) {
printf("Usage: %s <output.bin> [forceSizeInMegabits, default 8]\n", argv[0]);
return 1;
}
fdOutput = fopen(argv[1], "wb");
if (fdOutput == NULL) {
printf("Could not create %s\n", argv[1]);
return 1;
}
FM10K_PCI_MAPPED_DEVICE map;
FM10K_PCI_DEVICES devices = fm10k_pci_findDevices();
if (fm10k_pci_findDevice(&devices, &map)) {
printf("Unable to find FM10K device with BAR4 port access, or could not take SPI lock. Check bifurcation settings?\n");
return 1;
}
printf("FM10K device found at %s :: Vendor 0x%04x :: Class 0x%04x\n", map.device->bar4Path, map.device->vendor, map.device->class);
SPI_COMMAND_READ_MANUFACTURER_RESULT manufacturerInfo = fm10k_mem_spi_ReadManufacturerData((uintptr_t) map.map);
if (manufacturerInfo.value == 0) {
printf("Error getting manufacturer information\n");
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
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("SPI Device: %s\n", knownFlashDevice->name);
mbitsToRead = knownFlashDevice->sizeInMbit;
} else {
printf("SPI Device: Unknown, defaulting %uMbit size\n", mbitsToRead);
}
if (argc > 2) {
if (mbitsToRead != 8) {
printf("WARNING: %uMbit size was already auto-detected\n", mbitsToRead);
}
mbitsToRead = strtoul(argv[2], NULL, 10);
printf("Forcing %uMbit size\n", mbitsToRead);
}
if (mbitsToRead > 1024) {
printf("Too many MBit: %u\n", mbitsToRead);
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
return 1;
}
if (mbitsToRead < 8) {
printf("Too few MBit: %u\n", mbitsToRead);
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
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_mem_spi_ReadFlash((uintptr_t) map.map, addr, value + addr, strideSize)) {
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
return 2;
}
}
fwrite(value, 1, bootImageSize, fdOutput);
free(value);
fclose(fdOutput);
printf("\n");
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
fm10k_pci_unmapDevice(&map);
printf("Read %u bytes\n", bootImageSize);
return 0;
}

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

@ -0,0 +1,253 @@
/*****************************************************************************
* 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 <input.bin> <backup.bin>
*
* 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 <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
FILE *fdBackup;
FILE *fdInput;
if (argc < 3) {
printf("Usage: %s <input.bin> <backup.bin>\n", argv[0]);
return 1;
}
fdInput = fopen(argv[1], "rb");
if (fdInput == NULL) {
printf("Could not open %s\n", argv[1]);
return 1;
}
fdBackup = fopen(argv[2], "rb");
if (fdBackup == NULL) {
printf("Could not open existing backup %s\n", argv[2]);
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;
}
FM10K_PCI_MAPPED_DEVICE map;
FM10K_PCI_DEVICES devices = fm10k_pci_findDevices();
if (fm10k_pci_findDevice(&devices, &map)) {
printf("Unable to find FM10K device with BAR4 port access, or could not take SPI lock. Check bifurcation settings?\n");
return 1;
}
printf("FM10K device found at %s :: Vendor 0x%04x :: Class 0x%04x\n", map.device->bar4Path, map.device->vendor, map.device->class);
SPI_COMMAND_READ_MANUFACTURER_RESULT manufacturerInfo = fm10k_mem_spi_ReadManufacturerData((uintptr_t) map.map);
if (manufacturerInfo.value == 0) {
printf("Error getting manufacturer information\n");
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
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("SPI Device: %s\n", knownFlashDevice->name);
mbitsToReadMax = knownFlashDevice->sizeInMbit;
} else {
printf("SPI Device: Unknown, defaulting max %uMbit size\n", mbitsToReadMax);
}
if (mbitsToReadMax > 1024) {
printf("Too many MBit: %u\n", mbitsToReadMax);
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
return 1;
}
if (mbitsToReadMax == 0) {
printf("Too few MBit: %u\n", mbitsToReadMax);
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
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);
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
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");
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
return 1;
}
if (fread(valueBackup, 1, bootImageSizeMax, fdBackup) != bootImageSizeMax) {
printf("Error reading backup image file.\n");
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
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_mem_spi_ReadFlash((uintptr_t) map.map, addr, valueCheck + addr, strideSize)) {
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
return 2;
}
}
printf("\n");
if (memcmp(valueBackup, valueCheck, bootImageSizeMax) != 0) {
printf("Backup image mismatch! Dump image again.\n");
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
return 1;
} else {
printf("Backup matches.\n");
}
sleep(1);
printf("Disabling sector protection.\n");
fm10k_mem_spi_DisableSectorProtection((uintptr_t) map.map);
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_mem_spi_WriteFlash((uintptr_t) map.map, addr, value + addr, strideSize)) {
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
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_mem_spi_ReadFlash((uintptr_t) map.map, addr, valueCheck + addr, strideSize)) {
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
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_mem_spi_WriteFlash((uintptr_t) map.map, addr, valueBackup + addr, strideSize)) {
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
return 2;
}
sleep(1);
}
}
} else {
printf("Data written verified.\n");
}
sleep(1);
printf("\n");
fm10k_mem_ReleasePlatformLock((uintptr_t) map.map);
fm10k_pci_unmapDevice(&map);
free(value);
free(valueBackup);
free(valueCheck);
fclose(fdInput);
fclose(fdBackup);
printf("\nWritten %u bytes\n", bootImageSizeMax);
return 0;
}

642
src/fm10k.c Normal file
View file

@ -0,0 +1,642 @@
/*****************************************************************************
* 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;
}

189
src/fm10k.h Normal file
View file

@ -0,0 +1,189 @@
/*****************************************************************************
* File: fm10k.h
* 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.
*****************************************************************************/
#pragma once
#include <inttypes.h>
#include <sys/time.h>
#include <linux/limits.h>
#define FM10K_BAR4_SIZE 0x0000000004000000
#define FM10K_BAR4_OFFSET 0
//From Datasheet, 11.25.1 MGMT Map, Table 11-37
#define FM10K_REGISTER_BASE 0
#define FM10K_REGISTER_BSM_SCRATCH(r) ((0x800) + (FM10K_REGISTER_BASE) + (r))
#define FM10K_REGISTER_INTERRUPT_MASK_BSM(w) ((0x442) + (FM10K_REGISTER_BASE) + (w))
#define FM10K_API_SPI_LOCK_STATE FM10K_REGISTER_BSM_SCRATCH(0x000)
#define FM10K_API_EEPROM_IMAGE_VERSION FM10K_REGISTER_BSM_SCRATCH(0x191)
#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__ || __TINYC__
#if defined(__TINYC__)
#pragma pack(1)
#endif
typedef union {
uint32_t value;
struct {
uint8_t LockTaken: 1; //Whether lock is currently taken by anyone
uint8_t LockOwner: 2; //Who owns the lock. Switch API = 0, QV tools = 1, Board Manager = 2, RESERVED = 3
uint32_t Reserved: 29;
} __attribute__((packed)) fields;
} API_SPI_LOCK_STATE;
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;
#if defined(__TINYC__)
#pragma pack(1)
#endif
#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_mem_spi_SetCtrlReg(uintptr_t mem, SPI_CTRL value);
uint8_t fm10k_mem_spi_LockTake(uintptr_t mem);
void fm10k_mem_spi_LockRelease(uintptr_t mem);
void fm10k_mem_bsm_DisableInterrupts(uintptr_t mem);
void fm10k_mem_bsm_RestoreInterrupts(uintptr_t mem);
uint8_t fm10k_mem_TakePlatformLock(uintptr_t mem);
void fm10k_mem_ReleasePlatformLock(uintptr_t mem);
void fm10k_mem_spi_Enable(uintptr_t mem);
void fm10k_mem_spi_Disable(uintptr_t mem);
//Error if result.value is 0x00
SPI_COMMAND_READ_MANUFACTURER_RESULT fm10k_mem_spi_ReadManufacturerData(uintptr_t mem);
const KNOWN_FLASH_DEVICE *getKnownFlashFromManufacturerData(SPI_COMMAND_READ_MANUFACTURER_RESULT data);
uint32_t fm10k_mem_spi_EnableSectorProtection(uintptr_t mem);
uint32_t fm10k_mem_spi_DisableSectorProtection(uintptr_t mem);
uint32_t fm10k_mem_spi_ReadFlash(uintptr_t mem, uint32_t address, uint8_t *data, uint32_t len);
uint32_t fm10k_mem_spi_WriteFlash(uintptr_t mem, uint32_t address, const uint8_t *data, uint32_t len);
#define FM10K_PCI_DEVICE_MAX 16
typedef struct {
uint16_t vendor;
uint16_t class;
char bar4Path[PATH_MAX];
} FM10K_PCI_DEVICE;
typedef struct {
const FM10K_PCI_DEVICE *device;
void *map;
int fd;
} FM10K_PCI_MAPPED_DEVICE;
typedef struct {
uint32_t count;
FM10K_PCI_DEVICE devices[FM10K_PCI_DEVICE_MAX];
} FM10K_PCI_DEVICES;
FM10K_PCI_DEVICES fm10k_pci_findDevices();
int fm10k_pci_findDevice(const FM10K_PCI_DEVICES *devices, FM10K_PCI_MAPPED_DEVICE *map);
int fm10k_pci_mapDevice(const FM10K_PCI_DEVICE *device, FM10K_PCI_MAPPED_DEVICE *map);
int fm10k_pci_unmapDevice(FM10K_PCI_MAPPED_DEVICE *map);

59
src/test.c Normal file
View file

@ -0,0 +1,59 @@
/*****************************************************************************
* File: test.c
* Creation Date: January 06, 2021
* Description: Runs several platform alignment tests
* Compile: make test-bin
* Usage: ./test
*
* 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>
#define TEST(have, expected, i) if(have != expected){ printf("[TEST %u] FAIL: " i "\n", ++tests, have, expected); ++testsFailed; }else { printf("[TEST %u] PASS: " i "\n", ++tests, have, expected);}
int main(int argc, char **argv) {
uint32_t tests = 0;
uint32_t testsFailed = 0;
{
SPI_CTRL s;
TEST((uint32_t) sizeof(s.fields), (uint32_t) sizeof(s.value), "SPI_CTRL packed misalignment. size struct %u, size value %u");
}
{
SPI_COMMAND_READ_MANUFACTURER_RESULT s;
TEST((uint32_t) sizeof(s.fields), (uint32_t) sizeof(s.value),
"SPI_COMMAND_READ_MANUFACTURER_RESULT packed misalignment. size struct %u, size value %u");
}
{
API_SPI_LOCK_STATE s;
TEST((uint32_t) sizeof(s.fields), (uint32_t) sizeof(s.value),
"API_SPI_LOCK_STATE packed misalignment. size struct %u, size value %u");
}
printf("\nTests run, failed %u/%u\n", testsFailed, tests);
return testsFailed > 0 ? 1 : 0;
}