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