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