freebsd-src/usr.sbin/bhyve/tpm_ppi_qemu.c
Pierre Pronchery f7d45c5443 bhyve: avoid side effect in assertion
An assert() was setting the error variable instead of checking it.

Reported by:	Coverity Scan
CID:		1521431
Reviewed by:	jhb
Sponsored by:	The FreeBSD Foundation
Pull Request:	https://github.com/freebsd/freebsd-src/pull/1244
2024-05-17 15:45:18 -04:00

476 lines
12 KiB
C

/*-
* 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 0x1000
#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, sizeof(*ppi));
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 + 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);