Switchtec Userspace PROJECT_NUMBER = 4.4
Loading...
Searching...
No Matches
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
83struct 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
93struct 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
115struct eth_packet {
116 struct eth_header hdr;
117 uint8_t body[MRPC_MAX_DATA_LEN + 4];
118};
119
120static 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 if (!command_p)
129 return -1;
130
131 command_p->hdr.signature = htonl(ETH_PROT_SIGNATURE);
132 command_p->hdr.version_id = ETH_PROT_VERSION;
133 command_p->hdr.function_type = func_type;
134 command_p->hdr.packet_type = ETH_PACKET_TYPE_CMD;
135 command_p->hdr.payload_bytes = htons(data_len);
136 command_p->hdr.mrpc_output_bytes = htons(mrpc_output_len);
137
138 memcpy(command_p->body, data, data_len);
139
140 if (send(cmd_fd, command_p, packet_len, 0) < 0) {
141 free(command_p);
142 return -1;
143 }
144
145 free(command_p);
146 return 0;
147}
148
149static int recv_eth_response(int cmd_fd, uint32_t *result,
150 uint8_t *output, uint32_t *output_len)
151{
152 struct eth_packet recvd_p;
153 void *p;
154 uint32_t len;
155 uint16_t func_type;
156 uint16_t packet_type;
157
158 len = sizeof(struct eth_header);
159
160 if (recv(cmd_fd, &recvd_p.hdr, len, 0) < 0)
161 return -1;
162
163 func_type = recvd_p.hdr.function_type;
164 packet_type = recvd_p.hdr.packet_type;
165
166 if ((func_type == ETH_FUNC_TYPE_OPEN_CLOSE)
167 && (packet_type == ETH_PACKET_TYPE_OPEN))
168 return -2;
169
170 len = ntohs(recvd_p.hdr.payload_bytes);
171 p = recvd_p.body;
172
173 if (!len)
174 return 0;
175
176 if (recv(cmd_fd, p, len, 0) < 0)
177 return -3;
178
179 if (packet_type == ETH_PACKET_TYPE_CMD) {
180 *result = le32toh(*(uint32_t *)p);
181 p += sizeof(uint32_t);
182 len -= sizeof(uint32_t);
183 if (output)
184 memcpy(output, p, len);
185 if (output_len)
186 *output_len = len;
187 }
188
189 return 0;
190}
191
192static int switchtec_submit_cmd_eth(struct switchtec_dev *dev, uint32_t cmd,
193 const void *payload, size_t payload_len,
194 size_t resp_len)
195{
196 struct switchtec_eth *edev = to_switchtec_eth(dev);
197 uint32_t body_len;
198 int ret;
199
200 struct eth_mrpc_body{
201 uint32_t command_id;
202 uint8_t data[];
203 } __attribute__(( packed )) *mrpc_body;
204
205 body_len = offsetof(struct eth_mrpc_body, data) + payload_len;
206 mrpc_body = malloc(body_len);
207 if (!mrpc_body)
208 return -1;
209 memset(mrpc_body, 0, body_len);
210
211 mrpc_body->command_id = htole32(cmd);
212 memcpy(mrpc_body->data, payload, payload_len);
213
214 ret = send_eth_command(edev->cmd_fd, ETH_FUNC_TYPE_MRPC_CMD,
215 (uint8_t *)mrpc_body, body_len, resp_len);
216 free(mrpc_body);
217
218 return ret;
219}
220
221static int switchtec_read_resp_eth(struct switchtec_dev *dev, void *resp,
222 size_t resp_len)
223{
224 struct switchtec_eth *edev = to_switchtec_eth(dev);
225 uint32_t ret;
226 uint32_t result;
227 uint8_t buf[resp_len];
228 uint32_t received_len;
229
230 ret = recv_eth_response(edev->cmd_fd, &result, buf, &received_len);
231 if (ret)
232 return ret;
233
234 if (received_len != resp_len) {
235 errno = EIO;
236 return -errno;
237 }
238
239 if (result)
240 errno = result;
241
242 if (!resp)
243 return result;
244
245 memcpy(resp, buf, resp_len);
246
247 return result;
248}
249
250static int eth_cmd(struct switchtec_dev *dev, uint32_t cmd,
251 const void *payload, size_t payload_len,
252 void *resp, size_t resp_len)
253{
254 int ret;
255
256 ret = switchtec_submit_cmd_eth(dev, cmd, payload,
257 payload_len, resp_len);
258
259 if (ret < 0)
260 return ret;
261
262 return switchtec_read_resp_eth(dev, resp, resp_len);
263}
264
265#ifdef __CHECKER__
266#define __force __attribute__((force))
267#else
268#define __force
269#endif
270
271static int eth_gas_write_exec(int fd, uint32_t offset,
272 const void *data, uint16_t bytes)
273{
274 uint32_t result;
275 uint32_t body_len;
276 int ret;
277
278 struct eth_gas_write_body{
279 uint32_t command_id;
280 uint32_t offset;
281 uint16_t bytes;
282 uint16_t reserved;
283 uint8_t data[];
284 } __attribute__(( packed )) *gas_write_body;
285
286 body_len = offsetof(struct eth_gas_write_body, data) + bytes;
287 gas_write_body = malloc(body_len);
288 if (!gas_write_body)
289 return -1;
290 memset(gas_write_body, 0, body_len);
291
292 gas_write_body->command_id = htole32(ETH_GAS_WRITE_CMD_ID);
293 gas_write_body->offset = htole32(offset);
294 gas_write_body->bytes = htole16(bytes);
295
296 memcpy(gas_write_body->data, data, bytes);
297
298 ret = send_eth_command(fd, ETH_FUNC_TYPE_MOE_CMD,
299 (uint8_t *)gas_write_body, body_len, 0);
300
301 free(gas_write_body);
302
303 if (ret)
304 return ret;
305
306 ret = recv_eth_response(fd, &result, NULL, NULL);
307
308 return ret;
309}
310
311static int eth_gas_read_exec(struct switchtec_dev *dev, uint32_t offset,
312 uint8_t *data, size_t bytes)
313{
314 struct switchtec_eth *edev = to_switchtec_eth(dev);
315 uint32_t result;
316 uint32_t data_len;
317 size_t body_len;
318 int ret;
319
320 struct eth_gas_write_body{
321 uint32_t command_id;
322 uint32_t offset;
323 uint16_t bytes;
324 uint16_t reserved;
325 } __attribute__(( packed )) gas_read_body;
326
327 gas_read_body.command_id = htole32(ETH_GAS_READ_CMD_ID);
328 gas_read_body.offset = htole32(offset);
329 gas_read_body.bytes = htole16(bytes);
330
331 body_len = sizeof(gas_read_body);
332
333 ret = send_eth_command(edev->cmd_fd, ETH_FUNC_TYPE_MOE_CMD,
334 (uint8_t *)&gas_read_body, body_len, 0);
335
336 if (ret)
337 return ret;
338
339 ret = recv_eth_response(edev->cmd_fd, &result, data, &data_len);
340
341 return ret;
342}
343
344static void eth_gas_read(struct switchtec_dev *dev, void *dest,
345 const void __gas *src, size_t n)
346{
347 uint32_t gas_addr;
348 int ret;
349
350 gas_addr = (uint32_t)(src - (void __gas *)dev->gas_map);
351 ret = eth_gas_read_exec(dev, gas_addr, dest, n);
352 if (ret)
353 raise(SIGBUS);
354}
355
356static void eth_gas_write(struct switchtec_dev *dev, void __gas *dest,
357 const void *src, size_t n)
358{
359 uint32_t gas_addr;
360 struct switchtec_eth *edev = to_switchtec_eth(dev);
361 int ret;
362
363 gas_addr = (uint32_t)(dest - (void __gas *)dev->gas_map);
364 ret = eth_gas_write_exec(edev->cmd_fd, gas_addr, src, n);
365 if (ret)
366 raise(SIGBUS);
367}
368
369static void eth_gas_write8(struct switchtec_dev *dev, uint8_t val,
370 uint8_t __gas *addr)
371{
372 eth_gas_write(dev, addr, &val, sizeof(uint8_t));
373}
374
375static void eth_gas_write16(struct switchtec_dev *dev, uint16_t val,
376 uint16_t __gas *addr)
377{
378 val = htole16(val);
379 eth_gas_write(dev, addr, &val, sizeof(uint16_t));
380}
381
382static void eth_gas_write32(struct switchtec_dev *dev, uint32_t val,
383 uint32_t __gas *addr)
384{
385 val = htole32(val);
386 eth_gas_write(dev, addr, &val, sizeof(uint32_t));
387}
388
389static void eth_gas_write64(struct switchtec_dev *dev, uint64_t val,
390 uint64_t __gas *addr)
391{
392 val = htole64(val);
393 eth_gas_write(dev, addr, &val, sizeof(uint64_t));
394}
395
396static void eth_memcpy_from_gas(struct switchtec_dev *dev, void *dest,
397 const void __gas *src, size_t n)
398{
399 eth_gas_read(dev, dest, src, n);
400}
401
402static void eth_memcpy_to_gas(struct switchtec_dev *dev, void __gas *dest,
403 const void *src, size_t n)
404{
405 eth_gas_write(dev, dest, src, n);
406}
407
408static ssize_t eth_write_from_gas(struct switchtec_dev *dev, int fd,
409 const void __gas *src, size_t n)
410{
411 ssize_t ret = 0;
412 uint8_t buf[ETH_MAX_READ];
413 int cnt;
414
415 while (n) {
416 cnt = n > ETH_MAX_READ ? ETH_MAX_READ : n;
417 eth_memcpy_from_gas(dev, buf, src, cnt);
418 ret += write(fd, buf, cnt);
419
420 src += cnt;
421 n -= cnt;
422 }
423
424 return ret;
425}
426
427static uint8_t eth_gas_read8(struct switchtec_dev *dev, uint8_t __gas *addr)
428{
429 uint8_t val;
430
431 eth_gas_read(dev, &val, addr, sizeof(val));
432 return val;
433}
434
435static uint16_t eth_gas_read16(struct switchtec_dev *dev, uint16_t __gas *addr)
436{
437 uint16_t val;
438
439 eth_gas_read(dev, &val, addr, sizeof(val));
440 return le16toh(val);
441}
442
443static uint32_t eth_gas_read32(struct switchtec_dev *dev, uint32_t __gas *addr)
444{
445 uint32_t val;
446
447 eth_gas_read(dev, &val, addr, sizeof(val));
448 return le32toh(val);
449}
450
451static uint64_t eth_gas_read64(struct switchtec_dev *dev, uint64_t __gas *addr)
452{
453 uint64_t val;
454
455 eth_gas_read(dev, &val, addr, sizeof(val));
456 return le64toh(val);
457}
458
459static void eth_close(struct switchtec_dev *dev)
460{
461 struct switchtec_eth *edev = to_switchtec_eth(dev);
462
463 if (dev->gas_map)
464 munmap((void __force *)dev->gas_map, dev->gas_map_size);
465
466 close(edev->cmd_fd);
467 free(edev);
468}
469
470static int map_gas(struct switchtec_dev *dev)
471{
472 void *addr;
473 dev->gas_map_size = 4 << 20;
474
475 /*
476 * Ensure that if someone tries to do something stupid,
477 * like dereference the GAS directly we fail without
478 * trashing random memory somewhere. We do this by
479 * allocating an innaccessible range in the virtual
480 * address space and use that as the GAS address which
481 * will be subtracted by subsequent operations
482 */
483
484 addr = mmap(NULL, dev->gas_map_size, PROT_NONE,
485 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
486 if (addr == MAP_FAILED)
487 return -1;
488
489 dev->gas_map = (gasptr_t __force)addr;
490
491 return 0;
492}
493
494static gasptr_t eth_gas_map(struct switchtec_dev *dev, int writeable,
495 size_t *map_size)
496{
497 if (map_size)
498 *map_size = dev->gas_map_size;
499
500 return dev->gas_map;
501}
502
503static int eth_event_wait(struct switchtec_dev *dev, int timeout_ms)
504{
505 int ret;
506 struct eth_packet recvd_p;
507 struct switchtec_eth *edev = to_switchtec_eth(dev);
508 uint32_t len;
509
510 len = sizeof(struct eth_header);
511
512 if (timeout_ms != -1)
513 setsockopt(edev->evt_fd, SOL_SOCKET, SO_RCVTIMEO,
514 (char *)&timeout_ms, sizeof(int));
515
516 ret = recv(edev->evt_fd, &recvd_p.hdr, len, 0);
517 if (ret <= 0)
518 return ret;
519
520 if ((recvd_p.hdr.packet_type == ETH_PACKET_TYPE_CMD)
521 && (recvd_p.hdr.function_type == ETH_FUNC_TYPE_EVENT))
522 return 1;
523
524 return 0;
525}
526
527static const struct switchtec_ops eth_ops = {
528 .close = eth_close,
529 .gas_map = eth_gas_map,
530 .cmd = eth_cmd,
531 .get_device_id = gasop_get_device_id,
532 .get_fw_version = gasop_get_fw_version,
533 .get_device_version = gasop_get_device_version,
534 .pff_to_port = gasop_pff_to_port,
535 .port_to_pff = gasop_port_to_pff,
536 .flash_part = gasop_flash_part,
537 .event_summary = gasop_event_summary,
538 .event_ctl = gasop_event_ctl,
539 .event_wait = eth_event_wait,
540
541 .gas_read8 = eth_gas_read8,
542 .gas_read16 = eth_gas_read16,
543 .gas_read32 = eth_gas_read32,
544 .gas_read64 = eth_gas_read64,
545 .gas_write8 = eth_gas_write8,
546 .gas_write16 = eth_gas_write16,
547 .gas_write32 = eth_gas_write32,
548 .gas_write32_no_retry = eth_gas_write32,
549 .gas_write64 = eth_gas_write64,
550 .memcpy_to_gas = eth_memcpy_to_gas,
551 .memcpy_from_gas = eth_memcpy_from_gas,
552 .write_from_gas = eth_write_from_gas,
553};
554
555static int open_eth_chan(const char *server_ip, int server_port,
556 int chan_type, int moe_inst_id)
557{
558 int fd;
559 struct eth_packet *open_p;
560
561 struct sockaddr_in server;
562 uint32_t len;
563 int ret;
564
565 fd = socket(AF_INET, SOCK_STREAM, 0);
566 if (fd == -1)
567 return -1;
568 ret = fd;
569
570 server.sin_addr.s_addr = inet_addr(server_ip);
571 server.sin_family = AF_INET;
572 server.sin_port = htons(server_port);
573
574 if (connect(fd, (struct sockaddr *)&server , sizeof(server)) < 0) {
575 close(fd);
576 return -2;
577 }
578
579 len = sizeof(struct eth_header);
580
581 open_p = malloc(sizeof(struct eth_packet));
582 if (!open_p) {
583 close(fd);
584 return -1;
585 }
586
587 open_p->hdr.signature = htonl(ETH_PROT_SIGNATURE);
588 open_p->hdr.version_id = ETH_PROT_VERSION;
589 open_p->hdr.function_type = ETH_FUNC_TYPE_OPEN_REQUEST;
590 open_p->hdr.packet_type = ETH_PACKET_TYPE_OPEN;
591 open_p->hdr.service_inst = moe_inst_id;
592 open_p->hdr.service_type = chan_type;
593
594 if (send(fd, open_p, len, 0) < 0) {
595 ret = -3;
596 goto out_free;
597 }
598
599 len = sizeof(struct eth_header);
600 if (recv(fd, open_p, len, 0) < 0) {
601 ret = -4;
602 goto out_free;
603 }
604
605 if (!((open_p->hdr.function_type == ETH_FUNC_TYPE_OPEN_ACCEPT)
606 && (open_p->hdr.return_code == 0)))
607 ret = -5;
608out_free:
609 free(open_p);
610 return ret;
611
612}
613
614struct switchtec_dev *switchtec_open_eth(const char *ip, const int inst)
615{
616 struct switchtec_eth *edev;
617
618 edev = malloc(sizeof(*edev));
619 if (!edev)
620 return NULL;
621
622 edev->cmd_fd = open_eth_chan(ip, ETH_SERVER_PORT,
623 ETH_CHAN_TYPE_COMMAND, inst);
624 if (edev->cmd_fd < 0)
625 goto err_close_cmd_free;
626
627 edev->evt_fd = open_eth_chan(ip, ETH_SERVER_PORT,
628 ETH_CHAN_TYPE_EVENT, inst);
629 if (edev->evt_fd < 0)
630 goto err_close_free;
631
632 if (map_gas(&edev->dev))
633 goto err_close_free;
634
635 edev->dev.ops = &eth_ops;
636
637 gasop_set_partition_info(&edev->dev);
638
639 return &edev->dev;
640
641err_close_free:
642 close(edev->evt_fd);
643err_close_cmd_free:
644 close(edev->cmd_fd);
645
646 free(edev);
647 return NULL;
648}
649
650#endif
struct switchtec_dev * switchtec_open_eth(const char *ip, const int inst)
Open a switchtec device over ethernet.
Main Switchtec header.
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.
Definition switchtec.h:88