Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/ncsi/ncsi-cmd.c
26285 views
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
21
static const int padding_bytes = 26;
22
23
u32 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
*/
38
static 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
63
static 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
74
static 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
86
static 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
98
static 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
109
static 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
122
static 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
135
static 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
149
static 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
161
static 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
177
static 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
189
static 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
201
static 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
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
unsafe_memcpy(&cmd->mfr_id, nca->data, nca->payload,
232
/* skb allocated with enough to load the payload */);
233
ncsi_cmd_build_header(&cmd->cmd.common, nca);
234
235
return 0;
236
}
237
238
static struct ncsi_cmd_handler {
239
unsigned char type;
240
int payload;
241
int (*handler)(struct sk_buff *skb,
242
struct ncsi_cmd_arg *nca);
243
} ncsi_cmd_handlers[] = {
244
{ NCSI_PKT_CMD_CIS, 0, ncsi_cmd_handler_default },
245
{ NCSI_PKT_CMD_SP, 4, ncsi_cmd_handler_sp },
246
{ NCSI_PKT_CMD_DP, 0, ncsi_cmd_handler_default },
247
{ NCSI_PKT_CMD_EC, 0, ncsi_cmd_handler_default },
248
{ NCSI_PKT_CMD_DC, 4, ncsi_cmd_handler_dc },
249
{ NCSI_PKT_CMD_RC, 4, ncsi_cmd_handler_rc },
250
{ NCSI_PKT_CMD_ECNT, 0, ncsi_cmd_handler_default },
251
{ NCSI_PKT_CMD_DCNT, 0, ncsi_cmd_handler_default },
252
{ NCSI_PKT_CMD_AE, 8, ncsi_cmd_handler_ae },
253
{ NCSI_PKT_CMD_SL, 8, ncsi_cmd_handler_sl },
254
{ NCSI_PKT_CMD_GLS, 0, ncsi_cmd_handler_default },
255
{ NCSI_PKT_CMD_SVF, 8, ncsi_cmd_handler_svf },
256
{ NCSI_PKT_CMD_EV, 4, ncsi_cmd_handler_ev },
257
{ NCSI_PKT_CMD_DV, 0, ncsi_cmd_handler_default },
258
{ NCSI_PKT_CMD_SMA, 8, ncsi_cmd_handler_sma },
259
{ NCSI_PKT_CMD_EBF, 4, ncsi_cmd_handler_ebf },
260
{ NCSI_PKT_CMD_DBF, 0, ncsi_cmd_handler_default },
261
{ NCSI_PKT_CMD_EGMF, 4, ncsi_cmd_handler_egmf },
262
{ NCSI_PKT_CMD_DGMF, 0, ncsi_cmd_handler_default },
263
{ NCSI_PKT_CMD_SNFC, 4, ncsi_cmd_handler_snfc },
264
{ NCSI_PKT_CMD_GVI, 0, ncsi_cmd_handler_default },
265
{ NCSI_PKT_CMD_GC, 0, ncsi_cmd_handler_default },
266
{ NCSI_PKT_CMD_GP, 0, ncsi_cmd_handler_default },
267
{ NCSI_PKT_CMD_GCPS, 0, ncsi_cmd_handler_default },
268
{ NCSI_PKT_CMD_GNS, 0, ncsi_cmd_handler_default },
269
{ NCSI_PKT_CMD_GNPTS, 0, ncsi_cmd_handler_default },
270
{ NCSI_PKT_CMD_GPS, 0, ncsi_cmd_handler_default },
271
{ NCSI_PKT_CMD_OEM, -1, ncsi_cmd_handler_oem },
272
{ NCSI_PKT_CMD_PLDM, 0, NULL },
273
{ NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default },
274
{ NCSI_PKT_CMD_GMCMA, 0, ncsi_cmd_handler_default }
275
};
276
277
static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
278
{
279
struct ncsi_dev_priv *ndp = nca->ndp;
280
struct ncsi_dev *nd = &ndp->ndev;
281
struct net_device *dev = nd->dev;
282
int hlen = LL_RESERVED_SPACE(dev);
283
int tlen = dev->needed_tailroom;
284
int payload;
285
int len = hlen + tlen;
286
struct sk_buff *skb;
287
struct ncsi_request *nr;
288
289
nr = ncsi_alloc_request(ndp, nca->req_flags);
290
if (!nr)
291
return NULL;
292
293
/* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
294
* Payload needs padding so that the checksum field following payload is
295
* aligned to 32-bit boundary.
296
* The packet needs padding if its payload is less than 26 bytes to
297
* meet 64 bytes minimal ethernet frame length.
298
*/
299
len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
300
payload = ALIGN(nca->payload, 4);
301
len += max(payload, padding_bytes);
302
303
/* Allocate skb */
304
skb = alloc_skb(len, GFP_ATOMIC);
305
if (!skb) {
306
ncsi_free_request(nr);
307
return NULL;
308
}
309
310
nr->cmd = skb;
311
skb_reserve(skb, hlen);
312
skb_reset_network_header(skb);
313
314
skb->dev = dev;
315
skb->protocol = htons(ETH_P_NCSI);
316
317
return nr;
318
}
319
320
int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
321
{
322
struct ncsi_cmd_handler *nch = NULL;
323
struct ncsi_request *nr;
324
unsigned char type;
325
struct ethhdr *eh;
326
int i, ret;
327
328
/* Use OEM generic handler for Netlink request */
329
if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN)
330
type = NCSI_PKT_CMD_OEM;
331
else
332
type = nca->type;
333
334
/* Search for the handler */
335
for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
336
if (ncsi_cmd_handlers[i].type == type) {
337
if (ncsi_cmd_handlers[i].handler)
338
nch = &ncsi_cmd_handlers[i];
339
else
340
nch = NULL;
341
342
break;
343
}
344
}
345
346
if (!nch) {
347
netdev_err(nca->ndp->ndev.dev,
348
"Cannot send packet with type 0x%02x\n", nca->type);
349
return -ENOENT;
350
}
351
352
/* Get packet payload length and allocate the request
353
* It is expected that if length set as negative in
354
* handler structure means caller is initializing it
355
* and setting length in nca before calling xmit function
356
*/
357
if (nch->payload >= 0)
358
nca->payload = nch->payload;
359
nr = ncsi_alloc_command(nca);
360
if (!nr)
361
return -ENOMEM;
362
363
/* track netlink information */
364
if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
365
nr->snd_seq = nca->info->snd_seq;
366
nr->snd_portid = nca->info->snd_portid;
367
nr->nlhdr = *nca->info->nlhdr;
368
}
369
370
/* Prepare the packet */
371
nca->id = nr->id;
372
ret = nch->handler(nr->cmd, nca);
373
if (ret) {
374
ncsi_free_request(nr);
375
return ret;
376
}
377
378
/* Fill the ethernet header */
379
eh = skb_push(nr->cmd, sizeof(*eh));
380
eh->h_proto = htons(ETH_P_NCSI);
381
eth_broadcast_addr(eh->h_dest);
382
383
/* If mac address received from device then use it for
384
* source address as unicast address else use broadcast
385
* address as source address
386
*/
387
if (nca->ndp->gma_flag == 1)
388
memcpy(eh->h_source, nca->ndp->ndev.dev->dev_addr, ETH_ALEN);
389
else
390
eth_broadcast_addr(eh->h_source);
391
392
/* Start the timer for the request that might not have
393
* corresponding response. Given NCSI is an internal
394
* connection a 1 second delay should be sufficient.
395
*/
396
nr->enabled = true;
397
mod_timer(&nr->timer, jiffies + 1 * HZ);
398
399
/* Send NCSI packet */
400
skb_get(nr->cmd);
401
ret = dev_queue_xmit(nr->cmd);
402
if (ret < 0) {
403
ncsi_free_request(nr);
404
return ret;
405
}
406
407
return 0;
408
}
409
410