diff options
-rw-r--r-- | usr.sbin/ldomctl/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/ldomctl/config.c | 1832 | ||||
-rw-r--r-- | usr.sbin/ldomctl/ldomctl.c | 25 | ||||
-rw-r--r-- | usr.sbin/ldomctl/ldomctl.h | 102 | ||||
-rw-r--r-- | usr.sbin/ldomctl/mdstore.c | 8 | ||||
-rw-r--r-- | usr.sbin/ldomctl/parse.y | 521 |
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); +} |