// 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 #include "mmlink_server.h" #include "mm_debug_link_interface.h" // STP index in AFU #define FPGA_PORT_INDEX_STP 1 #define FPGA_PORT_STP_DFH_REVBIT 12 #define GETOPT_STRING ":hB:D:F:S:P:Iv" #define PRINT_ERR(format, ...) \ printf("%s:%u:%s() : " format "\n", __FILE__, __LINE__, __func__,\ ## __VA_ARGS__) struct option longopts[] = { {"help", no_argument, NULL, 'h'}, {"segment", required_argument, NULL, 0xe}, {"bus", required_argument, NULL, 'B'}, {"device", required_argument, NULL, 'D'}, {"function", required_argument, NULL, 'F'}, {"socket-id", required_argument, NULL, 'S'}, {"port", required_argument, NULL, 'P'}, {"ip", required_argument, NULL, 'I'}, {"version", no_argument, NULL, 'v'}, {0,0,0,0} }; // mmlink Command line struct struct MMLinkCommandLine { int segment; int bus; int device; int function; int socket; int port; char ip[16]; }; struct MMLinkCommandLine mmlinkCmdLine = { -1, -1, -1, -1, -1, 0, { 0, } }; // mmlink Command line input help void MMLinkAppShowHelp() { printf("Usage:\n"); printf("mmlink\n"); printf(" --segment=\n"); printf(" --bus= " "OR -B \n"); printf(" --device= " "OR -D \n"); printf(" --function= " "OR -F \n"); printf(" --socket-id= " "OR -S \n"); printf(" --port= " "OR -P \n"); printf(" --ip= " "OR -I \n"); printf(" -v,--version Print version and exit\n"); printf("\n"); } /* * macro to check return codes, print error message, and goto cleanup label * NOTE: this changes the program flow (uses goto)! */ #define ON_ERR_GOTO(res, label, desc) \ do { \ if ((res) != FPGA_OK) { \ print_err((desc), (res)); \ goto label; \ } \ } while (0) void print_err(const char *s, fpga_result res) { fprintf(stderr, "Error %s: %s\n", s, fpgaErrStr(res)); } void mmlink_sig_handler(int sig) { UNUSED_PARAM(sig); perror("SIGINT: stopping the server\n"); } int ParseCmds(struct MMLinkCommandLine *mmlinkCmdLine, int argc, char *argv[]); int run_mmlink(fpga_handle port_handle, uint64_t *mmio_ptr, struct MMLinkCommandLine *mmlinkCmdLine ); int main( int argc, char** argv ) { fpga_properties filter = NULL; uint32_t num_matches = 1; fpga_result result = FPGA_OK; fpga_token port_token = NULL; fpga_handle port_handle = NULL; uint64_t *mmio_ptr = NULL; int res; // Parse command line if ( argc < 2 ) { MMLinkAppShowHelp(); return 1; } else if ( 0 != (res = ParseCmds(&mmlinkCmdLine, argc, argv)) ) { if (res != -2) PRINT_ERR( "Error scanning command line \n."); return 2; } if ('\0' == mmlinkCmdLine.ip[0]) { strncpy(mmlinkCmdLine.ip, "0.0.0.0", 8); mmlinkCmdLine.ip[7] = '\0'; } printf(" ------- Command line Input START ----\n\n"); printf(" Segment : %d\n", mmlinkCmdLine.segment); printf(" Bus : %d\n", mmlinkCmdLine.bus); printf(" Device : %d\n", mmlinkCmdLine.device); printf(" Function : %d\n", mmlinkCmdLine.function); printf(" Socket-id : %d\n", mmlinkCmdLine.socket); printf(" Port : %d\n", mmlinkCmdLine.port); printf(" IP address : %s\n", mmlinkCmdLine.ip); printf(" ------- Command line Input END ----\n\n"); // Signal Handler signal(SIGINT, mmlink_sig_handler); // Enum FPGA device result = fpgaGetProperties(NULL, &filter); ON_ERR_GOTO(result, out_exit, "creating properties object"); result = fpgaPropertiesSetObjectType(filter, FPGA_ACCELERATOR); ON_ERR_GOTO(result, out_destroy_prop, "setting object type"); if (mmlinkCmdLine.segment > -1){ result = fpgaPropertiesSetSegment(filter, mmlinkCmdLine.segment); ON_ERR_GOTO(result, out_destroy_prop, "setting segment"); } if (mmlinkCmdLine.bus > -1){ result = fpgaPropertiesSetBus(filter, mmlinkCmdLine.bus); ON_ERR_GOTO(result, out_destroy_prop, "setting bus"); } if (mmlinkCmdLine.device > -1) { result = fpgaPropertiesSetDevice(filter, mmlinkCmdLine.device); ON_ERR_GOTO(result, out_destroy_prop, "setting device"); } if (mmlinkCmdLine.function > -1){ result = fpgaPropertiesSetFunction(filter, mmlinkCmdLine.function); ON_ERR_GOTO(result, out_destroy_prop, "setting function"); } if (mmlinkCmdLine.socket > -1){ result = fpgaPropertiesSetSocketID(filter, mmlinkCmdLine.socket); ON_ERR_GOTO(result, out_destroy_prop, "setting socket"); } result = fpgaEnumerate(&filter, 1, &port_token,1, &num_matches); ON_ERR_GOTO(result, out_destroy_prop, "enumerating FPGAs"); if (num_matches < 1) { fprintf(stderr, "PORT Resource not found.\n"); result = fpgaDestroyProperties(&filter); return FPGA_INVALID_PARAM; } fprintf(stderr, "PORT Resource found.\n"); result = fpgaOpen(port_token, &port_handle, FPGA_OPEN_SHARED); ON_ERR_GOTO(result, out_destroy_tok, "opening accelerator"); result = fpgaMapMMIO(port_handle, FPGA_PORT_INDEX_STP, &mmio_ptr); ON_ERR_GOTO(result, out_close, "mapping MMIO space"); if( run_mmlink(port_handle,mmio_ptr,&mmlinkCmdLine) != 0) { PRINT_ERR( "Failed to connect MMLINK \n."); result = FPGA_NOT_SUPPORTED; } /* Unmap MMIO space */ result = fpgaUnmapMMIO(port_handle, FPGA_PORT_INDEX_STP); ON_ERR_GOTO(result, out_close, "unmapping MMIO space"); /* Close driver handle */ out_close: result = fpgaClose(port_handle); ON_ERR_GOTO(result, out_destroy_tok, "closing Port"); /* Destroy token */ out_destroy_tok: result = fpgaDestroyToken(&port_token); ON_ERR_GOTO(result, out_destroy_prop, "destroying token"); /* Destroy properties object */ out_destroy_prop: result = fpgaDestroyProperties(&filter); ON_ERR_GOTO(result, out_exit, "destroying properties object"); out_exit: return result; } // run MMLink server int run_mmlink(fpga_handle port_handle, uint64_t *mmio_ptr, struct MMLinkCommandLine *mmlinkCmdLine ) { mmlink_server *server = NULL; int res = 0; struct sockaddr_in sock; uint64_t value = 0; if (mmio_ptr == NULL) { PRINT_ERR("Invalid input mmio pointer \n"); return -1; } if (mmlinkCmdLine == NULL) { PRINT_ERR("Invalid input command line \n"); return -1; } memset(&sock, 0, sizeof(sock)); sock.sin_family = AF_INET; sock.sin_port = htons(mmlinkCmdLine->port); if (1 != inet_pton(AF_INET, mmlinkCmdLine->ip, &sock.sin_addr)) { PRINT_ERR("Failed to convert IP address: %s\n", mmlinkCmdLine->ip); return -1; } res = fpgaReadMMIO64(port_handle, FPGA_PORT_INDEX_STP, 0, &value); if (res != 0) { PRINT_ERR("Failed to read STP DFH \n"); return -1; } //printf("STP DFH = 0x%lx\n" ,value); value &= 0x1000; value = value >> FPGA_PORT_STP_DFH_REVBIT; if(1 != value){ PRINT_ERR("Invalid STP revision number \n"); return -1; } mm_debug_link_interface *driver = get_mm_debug_link(); server = new (std::nothrow) mmlink_server(&sock, driver); if (!server) { PRINT_ERR("Failed to allocate memory \n"); return -1; } // Run MMLink server res = server->run((unsigned char*)mmio_ptr); if (server) delete server; return res; } // parse Input command line int ParseCmds(struct MMLinkCommandLine *mmlinkCmdLine, int argc, char *argv[]) { int getopt_ret = 0 ; int option_index = 0; char *endptr = NULL; while( -1 != ( getopt_ret = getopt_long(argc, argv, GETOPT_STRING, longopts, &option_index))){ const char *tmp_optarg = optarg; if((optarg) && ('=' == *tmp_optarg)){ ++tmp_optarg; } if((!optarg) && (optind < argc) && (NULL != argv[optind]) && ('-' != argv[optind][0]) ) { tmp_optarg = argv[optind++]; } switch(getopt_ret){ case 'h': // Command line help MMLinkAppShowHelp(); return -2; break; case 0xe: // segment number if (!tmp_optarg) { PRINT_ERR("Missing required argument for --segment"); return -1; } endptr = NULL; mmlinkCmdLine->segment = strtol(tmp_optarg, &endptr, 0); break; case 'B': // bus number if (!tmp_optarg) { PRINT_ERR("Missing required argument for --bus"); return -1; } endptr = NULL; mmlinkCmdLine->bus = strtol(tmp_optarg, &endptr, 0); break; case 'D': // Device number if (!tmp_optarg) { PRINT_ERR("Missing required argument for --device"); return -1; } endptr = NULL; mmlinkCmdLine->device = strtol(tmp_optarg, &endptr, 0); break; case 'F': // Function number if (!tmp_optarg) { PRINT_ERR("Missing required argument for --function"); return -1; } endptr = NULL; mmlinkCmdLine->function = strtol(tmp_optarg, &endptr, 0); break; case 'S': // Socket number if (!tmp_optarg) { PRINT_ERR("Missing required argument for --socket"); return -1; } endptr = NULL; mmlinkCmdLine->socket = strtol(tmp_optarg, &endptr, 0); break; case 'P': // TCP Port if (!tmp_optarg) { PRINT_ERR("Missing required argument for --port"); return -1; } endptr = NULL; mmlinkCmdLine->port = strtol(tmp_optarg, &endptr, 0); break; case 'I': // Ip address if (!tmp_optarg) { PRINT_ERR("Missing required argument for --ip"); return -1; } strncpy(mmlinkCmdLine->ip, tmp_optarg, 15); mmlinkCmdLine->ip[15] = '\0'; break; case 'v': // Version printf("mmlink %s %s%s\n", OPAE_VERSION, OPAE_GIT_COMMIT_HASH, OPAE_GIT_SRC_TREE_DIRTY ? "*":""); return -2; case '?': default: /* invalid option */ printf("Invalid cmdline options.\n"); return -1; } } return 0; }