diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 00000000..0ac9603c --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,8 @@ + +all: lkboot + +lkboot: lkboot.c network.c + gcc -Wall -o lkboot lkboot.c network.c + +clean:: + rm -f lkboot diff --git a/tools/lkboot.c b/tools/lkboot.c new file mode 100644 index 00000000..13ecc818 --- /dev/null +++ b/tools/lkboot.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014 Brian Swetland + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "network.h" +#include "../app/lkboot/lkboot.h" + +void usage(void) { + fprintf(stderr, +"usage: lkboot ...\n" +"\n" +" lkboot flash \n" +" lkboot erase \n" +" lkboot fpga \n" +" lkboot boot \n" +"\n" + ); + exit(1); +} + +int readx(int s, void *_data, int len) { + char *data = _data; + int r; + while (len > 0) { + r = read(s, data, len); + if (r == 0) return -1; + if (r < 0) { + if (errno == EINTR) continue; + return -1; + } + data += r; + len -= r; + } + return 0; +} + +int upload(int s, int txfd, int txlen) { + char buf[65536]; + msg_hdr_t hdr; + + while (txlen > 0) { + int xfer = (txlen > 65536) ? 65536 : txlen; + if (readx(txfd, buf, xfer)) { + fprintf(stderr, "error: reading from file\n"); + return -1; + } + hdr.opcode = MSG_SEND_DATA; + hdr.extra = 0; + hdr.length = xfer - 1; + if (write(s, &hdr, sizeof(hdr)) != sizeof(hdr)) { + fprintf(stderr, "error: writing socket\n"); + return -1; + } + if (write(s, buf, xfer) != xfer) { + fprintf(stderr, "error: writing socket\n"); + return -1; + } + txlen -= xfer; + } + hdr.opcode = MSG_END_DATA; + hdr.extra = 0; + hdr.length = 0; + if (write(s, &hdr, sizeof(hdr)) != sizeof(hdr)) { + fprintf(stderr, "error: writing socket\n"); + return -1; + } + return 0; +} + +int lkboot_txn(const char *host, const char *_cmd, int txfd, const char *args) { + msg_hdr_t hdr; + in_addr_t addr; + char cmd[128]; + off_t txlen = 0; + int len; + int s; + + if (txfd) { + txlen = lseek(txfd, 0, SEEK_END); + if (txlen > (512*1024*1024)) { + fprintf(stderr, "error: file too large\n"); + return -1; + } + lseek(txfd, 0, SEEK_SET); + } + + len = snprintf(cmd, 128, "%s:%d:%s", _cmd, (int) txlen, args); + if (len > 127) { + fprintf(stderr, "error: command too large\n"); + return -1; + } + + addr = lookup_hostname(host); + if (addr == 0) { + fprintf(stderr, "error: cannot find host '%s'\n", host); + return -1; + } + if ((s = tcp_connect(addr, 1023)) < 0) { + fprintf(stderr, "error: cannot connect to host '%s'\n", host); + return -1; + } + + hdr.opcode = MSG_CMD; + hdr.extra = 0; + hdr.length = len; + if (write(s, &hdr, sizeof(hdr)) != sizeof(hdr)) goto iofail; + if (write(s, cmd, len) != len) goto iofail; + + for (;;) { + if (readx(s, &hdr, sizeof(hdr))) goto iofail; + switch (hdr.opcode) { + case MSG_GO_AHEAD: + if (upload(s, txfd, txlen)) goto fail; + break; + case MSG_OKAY: + close(s); + return 0; + case MSG_FAIL: + len = (hdr.length > 127) ? 127 : hdr.length; + if (readx(s, cmd, len)) { + cmd[0] = 0; + } else { + cmd[len] = 0; + } + fprintf(stderr,"error: remote failure: %s\n", cmd); + goto fail; + default: + fprintf(stderr, "error: unknown opcode %d\n", hdr.opcode); + goto fail; + } + } + +iofail: + fprintf(stderr, "error: socket io\n"); +fail: + close(s); + return -1; +} + +int main(int argc, char **argv) { + const char *host = argv[1]; + const char *cmd = argv[2]; + const char *fn = argv[3]; + const char *args = ""; + int fd = -1; + + if (argc < 4) usage(); + + if (!strcmp(cmd, "flash")) { + if (argc < 5) usage(); + args = fn; + fn = argv[4]; + goto filetxn; + } else if (!strcmp(cmd, "fpga")) { + goto filetxn; + } else if (!strcmp(cmd, "boot")) { + goto filetxn; + } else if (!strcmp(cmd, "erase")) { + } else { + usage(); + } + return lkboot_txn(host, cmd, fd, args); + +filetxn: + if ((fd = open(fn, O_RDONLY)) < 0) { + fprintf(stderr, "error; cannot open '%s'\n", fn); + return -1; + } + return lkboot_txn(host, cmd, fd, args); +} diff --git a/tools/network.c b/tools/network.c new file mode 100644 index 00000000..5cfbab8e --- /dev/null +++ b/tools/network.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014 Brian Swetland + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "network.h" + +in_addr_t lookup_hostname(const char *hostname) { + return inet_addr(hostname); +} + +static int inet_listen(in_addr_t addr, int type, unsigned port, int shared) { + struct sockaddr_in sa; + int s; + if ((s = socket(AF_INET, type, 0)) < 0) { + return -1; + } + if (shared) { + int opt = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + //setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); + } + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + sa.sin_addr.s_addr = addr; + if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + close(s); + return -1; + } + return s; +} + +static int inet_connect(in_addr_t addr, int type, unsigned port) { + struct sockaddr_in sa; + int s; + if ((s = socket(AF_INET, type, 0)) < 0) { + return -1; + } + if (addr == 0xFFFFFFFF) { + int opt = 1; + setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)); + } + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + sa.sin_addr.s_addr = addr; + if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + close(s); + return -1; + } + return s; +} + +int udp_listen(in_addr_t addr, unsigned port, int shared) { + return inet_listen(addr, SOCK_DGRAM, port, shared); +} + +int udp_connect(in_addr_t addr, unsigned port) { + return inet_connect(addr, SOCK_DGRAM, port); +} + +int tcp_listen(in_addr_t addr, unsigned port) { + return inet_listen(addr, SOCK_STREAM, port, 0); +} + +int tcp_connect(in_addr_t addr, unsigned port) { + return inet_connect(addr, SOCK_STREAM, port); +} + diff --git a/tools/network.h b/tools/network.h new file mode 100644 index 00000000..8ec3935a --- /dev/null +++ b/tools/network.h @@ -0,0 +1,13 @@ + +#include +#include +#include +#include + +int udp_listen(in_addr_t addr, unsigned port, int shared); +int udp_connect(in_addr_t addr, unsigned port); + +int tcp_listen(in_addr_t addr, unsigned port); +int tcp_connect(in_addr_t addr, unsigned port); + +in_addr_t lookup_hostname(const char *hostname);