Velocity Userspace
linux-eth.c
1 /*
2  * Microsemi Switchtec(tm) PCIe Management Library
3  * Copyright (c) 2017, Microsemi Corporation
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  *
23  */
24 
25 #ifdef __linux__
26 
27 #include "../switchtec_priv.h"
28 #include "switchtec/switchtec.h"
29 #include "gasops.h"
30 
31 #include <linux/switchtec_ioctl.h>
32 
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <endian.h>
36 #include <dirent.h>
37 #include <libgen.h>
38 #include <signal.h>
39 #include <sys/stat.h>
40 #include <sys/ioctl.h>
41 #include <sys/mman.h>
42 #include <sys/sysmacros.h>
43 #include <arpa/inet.h>
44 #include <glob.h>
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <poll.h>
48 
49 #include <errno.h>
50 #include <string.h>
51 #include <stddef.h>
52 
53 #define ETH_SERVER_PORT 54545
54 
55 #define ETH_CHAN_TYPE_COMMAND 0x1
56 #define ETH_CHAN_TYPE_EVENT 0x2
57 
58 #define ETH_PROT_SIGNATURE 0x6d6c7373
59 #define ETH_PROT_VERSION 0x1
60 
61 #define ETH_PACKET_TYPE_OPEN 0xB1
62 #define ETH_PACKET_TYPE_CMD 0xB2
63 
64 #define ETH_FUNC_TYPE_OPEN_REQUEST 0x1
65 #define ETH_FUNC_TYPE_OPEN_ACCEPT 0x2
66 #define ETH_FUNC_TYPE_OPEN_REJECT 0x3
67 #define ETH_FUNC_TYPE_OPEN_CLOSE 0x4
68 
69 #define ETH_FUNC_TYPE_MRPC_CMD 0x1
70 #define ETH_FUNC_TYPE_MOE_CMD 0x2
71 #define ETH_FUNC_TYPE_MRPC_RESP 0x3
72 #define ETH_FUNC_TYPE_EVENT 0x4
73 #define ETH_FUNC_TYPE_MOE_RESP 0x5
74 
75 #define ETH_INST_ID_0 0x0
76 #define ETH_INST_ID_1 0x1
77 
78 #define ETH_GAS_READ_CMD_ID 0x1001
79 #define ETH_GAS_WRITE_CMD_ID 0x1002
80 
81 #define ETH_MAX_READ 512
82 
83 struct switchtec_eth {
84  struct switchtec_dev dev;
85  int cmd_fd;
86  int evt_fd;
87 };
88 
89 #define to_switchtec_eth(d) \
90  ((struct switchtec_eth *) \
91  ((char *)d - offsetof(struct switchtec_eth, dev)))
92 
93 struct eth_header {
94  uint32_t signature;
95  uint8_t version_id;
96  uint8_t rsvd0;
97  uint8_t function_type;
98  uint8_t packet_type;
99  union {
100  uint8_t service_inst;
101  uint8_t rsvd1;
102  };
103  union {
104  uint8_t service_type;
105  uint8_t rsvd2;
106  };
107  uint16_t payload_bytes;
108  union {
109  uint16_t mrpc_output_bytes;
110  uint16_t return_code;
111  };
112  uint16_t rsvd3;
113 };
114 
115 struct eth_packet {
116  struct eth_header hdr;
117  uint8_t body[MRPC_MAX_DATA_LEN + 4];
118 };
119 
120 static int send_eth_command(int cmd_fd, int func_type, uint8_t *data,
121  uint32_t data_len, uint32_t mrpc_output_len)
122 {
123  size_t packet_len;
124  struct eth_packet *command_p;
125 
126  packet_len = offsetof(struct eth_packet, body) + data_len;
127  command_p = malloc(packet_len);
128 
129  command_p->hdr.signature = htonl(ETH_PROT_SIGNATURE);
130  command_p->hdr.version_id = ETH_PROT_VERSION;
131  command_p->hdr.function_type = func_type;
132  command_p->hdr.packet_type = ETH_PACKET_TYPE_CMD;
133  command_p->hdr.payload_bytes = htons(data_len);
134  command_p->hdr.mrpc_output_bytes = htons(mrpc_output_len);
135 
136  memcpy(command_p->body, data, data_len);
137 
138  if (send(cmd_fd, command_p, packet_len, 0) < 0) {
139  free(command_p);
140  return -1;
141  }
142 
143  free(command_p);
144  return 0;
145 }
146 
147 static int recv_eth_response(int cmd_fd, uint32_t *result,
148  uint8_t *output, uint32_t *output_len)
149 {
150  struct eth_packet recvd_p;
151  void *p;
152  uint32_t len;
153  uint16_t func_type;
154  uint16_t packet_type;
155 
156  len = sizeof(struct eth_header);
157 
158  if (recv(cmd_fd, &recvd_p.hdr, len, 0) < 0)
159  return -1;
160 
161  func_type = recvd_p.hdr.function_type;
162  packet_type = recvd_p.hdr.packet_type;
163 
164  if ((func_type == ETH_FUNC_TYPE_OPEN_CLOSE)
165  && (packet_type == ETH_PACKET_TYPE_OPEN))
166  return -2;
167 
168  len = ntohs(recvd_p.hdr.payload_bytes);
169  p = recvd_p.body;
170 
171  if (!len)
172  return 0;
173 
174  if (recv(cmd_fd, p, len, 0) < 0)
175  return -3;
176 
177  if (packet_type == ETH_PACKET_TYPE_CMD) {
178  *result = le32toh(*(uint32_t *)p);
179  p += sizeof(uint32_t);
180  len -= sizeof(uint32_t);
181  if (output)
182  memcpy(output, p, len);
183  if (output_len)
184  *output_len = len;
185  }
186 
187  return 0;
188 }
189 
190 static int switchtec_submit_cmd_eth(struct switchtec_dev *dev, uint32_t cmd,
191  const void *payload, size_t payload_len,
192  size_t resp_len)
193 {
194  struct switchtec_eth *edev = to_switchtec_eth(dev);
195  uint32_t body_len;
196  int ret;
197 
198  struct eth_mrpc_body{
199  uint32_t command_id;
200  uint8_t data[];
201  } __attribute__(( packed )) *mrpc_body;
202 
203  body_len = offsetof(struct eth_mrpc_body, data) + payload_len;
204  mrpc_body = malloc(body_len);
205  memset(mrpc_body, 0, body_len);
206 
207  mrpc_body->command_id = htole32(cmd);
208  memcpy(mrpc_body->data, payload, payload_len);
209 
210  ret = send_eth_command(edev->cmd_fd, ETH_FUNC_TYPE_MRPC_CMD,
211  (uint8_t *)mrpc_body, body_len, resp_len);
212  free(mrpc_body);
213 
214  return ret;
215 }
216 
217 static int switchtec_read_resp_eth(struct switchtec_dev *dev, void *resp,
218  size_t resp_len)
219 {
220  struct switchtec_eth *edev = to_switchtec_eth(dev);
221  uint32_t ret;
222  uint32_t result;
223  uint8_t buf[resp_len];
224  uint32_t received_len;
225 
226  ret = recv_eth_response(edev->cmd_fd, &result, buf, &received_len);
227  if (ret)
228  return ret;
229 
230  if (received_len != resp_len) {
231  errno = EIO;
232  return -errno;
233  }
234 
235  if (result)
236  errno = result;
237 
238  if (!resp)
239  return result;
240 
241  memcpy(resp, buf, resp_len);
242 
243  return result;
244 }
245 
246 static int eth_cmd(struct switchtec_dev *dev, uint32_t cmd,
247  const void *payload, size_t payload_len,
248  void *resp, size_t resp_len)
249 {
250  int ret;
251 
252  ret = switchtec_submit_cmd_eth(dev, cmd, payload,
253  payload_len, resp_len);
254 
255  if (ret < 0)
256  return ret;
257 
258  return switchtec_read_resp_eth(dev, resp, resp_len);
259 }
260 
261 #ifdef __CHECKER__
262 #define __force __attribute__((force))
263 #else
264 #define __force
265 #endif
266 
267 static int eth_gas_write_exec(int fd, uint32_t offset,
268  const void *data, uint16_t bytes)
269 {
270  uint32_t result;
271  uint32_t body_len;
272  int ret;
273 
274  struct eth_gas_write_body{
275  uint32_t command_id;
276  uint32_t offset;
277  uint16_t bytes;
278  uint16_t reserved;
279  uint8_t data[];
280  } __attribute__(( packed )) *gas_write_body;
281 
282  body_len = offsetof(struct eth_gas_write_body, data) + bytes;
283  gas_write_body = malloc(body_len);
284  memset(gas_write_body, 0, body_len);
285 
286  gas_write_body->command_id = htole32(ETH_GAS_WRITE_CMD_ID);
287  gas_write_body->offset = htole32(offset);
288  gas_write_body->bytes = htole16(bytes);
289 
290  memcpy(gas_write_body->data, data, bytes);
291 
292  ret = send_eth_command(fd, ETH_FUNC_TYPE_MOE_CMD,
293  (uint8_t *)gas_write_body, body_len, 0);
294 
295  free(gas_write_body);
296 
297  if (ret)
298  return ret;
299 
300  ret = recv_eth_response(fd, &result, NULL, NULL);
301 
302  return ret;
303 }
304 
305 static int eth_gas_read_exec(struct switchtec_dev *dev, uint32_t offset,
306  uint8_t *data, size_t bytes)
307 {
308  struct switchtec_eth *edev = to_switchtec_eth(dev);
309  uint32_t result;
310  uint32_t data_len;
311  size_t body_len;
312  int ret;
313 
314  struct eth_gas_write_body{
315  uint32_t command_id;
316  uint32_t offset;
317  uint16_t bytes;
318  uint16_t reserved;
319  } __attribute__(( packed )) gas_read_body;
320 
321  gas_read_body.command_id = htole32(ETH_GAS_READ_CMD_ID);
322  gas_read_body.offset = htole32(offset);
323  gas_read_body.bytes = htole16(bytes);
324 
325  body_len = sizeof(gas_read_body);
326 
327  ret = send_eth_command(edev->cmd_fd, ETH_FUNC_TYPE_MOE_CMD,
328  (uint8_t *)&gas_read_body, body_len, 0);
329 
330  if (ret)
331  return ret;
332 
333  ret = recv_eth_response(edev->cmd_fd, &result, data, &data_len);
334 
335  return ret;
336 }
337 
338 static void eth_gas_read(struct switchtec_dev *dev, void *dest,
339  const void __gas *src, size_t n)
340 {
341  uint32_t gas_addr;
342  int ret;
343 
344  gas_addr = (uint32_t)(src - (void __gas *)dev->gas_map);
345  ret = eth_gas_read_exec(dev, gas_addr, dest, n);
346  if (ret)
347  raise(SIGBUS);
348 }
349 
350 static void eth_gas_write(struct switchtec_dev *dev, void __gas *dest,
351  const void *src, size_t n)
352 {
353  uint32_t gas_addr;
354  struct switchtec_eth *edev = to_switchtec_eth(dev);
355  int ret;
356 
357  gas_addr = (uint32_t)(dest - (void __gas *)dev->gas_map);
358  ret = eth_gas_write_exec(edev->cmd_fd, gas_addr, src, n);
359  if (ret)
360  raise(SIGBUS);
361 }
362 
363 static void eth_gas_write8(struct switchtec_dev *dev, uint8_t val,
364  uint8_t __gas *addr)
365 {
366  eth_gas_write(dev, addr, &val, sizeof(uint8_t));
367 }
368 
369 static void eth_gas_write16(struct switchtec_dev *dev, uint16_t val,
370  uint16_t __gas *addr)
371 {
372  val = htole16(val);
373  eth_gas_write(dev, addr, &val, sizeof(uint16_t));
374 }
375 
376 static void eth_gas_write32(struct switchtec_dev *dev, uint32_t val,
377  uint32_t __gas *addr)
378 {
379  val = htole32(val);
380  eth_gas_write(dev, addr, &val, sizeof(uint32_t));
381 }
382 
383 static void eth_gas_write64(struct switchtec_dev *dev, uint64_t val,
384  uint64_t __gas *addr)
385 {
386  val = htole64(val);
387  eth_gas_write(dev, addr, &val, sizeof(uint64_t));
388 }
389 
390 static void eth_memcpy_from_gas(struct switchtec_dev *dev, void *dest,
391  const void __gas *src, size_t n)
392 {
393  eth_gas_read(dev, dest, src, n);
394 }
395 
396 static void eth_memcpy_to_gas(struct switchtec_dev *dev, void __gas *dest,
397  const void *src, size_t n)
398 {
399  eth_gas_write(dev, dest, src, n);
400 }
401 
402 static ssize_t eth_write_from_gas(struct switchtec_dev *dev, int fd,
403  const void __gas *src, size_t n)
404 {
405  ssize_t ret = 0;
406  uint8_t buf[ETH_MAX_READ];
407  int cnt;
408 
409  while (n) {
410  cnt = n > ETH_MAX_READ ? ETH_MAX_READ : n;
411  eth_memcpy_from_gas(dev, buf, src, cnt);
412  ret +=write(fd, buf, cnt);
413 
414  src += cnt;
415  n -= cnt;
416  }
417 
418  return ret;
419 }
420 
421 static uint8_t eth_gas_read8(struct switchtec_dev *dev, uint8_t __gas *addr)
422 {
423  uint8_t val;
424 
425  eth_gas_read(dev, &val, addr, sizeof(val));
426  return val;
427 }
428 
429 static uint16_t eth_gas_read16(struct switchtec_dev *dev, uint16_t __gas *addr)
430 {
431  uint16_t val;
432 
433  eth_gas_read(dev, &val, addr, sizeof(val));
434  return le16toh(val);
435 }
436 
437 static uint32_t eth_gas_read32(struct switchtec_dev *dev, uint32_t __gas *addr)
438 {
439  uint32_t val;
440 
441  eth_gas_read(dev, &val, addr, sizeof(val));
442  return le32toh(val);
443 }
444 
445 static uint64_t eth_gas_read64(struct switchtec_dev *dev, uint64_t __gas *addr)
446 {
447  uint64_t val;
448 
449  eth_gas_read(dev, &val, addr, sizeof(val));
450  return le64toh(val);
451 }
452 
453 static void eth_close(struct switchtec_dev *dev)
454 {
455  struct switchtec_eth *edev = to_switchtec_eth(dev);
456 
457  if (dev->gas_map)
458  munmap((void __force *)dev->gas_map, dev->gas_map_size);
459 
460  close(edev->cmd_fd);
461  free(edev);
462 }
463 
464 static int map_gas(struct switchtec_dev *dev)
465 {
466  void *addr;
467  dev->gas_map_size = 4 << 20;
468 
469  /*
470  * Ensure that if someone tries to do something stupid,
471  * like dereference the GAS directly we fail without
472  * trashing random memory somewhere. We do this by
473  * allocating an innaccessible range in the virtual
474  * address space and use that as the GAS address which
475  * will be subtracted by subsequent operations
476  */
477 
478  addr = mmap(NULL, dev->gas_map_size, PROT_NONE,
479  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
480  if (addr == MAP_FAILED)
481  return -1;
482 
483  dev->gas_map = (gasptr_t __force)addr;
484 
485  return 0;
486 }
487 
488 static gasptr_t eth_gas_map(struct switchtec_dev *dev, int writeable,
489  size_t *map_size)
490 {
491  if (map_size)
492  *map_size = dev->gas_map_size;
493 
494  return dev->gas_map;
495 }
496 
497 static int eth_event_wait(struct switchtec_dev *dev, int timeout_ms)
498 {
499  int ret;
500  struct eth_packet recvd_p;
501  struct switchtec_eth *edev = to_switchtec_eth(dev);
502  uint32_t len;
503 
504  len = sizeof(struct eth_header);
505 
506  if (timeout_ms != -1)
507  setsockopt(edev->evt_fd, SOL_SOCKET, SO_RCVTIMEO,
508  (char *)&timeout_ms, sizeof(int));
509 
510  ret = recv(edev->evt_fd, &recvd_p.hdr, len, 0);
511  if (ret <= 0)
512  return ret;
513 
514  if ((recvd_p.hdr.packet_type == ETH_PACKET_TYPE_CMD)
515  && (recvd_p.hdr.function_type == ETH_FUNC_TYPE_EVENT))
516  return 1;
517 
518  return 0;
519 }
520 
521 static const struct switchtec_ops eth_ops = {
522  .close = eth_close,
523  .gas_map = eth_gas_map,
524  .cmd = eth_cmd,
525  .get_device_id = gasop_get_device_id,
526  .get_fw_version = gasop_get_fw_version,
527  .pff_to_port = gasop_pff_to_port,
528  .port_to_pff = gasop_port_to_pff,
529  .flash_part = gasop_flash_part,
530  .event_summary = gasop_event_summary,
531  .event_ctl = gasop_event_ctl,
532  .event_wait = eth_event_wait,
533 
534  .gas_read8 = eth_gas_read8,
535  .gas_read16 = eth_gas_read16,
536  .gas_read32 = eth_gas_read32,
537  .gas_read64 = eth_gas_read64,
538  .gas_write8 = eth_gas_write8,
539  .gas_write16 = eth_gas_write16,
540  .gas_write32 = eth_gas_write32,
541  .gas_write32_no_retry = eth_gas_write32,
542  .gas_write64 = eth_gas_write64,
543  .memcpy_to_gas = eth_memcpy_to_gas,
544  .memcpy_from_gas = eth_memcpy_from_gas,
545  .write_from_gas = eth_write_from_gas,
546 };
547 
548 static int open_eth_chan(const char *server_ip, int server_port,
549  int chan_type, int moe_inst_id)
550 {
551  int fd;
552  struct eth_packet *open_p;
553 
554  struct sockaddr_in server;
555  uint32_t len;
556  int ret;
557 
558  fd = socket(AF_INET, SOCK_STREAM, 0);
559  if (fd == -1)
560  return -1;
561  ret = fd;
562 
563  server.sin_addr.s_addr = inet_addr(server_ip);
564  server.sin_family = AF_INET;
565  server.sin_port = htons(server_port);
566 
567  if (connect(fd, (struct sockaddr *)&server , sizeof(server)) < 0) {
568  close(fd);
569  return -2;
570  }
571 
572  len = sizeof(struct eth_header);
573 
574  open_p = malloc(sizeof(struct eth_packet));
575 
576  open_p->hdr.signature = htonl(ETH_PROT_SIGNATURE);
577  open_p->hdr.version_id = ETH_PROT_VERSION;
578  open_p->hdr.function_type = ETH_FUNC_TYPE_OPEN_REQUEST;
579  open_p->hdr.packet_type = ETH_PACKET_TYPE_OPEN;
580  open_p->hdr.service_inst = moe_inst_id;
581  open_p->hdr.service_type = chan_type;
582 
583  if (send(fd, open_p, len, 0) < 0) {
584  ret = -3;
585  goto out_free;
586  }
587 
588  len = sizeof(struct eth_header);
589  if (recv(fd, open_p, len, 0) < 0) {
590  ret = -4;
591  goto out_free;
592  }
593 
594  if (!((open_p->hdr.function_type == ETH_FUNC_TYPE_OPEN_ACCEPT)
595  && (open_p->hdr.return_code == 0)))
596  ret = -5;
597 out_free:
598  free(open_p);
599  return ret;
600 
601 }
602 
603 struct switchtec_dev *switchtec_open_eth(const char *ip, const int inst)
604 {
605  struct switchtec_eth *edev;
606 
607  edev = malloc(sizeof(*edev));
608  if (!edev)
609  return NULL;
610 
611  edev->cmd_fd = open_eth_chan(ip, ETH_SERVER_PORT,
612  ETH_CHAN_TYPE_COMMAND, inst);
613  if (edev->cmd_fd < 0)
614  goto err_close_cmd_free;
615 
616  edev->evt_fd = open_eth_chan(ip, ETH_SERVER_PORT,
617  ETH_CHAN_TYPE_EVENT, inst);
618  if (edev->evt_fd < 0)
619  goto err_close_free;
620 
621  if (map_gas(&edev->dev))
622  goto err_close_free;
623 
624  edev->dev.ops = &eth_ops;
625 
626  gasop_set_partition_info(&edev->dev);
627 
628  return &edev->dev;
629 
630 err_close_free:
631  close(edev->evt_fd);
632 err_close_cmd_free:
633  close(edev->cmd_fd);
634 
635  free(edev);
636  return NULL;
637 }
638 
639 #endif
switchtec_open_eth
struct switchtec_dev * switchtec_open_eth(const char *ip, const int inst)
Open a switchtec device over ethernet.
Definition: linux-eth.c:603
eth_packet
Definition: linux-eth.c:115
switchtec.h
Main Switchtec header.
switchtec_eth
Definition: linux-eth.c:83
eth_header
Definition: linux-eth.c:93
gasptr_t
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.
Definition: switchtec.h:83