-# $OpenBSD: Makefile,v 1.30 2024/07/10 09:27:33 dv Exp $
+# $OpenBSD: Makefile,v 1.31 2024/09/11 15:42:52 bluhm Exp $
.if ${MACHINE} == "amd64" || ${MACHINE} == "arm64"
.if ${MACHINE} == "amd64"
SRCS+= i8253.c i8259.c fw_cfg.c loadfile_elf.c mc146818.c ns8250.c
SRCS+= x86_vm.c x86_mmio.c
+SRCS+= psp.c sev.c
.endif # amd64
.if ${MACHINE} == "arm64"
SRCS+= arm64_vm.c
-/* $OpenBSD: arm64_vm.c,v 1.1 2024/07/10 10:41:19 dv Exp $ */
+/* $OpenBSD: arm64_vm.c,v 1.2 2024/09/11 15:42:52 bluhm Exp $ */
/*
* Copyright (c) 2024 Dave Voutila <dv@openbsd.org>
*
/* NOTREACHED */
return (0xff);
}
+
+void
+set_return_data(struct vm_exit *vei, uint32_t data)
+{
+ fatalx("%s: unimplemented", __func__);
+ /* NOTREACHED */
+ return;
+}
+
+void
+get_input_data(struct vm_exit *vei, uint32_t *data)
+{
+ fatalx("%s: unimplemented", __func__);
+ /* NOTREACHED */
+ return;
+}
+
+int
+sev_init(struct vmd_vm *vm)
+{
+ fatalx("%s: unimplemented", __func__);
+ /* NOTREACHED */
+ return (-1);
+}
+
+int
+sev_shutdown(struct vmd_vm *vm)
+{
+ fatalx("%s: unimplemented", __func__);
+ /* NOTREACHED */
+ return (-1);
+}
+
+int
+sev_activate(struct vmd_vm *vm, int vcpu_id)
+{
+ fatalx("%s: unimplemented", __func__);
+ /* NOTREACHED */
+ return (-1);
+}
+
+int
+sev_encrypt_memory(struct vmd_vm *vm)
+{
+ fatalx("%s: unimplemented", __func__);
+ /* NOTREACHED */
+ return (-1);
+}
/* $NetBSD: loadfile.c,v 1.10 2000/12/03 02:53:04 tsutsui Exp $ */
-/* $OpenBSD: loadfile_elf.c,v 1.48 2024/07/09 09:31:37 dv Exp $ */
+/* $OpenBSD: loadfile_elf.c,v 1.49 2024/09/11 15:42:52 bluhm Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
extern char *__progname;
extern int vm_id;
+uint64_t pg_crypt = 0;
+
/*
* setsegment
*
setsegment(&sd[2], 0, 0xffffffff, SDT_MEMRWA, SEL_KPL, 1, 1);
write_mem(GDT_PAGE, gdtpage, PAGE_SIZE);
+ sev_register_encryption(GDT_PAGE, PAGE_SIZE);
}
/*
/* PDPDE0 - first 1GB */
memset(ptes, 0, sizeof(ptes));
- ptes[0] = PG_V | PML3_PAGE;
+ ptes[0] = pg_crypt | PG_V | PML3_PAGE;
write_mem(PML4_PAGE, ptes, PAGE_SIZE);
+ sev_register_encryption(PML4_PAGE, PAGE_SIZE);
/* PDE0 - first 1GB */
memset(ptes, 0, sizeof(ptes));
- ptes[0] = PG_V | PG_RW | PG_u | PML2_PAGE;
+ ptes[0] = pg_crypt | PG_V | PG_RW | PG_u | PML2_PAGE;
write_mem(PML3_PAGE, ptes, PAGE_SIZE);
+ sev_register_encryption(PML3_PAGE, PAGE_SIZE);
/* First 1GB (in 2MB pages) */
memset(ptes, 0, sizeof(ptes));
for (i = 0 ; i < 512; i++) {
- ptes[i] = PG_V | PG_RW | PG_u | PG_PS | ((2048 * 1024) * i);
+ ptes[i] = pg_crypt | PG_V | PG_RW | PG_u | PG_PS |
+ ((2048 * 1024) * i);
}
write_mem(PML2_PAGE, ptes, PAGE_SIZE);
+ sev_register_encryption(PML2_PAGE, PAGE_SIZE);
}
/*
vrs->vrs_crs[VCPU_REGS_CR4] = CR4_PSE;
vrs->vrs_msrs[VCPU_REGS_EFER] = 0ULL;
}
- else
+ else {
+ if (vcp->vcp_sev) {
+ if (vcp->vcp_poscbit == 0) {
+ log_warnx("SEV enabled but no C-bit reported");
+ return 1;
+ }
+ pg_crypt = (1ULL << vcp->vcp_poscbit);
+ log_debug("%s: poscbit %d pg_crypt 0x%016llx",
+ __func__, vcp->vcp_poscbit, pg_crypt);
+ }
push_pt_64();
+ }
if (bootdevice == VMBOOTDEV_NET) {
bootmac = &bm;
ba[i++] = 0xFFFFFFFF; /* BOOTARG_END */
write_mem(BOOTARGS_PAGE, ba, PAGE_SIZE);
+ sev_register_encryption(BOOTARGS_PAGE, PAGE_SIZE);
return (i * sizeof(uint32_t));
}
stack[--loc] = 0;
write_mem(STACK_PAGE, &stack, PAGE_SIZE);
+ sev_register_encryption(STACK_PAGE, PAGE_SIZE);
return (1024 - (loc - 1)) * sizeof(uint32_t);
}
size_t i, osz;
char buf[PAGE_SIZE];
+ sev_register_encryption(addr, sz);
+
/*
* break up the 'sz' bytes into PAGE_SIZE chunks for use with
* write_mem
int i, ct;
char buf[PAGE_SIZE];
+ sev_register_encryption(addr, sz);
+
/*
* break up the 'sz' bytes into PAGE_SIZE chunks for use with
* write_mem
{
if (write_mem(addr, NULL, sz))
return;
+ sev_register_encryption(addr, sz);
}
/*
mbcopy(void *src, paddr_t dst, int sz)
{
write_mem(dst, src, sz);
+ sev_register_encryption(dst, sz);
}
/*
-/* $OpenBSD: parse.y,v 1.69 2024/07/09 09:31:37 dv Exp $ */
+/* $OpenBSD: parse.y,v 1.70 2024/09/11 15:42:52 bluhm Exp $ */
/*
* Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org>
%token FORMAT GROUP
%token INET6 INSTANCE INTERFACE LLADDR LOCAL LOCKED MEMORY NET NIFS OWNER
%token PATH PREFIX RDOMAIN SIZE SOCKET SWITCH UP VM VMID STAGGERED START
-%token PARALLEL DELAY
+%token PARALLEL DELAY SEV
%token <v.number> NUMBER
%token <v.string> STRING
%type <v.lladdr> lladdr
%type <v.string> optstring
%type <v.string> string
%type <v.string> vm_instance
+%type <v.number> sev;
%%
vm_opts : disable {
vmc_disable = $1;
}
+ | sev {
+ vcp->vcp_sev = 1;
+ }
| DISK string image_format {
if (parse_disk($2, $3) != 0) {
yyerror("failed to parse disks: %s", $2);
| DISABLE { $$ = 1; }
;
+sev : SEV { $$ = 1; }
+ ;
+
bootdevice : CDROM { $$ = VMBOOTDEV_CDROM; }
| DISK { $$ = VMBOOTDEV_DISK; }
| NET { $$ = VMBOOTDEV_NET; }
{ "path", PATH },
{ "prefix", PREFIX },
{ "rdomain", RDOMAIN },
+ { "sev", SEV },
{ "size", SIZE },
{ "socket", SOCKET },
{ "staggered", STAGGERED },
--- /dev/null
+/* $OpenBSD: psp.c,v 1.1 2024/09/11 15:42:52 bluhm Exp $ */
+
+/*
+ * Copyright (c) 2023, 2024 Hans-Joerg Hoexer <hshoexer@genua.de>
+ *
+ * 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/device.h>
+#include <sys/ioctl.h>
+#include <sys/rwlock.h>
+
+#include <machine/bus.h>
+#include <dev/ic/pspvar.h>
+
+#include <string.h>
+
+#include "vmd.h"
+
+extern struct vmd *env;
+
+/* Guest policy */
+#define GPOL_NODBG (1ULL << 0) /* no debuggin */
+#define GPOL_NOKS (1ULL << 1) /* no key sharing */
+#define GPOL_ES (1ULL << 2) /* SEV-ES required */
+#define GPOL_NOSEND (1ULL << 3) /* no guest migration */
+#define GPOL_DOMAIN (1ULL << 4) /* no migration to other domain */
+#define GPOL_SEV (1ULL << 5) /* no migration to non-SEV platform */
+
+
+/*
+ * Retrieve platform state.
+ */
+int
+psp_get_pstate(uint16_t *state)
+{
+ struct psp_platform_status pst;
+
+ if (ioctl(env->vmd_psp_fd, PSP_IOC_GET_PSTATUS, &pst) < 0) {
+ log_warn("%s: ioctl", __func__);
+ return (-1);
+ }
+
+ if (state)
+ *state = pst.state;
+
+ return (0);
+}
+
+
+/*
+ * Flush data fabrics of all cores.
+ *
+ * This ensures all data of a SEV enabled guest is committed to
+ * memory. This needs to be done before an ASID is assigend to
+ * guest using psp_activate().
+ */
+int
+psp_df_flush(void)
+{
+ if (ioctl(env->vmd_psp_fd, PSP_IOC_DF_FLUSH) < 0) {
+ log_warn("%s: ioctl", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Retrieve guest state.
+ */
+int
+psp_get_gstate(uint32_t handle, uint32_t *policy, uint32_t *asid,
+ uint8_t *state)
+{
+ struct psp_guest_status gst;
+
+ memset(&gst, 0, sizeof(gst));
+ gst.handle = handle;
+
+ if (ioctl(env->vmd_psp_fd, PSP_IOC_GET_GSTATUS, &gst) < 0) {
+ log_warn("%s: ioctl", __func__);
+ return (-1);
+ }
+
+ if (policy)
+ *policy = gst.policy;
+ if (asid)
+ *asid = gst.asid;
+ if (state)
+ *state = gst.state;
+
+ return (0);
+}
+
+
+/*
+ * Start the launch sequence of a guest.
+ */
+int
+psp_launch_start(uint32_t *handle)
+{
+ struct psp_launch_start ls;
+
+ memset(&ls, 0, sizeof(ls));
+
+ /* Set guest policy. */
+ ls.policy = (GPOL_NODBG | GPOL_NOKS | GPOL_NOSEND | GPOL_DOMAIN |
+ GPOL_SEV);
+
+ if (ioctl(env->vmd_psp_fd, PSP_IOC_LAUNCH_START, &ls) < 0) {
+ log_warn("%s: ioctl", __func__);
+ return (-1);
+ }
+
+ if (handle)
+ *handle = ls.handle;
+
+ return (0);
+}
+
+
+/*
+ * Encrypt and measure a memory range.
+ */
+int
+psp_launch_update(uint32_t handle, vaddr_t v, size_t len)
+{
+ struct psp_launch_update_data lud;
+
+ memset(&lud, 0, sizeof(lud));
+ lud.handle = handle;
+ lud.paddr = v; /* will be converted to paddr */
+ lud.length = len;
+
+ if (ioctl(env->vmd_psp_fd, PSP_IOC_LAUNCH_UPDATE_DATA, &lud) < 0) {
+ log_warn("%s: ioctl", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Finalize and return memory measurement.
+ *
+ * We ask the PSP to provide a measurement (HMAC) over the encrypted
+ * memory. As we do not yet negotiate a shared integrity key with
+ * the PSP, the measurement is not really meaningful. Thus we just
+ * log it for now.
+ */
+int
+psp_launch_measure(uint32_t handle)
+{
+ struct psp_launch_measure lm;
+ char *p, buf[256];
+ size_t len;
+ unsigned int i;
+
+ memset(&lm, 0, sizeof(lm));
+ lm.handle = handle;
+ lm.measure_len = sizeof(lm.psp_measure);
+ memset(lm.measure, 0, sizeof(lm.measure));
+ memset(lm.measure_nonce, 0, sizeof(lm.measure_nonce));
+
+ if (ioctl(env->vmd_psp_fd, PSP_IOC_LAUNCH_MEASURE, &lm) < 0) {
+ log_warn("%s: ioctl", __func__);
+ return (-1);
+ }
+
+ /*
+ * We can not verify the measurement, yet. Therefore just
+ * log it.
+ */
+ len = sizeof(buf);
+ memset(buf, 0, len);
+ p = buf;
+ for (i = 0; i < sizeof(lm.measure) && len >= 2;
+ i++, p += 2, len -= 2) {
+ snprintf(p, len, "%02x", lm.measure[i]);
+ }
+ log_info("%s: measurement\t0x%s", __func__, buf);
+
+ len = sizeof(buf);
+ memset(buf, 0, len);
+ p = buf;
+ for (i = 0; i < sizeof(lm.measure_nonce) && len >= 2;
+ i++, p += 2, len -= 2) {
+ snprintf(p, len, "%02x", lm.measure_nonce[i]);
+ }
+ log_info("%s: nonce\t0x%s", __func__, buf);
+
+ return (0);
+}
+
+
+/*
+ * Finalize launch sequence.
+ */
+int
+psp_launch_finish(uint32_t handle)
+{
+ struct psp_launch_finish lf;
+
+ lf.handle = handle;
+
+ if (ioctl(env->vmd_psp_fd, PSP_IOC_LAUNCH_FINISH, &lf) < 0) {
+ log_warn("%s: ioctl", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Activate a guest.
+ *
+ * This associates the guest's ASID with the handle used to identify
+ * crypto contexts managed by the PSP.
+ */
+int
+psp_activate(uint32_t handle, uint32_t asid)
+{
+ struct psp_activate act;
+
+ act.handle = handle;
+ act.asid = asid;
+
+ if (ioctl(env->vmd_psp_fd, PSP_IOC_ACTIVATE, &act) < 0) {
+ log_warn("%s: ioctl", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Deactivate and decommission a guest.
+ *
+ * This deassociates the guest's ASID from the crypto contexts in
+ * the PSP. Then the PSP releases the crypto contexts (i.e. deletes
+ * keys).
+ */
+int
+psp_guest_shutdown(uint32_t handle)
+{
+ struct psp_guest_shutdown gshutdown;
+
+ gshutdown.handle = handle;
+
+ if (ioctl(env->vmd_psp_fd, PSP_IOC_GUEST_SHUTDOWN, &gshutdown) < 0) {
+ log_warn("%s: ioctl", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/* $OpenBSD: sev.c,v 1.1 2024/09/11 15:42:52 bluhm Exp $ */
+
+/*
+ * Copyright (c) 2023, 2024 Hans-Joerg Hoexer <hshoexer@genua.de>
+ *
+ * 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/device.h>
+#include <sys/param.h>
+#include <sys/rwlock.h>
+
+#include <machine/bus.h>
+#include <crypto/xform.h>
+#include <dev/ic/pspvar.h>
+
+#include <string.h>
+
+#include "vmd.h"
+
+extern struct vmd_vm *current_vm;
+
+/*
+ * Prepare guest to use SEV.
+ *
+ * This asks the PSP to create a new crypto context including a
+ * memory encryption key and assign a handle to the context.
+ *
+ * When the PSP driver psp(4) attaches, it initializes the platform.
+ * If this fails for whatever reason we can not run a guest using SEV.
+ */
+int
+sev_init(struct vmd_vm *vm)
+{
+ struct vmop_create_params *vmc = &vm->vm_params;
+ struct vm_create_params *vcp = &vmc->vmc_params;
+ uint32_t handle;
+ uint16_t pstate;
+ uint8_t gstate;
+
+ if (!vcp->vcp_sev)
+ return (0);
+
+ if (psp_get_pstate(&pstate)) {
+ log_warnx("%s: failed to get platform state", __func__);
+ return (-1);
+ }
+ if (pstate == PSP_PSTATE_UNINIT) {
+ log_warnx("%s: platform uninitialized", __func__);
+ return (-1);
+ }
+
+ if (psp_launch_start(&handle) < 0) {
+ log_warnx("%s: launch failed", __func__);
+ return (-1);
+ };
+ vm->vm_sev_handle = handle;
+
+ if (psp_get_gstate(vm->vm_sev_handle, NULL, NULL, &gstate)) {
+ log_warnx("%s: failed to get guest state", __func__);
+ return (-1);
+ }
+ if (gstate != PSP_GSTATE_LUPDATE) {
+ log_warnx("%s: invalid guest state: 0x%hx", __func__, gstate);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Record memory segments to be encrypted for SEV.
+ */
+int
+sev_register_encryption(vaddr_t addr, size_t size)
+{
+ struct vmop_create_params *vmc;
+ struct vm_create_params *vcp;
+ struct vm_mem_range *vmr;
+ size_t off;
+ int i;
+
+ vmc = ¤t_vm->vm_params;
+ vcp = &vmc->vmc_params;
+
+ if (!vcp->vcp_sev)
+ return (0);
+
+ if (size == 0)
+ return (0);
+
+ /* Adjust address and size to be aligend to AES_XTS_BLOCKSIZE. */
+ if (addr & (AES_XTS_BLOCKSIZE - 1)) {
+ size += (addr & (AES_XTS_BLOCKSIZE - 1));
+ addr &= ~(AES_XTS_BLOCKSIZE - 1);
+ }
+
+ vmr = find_gpa_range(¤t_vm->vm_params.vmc_params, addr, size);
+ if (vmr == NULL) {
+ log_warnx("%s: failed - invalid memory range addr = 0x%lx, "
+ "len = 0x%zx", __func__, addr, size);
+ return (-1);
+ }
+ if (current_vm->vm_sev_nmemsegments ==
+ nitems(current_vm->vm_sev_memsegments)) {
+ log_warnx("%s: failed - out of SEV memory segments", __func__);
+ return (-1);
+ }
+ i = current_vm->vm_sev_nmemsegments++;
+
+ off = addr - vmr->vmr_gpa;
+
+ current_vm->vm_sev_memsegments[i].vmr_va = vmr->vmr_va + off;
+ current_vm->vm_sev_memsegments[i].vmr_size = size;
+ current_vm->vm_sev_memsegments[i].vmr_gpa = vmr->vmr_gpa + off;
+
+ log_debug("%s: i %d addr 0x%lx size 0x%lx vmr_va 0x%lx vmr_gpa 0x%lx "
+ "vmr_size 0x%lx", __func__, i, addr, size,
+ current_vm->vm_sev_memsegments[i].vmr_va,
+ current_vm->vm_sev_memsegments[i].vmr_gpa,
+ current_vm->vm_sev_memsegments[i].vmr_size);
+
+ return (0);
+}
+
+/*
+ * Encrypt and measure previously recorded memroy segments.
+ *
+ * This encrypts the memory initially used by the guest. This
+ * includes the kernel or BIOS image, initial stack, boot arguments
+ * and page tables.
+ *
+ * We also ask the PSP to provide a measurement. However, right
+ * now we can not really verify it.
+ */
+int
+sev_encrypt_memory(struct vmd_vm *vm)
+{
+ struct vmop_create_params *vmc = &vm->vm_params;
+ struct vm_create_params *vcp = &vmc->vmc_params;
+ struct vm_mem_range *vmr;
+ size_t i;
+ uint8_t gstate;
+
+ if (!vcp->vcp_sev)
+ return (0);
+
+ for (i = 0; i < vm->vm_sev_nmemsegments; i++) {
+ vmr = &vm->vm_sev_memsegments[i];
+
+ /* tell PSP to encrypt this range */
+ if (psp_launch_update(vm->vm_sev_handle, vmr->vmr_va,
+ roundup(vmr->vmr_size, AES_XTS_BLOCKSIZE))) {
+ log_warnx("%s: failed to launch update page "
+ "%zu:0x%lx", __func__, i, vmr->vmr_va);
+ return (-1);
+ }
+
+ log_debug("%s: encrypted %zu:0x%lx size 0x%lx", __func__, i,
+ vmr->vmr_va, vmr->vmr_size);
+ }
+ if (psp_launch_measure(vm->vm_sev_handle)) {
+ log_warnx("%s: failed to launch measure", __func__);
+ return (-1);
+ }
+ if (psp_launch_finish(vm->vm_sev_handle)) {
+ log_warnx("%s: failed to launch finish", __func__);
+ return (-1);
+ }
+
+ if (psp_get_gstate(vm->vm_sev_handle, NULL, NULL, &gstate)) {
+ log_warnx("%s: failed to get guest state", __func__);
+ return (-1);
+ }
+ if (gstate != PSP_GSTATE_RUNNING) {
+ log_warnx("%s: invalid guest state: 0x%hx", __func__, gstate);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Activate a guest's SEV crypto state.
+ */
+int
+sev_activate(struct vmd_vm *vm, int vcpu_id)
+{
+ struct vmop_create_params *vmc = &vm->vm_params;
+ struct vm_create_params *vcp = &vmc->vmc_params;
+ uint8_t gstate;
+
+ if (!vcp->vcp_sev)
+ return (0);
+
+ if (psp_df_flush() ||
+ psp_activate(vm->vm_sev_handle, vm->vm_sev_asid[vcpu_id])) {
+ log_warnx("%s: failed to activate guest: 0x%x:0x%x", __func__,
+ vm->vm_sev_handle, vm->vm_sev_asid[vcpu_id]);
+ return (-1);
+ }
+
+ if (psp_get_gstate(vm->vm_sev_handle, NULL, NULL, &gstate)) {
+ log_warnx("%s: failed to get guest state", __func__);
+ return (-1);
+ }
+ if (gstate != PSP_GSTATE_LUPDATE) {
+ log_warnx("%s: invalid guest state: 0x%hx", __func__, gstate);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Deactivate and decommission a guest's SEV crypto state.
+ */
+int
+sev_shutdown(struct vmd_vm *vm)
+{
+ struct vmop_create_params *vmc = &vm->vm_params;
+ struct vm_create_params *vcp = &vmc->vmc_params;
+
+ if (!vcp->vcp_sev)
+ return (0);
+
+ if (psp_guest_shutdown(vm->vm_sev_handle)) {
+ log_warnx("failed to deactivate guest");
+ return (-1);
+ }
+ vm->vm_sev_handle = 0;
+
+ return (0);
+}
-/* $OpenBSD: vm.c,v 1.104 2024/07/10 09:27:33 dv Exp $ */
+/* $OpenBSD: vm.c,v 1.105 2024/09/11 15:42:52 bluhm Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
#include <util.h>
#include "atomicio.h"
+#include "loadfile.h"
#include "mmio.h"
#include "pci.h"
#include "virtio.h"
}
}
+ if (vcp->vcp_sev && env->vmd_psp_fd < 0) {
+ log_warnx("%s not available", PSP_NODE);
+ _exit(EINVAL);
+ }
+
ret = start_vm(&vm, fd);
_exit(ret);
}
return (ret);
}
+ /* Setup SEV. */
+ ret = sev_init(vm);
+ if (ret) {
+ log_warnx("could not initialize SEV");
+ return (ret);
+ }
+
/*
* Some of vmd currently relies on global state (current_vm, con_fd).
*/
*/
ret = run_vm(&vm->vm_params, &vrs);
+ /* Shutdown SEV. */
+ if (sev_shutdown(vm))
+ log_warnx("%s: could not shutdown SEV", __func__);
+
/* Ensure that any in-flight data is written back */
virtio_shutdown(vm);
}
imsg_flush(¤t_vm->vm_iev.ibuf);
+ if (sev_shutdown(current_vm))
+ log_warnx("%s: could not shutdown SEV", __func__);
+
_exit(0);
}
vmm_create_vm(struct vmd_vm *vm)
{
struct vm_create_params *vcp = &vm->vm_params.vmc_params;
+ size_t i;
/* Sanity check arguments */
if (vcp->vcp_ncpus > VMM_MAX_VCPUS_PER_VM)
if (ioctl(env->vmd_fd, VMM_IOC_CREATE, vcp) == -1)
return (errno);
+ for (i = 0; i < vcp->vcp_ncpus; i++)
+ vm->vm_sev_asid[i] = vcp->vcp_asid[i];
+
return (0);
}
return (EIO);
}
+ if (sev_activate(current_vm, i)) {
+ log_warnx("%s: SEV activatation failed for VCPU "
+ "%zu failed - exiting.", __progname, i);
+ return (EIO);
+ }
+
+ if (sev_encrypt_memory(current_vm)) {
+ log_warnx("%s: memory encryption failed for VCPU "
+ "%zu failed - exiting.", __progname, i);
+ return (EIO);
+ }
+
/* once more because reset_cpu changes regs */
if (current_vm->vm_state & VM_STATE_RECEIVED) {
vregsp.vrwp_vm_id = vcp->vcp_id;
-.\" $OpenBSD: vm.conf.5,v 1.63 2023/05/12 00:06:53 kn Exp $
+.\" $OpenBSD: vm.conf.5,v 1.64 2024/09/11 15:42:52 bluhm Exp $
.\"
.\" Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
.\" Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: May 12 2023 $
+.Dd $Mdocdate: September 11 2024 $
.Dt VM.CONF 5
.Os
.Sh NAME
.Pf : Ar group
is given,
only the group is set.
+.It Ic sev
+Enables SEV for guest.
.El
.Sh VM INSTANCES
It is possible to use configured or running VMs as a template for
-/* $OpenBSD: vmd.c,v 1.159 2024/07/10 09:27:33 dv Exp $ */
+/* $OpenBSD: vmd.c,v 1.160 2024/09/11 15:42:52 bluhm Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
int ch;
enum privsep_procid proc_id = PROC_PARENT;
int proc_instance = 0, vm_launch = 0;
- int vmm_fd = -1, vm_fd = -1;
+ int vmm_fd = -1, vm_fd = -1, psp_fd = -1;
const char *errp, *title = NULL;
int argc0 = argc;
char dev_type = '\0';
env->vmd_fd = -1;
env->vmd_fd6 = -1;
- while ((ch = getopt(argc, argv, "D:P:I:V:X:df:i:nt:vp:")) != -1) {
+ while ((ch = getopt(argc, argv, "D:P:I:V:X:df:i:j:nt:vp:")) != -1) {
switch (ch) {
case 'D':
if (cmdline_symset(optarg) < 0)
if (errp)
fatalx("invalid vmm fd");
break;
+ case 'j':
+ /* -1 means no PSP available */
+ psp_fd = strtonum(optarg, -1, 128, &errp);
+ if (errp)
+ fatalx("invalid psp fd");
+ break;
default:
usage();
}
ps = &env->vmd_ps;
ps->ps_env = env;
+ env->vmd_psp_fd = psp_fd;
if (config_init(env) == -1)
fatal("failed to initialize configuration");
if (!env->vmd_noaction)
proc_connect(ps);
+ if (env->vmd_noaction == 0 && proc_id == PROC_PARENT) {
+ env->vmd_psp_fd = open(PSP_NODE, O_RDWR);
+ if (env->vmd_psp_fd == -1)
+ log_debug("%s: failed to open %s", __func__, PSP_NODE);
+ }
+
if (vmd_configure() == -1)
fatalx("configuration failed");
proc_compose_imsg(&env->vmd_ps, PROC_VMM, -1,
IMSG_VMDOP_RECEIVE_VMM_FD, -1, env->vmd_fd, NULL, 0);
+ /* Send PSP device fd to vmm proc. */
+ if (env->vmd_psp_fd != -1) {
+ proc_compose_imsg(&env->vmd_ps, PROC_VMM, -1,
+ IMSG_VMDOP_RECEIVE_PSP_FD, -1, env->vmd_psp_fd, NULL, 0);
+ }
+
/* Send shared global configuration to all children */
if (config_setconfig(env) == -1)
return (-1);
-/* $OpenBSD: vmd.h,v 1.127 2024/07/10 09:27:33 dv Exp $ */
+/* $OpenBSD: vmd.h,v 1.128 2024/09/11 15:42:52 bluhm Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
#define VMD_CONF "/etc/vm.conf"
#define SOCKET_NAME "/var/run/vmd.sock"
#define VMM_NODE "/dev/vmm"
+#define PSP_NODE "/dev/psp"
#define VM_DEFAULT_BIOS "/etc/firmware/vmm-bios"
#define VM_DEFAULT_KERNEL "/bsd"
#define VM_DEFAULT_DEVICE "hd0a"
IMSG_VMDOP_GET_INFO_VM_END_DATA,
IMSG_VMDOP_LOAD,
IMSG_VMDOP_RECEIVE_VMM_FD,
+ IMSG_VMDOP_RECEIVE_PSP_FD,
IMSG_VMDOP_RELOAD,
IMSG_VMDOP_PRIV_IFDESCR,
IMSG_VMDOP_PRIV_IFADD,
struct vmop_create_params vm_params;
pid_t vm_pid;
uint32_t vm_vmid;
+ uint32_t vm_sev_handle;
+ uint32_t vm_sev_asid[VMM_MAX_VCPUS_PER_VM];
+
+#define VM_SEV_NSEGMENTS 128
+ size_t vm_sev_nmemsegments;
+ struct vm_mem_range vm_sev_memsegments[VM_SEV_NSEGMENTS];
int vm_kernel;
char *vm_kernel_path; /* Used by vm.conf. */
int vmd_fd;
int vmd_fd6;
int vmd_ptmfd;
+ int vmd_psp_fd;
};
struct vm_dev_pipe {
int dump_devs(int);
int dump_send_header(int);
void *hvaddr_mem(paddr_t, size_t);
+struct vm_mem_range *
+ find_gpa_range(struct vm_create_params *, paddr_t, size_t);
int write_mem(paddr_t, const void *, size_t);
int read_mem(paddr_t, void *, size_t);
int intr_ack(struct vmd_vm *);
void *);
void vm_pipe_send(struct vm_dev_pipe *, enum pipe_msg_type);
enum pipe_msg_type vm_pipe_recv(struct vm_dev_pipe *);
+int write_mem(paddr_t, const void *buf, size_t);
int remap_guest_mem(struct vmd_vm *, int);
__dead void vm_shutdown(unsigned int);
/* vioblk.c */
__dead void vioblk_main(int, int);
+/* psp.c */
+int psp_get_pstate(uint16_t *);
+int psp_df_flush(void);
+int psp_get_gstate(uint32_t, uint32_t *, uint32_t *, uint8_t *);
+int psp_launch_start(uint32_t *);
+int psp_launch_update(uint32_t, vaddr_t, size_t);
+int psp_launch_measure(uint32_t);
+int psp_launch_finish(uint32_t);
+int psp_activate(uint32_t, uint32_t);
+int psp_guest_shutdown(uint32_t);
+
+/* sev.c */
+int sev_init(struct vmd_vm *);
+int sev_register_encryption(vaddr_t, size_t);
+int sev_encrypt_memory(struct vmd_vm *);
+int sev_activate(struct vmd_vm *, int);
+int sev_shutdown(struct vmd_vm *);
+
#endif /* VMD_H */
-/* $OpenBSD: vmm.c,v 1.121 2024/07/10 09:27:33 dv Exp $ */
+/* $OpenBSD: vmm.c,v 1.122 2024/09/11 15:42:52 bluhm Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
/* Get and terminate all running VMs */
get_info_vm(ps, NULL, 1);
break;
+ case IMSG_VMDOP_RECEIVE_PSP_FD:
+ if (env->vmd_psp_fd > -1)
+ fatalx("already received psp fd");
+ env->vmd_psp_fd = imsg->fd;
+ break;
default:
return (-1);
}
{
struct vm_create_params *vcp;
struct vmd_vm *vm;
- char *nargv[8], num[32], vmm_fd[32];
+ char *nargv[10], num[32], vmm_fd[32], psp_fd[32];
int fd, ret = EINVAL;
int fds[2];
pid_t vm_pid;
close(fd);
}
+ if (env->vmd_psp_fd > 0)
+ fcntl(env->vmd_psp_fd, F_SETFD, 0); /* psp device fd */
+
/*
* Prepare our new argv for execvp(2) with the fd of our open
* pipe to the parent/vmm process as an argument.
snprintf(num, sizeof(num), "%d", fds[1]);
memset(vmm_fd, 0, sizeof(vmm_fd));
snprintf(vmm_fd, sizeof(vmm_fd), "%d", env->vmd_fd);
+ memset(psp_fd, 0, sizeof(psp_fd));
+ snprintf(psp_fd, sizeof(psp_fd), "%d", env->vmd_psp_fd);
nargv[0] = env->argv0;
nargv[1] = "-V";
nargv[3] = "-n";
nargv[4] = "-i";
nargv[5] = vmm_fd;
- nargv[6] = NULL;
+ nargv[6] = "-j";
+ nargv[7] = psp_fd;
+ nargv[8] = NULL;
if (env->vmd_verbose == 1) {
- nargv[6] = VMD_VERBOSE_1;
- nargv[7] = NULL;
+ nargv[8] = VMD_VERBOSE_1;
+ nargv[9] = NULL;
} else if (env->vmd_verbose > 1) {
- nargv[6] = VMD_VERBOSE_2;
- nargv[7] = NULL;
+ nargv[8] = VMD_VERBOSE_2;
+ nargv[9] = NULL;
}
/* Control resumes in vmd main(). */
-/* $OpenBSD: x86_vm.c,v 1.2 2024/07/12 13:51:12 dv Exp $ */
+/* $OpenBSD: x86_vm.c,v 1.3 2024/09/11 15:42:52 bluhm Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
*
void create_memory_map(struct vm_create_params *);
int translate_gva(struct vm_exit*, uint64_t, uint64_t *, int);
-static struct vm_mem_range *find_gpa_range(struct vm_create_params *, paddr_t,
- size_t);
static int loadfile_bios(gzFile, off_t, struct vcpu_reg_state *);
static int vcpu_exit_eptviolation(struct vm_run_params *);
static void vcpu_exit_inout(struct vm_run_params *);
* NULL: on failure if there is no memory range as described by the parameters
* Pointer to vm_mem_range that contains the start of the range otherwise.
*/
-static struct vm_mem_range *
+struct vm_mem_range *
find_gpa_range(struct vm_create_params *vcp, paddr_t gpa, size_t len)
{
size_t i, n;