| .. | .. |
|---|
| 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> |
|---|
| .. | .. |
|---|
| 17 | 13 | #include <net/ncsi.h> |
|---|
| 18 | 14 | #include <net/net_namespace.h> |
|---|
| 19 | 15 | #include <net/sock.h> |
|---|
| 16 | +#include <net/genetlink.h> |
|---|
| 20 | 17 | |
|---|
| 21 | 18 | #include "internal.h" |
|---|
| 22 | 19 | #include "ncsi-pkt.h" |
|---|
| 20 | + |
|---|
| 21 | +static const int padding_bytes = 26; |
|---|
| 23 | 22 | |
|---|
| 24 | 23 | u32 ncsi_calculate_checksum(unsigned char *data, int len) |
|---|
| 25 | 24 | { |
|---|
| .. | .. |
|---|
| 57 | 56 | checksum = ncsi_calculate_checksum((unsigned char *)h, |
|---|
| 58 | 57 | sizeof(*h) + nca->payload); |
|---|
| 59 | 58 | pchecksum = (__be32 *)((void *)h + sizeof(struct ncsi_pkt_hdr) + |
|---|
| 60 | | - nca->payload); |
|---|
| 59 | + ALIGN(nca->payload, 4)); |
|---|
| 61 | 60 | *pchecksum = htonl(checksum); |
|---|
| 62 | 61 | } |
|---|
| 63 | 62 | |
|---|
| .. | .. |
|---|
| 211 | 210 | return 0; |
|---|
| 212 | 211 | } |
|---|
| 213 | 212 | |
|---|
| 213 | +static 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 | + |
|---|
| 214 | 237 | static struct ncsi_cmd_handler { |
|---|
| 215 | 238 | unsigned char type; |
|---|
| 216 | 239 | int payload; |
|---|
| .. | .. |
|---|
| 244 | 267 | { NCSI_PKT_CMD_GNS, 0, ncsi_cmd_handler_default }, |
|---|
| 245 | 268 | { NCSI_PKT_CMD_GNPTS, 0, ncsi_cmd_handler_default }, |
|---|
| 246 | 269 | { NCSI_PKT_CMD_GPS, 0, ncsi_cmd_handler_default }, |
|---|
| 247 | | - { NCSI_PKT_CMD_OEM, 0, NULL }, |
|---|
| 270 | + { NCSI_PKT_CMD_OEM, -1, ncsi_cmd_handler_oem }, |
|---|
| 248 | 271 | { NCSI_PKT_CMD_PLDM, 0, NULL }, |
|---|
| 249 | 272 | { NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default } |
|---|
| 250 | 273 | }; |
|---|
| .. | .. |
|---|
| 256 | 279 | struct net_device *dev = nd->dev; |
|---|
| 257 | 280 | int hlen = LL_RESERVED_SPACE(dev); |
|---|
| 258 | 281 | int tlen = dev->needed_tailroom; |
|---|
| 282 | + int payload; |
|---|
| 259 | 283 | int len = hlen + tlen; |
|---|
| 260 | 284 | struct sk_buff *skb; |
|---|
| 261 | 285 | struct ncsi_request *nr; |
|---|
| .. | .. |
|---|
| 265 | 289 | return NULL; |
|---|
| 266 | 290 | |
|---|
| 267 | 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. |
|---|
| 268 | 294 | * The packet needs padding if its payload is less than 26 bytes to |
|---|
| 269 | 295 | * meet 64 bytes minimal ethernet frame length. |
|---|
| 270 | 296 | */ |
|---|
| 271 | 297 | len += sizeof(struct ncsi_cmd_pkt_hdr) + 4; |
|---|
| 272 | | - if (nca->payload < 26) |
|---|
| 273 | | - len += 26; |
|---|
| 274 | | - else |
|---|
| 275 | | - len += nca->payload; |
|---|
| 298 | + payload = ALIGN(nca->payload, 4); |
|---|
| 299 | + len += max(payload, padding_bytes); |
|---|
| 276 | 300 | |
|---|
| 277 | 301 | /* Allocate skb */ |
|---|
| 278 | 302 | skb = alloc_skb(len, GFP_ATOMIC); |
|---|
| .. | .. |
|---|
| 293 | 317 | |
|---|
| 294 | 318 | int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca) |
|---|
| 295 | 319 | { |
|---|
| 296 | | - struct ncsi_request *nr; |
|---|
| 297 | | - struct ethhdr *eh; |
|---|
| 298 | 320 | struct ncsi_cmd_handler *nch = NULL; |
|---|
| 321 | + struct ncsi_request *nr; |
|---|
| 322 | + unsigned char type; |
|---|
| 323 | + struct ethhdr *eh; |
|---|
| 299 | 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; |
|---|
| 300 | 331 | |
|---|
| 301 | 332 | /* Search for the handler */ |
|---|
| 302 | 333 | for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) { |
|---|
| 303 | | - if (ncsi_cmd_handlers[i].type == nca->type) { |
|---|
| 334 | + if (ncsi_cmd_handlers[i].type == type) { |
|---|
| 304 | 335 | if (ncsi_cmd_handlers[i].handler) |
|---|
| 305 | 336 | nch = &ncsi_cmd_handlers[i]; |
|---|
| 306 | 337 | else |
|---|
| .. | .. |
|---|
| 316 | 347 | return -ENOENT; |
|---|
| 317 | 348 | } |
|---|
| 318 | 349 | |
|---|
| 319 | | - /* Get packet payload length and allocate the request */ |
|---|
| 320 | | - nca->payload = nch->payload; |
|---|
| 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; |
|---|
| 321 | 357 | nr = ncsi_alloc_command(nca); |
|---|
| 322 | 358 | if (!nr) |
|---|
| 323 | 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 | + } |
|---|
| 324 | 367 | |
|---|
| 325 | 368 | /* Prepare the packet */ |
|---|
| 326 | 369 | nca->id = nr->id; |
|---|
| .. | .. |
|---|
| 334 | 377 | eh = skb_push(nr->cmd, sizeof(*eh)); |
|---|
| 335 | 378 | eh->h_proto = htons(ETH_P_NCSI); |
|---|
| 336 | 379 | eth_broadcast_addr(eh->h_dest); |
|---|
| 337 | | - eth_broadcast_addr(eh->h_source); |
|---|
| 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); |
|---|
| 338 | 389 | |
|---|
| 339 | 390 | /* Start the timer for the request that might not have |
|---|
| 340 | 391 | * corresponding response. Given NCSI is an internal |
|---|