// Copyright(c) 2018-2020, Intel Corporation // // 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 Intel Corporation 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. #ifdef HAVE_CONFIG_H #include #endif // HAVE_CONFIG_H #include #include #include #include #include #include #include #include "bitstream.h" #include "bits_utils.h" #include "metadatav1.h" #include #include #include STATIC fpga_result opae_bitstream_read_file(const char *file, uint8_t **buf, size_t *len) { FILE *fp; fpga_result res = FPGA_EXCEPTION; long pos; size_t sz; fp = fopen(file, "rb"); if (!fp) { OPAE_ERR("fopen failed"); return FPGA_EXCEPTION; } if (fseek(fp, 0, SEEK_END) < 0) { OPAE_ERR("fseek failed"); goto out_close; } pos = ftell(fp); if (pos < 0) { OPAE_ERR("ftell failed"); goto out_close; } *len = (size_t)pos; *buf = (uint8_t *)malloc(*len); if (!*buf) { OPAE_ERR("malloc failed"); res = FPGA_NO_MEMORY; *len = 0; goto out_close; } if (fseek(fp, 0, SEEK_SET) < 0) { OPAE_ERR("fseek failed"); goto out_free; } sz = fread(*buf, 1, *len, fp); if (ferror(fp)) { OPAE_ERR("ferror after read"); goto out_free; } if (sz != *len) { OPAE_ERR("file size and number " "of bytes read mismatch"); goto out_free; } fclose(fp); return FPGA_OK; out_free: free(*buf); *buf = NULL; *len = 0; out_close: fclose(fp); return res; } bool opae_is_legacy_bitstream(opae_bitstream_info *info) { opae_legacy_bitstream_header *hdr; if (info->data_len < sizeof(opae_legacy_bitstream_header)) return false; hdr = (opae_legacy_bitstream_header *)info->data; if (hdr->legacy_magic == OPAE_LEGACY_BITSTREAM_MAGIC) return true; return false; } STATIC void opae_resolve_legacy_bitstream(opae_bitstream_info *info) { opae_legacy_bitstream_header *hdr = (opae_legacy_bitstream_header *)info->data; uint8_t *p = &hdr->legacy_pr_ifc_id[15]; int i = 0; // The guid is encoded backwards. // Reverse it. while (p >= hdr->legacy_pr_ifc_id) { info->pr_interface_id[i++] = *p--; } info->rbf_data = info->data + sizeof(opae_legacy_bitstream_header); info->rbf_len = info->data_len - sizeof(opae_legacy_bitstream_header); } STATIC void *opae_bitstream_parse_metadata(const char *metadata, fpga_guid pr_interface_id, int *version) { json_object *root = NULL; json_object *j_version = NULL; enum json_tokener_error j_err = json_tokener_success; void *parsed = NULL; root = json_tokener_parse_verbose(metadata, &j_err); if (!root) { OPAE_ERR("invalid JSON metadata: %s", json_tokener_error_desc(j_err)); return NULL; } if (!json_object_object_get_ex(root, "version", &j_version)) { OPAE_ERR("metadata: failed to find \"version\" key"); goto out_put; } if (!json_object_is_type(j_version, json_type_int)) { OPAE_ERR("metadata: \"version\" key not integer"); goto out_put; } *version = json_object_get_int(j_version); switch (*version) { // Some invalid GBS's around the BBS 6.4.0 and // BBS 6.5.0 eras incorrectly set the metadata // version to 640/650 respectively. // Allow 640/650 to serve as an alias for 1. case 650: case 640: *version = 1; /* FALLTHROUGH */ case 1: parsed = opae_bitstream_parse_metadata_v1(root, pr_interface_id); break; default: OPAE_ERR("metadata: unsupported version: %d", *version); } out_put: json_object_put(root); return parsed; } STATIC fpga_guid valid_GBS_guid = { 0x58, 0x65, 0x6f, 0x6e, 0x46, 0x50, 0x47, 0x41, 0xb7, 0x47, 0x42, 0x53, 0x76, 0x30, 0x30, 0x31 }; STATIC fpga_result opae_resolve_bitstream(opae_bitstream_info *info) { opae_bitstream_header *hdr; size_t sz; char *buf; if (info->data_len < sizeof(opae_bitstream_header)) { OPAE_ERR("file length smaller than bitstream header: " "\"%s\"", info->filename); return FPGA_INVALID_PARAM; } hdr = (opae_bitstream_header *)info->data; if (uuid_compare(hdr->valid_gbs_guid, valid_GBS_guid) != 0) { OPAE_ERR("GBS guid is invalid: \"%s\"", info->filename); return FPGA_INVALID_PARAM; } // Check that metadata_length makes sense // given that we know the total file size. sz = sizeof(fpga_guid) + sizeof(uint32_t); sz += (size_t)hdr->metadata_length; if (sz > info->data_len) { OPAE_ERR("invalid metadata length in \"%s\"", info->filename); return FPGA_INVALID_PARAM; } info->rbf_data = info->data + sz; info->rbf_len = info->data_len - sz; buf = (char *)malloc(hdr->metadata_length + 1); if (!buf) { OPAE_ERR("malloc failed"); return FPGA_NO_MEMORY; } memcpy(buf, hdr->metadata, hdr->metadata_length); buf[hdr->metadata_length] = '\0'; info->parsed_metadata = opae_bitstream_parse_metadata(buf, info->pr_interface_id, &info->metadata_version); free(buf); return info->parsed_metadata ? FPGA_OK : FPGA_EXCEPTION; } fpga_result opae_load_bitstream(const char *file, opae_bitstream_info *info) { fpga_result res; if (!file || !info) return FPGA_INVALID_PARAM; if (!opae_bitstream_path_is_valid(file, OPAE_BITSTREAM_PATH_NO_SYMLINK)) { OPAE_ERR("invalid bitstream path \"%s\"", file); return FPGA_INVALID_PARAM; } memset(info, 0, sizeof(opae_bitstream_info)); res = opae_bitstream_read_file(file, &info->data, &info->data_len); if (res != FPGA_OK) { OPAE_ERR("error loading \"%s\"", file); return res; } info->filename = file; if (opae_is_legacy_bitstream(info)) { opae_resolve_legacy_bitstream(info); OPAE_MSG("Legacy bitstream (GBS) format detected."); OPAE_MSG("Legacy GBS support is deprecated " "and will be removed in a future release."); return FPGA_OK; } return opae_resolve_bitstream(info); } fpga_result opae_unload_bitstream(opae_bitstream_info *info) { fpga_result res = FPGA_OK; if (!info) return FPGA_INVALID_PARAM; if (info->data) free(info->data); if (info->parsed_metadata) { switch (info->metadata_version) { case 1: opae_bitstream_release_metadata_v1( (opae_bitstream_metadata_v1 *)info->parsed_metadata); break; default: OPAE_ERR("metadata: unsupported version: %d", info->metadata_version); res = FPGA_EXCEPTION; } } memset(info, 0, sizeof(opae_bitstream_info)); return res; }