os-workshop

same materials and sample source for RV32 OS projects
git clone http://frotz.net/git/os-workshop.git
Log | Files | Refs

netboot.c (6726B)


      1 // Copyright 2022, Brian Swetland <swetland@frotz.net>
      2 // Licensed under the Apache License, Version 2.0.
      3 
      4 #include <stdio.h>
      5 #include <string.h>
      6 #include <stdlib.h>
      7 #include <unistd.h>
      8 #include <poll.h>
      9 #include <time.h>
     10 
     11 #include <sys/types.h>
     12 #include <sys/socket.h>
     13 #include <arpa/inet.h>
     14 #include <net/if.h>
     15 #include <fcntl.h>
     16 
     17 #include "netboot.h"
     18 
     19 uint64_t get_time_ms(void) {
     20 	struct timespec ts;
     21 	if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
     22 		exit(-1);
     23 	}
     24 	return (((uint64_t)ts.tv_nsec) / 1000000ULL) + (((uint64_t)ts.tv_sec) * 1000ULL);
     25 }
     26 
     27 int open_udp6_socket(struct sockaddr_in6 *addr, int con) {
     28 	int s, n = 1;
     29 	if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
     30 		perror("open_udp6_socket: open socket");
     31 		return -1;
     32 	}
     33 	if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &n, sizeof(n)) < 0) {
     34 		perror("open_udp6_socket: SO_REUSEPORT");
     35 		goto fail;
     36 	}
     37 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) < 0) {
     38 		perror("open_udp6_socket: SO_REUSEADDR");
     39 		goto fail;
     40 	}
     41 	if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) {
     42 		perror("open_ud6_socket: set O_NONBLOCK");
     43 		goto fail;
     44 	}
     45 	if (con) {
     46 		if (connect(s, (struct sockaddr*) addr, sizeof(struct sockaddr_in6)) < 0) {
     47 			perror("open_udp6_socket: connect");
     48 			goto fail;
     49 		}
     50 	} else {
     51 		if (bind(s, (struct sockaddr*) addr, sizeof(struct sockaddr_in6)) < 0) {
     52 			perror("open_udp6_socket: bind");
     53 			goto fail;
     54 		}
     55 	}
     56 	return s;
     57 fail:
     58 	close(s);
     59 	return -1;
     60 }
     61 
     62 static uint32_t seq = 1;
     63 
     64 int open_node(int idx, const char *nodename) {
     65 	int s, r;
     66 
     67 	struct sockaddr_in6 addr = {
     68 		.sin6_family = AF_INET6,
     69 		.sin6_scope_id = idx,
     70 	};
     71 	if ((s = open_udp6_socket(&addr, 0)) < 0) {
     72 		return -1;
     73 	}
     74 
     75 	struct sockaddr_in6 mcast = {
     76 		.sin6_family = AF_INET6,
     77 		.sin6_port = htons(NB_PORT_QUERY),
     78 		.sin6_scope_id = idx,
     79 	};
     80 	inet_pton(AF_INET6, "ff02::1", &mcast.sin6_addr);
     81 
     82 	netboot_msg_t msg = {
     83 		.magic = NB_MAGIC,
     84 		.cmd = NB_CMD_QUERY,
     85 		.arg = 0,
     86 	};
     87 	uint32_t msglen = strlen(nodename);
     88 	if (msglen > NB_MSG_MAX) {
     89 		return -1;
     90 	}
     91 	memcpy(msg.db, nodename, msglen);
     92 	msglen += NB_MSG_MIN;
     93 
     94 	uint64_t next = get_time_ms() + 500;
     95 	for (;;) {
     96 		uint64_t now = get_time_ms();
     97 		if (now >= next) {
     98 			next = now + 500;
     99 			msg.seq = ++seq;
    100 			r = sendto(s, &msg, msglen, 0, (struct sockaddr*) &mcast, sizeof(mcast));
    101 			if (r != msglen) {
    102 				perror("write");
    103 				return -1;
    104 			}
    105 		}
    106 		struct pollfd pfd = {
    107 			.fd = s,
    108 			.events = POLLIN,
    109 		};
    110 		if (poll(&pfd, 1, next - now) == 1) {
    111 			uint8_t rxb[2048];
    112 			struct sockaddr_in6 rxaddr;
    113 			socklen_t rxalen = sizeof(rxaddr);
    114 			if ((r = recvfrom(s, rxb, sizeof(rxb), 1, (void*)&rxaddr, &rxalen)) > 0) {
    115 				netboot_msg_t *msg = (void*)rxb;
    116 				if (r < NB_MSG_MIN) {
    117 					continue;
    118 				}
    119 				if ((msg->magic != NB_MAGIC) ||
    120 					(msg->cmd != NB_CMD_STATUS) ||
    121 					(msg->seq != seq)) {
    122 					continue;
    123 				}
    124 				char _tmp[128];
    125 				const char *tmp = inet_ntop(AF_INET6, &rxaddr.sin6_addr, _tmp, sizeof(_tmp));
    126 				fprintf(stderr, "\n[ located '%s' at %s/%u ]\n", nodename,
    127 					tmp ? tmp : "unknown", rxaddr.sin6_scope_id);
    128 
    129 				int s0 = open_udp6_socket(&rxaddr, 1);
    130 				if (s0 >= 0) {
    131 					close(s);
    132 					return s0;
    133 				}
    134 			}
    135 		}
    136 	}
    137 }
    138 
    139 
    140 int send_msg(int s, netboot_msg_t *msg, unsigned len) {
    141 	int r;
    142 	for (int retry = 0; retry < 5; retry++) {
    143 		msg->seq = ++seq;
    144 		if ((r = write(s, msg, len)) != len) {
    145 			return -1;
    146 		}
    147 		struct pollfd pfd = {
    148 			.fd = s,
    149 			.events = POLLIN,
    150 		};
    151 		if (poll(&pfd, 1, 250) != 1) {
    152 			fprintf(stderr, "T");
    153 			continue;
    154 		}
    155 		netboot_msg_t rsp;
    156 		r = read(s, &rsp, sizeof(rsp));
    157 		if ((r < NB_MSG_MIN) || (rsp.magic != NB_MAGIC) || (rsp.cmd != NB_CMD_STATUS)) {
    158 			fprintf(stderr, "X");
    159 			continue;
    160 		}
    161 		if (rsp.seq != seq) {
    162 			fprintf(stderr, "S");
    163 			continue;
    164 		}
    165 		if (rsp.arg != NB_OK) {
    166 			fprintf(stderr, "E");
    167 			return -1;
    168 		}
    169 		fprintf(stderr, ".");
    170 		return 0;
    171 	}
    172 	return -1;
    173 }
    174 
    175 int send_file(int s, const char* fn, uint32_t addr) {
    176 	int r;
    177 
    178 	netboot_msg_t msg;
    179 	msg.magic = NB_MAGIC;
    180 	msg.cmd = NB_CMD_WRITE;
    181 
    182 	int fd = open(fn, O_RDONLY);
    183 	if (fd < 0) {
    184 		fprintf(stderr, "error: cannot open '%s'\n", fn);
    185 		return -1;
    186 	}
    187 	fprintf(stderr, "\n[ sending '%s' to 0x%08x ]\n", fn, addr);
    188 	for (;;) {
    189 		if ((r = read(fd, msg.db, NB_DATA_MAX)) <= 0) {
    190 			close(fd);
    191 			return r;
    192 		}
    193 		msg.arg = addr;
    194 		addr += r;
    195 		if (send_msg(s, &msg, r + NB_MSG_MIN)) {
    196 			close(fd);
    197 			return -1;
    198 		}
    199 	}
    200 }
    201 
    202 typedef struct {
    203 	const char* name;
    204 	uint32_t addr;
    205 } image_t;
    206 
    207 #define IMGMAX 8
    208 
    209 #define consume() \
    210 	do { argc--; argv++; if (argc == 1) { \
    211 		fprintf(stderr, "error: missing argument\n"); \
    212 		return -1; } \
    213 	} while (0)
    214 
    215 void usage(void) {
    216 	fprintf(stderr, "\n"
    217 	"usage:    netboot <option>* <image>+\n"
    218 	"\n"
    219 	"option:   -i <interface-name>\n"
    220 	"          -n <node-name>\n"
    221 	"\n"
    222 	"image:    <filename>@<hex-load-address>\n"
    223 	"\n"
    224 	"Send one or more images to <node-name> on <interface-name>.\n"
    225 	"Start execution at the address of the first image.\n\n");
    226 }
    227 
    228 int main(int argc, char **argv) {
    229 	int s, idx;
    230 
    231 	int do_exit = 0;
    232 	const char *ifname = "qemu0";
    233 	const char *nodename = "device";
    234 	image_t imagelist[IMGMAX];
    235 	int imagecount = 0;
    236 
    237 	char *tmp;
    238 	while (argc > 1) {
    239 		if ((tmp = strchr(argv[1], '@'))) {
    240 			if (imagecount == IMGMAX) {
    241 				fprintf(stderr, "error: too many images\n");
    242 				return -1;
    243 			}
    244 			*tmp++ = 0;
    245 			imagelist[imagecount].name = argv[1];
    246 			imagelist[imagecount].addr = strtoul(tmp, NULL, 16);
    247 			imagecount++;
    248 		} else if (!strcmp("-i", argv[1])) {
    249 			consume();
    250 			ifname = argv[1];
    251 		} else if (!strcmp("-n", argv[1])) {
    252 			consume();
    253 			nodename = argv[1];
    254 		} else if (!strcmp("-h", argv[1])) {
    255 			usage();
    256 			return 0;
    257 		} else if (!strcmp("-x", argv[1])) {
    258 			do_exit = 1;
    259 		} else {
    260 			fprintf(stderr, "error: unknown argument '%s'\n", argv[1]);
    261 			return -1;
    262 		} 
    263 		argc--;
    264 		argv++;
    265 	}
    266 
    267 	if (imagecount == 0) {
    268 		fprintf(stderr, "error: no files to send?\n");
    269 		return -1;
    270 	}
    271 	if ((idx = if_nametoindex(ifname)) == 0) {
    272 		fprintf(stderr, "unknown network interface: '%s'\n", argv[1]);
    273 		return -1;
    274 	}
    275 
    276 	fprintf(stderr, "\n[ netboot v0.1 ]\n");
    277 	fprintf(stderr, "[ netifc '%s' ]\n", ifname);
    278 	fprintf(stderr, "[ nodename '%s' ]\n", nodename);
    279 
    280 	for (;;) {
    281 again:
    282 		if ((s = open_node(idx, "device")) < 0) {
    283 			return -1;
    284 		}
    285 
    286 		for (unsigned n = 0; n < imagecount; n++) {
    287 			if (send_file(s, imagelist[n].name, imagelist[n].addr)) {
    288 				fprintf(stderr, "\n[ send failure ]\n");
    289 				close(s);
    290 				goto again;
    291 			}
    292 		}
    293 		fprintf(stderr, "\n[ start execution at 0x%08x ]\n", imagelist[0].addr);
    294 
    295 		netboot_msg_t msg;
    296 		msg.magic = NB_MAGIC;
    297 		msg.cmd = NB_CMD_EXEC;
    298 		msg.seq = ++seq;
    299 		msg.arg = imagelist[0].addr;
    300 		send_msg(s, &msg, NB_MSG_MIN);
    301 		fprintf(stderr, "\n");
    302 		close(s);
    303 
    304 		if (do_exit) break;
    305 	}
    306 
    307 	return 0;
    308 }