/* * patch_progfpga * * Copyright (C) 2019 Skip Hansen * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #define OPTION_STRING "12cgm" /* Decompiled flashFPGA_Series2_Golden routine extracted by Ghidra: RtnStatus_t flashFPGA_Series2_Golden(void *param_1) { uint32_t uVar1; uint uVar2; RtnStatus_t RVar3; RtnStatus_t local_28; uint32_t sectorSize_golden; uint32_t goldenSize; uint32_t spi_addr_golden; uint32_t sectorSize_header; uint32_t headerSize; uint32_t spi_addr_header; uint32_t flashType; uint32_t Rev; uVar1 = configRead(ep0Client,1); uVar2 = (uVar1 & 0x3800000) >> 0x17; if (uVar2 == 0) { log_severe("Flash type : LX150\n"); log_severe("look for lx150/header.9nimg"); goldenSize = 0x10ffff; sectorSize_golden = 0x43; log_severe("look for lx150/golden.9nimg\n"); } else { if (uVar2 != 1) { log_severe("Flash type : UNKNOWN\n"); return TEST_FAIL; } log_severe("Flash type : LX100\n"); log_severe("look for lx100/header.9nimg\n"); goldenSize = 0xcffff; sectorSize_golden = 0x33; log_severe("look for lx100/golden.9nimg\n"); } RVar3 = writeAndValidateImage ("Start writing golden header!",(uint32_t *)latestFpgaHeader,0,0xffff,3); if (RVar3 == TEST_FAIL) { local_28 = TEST_FAIL; } else { RVar3 = writeAndValidateImage ("Start writing golden image!",(uint32_t *)latestFpgaImage,0x10000,goldenSize, sectorSize_golden); if (RVar3 == TEST_FAIL) { local_28 = TEST_FAIL; } else { log_severe("Done Programming - power cycle to take effect\n"); write(ep0Client,0x8086,0x7ff); write(ep0Client,0x8087,0x1ff800); write(ep0Client,0x8013,0xc); local_28 = TEST_OK; } } return local_28; } Decompiled flashFPGA_Series2_Golden routine extracted by Ghidra: RtnStatus_t flashFPGA_Series2_Multiboot(void *param_1) { uint32_t uVar1; uint uVar2; RtnStatus_t RVar3; RtnStatus_t local_20; uint32_t sectorSize; uint32_t imageSize; uint32_t spi_addr; uint32_t spi_dat; uint32_t flashType; uint32_t Rev; uVar1 = configRead(ep0Client,1); uVar2 = (uVar1 & 0x3800000) >> 0x17; if (uVar2 == 0) { log_severe("Flash type : LX150"); spi_addr = 0x120000; imageSize = 0x10ffff; sectorSize = 0x43; log_severe("look for lx150/multiboot.9nimg"); } else { if (uVar2 != 1) { log_severe("Flash type : UNKNOWN"); return TEST_FAIL; } log_severe("Flash type : LX100"); spi_addr = 0xe0000; imageSize = 0xcffff; sectorSize = 0x33; log_severe("look for lx100/multiboot.9nimg"); } RVar3 = writeAndValidateImage ("Start writing multiboot image!",(uint32_t *)latestFpgaMultiBoot,spi_addr, imageSize,sectorSize); if (RVar3 != TEST_FAIL) { write(ep0Client,0x8086,0x7ff); write(ep0Client,0x8087,0x1ff800); write(ep0Client,0x8013,0xc); } local_20 = (RtnStatus_t)(RVar3 == TEST_FAIL); return local_20; } -rwxrwxr-x 1 skip skip 9180426 Apr 10 18:11 series1/progfpga skip@dell-790:~/pano/fpgaImage$ objdump -h series1/progfpga | grep '\.data ' 24 .data 001796d4 08143c00 08143c00 000fbc00 2**5 skip@dell-790:~/pano/fpgaImage$ objdump -t series1/progfpga | grep latestFpgaImage 08143c40 g O .data 0016c5d0 latestFpgaImage -rwxrwxr-x 1 skip skip 16144893 Apr 10 18:11 series2/lx150/progfpga_golden skip@dell-790:~/pano/fpgaImage$ objdump -h series2/lx150/progfpga_golden | grep '\.data ' 24 .data 00819ab4 08146c00 08146c00 000fec00 2**5 skip@dell-790:~/pano/fpgaImage$ objdump -t series2/lx150/progfpga_golden | grep latestFpgaImage 08146ca0 g O .data 0080ca68 latestFpgaImage -rwxrwxr-x 1 skip skip 16142365 Apr 10 18:11 series2/lx150/progfpga_multiboot skip@dell-790:~/pano/fpgaImage$ objdump -h series2/lx150/progfpga_multiboot | grep '\.data ' 24 .data 00819a54 08147280 08147280 000fe280 2**5 skip@dell-790:~/pano/fpgaImage$ objdump -t series2/lx150/progfpga_multiboot | grep latestFpgaMultiBoot 081472c0 g O .data 0080ca68 latestFpgaMultiBoot -rwxr-xr-x 1 skip skip 14337853 Apr 10 18:11 series2/lx100/progfpga_golden skip@dell-790:~/pano/fpgaImage$ objdump -h series2/lx100/progfpga_golden | grep '\.data ' 24 .data 00661174 08147280 08147280 000fe280 2**5 skip@dell-790:~/pano/fpgaImage$ objdump -t series2/lx100/progfpga_golden | grep latestFpgaImage 08147320 g O .data 00654128 latestFpgaImage -rwxr-xr-x 1 skip skip 14340189 Apr 10 18:11 series2/lx100/progfpga_multiboot skip@dell-790:~/pano/fpgaImage$ objdump -h series2/lx100/progfpga_multiboot | grep '\.data ' 24 .data 00661114 08146c00 08146c00 000fec00 2**5 skip@dell-790:~/pano/fpgaImage$ objdump -t series2/lx100/progfpga_multiboot | grep latestFpgaMultiBoot 08146c40 g O .data 00654128 latestFpgaMultiBoot Type .bin size XC6SLX100 3317908 XC6SLX150 4220212 */ struct { const char *Desc; const char *ProgFpgaPath; const char *ProgCmd; int ExecutableSize; int ImageOffset; int ImageSize; int StartAdr; } PatchData[] = { { "series1", "patched/series1/progfpga", "", 9180426, (0x8143c40 - 0x08143c00 + 0x000fbc00), (0x0016c5d0 / 2) - 4, 0 }, { "series2 Rev A/B with the default bitstream", "patched/series2/lx150/progfpga_multiboot", "flashFPGA_Series2_Multiboot", 16142365, (0x081472c0 - 0x08147280+ 0x000fe280), 0x0080ca68 / 2 }, { "series2 Rev A/B with the golden bitstream", "patched/series2/lx150/progfpga_golden", "flashFPGA_Series2_Golden", 16144893, (0x08146ca0 - 0x08146c00 + 0x000fec00), 0x0080ca68 / 2 }, { "series2 Rev C with the default bitstream", "patched/series2/lx100/progfpga_multiboot", "flashFPGA_Series2_Multiboot", 14340189, (0x08146c40 - 0x08146c00 + 0x000fec00), 0x00654128 / 2 }, { "series2 Rev C with the golden bitstream", "patched/series2/lx100/progfpga_golden", "flashFPGA_Series2_Golden", 14337853, (0x08147320 - 0x08147280 + 0x000fe280), 0x00654128 / 2 } }; enum { TYPE_SERIES_1, TYPE_SERIES_2, TYPE_SERIES_2_GOLDEN, TYPE_SERIES_2C, TYPE_SERIES_2C_GOLDEN }; int CreateScript(int BurnType); int WriteStrings2Script(FILE *fp,const char **Array); void Usage(void); int main(int argc, char **argv) { uint32_t Value; uint32_t Value1; uint32_t Adr = 0; uint8_t *cp = (uint8_t *) &Value; uint8_t *cp1 = (uint8_t *) &Value1; FILE *fp = NULL; FILE *fOut = NULL; int BurnType = -1; int bDisplayUsage = 1; int bGolden = 0; int bMultiboot = 0; struct stat Stat; int Ret = 0; int Option; int FilenameArg = 1; const char *ProgFpgaPath; const char *ImageFilePath; while(Ret == 0 && (Option = getopt(argc, argv,OPTION_STRING)) != -1) { FilenameArg++; switch(Option) { case '1': if(BurnType != -1) { Ret = EINVAL; } else { BurnType = TYPE_SERIES_1; } break; case '2': if(BurnType != -1) { Ret = EINVAL; } else { BurnType = TYPE_SERIES_2; } break; case 'c': if(BurnType != -1) { Ret = EINVAL; } else { BurnType = TYPE_SERIES_2C; } break; case 'g': bGolden = 1; break; case 'm': bMultiboot = 1; break; } } if(Ret == 0) do { if(FilenameArg != argc - 1) { break; } if(BurnType == -1) { printf("Error: Pano device type not specified.\n"); Ret = EINVAL; } if(bGolden) { if(BurnType == TYPE_SERIES_1) { printf("Error: series 1 devices don't have a golden bitstream.\n"); Ret = EINVAL; break; } BurnType++; } else if(!bMultiboot && BurnType != TYPE_SERIES_1) { printf("Error: bitstream type not specified.\n"); Ret = EINVAL; break; } bDisplayUsage = 0; ProgFpgaPath = PatchData[BurnType].ProgFpgaPath; ImageFilePath = argv[FilenameArg]; if(stat(ImageFilePath,&Stat)) { printf("Error: %s not found\n",ImageFilePath); Ret = errno; break; } if(PatchData[BurnType].ImageSize < Stat.st_size) { printf("Error incorrect FPGA bin file size (%lu), " "it must be %u bytes or smaller.\n",Stat.st_size, PatchData[BurnType].ImageSize); printf("NOTE: A .bit file is NOT the right format for this tool.\n"); Ret = EINVAL; break; } if((fp = fopen(ImageFilePath,"r")) == NULL) { printf("Error: couldn't open %s - %s\n",ImageFilePath, strerror(errno)); Ret = errno; break; } if(stat(ProgFpgaPath,&Stat)) { printf("Error: %s not found\n",ProgFpgaPath); Ret = errno; break; } if(PatchData[BurnType].ExecutableSize != Stat.st_size) { printf("Error: %s is not the expected size (%u != %lu)\n", ProgFpgaPath,PatchData[BurnType].ExecutableSize,Stat.st_size); break; } if((fOut = fopen(ProgFpgaPath,"r+")) == NULL) { printf("Error: couldn't open %s - %s\n",ProgFpgaPath,strerror(errno)); break; } if(fseek(fOut,PatchData[BurnType].ImageOffset,SEEK_SET) != 0) { printf("fseek failed: %s\n",strerror(errno)); break; } while(!feof(fp)) { if(fread(&Value,sizeof(Value),1,fp) != 1) { if(!feof(fp)) { printf("fread failed: %s\n",strerror(errno)); Ret = errno; } else { printf("%s updated successfully with %s\n",ProgFpgaPath, argv[FilenameArg]); } break; } if( Adr == PatchData[BurnType].ImageSize / 4 - 1 && Value != 0xFFFFFFFF ) { printf( "overrided end of bitstream file with 0xFFFFFFFF\n" ); Value = 0xFFFFFFFF; Adr = 0xFFFFFFFF; } // Reverse the byte order cp1[0] = cp[3]; cp1[1] = cp[2]; cp1[2] = cp[1]; cp1[3] = cp[0]; if(fwrite(&Adr,sizeof(Adr),1,fOut) != 1) { printf("fwrite failed: %s\n",strerror(errno)); Ret = errno; break; } if(fwrite(&Value1,sizeof(Value1),1,fOut) != 1) { printf("fwrite failed: %s\n",strerror(errno)); Ret = errno; break; } Adr++; } if(Ret != 0) { break; } // Create a script to execute the patched progfpga Ret = CreateScript(BurnType); } while(0); if(bDisplayUsage) { Usage(); } if(fp != NULL) { fclose(fp); } if(fOut != NULL) { fclose(fOut); } return errno; } const char *BoilerPlate1[] = { "#!/bin/sh\n", "# Warning: This file was generated by patch_progfpga, any edits will be lost\n", "# the next time patch_progfpga is run\n\n", NULL }; const char *BoilerPlate2[] = { "if [ $# -ne 1 ]; then\n", " echo \"Usage:\"\n", " echo \" ./progfpga -d\"\n", " echo \" ./progfpga <Pano device ip address>\"\n", " exit 1\n", "fi\n", "if [ $1 = \"-d\" ]; then\n", " series1/progfpga -d\n", " exit 0\n", "fi\n", "echo \"Program ${prog_type} y/n ?\"\n", "read answer\n", "if [ x$answer != \"xy\" ]; then\n", " exit 0\n", "fi\n", NULL }; const char *BoilerPlate3[] = { "sleep 1\n", "tail -f BurninLowLevel${1}.log\n", NULL }; // Create a script to execute the patched progfpga int CreateScript(int BurnType) { FILE *fp; int Ret = 0; int i; struct stat Stat; const char *DeleteOldLog = ""; do { if((fp = fopen("progfpga","w")) == NULL) { printf("Error: couldn't open ./progfpga for write - %s\n", strerror(errno)); Ret = errno; break; } if((Ret = WriteStrings2Script(fp,BoilerPlate1))!= 0) { break; } if(fprintf(fp,"prog_type=\"%s\"\n",PatchData[BurnType].Desc) <= 0) { Ret = errno; break; } if((Ret = WriteStrings2Script(fp,BoilerPlate2))!= 0) { break; } if(BurnType != TYPE_SERIES_1) { DeleteOldLog = "rm BurninLowLevel${1}.log\n"; } if(fprintf(fp,"%s./%s $1 %s%s\n", DeleteOldLog, PatchData[BurnType].ProgFpgaPath, PatchData[BurnType].ProgCmd, BurnType == TYPE_SERIES_1 ? "" : " &") <= 0) { Ret = errno; break; } if(BurnType != TYPE_SERIES_1) { if((Ret = WriteStrings2Script(fp,BoilerPlate3))!= 0) { break; } } } while(0); if(fp != NULL && Ret != 0) { printf("Error: fprintf failed - %s\n",strerror(errno)); } if(fp != NULL) { fclose(fp); if(stat("progfpga",&Stat)) { printf("stat failed - %s\n",strerror(errno)); Ret = errno; } else { Stat.st_mode |= S_IXUSR; if(chmod("progfpga",Stat.st_mode) != 0) { printf("chmod failed - %s\n",strerror(errno)); Ret = errno; } } printf("progfpga script created successfully.\n\n"); printf("To flash %s:\n",PatchData[BurnType].Desc); printf(" ./progfpga -d\n"); printf(" ./progfpga <ip address>\n"); } return Ret; } int WriteStrings2Script(FILE *fp,const char **Array) { int i; int Ret = 0; for(i = 0; Array[i] != NULL; i++) { if(fprintf(fp,"%s",Array[i]) <= 0) { Ret = errno; break; } } return Ret; } void Usage() { printf("usage: patch_progfpg [options] <path to Pano .bin file>\n"); printf("\t-1\tPano Series 1 (Spartan-3E XCS3S1600E)\n"); printf("\t-2\tPano Series 2 revision A or B (Spartan-6 XC6SLX150)\n"); printf("\t-c\tPano Series 2 revision C (Spartan-6 XC6SLX100)\n"); printf("\t-m\tburn multiboot (main) bitstream (series 2 devices only)\n"); printf("\t-g\tburn golden (backup) bitstream (series 2 devices only)\n"); }