/* Support for generating ACPI TPM tables
*
* Copyright (C) 2018 IBM, Corp.
* Copyright (C) 2018 Red Hat Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/acpi/tpm.h"
void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev)
{
Aml *method, *field, *ifctx, *ifctx2, *ifctx3, *func_mask,
*not_implemented, *pak, *tpm2, *tpm3, *pprm, *pprq, *zero, *one;
if (!object_property_get_bool(OBJECT(tpm), "ppi", &error_abort)) {
return;
}
zero = aml_int(0);
one = aml_int(1);
func_mask = aml_int(TPM_PPI_FUNC_MASK);
not_implemented = aml_int(TPM_PPI_FUNC_NOT_IMPLEMENTED);
/*
* TPP2 is for the registers that ACPI code used to pass
* the PPI code and parameter (PPRQ, PPRM) to the firmware.
*/
aml_append(dev,
aml_operation_region("TPP2", AML_SYSTEM_MEMORY,
aml_int(TPM_PPI_ADDR_BASE + 0x100),
0x5A));
field = aml_field("TPP2", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE);
aml_append(field, aml_named_field("PPIN", 8));
aml_append(field, aml_named_field("PPIP", 32));
aml_append(field, aml_named_field("PPRP", 32));
aml_append(field, aml_named_field("PPRQ", 32));
aml_append(field, aml_named_field("PPRM", 32));
aml_append(field, aml_named_field("LPPR", 32));
aml_append(dev, field);
pprq = aml_name("PPRQ");
pprm = aml_name("PPRM");
aml_append(dev,
aml_operation_region(
"TPP3", AML_SYSTEM_MEMORY,
aml_int(TPM_PPI_ADDR_BASE +
0x15a /* movv, docs/specs/tpm.txt */),
0x1));
field = aml_field("TPP3", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
aml_append(field, aml_named_field("MOVV", 8));
aml_append(dev, field);
/*
* DerefOf in Windows is broken with SYSTEM_MEMORY. Use a dynamic
* operation region inside of a method for getting FUNC[op].
*/
method = aml_method("TPFN", 1, AML_SERIALIZED);
{
Aml *op = aml_arg(0);
ifctx = aml_if(aml_lgreater_equal(op, aml_int(0x100)));
{
aml_append(ifctx, aml_return(zero));
}
aml_append(method, ifctx);
aml_append(method,
aml_operation_region("TPP1", AML_SYSTEM_MEMORY,
aml_add(aml_int(TPM_PPI_ADDR_BASE), op, NULL), 0x1));
field = aml_field("TPP1", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
aml_append(field, aml_named_field("TPPF", 8));
aml_append(method, field);
aml_append(method, aml_return(aml_name("TPPF")));
}
aml_append(dev, method);
/*
* Use global TPM2 & TPM3 variables to workaround Windows ACPI bug
* when returning packages.
*/
pak = aml_package(2);
aml_append(pak, zero);
aml_append(pak, zero);
aml_append(dev, aml_name_decl("TPM2", pak));
tpm2 = aml_name("TPM2");
pak = aml_package(3);
aml_append(pak, zero);
aml_append(pak, zero);
aml_append(pak, zero);
aml_append(dev, aml_name_decl("TPM3", pak));
tpm3 = aml_name("TPM3");
method = aml_method("_DSM", 4, AML_SERIALIZED);
{
uint8_t zerobyte[1] = { 0 };
Aml *function, *arguments, *rev, *op, *op_arg, *op_flags, *uuid;
uuid = aml_arg(0);
rev = aml_arg(1);
function = aml_arg(2);
arguments = aml_arg(3);
op = aml_local(0);
op_flags = aml_local(1);
/* Physical Presence Interface */
ifctx = aml_if(
aml_equal(uuid,
aml_touuid("3DDDFAA6-361B-4EB4-A424-8D10089D1653")));
{
/* standard DSM query function */
ifctx2 = aml_if(aml_equal(function, zero));
{
uint8_t byte_list[2] = { 0xff, 0x01 }; /* functions 1-8 */
aml_append(ifctx2,
aml_return(aml_buffer(sizeof(byte_list),
byte_list)));
}
aml_append(ifctx, ifctx2);
/*
* PPI 1.0: 2.1.1 Get Physical Presence Interface Version
*
* Arg 2 (Integer): Function Index = 1
* Arg 3 (Package): Arguments = Empty Package
* Returns: Type: String
*/
ifctx2 = aml_if(aml_equal(function, one));
{
aml_append(ifctx2, aml_return(aml_string("1.3")));
}
aml_append(ifctx, ifctx2);
/*
* PPI 1.0: 2.1.3 Submit TPM Operation Request to Pre-OS Environment
*
* Arg 2 (Integer): Function Index = 2
* Arg 3 (Package): Arguments = Package: Type: Integer
* Operation Value of the Request
* Returns: Type: Integer
* 0: Success
* 1: Operation Value of the Request Not Supported
* 2: General Failure
*/
ifctx2 = aml_if(aml_equal(function, aml_int(2)));
{
/* get opcode */
aml_append(ifctx2,
aml_store(aml_derefof(aml_index(arguments,
zero)), op));
/* get opcode flags */
aml_append(ifctx2,
aml_store(aml_call1("TPFN", op), op_flags));
/* if func[opcode] & TPM_PPI_FUNC_NOT_IMPLEMENTED */
ifctx3 = aml_if(
aml_equal(
aml_and(op_flags, func_mask, NULL),
not_implemented));
{
/* 1: Operation Value of the Request Not Supported */
aml_append(ifctx3, aml_return(one));
}
aml_append(ifctx2, ifctx3);
aml_append(ifctx2, aml_store(op, pprq));
aml_append(ifctx2, aml_store(zero, pprm));
/* 0: success */
aml_append(ifctx2, aml_return(zero));
}
aml_append(ifctx, ifctx2);
/*
* PPI 1.0: 2.1.4 Get Pending TPM Operation Requested By the OS
*
* Arg 2 (Integer): Function Index = 3
* Arg 3 (Package): Arguments = Empty Package
* Returns: Type: Package of Integers
* 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 Value of the Pending Request
*/
ifctx2 = aml_if(aml_equal(function, aml_int(3)));
{
/*
* Revision ID of 1, no integer parameter beyond
* parameter two are expected
*/
ifctx3 = aml_if(aml_equal(rev, one));
{
/* TPM2[1] = PPRQ */
aml_append(ifctx3,
aml_store(pprq, aml_index(tpm2, one)));
aml_append(ifctx3, aml_return(tpm2));
}
aml_append(ifctx2, ifctx3);
/*
* A return value of {0, 23, 1} indicates that
* operation 23 with argument 1 is pending.
*/
ifctx3 = aml_if(aml_equal(rev, aml_int(2)));
{
/* TPM3[1] = PPRQ */
aml_append(ifctx3,
aml_store(pprq, aml_index(tpm3, one)));
/* TPM3[2] = PPRM */
aml_append(ifctx3,
aml_store(pprm, aml_index(tpm3, aml_int(2))));
aml_append(ifctx3, aml_return(tpm3));
}
aml_append(ifctx2, ifctx3);
}
aml_append(ifctx, ifctx2);
/*
* PPI 1.0: 2.1.5 Get Platform-Specific Action to Transition to
* Pre-OS Environment
*
* Arg 2 (Integer): Function Index = 4
* Arg 3 (Package): Arguments = Empty Package
* Returns: Type: Integer
* 0: None
* 1: Shutdown
* 2: Reboot
* 3: OS Vendor-specific
*/
ifctx2 = aml_if(aml_equal(function, aml_int(4)));
{
/* reboot */
aml_append(ifctx2, aml_return(aml_int(2)));
}
aml_append(ifctx, ifctx2);
/*
* PPI 1.0: 2.1.6 Return TPM Operation Response to OS Environment
*
* Arg 2 (Integer): Function Index = 5
* Arg 3 (Package): Arguments = Empty Package
* Returns: Type: Package of Integer
* 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..0x00000FFF: Corresponding TPM
* error code
* 0xFFFFFFF0: User Abort or timeout of dialog
* 0xFFFFFFF1: firmware Failure
*/
ifctx2 = aml_if(aml_equal(function, aml_int(5)));
{
/* TPM3[1] = LPPR */
aml_append(ifctx2,
aml_store(aml_name("LPPR"),
aml_index(tpm3, one)));
/* TPM3[2] = PPRP */
aml_append(ifctx2,
aml_store(aml_name("PPRP"),
aml_index(tpm3, aml_int(2))));
aml_append(ifctx2, aml_return(tpm3));
}
aml_append(ifctx, ifctx2);
/*
* PPI 1.0: 2.1.7 Submit preferred user language
*
* Arg 2 (Integer): Function Index = 6
* Arg 3 (Package): Arguments = String Package
* Preferred language code
* Returns: Type: Integer
* Function Return Code
* 3: Not implemented
*/
ifctx2 = aml_if(aml_equal(function, aml_int(6)));
{
/* 3 = not implemented */
aml_append(ifctx2, aml_return(aml_int(3)));
}
aml_append(ifctx, ifctx2);
/*
* PPI 1.1: 2.1.7 Submit TPM Operation Request to
* Pre-OS Environment 2
*
* Arg 2 (Integer): Function Index = 7
* Arg 3 (Package): Arguments = Package: Type: Integer
* Integer 1: Operation Value of the Request
* Integer 2: Argument for Operation (optional)
* Returns: Type: Integer
* 0: Success
* 1: Not Implemented
* 2: General Failure
* 3: Operation blocked by current firmware settings
*/
ifctx2 = aml_if(aml_equal(function, aml_int(7)));
{
/* get opcode */
aml_append(ifctx2, aml_store(aml_derefof(aml_index(arguments,
zero)),
op));
/* get opcode flags */
aml_append(ifctx2, aml_store(aml_call1("TPFN", op),
op_flags));
/* if func[opcode] & TPM_PPI_FUNC_NOT_IMPLEMENTED */
ifctx3 = aml_if(
aml_equal(
aml_and(op_flags, func_mask, NULL),
not_implemented));
{
/* 1: not implemented */
aml_append(ifctx3, aml_return(one));
}
aml_append(ifctx2, ifctx3);
/* if func[opcode] & TPM_PPI_FUNC_BLOCKED */
ifctx3 = aml_if(
aml_equal(
aml_and(op_flags, func_mask, NULL),
aml_int(TPM_PPI_FUNC_BLOCKED)));
{
/* 3: blocked by firmware */
aml_append(ifctx3, aml_return(aml_int(3)));
}
aml_append(ifctx2, ifctx3);
/* revision to integer */
ifctx3 = aml_if(aml_equal(rev, one));
{
/* revision 1 */
/* PPRQ = op */
aml_append(ifctx3, aml_store(op, pprq));
/* no argument, PPRM = 0 */
aml_append(ifctx3, aml_store(zero, pprm));
}
aml_append(ifctx2, ifctx3);
ifctx3 = aml_if(aml_equal(rev, aml_int(2)));
{
/* revision 2 */
/* PPRQ = op */
op_arg = aml_derefof(aml_index(arguments, one));
aml_append(ifctx3, aml_store(op, pprq));
/* PPRM = arg3[1] */
aml_append(ifctx3, aml_store(op_arg, pprm));
}
aml_append(ifctx2, ifctx3);
/* 0: success */
aml_append(ifctx2, aml_return(zero));
}
aml_append(ifctx, ifctx2);
/*
* PPI 1.1: 2.1.8 Get User Confirmation Status for Operation
*
* Arg 2 (Integer): Function Index = 8
* Arg 3 (Package): Arguments = Package: Type: Integer
* Operation Value that may need user confirmation
* Returns: Type: Integer
* 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
*/
ifctx2 = aml_if(aml_equal(function, aml_int(8)));
{
/* get opcode */
aml_append(ifctx2,
aml_store(aml_derefof(aml_index(arguments,
zero)),
op));
/* get opcode flags */
aml_append(ifctx2, aml_store(aml_call1("TPFN", op),
op_flags));
/* return confirmation status code */
aml_append(ifctx2,
aml_return(
aml_and(op_flags, func_mask, NULL)));
}
aml_append(ifctx, ifctx2);
aml_append(ifctx, aml_return(aml_buffer(1, zerobyte)));
}
aml_append(method, ifctx);
/*
* "TCG Platform Reset Attack Mitigation Specification 1.00",
* Chapter 6 "ACPI _DSM Function"
*/
ifctx = aml_if(
aml_equal(uuid,
aml_touuid("376054ED-CC13-4675-901C-4756D7F2D45D")));
{
/* standard DSM query function */
ifctx2 = aml_if(aml_equal(function, zero));
{
uint8_t byte_list[1] = { 0x03 }; /* functions 1-2 supported */
aml_append(ifctx2,
aml_return(aml_buffer(sizeof(byte_list),
byte_list)));
}
aml_append(ifctx, ifctx2);
/*
* TCG Platform Reset Attack Mitigation Specification 1.0 Ch.6
*
* Arg 2 (Integer): Function Index = 1
* Arg 3 (Package): Arguments = Package: Type: Integer
* Operation Value of the Request
* Returns: Type: Integer
* 0: Success
* 1: General Failure
*/
ifctx2 = aml_if(aml_equal(function, one));
{
aml_append(ifctx2,
aml_store(aml_derefof(aml_index(arguments, zero)),
op));
{
aml_append(ifctx2, aml_store(op, aml_name("MOVV")));
/* 0: success */
aml_append(ifctx2, aml_return(zero));
}
}
aml_append(ifctx, ifctx2);
}
aml_append(method, ifctx);
}
aml_append(dev, method);
}