summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2016-11-24 07:58:56 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2016-11-24 07:58:56 +0000
commit81d8dac5286fc0d2981df1a3284c184ae3c6e9d5 (patch)
tree02c8d9ed18b4e832e7e4f28e5f1a183d2be41829
parent318a7884e9d299146de19d369e29bf264cc1180c (diff)
Add support for booting the kernel from the disk image.
This make the kernel/-k argument optional and, if not specified, tries to find the /bsd kernel in the primary hd0a partition of the first disk image itself. It doesn't support hd0a:/etc/boot.conf yet, and it is no BIOS or full boot loader, but it makes booting and handling of VMs a bit easier - booting an external kernel is still supported. The UFS file system code ufs.c is directly from libsa which is also used by the real boot loader. The code compiles with a few signedness warning which will be fixed separately. OK mlarkin@
-rw-r--r--usr.sbin/vmctl/vmctl.c9
-rw-r--r--usr.sbin/vmd/Makefile3
-rw-r--r--usr.sbin/vmd/config.c14
-rw-r--r--usr.sbin/vmd/loadfile.h4
-rw-r--r--usr.sbin/vmd/loadfile_elf.c76
-rw-r--r--usr.sbin/vmd/ufs.c718
-rw-r--r--usr.sbin/vmd/vmboot.c237
-rw-r--r--usr.sbin/vmd/vmboot.h70
-rw-r--r--usr.sbin/vmd/vmd.c18
-rw-r--r--usr.sbin/vmd/vmd.h8
-rw-r--r--usr.sbin/vmd/vmm.c18
11 files changed, 1111 insertions, 64 deletions
diff --git a/usr.sbin/vmctl/vmctl.c b/usr.sbin/vmctl/vmctl.c
index c4861260513..3f69c4b6bcf 100644
--- a/usr.sbin/vmctl/vmctl.c
+++ b/usr.sbin/vmctl/vmctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmctl.c,v 1.17 2016/11/22 11:31:38 edd Exp $ */
+/* $OpenBSD: vmctl.c,v 1.18 2016/11/24 07:58:55 reyk Exp $ */
/*
* Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
@@ -70,12 +70,12 @@ start_vm(const char *name, int memsize, int nnics, int ndisks, char **disks,
if (memsize < 1)
errx(1, "specified memory size too small");
- if (kernel == NULL)
- errx(1, "no kernel specified");
if (ndisks > VMM_MAX_DISKS_PER_VM)
errx(1, "too many disks");
else if (ndisks == 0)
warnx("starting without disks");
+ if (kernel == NULL && ndisks == 0)
+ errx(1, "no kernel or disk specified");
if (nnics == -1)
nnics = 0;
if (nnics == 0)
@@ -103,7 +103,8 @@ start_vm(const char *name, int memsize, int nnics, int ndisks, char **disks,
if (name != NULL)
strlcpy(vcp->vcp_name, name, VMM_MAX_NAME_LEN);
- strlcpy(vcp->vcp_kernel, kernel, VMM_MAX_KERNEL_PATH);
+ if (kernel != NULL)
+ strlcpy(vcp->vcp_kernel, kernel, VMM_MAX_KERNEL_PATH);
vcp->vcp_nnics = nnics;
imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, -1,
diff --git a/usr.sbin/vmd/Makefile b/usr.sbin/vmd/Makefile
index e85c5e98aa7..a4666ba3a07 100644
--- a/usr.sbin/vmd/Makefile
+++ b/usr.sbin/vmd/Makefile
@@ -1,10 +1,11 @@
-# $OpenBSD: Makefile,v 1.10 2016/10/26 05:26:36 mlarkin Exp $
+# $OpenBSD: Makefile,v 1.11 2016/11/24 07:58:55 reyk Exp $
.if ${MACHINE} == "amd64" || ${MACHINE} == "i386"
PROG= vmd
SRCS= vmm.c loadfile_elf.c pci.c virtio.c i8259.c mc146818.c
SRCS+= vmd.c control.c log.c priv.c proc.c config.c ns8250.c i8253.c
+SRCS+= vmboot.c ufs.c
SRCS+= parse.y
CFLAGS+= -Wall -I${.CURDIR}
diff --git a/usr.sbin/vmd/config.c b/usr.sbin/vmd/config.c
index 76f2c8851b7..6f79fa7f212 100644
--- a/usr.sbin/vmd/config.c
+++ b/usr.sbin/vmd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.20 2016/11/22 21:55:54 reyk Exp $ */
+/* $OpenBSD: config.c,v 1.21 2016/11/24 07:58:55 reyk Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -160,8 +160,9 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid)
vm->vm_peerid = peerid;
- /* Open kernel for child */
- if ((kernfd = open(vcp->vcp_kernel, O_RDONLY)) == -1) {
+ /* Open external kernel for child */
+ if (strlen(vcp->vcp_kernel) &&
+ (kernfd = open(vcp->vcp_kernel, O_RDONLY)) == -1) {
log_warn("%s: can't open kernel %s", __func__,
vcp->vcp_kernel);
goto fail;
@@ -305,12 +306,9 @@ config_getvm(struct privsep *ps, struct imsg *imsg)
errno = 0;
if (vm_register(ps, &vmc, &vm, imsg->hdr.peerid) == -1)
- return (-1);
-
- if (imsg->fd == -1) {
- log_debug("invalid kernel fd");
goto fail;
- }
+
+ /* If the fd is -1, the kernel will be searched on the disk */
vm->vm_kernel = imsg->fd;
vm->vm_running = 1;
diff --git a/usr.sbin/vmd/loadfile.h b/usr.sbin/vmd/loadfile.h
index 1010dbb6e41..aff1a9fd052 100644
--- a/usr.sbin/vmd/loadfile.h
+++ b/usr.sbin/vmd/loadfile.h
@@ -1,5 +1,5 @@
/* $NetBSD: loadfile.h,v 1.1 1999/04/28 09:08:50 christos Exp $ */
-/* $OpenBSD: loadfile.h,v 1.5 2016/09/01 16:04:47 stefan Exp $ */
+/* $OpenBSD: loadfile.h,v 1.6 2016/11/24 07:58:55 reyk Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -73,7 +73,7 @@
#define PML2_PAGE 0x13000
#define NPTE_PG (PAGE_SIZE / sizeof(pt_entry_t))
-int loadelf_main(int, struct vm_create_params *,
+int loadelf_main(FILE *, struct vm_create_params *,
struct vcpu_reg_state *);
#include <machine/loadfile_machdep.h>
diff --git a/usr.sbin/vmd/loadfile_elf.c b/usr.sbin/vmd/loadfile_elf.c
index 544795ee832..7ec9b2ae063 100644
--- a/usr.sbin/vmd/loadfile_elf.c
+++ b/usr.sbin/vmd/loadfile_elf.c
@@ -1,5 +1,5 @@
/* $NetBSD: loadfile.c,v 1.10 2000/12/03 02:53:04 tsutsui Exp $ */
-/* $OpenBSD: loadfile_elf.c,v 1.20 2016/10/26 05:26:36 mlarkin Exp $ */
+/* $OpenBSD: loadfile_elf.c,v 1.21 2016/11/24 07:58:55 reyk Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -118,13 +118,13 @@ static void setsegment(struct segment_descriptor *, uint32_t,
static void setsegment(struct mem_segment_descriptor *, uint32_t,
size_t, int, int, int, int);
#endif
-static int elf32_exec(int, Elf32_Ehdr *, u_long *, int);
-static int elf64_exec(int, Elf64_Ehdr *, u_long *, int);
+static int elf32_exec(FILE *, Elf32_Ehdr *, u_long *, int);
+static int elf64_exec(FILE *, Elf64_Ehdr *, u_long *, int);
static size_t create_bios_memmap(struct vm_create_params *, bios_memmap_t *);
static uint32_t push_bootargs(bios_memmap_t *, size_t);
static size_t push_stack(uint32_t, uint32_t);
static void push_gdt(void);
-static size_t mread(int, paddr_t, size_t);
+static size_t mread(FILE *, paddr_t, size_t);
static void marc4random_buf(paddr_t, int);
static void mbzero(paddr_t, int);
static void mbcopy(void *, paddr_t, int);
@@ -271,7 +271,7 @@ push_pt(void)
* various error codes returned from read(2) or loadelf functions
*/
int
-loadelf_main(int fd, struct vm_create_params *vcp, struct vcpu_reg_state *vrs)
+loadelf_main(FILE *fp, struct vm_create_params *vcp, struct vcpu_reg_state *vrs)
{
int r;
uint32_t bootargsz;
@@ -279,16 +279,16 @@ loadelf_main(int fd, struct vm_create_params *vcp, struct vcpu_reg_state *vrs)
u_long marks[MARK_MAX];
bios_memmap_t memmap[VMM_MAX_MEM_RANGES + 1];
- if ((r = read(fd, &hdr, sizeof(hdr))) != sizeof(hdr))
+ if ((r = fread(&hdr, 1, sizeof(hdr), fp)) != sizeof(hdr))
return 1;
memset(&marks, 0, sizeof(marks));
if (memcmp(hdr.elf32.e_ident, ELFMAG, SELFMAG) == 0 &&
hdr.elf32.e_ident[EI_CLASS] == ELFCLASS32) {
- r = elf32_exec(fd, &hdr.elf32, marks, LOAD_ALL);
+ r = elf32_exec(fp, &hdr.elf32, marks, LOAD_ALL);
} else if (memcmp(hdr.elf64.e_ident, ELFMAG, SELFMAG) == 0 &&
hdr.elf64.e_ident[EI_CLASS] == ELFCLASS64) {
- r = elf64_exec(fd, &hdr.elf64, marks, LOAD_ALL);
+ r = elf64_exec(fp, &hdr.elf64, marks, LOAD_ALL);
}
if (r)
@@ -476,9 +476,9 @@ push_stack(uint32_t bootargsz, uint32_t end)
* returns 'sz' if successful, or 0 otherwise.
*/
static size_t
-mread(int fd, paddr_t addr, size_t sz)
+mread(FILE *fp, paddr_t addr, size_t sz)
{
- int ct;
+ size_t ct;
size_t i, rd, osz;
char buf[PAGE_SIZE];
@@ -496,7 +496,7 @@ mread(int fd, paddr_t addr, size_t sz)
else
ct = sz;
- if (read(fd, buf, ct) != ct) {
+ if (fread(buf, 1, ct, fp) != ct) {
log_warn("%s: error %d in mread", __progname, errno);
return (0);
}
@@ -520,7 +520,7 @@ mread(int fd, paddr_t addr, size_t sz)
else
ct = PAGE_SIZE;
- if (read(fd, buf, ct) != ct) {
+ if (fread(buf, 1, ct, fp) != ct) {
log_warn("%s: error %d in mread", __progname, errno);
return (0);
}
@@ -668,13 +668,13 @@ mbcopy(void *src, paddr_t dst, int sz)
* 1 if unsuccessful
*/
static int
-elf64_exec(int fd, Elf64_Ehdr *elf, u_long *marks, int flags)
+elf64_exec(FILE *fp, Elf64_Ehdr *elf, u_long *marks, int flags)
{
Elf64_Shdr *shp;
Elf64_Phdr *phdr;
Elf64_Off off;
int i;
- ssize_t sz;
+ size_t sz;
int first;
int havesyms, havelines;
paddr_t minp = ~0, maxp = 0, pos = 0;
@@ -683,12 +683,12 @@ elf64_exec(int fd, Elf64_Ehdr *elf, u_long *marks, int flags)
sz = elf->e_phnum * sizeof(Elf64_Phdr);
phdr = malloc(sz);
- if (lseek(fd, (off_t)elf->e_phoff, SEEK_SET) == -1) {
+ if (fseeko(fp, (off_t)elf->e_phoff, SEEK_SET) == -1) {
free(phdr);
return 1;
}
- if (read(fd, phdr, sz) != sz) {
+ if (fread(phdr, 1, sz, fp) != sz) {
free(phdr);
return 1;
}
@@ -728,12 +728,12 @@ elf64_exec(int fd, Elf64_Ehdr *elf, u_long *marks, int flags)
(IS_DATA(phdr[i]) && (flags & LOAD_DATA))) {
/* Read in segment. */
- if (lseek(fd, (off_t)phdr[i].p_offset,
+ if (fseeko(fp, (off_t)phdr[i].p_offset,
SEEK_SET) == -1) {
free(phdr);
return 1;
}
- if (mread(fd, phdr[i].p_paddr, phdr[i].p_filesz) !=
+ if (mread(fp, phdr[i].p_paddr, phdr[i].p_filesz) !=
phdr[i].p_filesz) {
free(phdr);
return 1;
@@ -773,14 +773,14 @@ elf64_exec(int fd, Elf64_Ehdr *elf, u_long *marks, int flags)
maxp += sizeof(Elf64_Ehdr);
if (flags & (LOAD_SYM | COUNT_SYM)) {
- if (lseek(fd, (off_t)elf->e_shoff, SEEK_SET) == -1) {
+ if (fseeko(fp, (off_t)elf->e_shoff, SEEK_SET) == -1) {
WARN(("lseek section headers"));
return 1;
}
sz = elf->e_shnum * sizeof(Elf64_Shdr);
shp = malloc(sz);
- if (read(fd, shp, sz) != sz) {
+ if (fread(shp, 1, sz, fp) != sz) {
free(shp);
return 1;
}
@@ -788,15 +788,15 @@ elf64_exec(int fd, Elf64_Ehdr *elf, u_long *marks, int flags)
shpp = maxp;
maxp += roundup(sz, sizeof(Elf64_Addr));
- ssize_t shstrsz = shp[elf->e_shstrndx].sh_size;
+ size_t shstrsz = shp[elf->e_shstrndx].sh_size;
char *shstr = malloc(shstrsz);
- if (lseek(fd, (off_t)shp[elf->e_shstrndx].sh_offset,
+ if (fseeko(fp, (off_t)shp[elf->e_shstrndx].sh_offset,
SEEK_SET) == -1) {
free(shstr);
free(shp);
return 1;
}
- if (read(fd, shstr, shstrsz) != shstrsz) {
+ if (fread(shstr, 1, shstrsz, fp) != shstrsz) {
free(shstr);
free(shp);
return 1;
@@ -819,13 +819,13 @@ elf64_exec(int fd, Elf64_Ehdr *elf, u_long *marks, int flags)
!strcmp(shstr + shp[i].sh_name, ".debug_line") ||
!strcmp(shstr + shp[i].sh_name, ELF_CTF)) {
if (havesyms && (flags & LOAD_SYM)) {
- if (lseek(fd, (off_t)shp[i].sh_offset,
+ if (fseeko(fp, (off_t)shp[i].sh_offset,
SEEK_SET) == -1) {
free(shstr);
free(shp);
return 1;
}
- if (mread(fd, maxp,
+ if (mread(fp, maxp,
shp[i].sh_size) != shp[i].sh_size) {
free(shstr);
free(shp);
@@ -890,13 +890,13 @@ elf64_exec(int fd, Elf64_Ehdr *elf, u_long *marks, int flags)
* 1 if unsuccessful
*/
static int
-elf32_exec(int fd, Elf32_Ehdr *elf, u_long *marks, int flags)
+elf32_exec(FILE *fp, Elf32_Ehdr *elf, u_long *marks, int flags)
{
Elf32_Shdr *shp;
Elf32_Phdr *phdr;
Elf32_Off off;
int i;
- ssize_t sz;
+ size_t sz;
int first;
int havesyms, havelines;
paddr_t minp = ~0, maxp = 0, pos = 0;
@@ -905,12 +905,12 @@ elf32_exec(int fd, Elf32_Ehdr *elf, u_long *marks, int flags)
sz = elf->e_phnum * sizeof(Elf32_Phdr);
phdr = malloc(sz);
- if (lseek(fd, (off_t)elf->e_phoff, SEEK_SET) == -1) {
+ if (fseeko(fp, (off_t)elf->e_phoff, SEEK_SET) == -1) {
free(phdr);
return 1;
}
- if (read(fd, phdr, sz) != sz) {
+ if (fread(phdr, 1, sz, fp) != sz) {
free(phdr);
return 1;
}
@@ -950,12 +950,12 @@ elf32_exec(int fd, Elf32_Ehdr *elf, u_long *marks, int flags)
(IS_DATA(phdr[i]) && (flags & LOAD_DATA))) {
/* Read in segment. */
- if (lseek(fd, (off_t)phdr[i].p_offset,
+ if (fseeko(fp, (off_t)phdr[i].p_offset,
SEEK_SET) == -1) {
free(phdr);
return 1;
}
- if (mread(fd, phdr[i].p_paddr, phdr[i].p_filesz) !=
+ if (mread(fp, phdr[i].p_paddr, phdr[i].p_filesz) !=
phdr[i].p_filesz) {
free(phdr);
return 1;
@@ -995,14 +995,14 @@ elf32_exec(int fd, Elf32_Ehdr *elf, u_long *marks, int flags)
maxp += sizeof(Elf32_Ehdr);
if (flags & (LOAD_SYM | COUNT_SYM)) {
- if (lseek(fd, (off_t)elf->e_shoff, SEEK_SET) == -1) {
+ if (fseeko(fp, (off_t)elf->e_shoff, SEEK_SET) == -1) {
WARN(("lseek section headers"));
return 1;
}
sz = elf->e_shnum * sizeof(Elf32_Shdr);
shp = malloc(sz);
- if (read(fd, shp, sz) != sz) {
+ if (fread(shp, 1, sz, fp) != sz) {
free(shp);
return 1;
}
@@ -1010,15 +1010,15 @@ elf32_exec(int fd, Elf32_Ehdr *elf, u_long *marks, int flags)
shpp = maxp;
maxp += roundup(sz, sizeof(Elf32_Addr));
- ssize_t shstrsz = shp[elf->e_shstrndx].sh_size;
+ size_t shstrsz = shp[elf->e_shstrndx].sh_size;
char *shstr = malloc(shstrsz);
- if (lseek(fd, (off_t)shp[elf->e_shstrndx].sh_offset,
+ if (fseeko(fp, (off_t)shp[elf->e_shstrndx].sh_offset,
SEEK_SET) == -1) {
free(shstr);
free(shp);
return 1;
}
- if (read(fd, shstr, shstrsz) != shstrsz) {
+ if (fread(shstr, 1, shstrsz, fp) != shstrsz) {
free(shstr);
free(shp);
return 1;
@@ -1040,13 +1040,13 @@ elf32_exec(int fd, Elf32_Ehdr *elf, u_long *marks, int flags)
shp[i].sh_type == SHT_STRTAB ||
!strcmp(shstr + shp[i].sh_name, ".debug_line")) {
if (havesyms && (flags & LOAD_SYM)) {
- if (lseek(fd, (off_t)shp[i].sh_offset,
+ if (fseeko(fp, (off_t)shp[i].sh_offset,
SEEK_SET) == -1) {
free(shstr);
free(shp);
return 1;
}
- if (mread(fd, maxp,
+ if (mread(fp, maxp,
shp[i].sh_size) != shp[i].sh_size) {
free(shstr);
free(shp);
diff --git a/usr.sbin/vmd/ufs.c b/usr.sbin/vmd/ufs.c
new file mode 100644
index 00000000000..ce55a288ac8
--- /dev/null
+++ b/usr.sbin/vmd/ufs.c
@@ -0,0 +1,718 @@
+/* $OpenBSD: ufs.c,v 1.1 2016/11/24 07:58:55 reyk Exp $ */
+/* $NetBSD: ufs.c,v 1.16 1996/09/30 16:01:22 ws Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright (c) 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: David Golub
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * Stand-alone file reading package.
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+
+#include "vmboot.h"
+
+/* Used in the kernel by libsa */
+#define alloc malloc
+#define free(_p, _n) free(_p)
+#define twiddle() do { } while(0)
+
+/*
+ * In-core open file.
+ */
+struct file {
+ off_t f_seekp; /* seek pointer */
+ struct fs *f_fs; /* pointer to super-block */
+ struct ufs1_dinode f_di; /* copy of on-disk inode */
+ int f_nindir[NIADDR];
+ /* number of blocks mapped by
+ indirect block at level i */
+ char *f_blk[NIADDR]; /* buffer for indirect block at
+ level i */
+ size_t f_blksize[NIADDR];
+ /* size of buffer */
+ daddr32_t f_blkno[NIADDR];/* disk address of block in buffer */
+ char *f_buf; /* buffer for data block */
+ size_t f_buf_size; /* size of data block */
+ daddr32_t f_buf_blkno; /* block number of data block */
+};
+
+static int read_inode(ufsino_t, struct open_file *);
+static int block_map(struct open_file *, daddr32_t, daddr32_t *);
+static int buf_read_file(struct open_file *, char **, size_t *);
+static int search_directory(char *, struct open_file *, ufsino_t *);
+static int ufs_close_internal(struct file *);
+#ifdef COMPAT_UFS
+static void ffs_oldfscompat(struct fs *);
+#endif
+
+/*
+ * Read a new inode into a file structure.
+ */
+static int
+read_inode(ufsino_t inumber, struct open_file *f)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct fs *fs = fp->f_fs;
+ char *buf;
+ size_t rsize;
+ int rc;
+
+ /*
+ * Read inode and save it.
+ */
+ buf = alloc(fs->fs_bsize);
+ twiddle();
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fs, (daddr32_t)ino_to_fsba(fs, inumber)), fs->fs_bsize,
+ buf, &rsize);
+ if (rc)
+ goto out;
+ if (rsize != (size_t)fs->fs_bsize) {
+ rc = EIO;
+ goto out;
+ }
+
+ {
+ struct ufs1_dinode *dp;
+
+ dp = (struct ufs1_dinode *)buf;
+ fp->f_di = dp[ino_to_fsbo(fs, inumber)];
+ }
+
+ /*
+ * Clear out the old buffers
+ */
+ {
+ int level;
+
+ for (level = 0; level < NIADDR; level++)
+ fp->f_blkno[level] = -1;
+ fp->f_buf_blkno = -1;
+ fp->f_seekp = 0;
+ }
+out:
+ free(buf, fs->fs_bsize);
+ return (rc);
+}
+
+/*
+ * Given an offset in a file, find the disk block number that
+ * contains that block.
+ */
+static int
+block_map(struct open_file *f, daddr32_t file_block, daddr32_t *disk_block_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ daddr32_t ind_block_num, *ind_p;
+ struct fs *fs = fp->f_fs;
+ int level, idx, rc;
+
+ /*
+ * Index structure of an inode:
+ *
+ * di_db[0..NDADDR-1] hold block numbers for blocks
+ * 0..NDADDR-1
+ *
+ * di_ib[0] index block 0 is the single indirect block
+ * holds block numbers for blocks
+ * NDADDR .. NDADDR + NINDIR(fs)-1
+ *
+ * di_ib[1] index block 1 is the double indirect block
+ * holds block numbers for INDEX blocks for blocks
+ * NDADDR + NINDIR(fs) ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
+ *
+ * di_ib[2] index block 2 is the triple indirect block
+ * holds block numbers for double-indirect
+ * blocks for blocks
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2
+ * + NINDIR(fs)**3 - 1
+ */
+
+ if (file_block < NDADDR) {
+ /* Direct block. */
+ *disk_block_p = fp->f_di.di_db[file_block];
+ return (0);
+ }
+
+ file_block -= NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < NIADDR; level++) {
+ if (file_block < fp->f_nindir[level])
+ break;
+ file_block -= fp->f_nindir[level];
+ }
+ if (level == NIADDR) {
+ /* Block number too high */
+ return (EFBIG);
+ }
+
+ ind_block_num = fp->f_di.di_ib[level];
+
+ for (; level >= 0; level--) {
+ if (ind_block_num == 0) {
+ *disk_block_p = 0; /* missing */
+ return (0);
+ }
+
+ if (fp->f_blkno[level] != ind_block_num) {
+ if (fp->f_blk[level] == NULL)
+ fp->f_blk[level] =
+ alloc(fs->fs_bsize);
+ twiddle();
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fp->f_fs, ind_block_num), fs->fs_bsize,
+ fp->f_blk[level], &fp->f_blksize[level]);
+ if (rc)
+ return (rc);
+ if (fp->f_blksize[level] != (size_t)fs->fs_bsize)
+ return (EIO);
+ fp->f_blkno[level] = ind_block_num;
+ }
+
+ ind_p = (daddr32_t *)fp->f_blk[level];
+
+ if (level > 0) {
+ idx = file_block / fp->f_nindir[level - 1];
+ file_block %= fp->f_nindir[level - 1];
+ } else
+ idx = file_block;
+
+ ind_block_num = ind_p[idx];
+ }
+
+ *disk_block_p = ind_block_num;
+ return (0);
+}
+
+/*
+ * Read a portion of a file into an internal buffer. Return
+ * the location in the buffer and the amount in the buffer.
+ */
+static int
+buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct fs *fs = fp->f_fs;
+ daddr32_t file_block, disk_block;
+ size_t block_size;
+ long off;
+ int rc;
+
+ off = blkoff(fs, fp->f_seekp);
+ file_block = lblkno(fs, fp->f_seekp);
+ block_size = dblksize(fs, &fp->f_di, file_block);
+
+ if (file_block != fp->f_buf_blkno) {
+ rc = block_map(f, file_block, &disk_block);
+ if (rc)
+ return (rc);
+
+ if (fp->f_buf == NULL)
+ fp->f_buf = alloc(fs->fs_bsize);
+
+ if (disk_block == 0) {
+ bzero(fp->f_buf, block_size);
+ fp->f_buf_size = block_size;
+ } else {
+ twiddle();
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fs, disk_block),
+ block_size, fp->f_buf, &fp->f_buf_size);
+ if (rc)
+ return (rc);
+ }
+
+ fp->f_buf_blkno = file_block;
+ }
+
+ /*
+ * Return address of byte in buffer corresponding to
+ * offset, and size of remainder of buffer after that
+ * byte.
+ */
+ *buf_p = fp->f_buf + off;
+ *size_p = block_size - off;
+
+ /*
+ * But truncate buffer at end of file.
+ */
+ if (*size_p > fp->f_di.di_size - fp->f_seekp)
+ *size_p = fp->f_di.di_size - fp->f_seekp;
+
+ return (0);
+}
+
+/*
+ * Search a directory for a name and return its
+ * i_number.
+ */
+static int
+search_directory(char *name, struct open_file *f, ufsino_t *inumber_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ int namlen, length, rc;
+ struct direct *dp, *edp;
+ size_t buf_size;
+ char *buf;
+
+ length = strlen(name);
+
+ fp->f_seekp = 0;
+ while (fp->f_seekp < fp->f_di.di_size) {
+ rc = buf_read_file(f, &buf, &buf_size);
+ if (rc)
+ return (rc);
+
+ dp = (struct direct *)buf;
+ edp = (struct direct *)(buf + buf_size);
+ while (dp < edp) {
+ if (dp->d_ino == 0)
+ goto next;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ if (fp->f_fs->fs_maxsymlinklen <= 0)
+ namlen = dp->d_type;
+ else
+#endif
+ namlen = dp->d_namlen;
+ if (namlen == length &&
+ !strcmp(name, dp->d_name)) {
+ /* found entry */
+ *inumber_p = dp->d_ino;
+ return (0);
+ }
+ next:
+ dp = (struct direct *)((char *)dp + dp->d_reclen);
+ }
+ fp->f_seekp += buf_size;
+ }
+ return (ENOENT);
+}
+
+/*
+ * Open a file.
+ */
+int
+ufs_open(char *path, struct open_file *f)
+{
+ char namebuf[MAXPATHLEN+1], *cp, *ncp, *buf = NULL;
+ ufsino_t inumber, parent_inumber;
+ int rc, c, nlinks = 0;
+ struct file *fp;
+ size_t buf_size;
+ struct fs *fs;
+
+ /* allocate file system specific data structure */
+ fp = alloc(sizeof(struct file));
+ bzero(fp, sizeof(struct file));
+ f->f_fsdata = (void *)fp;
+
+ /* allocate space and read super block */
+ fs = alloc(SBSIZE);
+ fp->f_fs = fs;
+ twiddle();
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ SBLOCK, SBSIZE, (char *)fs, &buf_size);
+ if (rc)
+ goto out;
+
+ if (buf_size != SBSIZE || fs->fs_magic != FS_MAGIC ||
+ fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) {
+ rc = EINVAL;
+ goto out;
+ }
+#ifdef COMPAT_UFS
+ ffs_oldfscompat(fs);
+#endif
+
+ /*
+ * Calculate indirect block levels.
+ */
+ {
+ int mult;
+ int level;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fp->f_nindir[level] = mult;
+ }
+ }
+
+ inumber = ROOTINO;
+ if ((rc = read_inode(inumber, f)) != 0)
+ goto out;
+
+ cp = path;
+ while (*cp) {
+
+ /*
+ * Remove extra separators
+ */
+ while (*cp == '/')
+ cp++;
+ if (*cp == '\0')
+ break;
+
+ /*
+ * Check that current node is a directory.
+ */
+ if ((fp->f_di.di_mode & IFMT) != IFDIR) {
+ rc = ENOTDIR;
+ goto out;
+ }
+
+ /*
+ * Get next component of path name.
+ */
+ {
+ int len = 0;
+
+ ncp = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ if (++len > MAXNAMLEN) {
+ rc = ENOENT;
+ goto out;
+ }
+ cp++;
+ }
+ *cp = '\0';
+ }
+
+ /*
+ * Look up component in current directory.
+ * Save directory inumber in case we find a
+ * symbolic link.
+ */
+ parent_inumber = inumber;
+ rc = search_directory(ncp, f, &inumber);
+ *cp = c;
+ if (rc)
+ goto out;
+
+ /*
+ * Open next component.
+ */
+ if ((rc = read_inode(inumber, f)) != 0)
+ goto out;
+
+ /*
+ * Check for symbolic link.
+ */
+ if ((fp->f_di.di_mode & IFMT) == IFLNK) {
+ u_int64_t link_len = fp->f_di.di_size;
+ size_t len;
+
+ len = strlen(cp);
+
+ if (link_len + len > MAXPATHLEN ||
+ ++nlinks > MAXSYMLINKS) {
+ rc = ENOENT;
+ goto out;
+ }
+
+ bcopy(cp, &namebuf[link_len], len + 1);
+
+ if (link_len < fs->fs_maxsymlinklen) {
+ bcopy(fp->f_di.di_shortlink, namebuf, link_len);
+ } else {
+ /*
+ * Read file for symbolic link
+ */
+ size_t buf_size;
+ daddr32_t disk_block;
+ struct fs *fs = fp->f_fs;
+
+ if (!buf)
+ buf = alloc(fs->fs_bsize);
+ rc = block_map(f, (daddr32_t)0, &disk_block);
+ if (rc)
+ goto out;
+
+ twiddle();
+ rc = (f->f_dev->dv_strategy)(f->f_devdata,
+ F_READ, fsbtodb(fs, disk_block),
+ fs->fs_bsize, buf, &buf_size);
+ if (rc)
+ goto out;
+
+ bcopy(buf, namebuf, link_len);
+ }
+
+ /*
+ * If relative pathname, restart at parent directory.
+ * If absolute pathname, restart at root.
+ */
+ cp = namebuf;
+ if (*cp != '/')
+ inumber = parent_inumber;
+ else
+ inumber = ROOTINO;
+
+ if ((rc = read_inode(inumber, f)) != 0)
+ goto out;
+ }
+ }
+
+ /*
+ * Found terminal component.
+ */
+ rc = 0;
+out:
+ if (buf)
+ free(buf, fs->fs_bsize);
+ if (rc)
+ (void)ufs_close_internal(fp);
+
+ return (rc);
+}
+
+int
+ufs_close(struct open_file *f)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ f->f_fsdata = NULL;
+ if (fp == NULL)
+ return (0);
+
+ return (ufs_close_internal(fp));
+}
+
+static int
+ufs_close_internal(struct file *fp)
+{
+ int level;
+
+ for (level = 0; level < NIADDR; level++) {
+ if (fp->f_blk[level])
+ free(fp->f_blk[level], fp->f_fs->fs_bsize);
+ }
+ if (fp->f_buf)
+ free(fp->f_buf, fp->f_fs->fs_bsize);
+ free(fp->f_fs, SBSIZE);
+ free(fp, sizeof(struct file));
+ return (0);
+}
+
+/*
+ * Copy a portion of a file into kernel memory.
+ * Cross block boundaries when necessary.
+ */
+int
+ufs_read(struct open_file *f, void *start, size_t size, size_t *resid)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ char *buf, *addr = start;
+ size_t csize, buf_size;
+ int rc = 0;
+
+ while (size != 0) {
+ if (fp->f_seekp >= fp->f_di.di_size)
+ break;
+
+ rc = buf_read_file(f, &buf, &buf_size);
+ if (rc)
+ break;
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+
+ bcopy(buf, addr, csize);
+
+ fp->f_seekp += csize;
+ addr += csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+ return (rc);
+}
+
+/*
+ * Not implemented.
+ */
+int
+ufs_write(struct open_file *f, void *start, size_t size, size_t *resid)
+{
+
+ return (EROFS);
+}
+
+off_t
+ufs_seek(struct open_file *f, off_t offset, int where)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ switch (where) {
+ case SEEK_SET:
+ fp->f_seekp = offset;
+ break;
+ case SEEK_CUR:
+ fp->f_seekp += offset;
+ break;
+ case SEEK_END:
+ fp->f_seekp = fp->f_di.di_size - offset;
+ break;
+ default:
+ return (-1);
+ }
+ return (fp->f_seekp);
+}
+
+int
+ufs_stat(struct open_file *f, struct stat *sb)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ /* only important stuff */
+ sb->st_mode = fp->f_di.di_mode;
+ sb->st_uid = fp->f_di.di_uid;
+ sb->st_gid = fp->f_di.di_gid;
+ sb->st_size = fp->f_di.di_size;
+ return (0);
+}
+
+#ifndef NO_READDIR
+int
+ufs_readdir(struct open_file *f, char *name)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct direct *dp, *edp;
+ size_t buf_size;
+ int rc, namlen;
+ char *buf;
+
+ if (name == NULL)
+ fp->f_seekp = 0;
+ else {
+ /* end of dir */
+ if (fp->f_seekp >= fp->f_di.di_size) {
+ *name = '\0';
+ return -1;
+ }
+
+ do {
+ if ((rc = buf_read_file(f, &buf, &buf_size)) != 0)
+ return rc;
+
+ dp = (struct direct *)buf;
+ edp = (struct direct *)(buf + buf_size);
+ while (dp < edp && dp->d_ino == 0)
+ dp = (struct direct *)((char *)dp + dp->d_reclen);
+ fp->f_seekp += buf_size -
+ ((u_int8_t *)edp - (u_int8_t *)dp);
+ } while (dp >= edp);
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ if (fp->f_fs->fs_maxsymlinklen <= 0)
+ namlen = dp->d_type;
+ else
+#endif
+ namlen = dp->d_namlen;
+ strncpy(name, dp->d_name, namlen + 1);
+
+ fp->f_seekp += dp->d_reclen;
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef COMPAT_UFS
+/*
+ * Sanity checks for old file systems.
+ *
+ * XXX - goes away some day.
+ */
+static void
+ffs_oldfscompat(struct fs *fs)
+{
+ int i;
+
+ fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */
+ fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */
+ if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */
+ fs->fs_nrpos = 8; /* XXX */
+ if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
+ quad_t sizepb = fs->fs_bsize; /* XXX */
+ /* XXX */
+ fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */
+ for (i = 0; i < NIADDR; i++) { /* XXX */
+ sizepb *= NINDIR(fs); /* XXX */
+ fs->fs_maxfilesize += sizepb; /* XXX */
+ } /* XXX */
+ fs->fs_qbmask = ~fs->fs_bmask; /* XXX */
+ fs->fs_qfmask = ~fs->fs_fmask; /* XXX */
+ } /* XXX */
+}
+#endif
diff --git a/usr.sbin/vmd/vmboot.c b/usr.sbin/vmd/vmboot.c
new file mode 100644
index 00000000000..93b75620f5b
--- /dev/null
+++ b/usr.sbin/vmd/vmboot.c
@@ -0,0 +1,237 @@
+/* $OpenBSD: vmboot.c,v 1.1 2016/11/24 07:58:55 reyk Exp $ */
+
+/*
+ * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
+ *
+ * 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/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/disklabel.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+
+#include "vmd.h"
+#include "vmboot.h"
+
+int vmboot_strategy(void *, int, daddr32_t, size_t, void *, size_t *);
+off_t vmboot_findopenbsd(struct open_file *, off_t);
+void *vmboot_loadfile(struct open_file *, char *, size_t *);
+
+/*
+ * For ufs.c
+ */
+
+struct devsw vmboot_devsw = {
+ .dv_name = "vmboot",
+ .dv_strategy = vmboot_strategy,
+ /* other fields are not needed */
+};
+
+struct open_file vmboot_file = {
+ .f_dev = &vmboot_devsw,
+ .f_devdata = NULL
+};
+
+struct vmboot {
+ int fd;
+ off_t partoff;
+};
+
+int
+vmboot_strategy(void *devdata, int rw,
+ daddr32_t blk, size_t size, void *buf, size_t *rsize)
+{
+ struct vmboot *vmboot = devdata;
+ ssize_t rlen;
+
+ if (vmboot->fd == -1)
+ return (EIO);
+
+ switch (rw) {
+ case F_READ:
+ rlen = pread(vmboot->fd, buf, size,
+ (blk + vmboot->partoff) * DEV_BSIZE);
+ if (rlen == -1)
+ return (errno);
+ *rsize = (size_t)rlen;
+ break;
+ case F_WRITE:
+ rlen = pwrite(vmboot->fd, buf, size,
+ (blk + vmboot->partoff) * DEV_BSIZE);
+ if (rlen == -1)
+ return (errno);
+ *rsize = (size_t)rlen;
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+/*
+ * Based on findopenbsd() from biosdev.c that was partially written by me.
+ */
+off_t
+vmboot_findopenbsd(struct open_file *f, off_t mbroff)
+{
+ struct dos_mbr mbr;
+ struct dos_partition *dp;
+ off_t mbr_eoff = DOSBBSECTOR, nextebr;
+ int ret, i;
+ static int maxebr = DOS_MAXEBR;
+ size_t rsize;
+
+ if (!maxebr--) {
+ log_debug("%s: too many extended partitions", __func__);
+ return (-1);
+ }
+
+ memset(&mbr, 0, sizeof(mbr));
+ ret = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ mbroff, sizeof(mbr), &mbr, &rsize);
+ if (ret != 0 || rsize != sizeof(mbr)) {
+ log_debug("%s: failed to read MBR", __func__);
+ return (-1);
+ }
+
+ if (mbr.dmbr_sign != DOSMBR_SIGNATURE) {
+ log_debug("%s: bad MBR signature", __func__);
+ return (-1);
+ }
+
+ /* Search for the first OpenBSD partition */
+ nextebr = 0;
+ for (i = 0; i < NDOSPART; i++) {
+ dp = &mbr.dmbr_parts[i];
+ if (!dp->dp_size)
+ continue;
+
+ if (dp->dp_typ == DOSPTYP_OPENBSD) {
+ if (dp->dp_start > (dp->dp_start + mbroff))
+ continue;
+ return (dp->dp_start + mbroff);
+ }
+
+ if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
+ dp->dp_typ == DOSPTYP_EXTENDL)) {
+ nextebr = dp->dp_start + mbr_eoff;
+ if (nextebr < dp->dp_start)
+ nextebr = -1;
+ if (mbr_eoff == DOSBBSECTOR)
+ mbr_eoff = dp->dp_start;
+ }
+ }
+
+ if (nextebr && nextebr != -1) {
+ mbroff = nextebr;
+ return (vmboot_findopenbsd(f, mbroff));
+ }
+
+ return (-1);
+}
+
+void *
+vmboot_loadfile(struct open_file *f, char *file, size_t *size)
+{
+ char *buf = NULL;
+ struct stat st;
+ size_t rsize;
+ int ret;
+
+ *size = 0;
+
+ if ((ret = ufs_open(file, f)) != 0) {
+ log_debug("%s: failed to open hd0a:%s", __func__, file);
+ goto done;
+ }
+
+ if ((ret = ufs_stat(f, &st)) != 0) {
+ log_debug("%s: failed to stat hd0a:%s", __func__, file);
+ goto done;
+ }
+
+ if ((buf = calloc(1, roundup(st.st_size, DEV_BSIZE))) == NULL) {
+ log_debug("%s: failed to allocate buffer", __func__);
+ goto done;
+ }
+
+ if ((ret = ufs_read(f, buf, st.st_size, &rsize)) != 0) {
+ log_debug("%s: failed to read hd0a:%s", __func__, file);
+ goto done;
+ }
+
+ *size = st.st_size;
+ done:
+ ufs_close(f);
+ return (buf);
+}
+
+FILE *
+vmboot_open(int kernel_fd, int disk_fd, void **boot)
+{
+ char file[PATH_MAX];
+ char *buf = NULL;
+ struct vmboot vmboot;
+ size_t size;
+ FILE *fp = NULL;
+
+ *boot = NULL;
+
+ /* First open kernel directly if specified by fd */
+ if (kernel_fd != -1)
+ return (fdopen(kernel_fd, "r"));
+
+ if (disk_fd == -1)
+ return (NULL);
+
+ memset(&vmboot, 0, sizeof(vmboot));
+ vmboot.fd = disk_fd;
+ vmboot_file.f_devdata = &vmboot;
+
+ /* XXX try and parse hd0a:/etc/boot.conf */
+ strlcpy(file, VM_DEFAULT_KERNEL, sizeof(file));
+
+ if ((vmboot.partoff = vmboot_findopenbsd(&vmboot_file, 0)) == -1) {
+ log_debug("%s: could not find openbsd partition", __func__);
+ return (NULL);
+ }
+
+ if ((buf = vmboot_loadfile(&vmboot_file, file, &size)) == NULL)
+ return (NULL);
+ *boot = buf;
+
+ if ((fp = fmemopen(buf, size, "r")) == NULL) {
+ log_debug("%s: failed to open memory stream", __func__);
+ free(buf);
+ *boot = NULL;
+ }
+
+ return (fp);
+}
+
+void
+vmboot_close(FILE *fp, void *boot)
+{
+ fclose(fp);
+ free(boot);
+}
diff --git a/usr.sbin/vmd/vmboot.h b/usr.sbin/vmd/vmboot.h
new file mode 100644
index 00000000000..1df187b28db
--- /dev/null
+++ b/usr.sbin/vmd/vmboot.h
@@ -0,0 +1,70 @@
+/* $OpenBSD: vmboot.h,v 1.1 2016/11/24 07:58:55 reyk Exp $ */
+
+/*
+ * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
+ *
+ * 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/stat.h>
+#include <sys/stdarg.h>
+#include <sys/stdint.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef VMBOOT_H
+#define VMBOOT_H
+
+#define F_READ 1
+#define F_WRITE 2
+
+struct open_file;
+
+struct fs_ops {
+ int (*open)(char *, struct open_file *);
+ int (*close)(struct open_file *);
+ int (*read)(struct open_file *, void *, size_t, size_t *);
+ int (*write)(struct open_file *, void *, size_t, size_t *);
+ off_t (*seek)(struct open_file *, off_t, int);
+ int (*stat)(struct open_file *, struct stat *);
+ int (*readdir)(struct open_file *, char *);
+};
+
+struct devsw {
+ char *dv_name;
+ int (*dv_strategy)(void *, int, daddr32_t, size_t,
+ void *, size_t *);
+};
+
+struct open_file {
+ int f_flags;
+ struct devsw *f_dev;
+ void *f_devdata;
+ struct fs_ops *f_ops;
+ void *f_fsdata;
+ off_t f_offset;
+};
+
+int ufs_open(char *, struct open_file *);
+int ufs_close(struct open_file *);
+int ufs_read(struct open_file *, void *, size_t, size_t *);
+int ufs_write(struct open_file *, void *, size_t, size_t *);
+off_t ufs_seek(struct open_file *, off_t offset, int);
+int ufs_stat(struct open_file *, struct stat *);
+int ufs_readdir(struct open_file *, char *);
+
+#endif /* VMBOOT_H */
diff --git a/usr.sbin/vmd/vmd.c b/usr.sbin/vmd/vmd.c
index c29d4aac3ae..857976e3e0b 100644
--- a/usr.sbin/vmd/vmd.c
+++ b/usr.sbin/vmd/vmd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmd.c,v 1.42 2016/11/22 21:55:54 reyk Exp $ */
+/* $OpenBSD: vmd.c,v 1.43 2016/11/24 07:58:55 reyk Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -199,8 +199,11 @@ vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg)
break;
}
- log_info("%s: started vm %d successfully, tty %s",
- vcp->vcp_name, vcp->vcp_id, vm->vm_ttyname);
+ log_info("%s: started vm %d successfully, "
+ "kernel %s, tty %s", vcp->vcp_name, vcp->vcp_id,
+ strlen(vcp->vcp_kernel) ?
+ vcp->vcp_kernel : "hd0a:" VM_DEFAULT_KERNEL,
+ vm->vm_ttyname);
break;
case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
case IMSG_VMDOP_TERMINATE_VM_EVENT:
@@ -664,13 +667,16 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc,
if (vcp->vcp_ncpus == 0)
vcp->vcp_ncpus = 1;
if (vcp->vcp_ncpus > VMM_MAX_VCPUS_PER_VM) {
- log_debug("invalid number of CPUs");
+ log_warnx("invalid number of CPUs");
goto fail;
} else if (vcp->vcp_ndisks > VMM_MAX_DISKS_PER_VM) {
- log_debug("invalid number of disks");
+ log_warnx("invalid number of disks");
goto fail;
} else if (vcp->vcp_nnics > VMM_MAX_NICS_PER_VM) {
- log_debug("invalid number of interfaces");
+ log_warnx("invalid number of interfaces");
+ goto fail;
+ } else if (strlen(vcp->vcp_kernel) == 0 && vcp->vcp_ndisks == 0) {
+ log_warnx("no kernel or disk specified");
goto fail;
}
diff --git a/usr.sbin/vmd/vmd.h b/usr.sbin/vmd/vmd.h
index ab414049e80..8ec1b1c1c1b 100644
--- a/usr.sbin/vmd/vmd.h
+++ b/usr.sbin/vmd/vmd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmd.h,v 1.35 2016/11/22 11:31:38 edd Exp $ */
+/* $OpenBSD: vmd.h,v 1.36 2016/11/24 07:58:55 reyk Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -25,6 +25,7 @@
#include <net/if.h>
#include <limits.h>
+#include <stdio.h>
#include <pthread.h>
#include "proc.h"
@@ -36,6 +37,7 @@
#define VMD_CONF "/etc/vm.conf"
#define SOCKET_NAME "/var/run/vmd.sock"
#define VMM_NODE "/dev/vmm"
+#define VM_DEFAULT_KERNEL "/bsd"
#define VM_NAME_MAX 64
#define VM_TTYNAME_MAX 16
#define MAX_TAP 256
@@ -202,6 +204,10 @@ int config_getvm(struct privsep *, struct imsg *);
int config_getdisk(struct privsep *, struct imsg *);
int config_getif(struct privsep *, struct imsg *);
+/* vmboot.c */
+FILE *vmboot_open(int, int, void **);
+void vmboot_close(FILE *, void *);
+
/* parse.y */
int parse_config(const char *);
int cmdline_symset(char *);
diff --git a/usr.sbin/vmd/vmm.c b/usr.sbin/vmd/vmm.c
index 226b8ceefa4..ecd74545c1b 100644
--- a/usr.sbin/vmd/vmm.c
+++ b/usr.sbin/vmd/vmm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmm.c,v 1.55 2016/11/22 22:51:45 reyk Exp $ */
+/* $OpenBSD: vmm.c,v 1.56 2016/11/24 07:58:55 reyk Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -465,7 +465,9 @@ start_vm(struct imsg *imsg, uint32_t *id)
size_t i;
int ret = EINVAL;
int fds[2], nicfds[VMM_MAX_NICS_PER_VM];
- struct vcpu_reg_state vrs;
+ struct vcpu_reg_state vrs;
+ FILE *kernfp;
+ void *boot;
if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) {
log_warnx("%s: can't find vm", __func__);
@@ -566,14 +568,22 @@ start_vm(struct imsg *imsg, uint32_t *id)
*/
memcpy(&vrs, &vcpu_init_flat32, sizeof(struct vcpu_reg_state));
+ /* Find and open kernel image */
+ if ((kernfp = vmboot_open(vm->vm_kernel,
+ vm->vm_disks[0], &boot)) == NULL)
+ fatalx("failed to open kernel - exiting");
+
/* Load kernel image */
- ret = loadelf_main(vm->vm_kernel, vcp, &vrs);
+ ret = loadelf_main(kernfp, vcp, &vrs);
if (ret) {
errno = ret;
fatal("failed to load kernel - exiting");
}
- close(vm->vm_kernel);
+ vmboot_close(kernfp, boot);
+
+ if (vm->vm_kernel != -1)
+ close(vm->vm_kernel);
con_fd = vm->vm_tty;
if (fcntl(con_fd, F_SETFL, O_NONBLOCK) == -1)