summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/ldomctl/Makefile4
-rw-r--r--usr.sbin/ldomctl/config.c1832
-rw-r--r--usr.sbin/ldomctl/ldomctl.c25
-rw-r--r--usr.sbin/ldomctl/ldomctl.h102
-rw-r--r--usr.sbin/ldomctl/mdstore.c8
-rw-r--r--usr.sbin/ldomctl/parse.y521
6 files changed, 2477 insertions, 15 deletions
diff --git a/usr.sbin/ldomctl/Makefile b/usr.sbin/ldomctl/Makefile
index f852f4a3b1b..fc0ae0c9f46 100644
--- a/usr.sbin/ldomctl/Makefile
+++ b/usr.sbin/ldomctl/Makefile
@@ -1,11 +1,11 @@
-# $OpenBSD: Makefile,v 1.7 2012/11/04 20:09:02 kettenis Exp $
+# $OpenBSD: Makefile,v 1.8 2012/11/24 11:50:45 kettenis Exp $
.if ${MACHINE} == "sparc64"
.PATH: ${.CURDIR}/../ldomd
PROG= ldomctl
-SRCS= ldomctl.c ds.c mdesc.c util.c mdstore.c pri.c
+SRCS= ldomctl.c ds.c mdesc.c util.c mdstore.c pri.c config.c parse.y
CFLAGS+=-Wall
CFLAGS+=-I${.CURDIR}/../ldomd -I${.CURDIR}
DEBUG= -g
diff --git a/usr.sbin/ldomctl/config.c b/usr.sbin/ldomctl/config.c
new file mode 100644
index 00000000000..a7c40064606
--- /dev/null
+++ b/usr.sbin/ldomctl/config.c
@@ -0,0 +1,1832 @@
+/* $OpenBSD: config.c,v 1.1 2012/11/24 11:50:45 kettenis 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/queue.h>
+#include <assert.h>
+#include <err.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mdesc.h"
+#include "ldomctl.h"
+#include "util.h"
+
+#define LDC_GUEST 0
+#define LDC_HV 1
+#define LDC_SP 2
+
+#define LDC_HVCTL_SVC 1
+#define LDC_CONSOLE_SVC 2
+
+struct pri_cpu {
+ uint64_t pid;
+ TAILQ_ENTRY(pri_cpu) link;
+};
+
+struct pri_core {
+ TAILQ_HEAD(pri_cpu_head, pri_cpu) cpu_list;
+ TAILQ_ENTRY(pri_core) link;
+};
+
+TAILQ_HEAD(pri_core_head, pri_core) pri_core_list;
+
+struct frag {
+ TAILQ_ENTRY(frag) link;
+ uint64_t base;
+};
+
+struct guest **guests;
+struct console **consoles;
+struct cpu **cpus;
+struct mblock **mblocks;
+struct ldc_endpoint **ldc_endpoints;
+
+uint64_t max_cpus;
+
+uint64_t max_guests;
+uint64_t max_hv_ldcs;
+uint64_t max_guest_ldcs;
+uint64_t md_elbow_room;
+uint64_t max_mblocks;
+
+uint64_t rombase;
+uint64_t romsize;
+
+struct md *pri;
+struct md *hvmd;
+struct md *protomd;
+
+struct guest *guest_lookup(const char *);
+
+TAILQ_HEAD(, frag) free_frags = TAILQ_HEAD_INITIALIZER(free_frags);
+TAILQ_HEAD(, cpu) free_cpus = TAILQ_HEAD_INITIALIZER(free_cpus);
+
+struct pri_core *pri_cores;
+
+void
+pri_add_core(struct md *md, struct md_node *node, struct pri_core *core)
+{
+ struct pri_cpu *cpu;
+ struct md_node *node2;
+ struct md_prop *prop;
+
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "back") == 0) {
+ node2 = prop->d.arc.node;
+ if (strcmp(node2->name->str, "cpu") == 0) {
+ cpu = xmalloc(sizeof(*cpu));
+ md_get_prop_val(md, node2, "pid", &cpu->pid);
+ TAILQ_INSERT_TAIL(&core->cpu_list, cpu, link);
+ } else {
+ pri_add_core(md, node2, core);
+ }
+ }
+ }
+}
+
+void
+pri_init_cores(struct md *md)
+{
+ struct pri_core *core;
+ struct md_node *node;
+ const void *type;
+ size_t len;
+
+ TAILQ_INIT(&pri_core_list);
+
+ TAILQ_FOREACH(node, &md->node_list, link) {
+ if (strcmp(node->name->str, "tlb") == 0 &&
+ md_get_prop_data(md, node, "type", &type, &len) &&
+ strcmp(type, "data") == 0) {
+ core = xmalloc(sizeof(*core));
+ TAILQ_INIT(&core->cpu_list);
+ pri_add_core(md, node, core);
+ }
+ }
+}
+
+void
+pri_add_cpu(struct md *md, struct md_node *node)
+{
+ struct cpu *cpu;
+
+ cpu = xmalloc(sizeof(*cpu));
+ md_get_prop_val(md, node, "pid", &cpu->pid);
+ cpu->vid = -1;
+ cpu->gid = -1;
+ cpu->partid = -1;
+ cpu->resource_id = -1;
+ TAILQ_INSERT_TAIL(&free_cpus, cpu, link);
+}
+
+struct cpu *
+pri_alloc_cpu(uint64_t pid)
+{
+ struct cpu *cpu;
+
+ if (pid == -1 && !TAILQ_EMPTY(&free_cpus)) {
+ cpu = TAILQ_FIRST(&free_cpus);
+ TAILQ_REMOVE(&free_cpus, cpu, link);
+ return cpu;
+ }
+
+ TAILQ_FOREACH(cpu, &free_cpus, link) {
+ if (cpu->pid == pid) {
+ TAILQ_REMOVE(&free_cpus, cpu, link);
+ return cpu;
+ }
+ }
+
+ return NULL;
+}
+
+void
+pri_free_cpu(struct cpu *cpu)
+{
+ TAILQ_INSERT_TAIL(&free_cpus, cpu, link);
+}
+
+void
+pri_init(struct md *md)
+{
+ struct md_node *node, *node2;
+ struct md_prop *prop;
+ uint64_t base, size;
+ uint64_t offset, guest_use;
+
+ node = md_find_node(pri, "platform");
+ if (node == NULL)
+ errx(1, "platform node not found");
+
+ md_get_prop_val(md, node, "max-cpus", &max_cpus);
+
+ node = md_find_node(pri, "firmware");
+ if (node == NULL)
+ errx(1, "firmware node not found");
+
+ md_get_prop_val(md, node, "max_guests", &max_guests);
+ md_get_prop_val(md, node, "max_hv_ldcs", &max_hv_ldcs);
+ md_get_prop_val(md, node, "max_guest_ldcs", &max_guest_ldcs);
+ md_get_prop_val(md, node, "md_elbow_room", &md_elbow_room);
+ md_get_prop_val(md, node, "max_mblocks", &max_mblocks);
+
+ node = md_find_node(md, "read_only_memory");
+ if (node == NULL)
+ errx(1, "read_only_memory node not found");
+ if (!md_get_prop_val(md, node, "base", &base))
+ errx(1, "missing base property in read_only_memory node");
+ if (!md_get_prop_val(md, node, "size", &size))
+ errx(1, "missing size property in read_only_memory node");
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0) {
+ node2 = prop->d.arc.node;
+ if (!md_get_prop_val(md, node2, "guest_use",
+ &guest_use) || guest_use == 0)
+ continue;
+ if (!md_get_prop_val(md, node2, "offset", &offset) ||
+ !md_get_prop_val(md, node2, "size", &size))
+ continue;
+ rombase = base + offset;
+ romsize = size;
+ }
+ }
+ if (romsize == 0)
+ errx(1, "no suitable firmware image found");
+
+ node = md_find_node(md, "platform");
+ assert(node);
+ md_set_prop_val(md, node, "domaining-enabled", 0x1);
+
+ md_write(md, "pri");
+
+ protomd = md_copy(md);
+ md_find_delete_node(protomd, "components");
+ md_find_delete_node(protomd, "devalias");
+ md_find_delete_node(protomd, "domain-services");
+ md_find_delete_node(protomd, "channel-devices");
+ md_find_delete_node(protomd, "channel-endpoints");
+ md_find_delete_node(protomd, "firmware");
+ md_find_delete_node(protomd, "ldc_endpoints");
+ md_find_delete_node(protomd, "memory-segments");
+ md_collect_garbage(protomd);
+ md_write(protomd, "protomd");
+
+ guests = xzalloc(max_guests * sizeof(*guests));
+ consoles = xzalloc(max_guests * sizeof(*consoles));
+ cpus = xzalloc(max_cpus * sizeof(*cpus));
+ mblocks = xzalloc(max_mblocks * sizeof(*mblocks));
+ ldc_endpoints = xzalloc(max_guest_ldcs * sizeof(*ldc_endpoints));
+
+ node = md_find_node(md, "cpus");
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0)
+ pri_add_cpu(md, prop->d.arc.node);
+ }
+
+#if 0
+ pri_init_cores(md);
+#endif
+}
+
+uint64_t fragsize;
+
+void
+hvmd_init_frag(struct md *md, struct md_node *node)
+{
+ struct frag *frag;
+ uint64_t base, size;
+
+ md_get_prop_val(md, node, "base", &base);
+ md_get_prop_val(md, node, "size", &size);
+ while (size > fragsize) {
+ frag = xmalloc(sizeof(*frag));
+ frag->base = base;
+ TAILQ_INSERT_TAIL(&free_frags, frag, link);
+ base += fragsize;
+ size -= fragsize;
+ }
+}
+
+uint64_t
+hvmd_alloc_frag(uint64_t base)
+{
+ struct frag *frag = TAILQ_FIRST(&free_frags);
+
+ if (base != -1) {
+ TAILQ_FOREACH(frag, &free_frags, link) {
+ if (frag->base == base)
+ break;
+ }
+ }
+
+ if (frag == TAILQ_END(&free_frags))
+ return -1;
+
+ TAILQ_REMOVE(&free_frags, frag, link);
+ base = frag->base;
+ free(frag);
+
+ return base;
+}
+
+void
+hvmd_init_mblock(struct md *md, struct md_node *node)
+{
+ struct mblock *mblock;
+ uint64_t resource_id;
+
+ if (!md_get_prop_val(md, node, "resource_id", &resource_id))
+ errx(1, "missing resource_id property in mblock node");
+
+ if (resource_id >= max_mblocks)
+ errx(1, "resource_id larger than max_mblocks");
+
+ mblock = xmalloc(sizeof(*mblock));
+ md_get_prop_val(md, node, "membase", &mblock->membase);
+ md_get_prop_val(md, node, "memsize", &mblock->memsize);
+ md_get_prop_val(md, node, "realbase", &mblock->realbase);
+ mblock->resource_id = resource_id;
+ mblocks[resource_id] = mblock;
+ mblock->hv_node = node;
+}
+
+void
+hvmd_init_console(struct md *md, struct md_node *node)
+{
+ struct console *console;
+ uint64_t resource_id;
+
+ if (!md_get_prop_val(md, node, "resource_id", &resource_id))
+ errx(1, "missing resource_id property in console node");
+
+ if (resource_id >= max_guests)
+ errx(1, "resource_id larger than max_guests");
+
+ console = xmalloc(sizeof(*console));
+ md_get_prop_val(md, node, "ino", &console->ino);
+ console->resource_id = resource_id;
+ consoles[resource_id] = console;
+ console->hv_node = node;
+}
+
+void
+hvmd_fixup_guest(struct md *md, struct md_node *guest, struct md_node *node)
+{
+ struct md_prop *prop;
+
+ TAILQ_FOREACH(prop, &guest->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0) {
+ if (prop->d.arc.node == node)
+ return;
+ }
+ }
+
+ md_add_prop_arc(md, guest, "fwd", node);
+}
+
+void
+hvmd_init_cpu(struct md *md, struct md_node *node)
+{
+ struct cpu *cpu;
+ uint64_t pid;
+ uint64_t resource_id;
+ struct md_node *node2;
+ struct md_prop *prop;
+
+ if (!md_get_prop_val(md, node, "resource_id", &resource_id))
+ errx(1, "missing resource_id property in cpu node");
+
+ if (resource_id >= max_cpus)
+ errx(1, "resource_id larger than max-cpus");
+
+ if (!md_get_prop_val(md, node, "pid", &pid))
+ errx(1, "missing pid property in cpu node");
+
+ cpu = pri_alloc_cpu(pid);
+ md_get_prop_val(md, node, "vid", &cpu->vid);
+ if (!md_get_prop_val(md, node, "gid", &cpu->gid))
+ cpu->gid = 0;
+ md_get_prop_val(md, node, "partid", &cpu->partid);
+ cpu->resource_id = resource_id;
+ cpus[resource_id] = cpu;
+ cpu->hv_node = node;
+
+ /* Fixup missing links. */
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "back") == 0) {
+ node2 = prop->d.arc.node;
+ if (strcmp(node2->name->str, "guest") == 0)
+ hvmd_fixup_guest(md, node2, node);
+ }
+ }
+}
+
+void
+hvmd_init_endpoint(struct md *md, struct md_node *node)
+{
+ struct ldc_endpoint *endpoint;
+ uint64_t resource_id;
+
+ if (!md_get_prop_val(md, node, "resource_id", &resource_id))
+ errx(1, "missing resource_id property in ldc_endpoint node");
+
+ if (resource_id >= max_guest_ldcs)
+ errx(1, "resource_id larger than max_guest_ldcs");
+
+ if (ldc_endpoints[resource_id])
+ errx(1, "duplicate resource_id");
+
+ endpoint = xzalloc(sizeof(*endpoint));
+ md_get_prop_val(md, node, "target_type", &endpoint->target_type);
+ md_get_prop_val(md, node, "target_guest", &endpoint->target_guest);
+ md_get_prop_val(md, node, "channel", &endpoint->channel);
+ md_get_prop_val(md, node, "target_channel", &endpoint->target_channel);
+ md_get_prop_val(md, node, "tx-ino", &endpoint->tx_ino);
+ md_get_prop_val(md, node, "rx-ino", &endpoint->rx_ino);
+ md_get_prop_val(md, node, "private_svc", &endpoint->private_svc);
+ md_get_prop_val(md, node, "svc_id", &endpoint->svc_id);
+ endpoint->resource_id = resource_id;
+ ldc_endpoints[resource_id] = endpoint;
+ endpoint->hv_node = node;
+}
+
+void
+hvmd_init_guest(struct md *md, struct md_node *node)
+{
+ struct guest *guest;
+ struct md_node *node2;
+ struct md_prop *prop;
+ uint64_t resource_id;
+ struct ldc_endpoint *endpoint;
+ char *path;
+
+ if (!md_get_prop_val(md, node, "resource_id", &resource_id))
+ errx(1, "missing resource_id property in guest node");
+
+ if (resource_id >= max_guests)
+ errx(1, "resource_id larger than max_guests");
+
+ guest = xzalloc(sizeof(*guest));
+ TAILQ_INIT(&guest->cpu_list);
+ TAILQ_INIT(&guest->mblock_list);
+ TAILQ_INIT(&guest->endpoint_list);
+ md_get_prop_str(md, node, "name", &guest->name);
+ md_get_prop_val(md, node, "gid", &guest->gid);
+ md_get_prop_val(md, node, "pid", &guest->pid);
+ md_get_prop_val(md, node, "mdpa", &guest->mdpa);
+ guest->resource_id = resource_id;
+ guests[resource_id] = guest;
+ guest->hv_node = node;
+
+ if (strcmp(guest->name, "primary") == 0 && guest->gid != 0)
+ errx(1, "gid of primary domain isn't 0");
+
+ hvmd_alloc_frag(guest->mdpa);
+
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0) {
+ node2 = prop->d.arc.node;
+ if (strcmp(node2->name->str, "console") == 0) {
+ md_get_prop_val(md, node2, "resource_id",
+ &resource_id);
+ guest->console = consoles[resource_id];
+ consoles[resource_id]->guest = guest;
+ }
+ if (strcmp(node2->name->str, "cpu") == 0) {
+ md_get_prop_val(md, node2, "resource_id",
+ &resource_id);
+ TAILQ_INSERT_TAIL(&guest->cpu_list,
+ cpus[resource_id], link);
+ cpus[resource_id]->guest = guest;
+ }
+ if (strcmp(node2->name->str, "mblock") == 0) {
+ md_get_prop_val(md, node2, "resource_id",
+ &resource_id);
+ TAILQ_INSERT_TAIL(&guest->mblock_list,
+ mblocks[resource_id], link);
+ mblocks[resource_id]->guest = guest;
+ }
+ if (strcmp(node2->name->str, "ldc_endpoint") == 0) {
+ md_get_prop_val(md, node2, "resource_id",
+ &resource_id);
+ TAILQ_INSERT_TAIL(&guest->endpoint_list,
+ ldc_endpoints[resource_id], link);
+ ldc_endpoints[resource_id]->guest = guest;
+ }
+ }
+ }
+
+ TAILQ_FOREACH(endpoint, &guest->endpoint_list, link) {
+ if (endpoint->channel >= guest->endpoint_id)
+ guest->endpoint_id = endpoint->channel + 1;
+ }
+
+ xasprintf(&path, "%s.md", guest->name);
+ guest->md = md_read(path);
+ free(path);
+}
+
+void
+hvmd_init(struct md *md)
+{
+ struct md_node *node;
+ struct md_prop *prop;
+
+ node = md_find_node(md, "frag_space");
+ md_get_prop_val(md, node, "fragsize", &fragsize);
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0)
+ hvmd_init_frag(md, prop->d.arc.node);
+ }
+
+ node = md_find_node(md, "consoles");
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0)
+ hvmd_init_console(md, prop->d.arc.node);
+ }
+
+ node = md_find_node(md, "cpus");
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0)
+ hvmd_init_cpu(md, prop->d.arc.node);
+ }
+
+ node = md_find_node(md, "memory");
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0)
+ hvmd_init_mblock(md, prop->d.arc.node);
+ }
+
+ node = md_find_node(md, "ldc_endpoints");
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0)
+ hvmd_init_endpoint(md, prop->d.arc.node);
+ }
+
+ node = md_find_node(md, "guests");
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0)
+ hvmd_init_guest(md, prop->d.arc.node);
+ }
+
+ hvmd_alloc_frag(-1);
+}
+
+void
+hvmd_finalize_cpu(struct md *md, struct cpu *cpu)
+{
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "cpus");
+ assert(parent);
+
+ node = md_add_node(md, "cpu");
+ md_link_node(md, parent, node);
+ md_add_prop_val(md, node, "pid", cpu->pid);
+ md_add_prop_val(md, node, "vid", cpu->vid);
+ md_add_prop_val(md, node, "gid", cpu->gid);
+ md_add_prop_val(md, node, "partid", cpu->partid);
+ md_add_prop_val(md, node, "resource_id", cpu->resource_id);
+ cpu->hv_node = node;
+}
+
+void
+hvmd_finalize_cpus(struct md *md)
+{
+ struct md_node *parent;
+ struct md_node *node;
+ uint64_t resource_id;
+
+ md_find_delete_node(md, "cpus");
+ md_collect_garbage(md);
+
+ parent = md_find_node(md, "root");
+ assert(parent);
+
+ node = md_add_node(md, "cpus");
+ md_link_node(md, parent, node);
+
+ for (resource_id = 0; resource_id < max_cpus; resource_id++) {
+ if (cpus[resource_id])
+ hvmd_finalize_cpu(md, cpus[resource_id]);
+ }
+}
+
+void
+hvmd_finalize_mblock(struct md *md, struct mblock *mblock)
+{
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "memory");
+ assert(parent);
+
+ node = md_add_node(md, "mblock");
+ md_link_node(md, parent, node);
+ md_add_prop_val(md, node, "membase", mblock->membase);
+ md_add_prop_val(md, node, "memsize", mblock->memsize);
+ md_add_prop_val(md, node, "realbase", mblock->realbase);
+ md_add_prop_val(md, node, "resource_id", mblock->resource_id);
+ mblock->hv_node = node;
+}
+
+void
+hvmd_finalize_memory(struct md *md)
+{
+ struct md_node *parent;
+ struct md_node *node;
+ uint64_t resource_id;
+
+ md_find_delete_node(md, "memory");
+ md_collect_garbage(md);
+
+ parent = md_find_node(md, "root");
+ assert(parent);
+
+ node = md_add_node(md, "memory");
+ md_link_node(md, parent, node);
+
+ for (resource_id = 0; resource_id < max_cpus; resource_id++) {
+ if (mblocks[resource_id])
+ hvmd_finalize_mblock(md, mblocks[resource_id]);
+ }
+}
+
+void
+hvmd_finalize_endpoint(struct md *md, struct ldc_endpoint *endpoint)
+{
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "ldc_endpoints");
+ assert(parent);
+
+ node = md_add_node(md, "ldc_endpoint");
+ md_link_node(md, parent, node);
+ md_add_prop_val(md, node, "resource_id", endpoint->resource_id);
+ md_add_prop_val(md, node, "target_type", endpoint->target_type);
+ md_add_prop_val(md, node, "channel", endpoint->channel);
+ if (endpoint->target_guest != -1)
+ md_add_prop_val(md, node, "target_guest",
+ endpoint->target_guest);
+ md_add_prop_val(md, node, "target_channel", endpoint->target_channel);
+ if (endpoint->tx_ino != -1)
+ md_add_prop_val(md, node, "tx-ino", endpoint->tx_ino);
+ if (endpoint->rx_ino != -1)
+ md_add_prop_val(md, node, "rx-ino", endpoint->rx_ino);
+ if (endpoint->private_svc != -1)
+ md_add_prop_val(md, node, "private_svc",
+ endpoint->private_svc);
+ if (endpoint->svc_id != -1)
+ md_add_prop_val(md, node, "svc_id", endpoint->svc_id);
+ endpoint->hv_node = node;
+}
+
+void
+hvmd_finalize_endpoints(struct md *md)
+{
+ struct md_node *parent;
+ struct md_node *node;
+ uint64_t resource_id;
+
+ md_find_delete_node(md, "ldc_endpoints");
+ md_collect_garbage(md);
+
+ parent = md_find_node(md, "root");
+ assert(parent);
+
+ node = md_add_node(md, "ldc_endpoints");
+ md_link_node(md, parent, node);
+
+ for (resource_id = 0; resource_id < max_guest_ldcs; resource_id++) {
+ if (ldc_endpoints[resource_id])
+ hvmd_finalize_endpoint(md, ldc_endpoints[resource_id]);
+ }
+}
+
+void
+hvmd_finalize_console(struct md *md, struct console *console)
+{
+ struct md_node *parent;
+ struct md_node *node;
+ struct ldc_endpoint *endpoint;
+
+ parent = md_find_node(md, "consoles");
+ assert(parent);
+
+ node = md_add_node(md, "console");
+ md_link_node(md, parent, node);
+ md_add_prop_val(md, node, "resource_id", console->resource_id);
+ md_add_prop_val(md, node, "ino", console->ino);
+ console->hv_node = node;
+
+ TAILQ_FOREACH(endpoint, &console->guest->endpoint_list, link) {
+ if (endpoint->rx_ino == console->ino) {
+ md_link_node(md, node, endpoint->hv_node);
+ break;
+ }
+ }
+}
+
+void
+hvmd_finalize_consoles(struct md *md)
+{
+ struct md_node *parent;
+ struct md_node *node;
+ uint64_t resource_id;
+
+ md_find_delete_node(md, "consoles");
+ md_collect_garbage(md);
+
+ parent = md_find_node(md, "root");
+ assert(parent);
+
+ node = md_add_node(md, "consoles");
+ md_link_node(md, parent, node);
+
+ for (resource_id = 0; resource_id < max_guests; resource_id++) {
+ if (consoles[resource_id])
+ hvmd_finalize_console(md, consoles[resource_id]);
+ }
+}
+
+void
+hvmd_finalize_guest(struct md *md, struct guest *guest)
+{
+ struct md_node *node = guest->hv_node;
+ struct md_node *node2;
+ struct md_prop *prop, *tmp;
+ struct cpu *cpu;
+ struct mblock *mblock;
+ struct ldc_endpoint *endpoint;
+
+ TAILQ_FOREACH_SAFE(prop, &node->prop_list, link, tmp) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0) {
+ node2 = prop->d.arc.node;
+ if (strcmp(node2->name->str, "console") == 0 ||
+ strcmp(node2->name->str, "cpu") == 0 ||
+ strcmp(node2->name->str, "mblock") == 0 ||
+ strcmp(node2->name->str, "ldc_endpoint") == 0)
+ md_delete_prop(md, node, prop);
+ }
+ }
+ md_collect_garbage(md);
+
+ if (guest->console)
+ md_link_node(md, guest->hv_node, guest->console->hv_node);
+ TAILQ_FOREACH(cpu, &guest->cpu_list, link)
+ md_link_node(md, guest->hv_node, cpu->hv_node);
+ TAILQ_FOREACH(mblock, &guest->mblock_list, link)
+ md_link_node(md, guest->hv_node, mblock->hv_node);
+ TAILQ_FOREACH(endpoint, &guest->endpoint_list, link)
+ md_link_node(md, guest->hv_node, endpoint->hv_node);
+}
+
+void
+hvmd_finalize_guests(struct md *md)
+{
+ uint64_t resource_id;
+
+ for (resource_id = 0; resource_id < max_guests; resource_id++) {
+ if (guests[resource_id])
+ hvmd_finalize_guest(md, guests[resource_id]);
+ }
+}
+
+void
+hvmd_finalize(struct md *md)
+{
+ struct md_node *parent;
+ struct md_node *node;
+
+ md_find_delete_node(md, "maus");
+ md_collect_garbage(md);
+
+ parent = md_find_node(md, "root");
+ assert(parent);
+
+ node = md_add_node(md, "maus");
+ md_link_node(md, parent, node);
+
+ hvmd_finalize_cpus(md);
+ hvmd_finalize_memory(md);
+ hvmd_finalize_endpoints(md);
+ hvmd_finalize_consoles(md);
+ hvmd_finalize_guests(md);
+}
+
+struct ldc_endpoint *
+hvmd_add_endpoint(struct md *md, struct guest *guest)
+{
+ struct ldc_endpoint *endpoint;
+ uint64_t resource_id;
+
+ for (resource_id = 0; resource_id < max_guest_ldcs; resource_id++)
+ if (ldc_endpoints[resource_id] == NULL)
+ break;
+ assert(resource_id < max_guest_ldcs);
+
+ endpoint = xzalloc(sizeof(*endpoint));
+ endpoint->target_guest = -1;
+ endpoint->tx_ino = -1;
+ endpoint->rx_ino = -1;
+ endpoint->private_svc = -1;
+ endpoint->svc_id = -1;
+ endpoint->resource_id = resource_id;
+ ldc_endpoints[resource_id] = endpoint;
+
+ TAILQ_INSERT_TAIL(&guest->endpoint_list, endpoint, link);
+ endpoint->guest = guest;
+
+ return endpoint;
+}
+
+struct console *
+hvmd_add_console(struct md *md, struct guest *guest)
+{
+ struct guest *primary;
+ struct console *console;
+ uint64_t resource_id;
+ uint64_t client_channel, server_channel;
+
+ primary = guest_lookup("primary");
+ client_channel = guest->endpoint_id++;
+ server_channel = primary->endpoint_id++;
+
+ for (resource_id = 0; resource_id < max_guests; resource_id++)
+ if (consoles[resource_id] == NULL)
+ break;
+ assert(resource_id < max_guests);
+
+ console = xzalloc(sizeof(*console));
+ console->ino = 0x11;
+ console->resource_id = resource_id;
+ consoles[resource_id] = console;
+
+ console->client_endpoint = hvmd_add_endpoint(md, guest);
+ console->client_endpoint->tx_ino = 0x11;
+ console->client_endpoint->rx_ino = 0x11;
+ console->client_endpoint->target_type = LDC_GUEST;
+ console->client_endpoint->target_guest = primary->gid;
+ console->client_endpoint->target_channel = server_channel;
+ console->client_endpoint->channel = client_channel;
+ console->client_endpoint->private_svc = LDC_CONSOLE_SVC;
+
+ console->server_endpoint = hvmd_add_endpoint(md, primary);
+ console->server_endpoint->tx_ino = 2 * server_channel;
+ console->server_endpoint->rx_ino = 2 * server_channel + 1;
+ console->server_endpoint->target_type = LDC_GUEST;
+ console->server_endpoint->target_guest = guest->gid;
+ console->server_endpoint->channel = server_channel;
+ console->server_endpoint->target_channel = client_channel;
+
+ guest->console = console;
+ console->guest = guest;
+
+ return console;
+}
+
+void
+hvmd_add_domain_services(struct md *md, struct guest *guest)
+{
+ struct guest *primary;
+ struct ldc_channel *ds = &guest->domain_services;
+ uint64_t client_channel, server_channel;
+
+ primary = guest_lookup("primary");
+ client_channel = guest->endpoint_id++;
+ server_channel = primary->endpoint_id++;
+
+ ds->client_endpoint = hvmd_add_endpoint(md, guest);
+ ds->client_endpoint->tx_ino = 2 * client_channel;
+ ds->client_endpoint->rx_ino = 2 * client_channel + 1;
+ ds->client_endpoint->target_type = LDC_GUEST;
+ ds->client_endpoint->target_guest = primary->gid;
+ ds->client_endpoint->target_channel = server_channel;
+ ds->client_endpoint->channel = client_channel;
+
+ ds->server_endpoint = hvmd_add_endpoint(md, primary);
+ ds->server_endpoint->tx_ino = 2 * server_channel;
+ ds->server_endpoint->rx_ino = 2 * server_channel + 1;
+ ds->server_endpoint->target_type = LDC_GUEST;
+ ds->server_endpoint->target_guest = guest->gid;
+ ds->server_endpoint->channel = server_channel;
+ ds->server_endpoint->target_channel = client_channel;
+}
+
+struct ldc_channel *
+hvmd_add_vio(struct md *md, struct guest *guest)
+{
+ struct guest *primary;
+ struct ldc_channel *lc = &guest->vio[guest->num_vios++];
+ uint64_t client_channel, server_channel;
+
+ primary = guest_lookup("primary");
+ client_channel = guest->endpoint_id++;
+ server_channel = primary->endpoint_id++;
+
+ lc->client_endpoint = hvmd_add_endpoint(md, guest);
+ lc->client_endpoint->tx_ino = 2 * client_channel;
+ lc->client_endpoint->rx_ino = 2 * client_channel + 1;
+ lc->client_endpoint->target_type = LDC_GUEST;
+ lc->client_endpoint->target_guest = primary->gid;
+ lc->client_endpoint->target_channel = server_channel;
+ lc->client_endpoint->channel = client_channel;
+
+ lc->server_endpoint = hvmd_add_endpoint(md, primary);
+ lc->server_endpoint->tx_ino = 2 * server_channel;
+ lc->server_endpoint->rx_ino = 2 * server_channel + 1;
+ lc->server_endpoint->target_type = LDC_GUEST;
+ lc->server_endpoint->target_guest = guest->gid;
+ lc->server_endpoint->channel = server_channel;
+ lc->server_endpoint->target_channel = client_channel;
+
+ return lc;
+}
+
+struct guest *
+hvmd_add_guest(struct md *md, const char *name)
+{
+ struct guest *guest;
+ struct md_node *parent;
+ struct md_node *node;
+ uint64_t resource_id;
+
+ for (resource_id = 0; resource_id < max_guests; resource_id++)
+ if (guests[resource_id] == NULL)
+ break;
+ assert(resource_id < max_guests);
+
+ guest = xzalloc(sizeof(*guest));
+ TAILQ_INIT(&guest->cpu_list);
+ TAILQ_INIT(&guest->mblock_list);
+ TAILQ_INIT(&guest->endpoint_list);
+ guests[resource_id] = guest;
+ guest->name = name;
+ guest->gid = resource_id;
+ guest->pid = resource_id + 1;
+ guest->resource_id = resource_id;
+ guest->mdpa = hvmd_alloc_frag(-1);
+
+ parent = md_find_node(md, "guests");
+ assert(parent);
+
+ node = md_add_node(md, "guest");
+ md_link_node(md, parent, node);
+ md_add_prop_str(md, node, "name", name);
+ md_add_prop_val(md, node, "gid", guest->gid);
+ md_add_prop_val(md, node, "pid", guest->pid);
+ md_add_prop_val(md, node, "resource_id", resource_id);
+ md_add_prop_val(md, node, "tod-offset", 0);
+ md_add_prop_val(md, node, "rombase", rombase);
+ md_add_prop_val(md, node, "romsize", romsize);
+ md_add_prop_val(md, node, "perfctraccess", 0);
+ md_add_prop_val(md, node, "reset-reason", 0);
+ md_add_prop_val(md, node, "diagpriv", 0);
+ md_add_prop_val(md, node, "mdpa", guest->mdpa);
+ guest->hv_node = node;
+
+ node = md_add_node(md, "virtual_devices");
+ md_link_node(md, guest->hv_node, node);
+ md_add_prop_val(md, node, "cfghandle", 0x100);
+
+ node = md_add_node(md, "channel_devices");
+ md_link_node(md, guest->hv_node, node);
+ md_add_prop_val(md, node, "cfghandle", 0x200);
+
+ hvmd_add_console(md, guest);
+ hvmd_add_domain_services(md, guest);
+
+ return guest;
+}
+
+struct md_node *
+guest_add_channel_endpoints(struct guest *guest)
+{
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "root");
+ assert(parent);
+
+ node = md_add_node(md, "channel-endpoints");
+ md_link_node(md, parent, node);
+
+ return node;
+}
+
+struct md_node *
+guest_add_endpoint(struct guest *guest, uint64_t id)
+{
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "channel-endpoints");
+ if (parent == NULL)
+ parent = guest_add_channel_endpoints(guest);
+
+ node = md_add_node(md, "channel-endpoint");
+ md_link_node(md, parent, node);
+ md_add_prop_val(md, node, "id", id);
+ md_add_prop_val(md, node, "tx-ino", 2 * id);
+ md_add_prop_val(md, node, "rx-ino", 2 * id + 1);
+
+ return node;
+}
+
+struct md_node *
+guest_add_vcc(struct guest *guest)
+{
+ const char compatible[] = "SUNW,sun4v-virtual-console-concentrator";
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "channel-devices");
+ assert(parent != NULL);
+
+ node = md_add_node(md, "virtual-device");
+ md_link_node(md, parent, node);
+ md_add_prop_str(md, node, "name", "virtual-console-concentrator");
+ md_add_prop_data(md, node, "compatible", compatible,
+ sizeof(compatible));
+ md_add_prop_str(md, node, "device_type", "vcc");
+ md_add_prop_val(md, node, "cfg-handle", 0x0);
+ md_add_prop_str(md, node, "svc-name", "primary-vcc0");
+
+ return node;
+}
+
+struct md_node *
+guest_find_vcc(struct guest *guest)
+{
+ struct md *md = guest->md;
+ struct md_node *node, *node2;
+ struct md_prop *prop;
+ const char *name;
+
+ node = md_find_node(md, "channel-devices");
+ assert(node != NULL);
+
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0) {
+ node2 = prop->d.arc.node;
+ if (!md_get_prop_str(md, node2, "name", &name))
+ continue;
+ if (strcmp(name, "virtual-console-concentrator") == 0)
+ return node2;
+ }
+ }
+
+ return NULL;
+}
+
+struct md_node *
+guest_add_vcc_port(struct guest *guest, struct md_node *vcc,
+ const char *domain, uint64_t id, uint64_t channel)
+{
+ struct md *md = guest->md;
+ struct md_node *node;
+ struct md_node *child;
+
+ if (vcc == NULL)
+ vcc = guest_find_vcc(guest);
+ if (vcc == NULL)
+ vcc = guest_add_vcc(guest);
+
+ node = md_add_node(md, "virtual-device-port");
+ md_link_node(md, vcc, node);
+ md_add_prop_str(md, node, "name", "vcc-port");
+ md_add_prop_val(md, node, "id", id);
+ md_add_prop_str(md, node, "vcc-domain-name", domain);
+ md_add_prop_str(md, node, "vcc-group-name", domain);
+ /* OpenBSD doesn't care about this, but Solaris might. */
+ md_add_prop_val(md, node, "vcc-tcp-port", 5000 + id);
+
+ child = guest_add_endpoint(guest, channel);
+ md_link_node(md, node, child);
+
+ return node;
+}
+
+struct md_node *
+guest_add_vds(struct guest *guest)
+{
+ const char compatible[] = "SUNW,sun4v-disk-server";
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "channel-devices");
+ assert(parent != NULL);
+
+ node = md_add_node(md, "virtual-device");
+ md_link_node(md, parent, node);
+ md_add_prop_str(md, node, "name", "virtual-disk-server");
+ md_add_prop_data(md, node, "compatible", compatible,
+ sizeof(compatible));
+ md_add_prop_str(md, node, "device_type", "vds");
+ md_add_prop_val(md, node, "cfg-handle", 0x0);
+ md_add_prop_str(md, node, "svc-name", "primary-vds0");
+
+ return node;
+}
+
+struct md_node *
+guest_find_vds(struct guest *guest)
+{
+ struct md *md = guest->md;
+ struct md_node *node, *node2;
+ struct md_prop *prop;
+ const char *name;
+
+ node = md_find_node(md, "channel-devices");
+ assert(node != NULL);
+
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0) {
+ node2 = prop->d.arc.node;
+ if (!md_get_prop_str(md, node2, "name", &name))
+ continue;
+ if (strcmp(name, "virtual-disk-server") == 0)
+ return node2;
+ }
+ }
+
+ return NULL;
+}
+
+struct md_node *
+guest_add_vds_port(struct guest *guest, struct md_node *vds,
+ const char *path, uint64_t id, uint64_t channel)
+{
+ struct md *md = guest->md;
+ struct md_node *node;
+ struct md_node *child;
+
+ if (vds == NULL)
+ vds = guest_find_vds(guest);
+ if (vds == NULL)
+ vds = guest_add_vds(guest);
+
+ node = md_add_node(md, "virtual-device-port");
+ md_link_node(md, vds, node);
+ md_add_prop_str(md, node, "name", "vds-port");
+ md_add_prop_val(md, node, "id", id);
+ md_add_prop_str(md, node, "vds-block-device", path);
+
+ child = guest_add_endpoint(guest, channel);
+ md_link_node(md, node, child);
+
+ return node;
+}
+
+struct md_node *
+guest_add_vsw(struct guest *guest)
+{
+ const char compatible[] = "SUNW,sun4v-network-switch";
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "channel-devices");
+ assert(parent != NULL);
+
+ node = md_add_node(md, "virtual-device");
+ md_link_node(md, parent, node);
+ md_add_prop_str(md, node, "name", "virtual-network-switch");
+ md_add_prop_data(md, node, "compatible", compatible,
+ sizeof(compatible));
+ md_add_prop_str(md, node, "device_type", "vsw");
+ md_add_prop_val(md, node, "cfg-handle", 0x0);
+ md_add_prop_str(md, node, "svc-name", "primary-vsw0");
+
+ return node;
+}
+
+struct md_node *
+guest_find_vsw(struct guest *guest)
+{
+ struct md *md = guest->md;
+ struct md_node *node, *node2;
+ struct md_prop *prop;
+ const char *name;
+
+ node = md_find_node(md, "channel-devices");
+ assert(node != NULL);
+
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0) {
+ node2 = prop->d.arc.node;
+ if (!md_get_prop_str(md, node2, "name", &name))
+ continue;
+ if (strcmp(name, "virtual-network-switch") == 0)
+ return node2;
+ }
+ }
+
+ return NULL;
+}
+
+struct md_node *
+guest_add_vsw_port(struct guest *guest, struct md_node *vds,
+ uint64_t id, uint64_t channel)
+{
+ struct md *md = guest->md;
+ struct md_node *node;
+ struct md_node *child;
+
+ if (vds == NULL)
+ vds = guest_find_vsw(guest);
+ if (vds == NULL)
+ vds = guest_add_vsw(guest);
+
+ node = md_add_node(md, "virtual-device-port");
+ md_link_node(md, vds, node);
+ md_add_prop_str(md, node, "name", "vsw-port");
+ md_add_prop_val(md, node, "id", id);
+
+ child = guest_add_endpoint(guest, channel);
+ md_link_node(md, node, child);
+
+ return node;
+}
+
+struct md_node *
+guest_add_console_device(struct guest *guest)
+{
+ const char compatible[] = "SUNW,sun4v-console";
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "virtual-devices");
+ assert(parent);
+
+ node = md_add_node(md, "virtual-device");
+ md_link_node(md, parent, node);
+ md_add_prop_str(md, node, "name", "console");
+ md_add_prop_str(md, node, "device-type", "serial");
+ md_add_prop_val(md, node, "intr", 0x1);
+ md_add_prop_val(md, node, "ino", 0x11);
+ md_add_prop_val(md, node, "channel#", 0);
+ md_add_prop_val(md, node, "cfg-handle", 0x1);
+ md_add_prop_data(md, node, "compatible", compatible,
+ sizeof(compatible));
+
+ return node;
+}
+
+struct md_node *
+guest_add_vdc(struct guest *guest, uint64_t cfghandle)
+{
+ const char compatible[] = "SUNW,sun4v-disk";
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "channel-devices");
+ assert(parent);
+
+ node = md_add_node(md, "virtual-device");
+ md_link_node(md, parent, node);
+ md_add_prop_str(md, node, "name", "disk");
+ md_add_prop_str(md, node, "device-type", "block");
+ md_add_prop_val(md, node, "cfg-handle", cfghandle);
+ md_add_prop_data(md, node, "compatible", compatible,
+ sizeof(compatible));
+
+ return node;
+}
+
+struct md_node *
+guest_add_vdc_port(struct guest *guest, struct md_node *vdc,
+ uint64_t cfghandle, uint64_t id, uint64_t channel)
+{
+ struct md *md = guest->md;
+ struct md_node *node;
+ struct md_node *child;
+
+ if (vdc == NULL)
+ vdc = guest_add_vdc(guest, cfghandle);
+
+ node = md_add_node(md, "virtual-device-port");
+ md_link_node(md, vdc, node);
+ md_add_prop_str(md, node, "name", "vdc-port");
+ md_add_prop_val(md, node, "id", id);
+
+ child = guest_add_endpoint(guest, channel);
+ md_link_node(md, node, child);
+
+ return node;
+}
+
+struct md_node *
+guest_add_vnet(struct guest *guest, uint64_t mac_addr, uint64_t mtu,
+ uint64_t cfghandle)
+{
+ const char compatible[] = "SUNW,sun4v-network";
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "channel-devices");
+ assert(parent);
+
+ node = md_add_node(md, "virtual-device");
+ md_link_node(md, parent, node);
+ md_add_prop_str(md, node, "name", "network");
+ md_add_prop_str(md, node, "device-type", "network");
+ md_add_prop_val(md, node, "cfg-handle", cfghandle);
+ md_add_prop_data(md, node, "compatible", compatible,
+ sizeof(compatible));
+ if (mac_addr == -1)
+ mac_addr = 0x00144ff80000 + (arc4random() & 0x3ffff);
+ md_add_prop_val(md, node, "local-mac-address", mac_addr);
+ md_add_prop_val(md, node, "mtu", mtu);
+
+ return node;
+}
+
+struct md_node *
+guest_add_vnet_port(struct guest *guest, struct md_node *vdc,
+ uint64_t mac_addr, uint64_t mtu, uint64_t cfghandle, uint64_t id,
+ uint64_t channel)
+{
+ struct md *md = guest->md;
+ struct md_node *node;
+ struct md_node *child;
+
+ if (vdc == NULL)
+ vdc = guest_add_vnet(guest, mac_addr, mtu, cfghandle);
+
+ node = md_add_node(md, "virtual-device-port");
+ md_link_node(md, vdc, node);
+ md_add_prop_str(md, node, "name", "vnet-port");
+ md_add_prop_val(md, node, "id", id);
+
+ child = guest_add_endpoint(guest, channel);
+ md_link_node(md, node, child);
+
+ return node;
+}
+
+struct md_node *
+guest_add_channel_devices(struct guest *guest)
+{
+ const char compatible[] = "SUNW,sun4v-channel-devices";
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "virtual-devices");
+ assert(parent);
+
+ node = md_add_node(md, "channel-devices");
+ md_link_node(md, parent, node);
+ md_add_prop_str(md, node, "name", "channel-devices");
+ md_add_prop_str(md, node, "device-type", "channel-devices");
+ md_add_prop_data(md, node, "compatible", compatible,
+ sizeof(compatible));
+ md_add_prop_val(md, node, "cfg-handle", 0x200);
+
+ return node;
+}
+
+struct md_node *
+guest_add_domain_services(struct guest *guest)
+{
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ parent = md_find_node(md, "root");
+ assert(parent);
+
+ node = md_add_node(md, "domain-services");
+ md_link_node(md, parent, node);
+
+ return node;
+}
+
+struct md_node *
+guest_add_domain_services_port(struct guest *guest, uint64_t id)
+{
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+ struct md_node *child;
+
+ parent = md_find_node(md, "domain-services");
+ if (parent == NULL)
+ parent = guest_add_domain_services(guest);
+
+ node = md_add_node(md, "domain-services-port");
+ md_link_node(md, parent, node);
+ md_add_prop_val(md, node, "id", id);
+
+ child = guest_add_endpoint(guest,
+ guest->domain_services.client_endpoint->channel);
+ md_link_node(md, node, child);
+
+ return node;
+}
+
+void
+guest_add_devalias(struct guest *guest, const char *name, const char *path)
+{
+ struct md *md = guest->md;
+ struct md_node *parent;
+ struct md_node *node;
+
+ node = md_find_node(md, "devalias");
+ if (node == NULL) {
+ parent = md_find_node(md, "openboot");
+ assert(parent);
+
+ node = md_add_node(md, "devalias");
+ md_link_node(md, parent, node);
+ }
+
+ md_add_prop_str(md, node, name, path);
+}
+
+void
+guest_set_domaining_enabled(struct guest *guest)
+{
+ struct md *md = guest->md;
+ struct md_node *node;
+
+ node = md_find_node(md, "platform");
+ assert(node);
+
+ md_set_prop_val(md, node, "domaining-enabled", 0x1);
+}
+
+void
+guest_set_mac_address(struct guest *guest)
+{
+ struct md *md = guest->md;
+ struct md_node *node;
+ uint64_t mac_address;
+ uint64_t hostid;
+
+ node = md_find_node(md, "platform");
+ assert(node);
+
+ mac_address = 0x00144ff80000 + (arc4random() & 0x3ffff);
+ md_set_prop_val(md, node, "mac-address", mac_address);
+
+ hostid = 0x84000000 | (mac_address & 0x00ffffff);
+ md_set_prop_val(md, node, "hostid", hostid);
+}
+
+struct md_node *
+guest_find_vc(struct guest *guest)
+{
+ struct md *md = guest->md;
+ struct md_node *node, *node2;
+ struct md_node *vc = NULL;
+ struct md_prop *prop;
+ const char *name;
+
+ node = md_find_node(md, "channel-devices");
+ assert(node != NULL);
+
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0) {
+ node2 = prop->d.arc.node;
+ if (!md_get_prop_str(md, node2, "name", &name))
+ continue;
+ if (strcmp(name, "virtual-channel") == 0)
+ vc = node2;
+ }
+ }
+
+ return vc;
+}
+
+struct md_node *
+guest_add_vc_port(struct guest *guest, struct md_node *vc,
+ const char *domain, uint64_t id, uint64_t channel)
+{
+ struct md *md = guest->md;
+ struct md_node *node;
+ struct md_node *child;
+ char *str;
+
+ if (vc == NULL)
+ vc = guest_find_vc(guest);
+ assert(vc);
+
+ node = md_add_node(md, "virtual-device-port");
+ md_link_node(md, vc, node);
+ md_add_prop_str(md, node, "name", "vldc-port");
+ md_add_prop_val(md, node, "id", id);
+ xasprintf(&str, "ldom-%s", domain);
+ md_add_prop_str(md, node, "vldc-svc-name", str);
+ free(str);
+
+ child = guest_add_endpoint(guest, channel);
+ md_link_node(md, node, child);
+
+ return node;
+}
+
+struct guest *
+guest_create(const char *name)
+{
+ struct guest *guest;
+ struct guest *primary;
+ struct md_node *node;
+
+ primary = guest_lookup("primary");
+
+ guest = hvmd_add_guest(hvmd, name);
+ guest->md = md_copy(protomd);
+
+ md_find_delete_node(guest->md, "dimm_configuration");
+ md_find_delete_node(guest->md, "platform_services");
+ md_find_delete_node(guest->md, "phys_io");
+ md_collect_garbage(guest->md);
+
+ guest_set_domaining_enabled(guest);
+ guest_set_mac_address(guest);
+ guest_add_channel_devices(guest);
+ guest_add_domain_services_port(guest, 0);
+ guest_add_console_device(guest);
+ guest_add_devalias(guest, "virtual-console",
+ "/virtual-devices/console@1");
+
+ guest_add_vcc_port(primary, NULL, guest->name, guest->gid - 1,
+ guest->console->server_endpoint->channel);
+
+ guest_add_vc_port(primary, NULL, guest->name, guest->gid + 2,
+ guest->domain_services.server_endpoint->channel);
+
+ node = md_find_node(guest->md, "root");
+ md_add_prop_val(guest->md, node, "reset-reason", 0);
+
+ return guest;
+}
+
+struct guest *
+guest_lookup(const char *name)
+{
+ uint64_t resource_id;
+
+ for (resource_id = 0; resource_id < max_guests; resource_id++) {
+ if (guests[resource_id] &&
+ strcmp(guests[resource_id]->name, name) == 0)
+ return guests[resource_id];
+ }
+
+ return NULL;
+}
+
+void
+guest_delete_cpu(struct guest *guest, uint64_t vid)
+{
+ struct cpu *cpu;
+
+ TAILQ_FOREACH(cpu, &guest->cpu_list, link) {
+ if (cpu->vid == vid) {
+ TAILQ_REMOVE(&guest->cpu_list, cpu, link);
+ cpus[cpu->resource_id] = NULL;
+ pri_free_cpu(cpu);
+ return;
+ }
+ }
+}
+
+void
+guest_add_cpu(struct guest *guest)
+{
+ struct cpu *cpu;
+
+ cpu = pri_alloc_cpu(-1);
+
+ if (cpu->resource_id == -1) {
+ uint64_t resource_id;
+
+ for (resource_id = 0; resource_id < max_cpus; resource_id++)
+ if (cpus[resource_id] == NULL)
+ break;
+ assert(resource_id < max_cpus);
+ cpu->resource_id = resource_id;
+ }
+ cpus[cpu->resource_id] = cpu;
+
+ cpu->vid = guest->cpu_vid++;
+ cpu->gid = guest->gid;
+ cpu->partid = 1;
+
+ TAILQ_INSERT_TAIL(&guest->cpu_list, cpu, link);
+ cpu->guest = guest;
+}
+
+void
+guest_delete_memory(struct guest *guest, uint64_t base, uint64_t size)
+{
+ struct mblock *mblock;
+
+ TAILQ_FOREACH(mblock, &guest->mblock_list, link) {
+ if (base >= mblock->membase &&
+ base < mblock->membase + mblock->memsize) {
+ mblock->memsize = base - mblock->membase;
+ }
+ }
+}
+
+void
+guest_add_memory(struct guest *guest, uint64_t base, uint64_t size)
+{
+ struct mblock *mblock;
+ uint64_t resource_id;
+
+ mblock = xzalloc(sizeof(*mblock));
+ for (resource_id = 0; resource_id < max_cpus; resource_id++)
+ if (mblocks[resource_id] == NULL)
+ break;
+ assert(resource_id < max_mblocks);
+ mblock->resource_id = resource_id;
+ mblocks[resource_id] = mblock;
+
+ mblock->membase = base;
+ mblock->memsize = size;
+ mblock->realbase = base & 0x7fffffff;
+ if (mblock->realbase == 0)
+ mblock->realbase = 0x80000000;
+
+ TAILQ_INSERT_TAIL(&guest->mblock_list, mblock, link);
+ mblock->guest = guest;
+}
+
+void
+guest_add_vdisk(struct guest *guest, uint64_t id, const char *path)
+{
+ struct guest *primary;
+ struct ldc_channel *lc;
+ char *devalias;
+ char *devpath;
+
+ primary = guest_lookup("primary");
+
+ lc = hvmd_add_vio(hvmd, guest);
+ guest_add_vds_port(primary, NULL, path, id,
+ lc->server_endpoint->channel);
+ guest_add_vdc_port(guest, NULL, id, 0, lc->client_endpoint->channel);
+
+ xasprintf(&devalias, "disk%d", id);
+ xasprintf(&devpath,
+ "/virtual-devices@100/channel-devices@200/disk@%d", id);
+ if (id == 0)
+ guest_add_devalias(guest, "disk", devpath);
+ guest_add_devalias(guest, devalias, devpath);
+ free(devalias);
+ free(devpath);
+}
+
+void
+guest_add_vnetwork(struct guest *guest, uint64_t id, uint64_t mac_addr,
+ uint64_t mtu)
+{
+ struct guest *primary;
+ struct ldc_channel *lc;
+ char *devalias;
+ char *devpath;
+
+ primary = guest_lookup("primary");
+
+ lc = hvmd_add_vio(hvmd, guest);
+ guest_add_vsw_port(primary, NULL, id, lc->server_endpoint->channel);
+ guest_add_vnet_port(guest, NULL, mac_addr, mtu, id, 0,
+ lc->client_endpoint->channel);
+
+ xasprintf(&devalias, "net%d", id);
+ xasprintf(&devpath,
+ "/virtual-devices@100/channel-devices@200/network@%d", id);
+ if (id == 0)
+ guest_add_devalias(guest, "net", devpath);
+ guest_add_devalias(guest, devalias, devpath);
+ free(devalias);
+ free(devpath);
+}
+
+struct cpu *
+guest_find_cpu(struct guest *guest, uint64_t pid)
+{
+ struct cpu *cpu;
+
+ TAILQ_FOREACH(cpu, &guest->cpu_list, link)
+ if (cpu->pid == pid)
+ return cpu;
+
+ return NULL;
+}
+
+void
+guest_finalize(struct guest *guest)
+{
+ struct md *md = guest->md;
+ struct md_node *node, *node2;
+ struct md_prop *prop;
+ struct mblock *mblock;
+ struct md_node *parent;
+ struct md_node *child;
+ struct cpu *cpu;
+ uint64_t pid;
+ uint64_t id;
+ char *path;
+
+ node = md_find_node(md, "cpus");
+ TAILQ_FOREACH(prop, &node->prop_list, link) {
+ if (prop->tag == MD_PROP_ARC &&
+ strcmp(prop->name->str, "fwd") == 0) {
+ node2 = prop->d.arc.node;
+ if (!md_get_prop_val(md, node2, "pid", &pid))
+ continue;
+ cpu = guest_find_cpu(guest, pid);
+ if (cpu == NULL) {
+ md_delete_node(md, node2);
+ continue;
+ }
+ md_set_prop_val(md, node2, "id", cpu->vid);
+ }
+ }
+ md_collect_garbage(md);
+
+ node = md_find_node(md, "memory");
+ md_get_prop_val(md, node, "memory-generation-id#", &id);
+ md_delete_node(md, node);
+ md_collect_garbage(md);
+
+ parent = md_find_node(md, "root");
+ assert(parent);
+
+ node = md_add_node(md, "memory");
+ md_add_prop_val(md, node, "memory-generation-id#", id);
+ md_link_node(md, parent, node);
+
+ TAILQ_FOREACH(mblock, &guest->mblock_list, link) {
+ child = md_add_node(md, "mblock");
+ md_add_prop_val(md, child, "base", mblock->realbase);
+ md_add_prop_val(md, child, "size", mblock->memsize);
+ md_link_node(md, node, child);
+ }
+
+ if (strcmp(guest->name, "primary") == 0)
+ return;
+
+ xasprintf(&path, "%s.md", guest->name);
+ md_write(guest->md, path);
+ free(path);
+}
+
+struct guest *
+primary_init(void)
+{
+ struct guest *guest;
+ struct md_node *vcc;
+
+ guest = guest_lookup("primary");
+ assert(guest);
+
+ guest_set_domaining_enabled(guest);
+ vcc = guest_add_vcc(guest);
+
+ return guest;
+}
+
+void
+build_config(const char *filename)
+{
+ struct guest *primary;
+ struct guest *guest;
+ int i;
+
+ struct ldom_config conf;
+ struct domain *domain;
+ struct vdisk *vdisk;
+ struct vnet *vnet;
+ uint64_t membase = 0x40000000;
+
+ SIMPLEQ_INIT(&conf.domain_list);
+ parse_config(filename, &conf);
+
+ pri = md_read("pri");
+ if (pri == NULL)
+ err(1, "unable to get PRI");
+ hvmd = md_read("hv.md");
+ if (hvmd == NULL)
+ err(1, "unable to get Hypervisor MD");
+
+ pri_init(pri);
+ hvmd_init(hvmd);
+
+ primary = primary_init();
+ for (i = 8; i < max_cpus; i++)
+ guest_delete_cpu(primary, i);
+ guest_delete_memory(primary, 0x40000000, 0x1c0000000);
+ SIMPLEQ_FOREACH(domain, &conf.domain_list, entry) {
+ guest = guest_create(domain->name);
+ for (i = 0; i < domain->vcpu; i++)
+ guest_add_cpu(guest);
+ guest_add_memory(guest, membase, domain->memory);
+ membase += domain->memory;
+ i = 0;
+ SIMPLEQ_FOREACH(vdisk, &domain->vdisk_list, entry)
+ guest_add_vdisk(guest, i++, vdisk->path);
+ i = 0;
+ SIMPLEQ_FOREACH(vnet, &domain->vnet_list, entry)
+ guest_add_vnetwork(guest, i++, vnet->mac_addr,
+ vnet->mtu);
+
+ guest_finalize(guest);
+ }
+
+ hvmd_finalize(hvmd);
+ guest_finalize(primary);
+
+ md_write(hvmd, "hv.md");
+ md_write(primary->md, "primary.md");
+}
diff --git a/usr.sbin/ldomctl/ldomctl.c b/usr.sbin/ldomctl/ldomctl.c
index 9c0ef649891..81ae4ad64c5 100644
--- a/usr.sbin/ldomctl/ldomctl.c
+++ b/usr.sbin/ldomctl/ldomctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ldomctl.c,v 1.15 2012/11/05 19:50:54 kettenis Exp $ */
+/* $OpenBSD: ldomctl.c,v 1.16 2012/11/24 11:50:45 kettenis Exp $ */
/*
* Copyright (c) 2012 Mark Kettenis
@@ -41,7 +41,7 @@ struct command {
__dead void usage(void);
-struct guest_head guests;
+struct guest_head guest_list;
uint64_t find_guest(const char *);
@@ -55,6 +55,7 @@ void delete(int argc, char **argv);
void guest_start(int argc, char **argv);
void guest_stop(int argc, char **argv);
void guest_status(int argc, char **argv);
+void init_system(int argc, char **argv);
struct command commands[] = {
{ "download", download },
@@ -65,6 +66,7 @@ struct command commands[] = {
{ "start", guest_start },
{ "stop", guest_stop },
{ "status", guest_status },
+ { "init-system", init_system },
{ NULL, NULL }
};
@@ -133,7 +135,7 @@ main(int argc, char **argv)
hvmd = md_ingest(hvmd_buf, hvmd_len);
node = md_find_node(hvmd, "guests");
- TAILQ_INIT(&guests);
+ TAILQ_INIT(&guest_list);
TAILQ_FOREACH(prop, &node->prop_list, link) {
if (prop->tag == MD_PROP_ARC &&
strcmp(prop->name->str, "fwd") == 0)
@@ -179,7 +181,7 @@ add_guest(struct md_node *node)
}
}
- TAILQ_INSERT_TAIL(&guests, guest, link);
+ TAILQ_INSERT_TAIL(&guest_list, guest, link);
return;
free:
@@ -191,7 +193,7 @@ find_guest(const char *name)
{
struct guest *guest;
- TAILQ_FOREACH(guest, &guests, link) {
+ TAILQ_FOREACH(guest, &guest_list, link) {
if (strcmp(guest->name, name) == 0)
return guest->gid;
}
@@ -237,7 +239,7 @@ dump(int argc, char **argv)
fwrite(pri_buf, pri_len, 1, fp);
fclose(fp);
- TAILQ_FOREACH(guest, &guests, link) {
+ TAILQ_FOREACH(guest, &guest_list, link) {
hv_read(guest->mdpa, &hdr, sizeof(hdr));
md_len = sizeof(hdr) + hdr.node_blk_sz + hdr.name_blk_sz +
hdr.data_blk_sz;
@@ -259,6 +261,15 @@ dump(int argc, char **argv)
}
void
+init_system(int argc, char **argv)
+{
+ if (argc != 2)
+ usage();
+
+ build_config(argv[1]);
+}
+
+void
list(int argc, char **argv)
{
struct ds_conn *dc;
@@ -399,7 +410,7 @@ guest_status(int argc, char **argv)
if (argc == 2)
gid = find_guest(argv[1]);
- TAILQ_FOREACH(guest, &guests, link) {
+ TAILQ_FOREACH(guest, &guest_list, link) {
if (gid != -1 && guest->gid != gid)
continue;
diff --git a/usr.sbin/ldomctl/ldomctl.h b/usr.sbin/ldomctl/ldomctl.h
index 5c52530730a..008c9b3f01a 100644
--- a/usr.sbin/ldomctl/ldomctl.h
+++ b/usr.sbin/ldomctl/ldomctl.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ldomctl.h,v 1.1 2012/11/05 19:50:54 kettenis Exp $ */
+/* $OpenBSD: ldomctl.h,v 1.2 2012/11/24 11:50:45 kettenis Exp $ */
/*
* Copyright (c) 2012 Mark Kettenis
@@ -16,19 +16,117 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+struct guest;
+
+struct console {
+ uint64_t ino;
+ uint64_t resource_id;
+
+ struct guest *guest;
+ struct ldc_endpoint *client_endpoint;
+ struct ldc_endpoint *server_endpoint;
+ struct md_node *hv_node;
+ TAILQ_ENTRY(console) link;
+};
+
+struct cpu {
+ uint64_t pid, vid, gid, partid;
+ uint64_t resource_id;
+
+ struct guest *guest;
+ struct md_node *hv_node;
+ TAILQ_ENTRY(cpu) link;
+};
+
+struct mblock {
+ uint64_t membase;
+ uint64_t memsize;
+ uint64_t realbase;
+ uint64_t resource_id;
+
+ struct guest *guest;
+ struct md_node *hv_node;
+ TAILQ_ENTRY(mblock) link;
+};
+
+struct ldc_endpoint {
+ uint64_t target_type;
+ uint64_t target_guest;
+ uint64_t channel;
+ uint64_t target_channel;
+ uint64_t tx_ino;
+ uint64_t rx_ino;
+ uint64_t resource_id;
+ uint64_t private_svc;
+ uint64_t svc_id;
+
+ struct guest *guest;
+ struct md_node *hv_node;
+ TAILQ_ENTRY(ldc_endpoint) link;
+};
+
+struct ldc_channel {
+ struct ldc_endpoint *client_endpoint;
+ struct ldc_endpoint *server_endpoint;
+};
+
struct guest {
const char *name;
uint64_t gid;
+ uint64_t pid;
+ uint64_t resource_id;
uint64_t mdpa;
+ struct md_node *hv_node;
+
+ struct md *md;
+ uint64_t cpu_vid;
+ uint64_t endpoint_id;
+
+ struct console *console;
+ TAILQ_HEAD(, cpu) cpu_list;
int num_cpus;
+ TAILQ_HEAD(, mblock) mblock_list;
+ TAILQ_HEAD(, ldc_endpoint) endpoint_list;
+
+ struct ldc_channel domain_services;
+ struct ldc_channel vio[8];
+ int num_vios;
TAILQ_ENTRY(guest) link;
};
-extern TAILQ_HEAD(guest_head, guest) guests;
+extern TAILQ_HEAD(guest_head, guest) guest_list;
void add_guest(struct md_node *);
extern struct md *hvmd;
extern uint64_t hv_mdpa;
+
+
+struct vdisk {
+ SIMPLEQ_ENTRY(vdisk) entry;
+ const char *path;
+};
+
+struct vnet {
+ SIMPLEQ_ENTRY(vnet) entry;
+ uint64_t mac_addr;
+ uint64_t mtu;
+};
+
+struct domain {
+ SIMPLEQ_ENTRY(domain) entry;
+ const char *name;
+ uint64_t vcpu;
+ uint64_t memory;
+ SIMPLEQ_HEAD(, vdisk) vdisk_list;
+ SIMPLEQ_HEAD(, vnet) vnet_list;
+} *domain;
+
+struct ldom_config {
+ SIMPLEQ_HEAD(, domain) domain_list;
+};
+
+int parse_config(const char *, struct ldom_config *);
+void build_config(const char *);
diff --git a/usr.sbin/ldomctl/mdstore.c b/usr.sbin/ldomctl/mdstore.c
index 1539cba29ce..6167d86a2b0 100644
--- a/usr.sbin/ldomctl/mdstore.c
+++ b/usr.sbin/ldomctl/mdstore.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mdstore.c,v 1.4 2012/11/05 19:50:54 kettenis Exp $ */
+/* $OpenBSD: mdstore.c,v 1.5 2012/11/24 11:50:45 kettenis Exp $ */
/*
* Copyright (c) 2012 Mark Kettenis
@@ -337,7 +337,7 @@ mdstore_download(struct ds_conn *dc, const char *name)
err(1, "%s", name);
node = md_find_node(hvmd, "guests");
- TAILQ_INIT(&guests);
+ TAILQ_INIT(&guest_list);
TAILQ_FOREACH(prop, &node->prop_list, link) {
if (prop->tag == MD_PROP_ARC &&
strcmp(prop->name->str, "fwd") == 0) {
@@ -350,7 +350,7 @@ mdstore_download(struct ds_conn *dc, const char *name)
hv_mdpa = alloc_frag();
mdstore_begin(dc, dcs->svc_handle, name, nmds);
- TAILQ_FOREACH(guest, &guests, link) {
+ TAILQ_FOREACH(guest, &guest_list, link) {
if (asprintf(&path, "%s/%s.md", name, guest->name) == -1)
err(1, "asprintf");
type = 0;
@@ -412,7 +412,7 @@ add_frag_mblock(struct md_node *node)
}
delete_frag(hv_mdpa);
- TAILQ_FOREACH(guest, &guests, link)
+ TAILQ_FOREACH(guest, &guest_list, link)
delete_frag(guest->mdpa);
}
diff --git a/usr.sbin/ldomctl/parse.y b/usr.sbin/ldomctl/parse.y
new file mode 100644
index 00000000000..bf74fc41168
--- /dev/null
+++ b/usr.sbin/ldomctl/parse.y
@@ -0,0 +1,521 @@
+/* $OpenBSD: parse.y,v 1.1 2012/11/24 11:50:45 kettenis Exp $ */
+
+/*
+ * Copyright (c) 2012 Mark Kettenis <kettenis@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * 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/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ldomctl.h"
+#include "util.h"
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ int lineno;
+ int errors;
+} *file, *topfile;
+struct file *pushfile(const char *);
+int popfile(void);
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...);
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(int);
+int lungetc(int);
+int findeol(void);
+
+struct ldom_config *conf;
+
+struct opts {
+ uint64_t mac_addr;
+ uint64_t mtu;
+} opts;
+void opts_default(void);
+
+typedef struct {
+ union {
+ int64_t number;
+ char *string;
+ struct opts opts;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token DOMAIN
+%token VCPU MEMORY VDISK VNET
+%token MAC_ADDR MTU
+%token ERROR
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.number> memory
+%type <v.opts> vnet_opts vnet_opts_l vnet_opt
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar domain '\n'
+ | grammar error '\n' { file->errors++; }
+ ;
+
+domain : DOMAIN STRING optnl '{' optnl {
+ domain = xzalloc(sizeof(struct domain));
+ domain->name = $2;
+ SIMPLEQ_INIT(&domain->vdisk_list);
+ SIMPLEQ_INIT(&domain->vnet_list);
+ }
+ domainopts_l '}' {
+ SIMPLEQ_INSERT_TAIL(&conf->domain_list, domain, entry);
+ domain = NULL;
+ }
+ ;
+
+domainopts_l : domainopts_l domainoptsl
+ | domainoptsl
+ ;
+
+domainoptsl : domainopts nl
+ ;
+
+domainopts : VCPU NUMBER {
+ domain->vcpu = $2;
+ }
+ | MEMORY memory {
+ domain->memory = $2;
+ }
+ | VDISK STRING {
+ struct vdisk *vdisk = xmalloc(sizeof(struct vdisk));
+ vdisk->path = $2;
+ SIMPLEQ_INSERT_TAIL(&domain->vdisk_list, vdisk, entry);
+ }
+ | VNET vnet_opts {
+ struct vnet *vnet = xmalloc(sizeof(struct vnet));
+ vnet->mac_addr = $2.mac_addr;
+ vnet->mtu = $2.mtu;
+ SIMPLEQ_INSERT_TAIL(&domain->vnet_list, vnet, entry);
+ }
+ ;
+
+vnet_opts : { opts_default(); }
+ vnet_opts_l
+ { $$ = opts; }
+ | { opts_default(); $$ = opts; }
+ ;
+vnet_opts_l : vnet_opts_l vnet_opt
+ | vnet_opt
+ ;
+vnet_opt : mac_addr
+ | mtu
+ ;
+
+mac_addr : MAC_ADDR '=' STRING {
+ struct ether_addr *ea;
+
+ if ((ea = ether_aton($3)) == NULL) {
+ yyerror("invalid address: %s\n", $3);
+ YYERROR;
+ }
+
+ opts.mac_addr =
+ (uint64_t)ea->ether_addr_octet[0] << 40 |
+ (uint64_t)ea->ether_addr_octet[1] << 32 |
+ ea->ether_addr_octet[2] << 24 |
+ ea->ether_addr_octet[3] << 16 |
+ ea->ether_addr_octet[4] << 8 |
+ ea->ether_addr_octet[5];
+ }
+ ;
+
+mtu : MTU '=' NUMBER {
+ opts.mtu = $3;
+ }
+ ;
+
+memory : NUMBER {
+ $$ = $1;
+ }
+ | STRING {
+ uint64_t size;
+ char *cp;
+
+ size = strtoll($1, &cp, 10);
+ if (cp != NULL) {
+ if (strcmp(cp, "K") == 0)
+ size *= 1024;
+ else if (strcmp(cp, "M") == 0)
+ size *= 1024 * 1024;
+ else if (strcmp(cp, "G") == 0)
+ size *= 1024 * 1024 * 1024;
+ else {
+ yyerror("unknown unit %s", cp);
+ YYERROR;
+ }
+ }
+ $$ = size;
+ }
+ ;
+
+optnl : '\n' optnl
+ |
+ ;
+
+nl : '\n' optnl /* one newline or more */
+ ;
+
+%%
+
+void
+opts_default(void)
+{
+ opts.mac_addr = -1;
+ opts.mtu = 1500;
+}
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+
+ file->errors++;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d ", file->name, yylval.lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "domain", DOMAIN},
+ { "mac-addr", MAC_ADDR},
+ { "memory", MEMORY},
+ { "mtu", MTU},
+ { "vcpu", VCPU},
+ { "vdisk", VDISK},
+ { "vnet", VNET}
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return (p->k_val);
+ else
+ return (STRING);
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ if (quotec) {
+ if ((c = getc(file->stream)) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ return (quotec);
+ }
+ return (c);
+ }
+
+ while ((c = getc(file->stream)) == '\\') {
+ next = getc(file->stream);
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ c = getc(file->stream);
+ }
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ if (pushback_index)
+ c = pushback_buffer[--pushback_index];
+ else
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p;
+ int quotec, next, c;
+ int token;
+
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return (0);
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || c == ' ' || c == '\t')
+ c = next;
+ else if (next == '\n') {
+ file->lineno++;
+ continue;
+ } else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = xstrdup(buf);
+ return (STRING);
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return (findeol());
+ }
+ return (NUMBER);
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return (c);
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && x != '<' && x != '>' && \
+ x != '!' && x != '=' && x != '/' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_' || c == '*') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ yylval.v.string = xstrdup(buf);
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+struct file *
+pushfile(const char *name)
+{
+ struct file *nfile;
+
+ nfile = xzalloc(sizeof(struct file));
+ nfile->name = xstrdup(name);
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ warn("%s", nfile->name);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ nfile->lineno = 1;
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return (nfile);
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file);
+ file = prev;
+ return (file ? 0 : EOF);
+}
+
+int
+parse_config(const char *filename, struct ldom_config *xconf)
+{
+ int errors = 0;
+
+ conf = xconf;
+
+ if ((file = pushfile(filename)) == NULL) {
+ return (-1);
+ }
+ topfile = file;
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ return (errors ? -1 : 0);
+}