.. | .. |
---|
| 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 |
---|