// SPDX-License-Identifier: GPL-2.0 /* * (C) Copyright 2015 - 2016, Xilinx, Inc, * Michal Simek * Siva Durga Prasad */ #include #include #include #include #include #include #define DUMMY_WORD 0xffffffff /* Xilinx binary format header */ static const u32 bin_format[] = { DUMMY_WORD, /* Dummy words */ DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, DUMMY_WORD, 0x000000bb, /* Sync word */ 0x11220044, /* Sync word */ DUMMY_WORD, DUMMY_WORD, 0xaa995566, /* Sync word */ }; #define SWAP_NO 1 #define SWAP_DONE 2 /* * Load the whole word from unaligned buffer * Keep in your mind that it is byte loading on little-endian system */ static u32 load_word(const void *buf, u32 swap) { u32 word = 0; u8 *bitc = (u8 *)buf; int p; if (swap == SWAP_NO) { for (p = 0; p < 4; p++) { word <<= 8; word |= bitc[p]; } } else { for (p = 3; p >= 0; p--) { word <<= 8; word |= bitc[p]; } } return word; } static u32 check_header(const void *buf) { u32 i, pattern; int swap = SWAP_NO; u32 *test = (u32 *)buf; debug("%s: Let's check bitstream header\n", __func__); /* Checking that passing bin is not a bitstream */ for (i = 0; i < ARRAY_SIZE(bin_format); i++) { pattern = load_word(&test[i], swap); /* * Bitstreams in binary format are swapped * compare to regular bistream. * Do not swap dummy word but if swap is done assume * that parsing buffer is binary format */ if ((__swab32(pattern) != DUMMY_WORD) && (__swab32(pattern) == bin_format[i])) { swap = SWAP_DONE; debug("%s: data swapped - let's swap\n", __func__); } debug("%s: %d/%px: pattern %x/%x bin_format\n", __func__, i, &test[i], pattern, bin_format[i]); } debug("%s: Found bitstream header at %px %s swapinng\n", __func__, buf, swap == SWAP_NO ? "without" : "with"); return swap; } static void *check_data(u8 *buf, size_t bsize, u32 *swap) { u32 word, p = 0; /* possition */ /* Because buf doesn't need to be aligned let's read it by chars */ for (p = 0; p < bsize; p++) { word = load_word(&buf[p], SWAP_NO); debug("%s: word %x %x/%px\n", __func__, word, p, &buf[p]); /* Find the first bitstream dummy word */ if (word == DUMMY_WORD) { debug("%s: Found dummy word at position %x/%px\n", __func__, p, &buf[p]); *swap = check_header(&buf[p]); if (*swap) { /* FIXME add full bitstream checking here */ return &buf[p]; } } /* Loop can be huge - support CTRL + C */ if (ctrlc()) return NULL; } return NULL; } static ulong zynqmp_align_dma_buffer(u32 *buf, u32 len, u32 swap) { u32 *new_buf; u32 i; if ((ulong)buf != ALIGN((ulong)buf, ARCH_DMA_MINALIGN)) { new_buf = (u32 *)ALIGN((ulong)buf, ARCH_DMA_MINALIGN); /* * This might be dangerous but permits to flash if * ARCH_DMA_MINALIGN is greater than header size */ if (new_buf > (u32 *)buf) { debug("%s: Aligned buffer is after buffer start\n", __func__); new_buf -= ARCH_DMA_MINALIGN; } printf("%s: Align buffer at %px to %px(swap %d)\n", __func__, buf, new_buf, swap); for (i = 0; i < (len/4); i++) new_buf[i] = load_word(&buf[i], swap); buf = new_buf; } else if ((swap != SWAP_DONE) && (zynqmp_pmufw_version() <= PMUFW_V1_0)) { /* For bitstream which are aligned */ new_buf = buf; printf("%s: Bitstream is not swapped(%d) - swap it\n", __func__, swap); for (i = 0; i < (len/4); i++) new_buf[i] = load_word(&buf[i], swap); } return (ulong)buf; } static int zynqmp_validate_bitstream(xilinx_desc *desc, const void *buf, size_t bsize, u32 blocksize, u32 *swap) { ulong *buf_start; ulong diff; buf_start = check_data((u8 *)buf, blocksize, swap); if (!buf_start) return FPGA_FAIL; /* Check if data is postpone from start */ diff = (ulong)buf_start - (ulong)buf; if (diff) { printf("%s: Bitstream is not validated yet (diff %lx)\n", __func__, diff); return FPGA_FAIL; } if ((ulong)buf < SZ_1M) { printf("%s: Bitstream has to be placed up to 1MB (%px)\n", __func__, buf); return FPGA_FAIL; } return 0; } static int zynqmp_load(xilinx_desc *desc, const void *buf, size_t bsize, bitstream_type bstype) { ALLOC_CACHE_ALIGN_BUFFER(u32, bsizeptr, 1); u32 swap = 0; ulong bin_buf; int ret; u32 buf_lo, buf_hi; u32 ret_payload[PAYLOAD_ARG_CNT]; bool xilfpga_old = false; if (zynqmp_pmufw_version() <= PMUFW_V1_0) { puts("WARN: PMUFW v1.0 or less is detected\n"); puts("WARN: Not all bitstream formats are supported\n"); puts("WARN: Please upgrade PMUFW\n"); xilfpga_old = true; if (zynqmp_validate_bitstream(desc, buf, bsize, bsize, &swap)) return FPGA_FAIL; bsizeptr = (u32 *)&bsize; flush_dcache_range((ulong)bsizeptr, (ulong)bsizeptr + sizeof(size_t)); bstype |= BIT(ZYNQMP_FPGA_BIT_NS); } bin_buf = zynqmp_align_dma_buffer((u32 *)buf, bsize, swap); debug("%s called!\n", __func__); flush_dcache_range(bin_buf, bin_buf + bsize); buf_lo = (u32)bin_buf; buf_hi = upper_32_bits(bin_buf); if (xilfpga_old) ret = invoke_smc(ZYNQMP_SIP_SVC_PM_FPGA_LOAD, buf_lo, buf_hi, (u32)(uintptr_t)bsizeptr, bstype, ret_payload); else ret = invoke_smc(ZYNQMP_SIP_SVC_PM_FPGA_LOAD, buf_lo, buf_hi, (u32)bsize, 0, ret_payload); if (ret) puts("PL FPGA LOAD fail\n"); return ret; } #if defined(CONFIG_CMD_FPGA_LOAD_SECURE) && !defined(CONFIG_SPL_BUILD) static int zynqmp_loads(xilinx_desc *desc, const void *buf, size_t bsize, struct fpga_secure_info *fpga_sec_info) { int ret; u32 buf_lo, buf_hi; u32 ret_payload[PAYLOAD_ARG_CNT]; u8 flag = 0; flush_dcache_range((ulong)buf, (ulong)buf + ALIGN(bsize, CONFIG_SYS_CACHELINE_SIZE)); if (!fpga_sec_info->encflag) flag |= BIT(ZYNQMP_FPGA_BIT_ENC_DEV_KEY); if (fpga_sec_info->userkey_addr && fpga_sec_info->encflag == FPGA_ENC_USR_KEY) { flush_dcache_range((ulong)fpga_sec_info->userkey_addr, (ulong)fpga_sec_info->userkey_addr + ALIGN(KEY_PTR_LEN, CONFIG_SYS_CACHELINE_SIZE)); flag |= BIT(ZYNQMP_FPGA_BIT_ENC_USR_KEY); } if (!fpga_sec_info->authflag) flag |= BIT(ZYNQMP_FPGA_BIT_AUTH_OCM); if (fpga_sec_info->authflag == ZYNQMP_FPGA_AUTH_DDR) flag |= BIT(ZYNQMP_FPGA_BIT_AUTH_DDR); buf_lo = lower_32_bits((ulong)buf); buf_hi = upper_32_bits((ulong)buf); ret = invoke_smc(ZYNQMP_SIP_SVC_PM_FPGA_LOAD, buf_lo, buf_hi, (u32)(uintptr_t)fpga_sec_info->userkey_addr, flag, ret_payload); if (ret) puts("PL FPGA LOAD fail\n"); else puts("Bitstream successfully loaded\n"); return ret; } #endif static int zynqmp_pcap_info(xilinx_desc *desc) { int ret; u32 ret_payload[PAYLOAD_ARG_CNT]; ret = invoke_smc(ZYNQMP_SIP_SVC_PM_FPGA_STATUS, 0, 0, 0, 0, ret_payload); if (!ret) printf("PCAP status\t0x%x\n", ret_payload[1]); return ret; } struct xilinx_fpga_op zynqmp_op = { .load = zynqmp_load, #if defined CONFIG_CMD_FPGA_LOAD_SECURE .loads = zynqmp_loads, #endif .info = zynqmp_pcap_info, };