|  | /* 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.rst */), | 
|  | 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); | 
|  | } |