Switchtec Userspace PROJECT_NUMBER = 4.2
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
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
147static 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
190static 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
217static 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
246static 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
267static 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
305static 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
338static 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
350static 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
363static 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
369static 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
376static 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
383static 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
390static 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
396static 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
402static 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
421static 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
429static 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
437static 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
445static 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
453static 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
464static 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
488static 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
497static 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
521static 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
548static 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;
597out_free:
598 free(open_p);
599 return ret;
600
601}
602
603struct 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
630err_close_free:
631 close(edev->evt_fd);
632err_close_cmd_free:
633 close(edev->cmd_fd);
634
635 free(edev);
636 return NULL;
637}
638
639#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:80