GNU Linux-libre 4.14.290-gnu1
[releases.git] / net / ncsi / ncsi-cmd.c
1 /*
2  * 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  */
9
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/etherdevice.h>
14 #include <linux/netdevice.h>
15 #include <linux/skbuff.h>
16
17 #include <net/ncsi.h>
18 #include <net/net_namespace.h>
19 #include <net/sock.h>
20
21 #include "internal.h"
22 #include "ncsi-pkt.h"
23
24 u32 ncsi_calculate_checksum(unsigned char *data, int len)
25 {
26         u32 checksum = 0;
27         int i;
28
29         for (i = 0; i < len; i += 2)
30                 checksum += (((u32)data[i] << 8) | data[i + 1]);
31
32         checksum = (~checksum + 1);
33         return checksum;
34 }
35
36 /* This function should be called after the data area has been
37  * populated completely.
38  */
39 static void ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
40                                   struct ncsi_cmd_arg *nca)
41 {
42         u32 checksum;
43         __be32 *pchecksum;
44
45         h->mc_id        = 0;
46         h->revision     = NCSI_PKT_REVISION;
47         h->reserved     = 0;
48         h->id           = nca->id;
49         h->type         = nca->type;
50         h->channel      = NCSI_TO_CHANNEL(nca->package,
51                                           nca->channel);
52         h->length       = htons(nca->payload);
53         h->reserved1[0] = 0;
54         h->reserved1[1] = 0;
55
56         /* Fill with calculated checksum */
57         checksum = ncsi_calculate_checksum((unsigned char *)h,
58                                            sizeof(*h) + nca->payload);
59         pchecksum = (__be32 *)((void *)h + sizeof(struct ncsi_pkt_hdr) +
60                     nca->payload);
61         *pchecksum = htonl(checksum);
62 }
63
64 static int ncsi_cmd_handler_default(struct sk_buff *skb,
65                                     struct ncsi_cmd_arg *nca)
66 {
67         struct ncsi_cmd_pkt *cmd;
68
69         cmd = skb_put_zero(skb, sizeof(*cmd));
70         ncsi_cmd_build_header(&cmd->cmd.common, nca);
71
72         return 0;
73 }
74
75 static int ncsi_cmd_handler_sp(struct sk_buff *skb,
76                                struct ncsi_cmd_arg *nca)
77 {
78         struct ncsi_cmd_sp_pkt *cmd;
79
80         cmd = skb_put_zero(skb, sizeof(*cmd));
81         cmd->hw_arbitration = nca->bytes[0];
82         ncsi_cmd_build_header(&cmd->cmd.common, nca);
83
84         return 0;
85 }
86
87 static int ncsi_cmd_handler_dc(struct sk_buff *skb,
88                                struct ncsi_cmd_arg *nca)
89 {
90         struct ncsi_cmd_dc_pkt *cmd;
91
92         cmd = skb_put_zero(skb, sizeof(*cmd));
93         cmd->ald = nca->bytes[0];
94         ncsi_cmd_build_header(&cmd->cmd.common, nca);
95
96         return 0;
97 }
98
99 static int ncsi_cmd_handler_rc(struct sk_buff *skb,
100                                struct ncsi_cmd_arg *nca)
101 {
102         struct ncsi_cmd_rc_pkt *cmd;
103
104         cmd = skb_put_zero(skb, sizeof(*cmd));
105         ncsi_cmd_build_header(&cmd->cmd.common, nca);
106
107         return 0;
108 }
109
110 static int ncsi_cmd_handler_ae(struct sk_buff *skb,
111                                struct ncsi_cmd_arg *nca)
112 {
113         struct ncsi_cmd_ae_pkt *cmd;
114
115         cmd = skb_put_zero(skb, sizeof(*cmd));
116         cmd->mc_id = nca->bytes[0];
117         cmd->mode = htonl(nca->dwords[1]);
118         ncsi_cmd_build_header(&cmd->cmd.common, nca);
119
120         return 0;
121 }
122
123 static int ncsi_cmd_handler_sl(struct sk_buff *skb,
124                                struct ncsi_cmd_arg *nca)
125 {
126         struct ncsi_cmd_sl_pkt *cmd;
127
128         cmd = skb_put_zero(skb, sizeof(*cmd));
129         cmd->mode = htonl(nca->dwords[0]);
130         cmd->oem_mode = htonl(nca->dwords[1]);
131         ncsi_cmd_build_header(&cmd->cmd.common, nca);
132
133         return 0;
134 }
135
136 static int ncsi_cmd_handler_svf(struct sk_buff *skb,
137                                 struct ncsi_cmd_arg *nca)
138 {
139         struct ncsi_cmd_svf_pkt *cmd;
140
141         cmd = skb_put_zero(skb, sizeof(*cmd));
142         cmd->vlan = htons(nca->words[1]);
143         cmd->index = nca->bytes[6];
144         cmd->enable = nca->bytes[7];
145         ncsi_cmd_build_header(&cmd->cmd.common, nca);
146
147         return 0;
148 }
149
150 static int ncsi_cmd_handler_ev(struct sk_buff *skb,
151                                struct ncsi_cmd_arg *nca)
152 {
153         struct ncsi_cmd_ev_pkt *cmd;
154
155         cmd = skb_put_zero(skb, sizeof(*cmd));
156         cmd->mode = nca->bytes[3];
157         ncsi_cmd_build_header(&cmd->cmd.common, nca);
158
159         return 0;
160 }
161
162 static int ncsi_cmd_handler_sma(struct sk_buff *skb,
163                                 struct ncsi_cmd_arg *nca)
164 {
165         struct ncsi_cmd_sma_pkt *cmd;
166         int i;
167
168         cmd = skb_put_zero(skb, sizeof(*cmd));
169         for (i = 0; i < 6; i++)
170                 cmd->mac[i] = nca->bytes[i];
171         cmd->index = nca->bytes[6];
172         cmd->at_e = nca->bytes[7];
173         ncsi_cmd_build_header(&cmd->cmd.common, nca);
174
175         return 0;
176 }
177
178 static int ncsi_cmd_handler_ebf(struct sk_buff *skb,
179                                 struct ncsi_cmd_arg *nca)
180 {
181         struct ncsi_cmd_ebf_pkt *cmd;
182
183         cmd = skb_put_zero(skb, sizeof(*cmd));
184         cmd->mode = htonl(nca->dwords[0]);
185         ncsi_cmd_build_header(&cmd->cmd.common, nca);
186
187         return 0;
188 }
189
190 static int ncsi_cmd_handler_egmf(struct sk_buff *skb,
191                                  struct ncsi_cmd_arg *nca)
192 {
193         struct ncsi_cmd_egmf_pkt *cmd;
194
195         cmd = skb_put_zero(skb, sizeof(*cmd));
196         cmd->mode = htonl(nca->dwords[0]);
197         ncsi_cmd_build_header(&cmd->cmd.common, nca);
198
199         return 0;
200 }
201
202 static int ncsi_cmd_handler_snfc(struct sk_buff *skb,
203                                  struct ncsi_cmd_arg *nca)
204 {
205         struct ncsi_cmd_snfc_pkt *cmd;
206
207         cmd = skb_put_zero(skb, sizeof(*cmd));
208         cmd->mode = nca->bytes[0];
209         ncsi_cmd_build_header(&cmd->cmd.common, nca);
210
211         return 0;
212 }
213
214 static struct ncsi_cmd_handler {
215         unsigned char type;
216         int           payload;
217         int           (*handler)(struct sk_buff *skb,
218                                  struct ncsi_cmd_arg *nca);
219 } ncsi_cmd_handlers[] = {
220         { NCSI_PKT_CMD_CIS,    0, ncsi_cmd_handler_default },
221         { NCSI_PKT_CMD_SP,     4, ncsi_cmd_handler_sp      },
222         { NCSI_PKT_CMD_DP,     0, ncsi_cmd_handler_default },
223         { NCSI_PKT_CMD_EC,     0, ncsi_cmd_handler_default },
224         { NCSI_PKT_CMD_DC,     4, ncsi_cmd_handler_dc      },
225         { NCSI_PKT_CMD_RC,     4, ncsi_cmd_handler_rc      },
226         { NCSI_PKT_CMD_ECNT,   0, ncsi_cmd_handler_default },
227         { NCSI_PKT_CMD_DCNT,   0, ncsi_cmd_handler_default },
228         { NCSI_PKT_CMD_AE,     8, ncsi_cmd_handler_ae      },
229         { NCSI_PKT_CMD_SL,     8, ncsi_cmd_handler_sl      },
230         { NCSI_PKT_CMD_GLS,    0, ncsi_cmd_handler_default },
231         { NCSI_PKT_CMD_SVF,    8, ncsi_cmd_handler_svf     },
232         { NCSI_PKT_CMD_EV,     4, ncsi_cmd_handler_ev      },
233         { NCSI_PKT_CMD_DV,     0, ncsi_cmd_handler_default },
234         { NCSI_PKT_CMD_SMA,    8, ncsi_cmd_handler_sma     },
235         { NCSI_PKT_CMD_EBF,    4, ncsi_cmd_handler_ebf     },
236         { NCSI_PKT_CMD_DBF,    0, ncsi_cmd_handler_default },
237         { NCSI_PKT_CMD_EGMF,   4, ncsi_cmd_handler_egmf    },
238         { NCSI_PKT_CMD_DGMF,   0, ncsi_cmd_handler_default },
239         { NCSI_PKT_CMD_SNFC,   4, ncsi_cmd_handler_snfc    },
240         { NCSI_PKT_CMD_GVI,    0, ncsi_cmd_handler_default },
241         { NCSI_PKT_CMD_GC,     0, ncsi_cmd_handler_default },
242         { NCSI_PKT_CMD_GP,     0, ncsi_cmd_handler_default },
243         { NCSI_PKT_CMD_GCPS,   0, ncsi_cmd_handler_default },
244         { NCSI_PKT_CMD_GNS,    0, ncsi_cmd_handler_default },
245         { NCSI_PKT_CMD_GNPTS,  0, ncsi_cmd_handler_default },
246         { NCSI_PKT_CMD_GPS,    0, ncsi_cmd_handler_default },
247         { NCSI_PKT_CMD_OEM,    0, NULL                     },
248         { NCSI_PKT_CMD_PLDM,   0, NULL                     },
249         { NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default }
250 };
251
252 static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
253 {
254         struct ncsi_dev_priv *ndp = nca->ndp;
255         struct ncsi_dev *nd = &ndp->ndev;
256         struct net_device *dev = nd->dev;
257         int hlen = LL_RESERVED_SPACE(dev);
258         int tlen = dev->needed_tailroom;
259         int len = hlen + tlen;
260         struct sk_buff *skb;
261         struct ncsi_request *nr;
262
263         nr = ncsi_alloc_request(ndp, nca->req_flags);
264         if (!nr)
265                 return NULL;
266
267         /* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
268          * The packet needs padding if its payload is less than 26 bytes to
269          * meet 64 bytes minimal ethernet frame length.
270          */
271         len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
272         if (nca->payload < 26)
273                 len += 26;
274         else
275                 len += nca->payload;
276
277         /* Allocate skb */
278         skb = alloc_skb(len, GFP_ATOMIC);
279         if (!skb) {
280                 ncsi_free_request(nr);
281                 return NULL;
282         }
283
284         nr->cmd = skb;
285         skb_reserve(skb, hlen);
286         skb_reset_network_header(skb);
287
288         skb->dev = dev;
289         skb->protocol = htons(ETH_P_NCSI);
290
291         return nr;
292 }
293
294 int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
295 {
296         struct ncsi_request *nr;
297         struct ethhdr *eh;
298         struct ncsi_cmd_handler *nch = NULL;
299         int i, ret;
300
301         /* Search for the handler */
302         for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
303                 if (ncsi_cmd_handlers[i].type == nca->type) {
304                         if (ncsi_cmd_handlers[i].handler)
305                                 nch = &ncsi_cmd_handlers[i];
306                         else
307                                 nch = NULL;
308
309                         break;
310                 }
311         }
312
313         if (!nch) {
314                 netdev_err(nca->ndp->ndev.dev,
315                            "Cannot send packet with type 0x%02x\n", nca->type);
316                 return -ENOENT;
317         }
318
319         /* Get packet payload length and allocate the request */
320         nca->payload = nch->payload;
321         nr = ncsi_alloc_command(nca);
322         if (!nr)
323                 return -ENOMEM;
324
325         /* Prepare the packet */
326         nca->id = nr->id;
327         ret = nch->handler(nr->cmd, nca);
328         if (ret) {
329                 ncsi_free_request(nr);
330                 return ret;
331         }
332
333         /* Fill the ethernet header */
334         eh = skb_push(nr->cmd, sizeof(*eh));
335         eh->h_proto = htons(ETH_P_NCSI);
336         eth_broadcast_addr(eh->h_dest);
337         eth_broadcast_addr(eh->h_source);
338
339         /* Start the timer for the request that might not have
340          * corresponding response. Given NCSI is an internal
341          * connection a 1 second delay should be sufficient.
342          */
343         nr->enabled = true;
344         mod_timer(&nr->timer, jiffies + 1 * HZ);
345
346         /* Send NCSI packet */
347         skb_get(nr->cmd);
348         ret = dev_queue_xmit(nr->cmd);
349         if (ret < 0) {
350                 ncsi_free_request(nr);
351                 return ret;
352         }
353
354         return 0;
355 }