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