Created
October 20, 2018 08:39
-
-
Save bluecmd/8d17d8716a5bffb752abbee0317576a6 to your computer and use it in GitHub Desktop.
NC-SI MAC read
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/include/uapi/linux/ncsi.h b/include/uapi/linux/ncsi.h | |
index 4c292ecbb748..0a26a5576645 100644 | |
--- a/include/uapi/linux/ncsi.h | |
+++ b/include/uapi/linux/ncsi.h | |
@@ -23,6 +23,9 @@ | |
* optionally the preferred NCSI_ATTR_CHANNEL_ID. | |
* @NCSI_CMD_CLEAR_INTERFACE: clear any preferred package/channel combination. | |
* Requires NCSI_ATTR_IFINDEX. | |
+ * @NCSI_CMD_SEND_CMD: send NC-SI command to network card. | |
+ * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID | |
+ * and NCSI_ATTR_CHANNEL_ID. | |
* @NCSI_CMD_MAX: highest command number | |
*/ | |
enum ncsi_nl_commands { | |
@@ -30,6 +33,7 @@ enum ncsi_nl_commands { | |
NCSI_CMD_PKG_INFO, | |
NCSI_CMD_SET_INTERFACE, | |
NCSI_CMD_CLEAR_INTERFACE, | |
+ NCSI_CMD_SEND_CMD, | |
__NCSI_CMD_AFTER_LAST, | |
NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1 | |
@@ -43,6 +47,7 @@ enum ncsi_nl_commands { | |
* @NCSI_ATTR_PACKAGE_LIST: nested array of NCSI_PKG_ATTR attributes | |
* @NCSI_ATTR_PACKAGE_ID: package ID | |
* @NCSI_ATTR_CHANNEL_ID: channel ID | |
+ * @NCSI_ATTR_DATA: command payload | |
* @NCSI_ATTR_MAX: highest attribute number | |
*/ | |
enum ncsi_nl_attrs { | |
@@ -51,6 +56,7 @@ enum ncsi_nl_attrs { | |
NCSI_ATTR_PACKAGE_LIST, | |
NCSI_ATTR_PACKAGE_ID, | |
NCSI_ATTR_CHANNEL_ID, | |
+ NCSI_ATTR_DATA, | |
__NCSI_ATTR_AFTER_LAST, | |
NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1 | |
diff --git a/net/ncsi/Kconfig b/net/ncsi/Kconfig | |
index 08a8a6031fd7..7f2b46108a24 100644 | |
--- a/net/ncsi/Kconfig | |
+++ b/net/ncsi/Kconfig | |
@@ -10,3 +10,9 @@ config NET_NCSI | |
support. Enable this only if your system connects to a network | |
device via NCSI and the ethernet driver you're using supports | |
the protocol explicitly. | |
+config NCSI_OEM_CMD_GET_MAC | |
+ bool "Get NCSI OEM MAC Address" | |
+ depends on NET_NCSI | |
+ ---help--- | |
+ This allows to get MAC address from NCSI firmware and set them back to | |
+ controller. | |
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h | |
index 8055e3965cef..71670d3270f3 100644 | |
--- a/net/ncsi/internal.h | |
+++ b/net/ncsi/internal.h | |
@@ -68,6 +68,22 @@ enum { | |
NCSI_MODE_MAX | |
}; | |
+/* OEM Vendor Manufacture ID */ | |
+#define NCSI_OEM_MFR_MLX_ID 0x8119 | |
+#define NCSI_OEM_MFR_BCM_ID 0x113d | |
+/* 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 */ | |
+/* OEM Command payload lengths*/ | |
+#define NCSI_OEM_BCM_CMD_GMA_LEN 12 | |
+#define NCSI_OEM_MLX_CMD_GMA_LEN 8 | |
+/* Mac address offset in OEM response */ | |
+#define BCM_MAC_ADDR_OFFSET 28 | |
+#define MLX_MAC_ADDR_OFFSET 8 | |
+ | |
+ | |
struct ncsi_channel_version { | |
u32 version; /* Supported BCD encoded NCSI version */ | |
u32 alpha2; /* Supported BCD encoded NCSI version */ | |
@@ -171,6 +187,8 @@ struct ncsi_package; | |
#define NCSI_RESERVED_CHANNEL 0x1f | |
#define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1)) | |
#define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c)) | |
+#define NCSI_MAX_PACKAGE 8 | |
+#define NCSI_MAX_CHANNEL 32 | |
struct ncsi_channel { | |
unsigned char id; | |
@@ -216,11 +234,15 @@ struct ncsi_request { | |
bool used; /* Request that has been assigned */ | |
unsigned int flags; /* NCSI request property */ | |
#define NCSI_REQ_FLAG_EVENT_DRIVEN 1 | |
+#define NCSI_REQ_FLAG_NETLINK_DRIVEN 2 | |
struct ncsi_dev_priv *ndp; /* Associated NCSI device */ | |
struct sk_buff *cmd; /* Associated NCSI command packet */ | |
struct sk_buff *rsp; /* Associated NCSI response packet */ | |
struct timer_list timer; /* Timer on waiting for response */ | |
bool enabled; /* Time has been enabled or not */ | |
+ u32 snd_seq; /* netlink sending sequence number */ | |
+ u32 snd_portid; /* netlink portid of sender */ | |
+ struct nlmsghdr nlhdr; /* netlink message header */ | |
}; | |
enum { | |
@@ -236,6 +258,7 @@ enum { | |
ncsi_dev_state_probe_dp, | |
ncsi_dev_state_config_sp = 0x0301, | |
ncsi_dev_state_config_cis, | |
+ ncsi_dev_state_config_oem_gma, | |
ncsi_dev_state_config_clear_vids, | |
ncsi_dev_state_config_svf, | |
ncsi_dev_state_config_ev, | |
@@ -305,6 +328,8 @@ struct ncsi_cmd_arg { | |
unsigned short words[8]; | |
unsigned int dwords[4]; | |
}; | |
+ unsigned char *data; /* NCSI OEM data */ | |
+ struct genl_info *info; /* Netlink information */ | |
}; | |
extern struct list_head ncsi_dev_list; | |
diff --git a/net/ncsi/ncsi-cmd.c b/net/ncsi/ncsi-cmd.c | |
index 7567ca63aae2..356af474e43c 100644 | |
--- a/net/ncsi/ncsi-cmd.c | |
+++ b/net/ncsi/ncsi-cmd.c | |
@@ -17,6 +17,7 @@ | |
#include <net/ncsi.h> | |
#include <net/net_namespace.h> | |
#include <net/sock.h> | |
+#include <net/genetlink.h> | |
#include "internal.h" | |
#include "ncsi-pkt.h" | |
@@ -211,6 +212,25 @@ static int ncsi_cmd_handler_snfc(struct sk_buff *skb, | |
return 0; | |
} | |
+static int ncsi_cmd_handler_oem(struct sk_buff *skb, | |
+ struct ncsi_cmd_arg *nca) | |
+{ | |
+ struct ncsi_cmd_oem_pkt *cmd; | |
+ unsigned int len; | |
+ | |
+ len = sizeof(struct ncsi_cmd_pkt_hdr) + 4; | |
+ if (nca->payload < 26) | |
+ len += 26; | |
+ else | |
+ len += nca->payload; | |
+ | |
+ cmd = skb_put_zero(skb, len); | |
+ memcpy(&cmd->mfr_id, nca->data, nca->payload); | |
+ ncsi_cmd_build_header(&cmd->cmd.common, nca); | |
+ | |
+ return 0; | |
+} | |
+ | |
static struct ncsi_cmd_handler { | |
unsigned char type; | |
int payload; | |
@@ -244,7 +264,7 @@ static struct ncsi_cmd_handler { | |
{ NCSI_PKT_CMD_GNS, 0, ncsi_cmd_handler_default }, | |
{ NCSI_PKT_CMD_GNPTS, 0, ncsi_cmd_handler_default }, | |
{ NCSI_PKT_CMD_GPS, 0, ncsi_cmd_handler_default }, | |
- { NCSI_PKT_CMD_OEM, 0, NULL }, | |
+ { NCSI_PKT_CMD_OEM, -1, ncsi_cmd_handler_oem }, | |
{ NCSI_PKT_CMD_PLDM, 0, NULL }, | |
{ NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default } | |
}; | |
@@ -316,12 +336,24 @@ int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca) | |
return -ENOENT; | |
} | |
- /* Get packet payload length and allocate the request */ | |
- nca->payload = nch->payload; | |
+ /* Get packet payload length and allocate the request | |
+ * It is expected that if length set as negative in | |
+ * handler structure means caller is initializing it | |
+ * and setting length in nca before calling xmit function | |
+ */ | |
+ if (nch->payload >= 0) | |
+ nca->payload = nch->payload; | |
nr = ncsi_alloc_command(nca); | |
if (!nr) | |
return -ENOMEM; | |
+ /* track netlink information */ | |
+ if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) { | |
+ nr->snd_seq = nca->info->snd_seq; | |
+ nr->snd_portid = nca->info->snd_portid; | |
+ nr->nlhdr = *nca->info->nlhdr; | |
+ } | |
+ | |
/* Prepare the packet */ | |
nca->id = nr->id; | |
ret = nch->handler(nr->cmd, nca); | |
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c | |
index 091284760d21..961ff26dfd4c 100644 | |
--- a/net/ncsi/ncsi-manage.c | |
+++ b/net/ncsi/ncsi-manage.c | |
@@ -19,6 +19,7 @@ | |
#include <net/addrconf.h> | |
#include <net/ipv6.h> | |
#include <net/if_inet6.h> | |
+#include <net/genetlink.h> | |
#include "internal.h" | |
#include "ncsi-pkt.h" | |
@@ -406,6 +407,9 @@ static void ncsi_request_timeout(struct timer_list *t) | |
{ | |
struct ncsi_request *nr = from_timer(nr, t, timer); | |
struct ncsi_dev_priv *ndp = nr->ndp; | |
+ struct ncsi_cmd_pkt *cmd; | |
+ struct ncsi_package *np; | |
+ struct ncsi_channel *nc; | |
unsigned long flags; | |
/* If the request already had associated response, | |
@@ -419,6 +423,18 @@ static void ncsi_request_timeout(struct timer_list *t) | |
} | |
spin_unlock_irqrestore(&ndp->lock, flags); | |
+ if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) { | |
+ if (nr->cmd) { | |
+ /* Find the package */ | |
+ cmd = (struct ncsi_cmd_pkt *) | |
+ skb_network_header(nr->cmd); | |
+ ncsi_find_package_and_channel(ndp, | |
+ cmd->cmd.common.channel, | |
+ &np, &nc); | |
+ ncsi_send_netlink_timeout(nr, np, nc); | |
+ } | |
+ } | |
+ | |
/* Release the request */ | |
ncsi_free_request(nr); | |
} | |
@@ -635,6 +651,61 @@ static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc, | |
return 0; | |
} | |
+#if IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC) | |
+ | |
+/* NCSI OEM Command APIs */ | |
+static void ncsi_oem_gma_handler_bcm(struct ncsi_cmd_arg *nca) | |
+{ | |
+ int ret = 0; | |
+ unsigned char data[NCSI_OEM_BCM_CMD_GMA_LEN]; | |
+ | |
+ nca->payload = NCSI_OEM_BCM_CMD_GMA_LEN; | |
+ | |
+ memset(data, 0, NCSI_OEM_BCM_CMD_GMA_LEN); | |
+ *(unsigned int *)data = ntohl(NCSI_OEM_MFR_BCM_ID); | |
+ data[5] = NCSI_OEM_BCM_CMD_GMA; | |
+ | |
+ nca->data = data; | |
+ | |
+ ret = ncsi_xmit_cmd(nca); | |
+ if (ret) | |
+ netdev_err(nca->ndp->ndev.dev, | |
+ "NCSI: Failed to transmit cmd 0x%x during configure\n", | |
+ nca->type); | |
+} | |
+ | |
+static void ncsi_oem_gma_handler_mlx(struct ncsi_cmd_arg *nca) | |
+{ | |
+ int ret = 0; | |
+ unsigned char data[NCSI_OEM_MLX_CMD_GMA_LEN]; | |
+ | |
+ nca->payload = NCSI_OEM_MLX_CMD_GMA_LEN; | |
+ | |
+ memset(data, 0, NCSI_OEM_MLX_CMD_GMA_LEN); | |
+ *(unsigned int *)data = ntohl(NCSI_OEM_MFR_MLX_ID); | |
+ data[5] = NCSI_OEM_MLX_CMD_GMA; | |
+ data[6] = NCSI_OEM_MLX_CMD_GMA_PARAM; | |
+ | |
+ nca->data = data; | |
+ | |
+ ret = ncsi_xmit_cmd(nca); | |
+ if (ret) | |
+ netdev_err(nca->ndp->ndev.dev, | |
+ "NCSI: Failed to transmit cmd 0x%x during configure\n", | |
+ nca->type); | |
+} | |
+ | |
+/* OEM Command handlers initialization */ | |
+static struct ncsi_oem_gma_handler { | |
+ unsigned int mfr_id; | |
+ void (*handler)(struct ncsi_cmd_arg *nca); | |
+} ncsi_oem_gma_handlers[] = { | |
+ { NCSI_OEM_MFR_BCM_ID, ncsi_oem_gma_handler_bcm }, | |
+ { NCSI_OEM_MFR_MLX_ID, ncsi_oem_gma_handler_mlx } | |
+}; | |
+ | |
+#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */ | |
+ | |
static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) | |
{ | |
struct ncsi_dev *nd = &ndp->ndev; | |
@@ -643,9 +714,10 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) | |
struct ncsi_channel *nc = ndp->active_channel; | |
struct ncsi_channel *hot_nc = NULL; | |
struct ncsi_cmd_arg nca; | |
+ struct ncsi_oem_gma_handler *nch = NULL; | |
unsigned char index; | |
unsigned long flags; | |
- int ret; | |
+ int ret, i; | |
nca.ndp = ndp; | |
nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN; | |
@@ -685,6 +757,40 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) | |
goto error; | |
} | |
+#if IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC) | |
+ nd->state = ncsi_dev_state_config_oem_gma; | |
+ break; | |
+ case ncsi_dev_state_config_oem_gma: | |
+ nca.type = NCSI_PKT_CMD_OEM; | |
+ nca.package = np->id; | |
+ nca.channel = nc->id; | |
+ ndp->pending_req_num = 1; | |
+ | |
+ /* Check for manufacturer id and Find the handler */ | |
+ for (i = 0; i < ARRAY_SIZE(ncsi_oem_gma_handlers); i++) { | |
+ if (ncsi_oem_gma_handlers[i].mfr_id == | |
+ nc->version.mf_id) { | |
+ if (ncsi_oem_gma_handlers[i].handler) | |
+ nch = &ncsi_oem_gma_handlers[i]; | |
+ else | |
+ nch = NULL; | |
+ | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (!nch) { | |
+ netdev_err(ndp->ndev.dev, "No handler available for GMA with MFR-ID (0x%x)\n", | |
+ nc->version.mf_id); | |
+ nd->state = ncsi_dev_state_config_clear_vids; | |
+ schedule_work(&ndp->work); | |
+ break; | |
+ } | |
+ | |
+ /* Get Mac address from NCSI device */ | |
+ nch->handler(&nca); | |
+#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */ | |
+ | |
nd->state = ncsi_dev_state_config_clear_vids; | |
break; | |
case ncsi_dev_state_config_clear_vids: | |
diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c | |
index 45f33d6dedf7..9b48b9fd31f3 100644 | |
--- a/net/ncsi/ncsi-netlink.c | |
+++ b/net/ncsi/ncsi-netlink.c | |
@@ -20,6 +20,7 @@ | |
#include <uapi/linux/ncsi.h> | |
#include "internal.h" | |
+#include "ncsi-pkt.h" | |
#include "ncsi-netlink.h" | |
static struct genl_family ncsi_genl_family; | |
@@ -29,6 +30,7 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { | |
[NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED }, | |
[NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, | |
[NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, | |
+ [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, | |
}; | |
static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) | |
@@ -366,6 +368,202 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) | |
return 0; | |
} | |
+static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info) | |
+{ | |
+ struct ncsi_dev_priv *ndp; | |
+ struct ncsi_pkt_hdr *hdr; | |
+ struct ncsi_cmd_arg nca; | |
+ unsigned char *data; | |
+ u32 package_id; | |
+ u32 channel_id; | |
+ int len, ret; | |
+ | |
+ if (!info || !info->attrs) { | |
+ ret = -EINVAL; | |
+ goto out; | |
+ } | |
+ | |
+ if (!info->attrs[NCSI_ATTR_IFINDEX]) { | |
+ ret = -EINVAL; | |
+ goto out; | |
+ } | |
+ | |
+ if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) { | |
+ ret = -EINVAL; | |
+ goto out; | |
+ } | |
+ | |
+ if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { | |
+ ret = -EINVAL; | |
+ goto out; | |
+ } | |
+ | |
+ if (!info->attrs[NCSI_ATTR_DATA]) { | |
+ ret = -EINVAL; | |
+ goto out; | |
+ } | |
+ | |
+ ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), | |
+ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); | |
+ if (!ndp) { | |
+ ret = -ENODEV; | |
+ goto out; | |
+ } | |
+ | |
+ package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); | |
+ channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); | |
+ | |
+ if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) { | |
+ ret = -ERANGE; | |
+ goto out_netlink; | |
+ } | |
+ | |
+ len = nla_len(info->attrs[NCSI_ATTR_DATA]); | |
+ if (len < sizeof(struct ncsi_pkt_hdr)) { | |
+ netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n", | |
+ package_id); | |
+ ret = -EINVAL; | |
+ goto out_netlink; | |
+ } else { | |
+ data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]); | |
+ } | |
+ | |
+ hdr = (struct ncsi_pkt_hdr *)data; | |
+ | |
+ nca.ndp = ndp; | |
+ nca.package = (unsigned char)package_id; | |
+ nca.channel = (unsigned char)channel_id; | |
+ nca.type = hdr->type; | |
+ nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN; | |
+ nca.info = info; | |
+ nca.payload = ntohs(hdr->length); | |
+ nca.data = data + sizeof(*hdr); | |
+ | |
+ ret = ncsi_xmit_cmd(&nca); | |
+out_netlink: | |
+ if (ret != 0) { | |
+ netdev_err(ndp->ndev.dev, | |
+ "NCSI: Error %d sending command\n", | |
+ ret); | |
+ ncsi_send_netlink_err(ndp->ndev.dev, | |
+ info->snd_seq, | |
+ info->snd_portid, | |
+ info->nlhdr, | |
+ ret); | |
+ } | |
+out: | |
+ return ret; | |
+} | |
+ | |
+int ncsi_send_netlink_rsp(struct ncsi_request *nr, | |
+ struct ncsi_package *np, | |
+ struct ncsi_channel *nc) | |
+{ | |
+ struct sk_buff *skb; | |
+ struct net *net; | |
+ void *hdr; | |
+ int rc; | |
+ | |
+ net = dev_net(nr->rsp->dev); | |
+ | |
+ skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | |
+ if (!skb) | |
+ return -ENOMEM; | |
+ | |
+ hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, | |
+ &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); | |
+ if (!hdr) { | |
+ kfree_skb(skb); | |
+ return -EMSGSIZE; | |
+ } | |
+ | |
+ nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex); | |
+ if (np) | |
+ nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); | |
+ if (nc) | |
+ nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); | |
+ else | |
+ nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); | |
+ | |
+ rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data); | |
+ if (rc) | |
+ goto err; | |
+ | |
+ genlmsg_end(skb, hdr); | |
+ return genlmsg_unicast(net, skb, nr->snd_portid); | |
+ | |
+err: | |
+ kfree_skb(skb); | |
+ return rc; | |
+} | |
+ | |
+int ncsi_send_netlink_timeout(struct ncsi_request *nr, | |
+ struct ncsi_package *np, | |
+ struct ncsi_channel *nc) | |
+{ | |
+ struct sk_buff *skb; | |
+ struct net *net; | |
+ void *hdr; | |
+ | |
+ skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | |
+ if (!skb) | |
+ return -ENOMEM; | |
+ | |
+ hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, | |
+ &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); | |
+ if (!hdr) { | |
+ kfree_skb(skb); | |
+ return -EMSGSIZE; | |
+ } | |
+ | |
+ net = dev_net(nr->cmd->dev); | |
+ | |
+ nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex); | |
+ | |
+ if (np) | |
+ nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); | |
+ else | |
+ nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, | |
+ NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *) | |
+ nr->cmd->data)->channel))); | |
+ | |
+ if (nc) | |
+ nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); | |
+ else | |
+ nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); | |
+ | |
+ genlmsg_end(skb, hdr); | |
+ return genlmsg_unicast(net, skb, nr->snd_portid); | |
+} | |
+ | |
+int ncsi_send_netlink_err(struct net_device *dev, | |
+ u32 snd_seq, | |
+ u32 snd_portid, | |
+ struct nlmsghdr *nlhdr, | |
+ int err) | |
+{ | |
+ struct nlmsghdr *nlh; | |
+ struct nlmsgerr *nle; | |
+ struct sk_buff *skb; | |
+ struct net *net; | |
+ | |
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | |
+ if (!skb) | |
+ return -ENOMEM; | |
+ | |
+ net = dev_net(dev); | |
+ | |
+ nlh = nlmsg_put(skb, snd_portid, snd_seq, | |
+ NLMSG_ERROR, sizeof(*nle), 0); | |
+ nle = (struct nlmsgerr *)nlmsg_data(nlh); | |
+ nle->error = err; | |
+ memcpy(&nle->msg, nlhdr, sizeof(*nlh)); | |
+ | |
+ nlmsg_end(skb, nlh); | |
+ | |
+ return nlmsg_unicast(net->genl_sock, skb, snd_portid); | |
+} | |
+ | |
static const struct genl_ops ncsi_ops[] = { | |
{ | |
.cmd = NCSI_CMD_PKG_INFO, | |
@@ -386,6 +584,12 @@ static const struct genl_ops ncsi_ops[] = { | |
.doit = ncsi_clear_interface_nl, | |
.flags = GENL_ADMIN_PERM, | |
}, | |
+ { | |
+ .cmd = NCSI_CMD_SEND_CMD, | |
+ .policy = ncsi_genl_policy, | |
+ .doit = ncsi_send_cmd_nl, | |
+ .flags = GENL_ADMIN_PERM, | |
+ }, | |
}; | |
static struct genl_family ncsi_genl_family __ro_after_init = { | |
diff --git a/net/ncsi/ncsi-netlink.h b/net/ncsi/ncsi-netlink.h | |
index 91a5c256f8c4..c4a46887a932 100644 | |
--- a/net/ncsi/ncsi-netlink.h | |
+++ b/net/ncsi/ncsi-netlink.h | |
@@ -14,6 +14,18 @@ | |
#include "internal.h" | |
+int ncsi_send_netlink_rsp(struct ncsi_request *nr, | |
+ struct ncsi_package *np, | |
+ struct ncsi_channel *nc); | |
+int ncsi_send_netlink_timeout(struct ncsi_request *nr, | |
+ struct ncsi_package *np, | |
+ struct ncsi_channel *nc); | |
+int ncsi_send_netlink_err(struct net_device *dev, | |
+ u32 snd_seq, | |
+ u32 snd_portid, | |
+ struct nlmsghdr *nlhdr, | |
+ int err); | |
+ | |
int ncsi_init_netlink(struct net_device *dev); | |
int ncsi_unregister_netlink(struct net_device *dev); | |
diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h | |
index 91b4b66438df..2a6d83a596c9 100644 | |
--- a/net/ncsi/ncsi-pkt.h | |
+++ b/net/ncsi/ncsi-pkt.h | |
@@ -151,6 +151,37 @@ struct ncsi_cmd_snfc_pkt { | |
unsigned char pad[22]; | |
}; | |
+/* 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 */ | |
+}; | |
+ | |
+/* 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 */ | |
+}; | |
+ | |
+/* 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 */ | |
+}; | |
+ | |
+/* Broadcom Response Data */ | |
+struct ncsi_rsp_oem_bcm_pkt { | |
+ unsigned char ver; /* Payload Version */ | |
+ unsigned char type; /* OEM Command type */ | |
+ __be16 len; /* Payload Length */ | |
+ unsigned char data[]; /* Cmd specific Data */ | |
+}; | |
+ | |
/* Get Link Status */ | |
struct ncsi_rsp_gls_pkt { | |
struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c | |
index 930c1d3796f0..56145f9fc92d 100644 | |
--- a/net/ncsi/ncsi-rsp.c | |
+++ b/net/ncsi/ncsi-rsp.c | |
@@ -16,9 +16,11 @@ | |
#include <net/ncsi.h> | |
#include <net/net_namespace.h> | |
#include <net/sock.h> | |
+#include <net/genetlink.h> | |
#include "internal.h" | |
#include "ncsi-pkt.h" | |
+#include "ncsi-netlink.h" | |
static int ncsi_validate_rsp_pkt(struct ncsi_request *nr, | |
unsigned short payload) | |
@@ -32,15 +34,25 @@ static int ncsi_validate_rsp_pkt(struct ncsi_request *nr, | |
* before calling this function. | |
*/ | |
h = (struct ncsi_rsp_pkt_hdr *)skb_network_header(nr->rsp); | |
- if (h->common.revision != NCSI_PKT_REVISION) | |
+ | |
+ if (h->common.revision != NCSI_PKT_REVISION) { | |
+ netdev_dbg(nr->ndp->ndev.dev, | |
+ "NCSI: unsupported header revision\n"); | |
return -EINVAL; | |
- if (ntohs(h->common.length) != payload) | |
+ } | |
+ if (ntohs(h->common.length) != payload) { | |
+ netdev_dbg(nr->ndp->ndev.dev, | |
+ "NCSI: payload length mismatched\n"); | |
return -EINVAL; | |
+ } | |
/* Check on code and reason */ | |
if (ntohs(h->code) != NCSI_PKT_RSP_C_COMPLETED || | |
- ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR) | |
- return -EINVAL; | |
+ ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR) { | |
+ netdev_dbg(nr->ndp->ndev.dev, | |
+ "NCSI: non zero response/reason code\n"); | |
+ return -EPERM; | |
+ } | |
/* Validate checksum, which might be zeroes if the | |
* sender doesn't support checksum according to NCSI | |
@@ -52,8 +64,11 @@ static int ncsi_validate_rsp_pkt(struct ncsi_request *nr, | |
checksum = ncsi_calculate_checksum((unsigned char *)h, | |
sizeof(*h) + payload - 4); | |
- if (*pchecksum != htonl(checksum)) | |
+ | |
+ if (*pchecksum != htonl(checksum)) { | |
+ netdev_dbg(nr->ndp->ndev.dev, "NCSI: checksum mismatched\n"); | |
return -EINVAL; | |
+ } | |
return 0; | |
} | |
@@ -596,6 +611,124 @@ static int ncsi_rsp_handler_snfc(struct ncsi_request *nr) | |
return 0; | |
} | |
+/* Response handler for Mellanox command Get Mac Address */ | |
+static int ncsi_rsp_handler_oem_mlx_gma(struct ncsi_request *nr) | |
+{ | |
+ struct ncsi_rsp_oem_pkt *rsp; | |
+ struct ncsi_dev_priv *ndp = nr->ndp; | |
+ struct net_device *ndev = ndp->ndev.dev; | |
+ int ret = 0; | |
+ const struct net_device_ops *ops = ndev->netdev_ops; | |
+ struct sockaddr saddr; | |
+ | |
+ /* Get the response header */ | |
+ rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp); | |
+ | |
+ saddr.sa_family = ndev->type; | |
+ ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; | |
+ memcpy(saddr.sa_data, &rsp->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN); | |
+ ret = ops->ndo_set_mac_address(ndev, &saddr); | |
+ if (ret < 0) | |
+ netdev_warn(ndev, "NCSI: Writing mac address to device failed\n"); | |
+ | |
+ return ret; | |
+} | |
+ | |
+/* Response handler for Mellanox card */ | |
+static int ncsi_rsp_handler_oem_mlx(struct ncsi_request *nr) | |
+{ | |
+ struct ncsi_rsp_oem_pkt *rsp; | |
+ struct ncsi_rsp_oem_mlx_pkt *mlx; | |
+ | |
+ /* Get the response header */ | |
+ rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp); | |
+ mlx = (struct ncsi_rsp_oem_mlx_pkt *)(rsp->data); | |
+ | |
+ if (mlx->cmd == NCSI_OEM_MLX_CMD_GMA && | |
+ mlx->param == NCSI_OEM_MLX_CMD_GMA_PARAM) | |
+ return ncsi_rsp_handler_oem_mlx_gma(nr); | |
+ return 0; | |
+} | |
+ | |
+/* Response handler for Broadcom command Get Mac Address */ | |
+static int ncsi_rsp_handler_oem_bcm_gma(struct ncsi_request *nr) | |
+{ | |
+ struct ncsi_rsp_oem_pkt *rsp; | |
+ struct ncsi_dev_priv *ndp = nr->ndp; | |
+ struct net_device *ndev = ndp->ndev.dev; | |
+ int ret = 0; | |
+ const struct net_device_ops *ops = ndev->netdev_ops; | |
+ struct sockaddr saddr; | |
+ | |
+ /* Get the response header */ | |
+ rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp); | |
+ | |
+ saddr.sa_family = ndev->type; | |
+ ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; | |
+ memcpy(saddr.sa_data, &rsp->data[BCM_MAC_ADDR_OFFSET], ETH_ALEN); | |
+ ret = ops->ndo_set_mac_address(ndev, &saddr); | |
+ if (ret < 0) | |
+ netdev_warn(ndev, "NCSI: Writing mac address to device failed\n"); | |
+ | |
+ return ret; | |
+} | |
+ | |
+/* Response handler for Broadcom card */ | |
+static int ncsi_rsp_handler_oem_bcm(struct ncsi_request *nr) | |
+{ | |
+ struct ncsi_rsp_oem_pkt *rsp; | |
+ struct ncsi_rsp_oem_bcm_pkt *bcm; | |
+ | |
+ /* Get the response header */ | |
+ rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp); | |
+ bcm = (struct ncsi_rsp_oem_bcm_pkt *)(rsp->data); | |
+ | |
+ if (bcm->type == NCSI_OEM_BCM_CMD_GMA) | |
+ return ncsi_rsp_handler_oem_bcm_gma(nr); | |
+ return 0; | |
+} | |
+ | |
+static struct ncsi_rsp_oem_handler { | |
+ unsigned int mfr_id; | |
+ int (*handler)(struct ncsi_request *nr); | |
+} ncsi_rsp_oem_handlers[] = { | |
+ { NCSI_OEM_MFR_MLX_ID, ncsi_rsp_handler_oem_mlx }, | |
+ { NCSI_OEM_MFR_BCM_ID, ncsi_rsp_handler_oem_bcm } | |
+}; | |
+ | |
+/* Response handler for OEM command */ | |
+static int ncsi_rsp_handler_oem(struct ncsi_request *nr) | |
+{ | |
+ struct ncsi_rsp_oem_pkt *rsp; | |
+ struct ncsi_rsp_oem_handler *nrh = NULL; | |
+ unsigned int mfr_id, i; | |
+ | |
+ /* Get the response header */ | |
+ rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp); | |
+ mfr_id = ntohl(rsp->mfr_id); | |
+ | |
+ /* Check for manufacturer id and Find the handler */ | |
+ for (i = 0; i < ARRAY_SIZE(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) { | |
+ netdev_err(nr->ndp->ndev.dev, "Received unrecognized OEM packet with MFR-ID (0x%x)\n", | |
+ mfr_id); | |
+ return -ENOENT; | |
+ } | |
+ | |
+ /* Process the packet */ | |
+ return nrh->handler(nr); | |
+} | |
+ | |
static int ncsi_rsp_handler_gvi(struct ncsi_request *nr) | |
{ | |
struct ncsi_rsp_gvi_pkt *rsp; | |
@@ -900,6 +1033,26 @@ static int ncsi_rsp_handler_gpuuid(struct ncsi_request *nr) | |
return 0; | |
} | |
+static int ncsi_rsp_handler_netlink(struct ncsi_request *nr) | |
+{ | |
+ struct ncsi_dev_priv *ndp = nr->ndp; | |
+ struct ncsi_rsp_pkt *rsp; | |
+ struct ncsi_package *np; | |
+ struct ncsi_channel *nc; | |
+ int ret; | |
+ | |
+ /* Find the package */ | |
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp); | |
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, | |
+ &np, &nc); | |
+ if (!np) | |
+ return -ENODEV; | |
+ | |
+ ret = ncsi_send_netlink_rsp(nr, np, nc); | |
+ | |
+ return ret; | |
+} | |
+ | |
static struct ncsi_rsp_handler { | |
unsigned char type; | |
int payload; | |
@@ -932,7 +1085,7 @@ static struct ncsi_rsp_handler { | |
{ NCSI_PKT_RSP_GNS, 172, ncsi_rsp_handler_gns }, | |
{ NCSI_PKT_RSP_GNPTS, 172, ncsi_rsp_handler_gnpts }, | |
{ NCSI_PKT_RSP_GPS, 8, ncsi_rsp_handler_gps }, | |
- { NCSI_PKT_RSP_OEM, 0, NULL }, | |
+ { NCSI_PKT_RSP_OEM, -1, ncsi_rsp_handler_oem }, | |
{ NCSI_PKT_RSP_PLDM, 0, NULL }, | |
{ NCSI_PKT_RSP_GPUUID, 20, ncsi_rsp_handler_gpuuid } | |
}; | |
@@ -1002,6 +1155,17 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev, | |
netdev_warn(ndp->ndev.dev, | |
"NCSI: 'bad' packet ignored for type 0x%x\n", | |
hdr->type); | |
+ | |
+ if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) { | |
+ if (ret == -EPERM) | |
+ goto out_netlink; | |
+ else | |
+ ncsi_send_netlink_err(ndp->ndev.dev, | |
+ nr->snd_seq, | |
+ nr->snd_portid, | |
+ &nr->nlhdr, | |
+ ret); | |
+ } | |
goto out; | |
} | |
@@ -1011,6 +1175,17 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev, | |
netdev_err(ndp->ndev.dev, | |
"NCSI: Handler for packet type 0x%x returned %d\n", | |
hdr->type, ret); | |
+ | |
+out_netlink: | |
+ if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) { | |
+ ret = ncsi_rsp_handler_netlink(nr); | |
+ if (ret) { | |
+ netdev_err(ndp->ndev.dev, | |
+ "NCSI: Netlink handler for packet type 0x%x returned %d\n", | |
+ hdr->type, ret); | |
+ } | |
+ } | |
+ | |
out: | |
ncsi_free_request(nr); | |
return ret; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment