// Copyright(c) 2017-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 "opae/utils.h" #include "common_int.h" #include "bitstream_int.h" #define METADATA_GUID "58656F6E-4650-4741-B747-425376303031" #define METADATA_GUID_LEN 16 #define METADATA_MAX_LEN 8192 #define FPGA_GBS_6_3_0_MAGIC 0x1d1f8680 // dec: 488605312 #define PR_INTERFACE_ID "pr/interface_id" #define INTFC_ID_LOW_LEN 16 #define INTFC_ID_HIGH_LEN 16 #define BUFFER_SIZE 32 // GBS json metadata // GBS version #define GBS_VERSION "version" // AFU image #define GBS_AFU_IMAGE "afu-image" #define GBS_MAGIC_NUM "magic-no" #define BBS_INTERFACE_ID "interface-uuid" #define GBS_CLOCK_FREQUENCY_HIGH "clock-frequency-high" #define GBS_CLOCK_FREQUENCY_LOW "clock-frequency-low" #define GBS_AFU_POWER "power" // AFU Clusters #define GBS_ACCELERATOR_CLUSTERS "accelerator-clusters" #define GBS_AFU_NAME "name" #define GBS_ACCELERATOR_TYPE_UUID "accelerator-type-uuid" #define GBS_ACCELERATOR_TOTAL_CONTEXTS "total-contexts" fpga_result string_to_guid(const char *guid, fpga_guid *result) { if (uuid_parse(guid, *result) < 0) { OPAE_MSG("Error parsing GUID %s\n", guid); return FPGA_INVALID_PARAM; } return FPGA_OK; } STATIC json_bool get_json_object(json_object **object, json_object **parent, char *field_name) { return json_object_object_get_ex(*parent, field_name, &(*object)); } STATIC uint64_t read_int_from_bitstream(const uint8_t *bitstream, uint8_t size) { uint64_t ret = 0; switch (size) { case sizeof(uint8_t): ret = *((uint8_t *) bitstream); break; case sizeof(uint16_t): ret = *((uint16_t *) bitstream); break; case sizeof(uint32_t): ret = *((uint32_t *) bitstream); break; case sizeof(uint64_t): ret = *((uint64_t *) bitstream); break; default: OPAE_ERR("Unknown integer size"); } return ret; } STATIC int64_t int64_be_to_le(int64_t val) { val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL); val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL); return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); } fpga_result get_interface_id(fpga_handle handle, uint64_t *id_l, uint64_t *id_h) { struct _fpga_token *_token; struct _fpga_handle *_handle = (struct _fpga_handle *)handle; fpga_result result = FPGA_OK; fpga_guid guid; _token = (struct _fpga_token *)_handle->token; if (!_token) { OPAE_MSG("Token is NULL"); return FPGA_INVALID_PARAM; } if (_token->magic != FPGA_TOKEN_MAGIC) { OPAE_MSG("Invalid token in handle"); return FPGA_INVALID_PARAM; } if (id_l == NULL || id_h == NULL) { OPAE_MSG("id_l or id_h are NULL"); return FPGA_INVALID_PARAM; } // PR Interface id result = sysfs_get_interface_id(_token, guid); if (FPGA_OK != result) { OPAE_ERR("Failed to get PR interface id"); return FPGA_EXCEPTION; } memcpy(id_h, guid, sizeof(uint64_t)); *id_h = int64_be_to_le(*id_h); memcpy(id_l, guid + sizeof(uint64_t), sizeof(uint64_t)); *id_l = int64_be_to_le(*id_l); return FPGA_OK; } fpga_result check_interface_id(fpga_handle handle, uint32_t bitstream_magic_no, uint64_t ifid_l, uint64_t ifid_h) { uint64_t intfc_id_l = 0; uint64_t intfc_id_h = 0; fpga_result result = FPGA_OK; if (bitstream_magic_no != FPGA_GBS_6_3_0_MAGIC) { OPAE_MSG("Invalid bitstream magic number"); return FPGA_NOT_FOUND; } if (get_interface_id(handle, &intfc_id_l, &intfc_id_h)) { OPAE_MSG("Get interface ID failed"); return FPGA_NOT_FOUND; } if ((ifid_l != intfc_id_l) || (ifid_h != intfc_id_h)) { OPAE_MSG("Interface id doesn't match metadata"); return FPGA_NOT_FOUND; } return result; } fpga_result check_bitstream_guid(const uint8_t *bitstream) { fpga_guid bitstream_guid; fpga_guid expected_guid; memcpy(bitstream_guid, bitstream, sizeof(fpga_guid)); if (string_to_guid(METADATA_GUID, &expected_guid) != FPGA_OK) return FPGA_INVALID_PARAM; if (uuid_compare(bitstream_guid, expected_guid) != 0) return FPGA_INVALID_PARAM; return FPGA_OK; } int get_bitstream_header_len(const uint8_t *bitstream) { uint32_t json_len = 0; if (!bitstream) { OPAE_ERR("NULL input bitstream pointer"); return -1; } if (check_bitstream_guid(bitstream) != FPGA_OK) return -1; json_len = read_int_from_bitstream(bitstream + METADATA_GUID_LEN, sizeof(uint32_t)); return (METADATA_GUID_LEN + sizeof(uint32_t) + json_len); } int32_t get_bitstream_json_len(const uint8_t *bitstream) { uint32_t json_len = 0; if (!bitstream) { OPAE_ERR("NULL input bitstream pointer"); return -1; } if (check_bitstream_guid(bitstream) != FPGA_OK) return -1; json_len = read_int_from_bitstream(bitstream + METADATA_GUID_LEN, sizeof(uint32_t)); return json_len; } fpga_result validate_bitstream_metadata(fpga_handle handle, const uint8_t *bitstream) { fpga_result result = FPGA_EXCEPTION; char *json_metadata = NULL; uint32_t json_len = 0; uint32_t bitstream_magic_no = 0; uint64_t ifc_id_val_l, ifc_id_val_h; const uint8_t *json_metadata_ptr = NULL; json_object *root = NULL; json_object *afu_image = NULL, *magic_no = NULL; json_object *interface_id = NULL; fpga_guid expected_guid; if (check_bitstream_guid(bitstream) != FPGA_OK) goto out_free; json_len = read_int_from_bitstream(bitstream + METADATA_GUID_LEN, sizeof(uint32_t)); if (json_len == 0) { OPAE_MSG("Bitstream has no metadata"); result = FPGA_OK; goto out_free; } if (json_len >= METADATA_MAX_LEN) { OPAE_ERR("Bitstream metadata too large"); goto out_free; } json_metadata_ptr = bitstream + METADATA_GUID_LEN + sizeof(uint32_t); json_metadata = (char *) malloc(json_len + 1); if (json_metadata == NULL) { OPAE_ERR("Could not allocate memory for metadata"); return FPGA_NO_MEMORY; } memcpy(json_metadata, json_metadata_ptr, json_len); json_metadata[json_len] = '\0'; root = json_tokener_parse(json_metadata); if (root != NULL) { if (get_json_object(&afu_image, &root, GBS_AFU_IMAGE)) { get_json_object(&magic_no, &afu_image, GBS_MAGIC_NUM); get_json_object(&interface_id, &afu_image, BBS_INTERFACE_ID); if (magic_no == NULL || interface_id == NULL) { OPAE_ERR("Invalid metadata"); result = FPGA_INVALID_PARAM; goto out_free; } result = string_to_guid( json_object_get_string(interface_id), &expected_guid); if (result != FPGA_OK) { OPAE_ERR("Invalid BBS interface ID"); goto out_free; } memcpy(&ifc_id_val_h, expected_guid, sizeof(uint64_t)); ifc_id_val_h = int64_be_to_le(ifc_id_val_h); memcpy(&ifc_id_val_l, expected_guid + sizeof(uint64_t), sizeof(uint64_t)); ifc_id_val_l = int64_be_to_le(ifc_id_val_l); bitstream_magic_no = json_object_get_int(magic_no); result = check_interface_id(handle, bitstream_magic_no, ifc_id_val_l, ifc_id_val_h); if (result != FPGA_OK) { OPAE_ERR("Interface ID check failed"); goto out_free; } } else { OPAE_ERR("Invalid metadata"); result = FPGA_INVALID_PARAM; goto out_free; } } out_free: if (root) json_object_put(root); if (json_metadata) free(json_metadata); return result; } fpga_result read_gbs_metadata(const uint8_t *bitstream, struct gbs_metadata *gbs_metadata) { uint32_t json_len = 0; fpga_result result = FPGA_OK; const uint8_t *json_metadata_ptr = NULL; char *json_metadata = NULL; json_object *root = NULL; json_object *magic_num = NULL; json_object *interface_id = NULL; json_object *afu_image = NULL; json_object *version = NULL; json_object *accelerator_clusters = NULL; json_object *cluster = NULL; json_object *uuid = NULL; json_object *name = NULL; json_object *contexts = NULL; json_object *power = NULL; json_object *userclk1 = NULL; json_object *userclk2 = NULL; if (gbs_metadata == NULL) { OPAE_ERR("Invalid input metadata"); return FPGA_INVALID_PARAM; } if (bitstream == NULL) { OPAE_ERR("Invalid input bitstream"); return FPGA_INVALID_PARAM; } if (check_bitstream_guid(bitstream) != FPGA_OK) { OPAE_ERR("Failed to read GUID"); return FPGA_INVALID_PARAM; } json_len = *((uint32_t *) (bitstream + METADATA_GUID_LEN)); if (!json_len || json_len >= METADATA_MAX_LEN) { OPAE_ERR("Invalid bitstream metadata size"); return FPGA_INVALID_PARAM; } json_metadata_ptr = bitstream + METADATA_GUID_LEN + sizeof(uint32_t); json_metadata = (char *) malloc(json_len + 1); if (!json_metadata) { OPAE_ERR("Could not allocate memory for metadata"); return FPGA_NO_MEMORY; } memcpy(json_metadata, json_metadata_ptr, json_len); json_metadata[json_len] = '\0'; root = json_tokener_parse(json_metadata); if (root) { // GBS version if (get_json_object(&version, &root, GBS_VERSION)) { gbs_metadata->version = json_object_get_double(version); } else { OPAE_ERR("No GBS version"); result = FPGA_INVALID_PARAM; goto out_free; } // afu-image if (get_json_object(&afu_image, &root, GBS_AFU_IMAGE)) { // magic number if (get_json_object(&magic_num, &afu_image, GBS_MAGIC_NUM)) { gbs_metadata->afu_image.magic_num = json_object_get_int64(magic_num); } // Interface type GUID if (get_json_object(&interface_id, &afu_image, BBS_INTERFACE_ID)) { memcpy(gbs_metadata->afu_image.interface_uuid, json_object_get_string(interface_id), GUID_LEN); gbs_metadata->afu_image.interface_uuid[GUID_LEN] = '\0'; } else { OPAE_ERR("No interface ID found in JSON metadata"); result = FPGA_INVALID_PARAM; goto out_free; } // AFU user clock frequency High if (get_json_object(&userclk1, &afu_image, GBS_CLOCK_FREQUENCY_HIGH)) { gbs_metadata->afu_image.clock_frequency_high = json_object_get_int64(userclk1); } // AFU user clock frequency Low if (get_json_object(&userclk2, &afu_image, GBS_CLOCK_FREQUENCY_LOW)) { gbs_metadata->afu_image.clock_frequency_low = json_object_get_int64(userclk2); } // GBS power if (get_json_object(&power, &afu_image, GBS_AFU_POWER)) { gbs_metadata->afu_image.power = json_object_get_int64(power); } } else { OPAE_ERR("No AFU image in metadata"); result = FPGA_INVALID_PARAM; goto out_free; } // afu clusters if (get_json_object(&afu_image, &root, GBS_AFU_IMAGE) && get_json_object(&accelerator_clusters, &afu_image, GBS_ACCELERATOR_CLUSTERS)) { cluster = json_object_array_get_idx(accelerator_clusters, 0); // AFU GUID if (get_json_object(&uuid, &cluster, GBS_ACCELERATOR_TYPE_UUID)) { memcpy(gbs_metadata->afu_image.afu_clusters.afu_uuid, json_object_get_string(uuid), GUID_LEN); gbs_metadata->afu_image.afu_clusters.afu_uuid[GUID_LEN] = '\0'; } else { OPAE_ERR("No accelerator-type-uuid in JSON metadata"); result = FPGA_INVALID_PARAM; goto out_free; } // AFU Name if (get_json_object(&name, &cluster, GBS_AFU_NAME)) { memcpy(gbs_metadata->afu_image.afu_clusters.name, json_object_get_string(name), json_object_get_string_len(name)); } // AFU Total number of contexts if (get_json_object(&contexts, &cluster, GBS_ACCELERATOR_TOTAL_CONTEXTS)) { gbs_metadata->afu_image.afu_clusters.total_contexts = json_object_get_int64(contexts); } } else { OPAE_ERR("No accelerator clusters in metadata"); result = FPGA_INVALID_PARAM; goto out_free; } } else { OPAE_ERR("Invalid JSON in metadata"); result = FPGA_INVALID_PARAM; goto out_free; } out_free: if (root) json_object_put(root); if (json_metadata) free(json_metadata); return result; }