diff options
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | cmd/fpga.c | 652 | ||||
-rw-r--r-- | drivers/fpga/Kconfig | 16 | ||||
-rw-r--r-- | test/py/tests/test_fpga.py | 552 |
4 files changed, 904 insertions, 324 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 64f6b38196..39d28e5d45 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -391,6 +391,14 @@ F: test/py/tests/test_efi* F: cmd/bootefi.c F: tools/file2include.c +FPGA +M: Michal Simek <michal.simek@xilinx.com> +S: Maintained +T: git git://git.denx.de/u-boot-microblaze.git +F: drivers/fpga/ +F: cmd/fpga.c +F: include/fpga.h + FLATTENED DEVICE TREE M: Simon Glass <sjg@chromium.org> S: Maintained diff --git a/cmd/fpga.c b/cmd/fpga.c index 74ae80c807..88a8e3f318 100644 --- a/cmd/fpga.c +++ b/cmd/fpga.c @@ -13,388 +13,408 @@ #include <fs.h> #include <malloc.h> -/* Local functions */ -static int fpga_get_op(char *opstr); - -/* Local defines */ -enum { - FPGA_NONE = -1, - FPGA_INFO, - FPGA_LOAD, - FPGA_LOADB, - FPGA_DUMP, - FPGA_LOADMK, - FPGA_LOADP, - FPGA_LOADBP, - FPGA_LOADFS, - FPGA_LOADS, -}; - -/* ------------------------------------------------------------------------- */ -/* command form: - * fpga <op> <device number> <data addr> <datasize> - * where op is 'load', 'dump', or 'info' - * If there is no device number field, the fpga environment variable is used. - * If there is no data addr field, the fpgadata environment variable is used. - * The info command requires no data address field. - */ -int do_fpga(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +static long do_fpga_get_device(char *arg) { - int op, dev = FPGA_INVALID_DEVICE; - size_t data_size = 0; - void *fpga_data = NULL; + long dev = FPGA_INVALID_DEVICE; char *devstr = env_get("fpga"); - char *datastr = env_get("fpgadata"); - int rc = FPGA_FAIL; - int wrong_parms = 0; -#if defined(CONFIG_FIT) - const char *fit_uname = NULL; - ulong fit_addr; -#endif -#if defined(CONFIG_CMD_FPGA_LOADFS) - fpga_fs_info fpga_fsinfo; - fpga_fsinfo.fstype = FS_TYPE_ANY; -#endif -#if defined(CONFIG_CMD_FPGA_LOAD_SECURE) - struct fpga_secure_info fpga_sec_info; - - memset(&fpga_sec_info, 0, sizeof(fpga_sec_info)); -#endif if (devstr) - dev = (int) simple_strtoul(devstr, NULL, 16); - if (datastr) - fpga_data = (void *)simple_strtoul(datastr, NULL, 16); + /* Should be strtol to handle -1 cases */ + dev = simple_strtol(devstr, NULL, 16); + + if (dev == FPGA_INVALID_DEVICE && arg) + dev = simple_strtol(arg, NULL, 16); - if (argc > 9 || argc < 2) { - debug("%s: Too many or too few args (%d)\n", __func__, argc); + debug("%s: device = %ld\n", __func__, dev); + + return dev; +} + +static int do_fpga_check_params(long *dev, long *fpga_data, size_t *data_size, + cmd_tbl_t *cmdtp, int argc, char *const argv[]) +{ + size_t local_data_size; + long local_fpga_data; + + debug("%s %d, %d\n", __func__, argc, cmdtp->maxargs); + + if (argc != cmdtp->maxargs) { + debug("fpga: incorrect parameters passed\n"); return CMD_RET_USAGE; } - op = (int)fpga_get_op(argv[1]); + *dev = do_fpga_get_device(argv[0]); - switch (op) { -#if defined(CONFIG_CMD_FPGA_LOADFS) - case FPGA_LOADFS: - if (argc < 9) - return CMD_RET_USAGE; - fpga_fsinfo.blocksize = (unsigned int) - simple_strtoul(argv[5], NULL, 16); - fpga_fsinfo.interface = argv[6]; - fpga_fsinfo.dev_part = argv[7]; - fpga_fsinfo.filename = argv[8]; - argc = 5; - break; -#endif -#if defined(CONFIG_CMD_FPGA_LOAD_SECURE) - case FPGA_LOADS: - if (argc < 7) - return CMD_RET_USAGE; - if (argc == 8) - fpga_sec_info.userkey_addr = (u8 *)(uintptr_t) - simple_strtoull(argv[7], - NULL, 16); - fpga_sec_info.encflag = (u8)simple_strtoul(argv[6], NULL, 16); - fpga_sec_info.authflag = (u8)simple_strtoul(argv[5], NULL, 16); - argc = 5; - break; -#endif - default: - break; + local_fpga_data = simple_strtol(argv[1], NULL, 16); + if (!local_fpga_data) { + debug("fpga: zero fpga_data address\n"); + return CMD_RET_USAGE; } + *fpga_data = local_fpga_data; - switch (argc) { - case 5: /* fpga <op> <dev> <data> <datasize> */ - data_size = simple_strtoul(argv[4], NULL, 16); + local_data_size = simple_strtoul(argv[2], NULL, 16); + if (!local_data_size) { + debug("fpga: zero size\n"); + return CMD_RET_USAGE; + } + *data_size = local_data_size; - case 4: /* fpga <op> <dev> <data> */ -#if defined(CONFIG_FIT) - if (fit_parse_subimage(argv[3], (ulong)fpga_data, - &fit_addr, &fit_uname)) { - fpga_data = (void *)fit_addr; - debug("* fpga: subimage '%s' from FIT image ", - fit_uname); - debug("at 0x%08lx\n", fit_addr); - } else -#endif - { - fpga_data = (void *)simple_strtoul(argv[3], NULL, 16); - debug("* fpga: cmdline image address = 0x%08lx\n", - (ulong)fpga_data); - } - debug("%s: fpga_data = 0x%lx\n", __func__, (ulong)fpga_data); + return 0; +} + +#if defined(CONFIG_CMD_FPGA_LOAD_SECURE) +int do_fpga_loads(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + struct fpga_secure_info fpga_sec_info; - case 3: /* fpga <op> <dev | data addr> */ - dev = (int)simple_strtoul(argv[2], NULL, 16); - debug("%s: device = %d\n", __func__, dev); - /* FIXME - this is a really weak test */ - if ((argc == 3) && (dev > fpga_count())) { - /* must be buffer ptr */ - debug("%s: Assuming buffer pointer in arg 3\n", - __func__); + memset(&fpga_sec_info, 0, sizeof(fpga_sec_info)); -#if defined(CONFIG_FIT) - if (fit_parse_subimage(argv[2], (ulong)fpga_data, - &fit_addr, &fit_uname)) { - fpga_data = (void *)fit_addr; - debug("* fpga: subimage '%s' from FIT image ", - fit_uname); - debug("at 0x%08lx\n", fit_addr); - } else -#endif - { - fpga_data = (void *)(uintptr_t)dev; - debug("* fpga: cmdline image addr = 0x%08lx\n", - (ulong)fpga_data); - } + if (argc < 5) { + debug("fpga: incorrect parameters passed\n"); + return CMD_RET_USAGE; + } - debug("%s: fpga_data = 0x%lx\n", - __func__, (ulong)fpga_data); - dev = FPGA_INVALID_DEVICE; /* reset device num */ - } + if (argc == 6) + fpga_sec_info.userkey_addr = (u8 *)(uintptr_t) + simple_strtoull(argv[5], + NULL, 16); + else + /* + * If 6th parameter is not passed then do_fpga_check_params + * will get 5 instead of expected 6 which means that function + * return CMD_RET_USAGE. Increase number of params +1 to pass + * this. + */ + argc++; + + fpga_sec_info.encflag = (u8)simple_strtoul(argv[4], NULL, 16); + fpga_sec_info.authflag = (u8)simple_strtoul(argv[3], NULL, 16); + + if (fpga_sec_info.authflag >= FPGA_NO_ENC_OR_NO_AUTH && + fpga_sec_info.encflag >= FPGA_NO_ENC_OR_NO_AUTH) { + debug("fpga: Use <fpga load> for NonSecure bitstream\n"); + return CMD_RET_USAGE; } - if (dev == FPGA_INVALID_DEVICE) { - puts("FPGA device not specified\n"); - op = FPGA_NONE; + if (fpga_sec_info.encflag == FPGA_ENC_USR_KEY && + !fpga_sec_info.userkey_addr) { + debug("fpga: User key not provided\n"); + return CMD_RET_USAGE; } - switch (op) { - case FPGA_NONE: - case FPGA_INFO: - break; -#if defined(CONFIG_CMD_FPGA_LOADFS) - case FPGA_LOADFS: - /* Blocksize can be zero */ - if (!fpga_fsinfo.interface || !fpga_fsinfo.dev_part || - !fpga_fsinfo.filename) - wrong_parms = 1; - break; -#endif -#if defined(CONFIG_CMD_FPGA_LOAD_SECURE) - case FPGA_LOADS: - if (fpga_sec_info.authflag >= FPGA_NO_ENC_OR_NO_AUTH && - fpga_sec_info.encflag >= FPGA_NO_ENC_OR_NO_AUTH) { - puts("ERR: use <fpga load> for NonSecure bitstream\n"); - wrong_parms = 1; - } + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; - if (fpga_sec_info.encflag == FPGA_ENC_USR_KEY && - !fpga_sec_info.userkey_addr) { - wrong_parms = 1; - puts("ERR:User key not provided\n"); - } - break; + return fpga_loads(dev, (void *)fpga_data, data_size, &fpga_sec_info); +} #endif - case FPGA_LOAD: - case FPGA_LOADP: - case FPGA_LOADB: - case FPGA_LOADBP: - case FPGA_DUMP: - if (!fpga_data || !data_size) - wrong_parms = 1; - break; -#if defined(CONFIG_CMD_FPGA_LOADMK) - case FPGA_LOADMK: - if (!fpga_data) - wrong_parms = 1; - break; + +#if defined(CONFIG_CMD_FPGA_LOADFS) +static int do_fpga_loadfs(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + fpga_fs_info fpga_fsinfo; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + fpga_fsinfo.fstype = FS_TYPE_ANY; + fpga_fsinfo.blocksize = (unsigned int)simple_strtoul(argv[3], NULL, 16); + fpga_fsinfo.interface = argv[4]; + fpga_fsinfo.dev_part = argv[5]; + fpga_fsinfo.filename = argv[6]; + + return fpga_fsload(dev, (void *)fpga_data, data_size, &fpga_fsinfo); +} #endif - } - if (wrong_parms) { - puts("Wrong parameters for FPGA request\n"); - op = FPGA_NONE; - } +static int do_fpga_info(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + long dev = do_fpga_get_device(argv[0]); - switch (op) { - case FPGA_NONE: - return CMD_RET_USAGE; + return fpga_info(dev); +} + +static int do_fpga_dump(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + return fpga_dump(dev, (void *)fpga_data, data_size); +} - case FPGA_INFO: - rc = fpga_info(dev); - break; +static int do_fpga_load(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; - case FPGA_LOAD: - rc = fpga_load(dev, fpga_data, data_size, BIT_FULL); - break; + return fpga_load(dev, (void *)fpga_data, data_size, BIT_FULL); +} + +static int do_fpga_loadb(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + return fpga_loadbitstream(dev, (void *)fpga_data, data_size, BIT_FULL); +} #if defined(CONFIG_CMD_FPGA_LOADP) - case FPGA_LOADP: - rc = fpga_load(dev, fpga_data, data_size, BIT_PARTIAL); - break; -#endif +static int do_fpga_loadp(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; - case FPGA_LOADB: - rc = fpga_loadbitstream(dev, fpga_data, data_size, BIT_FULL); - break; + return fpga_load(dev, (void *)fpga_data, data_size, BIT_PARTIAL); +} +#endif #if defined(CONFIG_CMD_FPGA_LOADBP) - case FPGA_LOADBP: - rc = fpga_loadbitstream(dev, fpga_data, data_size, BIT_PARTIAL); - break; +static int do_fpga_loadbp(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + size_t data_size = 0; + long fpga_data, dev; + int ret; + + ret = do_fpga_check_params(&dev, &fpga_data, &data_size, + cmdtp, argc, argv); + if (ret) + return ret; + + return fpga_loadbitstream(dev, (void *)fpga_data, data_size, + BIT_PARTIAL); +} #endif -#if defined(CONFIG_CMD_FPGA_LOADFS) - case FPGA_LOADFS: - rc = fpga_fsload(dev, fpga_data, data_size, &fpga_fsinfo); - break; +#if defined(CONFIG_CMD_FPGA_LOADMK) +static int do_fpga_loadmk(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + size_t data_size = 0; + void *fpga_data = NULL; +#if defined(CONFIG_FIT) + const char *fit_uname = NULL; + ulong fit_addr; #endif + ulong dev = do_fpga_get_device(argv[0]); + char *datastr = env_get("fpgadata"); -#if defined(CONFIG_CMD_FPGA_LOAD_SECURE) - case FPGA_LOADS: - rc = fpga_loads(dev, fpga_data, data_size, &fpga_sec_info); - break; + debug("fpga: argc %x, dev %lx, datastr %s\n", argc, dev, datastr); + + if (dev == FPGA_INVALID_DEVICE) { + debug("fpga: Invalid fpga device\n"); + return CMD_RET_USAGE; + } + + if (argc == 0 && !datastr) { + debug("fpga: No datastr passed\n"); + return CMD_RET_USAGE; + } + + if (argc == 2) { + datastr = argv[1]; + debug("fpga: Full command with two args\n"); + } else if (argc == 1 && !datastr) { + debug("fpga: Dev is setup - fpgadata passed\n"); + datastr = argv[0]; + } + +#if defined(CONFIG_FIT) + if (fit_parse_subimage(datastr, (ulong)fpga_data, + &fit_addr, &fit_uname)) { + fpga_data = (void *)fit_addr; + debug("* fpga: subimage '%s' from FIT image ", + fit_uname); + debug("at 0x%08lx\n", fit_addr); + } else #endif + { + fpga_data = (void *)simple_strtoul(datastr, NULL, 16); + debug("* fpga: cmdline image address = 0x%08lx\n", + (ulong)fpga_data); + } + debug("%s: fpga_data = 0x%lx\n", __func__, (ulong)fpga_data); + if (!fpga_data) { + puts("Zero fpga_data address\n"); + return CMD_RET_USAGE; + } -#if defined(CONFIG_CMD_FPGA_LOADMK) - case FPGA_LOADMK: - switch (genimg_get_format(fpga_data)) { + switch (genimg_get_format(fpga_data)) { #if defined(CONFIG_IMAGE_FORMAT_LEGACY) - case IMAGE_FORMAT_LEGACY: - { - image_header_t *hdr = - (image_header_t *)fpga_data; - ulong data; - uint8_t comp; - - comp = image_get_comp(hdr); - if (comp == IH_COMP_GZIP) { + case IMAGE_FORMAT_LEGACY: + { + image_header_t *hdr = (image_header_t *)fpga_data; + ulong data; + u8 comp; + + comp = image_get_comp(hdr); + if (comp == IH_COMP_GZIP) { #if defined(CONFIG_GZIP) - ulong image_buf = image_get_data(hdr); - data = image_get_load(hdr); - ulong image_size = ~0UL; - - if (gunzip((void *)data, ~0UL, - (void *)image_buf, - &image_size) != 0) { - puts("GUNZIP: error\n"); - return 1; - } - data_size = image_size; + ulong image_buf = image_get_data(hdr); + ulong image_size = ~0UL; + + data = image_get_load(hdr); + + if (gunzip((void *)data, ~0UL, (void *)image_buf, + &image_size) != 0) { + puts("GUNZIP: error\n"); + return CMD_RET_FAILURE; + } + data_size = image_size; #else - puts("Gunzip image is not supported\n"); - return 1; + puts("Gunzip image is not supported\n"); + return 1; #endif - } else { - data = (ulong)image_get_data(hdr); - data_size = image_get_data_size(hdr); - } - rc = fpga_load(dev, (void *)data, data_size, - BIT_FULL); - } - break; + } else { + data = (ulong)image_get_data(hdr); + data_size = image_get_data_size(hdr); + } + return fpga_load(dev, (void *)data, data_size, + BIT_FULL); + } #endif #if defined(CONFIG_FIT) - case IMAGE_FORMAT_FIT: - { - const void *fit_hdr = (const void *)fpga_data; - int noffset; - const void *fit_data; - - if (fit_uname == NULL) { - puts("No FIT subimage unit name\n"); - return 1; - } - - if (!fit_check_format(fit_hdr)) { - puts("Bad FIT image format\n"); - return 1; - } - - /* get fpga component image node offset */ - noffset = fit_image_get_node(fit_hdr, - fit_uname); - if (noffset < 0) { - printf("Can't find '%s' FIT subimage\n", - fit_uname); - return 1; - } - - /* verify integrity */ - if (!fit_image_verify(fit_hdr, noffset)) { - puts ("Bad Data Hash\n"); - return 1; - } - - /* get fpga subimage data address and length */ - if (fit_image_get_data(fit_hdr, noffset, - &fit_data, &data_size)) { - puts("Fpga subimage data not found\n"); - return 1; - } - - rc = fpga_load(dev, fit_data, data_size, - BIT_FULL); - } - break; -#endif - default: - puts("** Unknown image type\n"); - rc = FPGA_FAIL; - break; + case IMAGE_FORMAT_FIT: + { + const void *fit_hdr = (const void *)fpga_data; + int noffset; + const void *fit_data; + + if (!fit_uname) { + puts("No FIT subimage unit name\n"); + return CMD_RET_FAILURE; + } + + if (!fit_check_format(fit_hdr)) { + puts("Bad FIT image format\n"); + return CMD_RET_FAILURE; } - break; -#endif - case FPGA_DUMP: - rc = fpga_dump(dev, fpga_data, data_size); - break; + /* get fpga component image node offset */ + noffset = fit_image_get_node(fit_hdr, fit_uname); + if (noffset < 0) { + printf("Can't find '%s' FIT subimage\n", fit_uname); + return CMD_RET_FAILURE; + } + /* verify integrity */ + if (!fit_image_verify(fit_hdr, noffset)) { + puts("Bad Data Hash\n"); + return CMD_RET_FAILURE; + } + + /* get fpga subimage data address and length */ + if (fit_image_get_data(fit_hdr, noffset, &fit_data, + &data_size)) { + puts("Fpga subimage data not found\n"); + return CMD_RET_FAILURE; + } + + return fpga_load(dev, fit_data, data_size, BIT_FULL); + } +#endif default: - printf("Unknown operation\n"); - return CMD_RET_USAGE; + puts("** Unknown image type\n"); + return CMD_RET_FAILURE; } - return rc; } +#endif -/* - * Map op to supported operations. We don't use a table since we - * would just have to relocate it from flash anyway. - */ -static int fpga_get_op(char *opstr) -{ - int op = FPGA_NONE; - - if (!strcmp("info", opstr)) - op = FPGA_INFO; - else if (!strcmp("loadb", opstr)) - op = FPGA_LOADB; - else if (!strcmp("load", opstr)) - op = FPGA_LOAD; +static cmd_tbl_t fpga_commands[] = { + U_BOOT_CMD_MKENT(info, 1, 1, do_fpga_info, "", ""), + U_BOOT_CMD_MKENT(dump, 3, 1, do_fpga_dump, "", ""), + U_BOOT_CMD_MKENT(load, 3, 1, do_fpga_load, "", ""), + U_BOOT_CMD_MKENT(loadb, 3, 1, do_fpga_loadb, "", ""), #if defined(CONFIG_CMD_FPGA_LOADP) - else if (!strcmp("loadp", opstr)) - op = FPGA_LOADP; + U_BOOT_CMD_MKENT(loadp, 3, 1, do_fpga_loadp, "", ""), #endif #if defined(CONFIG_CMD_FPGA_LOADBP) - else if (!strcmp("loadbp", opstr)) - op = FPGA_LOADBP; + U_BOOT_CMD_MKENT(loadbp, 3, 1, do_fpga_loadbp, "", ""), #endif #if defined(CONFIG_CMD_FPGA_LOADFS) - else if (!strcmp("loadfs", opstr)) - op = FPGA_LOADFS; + U_BOOT_CMD_MKENT(loadfs, 7, 1, do_fpga_loadfs, "", ""), #endif #if defined(CONFIG_CMD_FPGA_LOADMK) - else if (!strcmp("loadmk", opstr)) - op = FPGA_LOADMK; + U_BOOT_CMD_MKENT(loadmk, 2, 1, do_fpga_loadmk, "", ""), #endif - else if (!strcmp("dump", opstr)) - op = FPGA_DUMP; #if defined(CONFIG_CMD_FPGA_LOAD_SECURE) - else if (!strcmp("loads", opstr)) - op = FPGA_LOADS; + U_BOOT_CMD_MKENT(loads, 6, 1, do_fpga_loads, "", ""), #endif +}; + +static int do_fpga_wrapper(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + cmd_tbl_t *fpga_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + fpga_cmd = find_cmd_tbl(argv[1], fpga_commands, + ARRAY_SIZE(fpga_commands)); + if (!fpga_cmd) { + debug("fpga: non existing command\n"); + return CMD_RET_USAGE; + } + + argc -= 2; + argv += 2; + + if (argc > fpga_cmd->maxargs) { + debug("fpga: more parameters passed\n"); + return CMD_RET_USAGE; + } - if (op == FPGA_NONE) - printf("Unknown fpga operation \"%s\"\n", opstr); + ret = fpga_cmd->cmd(fpga_cmd, flag, argc, argv); - return op; + return cmd_process_error(fpga_cmd, ret); } #if defined(CONFIG_CMD_FPGA_LOADFS) || defined(CONFIG_CMD_FPGA_LOAD_SECURE) -U_BOOT_CMD(fpga, 9, 1, do_fpga, +U_BOOT_CMD(fpga, 9, 1, do_fpga_wrapper, #else -U_BOOT_CMD(fpga, 6, 1, do_fpga, +U_BOOT_CMD(fpga, 6, 1, do_fpga_wrapper, #endif "loadable FPGA image support", "[operation type] [device number] [image address] [image size]\n" diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index d36c4c5e28..50e901973d 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -46,15 +46,15 @@ config FPGA_ZYNQMPPL on Xilinx Zynq UltraScale+ (ZynqMP) device. config FPGA_SPARTAN3 - bool "Enable Spartan3 FPGA driver" - help - Enable Spartan3 FPGA driver for loading in BIT format. + bool "Enable Spartan3 FPGA driver" + help + Enable Spartan3 FPGA driver for loading in BIT format. config FPGA_ZYNQPL - bool "Enable Xilinx FPGA for Zynq" - depends on ARCH_ZYNQ - help - Enable FPGA driver for loading bitstream in BIT and BIN format - on Xilinx Zynq devices. + bool "Enable Xilinx FPGA for Zynq" + depends on ARCH_ZYNQ + help + Enable FPGA driver for loading bitstream in BIT and BIN format + on Xilinx Zynq devices. endmenu diff --git a/test/py/tests/test_fpga.py b/test/py/tests/test_fpga.py new file mode 100644 index 0000000000..7459ce5867 --- /dev/null +++ b/test/py/tests/test_fpga.py @@ -0,0 +1,552 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2018, Xilinx Inc. +# +# Michal Simek +# Siva Durga Prasad Paladugu + +import pytest +import re +import random +import u_boot_utils + +""" +Note: This test relies on boardenv_* containing configuration values to define +the network available and files to be used for testing. Without this, this test +will be automatically skipped. + +For example: + +# True if a DHCP server is attached to the network, and should be tested. +env__net_dhcp_server = True + +# A list of environment variables that should be set in order to configure a +# static IP. In this test case we atleast need serverip for performing tftpb +# to get required files. +env__net_static_env_vars = [ + ("ipaddr", "10.0.0.100"), + ("netmask", "255.255.255.0"), + ("serverip", "10.0.0.1"), +] + +# Details regarding the files that may be read from a TFTP server. . +env__fpga_secure_readable_file = { + "fn": "auth_bhdr_ppk1_bit.bin", + "enckupfn": "auth_bhdr_enc_kup_load_bit.bin", + "addr": 0x1000000, + "keyaddr": 0x100000, + "keyfn": "key.txt", +} + +env__fpga_under_test = { + "dev": 0, + "addr" : 0x1000000, + "bitstream_load": "compress.bin", + "bitstream_load_size": 1831960, + "bitstream_loadp": "compress_pr.bin", + "bitstream_loadp_size": 423352, + "bitstream_loadb": "compress.bit", + "bitstream_loadb_size": 1832086, + "bitstream_loadbp": "compress_pr.bit", + "bitstream_loadbp_size": 423491, + "mkimage_legacy": "download.ub", + "mkimage_legacy_size": 13321468, + "mkimage_legacy_gz": "download.gz.ub", + "mkimage_legacy_gz_size": 53632, + "mkimage_fit": "download-fit.ub", + "mkimage_fit_size": 13322784, + "loadfs": "mmc 0 compress.bin", + "loadfs_size": 1831960, + "loadfs_block_size": 0x10000, +} +""" + +import test_net + +def check_dev(u_boot_console): + f = u_boot_console.config.env.get('env__fpga_under_test', None) + if not f: + pytest.skip('No FPGA to test') + + dev = f.get('dev', -1) + if dev < 0: + pytest.fail('No dev specified via env__fpga_under_test') + + return dev, f + +def load_file_from_var(u_boot_console, name): + dev, f = check_dev(u_boot_console) + + addr = f.get('addr', -1) + if addr < 0: + pytest.fail('No address specified via env__fpga_under_test') + + test_net.test_net_dhcp(u_boot_console) + test_net.test_net_setup_static(u_boot_console) + bit = f['%s' % (name)] + bit_size = f['%s_size' % (name)] + + expected_tftp = 'Bytes transferred = %d' % bit_size + output = u_boot_console.run_command('tftpboot %x %s' % (addr, bit)) + assert expected_tftp in output + + return f, dev, addr, bit, bit_size + +###### FPGA FAIL test ###### +expected_usage = 'fpga - loadable FPGA image support' + +@pytest.mark.xfail +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_fail(u_boot_console): + # Test non valid fpga subcommand + expected = 'fpga: non existing command' + output = u_boot_console.run_command('fpga broken 0') + #assert expected in output + assert expected_usage in output + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_help(u_boot_console): + # Just show help + output = u_boot_console.run_command('fpga') + assert expected_usage in output + + +###### FPGA DUMP tests ###### + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_dump(u_boot_console): + pytest.skip('Not implemented now') + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_dump_variable(u_boot_console): + # Same as above but via "fpga" variable + pytest.skip('Not implemented now') + +###### FPGA INFO tests ###### + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_info_fail(u_boot_console): + # Maybe this can be skipped completely + dev, f = check_dev(u_boot_console) + + # Multiple parameters to fpga info should fail + expected = 'fpga: more parameters passed' + output = u_boot_console.run_command('fpga info 0 0') + #assert expected in output + assert expected_usage in output + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_info_list(u_boot_console): + # Maybe this can be skipped completely + dev, f = check_dev(u_boot_console) + + # Code is design in a way that if fpga dev is not passed it should + # return list of all fpga devices in the system + u_boot_console.run_command('setenv fpga') + output = u_boot_console.run_command('fpga info') + assert expected_usage not in output + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_info(u_boot_console): + dev, f = check_dev(u_boot_console) + + output = u_boot_console.run_command('fpga info %x' % (dev)) + assert expected_usage not in output + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_info_variable(u_boot_console): + dev, f = check_dev(u_boot_console) + + # + # fpga variable is storing device number which doesn't need to be passed + # + u_boot_console.run_command('setenv fpga %x' % (dev)) + + output = u_boot_console.run_command('fpga info') + # Variable cleanup + u_boot_console.run_command('setenv fpga') + assert expected_usage not in output + +###### FPGA LOAD tests ###### + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_load_fail(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load') + + for cmd in ['dump', 'load', 'loadb']: + # missing dev parameter + expected = 'fpga: incorrect parameters passed' + output = u_boot_console.run_command('fpga %s %x $filesize' % (cmd, addr)) + #assert expected in output + assert expected_usage in output + + # more parameters - 0 at the end + expected = 'fpga: more parameters passed' + output = u_boot_console.run_command('fpga %s %x %x $filesize 0' % (cmd, dev, addr)) + #assert expected in output + assert expected_usage in output + + # 0 address + expected = 'fpga: zero fpga_data address' + output = u_boot_console.run_command('fpga %s %x 0 $filesize' % (cmd, dev)) + #assert expected in output + assert expected_usage in output + + # 0 filesize + expected = 'fpga: zero size' + output = u_boot_console.run_command('fpga %s %x %x 0' % (cmd, dev, addr)) + #assert expected in output + assert expected_usage in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_load(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga load %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadp') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadp(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga load %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + + # And load also partial bistream + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadp') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadp %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadb(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadb') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadb %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadbp') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadbp(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadb') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadb %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + + # And load also partial bistream in bit format + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadbp') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadbp %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +###### FPGA LOADMK tests ###### + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +def test_fpga_loadmk_fail(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy') + + u_boot_console.run_command('imi %x' % (addr)) + + # load image but pass incorrect address to show error message + expected = 'Unknown image type' + output = u_boot_console.run_command('fpga loadmk %x %x' % (dev, addr + 0x10)) + assert expected in output + + # Pass more parameters then command expects - 0 at the end + output = u_boot_console.run_command('fpga loadmk %x %x 0' % (dev, addr)) + #assert expected in output + assert expected_usage in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +def test_fpga_loadmk_legacy(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy') + + u_boot_console.run_command('imi %x' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x %x && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.xfail +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +def test_fpga_loadmk_legacy_variable_fpga(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy') + + u_boot_console.run_command('imi %x' % (addr)) + + u_boot_console.run_command('setenv fpga %x' % (dev)) + + # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (addr, expected_text)) + u_boot_console.run_command('setenv fpga') + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +def test_fpga_loadmk_legacy_variable_fpgadata(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy') + + u_boot_console.run_command('imi %x' % (addr)) + + u_boot_console.run_command('setenv fpgadata %x' % (addr)) + + # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (dev, expected_text)) + u_boot_console.run_command('setenv fpgadata') + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +def test_fpga_loadmk_legacy_variable(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy') + + u_boot_console.run_command('imi %x' % (addr)) + + u_boot_console.run_command('setenv fpga %x' % (dev)) + u_boot_console.run_command('setenv fpgadata %x' % (addr)) + + # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk && echo %s' % (expected_text)) + u_boot_console.run_command('setenv fpga') + u_boot_console.run_command('setenv fpgadata') + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +@pytest.mark.buildconfigspec('gzip') +def test_fpga_loadmk_legacy_gz(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy_gz') + + u_boot_console.run_command('imi %x' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x %x && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadmk_fit(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit') + + u_boot_console.run_command('imi %x' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x %x:fpga && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadmk_fit_variable_fpga(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit') + + u_boot_console.run_command('imi %x' % (addr)) + # FIXME this should fail - broken support in past + u_boot_console.run_command('setenv fpga %x' % (dev)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x:fpga && echo %s' % (addr, expected_text)) + u_boot_console.run_command('setenv fpga') + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadmk_fit_variable_fpgadata(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit') + + u_boot_console.run_command('imi %x' % (addr)) + # FIXME this should fail - broken support in past + u_boot_console.run_command('setenv fpgadata %x:fpga' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (dev, expected_text)) + u_boot_console.run_command('setenv fpgadata') + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadmk_fit_variable(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit') + + u_boot_console.run_command('imi %x' % (addr)) + + u_boot_console.run_command('setenv fpga %x' % (dev)) + u_boot_console.run_command('setenv fpgadata %x:fpga' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk && echo %s' % (expected_text)) + u_boot_console.run_command('setenv fpga') + u_boot_console.run_command('setenv fpgadata') + assert expected_text in output + +###### FPGA LOAD tests ###### + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_loadfs_fail(u_boot_console): + dev, f = check_dev(u_boot_console) + + addr = f.get('addr', -1) + if addr < 0: + pytest.fail('No address specified via env__fpga_under_test') + + bit = f['loadfs'] + bit_size = f['loadfs_size'] + block_size = f['loadfs_block_size'] + + # less params - dev number removed + expected = 'fpga: incorrect parameters passed' + output = u_boot_console.run_command('fpga loadfs %x %x %x %s' % (addr, bit_size, block_size, bit)) + #assert expected in output + assert expected_usage in output + + # one more param - 0 at the end + # This is the longest command that's why there is no message from cmd/fpga.c + output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s 0' % (dev, addr, bit_size, block_size, bit)) + assert expected_usage in output + + # zero address 0 + expected = 'fpga: zero fpga_data address' + output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, 0, bit_size, block_size, bit)) + #assert expected in output + assert expected_usage in output + + # bit_size 0 + expected = 'fpga: zero size' + output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, addr, 0, block_size, bit)) + #assert expected in output + assert expected_usage in output + + # block size 0 + # FIXME this should pass but it failing too + output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, addr, bit_size, 0, bit)) + assert expected_usage in output + + # non existing bitstream name + expected = 'Unable to read file noname' + output = u_boot_console.run_command('fpga loadfs %x %x %x %x mmc 0 noname' % (dev, addr, bit_size, block_size)) + assert expected in output + assert expected_usage in output + + # -1 dev number + expected = 'fpga_fsload: Invalid device number -1' + output = u_boot_console.run_command('fpga loadfs %d %x %x %x mmc 0 noname' % (-1, addr, bit_size, block_size)) + assert expected in output + assert expected_usage in output + + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadfs(u_boot_console): + dev, f = check_dev(u_boot_console) + + addr = f.get('addr', -1) + if addr < 0: + pytest.fail('No address specified via env__fpga_under_test') + + bit = f['loadfs'] + bit_size = f['loadfs_size'] + block_size = f['loadfs_block_size'] + + # This should be done better + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s && echo %s' % (dev, addr, bit_size, block_size, bit, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_load_secure') +@pytest.mark.buildconfigspec('cmd_net') +@pytest.mark.buildconfigspec('cmd_dhcp') +@pytest.mark.buildconfigspec('net') +def test_fpga_secure_bit_auth(u_boot_console): + + test_net.test_net_dhcp(u_boot_console) + test_net.test_net_setup_static(u_boot_console) + + f = u_boot_console.config.env.get('env__fpga_secure_readable_file', None) + if not f: + pytest.skip('No TFTP readable file to read') + + addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(u_boot_console) + + expected_tftp = 'Bytes transferred = ' + fn = f['fn'] + output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn)) + assert expected_tftp in output + + expected_zynqmpsecure = 'Bitstream successfully loaded' + output = u_boot_console.run_command('fpga loads 0 %x $filesize 0 2' % (addr)) + assert expected_zynqmpsecure in output + + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_load_secure') +@pytest.mark.buildconfigspec('cmd_net') +@pytest.mark.buildconfigspec('cmd_dhcp') +@pytest.mark.buildconfigspec('net') +def test_fpga_secure_bit_img_auth_kup(u_boot_console): + + test_net.test_net_dhcp(u_boot_console) + test_net.test_net_setup_static(u_boot_console) + + f = u_boot_console.config.env.get('env__fpga_secure_readable_file', None) + if not f: + pytest.skip('No TFTP readable file to read') + + keyaddr = f.get('keyaddr', None) + if not keyaddr: + addr = u_boot_utils.find_ram_base(u_boot_console) + expected_tftp = 'Bytes transferred = ' + keyfn = f['keyfn'] + output = u_boot_console.run_command('tftpboot %x %s' % (keyaddr, keyfn)) + assert expected_tftp in output + + addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(u_boot_console) + expected_tftp = 'Bytes transferred = ' + fn = f['enckupfn'] + output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn)) + assert expected_tftp in output + + expected_zynqmpsecure = 'Bitstream successfully loaded' + output = u_boot_console.run_command('fpga loads 0 %x $filesize 0 1 %x' % (addr, keyaddr)) + assert expected_zynqmpsecure in output |