aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2022-05-28 20:08:30 +0000
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2022-05-28 20:08:30 +0000
commitff0694bbdd7222a9daf47ef45ffaad70e290463c (patch)
tree020234a1743c05524918ac7ec91cfd3171aba0e3
parent2facdf288df59690b62037cb91df472b2215e975 (diff)
parentca73d965d73a930ab366a61e96e1af181ff45594 (diff)
downloadslirp-ff0694bbdd7222a9daf47ef45ffaad70e290463c.zip
slirp-ff0694bbdd7222a9daf47ef45ffaad70e290463c.tar.gz
slirp-ff0694bbdd7222a9daf47ef45ffaad70e290463c.tar.bz2
Merge branch 'master' into 'master'
ncsi: Add Mellanox Get MAC Address handler See merge request slirp/libslirp!125
-rw-r--r--meson.build8
-rw-r--r--src/libslirp.h5
-rw-r--r--src/ncsi-pkt.h50
-rw-r--r--src/ncsi.c136
-rw-r--r--src/slirp.c2
-rw-r--r--src/slirp.h1
-rw-r--r--test/ncsitest.c174
7 files changed, 367 insertions, 9 deletions
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/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/ncsi-pkt.h b/src/ncsi-pkt.h
index 39cf844..9dd167c 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,31 @@ 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
+
+/* 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 5bf731b..adb3398 100644
--- a/src/ncsi.c
+++ b/src/ncsi.c
@@ -55,8 +55,123 @@ 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, ncsi_rsp_handler_oem_mlx },
+ { 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, 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 +182,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 +198,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 +208,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 +225,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 },
@@ -135,7 +254,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 } };
@@ -188,10 +307,9 @@ 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;
+ ncsi_rsp_len += ntohs(rnh->common.length);
} else {
rnh->common.length = 0;
rnh->code = htons(NCSI_PKT_RSP_C_UNAVAILABLE);
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;
diff --git a/test/ncsitest.c b/test/ncsitest.c
new file mode 100644
index 0000000..c763af2
--- /dev/null
+++ b/test/ncsitest.c
@@ -0,0 +1,174 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#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(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 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);
+ 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);
+ test_ncsi_oem_mlx_unsupported_command(slirp);
+ test_ncsi_oem_mlx_gma(slirp);
+
+ slirp_cleanup(slirp);
+}