510 lines
9.8 KiB
C
510 lines
9.8 KiB
C
/* $OpenBSD: ldomd.c,v 1.11 2021/10/24 21:24:18 deraadt Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2012 Mark Kettenis
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <assert.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "ds.h"
|
|
#include "hvctl.h"
|
|
#include "mdesc.h"
|
|
#include "ldom_util.h"
|
|
#include "ldomd.h"
|
|
|
|
TAILQ_HEAD(guest_head, guest) guests;
|
|
|
|
void add_guest(struct md_node *);
|
|
void map_domain_services(struct md *);
|
|
|
|
void frag_init(void);
|
|
void add_frag_mblock(struct md_node *);
|
|
void add_frag(uint64_t);
|
|
void delete_frag(uint64_t);
|
|
uint64_t alloc_frag(void);
|
|
|
|
void hv_update_md(struct guest *guest);
|
|
void hv_open(void);
|
|
void hv_close(void);
|
|
void hv_read(uint64_t, void *, size_t);
|
|
void hv_write(uint64_t, void *, size_t);
|
|
|
|
int hvctl_seq = 1;
|
|
int hvctl_fd;
|
|
|
|
void *hvmd_buf;
|
|
size_t hvmd_len;
|
|
struct md *hvmd;
|
|
uint64_t hv_mdpa;
|
|
|
|
__dead void usage(void);
|
|
void logit(int, const char *, ...);
|
|
void vlog(int, const char *, va_list);
|
|
|
|
void
|
|
log_init(int n_debug)
|
|
{
|
|
extern char *__progname;
|
|
|
|
debug = n_debug;
|
|
|
|
if (!debug)
|
|
openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
|
|
|
|
tzset();
|
|
}
|
|
|
|
void
|
|
fatal(const char *emsg)
|
|
{
|
|
if (errno)
|
|
logit(LOG_CRIT, "fatal: %s: %s\n", emsg, strerror(errno));
|
|
else
|
|
logit(LOG_CRIT, "fatal: %s\n", emsg);
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
void
|
|
logit(int pri, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
vlog(pri, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void
|
|
vlog(int pri, const char *fmt, va_list ap)
|
|
{
|
|
char *nfmt;
|
|
|
|
if (debug) {
|
|
/* best effort in out of mem situations */
|
|
if (asprintf(&nfmt, "%s\n", fmt) == -1) {
|
|
vfprintf(stderr, fmt, ap);
|
|
fprintf(stderr, "\n");
|
|
} else {
|
|
vfprintf(stderr, nfmt, ap);
|
|
free(nfmt);
|
|
}
|
|
fflush(stderr);
|
|
} else
|
|
vsyslog(pri, fmt, ap);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct hvctl_msg msg;
|
|
ssize_t nbytes;
|
|
struct md_header hdr;
|
|
struct md_node *node;
|
|
struct md_prop *prop;
|
|
struct guest *guest;
|
|
int debug = 0;
|
|
int ch;
|
|
int i;
|
|
|
|
log_init(1);
|
|
|
|
while ((ch = getopt(argc, argv, "d")) != -1) {
|
|
switch (ch) {
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
/* NOTREACHED */
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
if (argc > 0)
|
|
usage();
|
|
|
|
if (!debug)
|
|
if (daemon(0, 0))
|
|
fatal("daemon");
|
|
|
|
log_init(debug);
|
|
|
|
hv_open();
|
|
|
|
/*
|
|
* Request config.
|
|
*/
|
|
bzero(&msg, sizeof(msg));
|
|
msg.hdr.op = HVCTL_OP_GET_HVCONFIG;
|
|
msg.hdr.seq = hvctl_seq++;
|
|
nbytes = write(hvctl_fd, &msg, sizeof(msg));
|
|
if (nbytes != sizeof(msg))
|
|
fatal("write");
|
|
|
|
bzero(&msg, sizeof(msg));
|
|
nbytes = read(hvctl_fd, &msg, sizeof(msg));
|
|
if (nbytes != sizeof(msg))
|
|
fatal("read");
|
|
|
|
hv_mdpa = msg.msg.hvcnf.hvmdp;
|
|
hv_read(hv_mdpa, &hdr, sizeof(hdr));
|
|
hvmd_len = sizeof(hdr) + hdr.node_blk_sz + hdr.name_blk_sz +
|
|
hdr.data_blk_sz;
|
|
hvmd_buf = xmalloc(hvmd_len);
|
|
hv_read(hv_mdpa, hvmd_buf, hvmd_len);
|
|
|
|
hvmd = md_ingest(hvmd_buf, hvmd_len);
|
|
node = md_find_node(hvmd, "guests");
|
|
TAILQ_INIT(&guests);
|
|
TAILQ_FOREACH(prop, &node->prop_list, link) {
|
|
if (prop->tag == MD_PROP_ARC &&
|
|
strcmp(prop->name->str, "fwd") == 0)
|
|
add_guest(prop->d.arc.node);
|
|
}
|
|
|
|
frag_init();
|
|
|
|
TAILQ_FOREACH(guest, &guests, link) {
|
|
struct ds_conn *dc;
|
|
char path[PATH_MAX];
|
|
|
|
if (strcmp(guest->name, "primary") == 0)
|
|
continue;
|
|
|
|
snprintf(path, sizeof(path), "/dev/ldom-%s", guest->name);
|
|
dc = ds_conn_open(path, guest);
|
|
ds_conn_register_service(dc, &var_config_service);
|
|
}
|
|
|
|
hv_close();
|
|
|
|
/*
|
|
* Open all virtual disk server port device files. As long as
|
|
* we keep these device files open, the corresponding virtual
|
|
* disks will be available to the guest domains. For now we
|
|
* just keep them open until we exit, so there is not reason
|
|
* to keep track of the file descriptors.
|
|
*/
|
|
for (i = 0; i < 256; i++) {
|
|
char path[PATH_MAX];
|
|
|
|
snprintf(path, sizeof(path), "/dev/vdsp%d", i);
|
|
if (open(path, O_RDWR) == -1)
|
|
break;
|
|
}
|
|
|
|
ds_conn_serve();
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
extern char *__progname;
|
|
|
|
fprintf(stderr, "usage: %s [-d]\n", __progname);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
void
|
|
add_guest(struct md_node *node)
|
|
{
|
|
struct guest *guest;
|
|
struct md_header hdr;
|
|
void *buf;
|
|
size_t len;
|
|
|
|
guest = xmalloc(sizeof(*guest));
|
|
|
|
if (!md_get_prop_str(hvmd, node, "name", &guest->name))
|
|
goto free;
|
|
if (!md_get_prop_val(hvmd, node, "gid", &guest->gid))
|
|
goto free;
|
|
if (!md_get_prop_val(hvmd, node, "mdpa", &guest->mdpa))
|
|
goto free;
|
|
|
|
hv_read(guest->mdpa, &hdr, sizeof(hdr));
|
|
len = sizeof(hdr) + hdr.node_blk_sz + hdr.name_blk_sz +
|
|
hdr.data_blk_sz;
|
|
buf = xmalloc(len);
|
|
hv_read(guest->mdpa, buf, len);
|
|
|
|
guest->node = node;
|
|
guest->md = md_ingest(buf, len);
|
|
if (strcmp(guest->name, "primary") == 0)
|
|
map_domain_services(guest->md);
|
|
|
|
TAILQ_INSERT_TAIL(&guests, guest, link);
|
|
return;
|
|
|
|
free:
|
|
free(guest);
|
|
}
|
|
|
|
void
|
|
map_domain_services(struct md *md)
|
|
{
|
|
struct md_node *node;
|
|
const char *name;
|
|
char source[64];
|
|
char target[64];
|
|
int unit = 0;
|
|
|
|
TAILQ_FOREACH(node, &md->node_list, link) {
|
|
if (strcmp(node->name->str, "virtual-device-port") != 0)
|
|
continue;
|
|
|
|
if (!md_get_prop_str(md, node, "vldc-svc-name", &name))
|
|
continue;
|
|
|
|
if (strncmp(name, "ldom-", 5) != 0 ||
|
|
strcmp(name, "ldom-primary") == 0)
|
|
continue;
|
|
|
|
snprintf(source, sizeof(source), "/dev/ldom%d", unit++);
|
|
snprintf(target, sizeof(target), "/dev/%s", name);
|
|
unlink(target);
|
|
symlink(source, target);
|
|
}
|
|
}
|
|
|
|
struct frag {
|
|
TAILQ_ENTRY(frag) link;
|
|
uint64_t base;
|
|
};
|
|
|
|
TAILQ_HEAD(frag_head, frag) free_frags;
|
|
|
|
uint64_t fragsize;
|
|
|
|
void
|
|
frag_init(void)
|
|
{
|
|
struct md_node *node;
|
|
struct md_prop *prop;
|
|
|
|
node = md_find_node(hvmd, "frag_space");
|
|
md_get_prop_val(hvmd, node, "fragsize", &fragsize);
|
|
TAILQ_INIT(&free_frags);
|
|
TAILQ_FOREACH(prop, &node->prop_list, link) {
|
|
if (prop->tag == MD_PROP_ARC &&
|
|
strcmp(prop->name->str, "fwd") == 0)
|
|
add_frag_mblock(prop->d.arc.node);
|
|
}
|
|
}
|
|
|
|
void
|
|
add_frag_mblock(struct md_node *node)
|
|
{
|
|
uint64_t base, size;
|
|
struct guest *guest;
|
|
|
|
md_get_prop_val(hvmd, node, "base", &base);
|
|
md_get_prop_val(hvmd, node, "size", &size);
|
|
while (size > fragsize) {
|
|
add_frag(base);
|
|
size -= fragsize;
|
|
base += fragsize;
|
|
}
|
|
|
|
delete_frag(hv_mdpa);
|
|
TAILQ_FOREACH(guest, &guests, link)
|
|
delete_frag(guest->mdpa);
|
|
}
|
|
|
|
void
|
|
add_frag(uint64_t base)
|
|
{
|
|
struct frag *frag;
|
|
|
|
frag = xmalloc(sizeof(*frag));
|
|
frag->base = base;
|
|
TAILQ_INSERT_TAIL(&free_frags, frag, link);
|
|
}
|
|
|
|
void
|
|
delete_frag(uint64_t base)
|
|
{
|
|
struct frag *frag;
|
|
struct frag *tmp;
|
|
|
|
TAILQ_FOREACH_SAFE(frag, &free_frags, link, tmp) {
|
|
if (frag->base == base) {
|
|
TAILQ_REMOVE(&free_frags, frag, link);
|
|
free(frag);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint64_t
|
|
alloc_frag(void)
|
|
{
|
|
struct frag *frag;
|
|
uint64_t base;
|
|
|
|
frag = TAILQ_FIRST(&free_frags);
|
|
if (frag == NULL)
|
|
return -1;
|
|
|
|
TAILQ_REMOVE(&free_frags, frag, link);
|
|
base = frag->base;
|
|
free(frag);
|
|
|
|
return base;
|
|
}
|
|
|
|
void
|
|
hv_update_md(struct guest *guest)
|
|
{
|
|
struct hvctl_msg msg;
|
|
size_t nbytes;
|
|
void *buf;
|
|
size_t size;
|
|
uint64_t mdpa;
|
|
|
|
hv_open();
|
|
|
|
mdpa = alloc_frag();
|
|
size = md_exhume(guest->md, &buf);
|
|
hv_write(mdpa, buf, size);
|
|
add_frag(guest->mdpa);
|
|
guest->mdpa = mdpa;
|
|
free(buf);
|
|
|
|
md_set_prop_val(hvmd, guest->node, "mdpa", guest->mdpa);
|
|
|
|
mdpa = alloc_frag();
|
|
size = md_exhume(hvmd, &buf);
|
|
hv_write(mdpa, buf, size);
|
|
add_frag(hv_mdpa);
|
|
hv_mdpa = mdpa;
|
|
free(buf);
|
|
|
|
/* Update config. */
|
|
bzero(&msg, sizeof(msg));
|
|
msg.hdr.op = HVCTL_OP_RECONFIGURE;
|
|
msg.hdr.seq = hvctl_seq++;
|
|
msg.msg.reconfig.guestid = -1;
|
|
msg.msg.reconfig.hvmdp = hv_mdpa;
|
|
nbytes = write(hvctl_fd, &msg, sizeof(msg));
|
|
if (nbytes != sizeof(msg))
|
|
fatal("write");
|
|
|
|
bzero(&msg, sizeof(msg));
|
|
nbytes = read(hvctl_fd, &msg, sizeof(msg));
|
|
if (nbytes != sizeof(msg))
|
|
fatal("read");
|
|
|
|
hv_close();
|
|
|
|
if (msg.hdr.status != HVCTL_ST_OK)
|
|
logit(LOG_CRIT, "reconfigure failed: %d", msg.hdr.status);
|
|
}
|
|
|
|
void
|
|
hv_open(void)
|
|
{
|
|
struct hvctl_msg msg;
|
|
ssize_t nbytes;
|
|
uint64_t code;
|
|
|
|
hvctl_fd = open("/dev/hvctl", O_RDWR);
|
|
if (hvctl_fd == -1)
|
|
fatal("cannot open /dev/hvctl");
|
|
|
|
/*
|
|
* Say "Hello".
|
|
*/
|
|
bzero(&msg, sizeof(msg));
|
|
msg.hdr.op = HVCTL_OP_HELLO;
|
|
msg.hdr.seq = hvctl_seq++;
|
|
msg.msg.hello.major = 1;
|
|
nbytes = write(hvctl_fd, &msg, sizeof(msg));
|
|
if (nbytes != sizeof(msg))
|
|
fatal("write");
|
|
|
|
bzero(&msg, sizeof(msg));
|
|
nbytes = read(hvctl_fd, &msg, sizeof(msg));
|
|
if (nbytes != sizeof(msg))
|
|
fatal("read");
|
|
|
|
code = msg.msg.clnge.code ^ 0xbadbeef20;
|
|
|
|
/*
|
|
* Respond to challenge.
|
|
*/
|
|
bzero(&msg, sizeof(msg));
|
|
msg.hdr.op = HVCTL_OP_RESPONSE;
|
|
msg.hdr.seq = hvctl_seq++;
|
|
msg.msg.clnge.code = code ^ 0x12cafe42a;
|
|
nbytes = write(hvctl_fd, &msg, sizeof(msg));
|
|
if (nbytes != sizeof(msg))
|
|
fatal("write");
|
|
|
|
bzero(&msg, sizeof(msg));
|
|
nbytes = read(hvctl_fd, &msg, sizeof(msg));
|
|
if (nbytes != sizeof(msg))
|
|
fatal("read");
|
|
}
|
|
|
|
void
|
|
hv_close(void)
|
|
{
|
|
close(hvctl_fd);
|
|
hvctl_fd = -1;
|
|
}
|
|
|
|
void
|
|
hv_read(uint64_t addr, void *buf, size_t len)
|
|
{
|
|
struct hv_io hi;
|
|
|
|
hi.hi_cookie = addr;
|
|
hi.hi_addr = buf;
|
|
hi.hi_len = len;
|
|
|
|
if (ioctl(hvctl_fd, HVIOCREAD, &hi) == -1)
|
|
fatal("ioctl");
|
|
}
|
|
|
|
void
|
|
hv_write(uint64_t addr, void *buf, size_t len)
|
|
{
|
|
struct hv_io hi;
|
|
|
|
hi.hi_cookie = addr;
|
|
hi.hi_addr = buf;
|
|
hi.hi_len = len;
|
|
|
|
if (ioctl(hvctl_fd, HVIOCWRITE, &hi) == -1)
|
|
fatal("ioctl");
|
|
}
|