aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/bhyve/tpm_ppi_qemu.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bhyve/tpm_ppi_qemu.c')
-rw-r--r--usr.sbin/bhyve/tpm_ppi_qemu.c475
1 files changed, 475 insertions, 0 deletions
diff --git a/usr.sbin/bhyve/tpm_ppi_qemu.c b/usr.sbin/bhyve/tpm_ppi_qemu.c
new file mode 100644
index 000000000000..6974b574b983
--- /dev/null
+++ b/usr.sbin/bhyve/tpm_ppi_qemu.c
@@ -0,0 +1,475 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/linker_set.h>
+
+#include <machine/vmm.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <vmmapi.h>
+
+#include "acpi.h"
+#include "acpi_device.h"
+#include "config.h"
+#include "mem.h"
+#include "qemu_fwcfg.h"
+#include "tpm_ppi.h"
+
+#define TPM_PPI_ADDRESS 0xFED45000
+#define TPM_PPI_SIZE 0x400
+
+#define TPM_PPI_FWCFG_FILE "etc/tpm/config"
+
+#define TPM_PPI_QEMU_NAME "qemu"
+
+struct tpm_ppi_qemu {
+ uint8_t func[256]; // FUNC
+ uint8_t in; // PPIN
+ uint32_t ip; // PPIP
+ uint32_t response; // PPRP
+ uint32_t request; // PPRQ
+ uint32_t request_parameter; // PPRM
+ uint32_t last_request; // LPPR
+ uint32_t func_ret; // FRET
+ uint8_t _reserved1[0x40]; // RES1
+ uint8_t next_step; // next_step
+} __packed;
+static_assert(sizeof(struct tpm_ppi_qemu) <= TPM_PPI_SIZE,
+ "Wrong size of tpm_ppi_qemu");
+
+struct tpm_ppi_fwcfg {
+ uint32_t ppi_address;
+ uint8_t tpm_version;
+ uint8_t ppi_version;
+} __packed;
+
+static int
+tpm_ppi_mem_handler(struct vcpu *const vcpu __unused, const int dir,
+ const uint64_t addr, const int size, uint64_t *const val, void *const arg1,
+ const long arg2 __unused)
+{
+ struct tpm_ppi_qemu *ppi;
+ uint8_t *ptr;
+ uint64_t off;
+
+ if ((addr & (size - 1)) != 0) {
+ warnx("%s: unaligned %s access @ %16lx [size = %x]", __func__,
+ (dir == MEM_F_READ) ? "read" : "write", addr, size);
+ }
+
+ ppi = arg1;
+
+ off = addr - TPM_PPI_ADDRESS;
+ ptr = (uint8_t *)ppi + off;
+
+ if (off > TPM_PPI_SIZE || off + size > TPM_PPI_SIZE) {
+ return (EINVAL);
+ }
+
+ assert(size == 1 || size == 2 || size == 4 || size == 8);
+ if (dir == MEM_F_READ) {
+ memcpy(val, ptr, size);
+ } else {
+ memcpy(ptr, val, size);
+ }
+
+ return (0);
+}
+
+static struct mem_range ppi_mmio = {
+ .name = "ppi-mmio",
+ .base = TPM_PPI_ADDRESS,
+ .size = TPM_PPI_SIZE,
+ .flags = MEM_F_RW,
+ .handler = tpm_ppi_mem_handler,
+};
+
+static int
+tpm_ppi_init(void **sc)
+{
+ struct tpm_ppi_qemu *ppi = NULL;
+ struct tpm_ppi_fwcfg *fwcfg = NULL;
+ int error;
+
+ ppi = calloc(1, TPM_PPI_SIZE);
+ if (ppi == NULL) {
+ warnx("%s: failed to allocate acpi region for ppi", __func__);
+ error = ENOMEM;
+ goto err_out;
+ }
+
+ fwcfg = calloc(1, sizeof(struct tpm_ppi_fwcfg));
+ if (fwcfg == NULL) {
+ warnx("%s: failed to allocate fwcfg item", __func__);
+ error = ENOMEM;
+ goto err_out;
+ }
+
+ fwcfg->ppi_address = htole32(TPM_PPI_ADDRESS);
+ fwcfg->tpm_version = 2;
+ fwcfg->ppi_version = 1;
+
+ error = qemu_fwcfg_add_file(TPM_PPI_FWCFG_FILE,
+ sizeof(struct tpm_ppi_fwcfg), fwcfg);
+ if (error) {
+ warnx("%s: failed to add fwcfg file", __func__);
+ goto err_out;
+ }
+
+ /*
+ * We would just need to create some guest memory for the PPI region.
+ * Sadly, bhyve has a strange memory interface. We can't just add more
+ * memory to the VM. So, create a trap instead which reads and writes to
+ * the ppi region. It's very slow but ppi shouldn't be used frequently.
+ */
+ ppi_mmio.arg1 = ppi;
+ error = register_mem(&ppi_mmio);
+ if (error) {
+ warnx("%s: failed to create trap for ppi accesses", __func__);
+ goto err_out;
+ }
+
+ *sc = ppi;
+
+ return (0);
+
+err_out:
+ free(fwcfg);
+ free(ppi);
+
+ return (error);
+}
+
+static void
+tpm_ppi_deinit(void *sc)
+{
+ struct tpm_ppi_qemu *ppi;
+ int error;
+
+ if (sc == NULL)
+ return;
+
+ ppi = sc;
+
+ error = unregister_mem(&ppi_mmio);
+ assert(error == 0);
+
+ free(ppi);
+}
+
+static int
+tpm_ppi_write_dsdt_regions(void *sc __unused)
+{
+ /*
+ * struct tpm_ppi_qemu
+ */
+ /*
+ * According to qemu the Windows ACPI parser has a bug that DerefOf is
+ * broken for SYSTEM_MEMORY. Due to that bug, qemu uses a dynamic
+ * operation region inside a method.
+ */
+ dsdt_line("Method(TPFN, 1, Serialized)");
+ dsdt_line("{");
+ dsdt_line(" If(LGreaterEqual(Arg0, 0x100))");
+ dsdt_line(" {");
+ dsdt_line(" Return(Zero)");
+ dsdt_line(" }");
+ dsdt_line(
+ " OperationRegion(TPP1, SystemMemory, Add(0x%8x, Arg0), One)",
+ TPM_PPI_ADDRESS);
+ dsdt_line(" Field(TPP1, ByteAcc, NoLock, Preserve)");
+ dsdt_line(" {");
+ dsdt_line(" TPPF, 8,");
+ dsdt_line(" }");
+ dsdt_line(" Return(TPPF)");
+ dsdt_line("}");
+ dsdt_line("OperationRegion(TPP2, SystemMemory, 0x%8x, 0x%x)",
+ TPM_PPI_ADDRESS + 0x100, 0x5A);
+ dsdt_line("Field(TPP2, AnyAcc, NoLock, Preserve)");
+ dsdt_line("{");
+ dsdt_line(" PPIN, 8,");
+ dsdt_line(" PPIP, 32,");
+ dsdt_line(" PPRP, 32,");
+ dsdt_line(" PPRQ, 32,");
+ dsdt_line(" PPRM, 32,");
+ dsdt_line(" LPPR, 32,");
+ dsdt_line("}");
+ /*
+ * Used for TCG Platform Reset Attack Mitigation
+ */
+ dsdt_line("OperationRegion(TPP3, SystemMemory, 0x%8x, 1)",
+ TPM_PPI_ADDRESS + (uint32_t)sizeof(struct tpm_ppi_qemu));
+ dsdt_line("Field(TPP3, ByteAcc, NoLock, Preserve)");
+ dsdt_line("{");
+ dsdt_line(" MOVV, 8,");
+ dsdt_line("}");
+
+ return (0);
+}
+
+static int
+tpm_ppi_write_dsdt_dsm(void *sc __unused)
+{
+ /*
+ * Physical Presence Interface
+ */
+ dsdt_line(
+ "If(LEqual(Arg0, ToUUID(\"3DDDFAA6-361B-4EB4-A424-8D10089D1653\"))) /* UUID */");
+ dsdt_line("{");
+ /*
+ * Function 0 - _DSM Query Function
+ * Arguments:
+ * Empty Package
+ * Return:
+ * Buffer - Index field of supported functions
+ */
+ dsdt_line(" If(LEqual(Arg2, 0)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" Return(Buffer(0x02)");
+ dsdt_line(" {");
+ dsdt_line(" 0xFF, 0x01");
+ dsdt_line(" })");
+ dsdt_line(" }");
+ /*
+ * Function 1 - Get Physical Presence Interface Version
+ * Arguments:
+ * Empty Package
+ * Return:
+ * String - Supported Physical Presence Interface revision
+ */
+ dsdt_line(" If(LEqual(Arg2, 1)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" Return(\"1.3\")");
+ dsdt_line(" }");
+ /*
+ * Function 2 - Submit TPM Operation Request to Pre-OS Environment
+ * !!!DEPRECATED BUT MANDATORY!!!
+ * Arguments:
+ * Integer - Operation Value of the Request
+ * Return:
+ * Integer - Function Return Code
+ * 0 - Success
+ * 1 - Operation Value of the Request Not Supported
+ * 2 - General Failure
+ */
+ dsdt_line(" If(LEqual(Arg2, 2)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" Store(DerefOf(Index(Arg3, 0)), Local0)");
+ dsdt_line(" Store(TPFN(Local0), Local1)");
+ dsdt_line(" If (LEqual(And(Local1, 7), 0))");
+ dsdt_line(" {");
+ dsdt_line(" Return(1)");
+ dsdt_line(" }");
+ dsdt_line(" Store(Local0, PPRQ)");
+ dsdt_line(" Store(0, PPRM)");
+ dsdt_line(" Return(0)");
+ dsdt_line(" }");
+ /*
+ * Function 3 - Get Pending TPM Operation Request By the OS
+ * Arguments:
+ * Empty Package
+ * Return:
+ * Package
+ * Integer 1 - Function Return Code
+ * 0 - Success
+ * 1 - General Failure
+ * Integer 2 - Pending operation requested by the OS
+ * 0 - None
+ * >0 - Operation Value of the Pending Request
+ * Integer 3 - Optional argument to pending operation requested by
+ * the OS
+ * 0 - None
+ * >0 - Argument of the Pending Request
+ */
+ dsdt_line(" If(LEqual(Arg2, 3)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" If(LEqual(Arg1, 1)) /* Revision */");
+ dsdt_line(" {");
+ dsdt_line(" Store(PPRQ, Index(TPM2, 1))");
+ dsdt_line(" Return(TPM2)");
+ dsdt_line(" }");
+ dsdt_line(" If(LEqual(Arg1, 2)) /* Revision */");
+ dsdt_line(" {");
+ dsdt_line(" Store(PPRQ, Index(TPM3, 1))");
+ dsdt_line(" Store(PPRM, Index(TPM3, 2))");
+ dsdt_line(" Return(TPM3)");
+ dsdt_line(" }");
+ dsdt_line(" }");
+ /*
+ * Function 4 - Get Platform-Specific Action to Transition to Pre-OS
+ * Environment
+ * Arguments:
+ * Empty Package
+ * Return:
+ * Integer - Action that the OS should take to transition to the
+ * pre-OS environment for execution of a requested operation
+ * 0 - None
+ * 1 - Shutdown
+ * 2 - Reboot
+ * 3 - OS Vendor-specific
+ */
+ dsdt_line(" If(LEqual(Arg2, 4)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" Return(2)");
+ dsdt_line(" }");
+ /*
+ * Function 5 - Return TPM Operation Response to OS Environment
+ * Arguments:
+ * Empty Package
+ * Return:
+ * Package
+ * Integer 1 - Function Return Code
+ * 0 - Success
+ * 1 - General Failure
+ * Integer 2 - Most recent operation request
+ * 0 - None
+ * >0 - Operation value of the most recent request
+ * Integer 3 - Response to the most recent operation request
+ * 0 - Success
+ * 0x00000001..0x000000FF - Corresponding TPM error code
+ * 0xFFFFFFF0 - User Abort or timeout of dialog
+ * 0xFFFFFFF1 - firmware failure
+ */
+ dsdt_line(" If(LEqual(Arg2, 5)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" Store(LPPR, Index(TPM3, 1))");
+ dsdt_line(" Store(PPRP, Index(TPM3, 2))");
+ dsdt_line(" Return(TPM3)");
+ dsdt_line(" }");
+ /*
+ * Function 6 - Submit preferred user language
+ * !!!DEPRECATED BUT MANDATORY!!!
+ * Arguments:
+ * Package
+ * String - Preferred language code
+ * Return:
+ * Integer
+ * 3 - Not implemented
+ */
+ dsdt_line(" If(LEqual(Arg2, 6)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" Return(3)");
+ dsdt_line(" }");
+ /*
+ * Function 7 - Submit TPM Operation Request to Pre-OS Environment 2
+ * Arguments:
+ * Package
+ * Integer 1 - Operation Value of the Request
+ * Integer 2 - Argument for Operation
+ * Return:
+ * Integer - Function Return Code
+ * 0 - Success
+ * 1 - Not Implemented
+ * 2 - General Failure
+ * 3 - Operation blocked by current firmware settings
+ */
+ dsdt_line(" If(LEqual(Arg2, 7)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" Store(DerefOf(Index(Arg3, 0)), Local0)");
+ dsdt_line(" Store(TPFN(Local0), Local1)");
+ dsdt_line(" If (LEqual(And(Local1, 7), 0)) /* Not Implemented */");
+ dsdt_line(" {");
+ dsdt_line(" Return(1)");
+ dsdt_line(" }");
+ dsdt_line(" If (LEqual(And(Local1, 7), 2)) /* Blocked */ ");
+ dsdt_line(" {");
+ dsdt_line(" Return(3)");
+ dsdt_line(" }");
+ dsdt_line(" If(LEqual(Arg1, 1)) /* Revision */");
+ dsdt_line(" {");
+ dsdt_line(" Store(Local0, PPRQ)");
+ dsdt_line(" Store(0, PPRM)");
+ dsdt_line(" }");
+ dsdt_line(" If(LEqual(Arg1, 2)) /* Revision */");
+ dsdt_line(" {");
+ dsdt_line(" Store(Local0, PPRQ)");
+ dsdt_line(" Store(DerefOf(Index(Arg3, 1)), PPRM)");
+ dsdt_line(" }");
+ dsdt_line(" Return(0)");
+ dsdt_line(" }");
+ /*
+ * Function 8 - Get User Confirmation Status for Operation
+ * Arguments:
+ * Package
+ * Integer - Operation Value that may need user confirmation
+ * Return:
+ * Integer - Function Return Code
+ * 0 - Not implemented
+ * 1 - Firmware only
+ * 2 - Blocked for OS by firmware configuration
+ * 3 - Allowed and physically present user required
+ * 4 - Allowed and physically present user not required
+ */
+ dsdt_line(" If(LEqual(Arg2, 8)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" Store(DerefOf(Index(Arg3, 0)), Local0)");
+ dsdt_line(" Store(TPFN(Local0), Local1)");
+ dsdt_line(" Return(And(Local1, 7))");
+ dsdt_line(" }");
+ /*
+ * Unknown function
+ */
+ dsdt_line(" Return(Buffer(1)");
+ dsdt_line(" {");
+ dsdt_line(" 0x00");
+ dsdt_line(" })");
+ dsdt_line("}");
+
+ /*
+ * TCG Platform Reset Attack Mitigation
+ */
+ dsdt_line(
+ "If(LEqual(Arg0, ToUUID(\"376054ED-CC13-4675-901C-4756D7F2D45D\"))) /* UUID */");
+ dsdt_line("{");
+ /*
+ * Function 0 - _DSM Query Function
+ * Arguments:
+ * Empty Package
+ * Return:
+ * Buffer - Index field of supported functions
+ */
+ dsdt_line(" If(LEqual(Arg2, 0)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" Return(Buffer(1)");
+ dsdt_line(" {");
+ dsdt_line(" 0x03");
+ dsdt_line(" })");
+ dsdt_line(" }");
+ /*
+ * Function 1 - Memory Clear
+ * Arguments:
+ * Package
+ * Integer - Operation Value of the Request
+ * Return:
+ * Integer - Function Return Code
+ * 0 - Success
+ * 1 - General Failure
+ */
+ dsdt_line(" If(LEqual(Arg2, 1)) /* Function */");
+ dsdt_line(" {");
+ dsdt_line(" Store(DerefOf(Index(Arg3, 0)), Local0)");
+ dsdt_line(" Store(Local0, MOVV)");
+ dsdt_line(" Return(0)");
+ dsdt_line(" }");
+ dsdt_line("}");
+
+ return (0);
+}
+
+static struct tpm_ppi tpm_ppi_qemu = {
+ .name = TPM_PPI_QEMU_NAME,
+ .init = tpm_ppi_init,
+ .deinit = tpm_ppi_deinit,
+ .write_dsdt_regions = tpm_ppi_write_dsdt_regions,
+ .write_dsdt_dsm = tpm_ppi_write_dsdt_dsm,
+};
+TPM_PPI_SET(tpm_ppi_qemu);