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