/***************************************************************************** * 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 * * 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 #include #include #include int main(int argc, char **argv) { FILE *fdBackup; FILE *fdInput; if (argc < 3) { printf("Usage: %s \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; }