root/net/ncsi/ncsi-cmd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ncsi_calculate_checksum
  2. ncsi_cmd_build_header
  3. ncsi_cmd_handler_default
  4. ncsi_cmd_handler_sp
  5. ncsi_cmd_handler_dc
  6. ncsi_cmd_handler_rc
  7. ncsi_cmd_handler_ae
  8. ncsi_cmd_handler_sl
  9. ncsi_cmd_handler_svf
  10. ncsi_cmd_handler_ev
  11. ncsi_cmd_handler_sma
  12. ncsi_cmd_handler_ebf
  13. ncsi_cmd_handler_egmf
  14. ncsi_cmd_handler_snfc
  15. ncsi_cmd_handler_oem
  16. ncsi_alloc_command
  17. ncsi_xmit_cmd

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright Gavin Shan, IBM Corporation 2016.
   4  */
   5 
   6 #include <linux/module.h>
   7 #include <linux/kernel.h>
   8 #include <linux/init.h>
   9 #include <linux/etherdevice.h>
  10 #include <linux/netdevice.h>
  11 #include <linux/skbuff.h>
  12 
  13 #include <net/ncsi.h>
  14 #include <net/net_namespace.h>
  15 #include <net/sock.h>
  16 #include <net/genetlink.h>
  17 
  18 #include "internal.h"
  19 #include "ncsi-pkt.h"
  20 
  21 u32 ncsi_calculate_checksum(unsigned char *data, int len)
  22 {
  23         u32 checksum = 0;
  24         int i;
  25 
  26         for (i = 0; i < len; i += 2)
  27                 checksum += (((u32)data[i] << 8) | data[i + 1]);
  28 
  29         checksum = (~checksum + 1);
  30         return checksum;
  31 }
  32 
  33 /* This function should be called after the data area has been
  34  * populated completely.
  35  */
  36 static void ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
  37                                   struct ncsi_cmd_arg *nca)
  38 {
  39         u32 checksum;
  40         __be32 *pchecksum;
  41 
  42         h->mc_id        = 0;
  43         h->revision     = NCSI_PKT_REVISION;
  44         h->reserved     = 0;
  45         h->id           = nca->id;
  46         h->type         = nca->type;
  47         h->channel      = NCSI_TO_CHANNEL(nca->package,
  48                                           nca->channel);
  49         h->length       = htons(nca->payload);
  50         h->reserved1[0] = 0;
  51         h->reserved1[1] = 0;
  52 
  53         /* Fill with calculated checksum */
  54         checksum = ncsi_calculate_checksum((unsigned char *)h,
  55                                            sizeof(*h) + nca->payload);
  56         pchecksum = (__be32 *)((void *)h + sizeof(struct ncsi_pkt_hdr) +
  57                     ALIGN(nca->payload, 4));
  58         *pchecksum = htonl(checksum);
  59 }
  60 
  61 static int ncsi_cmd_handler_default(struct sk_buff *skb,
  62                                     struct ncsi_cmd_arg *nca)
  63 {
  64         struct ncsi_cmd_pkt *cmd;
  65 
  66         cmd = skb_put_zero(skb, sizeof(*cmd));
  67         ncsi_cmd_build_header(&cmd->cmd.common, nca);
  68 
  69         return 0;
  70 }
  71 
  72 static int ncsi_cmd_handler_sp(struct sk_buff *skb,
  73                                struct ncsi_cmd_arg *nca)
  74 {
  75         struct ncsi_cmd_sp_pkt *cmd;
  76 
  77         cmd = skb_put_zero(skb, sizeof(*cmd));
  78         cmd->hw_arbitration = nca->bytes[0];
  79         ncsi_cmd_build_header(&cmd->cmd.common, nca);
  80 
  81         return 0;
  82 }
  83 
  84 static int ncsi_cmd_handler_dc(struct sk_buff *skb,
  85                                struct ncsi_cmd_arg *nca)
  86 {
  87         struct ncsi_cmd_dc_pkt *cmd;
  88 
  89         cmd = skb_put_zero(skb, sizeof(*cmd));
  90         cmd->ald = nca->bytes[0];
  91         ncsi_cmd_build_header(&cmd->cmd.common, nca);
  92 
  93         return 0;
  94 }
  95 
  96 static int ncsi_cmd_handler_rc(struct sk_buff *skb,
  97                                struct ncsi_cmd_arg *nca)
  98 {
  99         struct ncsi_cmd_rc_pkt *cmd;
 100 
 101         cmd = skb_put_zero(skb, sizeof(*cmd));
 102         ncsi_cmd_build_header(&cmd->cmd.common, nca);
 103 
 104         return 0;
 105 }
 106 
 107 static int ncsi_cmd_handler_ae(struct sk_buff *skb,
 108                                struct ncsi_cmd_arg *nca)
 109 {
 110         struct ncsi_cmd_ae_pkt *cmd;
 111 
 112         cmd = skb_put_zero(skb, sizeof(*cmd));
 113         cmd->mc_id = nca->bytes[0];
 114         cmd->mode = htonl(nca->dwords[1]);
 115         ncsi_cmd_build_header(&cmd->cmd.common, nca);
 116 
 117         return 0;
 118 }
 119 
 120 static int ncsi_cmd_handler_sl(struct sk_buff *skb,
 121                                struct ncsi_cmd_arg *nca)
 122 {
 123         struct ncsi_cmd_sl_pkt *cmd;
 124 
 125         cmd = skb_put_zero(skb, sizeof(*cmd));
 126         cmd->mode = htonl(nca->dwords[0]);
 127         cmd->oem_mode = htonl(nca->dwords[1]);
 128         ncsi_cmd_build_header(&cmd->cmd.common, nca);
 129 
 130         return 0;
 131 }
 132 
 133 static int ncsi_cmd_handler_svf(struct sk_buff *skb,
 134                                 struct ncsi_cmd_arg *nca)
 135 {
 136         struct ncsi_cmd_svf_pkt *cmd;
 137 
 138         cmd = skb_put_zero(skb, sizeof(*cmd));
 139         cmd->vlan = htons(nca->words[1]);
 140         cmd->index = nca->bytes[6];
 141         cmd->enable = nca->bytes[7];
 142         ncsi_cmd_build_header(&cmd->cmd.common, nca);
 143 
 144         return 0;
 145 }
 146 
 147 static int ncsi_cmd_handler_ev(struct sk_buff *skb,
 148                                struct ncsi_cmd_arg *nca)
 149 {
 150         struct ncsi_cmd_ev_pkt *cmd;
 151 
 152         cmd = skb_put_zero(skb, sizeof(*cmd));
 153         cmd->mode = nca->bytes[3];
 154         ncsi_cmd_build_header(&cmd->cmd.common, nca);
 155 
 156         return 0;
 157 }
 158 
 159 static int ncsi_cmd_handler_sma(struct sk_buff *skb,
 160                                 struct ncsi_cmd_arg *nca)
 161 {
 162         struct ncsi_cmd_sma_pkt *cmd;
 163         int i;
 164 
 165         cmd = skb_put_zero(skb, sizeof(*cmd));
 166         for (i = 0; i < 6; i++)
 167                 cmd->mac[i] = nca->bytes[i];
 168         cmd->index = nca->bytes[6];
 169         cmd->at_e = nca->bytes[7];
 170         ncsi_cmd_build_header(&cmd->cmd.common, nca);
 171 
 172         return 0;
 173 }
 174 
 175 static int ncsi_cmd_handler_ebf(struct sk_buff *skb,
 176                                 struct ncsi_cmd_arg *nca)
 177 {
 178         struct ncsi_cmd_ebf_pkt *cmd;
 179 
 180         cmd = skb_put_zero(skb, sizeof(*cmd));
 181         cmd->mode = htonl(nca->dwords[0]);
 182         ncsi_cmd_build_header(&cmd->cmd.common, nca);
 183 
 184         return 0;
 185 }
 186 
 187 static int ncsi_cmd_handler_egmf(struct sk_buff *skb,
 188                                  struct ncsi_cmd_arg *nca)
 189 {
 190         struct ncsi_cmd_egmf_pkt *cmd;
 191 
 192         cmd = skb_put_zero(skb, sizeof(*cmd));
 193         cmd->mode = htonl(nca->dwords[0]);
 194         ncsi_cmd_build_header(&cmd->cmd.common, nca);
 195 
 196         return 0;
 197 }
 198 
 199 static int ncsi_cmd_handler_snfc(struct sk_buff *skb,
 200                                  struct ncsi_cmd_arg *nca)
 201 {
 202         struct ncsi_cmd_snfc_pkt *cmd;
 203 
 204         cmd = skb_put_zero(skb, sizeof(*cmd));
 205         cmd->mode = nca->bytes[0];
 206         ncsi_cmd_build_header(&cmd->cmd.common, nca);
 207 
 208         return 0;
 209 }
 210 
 211 static int ncsi_cmd_handler_oem(struct sk_buff *skb,
 212                                 struct ncsi_cmd_arg *nca)
 213 {
 214         struct ncsi_cmd_oem_pkt *cmd;
 215         unsigned int len;
 216 
 217         len = sizeof(struct ncsi_cmd_pkt_hdr) + 4;
 218         if (nca->payload < 26)
 219                 len += 26;
 220         else
 221                 len += nca->payload;
 222 
 223         cmd = skb_put_zero(skb, len);
 224         memcpy(&cmd->mfr_id, nca->data, nca->payload);
 225         ncsi_cmd_build_header(&cmd->cmd.common, nca);
 226 
 227         return 0;
 228 }
 229 
 230 static struct ncsi_cmd_handler {
 231         unsigned char type;
 232         int           payload;
 233         int           (*handler)(struct sk_buff *skb,
 234                                  struct ncsi_cmd_arg *nca);
 235 } ncsi_cmd_handlers[] = {
 236         { NCSI_PKT_CMD_CIS,    0, ncsi_cmd_handler_default },
 237         { NCSI_PKT_CMD_SP,     4, ncsi_cmd_handler_sp      },
 238         { NCSI_PKT_CMD_DP,     0, ncsi_cmd_handler_default },
 239         { NCSI_PKT_CMD_EC,     0, ncsi_cmd_handler_default },
 240         { NCSI_PKT_CMD_DC,     4, ncsi_cmd_handler_dc      },
 241         { NCSI_PKT_CMD_RC,     4, ncsi_cmd_handler_rc      },
 242         { NCSI_PKT_CMD_ECNT,   0, ncsi_cmd_handler_default },
 243         { NCSI_PKT_CMD_DCNT,   0, ncsi_cmd_handler_default },
 244         { NCSI_PKT_CMD_AE,     8, ncsi_cmd_handler_ae      },
 245         { NCSI_PKT_CMD_SL,     8, ncsi_cmd_handler_sl      },
 246         { NCSI_PKT_CMD_GLS,    0, ncsi_cmd_handler_default },
 247         { NCSI_PKT_CMD_SVF,    8, ncsi_cmd_handler_svf     },
 248         { NCSI_PKT_CMD_EV,     4, ncsi_cmd_handler_ev      },
 249         { NCSI_PKT_CMD_DV,     0, ncsi_cmd_handler_default },
 250         { NCSI_PKT_CMD_SMA,    8, ncsi_cmd_handler_sma     },
 251         { NCSI_PKT_CMD_EBF,    4, ncsi_cmd_handler_ebf     },
 252         { NCSI_PKT_CMD_DBF,    0, ncsi_cmd_handler_default },
 253         { NCSI_PKT_CMD_EGMF,   4, ncsi_cmd_handler_egmf    },
 254         { NCSI_PKT_CMD_DGMF,   0, ncsi_cmd_handler_default },
 255         { NCSI_PKT_CMD_SNFC,   4, ncsi_cmd_handler_snfc    },
 256         { NCSI_PKT_CMD_GVI,    0, ncsi_cmd_handler_default },
 257         { NCSI_PKT_CMD_GC,     0, ncsi_cmd_handler_default },
 258         { NCSI_PKT_CMD_GP,     0, ncsi_cmd_handler_default },
 259         { NCSI_PKT_CMD_GCPS,   0, ncsi_cmd_handler_default },
 260         { NCSI_PKT_CMD_GNS,    0, ncsi_cmd_handler_default },
 261         { NCSI_PKT_CMD_GNPTS,  0, ncsi_cmd_handler_default },
 262         { NCSI_PKT_CMD_GPS,    0, ncsi_cmd_handler_default },
 263         { NCSI_PKT_CMD_OEM,   -1, ncsi_cmd_handler_oem     },
 264         { NCSI_PKT_CMD_PLDM,   0, NULL                     },
 265         { NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default }
 266 };
 267 
 268 static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
 269 {
 270         struct ncsi_dev_priv *ndp = nca->ndp;
 271         struct ncsi_dev *nd = &ndp->ndev;
 272         struct net_device *dev = nd->dev;
 273         int hlen = LL_RESERVED_SPACE(dev);
 274         int tlen = dev->needed_tailroom;
 275         int len = hlen + tlen;
 276         struct sk_buff *skb;
 277         struct ncsi_request *nr;
 278 
 279         nr = ncsi_alloc_request(ndp, nca->req_flags);
 280         if (!nr)
 281                 return NULL;
 282 
 283         /* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
 284          * The packet needs padding if its payload is less than 26 bytes to
 285          * meet 64 bytes minimal ethernet frame length.
 286          */
 287         len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
 288         if (nca->payload < 26)
 289                 len += 26;
 290         else
 291                 len += nca->payload;
 292 
 293         /* Allocate skb */
 294         skb = alloc_skb(len, GFP_ATOMIC);
 295         if (!skb) {
 296                 ncsi_free_request(nr);
 297                 return NULL;
 298         }
 299 
 300         nr->cmd = skb;
 301         skb_reserve(skb, hlen);
 302         skb_reset_network_header(skb);
 303 
 304         skb->dev = dev;
 305         skb->protocol = htons(ETH_P_NCSI);
 306 
 307         return nr;
 308 }
 309 
 310 int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
 311 {
 312         struct ncsi_cmd_handler *nch = NULL;
 313         struct ncsi_request *nr;
 314         unsigned char type;
 315         struct ethhdr *eh;
 316         int i, ret;
 317 
 318         /* Use OEM generic handler for Netlink request */
 319         if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN)
 320                 type = NCSI_PKT_CMD_OEM;
 321         else
 322                 type = nca->type;
 323 
 324         /* Search for the handler */
 325         for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
 326                 if (ncsi_cmd_handlers[i].type == type) {
 327                         if (ncsi_cmd_handlers[i].handler)
 328                                 nch = &ncsi_cmd_handlers[i];
 329                         else
 330                                 nch = NULL;
 331 
 332                         break;
 333                 }
 334         }
 335 
 336         if (!nch) {
 337                 netdev_err(nca->ndp->ndev.dev,
 338                            "Cannot send packet with type 0x%02x\n", nca->type);
 339                 return -ENOENT;
 340         }
 341 
 342         /* Get packet payload length and allocate the request
 343          * It is expected that if length set as negative in
 344          * handler structure means caller is initializing it
 345          * and setting length in nca before calling xmit function
 346          */
 347         if (nch->payload >= 0)
 348                 nca->payload = nch->payload;
 349         nr = ncsi_alloc_command(nca);
 350         if (!nr)
 351                 return -ENOMEM;
 352 
 353         /* track netlink information */
 354         if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
 355                 nr->snd_seq = nca->info->snd_seq;
 356                 nr->snd_portid = nca->info->snd_portid;
 357                 nr->nlhdr = *nca->info->nlhdr;
 358         }
 359 
 360         /* Prepare the packet */
 361         nca->id = nr->id;
 362         ret = nch->handler(nr->cmd, nca);
 363         if (ret) {
 364                 ncsi_free_request(nr);
 365                 return ret;
 366         }
 367 
 368         /* Fill the ethernet header */
 369         eh = skb_push(nr->cmd, sizeof(*eh));
 370         eh->h_proto = htons(ETH_P_NCSI);
 371         eth_broadcast_addr(eh->h_dest);
 372         eth_broadcast_addr(eh->h_source);
 373 
 374         /* Start the timer for the request that might not have
 375          * corresponding response. Given NCSI is an internal
 376          * connection a 1 second delay should be sufficient.
 377          */
 378         nr->enabled = true;
 379         mod_timer(&nr->timer, jiffies + 1 * HZ);
 380 
 381         /* Send NCSI packet */
 382         skb_get(nr->cmd);
 383         ret = dev_queue_xmit(nr->cmd);
 384         if (ret < 0) {
 385                 ncsi_free_request(nr);
 386                 return ret;
 387         }
 388 
 389         return 0;
 390 }

/* [<][>][^][v][top][bottom][index][help] */