0001
0002
0003
0004
0005
0006 #include <linux/module.h>
0007 #include <linux/kernel.h>
0008 #include <linux/init.h>
0009 #include <linux/etherdevice.h>
0010 #include <linux/netdevice.h>
0011 #include <linux/skbuff.h>
0012
0013 #include <net/ncsi.h>
0014 #include <net/net_namespace.h>
0015 #include <net/sock.h>
0016 #include <net/genetlink.h>
0017
0018 #include "internal.h"
0019 #include "ncsi-pkt.h"
0020
0021 static const int padding_bytes = 26;
0022
0023 u32 ncsi_calculate_checksum(unsigned char *data, int len)
0024 {
0025 u32 checksum = 0;
0026 int i;
0027
0028 for (i = 0; i < len; i += 2)
0029 checksum += (((u32)data[i] << 8) | data[i + 1]);
0030
0031 checksum = (~checksum + 1);
0032 return checksum;
0033 }
0034
0035
0036
0037
0038 static void ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
0039 struct ncsi_cmd_arg *nca)
0040 {
0041 u32 checksum;
0042 __be32 *pchecksum;
0043
0044 h->mc_id = 0;
0045 h->revision = NCSI_PKT_REVISION;
0046 h->reserved = 0;
0047 h->id = nca->id;
0048 h->type = nca->type;
0049 h->channel = NCSI_TO_CHANNEL(nca->package,
0050 nca->channel);
0051 h->length = htons(nca->payload);
0052 h->reserved1[0] = 0;
0053 h->reserved1[1] = 0;
0054
0055
0056 checksum = ncsi_calculate_checksum((unsigned char *)h,
0057 sizeof(*h) + nca->payload);
0058 pchecksum = (__be32 *)((void *)h + sizeof(struct ncsi_pkt_hdr) +
0059 ALIGN(nca->payload, 4));
0060 *pchecksum = htonl(checksum);
0061 }
0062
0063 static int ncsi_cmd_handler_default(struct sk_buff *skb,
0064 struct ncsi_cmd_arg *nca)
0065 {
0066 struct ncsi_cmd_pkt *cmd;
0067
0068 cmd = skb_put_zero(skb, sizeof(*cmd));
0069 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0070
0071 return 0;
0072 }
0073
0074 static int ncsi_cmd_handler_sp(struct sk_buff *skb,
0075 struct ncsi_cmd_arg *nca)
0076 {
0077 struct ncsi_cmd_sp_pkt *cmd;
0078
0079 cmd = skb_put_zero(skb, sizeof(*cmd));
0080 cmd->hw_arbitration = nca->bytes[0];
0081 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0082
0083 return 0;
0084 }
0085
0086 static int ncsi_cmd_handler_dc(struct sk_buff *skb,
0087 struct ncsi_cmd_arg *nca)
0088 {
0089 struct ncsi_cmd_dc_pkt *cmd;
0090
0091 cmd = skb_put_zero(skb, sizeof(*cmd));
0092 cmd->ald = nca->bytes[0];
0093 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0094
0095 return 0;
0096 }
0097
0098 static int ncsi_cmd_handler_rc(struct sk_buff *skb,
0099 struct ncsi_cmd_arg *nca)
0100 {
0101 struct ncsi_cmd_rc_pkt *cmd;
0102
0103 cmd = skb_put_zero(skb, sizeof(*cmd));
0104 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0105
0106 return 0;
0107 }
0108
0109 static int ncsi_cmd_handler_ae(struct sk_buff *skb,
0110 struct ncsi_cmd_arg *nca)
0111 {
0112 struct ncsi_cmd_ae_pkt *cmd;
0113
0114 cmd = skb_put_zero(skb, sizeof(*cmd));
0115 cmd->mc_id = nca->bytes[0];
0116 cmd->mode = htonl(nca->dwords[1]);
0117 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0118
0119 return 0;
0120 }
0121
0122 static int ncsi_cmd_handler_sl(struct sk_buff *skb,
0123 struct ncsi_cmd_arg *nca)
0124 {
0125 struct ncsi_cmd_sl_pkt *cmd;
0126
0127 cmd = skb_put_zero(skb, sizeof(*cmd));
0128 cmd->mode = htonl(nca->dwords[0]);
0129 cmd->oem_mode = htonl(nca->dwords[1]);
0130 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0131
0132 return 0;
0133 }
0134
0135 static int ncsi_cmd_handler_svf(struct sk_buff *skb,
0136 struct ncsi_cmd_arg *nca)
0137 {
0138 struct ncsi_cmd_svf_pkt *cmd;
0139
0140 cmd = skb_put_zero(skb, sizeof(*cmd));
0141 cmd->vlan = htons(nca->words[1]);
0142 cmd->index = nca->bytes[6];
0143 cmd->enable = nca->bytes[7];
0144 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0145
0146 return 0;
0147 }
0148
0149 static int ncsi_cmd_handler_ev(struct sk_buff *skb,
0150 struct ncsi_cmd_arg *nca)
0151 {
0152 struct ncsi_cmd_ev_pkt *cmd;
0153
0154 cmd = skb_put_zero(skb, sizeof(*cmd));
0155 cmd->mode = nca->bytes[3];
0156 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0157
0158 return 0;
0159 }
0160
0161 static int ncsi_cmd_handler_sma(struct sk_buff *skb,
0162 struct ncsi_cmd_arg *nca)
0163 {
0164 struct ncsi_cmd_sma_pkt *cmd;
0165 int i;
0166
0167 cmd = skb_put_zero(skb, sizeof(*cmd));
0168 for (i = 0; i < 6; i++)
0169 cmd->mac[i] = nca->bytes[i];
0170 cmd->index = nca->bytes[6];
0171 cmd->at_e = nca->bytes[7];
0172 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0173
0174 return 0;
0175 }
0176
0177 static int ncsi_cmd_handler_ebf(struct sk_buff *skb,
0178 struct ncsi_cmd_arg *nca)
0179 {
0180 struct ncsi_cmd_ebf_pkt *cmd;
0181
0182 cmd = skb_put_zero(skb, sizeof(*cmd));
0183 cmd->mode = htonl(nca->dwords[0]);
0184 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0185
0186 return 0;
0187 }
0188
0189 static int ncsi_cmd_handler_egmf(struct sk_buff *skb,
0190 struct ncsi_cmd_arg *nca)
0191 {
0192 struct ncsi_cmd_egmf_pkt *cmd;
0193
0194 cmd = skb_put_zero(skb, sizeof(*cmd));
0195 cmd->mode = htonl(nca->dwords[0]);
0196 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0197
0198 return 0;
0199 }
0200
0201 static int ncsi_cmd_handler_snfc(struct sk_buff *skb,
0202 struct ncsi_cmd_arg *nca)
0203 {
0204 struct ncsi_cmd_snfc_pkt *cmd;
0205
0206 cmd = skb_put_zero(skb, sizeof(*cmd));
0207 cmd->mode = nca->bytes[0];
0208 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0209
0210 return 0;
0211 }
0212
0213 static int ncsi_cmd_handler_oem(struct sk_buff *skb,
0214 struct ncsi_cmd_arg *nca)
0215 {
0216 struct ncsi_cmd_oem_pkt *cmd;
0217 unsigned int len;
0218 int payload;
0219
0220
0221
0222
0223
0224
0225
0226 payload = ALIGN(nca->payload, 4);
0227 len = sizeof(struct ncsi_cmd_pkt_hdr) + 4;
0228 len += max(payload, padding_bytes);
0229
0230 cmd = skb_put_zero(skb, len);
0231 memcpy(&cmd->mfr_id, nca->data, nca->payload);
0232 ncsi_cmd_build_header(&cmd->cmd.common, nca);
0233
0234 return 0;
0235 }
0236
0237 static struct ncsi_cmd_handler {
0238 unsigned char type;
0239 int payload;
0240 int (*handler)(struct sk_buff *skb,
0241 struct ncsi_cmd_arg *nca);
0242 } ncsi_cmd_handlers[] = {
0243 { NCSI_PKT_CMD_CIS, 0, ncsi_cmd_handler_default },
0244 { NCSI_PKT_CMD_SP, 4, ncsi_cmd_handler_sp },
0245 { NCSI_PKT_CMD_DP, 0, ncsi_cmd_handler_default },
0246 { NCSI_PKT_CMD_EC, 0, ncsi_cmd_handler_default },
0247 { NCSI_PKT_CMD_DC, 4, ncsi_cmd_handler_dc },
0248 { NCSI_PKT_CMD_RC, 4, ncsi_cmd_handler_rc },
0249 { NCSI_PKT_CMD_ECNT, 0, ncsi_cmd_handler_default },
0250 { NCSI_PKT_CMD_DCNT, 0, ncsi_cmd_handler_default },
0251 { NCSI_PKT_CMD_AE, 8, ncsi_cmd_handler_ae },
0252 { NCSI_PKT_CMD_SL, 8, ncsi_cmd_handler_sl },
0253 { NCSI_PKT_CMD_GLS, 0, ncsi_cmd_handler_default },
0254 { NCSI_PKT_CMD_SVF, 8, ncsi_cmd_handler_svf },
0255 { NCSI_PKT_CMD_EV, 4, ncsi_cmd_handler_ev },
0256 { NCSI_PKT_CMD_DV, 0, ncsi_cmd_handler_default },
0257 { NCSI_PKT_CMD_SMA, 8, ncsi_cmd_handler_sma },
0258 { NCSI_PKT_CMD_EBF, 4, ncsi_cmd_handler_ebf },
0259 { NCSI_PKT_CMD_DBF, 0, ncsi_cmd_handler_default },
0260 { NCSI_PKT_CMD_EGMF, 4, ncsi_cmd_handler_egmf },
0261 { NCSI_PKT_CMD_DGMF, 0, ncsi_cmd_handler_default },
0262 { NCSI_PKT_CMD_SNFC, 4, ncsi_cmd_handler_snfc },
0263 { NCSI_PKT_CMD_GVI, 0, ncsi_cmd_handler_default },
0264 { NCSI_PKT_CMD_GC, 0, ncsi_cmd_handler_default },
0265 { NCSI_PKT_CMD_GP, 0, ncsi_cmd_handler_default },
0266 { NCSI_PKT_CMD_GCPS, 0, ncsi_cmd_handler_default },
0267 { NCSI_PKT_CMD_GNS, 0, ncsi_cmd_handler_default },
0268 { NCSI_PKT_CMD_GNPTS, 0, ncsi_cmd_handler_default },
0269 { NCSI_PKT_CMD_GPS, 0, ncsi_cmd_handler_default },
0270 { NCSI_PKT_CMD_OEM, -1, ncsi_cmd_handler_oem },
0271 { NCSI_PKT_CMD_PLDM, 0, NULL },
0272 { NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default }
0273 };
0274
0275 static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
0276 {
0277 struct ncsi_dev_priv *ndp = nca->ndp;
0278 struct ncsi_dev *nd = &ndp->ndev;
0279 struct net_device *dev = nd->dev;
0280 int hlen = LL_RESERVED_SPACE(dev);
0281 int tlen = dev->needed_tailroom;
0282 int payload;
0283 int len = hlen + tlen;
0284 struct sk_buff *skb;
0285 struct ncsi_request *nr;
0286
0287 nr = ncsi_alloc_request(ndp, nca->req_flags);
0288 if (!nr)
0289 return NULL;
0290
0291
0292
0293
0294
0295
0296
0297 len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
0298 payload = ALIGN(nca->payload, 4);
0299 len += max(payload, padding_bytes);
0300
0301
0302 skb = alloc_skb(len, GFP_ATOMIC);
0303 if (!skb) {
0304 ncsi_free_request(nr);
0305 return NULL;
0306 }
0307
0308 nr->cmd = skb;
0309 skb_reserve(skb, hlen);
0310 skb_reset_network_header(skb);
0311
0312 skb->dev = dev;
0313 skb->protocol = htons(ETH_P_NCSI);
0314
0315 return nr;
0316 }
0317
0318 int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
0319 {
0320 struct ncsi_cmd_handler *nch = NULL;
0321 struct ncsi_request *nr;
0322 unsigned char type;
0323 struct ethhdr *eh;
0324 int i, ret;
0325
0326
0327 if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN)
0328 type = NCSI_PKT_CMD_OEM;
0329 else
0330 type = nca->type;
0331
0332
0333 for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
0334 if (ncsi_cmd_handlers[i].type == type) {
0335 if (ncsi_cmd_handlers[i].handler)
0336 nch = &ncsi_cmd_handlers[i];
0337 else
0338 nch = NULL;
0339
0340 break;
0341 }
0342 }
0343
0344 if (!nch) {
0345 netdev_err(nca->ndp->ndev.dev,
0346 "Cannot send packet with type 0x%02x\n", nca->type);
0347 return -ENOENT;
0348 }
0349
0350
0351
0352
0353
0354
0355 if (nch->payload >= 0)
0356 nca->payload = nch->payload;
0357 nr = ncsi_alloc_command(nca);
0358 if (!nr)
0359 return -ENOMEM;
0360
0361
0362 if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
0363 nr->snd_seq = nca->info->snd_seq;
0364 nr->snd_portid = nca->info->snd_portid;
0365 nr->nlhdr = *nca->info->nlhdr;
0366 }
0367
0368
0369 nca->id = nr->id;
0370 ret = nch->handler(nr->cmd, nca);
0371 if (ret) {
0372 ncsi_free_request(nr);
0373 return ret;
0374 }
0375
0376
0377 eh = skb_push(nr->cmd, sizeof(*eh));
0378 eh->h_proto = htons(ETH_P_NCSI);
0379 eth_broadcast_addr(eh->h_dest);
0380
0381
0382
0383
0384
0385 if (nca->ndp->gma_flag == 1)
0386 memcpy(eh->h_source, nca->ndp->ndev.dev->dev_addr, ETH_ALEN);
0387 else
0388 eth_broadcast_addr(eh->h_source);
0389
0390
0391
0392
0393
0394 nr->enabled = true;
0395 mod_timer(&nr->timer, jiffies + 1 * HZ);
0396
0397
0398 skb_get(nr->cmd);
0399 ret = dev_queue_xmit(nr->cmd);
0400 if (ret < 0) {
0401 ncsi_free_request(nr);
0402 return ret;
0403 }
0404
0405 return 0;
0406 }