// 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 <config.h>
#endif // HAVE_CONFIG_H
#include <stdlib.h>
#include <getopt.h>
#include <signal.h>
#include <new>
#include <arpa/inet.h>
#include <opae/fpga.h>
#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> --segment=<SEGMENT NUMBER>\n");
printf("<Bus> --bus=<BUS NUMBER> "
"OR -B <BUS NUMBER>\n");
printf("<Device> --device=<DEVICE NUMBER> "
"OR -D <DEVICE NUMBER>\n");
printf("<Function> --function=<FUNCTION NUMBER> "
"OR -F <FUNCTION NUMBER>\n");
printf("<Socket-id> --socket-id=<SOCKET NUMBER> "
"OR -S <SOCKET NUMBER>\n");
printf("<TCP PORT> --port=<PORT> "
"OR -P <PORT>\n");
printf("<IP ADDRESS> --ip=<IP ADDRESS> "
"OR -I <IP ADDRESS>\n");
printf("<Version> -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;
}