.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright Gavin Shan, IBM Corporation 2016. |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or modify |
---|
5 | | - * it under the terms of the GNU General Public License as published by |
---|
6 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
7 | | - * (at your option) any later version. |
---|
8 | 4 | */ |
---|
9 | 5 | |
---|
10 | 6 | #include <linux/module.h> |
---|
11 | 7 | #include <linux/kernel.h> |
---|
12 | 8 | #include <linux/init.h> |
---|
13 | 9 | #include <linux/netdevice.h> |
---|
| 10 | +#include <linux/etherdevice.h> |
---|
14 | 11 | #include <linux/skbuff.h> |
---|
15 | 12 | |
---|
16 | 13 | #include <net/ncsi.h> |
---|
17 | 14 | #include <net/net_namespace.h> |
---|
18 | 15 | #include <net/sock.h> |
---|
| 16 | +#include <net/genetlink.h> |
---|
19 | 17 | |
---|
20 | 18 | #include "internal.h" |
---|
21 | 19 | #include "ncsi-pkt.h" |
---|
| 20 | +#include "ncsi-netlink.h" |
---|
22 | 21 | |
---|
23 | 22 | static int ncsi_validate_rsp_pkt(struct ncsi_request *nr, |
---|
24 | 23 | unsigned short payload) |
---|
.. | .. |
---|
32 | 31 | * before calling this function. |
---|
33 | 32 | */ |
---|
34 | 33 | h = (struct ncsi_rsp_pkt_hdr *)skb_network_header(nr->rsp); |
---|
35 | | - if (h->common.revision != NCSI_PKT_REVISION) |
---|
| 34 | + |
---|
| 35 | + if (h->common.revision != NCSI_PKT_REVISION) { |
---|
| 36 | + netdev_dbg(nr->ndp->ndev.dev, |
---|
| 37 | + "NCSI: unsupported header revision\n"); |
---|
36 | 38 | return -EINVAL; |
---|
37 | | - if (ntohs(h->common.length) != payload) |
---|
| 39 | + } |
---|
| 40 | + if (ntohs(h->common.length) != payload) { |
---|
| 41 | + netdev_dbg(nr->ndp->ndev.dev, |
---|
| 42 | + "NCSI: payload length mismatched\n"); |
---|
38 | 43 | return -EINVAL; |
---|
| 44 | + } |
---|
39 | 45 | |
---|
40 | 46 | /* Check on code and reason */ |
---|
41 | 47 | if (ntohs(h->code) != NCSI_PKT_RSP_C_COMPLETED || |
---|
42 | | - ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR) |
---|
43 | | - return -EINVAL; |
---|
| 48 | + ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR) { |
---|
| 49 | + netdev_dbg(nr->ndp->ndev.dev, |
---|
| 50 | + "NCSI: non zero response/reason code %04xh, %04xh\n", |
---|
| 51 | + ntohs(h->code), ntohs(h->reason)); |
---|
| 52 | + return -EPERM; |
---|
| 53 | + } |
---|
44 | 54 | |
---|
45 | 55 | /* Validate checksum, which might be zeroes if the |
---|
46 | 56 | * sender doesn't support checksum according to NCSI |
---|
47 | 57 | * specification. |
---|
48 | 58 | */ |
---|
49 | | - pchecksum = (__be32 *)((void *)(h + 1) + payload - 4); |
---|
| 59 | + pchecksum = (__be32 *)((void *)(h + 1) + ALIGN(payload, 4) - 4); |
---|
50 | 60 | if (ntohl(*pchecksum) == 0) |
---|
51 | 61 | return 0; |
---|
52 | 62 | |
---|
53 | 63 | checksum = ncsi_calculate_checksum((unsigned char *)h, |
---|
54 | 64 | sizeof(*h) + payload - 4); |
---|
55 | | - if (*pchecksum != htonl(checksum)) |
---|
| 65 | + |
---|
| 66 | + if (*pchecksum != htonl(checksum)) { |
---|
| 67 | + netdev_dbg(nr->ndp->ndev.dev, |
---|
| 68 | + "NCSI: checksum mismatched; recd: %08x calc: %08x\n", |
---|
| 69 | + *pchecksum, htonl(checksum)); |
---|
56 | 70 | return -EINVAL; |
---|
| 71 | + } |
---|
57 | 72 | |
---|
58 | 73 | return 0; |
---|
59 | 74 | } |
---|
.. | .. |
---|
241 | 256 | if (!ncm->enable) |
---|
242 | 257 | return 0; |
---|
243 | 258 | |
---|
244 | | - ncm->enable = 1; |
---|
| 259 | + ncm->enable = 0; |
---|
245 | 260 | return 0; |
---|
246 | 261 | } |
---|
247 | 262 | |
---|
.. | .. |
---|
456 | 471 | memcpy(&ncf->addrs[index], cmd->mac, ETH_ALEN); |
---|
457 | 472 | } else { |
---|
458 | 473 | clear_bit(cmd->index - 1, bitmap); |
---|
459 | | - memset(&ncf->addrs[index], 0, ETH_ALEN); |
---|
| 474 | + eth_zero_addr(&ncf->addrs[index]); |
---|
460 | 475 | } |
---|
461 | 476 | spin_unlock_irqrestore(&nc->lock, flags); |
---|
462 | 477 | |
---|
.. | .. |
---|
594 | 609 | ncm->data[0] = cmd->mode; |
---|
595 | 610 | |
---|
596 | 611 | return 0; |
---|
| 612 | +} |
---|
| 613 | + |
---|
| 614 | +/* Response handler for Mellanox command Get Mac Address */ |
---|
| 615 | +static int ncsi_rsp_handler_oem_mlx_gma(struct ncsi_request *nr) |
---|
| 616 | +{ |
---|
| 617 | + struct ncsi_dev_priv *ndp = nr->ndp; |
---|
| 618 | + struct net_device *ndev = ndp->ndev.dev; |
---|
| 619 | + const struct net_device_ops *ops = ndev->netdev_ops; |
---|
| 620 | + struct ncsi_rsp_oem_pkt *rsp; |
---|
| 621 | + struct sockaddr saddr; |
---|
| 622 | + int ret = 0; |
---|
| 623 | + |
---|
| 624 | + /* Get the response header */ |
---|
| 625 | + rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp); |
---|
| 626 | + |
---|
| 627 | + saddr.sa_family = ndev->type; |
---|
| 628 | + ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; |
---|
| 629 | + memcpy(saddr.sa_data, &rsp->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN); |
---|
| 630 | + /* Set the flag for GMA command which should only be called once */ |
---|
| 631 | + ndp->gma_flag = 1; |
---|
| 632 | + |
---|
| 633 | + ret = ops->ndo_set_mac_address(ndev, &saddr); |
---|
| 634 | + if (ret < 0) |
---|
| 635 | + netdev_warn(ndev, "NCSI: 'Writing mac address to device failed\n"); |
---|
| 636 | + |
---|
| 637 | + return ret; |
---|
| 638 | +} |
---|
| 639 | + |
---|
| 640 | +/* Response handler for Mellanox card */ |
---|
| 641 | +static int ncsi_rsp_handler_oem_mlx(struct ncsi_request *nr) |
---|
| 642 | +{ |
---|
| 643 | + struct ncsi_rsp_oem_mlx_pkt *mlx; |
---|
| 644 | + struct ncsi_rsp_oem_pkt *rsp; |
---|
| 645 | + |
---|
| 646 | + /* Get the response header */ |
---|
| 647 | + rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp); |
---|
| 648 | + mlx = (struct ncsi_rsp_oem_mlx_pkt *)(rsp->data); |
---|
| 649 | + |
---|
| 650 | + if (mlx->cmd == NCSI_OEM_MLX_CMD_GMA && |
---|
| 651 | + mlx->param == NCSI_OEM_MLX_CMD_GMA_PARAM) |
---|
| 652 | + return ncsi_rsp_handler_oem_mlx_gma(nr); |
---|
| 653 | + return 0; |
---|
| 654 | +} |
---|
| 655 | + |
---|
| 656 | +/* Response handler for Broadcom command Get Mac Address */ |
---|
| 657 | +static int ncsi_rsp_handler_oem_bcm_gma(struct ncsi_request *nr) |
---|
| 658 | +{ |
---|
| 659 | + struct ncsi_dev_priv *ndp = nr->ndp; |
---|
| 660 | + struct net_device *ndev = ndp->ndev.dev; |
---|
| 661 | + const struct net_device_ops *ops = ndev->netdev_ops; |
---|
| 662 | + struct ncsi_rsp_oem_pkt *rsp; |
---|
| 663 | + struct sockaddr saddr; |
---|
| 664 | + int ret = 0; |
---|
| 665 | + |
---|
| 666 | + /* Get the response header */ |
---|
| 667 | + rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp); |
---|
| 668 | + |
---|
| 669 | + saddr.sa_family = ndev->type; |
---|
| 670 | + ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; |
---|
| 671 | + memcpy(saddr.sa_data, &rsp->data[BCM_MAC_ADDR_OFFSET], ETH_ALEN); |
---|
| 672 | + /* Increase mac address by 1 for BMC's address */ |
---|
| 673 | + eth_addr_inc((u8 *)saddr.sa_data); |
---|
| 674 | + if (!is_valid_ether_addr((const u8 *)saddr.sa_data)) |
---|
| 675 | + return -ENXIO; |
---|
| 676 | + |
---|
| 677 | + /* Set the flag for GMA command which should only be called once */ |
---|
| 678 | + ndp->gma_flag = 1; |
---|
| 679 | + |
---|
| 680 | + ret = ops->ndo_set_mac_address(ndev, &saddr); |
---|
| 681 | + if (ret < 0) |
---|
| 682 | + netdev_warn(ndev, "NCSI: 'Writing mac address to device failed\n"); |
---|
| 683 | + |
---|
| 684 | + return ret; |
---|
| 685 | +} |
---|
| 686 | + |
---|
| 687 | +/* Response handler for Broadcom card */ |
---|
| 688 | +static int ncsi_rsp_handler_oem_bcm(struct ncsi_request *nr) |
---|
| 689 | +{ |
---|
| 690 | + struct ncsi_rsp_oem_bcm_pkt *bcm; |
---|
| 691 | + struct ncsi_rsp_oem_pkt *rsp; |
---|
| 692 | + |
---|
| 693 | + /* Get the response header */ |
---|
| 694 | + rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp); |
---|
| 695 | + bcm = (struct ncsi_rsp_oem_bcm_pkt *)(rsp->data); |
---|
| 696 | + |
---|
| 697 | + if (bcm->type == NCSI_OEM_BCM_CMD_GMA) |
---|
| 698 | + return ncsi_rsp_handler_oem_bcm_gma(nr); |
---|
| 699 | + return 0; |
---|
| 700 | +} |
---|
| 701 | + |
---|
| 702 | +static struct ncsi_rsp_oem_handler { |
---|
| 703 | + unsigned int mfr_id; |
---|
| 704 | + int (*handler)(struct ncsi_request *nr); |
---|
| 705 | +} ncsi_rsp_oem_handlers[] = { |
---|
| 706 | + { NCSI_OEM_MFR_MLX_ID, ncsi_rsp_handler_oem_mlx }, |
---|
| 707 | + { NCSI_OEM_MFR_BCM_ID, ncsi_rsp_handler_oem_bcm } |
---|
| 708 | +}; |
---|
| 709 | + |
---|
| 710 | +/* Response handler for OEM command */ |
---|
| 711 | +static int ncsi_rsp_handler_oem(struct ncsi_request *nr) |
---|
| 712 | +{ |
---|
| 713 | + struct ncsi_rsp_oem_handler *nrh = NULL; |
---|
| 714 | + struct ncsi_rsp_oem_pkt *rsp; |
---|
| 715 | + unsigned int mfr_id, i; |
---|
| 716 | + |
---|
| 717 | + /* Get the response header */ |
---|
| 718 | + rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp); |
---|
| 719 | + mfr_id = ntohl(rsp->mfr_id); |
---|
| 720 | + |
---|
| 721 | + /* Check for manufacturer id and Find the handler */ |
---|
| 722 | + for (i = 0; i < ARRAY_SIZE(ncsi_rsp_oem_handlers); i++) { |
---|
| 723 | + if (ncsi_rsp_oem_handlers[i].mfr_id == mfr_id) { |
---|
| 724 | + if (ncsi_rsp_oem_handlers[i].handler) |
---|
| 725 | + nrh = &ncsi_rsp_oem_handlers[i]; |
---|
| 726 | + else |
---|
| 727 | + nrh = NULL; |
---|
| 728 | + |
---|
| 729 | + break; |
---|
| 730 | + } |
---|
| 731 | + } |
---|
| 732 | + |
---|
| 733 | + if (!nrh) { |
---|
| 734 | + netdev_err(nr->ndp->ndev.dev, "Received unrecognized OEM packet with MFR-ID (0x%x)\n", |
---|
| 735 | + mfr_id); |
---|
| 736 | + return -ENOENT; |
---|
| 737 | + } |
---|
| 738 | + |
---|
| 739 | + /* Process the packet */ |
---|
| 740 | + return nrh->handler(nr); |
---|
597 | 741 | } |
---|
598 | 742 | |
---|
599 | 743 | static int ncsi_rsp_handler_gvi(struct ncsi_request *nr) |
---|
.. | .. |
---|
900 | 1044 | return 0; |
---|
901 | 1045 | } |
---|
902 | 1046 | |
---|
| 1047 | +static int ncsi_rsp_handler_pldm(struct ncsi_request *nr) |
---|
| 1048 | +{ |
---|
| 1049 | + return 0; |
---|
| 1050 | +} |
---|
| 1051 | + |
---|
| 1052 | +static int ncsi_rsp_handler_netlink(struct ncsi_request *nr) |
---|
| 1053 | +{ |
---|
| 1054 | + struct ncsi_dev_priv *ndp = nr->ndp; |
---|
| 1055 | + struct ncsi_rsp_pkt *rsp; |
---|
| 1056 | + struct ncsi_package *np; |
---|
| 1057 | + struct ncsi_channel *nc; |
---|
| 1058 | + int ret; |
---|
| 1059 | + |
---|
| 1060 | + /* Find the package */ |
---|
| 1061 | + rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp); |
---|
| 1062 | + ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, |
---|
| 1063 | + &np, &nc); |
---|
| 1064 | + if (!np) |
---|
| 1065 | + return -ENODEV; |
---|
| 1066 | + |
---|
| 1067 | + ret = ncsi_send_netlink_rsp(nr, np, nc); |
---|
| 1068 | + |
---|
| 1069 | + return ret; |
---|
| 1070 | +} |
---|
| 1071 | + |
---|
903 | 1072 | static struct ncsi_rsp_handler { |
---|
904 | 1073 | unsigned char type; |
---|
905 | 1074 | int payload; |
---|
.. | .. |
---|
928 | 1097 | { NCSI_PKT_RSP_GVI, 40, ncsi_rsp_handler_gvi }, |
---|
929 | 1098 | { NCSI_PKT_RSP_GC, 32, ncsi_rsp_handler_gc }, |
---|
930 | 1099 | { NCSI_PKT_RSP_GP, -1, ncsi_rsp_handler_gp }, |
---|
931 | | - { NCSI_PKT_RSP_GCPS, 172, ncsi_rsp_handler_gcps }, |
---|
932 | | - { NCSI_PKT_RSP_GNS, 172, ncsi_rsp_handler_gns }, |
---|
933 | | - { NCSI_PKT_RSP_GNPTS, 172, ncsi_rsp_handler_gnpts }, |
---|
| 1100 | + { NCSI_PKT_RSP_GCPS, 204, ncsi_rsp_handler_gcps }, |
---|
| 1101 | + { NCSI_PKT_RSP_GNS, 32, ncsi_rsp_handler_gns }, |
---|
| 1102 | + { NCSI_PKT_RSP_GNPTS, 48, ncsi_rsp_handler_gnpts }, |
---|
934 | 1103 | { NCSI_PKT_RSP_GPS, 8, ncsi_rsp_handler_gps }, |
---|
935 | | - { NCSI_PKT_RSP_OEM, 0, NULL }, |
---|
936 | | - { NCSI_PKT_RSP_PLDM, 0, NULL }, |
---|
937 | | - { NCSI_PKT_RSP_GPUUID, 20, ncsi_rsp_handler_gpuuid } |
---|
| 1104 | + { NCSI_PKT_RSP_OEM, -1, ncsi_rsp_handler_oem }, |
---|
| 1105 | + { NCSI_PKT_RSP_PLDM, -1, ncsi_rsp_handler_pldm }, |
---|
| 1106 | + { NCSI_PKT_RSP_GPUUID, 20, ncsi_rsp_handler_gpuuid }, |
---|
| 1107 | + { NCSI_PKT_RSP_QPNPR, -1, ncsi_rsp_handler_pldm }, |
---|
| 1108 | + { NCSI_PKT_RSP_SNPR, -1, ncsi_rsp_handler_pldm } |
---|
938 | 1109 | }; |
---|
939 | 1110 | |
---|
940 | 1111 | int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev, |
---|
.. | .. |
---|
1002 | 1173 | netdev_warn(ndp->ndev.dev, |
---|
1003 | 1174 | "NCSI: 'bad' packet ignored for type 0x%x\n", |
---|
1004 | 1175 | hdr->type); |
---|
| 1176 | + |
---|
| 1177 | + if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) { |
---|
| 1178 | + if (ret == -EPERM) |
---|
| 1179 | + goto out_netlink; |
---|
| 1180 | + else |
---|
| 1181 | + ncsi_send_netlink_err(ndp->ndev.dev, |
---|
| 1182 | + nr->snd_seq, |
---|
| 1183 | + nr->snd_portid, |
---|
| 1184 | + &nr->nlhdr, |
---|
| 1185 | + ret); |
---|
| 1186 | + } |
---|
1005 | 1187 | goto out; |
---|
1006 | 1188 | } |
---|
1007 | 1189 | |
---|
.. | .. |
---|
1011 | 1193 | netdev_err(ndp->ndev.dev, |
---|
1012 | 1194 | "NCSI: Handler for packet type 0x%x returned %d\n", |
---|
1013 | 1195 | hdr->type, ret); |
---|
| 1196 | + |
---|
| 1197 | +out_netlink: |
---|
| 1198 | + if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) { |
---|
| 1199 | + ret = ncsi_rsp_handler_netlink(nr); |
---|
| 1200 | + if (ret) { |
---|
| 1201 | + netdev_err(ndp->ndev.dev, |
---|
| 1202 | + "NCSI: Netlink handler for packet type 0x%x returned %d\n", |
---|
| 1203 | + hdr->type, ret); |
---|
| 1204 | + } |
---|
| 1205 | + } |
---|
| 1206 | + |
---|
1014 | 1207 | out: |
---|
1015 | 1208 | ncsi_free_request(nr); |
---|
1016 | 1209 | return ret; |
---|