/****************************************************************************** * * Copyright (C) 2019-2022 Xilinx, Inc. All rights reserved. * Copyright (C) 2022-2024 Advanced Micro Devices, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * ******************************************************************************/ /*****************************************************************************/ /** * @file: fpgautil.c * Simple command line tool to load fpga via overlay or through sysfs interface * and read fpga configuration using Xilinx Zynq/ZynqMP fpga manager * Author: Appana Durga Kedareswara Rao * Author: Nava kishore Manne */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OVERLAY 1 #define FPGA_SYSFS 2 #define READBACK 3 #define ENCRYPTION_USERKEY_EN (0x20U) int fpga_getplatform() { char fpstr[100]; FILE *fptr; char *zynqmpstr = "Xilinx ZynqMP FPGA Manager"; if ((fptr = fopen("/sys/class/fpga_manager/fpga0/name", "r")) == NULL) { printf("Error! opening file"); // Program exits if file pointer returns NULL. exit(1); } // reads text until newline fscanf(fptr,"%[^\n]", fpstr); fclose(fptr); if (!strcmp(zynqmpstr, fpstr)) return 1; else return 0; } static bool file_exists(const char *filename) { FILE *fp = fopen(filename, "r"); bool is_exist = false; if (fp != NULL) { is_exist = true; fclose(fp); // close the file } return is_exist; } void print_usage(char *prg) { int iszynqmp = fpga_getplatform(); fprintf(stderr, "\n%s: FPGA Utility for Loading/reading PL Configuration\n\n", prg); fprintf(stderr, "Usage: %s -b -o \n\r", prg); fprintf(stderr, "\n"); fprintf(stderr, "Options: -b (Bin file path)\n"); fprintf(stderr, " -o (DTBO file path)\n"); fprintf(stderr, " -f Optional: \n"); fprintf(stderr, " f := \n"); fprintf(stderr, " -n FPGA Regions represent FPGA's\n"); fprintf(stderr, " and partial reconfiguration\n"); fprintf(stderr, " regions of FPGA's in the\n"); fprintf(stderr, " Device Tree\n"); if (iszynqmp) { fprintf(stderr, " Default: \n"); fprintf(stderr, " -s Optional: \n"); fprintf(stderr, " s := \n"); fprintf(stderr, " -k Optional: \n"); fprintf(stderr, " -r Optional: \n"); fprintf(stderr, " Default: By default Read back contents will be stored in readback.bin file\n"); fprintf(stderr, " -t Optional: \n"); fprintf(stderr, " 0 - Configuration Register readback\n"); fprintf(stderr, " 1 - Configuration Data Frames readback\n"); fprintf(stderr, " Default: 0 (Configuration register readback)\n"); fprintf(stderr, " -R Optional: Remove overlay from a live tree\n"); } fprintf(stderr, " \n"); fprintf(stderr, "Examples:\n"); fprintf(stderr, "(Load Full bitstream using Overlay)\n"); fprintf(stderr, "%s -b top.bit.bin -o can.dtbo -f Full -n full \n", prg); fprintf(stderr, "(Load Partial bitstream using Overlay)\n"); fprintf(stderr, "%s -b rm0.bit.bin -o rm0.dtbo -f Partial -n PR0\n", prg); fprintf(stderr, "(Load Full bitstream using sysfs interface)\n"); fprintf(stderr, "%s -b top.bit.bin -f Full\n", prg); fprintf(stderr, "(Load Partial bitstream using sysfs interface)\n"); fprintf(stderr, "%s -b rm0.bit.bin -f Partial\n", prg); if (iszynqmp) { fprintf(stderr, "(Load Authenticated bitstream through the sysfs interface)\n"); fprintf(stderr, "%s -b top.bit.bin -f Full -s AuthDDR \n", prg); fprintf(stderr, "(Load Parital Encrypted Userkey bitstream using Overlay)\n"); fprintf(stderr, "%s -b top.bit.bin -o pl.dtbo -f Partial -s EnUsrKey -k <32byte key value>\n", prg); fprintf(stderr, "(Read PL Configuration Registers)\n"); fprintf(stderr, "%s -b top.bit.bin -r\n", prg); } fprintf(stderr, "(Remove Partial Overlay)\n"); fprintf(stderr, "%s -R -n PR0\n", prg); fprintf(stderr, "(Remove Full Overlay)\n"); fprintf(stderr, "%s -R -n full\n", prg); fprintf(stderr, "Note: %s -R is responsible for only removing the dtbo file from the livetree. it will not remove the PL logic from the FPGA region.\n", prg); fprintf(stderr, " \n"); } int gettime(struct timeval t0, struct timeval t1) { return ((t1.tv_sec - t0.tv_sec) * 1000.0f + (t1.tv_usec -t0.tv_usec) / 1000.0f); } int fpga_state() { FILE *fptr; char buf[10]; char *state_operating = "operating"; char *state_unknown = "unknown"; system("cat /sys/class/fpga_manager/fpga0/state >> state.txt"); fptr = fopen("state.txt", "r"); if (fptr) { fgets(buf, 10, fptr); fclose(fptr); system("rm state.txt"); if ((strncmp(buf, state_operating, 9) == 0) || (strncmp(buf, state_unknown, 7) == 0)) return 0; else return 1; } return 1; } static int fpga_overlay_check(char *cmd, char *state) { char buf[512]; FILE *fptr; int len; system(cmd); len = strlen(state) + 1; fptr = fopen("state.txt", "r"); if (fptr) { fgets(buf, len, fptr); fclose(fptr); system("rm state.txt"); if (!strcmp(buf, state)) return 0; else return 1; } return 1; } struct fpgaflag { char *flag; unsigned int value; }; static struct fpgaflag flagdump[] = { {.flag = "Full", .value = 0x0}, {.flag = "Partial", .value = 0x1}, {.flag = "AuthDDR", .value = 0x40}, {.flag = "AuthOCM", .value = 0x80}, {.flag = "EnUsrKey", .value = 0x20}, {.flag = "EnDevKey", .value = 0x4}, {.flag = "AuthEnUsrKeyDDR", .value = 0x60}, {.flag = "AuthEnUsrKeyOCM", .value = 0xA0}, {.flag = "AuthEnDevKeyDDR", .value = 0x44}, {.flag = "AuthEnDevKeyOCM", .value = 0x84}, {} }; static int cmd_flags(int argc, const char *name) { int valid_flag = 0; int flag = 0; struct fpgaflag *p = flagdump; while (p->flag) { if (!strcmp(name, p->flag)) { flag = p->value; break; } p++; } return flag; } static int isvalid_flags(int argc, const char *name, bool is_secure) { int valid_flag = 0; int count = 0; struct fpgaflag *p; if (!is_secure) p = flagdump; else p = &flagdump[2]; while (p->flag) { if (!strcmp(name, p->flag)) return 0; else if ((!is_secure) && (++count == 2)) return 1; p++; } return 1; } int main(int argc, char **argv) { int ret; int iszynqmp = fpga_getplatform(); char *binfile = NULL, *overlay = NULL, *AesKey = NULL, *flag = NULL, *partial_overlay = NULL; char *region = NULL, *Module[100] = {0}; int opt, flags = 0, flow = 0,rm_overlay = 0, readback_type = 0, sflags = 0; char command[2048], folder[512], *token, *tmp, *tmp1, *tmp2 , *tmp3; const char *filename = "readback", *name; struct stat sb; double time; struct timeval t1, t0; uid_t euid = geteuid(); if (euid) { printf("Error: This binary requires root access to execute. \n"); return 0; } if (argc == 1) { print_usage(basename(argv[0])); return 1; } while ((opt = getopt(argc, argv, "o:b:n:f:s:p:k:rt::Rh?:")) != -1) { switch (opt) { case 'o': overlay = optarg; if (!file_exists(overlay)) { printf("Error: User provided Overlay file doesn't exist\r\n"); return 1; } flow = OVERLAY; break; case 'b': binfile = optarg; if (!file_exists(binfile)) { printf("Error: User provided bitstream file doesn't exist\r\n"); return 1; } if (!(flow == OVERLAY)) flow = FPGA_SYSFS; break; case 'n': region = optarg; break; case 'f': if (flow == OVERLAY) { name = argv[6]; flags = cmd_flags(argc, name); } else if (flow == FPGA_SYSFS) { name = argv[4]; flags = cmd_flags(argc, name); } ret = isvalid_flags(argc, name, false); if (ret) { printf("Error: Invalid arugments :%s\n", strerror(1)); print_usage(basename(argv[0])); return -EINVAL; } flags += sflags; break; case 's': if (flow == OVERLAY) { name = argv[8]; sflags = cmd_flags(argc, name); } else if (flow == FPGA_SYSFS) { name = argv[6]; sflags = cmd_flags(argc, name); } ret = isvalid_flags(argc, name, true); if (ret) { printf("Error: Invalid arugments :%s\n", strerror(1)); print_usage(basename(argv[0])); return -EINVAL; } flags += sflags; break; case 'p': partial_overlay = optarg; break; case 'k': AesKey = optarg; break; case 't': if (optarg == NULL && argv[4] != NULL) readback_type = atoi(argv[4]); break; case 'r': if (optarg == NULL && argv[2] != NULL) filename = argv[2]; flow = READBACK; break; case 'R': rm_overlay = 1; break; case '?': case 'h': default: print_usage(basename(argv[0])); return 1; break; } } if(region != NULL) snprintf(folder, sizeof(folder), "/configfs/device-tree/overlays/%s", region); else if (!(flags & 1)) snprintf(folder, sizeof(folder), "/configfs/device-tree/overlays/full"); else if (overlay != NULL) { printf("Error: Provide valid Overlay region info\n\r"); return 1; } system("mkdir -p /lib/firmware"); if (rm_overlay) { if (((stat(folder, &sb) == 0) && S_ISDIR(sb.st_mode))) { snprintf(command, sizeof(command), "rmdir %s", folder); system(command); } return 0; } if (flow == OVERLAY) { if (((stat(folder, &sb) == 0) && S_ISDIR(sb.st_mode))) { printf("Error: Overlay already exists in the live tree\n\r"); return 1; } if (((stat("/configfs/device-tree/", &sb) == 0) && S_ISDIR(sb.st_mode))) { } else { system("mkdir /configfs"); system("mount -t configfs configfs /configfs"); } if (binfile != NULL) { snprintf(command, sizeof(command), "cp %s /lib/firmware", binfile); system(command); } snprintf(command, sizeof(command), "cp %s /lib/firmware", overlay); system(command); tmp = strdup(overlay); while((token = strsep(&tmp, "/"))) { tmp1 = token; } if (binfile != NULL) { snprintf(command, sizeof(command), "echo %x > /sys/class/fpga_manager/fpga0/flags", flags); system(command); if (ENCRYPTION_USERKEY_EN & flags) { snprintf(command, sizeof(command), "echo %s > /sys/class/fpga_manager/fpga0/key", AesKey); system(command); } } snprintf(command, sizeof(command), "mkdir %s", folder); system(command); snprintf(command, sizeof(command), "echo -n %s > %s/path", tmp1, folder); gettimeofday(&t0, NULL); system(command); gettimeofday(&t1, NULL); time = gettime(t0, t1); snprintf(command, sizeof(command), "cat %s/path >> state.txt", folder); ret = fpga_overlay_check(command, tmp1); if (ret) { printf("Failed to apply Overlay\n\r"); } /* Delete Bin file and DTBO file*/ snprintf(command, sizeof(command), "rm /lib/firmware/%s", tmp1); system(command); if (binfile != NULL) { tmp = strdup(binfile); while((token = strsep(&tmp, "/"))) { tmp1 = token; } snprintf(command, sizeof(command), "rm /lib/firmware/%s", tmp1); system(command); } /* FPGA state check */ if (binfile != NULL) { if (!fpga_state()) { printf("Time taken to load BIN is %f Milli Seconds\n\r", time); if (ret) { printf("BIN FILE loaded through FPGA manager successfull but failed to apply Overlay\n\r"); } else { printf("BIN FILE loaded through FPGA manager successfully\n\r"); } } else { printf("BIN FILE loading through FPGA manager failed\n\r"); } } } else if (flow == FPGA_SYSFS) { if (argc < 3) { printf("%s: For more information run %s -h\n", strerror(22), basename(argv[0])); return 1; } snprintf(command, sizeof(command), "cp %s /lib/firmware", binfile); system(command); snprintf(command, sizeof(command), "echo %x > /sys/class/fpga_manager/fpga0/flags", flags); system(command); if (ENCRYPTION_USERKEY_EN & flags) { snprintf(command, sizeof(command), "echo %s > /sys/class/fpga_manager/fpga0/key", AesKey); system(command); } tmp = strdup(binfile); while((token = strsep(&tmp, "/"))) { tmp1 = token; } snprintf(command, sizeof(command), "echo %s > /sys/class/fpga_manager/fpga0/firmware", tmp1); gettimeofday(&t0, NULL); system(command); gettimeofday(&t1, NULL); time = gettime(t0, t1); /* Delete Bin file and DTBO file*/ snprintf(command, sizeof(command), "rm /lib/firmware/%s", tmp1); system(command); /* FPGA state check */ if (!fpga_state()) { printf("Time taken to load BIN is %f Milli Seconds\n\r", time); printf("BIN FILE loaded through FPGA manager successfully\n\r"); } else { printf("BIN FILE loading through FPGA manager failed\n\r"); } } else if (flow == READBACK) { if (readback_type > 1) { printf("Invalid arugments :%s\n", strerror(1)); printf("For more information run %s -h\n", basename(argv[0])); return -EINVAL; } snprintf(command, sizeof(command), "echo %x > /sys/module/zynqmp_fpga/parameters/readback_type", readback_type); system(command); snprintf(command, sizeof(command), "cat /sys/kernel/debug/fpga/fpga0/image >> %s.bin", filename); system(command); printf("Readback contents are stored in the file %s.bin\n\r", filename); } return 0; }