1 /*
2  * Copyright (c) 2014 Brian Swetland
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <sys/types.h>
16 
17 #include "network.h"
18 #include "../app/lkboot/lkboot_protocol.h"
19 
readx(int s,void * _data,int len)20 static int readx(int s, void *_data, int len) {
21     char *data = _data;
22     int r;
23     while (len > 0) {
24         errno = NO_ERROR;
25         r = read(s, data, len);
26         if (r == 0) {
27             fprintf(stderr, "error: eof during socket read\n");
28             return -1;
29         }
30         if (r < 0) {
31             if (errno == EINTR) continue;
32             fprintf(stderr, "error: %s during socket read\n", strerror(errno));
33             return -1;
34         }
35         data += r;
36         len -= r;
37     }
38     return 0;
39 }
40 
upload(int s,int txfd,size_t txlen,int do_endian_swap)41 static int upload(int s, int txfd, size_t txlen, int do_endian_swap) {
42     int err = 0;
43     msg_hdr_t hdr;
44 
45     char *buf = malloc(txlen);
46     if (!buf)
47         return -1;
48 
49     if (readx(txfd, buf, txlen)) {
50         fprintf(stderr, "error: reading from file\n");
51         err = -1;
52         goto done;
53     }
54 
55     /* 4 byte swap data if requested */
56     if (do_endian_swap) {
57         size_t i;
58         for (i = 0; i < txlen; i += 4) {
59             char temp = buf[i];
60             buf[i] = buf[i + 3];
61             buf[i + 3] = temp;
62 
63             temp = buf[i + 1];
64             buf[i + 1] = buf[i + 2];
65             buf[i + 2] = temp;
66         }
67     }
68 
69     size_t pos = 0;
70     while (pos < txlen) {
71         size_t xfer = (txlen - pos > 65536) ? 65536 : txlen - pos;
72 
73         hdr.opcode = MSG_SEND_DATA;
74         hdr.extra = 0;
75         hdr.length = xfer - 1;
76         if (write(s, &hdr, sizeof(hdr)) != sizeof(hdr)) {
77             fprintf(stderr, "error: writing socket\n");
78             err = -1;
79             goto done;
80         }
81         if (write(s, buf + pos, xfer) != xfer) {
82             fprintf(stderr, "error: writing socket\n");
83             err = -1;
84             goto done;
85         }
86         pos += xfer;
87     }
88 
89     hdr.opcode = MSG_END_DATA;
90     hdr.extra = 0;
91     hdr.length = 0;
92     if (write(s, &hdr, sizeof(hdr)) != sizeof(hdr)) {
93         fprintf(stderr, "error: writing socket\n");
94         err = -1;
95         goto done;
96     }
97 
98 done:
99     free(buf);
100 
101     return err;
102 }
103 
trim_fpga_image(int fd,off_t len)104 static off_t trim_fpga_image(int fd, off_t len) {
105     /* fd should be at start of bitfile, seek until the
106      * ff ff ff ff aa 99 55 66 pattern is found and subtract
107      * the number of bytes read until pattern found.
108      */
109     const unsigned char pat[] = { 0xff, 0xff, 0xff, 0xff, 0xaa, 0x99, 0x55, 0x66 };
110     unsigned char buf[sizeof(pat)];
111 
112     memset(buf, 0, sizeof(buf));
113 
114     off_t i;
115     for (i = 0; i < len; i++) {
116         memmove(buf, buf + 1, sizeof(buf) - 1);
117         if (read(fd, &buf[sizeof(buf) - 1], 1) < 1) {
118             return -1;
119         }
120 
121         /* look for pattern */
122         if (memcmp(pat, buf, sizeof(pat)) == 0) {
123             /* found it, rewind the fd and return the truncated length */
124             lseek(fd, -sizeof(pat), SEEK_CUR);
125             return len - (i + 1 - sizeof(pat));
126         }
127     }
128 
129     return -1;
130 }
131 
132 #define DCC_SUBPROCESS "zynq-dcc"
133 
start_dcc_subprocess(int * fd_in,int * fd_out)134 static int start_dcc_subprocess(int *fd_in, int *fd_out) {
135     int outpipe[2];
136     if (pipe(outpipe) != 0)
137         return -1;
138 
139     int inpipe[2];
140     if (pipe(inpipe) != 0)
141         return -1;
142 
143     *fd_in = inpipe[0];
144     *fd_out = outpipe[1];
145 
146     pid_t pid = fork();
147     if (pid == 0) {
148         /* we are the child */
149         close(STDIN_FILENO);
150         close(STDOUT_FILENO);
151 
152         dup2(outpipe[0], STDIN_FILENO);
153         close(outpipe[1]);
154 
155         dup2(inpipe[1], STDOUT_FILENO);
156         close(inpipe[0]);
157 
158         fprintf(stderr, "in the child\n");
159 
160         execlp(DCC_SUBPROCESS, DCC_SUBPROCESS, NULL);
161         fprintf(stderr, "after exec, didn't work!\n");
162     } else {
163         fprintf(stderr, "in parent, pid %u\n", pid);
164 
165         close(outpipe[0]);
166         close(inpipe[1]);
167     }
168 
169     return 0;
170 }
171 
172 #define REPLYMAX (9 * 1024 * 1024)
173 static unsigned char replybuf[REPLYMAX];
174 static unsigned replylen = 0;
175 
lkboot_get_reply(void ** ptr)176 unsigned lkboot_get_reply(void **ptr) {
177     *ptr = replybuf;
178     return replylen;
179 }
180 
lkboot_txn(const char * host,const char * _cmd,int txfd,const char * args)181 int lkboot_txn(const char *host, const char *_cmd, int txfd, const char *args) {
182     msg_hdr_t hdr;
183     char cmd[128];
184     char tmp[65536];
185     off_t txlen = 0;
186     int do_endian_swap = 0;
187     int once = 1;
188     int len;
189     int fd_in, fd_out;
190     int ret = 0;
191 
192     if (txfd != -1) {
193         txlen = lseek(txfd, 0, SEEK_END);
194         if (txlen > (512*1024*1024)) {
195             fprintf(stderr, "error: file too large\n");
196             return -1;
197         }
198         lseek(txfd, 0, SEEK_SET);
199     }
200 
201     if (!strcmp(_cmd, "fpga")) {
202         /* if we were asked to send an fpga image, try to find the sync words and
203          * trim all the data before it
204          */
205         txlen = trim_fpga_image(txfd, txlen);
206         if (txlen < 0) {
207             fprintf(stderr, "error: fpga image doesn't contain sync pattern\n");
208             return -1;
209         }
210 
211         /* it'll need a 4 byte endian swap as well */
212         do_endian_swap = 1;
213     }
214 
215     len = snprintf(cmd, 128, "%s:%d:%s", _cmd, (int) txlen, args);
216     if (len > 127) {
217         fprintf(stderr, "error: command too large\n");
218         return -1;
219     }
220 
221     /* if host is -, use stdin/stdout */
222     if (!strcmp(host, "-")) {
223         fprintf(stderr, "using stdin/stdout for io\n");
224         fd_in = STDIN_FILENO;
225         fd_out = STDOUT_FILENO;
226     } else if (!strcasecmp(host, "jtag")) {
227         fprintf(stderr, "using zynq-dcc utility for io\n");
228         if (start_dcc_subprocess(&fd_in, &fd_out) < 0) {
229             fprintf(stderr, "error starting jtag subprocess, is it in your path?\n");
230             return -1;
231         }
232     } else {
233         in_addr_t addr = lookup_hostname(host);
234         if (addr == 0) {
235             fprintf(stderr, "error: cannot find host '%s'\n", host);
236             return -1;
237         }
238         while ((fd_in = tcp_connect(addr, 1023)) < 0) {
239             if (once) {
240                 fprintf(stderr, "error: cannot connect to host '%s'. retrying...\n", host);
241                 once = 0;
242             }
243             usleep(100000);
244         }
245         fd_out = fd_in;
246     }
247 
248     hdr.opcode = MSG_CMD;
249     hdr.extra = 0;
250     hdr.length = len;
251     if (write(fd_out, &hdr, sizeof(hdr)) != sizeof(hdr)) goto iofail;
252     if (write(fd_out, cmd, len) != len) goto iofail;
253 
254     for (;;) {
255         if (readx(fd_in, &hdr, sizeof(hdr))) goto iofail;
256         switch (hdr.opcode) {
257             case MSG_GO_AHEAD:
258                 if (upload(fd_out, txfd, txlen, do_endian_swap)) {
259                     ret = -1;
260                     goto out;
261                 }
262                 break;
263             case MSG_OKAY:
264                 ret = 0;
265                 goto out;
266             case MSG_FAIL:
267                 len = (hdr.length > 127) ? 127 : hdr.length;
268                 if (readx(fd_in, cmd, len)) {
269                     cmd[0] = 0;
270                 } else {
271                     cmd[len] = 0;
272                 }
273                 fprintf(stderr,"error: remote failure: %s\n", cmd);
274                 ret = -1;
275                 goto out;
276             case MSG_SEND_DATA:
277                 len = hdr.length + 1;
278                 if (readx(fd_in, tmp, len)) goto iofail;
279                 if (len > (REPLYMAX - replylen)) {
280                     fprintf(stderr, "error: too much reply data\n");
281                     ret = -1;
282                     goto out;
283                 }
284                 memcpy(replybuf + replylen, tmp, len);
285                 replylen += len;
286                 break;
287             default:
288                 fprintf(stderr, "error: unknown opcode %d\n", hdr.opcode);
289                 ret = -1;
290                 goto out;
291         }
292     }
293 
294 iofail:
295     fprintf(stderr, "error: socket io\n");
296     ret = -1;
297 
298 out:
299     close(fd_in);
300     if (fd_out != fd_in)
301         close(fd_out);
302     return ret;
303 }
304