xref: /openbsd/usr.sbin/ldomd/ldomd.c (revision 9b7c3dbb)
1 /*	$OpenBSD: ldomd.c,v 1.7 2016/08/28 00:51:48 guenther Exp $	*/
2 
3 /*
4  * Copyright (c) 2012 Mark Kettenis
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <assert.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <time.h>
31 #include <unistd.h>
32 
33 #include "ds.h"
34 #include "hvctl.h"
35 #include "mdesc.h"
36 #include "util.h"
37 #include "ldomd.h"
38 
39 TAILQ_HEAD(guest_head, guest) guests;
40 
41 void add_guest(struct md_node *);
42 void map_domain_services(struct md *);
43 
44 void frag_init(void);
45 void add_frag_mblock(struct md_node *);
46 void add_frag(uint64_t);
47 void delete_frag(uint64_t);
48 uint64_t alloc_frag(void);
49 
50 void hv_update_md(struct guest *guest);
51 void hv_open(void);
52 void hv_close(void);
53 void hv_read(uint64_t, void *, size_t);
54 void hv_write(uint64_t, void *, size_t);
55 
56 int hvctl_seq = 1;
57 int hvctl_fd;
58 
59 void *hvmd_buf;
60 size_t hvmd_len;
61 struct md *hvmd;
62 uint64_t hv_mdpa;
63 
64 __dead void	usage(void);
65 void	logit(int, const char *, ...);
66 void	vlog(int, const char *, va_list);
67 
68 void
69 log_init(int n_debug)
70 {
71 	extern char *__progname;
72 
73 	debug = n_debug;
74 
75 	if (!debug)
76 		openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
77 
78 	tzset();
79 }
80 
81 void
82 fatal(const char *emsg)
83 {
84 	if (errno)
85 		logit(LOG_CRIT, "fatal: %s: %s\n", emsg, strerror(errno));
86 	else
87 		logit(LOG_CRIT, "fatal: %s\n", emsg);
88 
89 	exit(EXIT_FAILURE);
90 }
91 
92 void
93 logit(int pri, const char *fmt, ...)
94 {
95 	va_list ap;
96 
97 	va_start(ap, fmt);
98 	vlog(pri, fmt, ap);
99 	va_end(ap);
100 }
101 
102 void
103 vlog(int pri, const char *fmt, va_list ap)
104 {
105 	char *nfmt;
106 
107 	if (debug) {
108 		/* best effort in out of mem situations */
109 		if (asprintf(&nfmt, "%s\n", fmt) == -1) {
110 			vfprintf(stderr, fmt, ap);
111 			fprintf(stderr, "\n");
112 		} else {
113 			vfprintf(stderr, nfmt, ap);
114 			free(nfmt);
115 		}
116 		fflush(stderr);
117 	} else
118 		vsyslog(pri, fmt, ap);
119 }
120 
121 int
122 main(int argc, char **argv)
123 {
124 	struct hvctl_msg msg;
125 	ssize_t nbytes;
126 	struct md_header hdr;
127 	struct md_node *node;
128 	struct md_prop *prop;
129 	struct guest *guest;
130 	int debug = 0;
131 	int ch;
132 	int i;
133 
134 	log_init(1);
135 
136 	while ((ch = getopt(argc, argv, "d")) != -1) {
137 		switch (ch) {
138 		case 'd':
139 			debug = 1;
140 			break;
141 		default:
142 			usage();
143 			/* NOTREACHED */
144 		}
145 	}
146 
147 	argc -= optind;
148 	argv += optind;
149 	if (argc > 0)
150 		usage();
151 
152 	if (!debug)
153 		if (daemon(0, 0))
154 			fatal("daemon");
155 
156 	log_init(debug);
157 
158 	hv_open();
159 
160 	/*
161 	 * Request config.
162 	 */
163 	bzero(&msg, sizeof(msg));
164 	msg.hdr.op = HVCTL_OP_GET_HVCONFIG;
165 	msg.hdr.seq = hvctl_seq++;
166 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
167 	if (nbytes != sizeof(msg))
168 		fatal("write");
169 
170 	bzero(&msg, sizeof(msg));
171 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
172 	if (nbytes != sizeof(msg))
173 		fatal("read");
174 
175 	hv_mdpa = msg.msg.hvcnf.hvmdp;
176 	hv_read(hv_mdpa, &hdr, sizeof(hdr));
177 	hvmd_len = sizeof(hdr) + hdr.node_blk_sz + hdr.name_blk_sz +
178 	    hdr.data_blk_sz;
179 	hvmd_buf = xmalloc(hvmd_len);
180 	hv_read(hv_mdpa, hvmd_buf, hvmd_len);
181 
182 	hvmd = md_ingest(hvmd_buf, hvmd_len);
183 	node = md_find_node(hvmd, "guests");
184 	TAILQ_INIT(&guests);
185 	TAILQ_FOREACH(prop, &node->prop_list, link) {
186 		if (prop->tag == MD_PROP_ARC &&
187 		    strcmp(prop->name->str, "fwd") == 0)
188 			add_guest(prop->d.arc.node);
189 	}
190 
191 	frag_init();
192 
193 	TAILQ_FOREACH(guest, &guests, link) {
194 		struct ds_conn *dc;
195 		char path[64];
196 
197 		if (strcmp(guest->name, "primary") == 0)
198 			continue;
199 
200 		snprintf(path, sizeof(path), "/dev/ldom-%s", guest->name);
201 		dc = ds_conn_open(path, guest);
202 		ds_conn_register_service(dc, &var_config_service);
203 	}
204 
205 	hv_close();
206 
207 	/*
208 	 * Open all virtual disk server port device files.  As long as
209 	 * we keep these device files open, the corresponding virtual
210 	 * disks will be available to the guest domains.  For now we
211 	 * just keep them open until we exit, so there is not reason
212 	 * to keep track of the file descriptors.
213 	 */
214 	for (i = 0; i < 256; i++) {
215 		char path[64];
216 
217 		snprintf(path, sizeof(path), "/dev/vdsp%d", i);
218 		if (open(path, O_RDWR, 0) == -1)
219 			break;
220 	}
221 
222 	ds_conn_serve();
223 
224 	exit(EXIT_SUCCESS);
225 }
226 
227 void
228 usage(void)
229 {
230 	extern char *__progname;
231 
232 	fprintf(stderr, "usage: %s [-d]\n", __progname);
233 	exit(EXIT_FAILURE);
234 }
235 
236 void
237 add_guest(struct md_node *node)
238 {
239 	struct guest *guest;
240 	struct md_header hdr;
241 	void *buf;
242 	size_t len;
243 
244 	guest = xmalloc(sizeof(*guest));
245 
246 	if (!md_get_prop_str(hvmd, node, "name", &guest->name))
247 		goto free;
248 	if (!md_get_prop_val(hvmd, node, "gid", &guest->gid))
249 		goto free;
250 	if (!md_get_prop_val(hvmd, node, "mdpa", &guest->mdpa))
251 		goto free;
252 
253 	hv_read(guest->mdpa, &hdr, sizeof(hdr));
254 	len = sizeof(hdr) + hdr.node_blk_sz + hdr.name_blk_sz +
255 	    hdr.data_blk_sz;
256 	buf = xmalloc(len);
257 	hv_read(guest->mdpa, buf, len);
258 
259 	guest->node = node;
260 	guest->md = md_ingest(buf, len);
261 	if (strcmp(guest->name, "primary") == 0)
262 		map_domain_services(guest->md);
263 
264 	TAILQ_INSERT_TAIL(&guests, guest, link);
265 	return;
266 
267 free:
268 	free(guest);
269 }
270 
271 void
272 map_domain_services(struct md *md)
273 {
274 	struct md_node *node;
275 	const char *name;
276 	char source[64];
277 	char target[64];
278 	int unit = 0;
279 
280 	TAILQ_FOREACH(node, &md->node_list, link) {
281 		if (strcmp(node->name->str, "virtual-device-port") != 0)
282 			continue;
283 
284 		if (!md_get_prop_str(md, node, "vldc-svc-name", &name))
285 			continue;
286 
287 		if (strncmp(name, "ldom-", 5) != 0 ||
288 		    strcmp(name, "ldom-primary") == 0)
289 			continue;
290 
291 		snprintf(source, sizeof(source), "/dev/ldom%d", unit++);
292 		snprintf(target, sizeof(target), "/dev/%s", name);
293 		unlink(target);
294 		symlink(source, target);
295 	}
296 }
297 
298 struct frag {
299 	TAILQ_ENTRY(frag) link;
300 	uint64_t base;
301 };
302 
303 TAILQ_HEAD(frag_head, frag) free_frags;
304 
305 uint64_t fragsize;
306 
307 void
308 frag_init(void)
309 {
310 	struct md_node *node;
311 	struct md_prop *prop;
312 
313 	node = md_find_node(hvmd, "frag_space");
314 	md_get_prop_val(hvmd, node, "fragsize", &fragsize);
315 	TAILQ_INIT(&free_frags);
316 	TAILQ_FOREACH(prop, &node->prop_list, link) {
317 		if (prop->tag == MD_PROP_ARC &&
318 		    strcmp(prop->name->str, "fwd") == 0)
319 			add_frag_mblock(prop->d.arc.node);
320 	}
321 }
322 
323 void
324 add_frag_mblock(struct md_node *node)
325 {
326 	uint64_t base, size;
327 	struct guest *guest;
328 
329 	md_get_prop_val(hvmd, node, "base", &base);
330 	md_get_prop_val(hvmd, node, "size", &size);
331 	while (size > fragsize) {
332 		add_frag(base);
333 		size -= fragsize;
334 		base += fragsize;
335 	}
336 
337 	delete_frag(hv_mdpa);
338 	TAILQ_FOREACH(guest, &guests, link)
339 		delete_frag(guest->mdpa);
340 }
341 
342 void
343 add_frag(uint64_t base)
344 {
345 	struct frag *frag;
346 
347 	frag = xmalloc(sizeof(*frag));
348 	frag->base = base;
349 	TAILQ_INSERT_TAIL(&free_frags, frag, link);
350 }
351 
352 void
353 delete_frag(uint64_t base)
354 {
355 	struct frag *frag;
356 	struct frag *tmp;
357 
358 	TAILQ_FOREACH_SAFE(frag, &free_frags, link, tmp) {
359 		if (frag->base == base) {
360 			TAILQ_REMOVE(&free_frags, frag, link);
361 			free(frag);
362 		}
363 	}
364 }
365 
366 uint64_t
367 alloc_frag(void)
368 {
369 	struct frag *frag;
370 	uint64_t base;
371 
372 	frag = TAILQ_FIRST(&free_frags);
373 	if (frag == NULL)
374 		return -1;
375 
376 	TAILQ_REMOVE(&free_frags, frag, link);
377 	base = frag->base;
378 	free(frag);
379 
380 	return base;
381 }
382 
383 void
384 hv_update_md(struct guest *guest)
385 {
386 	struct hvctl_msg msg;
387 	size_t nbytes;
388 	void *buf;
389 	size_t size;
390 	uint64_t mdpa;
391 
392 	hv_open();
393 
394 	mdpa = alloc_frag();
395 	size = md_exhume(guest->md, &buf);
396 	hv_write(mdpa, buf, size);
397 	add_frag(guest->mdpa);
398 	guest->mdpa = mdpa;
399 	free(buf);
400 
401 	md_set_prop_val(hvmd, guest->node, "mdpa", guest->mdpa);
402 
403 	mdpa = alloc_frag();
404 	size = md_exhume(hvmd, &buf);
405 	hv_write(mdpa, buf, size);
406 	add_frag(hv_mdpa);
407 	hv_mdpa = mdpa;
408 	free(buf);
409 
410 	/* Update config.  */
411 	bzero(&msg, sizeof(msg));
412 	msg.hdr.op = HVCTL_OP_RECONFIGURE;
413 	msg.hdr.seq = hvctl_seq++;
414 	msg.msg.reconfig.guestid = -1;
415 	msg.msg.reconfig.hvmdp = hv_mdpa;
416 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
417 	if (nbytes != sizeof(msg))
418 		fatal("write");
419 
420 	bzero(&msg, sizeof(msg));
421 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
422 	if (nbytes != sizeof(msg))
423 		fatal("read");
424 
425 	hv_close();
426 
427 	if (msg.hdr.status != HVCTL_ST_OK)
428 		logit(LOG_CRIT, "reconfigure failed: %d", msg.hdr.status);
429 }
430 
431 void
432 hv_open(void)
433 {
434 	struct hvctl_msg msg;
435 	ssize_t nbytes;
436 	uint64_t code;
437 
438 	hvctl_fd = open("/dev/hvctl", O_RDWR, 0);
439 	if (hvctl_fd == -1)
440 		fatal("cannot open /dev/hvctl");
441 
442 	/*
443 	 * Say "Hello".
444 	 */
445 	bzero(&msg, sizeof(msg));
446 	msg.hdr.op = HVCTL_OP_HELLO;
447 	msg.hdr.seq = hvctl_seq++;
448 	msg.msg.hello.major = 1;
449 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
450 	if (nbytes != sizeof(msg))
451 		fatal("write");
452 
453 	bzero(&msg, sizeof(msg));
454 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
455 	if (nbytes != sizeof(msg))
456 		fatal("read");
457 
458 	code = msg.msg.clnge.code ^ 0xbadbeef20;
459 
460 	/*
461 	 * Respond to challenge.
462 	 */
463 	bzero(&msg, sizeof(msg));
464 	msg.hdr.op = HVCTL_OP_RESPONSE;
465 	msg.hdr.seq = hvctl_seq++;
466 	msg.msg.clnge.code = code ^ 0x12cafe42a;
467 	nbytes = write(hvctl_fd, &msg, sizeof(msg));
468 	if (nbytes != sizeof(msg))
469 		fatal("write");
470 
471 	bzero(&msg, sizeof(msg));
472 	nbytes = read(hvctl_fd, &msg, sizeof(msg));
473 	if (nbytes != sizeof(msg))
474 		fatal("read");
475 }
476 
477 void
478 hv_close(void)
479 {
480 	close(hvctl_fd);
481 	hvctl_fd = -1;
482 }
483 
484 void
485 hv_read(uint64_t addr, void *buf, size_t len)
486 {
487 	struct hv_io hi;
488 
489 	hi.hi_cookie = addr;
490 	hi.hi_addr = buf;
491 	hi.hi_len = len;
492 
493 	if (ioctl(hvctl_fd, HVIOCREAD, &hi) == -1)
494 		fatal("ioctl");
495 }
496 
497 void
498 hv_write(uint64_t addr, void *buf, size_t len)
499 {
500 	struct hv_io hi;
501 
502 	hi.hi_cookie = addr;
503 	hi.hi_addr = buf;
504 	hi.hi_len = len;
505 
506 	if (ioctl(hvctl_fd, HVIOCWRITE, &hi) == -1)
507 		fatal("ioctl");
508 }
509