From da3afd3ccdde4f2317f3a73e31c879c0a2847a25 Mon Sep 17 00:00:00 2001 From: Peter Delevoryas Date: Fri, 13 May 2022 19:56:45 -0700 Subject: ncsi: Pass command header to response handlers This change passes the command header as an additional read-only parameter to each response handler so that they can make more response handling descisions based on the command header fields. This is especially useful for handling OEM NC-SI commands, or any protocol that's encapsulated in an NC-SI header. Signed-off-by: Peter Delevoryas --- src/ncsi.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/ncsi.c b/src/ncsi.c index 5bf731b..bfe3088 100644 --- a/src/ncsi.c +++ b/src/ncsi.c @@ -56,7 +56,8 @@ static uint32_t ncsi_calculate_checksum(uint8_t *data, int len) } /* Get Version ID */ -static int ncsi_rsp_handler_gvi(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) +static int ncsi_rsp_handler_gvi(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) { struct ncsi_rsp_gvi_pkt *rsp = (struct ncsi_rsp_gvi_pkt *)rnh; @@ -67,7 +68,8 @@ static int ncsi_rsp_handler_gvi(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) } /* Get Capabilities */ -static int ncsi_rsp_handler_gc(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) +static int ncsi_rsp_handler_gc(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) { struct ncsi_rsp_gc_pkt *rsp = (struct ncsi_rsp_gc_pkt *)rnh; @@ -82,7 +84,8 @@ static int ncsi_rsp_handler_gc(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) } /* Get Link status */ -static int ncsi_rsp_handler_gls(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) +static int ncsi_rsp_handler_gls(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) { struct ncsi_rsp_gls_pkt *rsp = (struct ncsi_rsp_gls_pkt *)rnh; @@ -91,7 +94,8 @@ static int ncsi_rsp_handler_gls(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) } /* Get Parameters */ -static int ncsi_rsp_handler_gp(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) +static int ncsi_rsp_handler_gp(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) { struct ncsi_rsp_gp_pkt *rsp = (struct ncsi_rsp_gp_pkt *)rnh; @@ -107,7 +111,8 @@ static int ncsi_rsp_handler_gp(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) static const struct ncsi_rsp_handler { unsigned char type; int payload; - int (*handler)(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh); + int (*handler)(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh); } ncsi_rsp_handlers[] = { { NCSI_PKT_RSP_CIS, 4, NULL }, { NCSI_PKT_RSP_SP, 4, NULL }, { NCSI_PKT_RSP_DP, 4, NULL }, @@ -188,8 +193,7 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) rnh->reason = htons(NCSI_PKT_RSP_R_NO_ERROR); if (handler->handler) { - /* TODO: handle errors */ - handler->handler(slirp, rnh); + handler->handler(slirp, nh, rnh); } ncsi_rsp_len += handler->payload; } else { -- cgit v1.1 From ec7f20e8b634d4957258969ec82c7b3fe3802705 Mon Sep 17 00:00:00 2001 From: Peter Delevoryas Date: Fri, 13 May 2022 20:12:35 -0700 Subject: ncsi: Use response header for payload length In the Linux NC-SI driver[1], each response's length is validated with a statically declared payload length, _unless_ it's an OEM command or some more complicated NC-SI packet that you can't determine the length of just from the "type" field, in which case it just uses the length provided by the response's header. To support OEM response handlers without requiring too many modifications we can make the default payload length use the value specified in the handler table, and then allow OEM handlers to override the length by modifying the "length" in the response header within the handler implementation. [1] https://github.com/torvalds/linux/blob/ec7f49619d8ee13e108740c82f942cd401b989e9/net/ncsi/ncsi-rsp.c#L1215-L1220 Signed-off-by: Peter Delevoryas --- src/ncsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ncsi.c b/src/ncsi.c index bfe3088..d0632bb 100644 --- a/src/ncsi.c +++ b/src/ncsi.c @@ -195,7 +195,7 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) if (handler->handler) { handler->handler(slirp, nh, rnh); } - ncsi_rsp_len += handler->payload; + ncsi_rsp_len += ntohs(rnh->common.length); } else { rnh->common.length = 0; rnh->code = htons(NCSI_PKT_RSP_C_UNAVAILABLE); -- cgit v1.1 From 0f8dcfa2ab62be6e635148a514d978268e958356 Mon Sep 17 00:00:00 2001 From: Peter Delevoryas Date: Sat, 14 May 2022 15:32:13 -0700 Subject: ncsi: Add basic test for Get Version ID response Signed-off-by: Peter Delevoryas --- meson.build | 8 +++++++ test/ncsitest.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 test/ncsitest.c diff --git a/meson.build b/meson.build index 5fe33d3..435f331 100644 --- a/meson.build +++ b/meson.build @@ -151,6 +151,14 @@ pingtest = executable('pingtest', 'test/pingtest.c', test('ping', pingtest) +ncsitest = executable('ncsitest', 'test/ncsitest.c', + link_with: [lib], + include_directories: ['src'], + dependencies: [glib_dep, platform_deps] +) + +test('ncsi', ncsitest) + if install_devel install_headers(['src/libslirp.h'], subdir : 'slirp') diff --git a/test/ncsitest.c b/test/ncsitest.c new file mode 100644 index 0000000..12adfa3 --- /dev/null +++ b/test/ncsitest.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + */ + +/* + * This test verifies slirp responses to NC-SI commands. + */ + +#include +#include +#include + +#include "slirp.h" +#include "ncsi-pkt.h" + +#define NCSI_RESPONSE_CAPACITY 1024 + +static void test_ncsi_get_version_id(Slirp *slirp) +{ + slirp->mfr_id = 0xabcdef01; + + uint8_t command[] = { + /* Destination MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Source MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Ethertype */ + 0x88, 0xf8, + /* NC-SI Control packet header */ + 0x00, /* MC ID */ + 0x01, /* Header revision */ + 0x00, /* Reserved */ + 0x01, /* Instance ID */ + 0x15, /* Control Packet Type */ + 0x00, /* Channel ID */ + 0x00, /* Reserved */ + 0x00, /* Payload length */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + }; + slirp_input(slirp, command, sizeof(command)); + + const struct ncsi_rsp_gvi_pkt *gvi = slirp->opaque + ETH_HLEN; + + assert(gvi->rsp.code == NCSI_PKT_RSP_C_COMPLETED); + assert(gvi->rsp.code == NCSI_PKT_RSP_R_NO_ERROR); + assert(ntohl(gvi->mf_id) == slirp->mfr_id); + + slirp->mfr_id = 0; +} + +static ssize_t send_packet(const void *buf, size_t len, void *opaque) +{ + assert(len <= NCSI_RESPONSE_CAPACITY); + memcpy(opaque, buf, len); + return len; +} + +int main(int argc, char *argv[]) +{ + SlirpConfig config = {}; + SlirpCb callbacks = {}; + Slirp *slirp = NULL; + uint8_t ncsi_response[NCSI_RESPONSE_CAPACITY]; + + config.version = SLIRP_CONFIG_VERSION_MAX; + callbacks.send_packet = send_packet; + slirp = slirp_new(&config, &callbacks, ncsi_response); + + test_ncsi_get_version_id(slirp); + + slirp_cleanup(slirp); +} -- cgit v1.1 From 70f26099b1117c48a31139585ff84266e3e9c49a Mon Sep 17 00:00:00 2001 From: Peter Delevoryas Date: Sat, 14 May 2022 15:33:16 -0700 Subject: ncsi: Add OEM command handler This commit just sets up the OEM command handler to respond with "unsupported" for now, as verified in the test. Signed-off-by: Peter Delevoryas --- src/ncsi-pkt.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/ncsi.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- test/ncsitest.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 147 insertions(+), 3 deletions(-) diff --git a/src/ncsi-pkt.h b/src/ncsi-pkt.h index 39cf844..606684f 100644 --- a/src/ncsi-pkt.h +++ b/src/ncsi-pkt.h @@ -181,6 +181,29 @@ struct ncsi_cmd_snfc_pkt { unsigned char pad[22]; } SLIRP_PACKED; +/* OEM Request Command as per NCSI Specification */ +struct ncsi_cmd_oem_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mfr_id; /* Manufacture ID */ + unsigned char data[]; /* OEM Payload Data */ +} SLIRP_PACKED; + +/* OEM Response Packet as per NCSI Specification */ +struct ncsi_rsp_oem_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Command header */ + __be32 mfr_id; /* Manufacture ID */ + unsigned char data[]; /* Payload data */ +} SLIRP_PACKED; + +/* Mellanox Response Data */ +struct ncsi_rsp_oem_mlx_pkt { + unsigned char cmd_rev; /* Command Revision */ + unsigned char cmd; /* Command ID */ + unsigned char param; /* Parameter */ + unsigned char optional; /* Optional data */ + unsigned char data[]; /* Data */ +} SLIRP_PACKED; + /* Get Link Status */ struct ncsi_rsp_gls_pkt { struct ncsi_rsp_pkt_hdr rsp; /* Response header */ @@ -442,4 +465,26 @@ struct ncsi_aen_hncdsc_pkt { #define NCSI_PKT_AEN_CR 0x01 /* Configuration required */ #define NCSI_PKT_AEN_HNCDSC 0x02 /* HNC driver status change */ +/* OEM Vendor Manufacture ID */ +#define NCSI_OEM_MFR_MLX_ID 0x8119 +#define NCSI_OEM_MFR_BCM_ID 0x113d +#define NCSI_OEM_MFR_INTEL_ID 0x157 +/* Intel specific OEM command */ +#define NCSI_OEM_INTEL_CMD_GMA 0x06 /* CMD ID for Get MAC */ +#define NCSI_OEM_INTEL_CMD_KEEP_PHY 0x20 /* CMD ID for Keep PHY up */ +/* Broadcom specific OEM Command */ +#define NCSI_OEM_BCM_CMD_GMA 0x01 /* CMD ID for Get MAC */ +/* Mellanox specific OEM Command */ +#define NCSI_OEM_MLX_CMD_GMA 0x00 /* CMD ID for Get MAC */ +#define NCSI_OEM_MLX_CMD_GMA_PARAM 0x1b /* Parameter for GMA */ +#define NCSI_OEM_MLX_CMD_SMAF 0x01 /* CMD ID for Set MC Affinity */ +#define NCSI_OEM_MLX_CMD_SMAF_PARAM 0x07 /* Parameter for SMAF */ +/* Offset in OEM request */ +#define MLX_SMAF_MAC_ADDR_OFFSET 8 /* Offset for MAC in SMAF */ +#define MLX_SMAF_MED_SUPPORT_OFFSET 14 /* Offset for medium in SMAF */ +/* Mac address offset in OEM response */ +#define BCM_MAC_ADDR_OFFSET 28 +#define MLX_MAC_ADDR_OFFSET 8 +#define INTEL_MAC_ADDR_OFFSET 1 + #endif /* NCSI_PKT_H */ diff --git a/src/ncsi.c b/src/ncsi.c index d0632bb..0d4c491 100644 --- a/src/ncsi.c +++ b/src/ncsi.c @@ -55,6 +55,59 @@ static uint32_t ncsi_calculate_checksum(uint8_t *data, int len) return checksum; } +static const struct ncsi_rsp_oem_handler { + unsigned int mfr_id; + int (*handler)(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh); +} ncsi_rsp_oem_handlers[] = { + { NCSI_OEM_MFR_MLX_ID, NULL }, + { NCSI_OEM_MFR_BCM_ID, NULL }, + { NCSI_OEM_MFR_INTEL_ID, NULL }, +}; + +/* Response handler for OEM command */ +static int ncsi_rsp_handler_oem(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + const struct ncsi_rsp_oem_handler *nrh = NULL; + const struct ncsi_cmd_oem_pkt *cmd = (const struct ncsi_cmd_oem_pkt *)nh; + struct ncsi_rsp_oem_pkt *rsp = (struct ncsi_rsp_oem_pkt *)rnh; + uint32_t mfr_id = ntohl(cmd->mfr_id); + int i; + + rsp->mfr_id = cmd->mfr_id; + + if (mfr_id != slirp->mfr_id) { + goto error; + } + + /* Check for manufacturer id and Find the handler */ + for (i = 0; i < G_N_ELEMENTS(ncsi_rsp_oem_handlers); i++) { + if (ncsi_rsp_oem_handlers[i].mfr_id == mfr_id) { + if (ncsi_rsp_oem_handlers[i].handler) + nrh = &ncsi_rsp_oem_handlers[i]; + else + nrh = NULL; + + break; + } + } + + if (!nrh) { + goto error; + } + + /* Process the packet */ + return nrh->handler(slirp, nh, rnh); + +error: + rsp->rsp.common.length = htons(8); + rsp->rsp.code = htons(NCSI_PKT_RSP_C_UNSUPPORTED); + rsp->rsp.reason = htons(NCSI_PKT_RSP_R_UNKNOWN); + return -ENOENT; +} + + /* Get Version ID */ static int ncsi_rsp_handler_gvi(Slirp *slirp, const struct ncsi_pkt_hdr *nh, struct ncsi_rsp_pkt_hdr *rnh) @@ -140,7 +193,7 @@ static const struct ncsi_rsp_handler { { NCSI_PKT_RSP_GNS, 172, NULL }, { NCSI_PKT_RSP_GNPTS, 172, NULL }, { NCSI_PKT_RSP_GPS, 8, NULL }, - { NCSI_PKT_RSP_OEM, 0, NULL }, + { NCSI_PKT_RSP_OEM, 0, ncsi_rsp_handler_oem }, { NCSI_PKT_RSP_PLDM, 0, NULL }, { NCSI_PKT_RSP_GPUUID, 20, NULL } }; diff --git a/test/ncsitest.c b/test/ncsitest.c index 12adfa3..4a6cada 100644 --- a/test/ncsitest.c +++ b/test/ncsitest.c @@ -43,13 +43,58 @@ static void test_ncsi_get_version_id(Slirp *slirp) const struct ncsi_rsp_gvi_pkt *gvi = slirp->opaque + ETH_HLEN; - assert(gvi->rsp.code == NCSI_PKT_RSP_C_COMPLETED); - assert(gvi->rsp.code == NCSI_PKT_RSP_R_NO_ERROR); + assert(ntohs(gvi->rsp.code) == NCSI_PKT_RSP_C_COMPLETED); + assert(ntohs(gvi->rsp.code) == NCSI_PKT_RSP_R_NO_ERROR); assert(ntohl(gvi->mf_id) == slirp->mfr_id); slirp->mfr_id = 0; } +static void test_ncsi_oem_mlx_unsupported_command(Slirp *slirp) +{ + uint8_t command[] = { + /* Destination MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Source MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Ethertype */ + 0x88, 0xf8, + /* NC-SI Control packet header */ + 0x00, /* MC ID */ + 0x01, /* Header revision */ + 0x00, /* Reserved */ + 0x01, /* Instance ID */ + 0x50, /* Control Packet Type */ + 0x00, /* Channel ID */ + 0x00, /* Reserved */ + 0x08, /* Payload length */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + /* NC-SI OEM packet header */ + 0x00, 0x00, 0x81, 0x19, /* Manufacturer ID: Mellanox */ + /* Vendor Data */ + 0xff, /* Command Revision */ + 0xff, /* Command ID */ + 0x00, /* Parameter */ + 0x00, /* Optional data */ + }; + const struct ncsi_rsp_oem_pkt *oem = slirp->opaque + ETH_HLEN; + + slirp->mfr_id = 0x00000000; + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_UNSUPPORTED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_UNKNOWN); + assert(ntohl(oem->mfr_id) == 0x8119); + + slirp->mfr_id = 0x8119; + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_UNSUPPORTED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_UNKNOWN); + assert(ntohl(oem->mfr_id) == 0x8119); +} + static ssize_t send_packet(const void *buf, size_t len, void *opaque) { assert(len <= NCSI_RESPONSE_CAPACITY); @@ -69,6 +114,7 @@ int main(int argc, char *argv[]) slirp = slirp_new(&config, &callbacks, ncsi_response); test_ncsi_get_version_id(slirp); + test_ncsi_oem_mlx_unsupported_command(slirp); slirp_cleanup(slirp); } -- cgit v1.1 From 177ff459970a18fc4aa70b3c6a5add5248f5a0c1 Mon Sep 17 00:00:00 2001 From: Peter Delevoryas Date: Mon, 16 May 2022 16:05:35 -0700 Subject: slirp: Add out-of-band ethernet address If a network card supports NC-SI, then it redirects all traffic with the out-of-band (OOB) management controller's (MC) ethernet address to the out-of-band management controller, usually over some sideband RMII interface, not like the PCIe connection to the main host. It's also pretty common for the network card to provision the out-of-band management controller's ethernet address. At startup, the OOB MC asks the network card what its MAC address is through OEM NC-SI commands. This protocol is so common that it's going to be standardized soon in NC-SI 1.2.0 [1] as "Get MC MAC Address". Note: At some point, the network card may provision *multiple* OOB ethernet addresses, but right now everything just uses one. [1] https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.2.0WIP80.pdf Signed-off-by: Peter Delevoryas --- src/libslirp.h | 5 +++++ src/slirp.c | 2 ++ src/slirp.h | 1 + 3 files changed, 8 insertions(+) diff --git a/src/libslirp.h b/src/libslirp.h index 35ddab6..3afad21 100644 --- a/src/libslirp.h +++ b/src/libslirp.h @@ -144,6 +144,11 @@ typedef struct SlirpConfig { * Fields introduced in SlirpConfig version 5 begin */ uint32_t mfr_id; /* Manufacturer ID (IANA Private Enterprise number) */ + /* + * MAC address allocated for an out-of-band management controller, to be + * retrieved through NC-SI. + */ + uint8_t oob_eth_addr[6]; } SlirpConfig; /* Create a new instance of a slirp stack */ diff --git a/src/slirp.c b/src/slirp.c index 588cada..54056c9 100644 --- a/src/slirp.c +++ b/src/slirp.c @@ -650,8 +650,10 @@ Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque) if (cfg->version >= 5) { slirp->mfr_id = cfg->mfr_id; + memcpy(slirp->oob_eth_addr, cfg->oob_eth_addr, ETH_ALEN); } else { slirp->mfr_id = 0; + memset(slirp->oob_eth_addr, 0, ETH_ALEN); } ip6_post_init(slirp); diff --git a/src/slirp.h b/src/slirp.h index 4c956d9..e413867 100644 --- a/src/slirp.h +++ b/src/slirp.h @@ -152,6 +152,7 @@ struct Slirp { bool disable_host_loopback; uint32_t mfr_id; + uint8_t oob_eth_addr[ETH_ALEN]; /* mbuf states */ struct slirp_quehead m_freelist; -- cgit v1.1 From ca73d965d73a930ab366a61e96e1af181ff45594 Mon Sep 17 00:00:00 2001 From: Peter Delevoryas Date: Mon, 16 May 2022 15:49:48 -0700 Subject: ncsi: Add Mellanox Get Mac Address handler Attempted to mirror the upstream Linux driver[1] as closely as reasonably possible. [1] https://github.com/torvalds/linux/blob/42226c989789d8da4af1de0c31070c96726d990c/net/ncsi/ncsi-rsp.c#L614-L638 Signed-off-by: Peter Delevoryas --- src/ncsi-pkt.h | 5 +++++ src/ncsi.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- test/ncsitest.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/src/ncsi-pkt.h b/src/ncsi-pkt.h index 606684f..9dd167c 100644 --- a/src/ncsi-pkt.h +++ b/src/ncsi-pkt.h @@ -487,4 +487,9 @@ struct ncsi_aen_hncdsc_pkt { #define MLX_MAC_ADDR_OFFSET 8 #define INTEL_MAC_ADDR_OFFSET 1 +/* Status offset in OEM response */ +#define MLX_GMA_STATUS_OFFSET 0 +/* OEM Response payload length */ +#define MLX_GMA_PAYLOAD_LEN 24 + #endif /* NCSI_PKT_H */ diff --git a/src/ncsi.c b/src/ncsi.c index 0d4c491..adb3398 100644 --- a/src/ncsi.c +++ b/src/ncsi.c @@ -55,12 +55,73 @@ static uint32_t ncsi_calculate_checksum(uint8_t *data, int len) return checksum; } +/* Response handler for Mellanox command Get Mac Address */ +static int ncsi_rsp_handler_oem_mlx_gma(Slirp *slirp, + const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + uint8_t oob_eth_addr_allocated = 0; + struct ncsi_rsp_oem_pkt *rsp; + int i; + + rsp = (struct ncsi_rsp_oem_pkt *)rnh; + + /* Set the payload length */ + rsp->rsp.common.length = htons(MLX_GMA_PAYLOAD_LEN); + + for (i = 0; i < ETH_ALEN; i++) { + if (slirp->oob_eth_addr[i] != 0x00) { + oob_eth_addr_allocated = 1; + break; + } + } + rsp->data[MLX_GMA_STATUS_OFFSET] = oob_eth_addr_allocated; + + /* Set the allocated management address */ + memcpy(&rsp->data[MLX_MAC_ADDR_OFFSET], slirp->oob_eth_addr, ETH_ALEN); + + return 0; +} + +/* Response handler for Mellanox card */ +static int ncsi_rsp_handler_oem_mlx(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + const struct ncsi_cmd_oem_pkt *cmd; + const struct ncsi_rsp_oem_mlx_pkt *cmd_mlx; + struct ncsi_rsp_oem_pkt *rsp; + struct ncsi_rsp_oem_mlx_pkt *rsp_mlx; + + /* Get the command header */ + cmd = (const struct ncsi_cmd_oem_pkt *)nh; + cmd_mlx = (const struct ncsi_rsp_oem_mlx_pkt *)cmd->data; + + /* Get the response header */ + rsp = (struct ncsi_rsp_oem_pkt *)rnh; + rsp_mlx = (struct ncsi_rsp_oem_mlx_pkt *)rsp->data; + + /* Ensure the OEM response header matches the command's */ + rsp_mlx->cmd_rev = cmd_mlx->cmd_rev; + rsp_mlx->cmd = cmd_mlx->cmd; + rsp_mlx->param = cmd_mlx->param; + rsp_mlx->optional = cmd_mlx->optional; + + if (cmd_mlx->cmd == NCSI_OEM_MLX_CMD_GMA && + cmd_mlx->param == NCSI_OEM_MLX_CMD_GMA_PARAM) + return ncsi_rsp_handler_oem_mlx_gma(slirp, nh, rnh); + + rsp->rsp.common.length = htons(8); + rsp->rsp.code = htons(NCSI_PKT_RSP_C_UNSUPPORTED); + rsp->rsp.reason = htons(NCSI_PKT_RSP_R_UNKNOWN); + return -ENOENT; +} + static const struct ncsi_rsp_oem_handler { unsigned int mfr_id; int (*handler)(Slirp *slirp, const struct ncsi_pkt_hdr *nh, struct ncsi_rsp_pkt_hdr *rnh); } ncsi_rsp_oem_handlers[] = { - { NCSI_OEM_MFR_MLX_ID, NULL }, + { NCSI_OEM_MFR_MLX_ID, ncsi_rsp_handler_oem_mlx }, { NCSI_OEM_MFR_BCM_ID, NULL }, { NCSI_OEM_MFR_INTEL_ID, NULL }, }; diff --git a/test/ncsitest.c b/test/ncsitest.c index 4a6cada..c763af2 100644 --- a/test/ncsitest.c +++ b/test/ncsitest.c @@ -95,6 +95,59 @@ static void test_ncsi_oem_mlx_unsupported_command(Slirp *slirp) assert(ntohl(oem->mfr_id) == 0x8119); } +static void test_ncsi_oem_mlx_gma(Slirp *slirp) +{ + uint8_t oob_eth_addr[ETH_ALEN] = {0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}; + uint8_t command[] = { + /* Destination MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Source MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Ethertype */ + 0x88, 0xf8, + /* NC-SI Control packet header */ + 0x00, /* MC ID */ + 0x01, /* Header revision */ + 0x00, /* Reserved */ + 0x01, /* Instance ID */ + 0x50, /* Control Packet Type */ + 0x00, /* Channel ID */ + 0x00, /* Reserved */ + 0x08, /* Payload length */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + /* NC-SI OEM packet header */ + 0x00, 0x00, 0x81, 0x19, /* Manufacturer ID: Mellanox */ + /* Vendor Data */ + 0x00, /* Command Revision */ + 0x00, /* Command ID */ + 0x1b, /* Parameter */ + 0x00, /* Optional data */ + }; + const struct ncsi_rsp_oem_pkt *oem = slirp->opaque + ETH_HLEN; + + memset(slirp->oob_eth_addr, 0, ETH_ALEN); + slirp->mfr_id = 0x8119; + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_COMPLETED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_NO_ERROR); + assert(ntohl(oem->mfr_id) == slirp->mfr_id); + assert(ntohs(oem->rsp.common.length) == MLX_GMA_PAYLOAD_LEN); + assert(memcmp(slirp->oob_eth_addr, &oem->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN) == 0); + assert(oem->data[MLX_GMA_STATUS_OFFSET] == 0); + + memcpy(slirp->oob_eth_addr, oob_eth_addr, ETH_ALEN); + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_COMPLETED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_NO_ERROR); + assert(ntohl(oem->mfr_id) == slirp->mfr_id); + assert(ntohs(oem->rsp.common.length) == MLX_GMA_PAYLOAD_LEN); + assert(memcmp(oob_eth_addr, &oem->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN) == 0); + assert(oem->data[MLX_GMA_STATUS_OFFSET] == 1); +} + static ssize_t send_packet(const void *buf, size_t len, void *opaque) { assert(len <= NCSI_RESPONSE_CAPACITY); @@ -115,6 +168,7 @@ int main(int argc, char *argv[]) test_ncsi_get_version_id(slirp); test_ncsi_oem_mlx_unsupported_command(slirp); + test_ncsi_oem_mlx_gma(slirp); slirp_cleanup(slirp); } -- cgit v1.1