diff options
-rw-r--r-- | src/drivers/pc80/tpm/acpi/tpm.asl | 219 | ||||
-rw-r--r-- | src/drivers/pc80/tpm/tpm.c | 248 |
2 files changed, 247 insertions, 220 deletions
diff --git a/src/drivers/pc80/tpm/acpi/tpm.asl b/src/drivers/pc80/tpm/acpi/tpm.asl index 090bf4c820..e69de29bb2 100644 --- a/src/drivers/pc80/tpm/acpi/tpm.asl +++ b/src/drivers/pc80/tpm/acpi/tpm.asl @@ -1,219 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright (C) 2014 Google 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; version 2 of the License. - * - * 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. - */ - -/* Trusted Platform Module */ - -#if CONFIG_LPC_TPM - -Device (TPM) -{ - Name (_HID, EISAID ("PNP0C31")) - Name (_CID, 0x310cd041) - Name (_UID, 1) - - OperationRegion (TREG, SystemMemory, - CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000) - Field (TREG, ByteAcc, NoLock, Preserve) - { - /* TPM_INT_ENABLE_0 */ - Offset (0x0008), - , 3, - ITPL, 2, /* Interrupt type and polarity */ - - /* TPM_INT_VECTOR_0 */ - Offset (0x000C), - IVEC, 4, /* SERIRQ vector */ - - /* TPM_DID_VID */ - Offset (0x0f00), - DVID, 32, /* Device and vendor ID */ - } - - Method (_STA, 0) - { -#if !CONFIG_TPM_DEACTIVATE - If (LAnd (LGreater (DVID, 0), LLess (DVID, 0xffffffff))) { - Return (0xf) - } Else { - /* TPM module missing */ - Return (0x0) - } -#else - Return (0x0) -#endif - } - - Name (IBUF, ResourceTemplate () - { - /* Updated based on TPM interrupt for Locality 0 */ - Interrupt (ResourceConsumer, Edge, ActiveHigh, - Exclusive, , , TIRQ) { 0 } - }) - - Name (RBUF, ResourceTemplate () - { - IO (Decode16, 0x2e, 0x2e, 0x01, 0x02) - Memory32Fixed (ReadWrite, CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000) - }) - - Method (_CRS, 0, Serialized) - { - CreateField (^IBUF, ^TIRQ._INT, 32, TVEC) - CreateBitField (^IBUF, ^TIRQ._HE, TTYP) - CreateBitField (^IBUF, ^TIRQ._LL, TPOL) - CreateBitField (^IBUF, ^TIRQ._SHR, TSHR) - - If (LGreater (CONFIG_TPM_PIRQ, 0)) { - /* - * PIRQ: Update interrupt vector with configured PIRQ - */ - Store (CONFIG_TPM_PIRQ, TVEC) - - /* Active-Low Level-Triggered Shared */ - Store (One, TPOL) - Store (Zero, TTYP) - Store (One, TSHR) - - /* Merge IRQ with base address */ - Return (ConcatenateResTemplate (RBUF, IBUF)) - } ElseIf (LGreater (IVEC, 0)) { - /* - * SERIRQ: Update interrupt vector based on TPM register - */ - Store (IVEC, TVEC) - - If (LEqual (ITPL, 0x0)) { - /* Active-High Level-Triggered Shared */ - Store (Zero, TPOL) - Store (Zero, TTYP) - Store (One, TSHR) - } ElseIf (LEqual (ITPL, 0x1)) { - /* Active-Low Level-Triggered Shared */ - Store (One, TPOL) - Store (Zero, TTYP) - Store (One, TSHR) - } ElseIf (LEqual (ITPL, 0x2)) { - /* Active-High Edge-Triggered Exclusive */ - Store (Zero, TPOL) - Store (One, TTYP) - Store (Zero, TSHR) - } ElseIf (LEqual (ITPL, 0x3)) { - /* Active-Low Edge-Triggered Exclusive */ - Store (One, TPOL) - Store (One, TTYP) - Store (Zero, TSHR) - } - - /* Merge IRQ with base address */ - Return (ConcatenateResTemplate (RBUF, IBUF)) - } Else { - Return (RBUF) - } - } - - /* Dummy _DSM to make Bitlocker work. */ - Method (_DSM, 4, Serialized) - { - /* Physical presence interface. - This is used to submit commands like "Clear TPM" to - be run at next reboot provided that user confirms them. - Spec allows user to cancel all commands and/or - configure BIOS to reject commands. So we pretend that - user did just this: cancelled everything. If user - really wants to clear TPM the only option now is to do it manually - in payload. - */ - If (LEqual (Arg0, ToUUID ("3dddfaa6-361b-4eb4-a424-8d10089d1653"))) - { - If (LEqual (Arg2, 0)) - { - /* Functions 1-8. */ - Return (Buffer (2) { 0xFF, 0x01 }) - } - - /* Interface version: 1.2 */ - If (LEqual (Arg2, 1)) - { - Return ("1.2") - } - - /* Submit operations: drop on the floor and return success. */ - If (LEqual (Arg2, 2)) - { - Return (0x00) - } - - /* Pending operation: none. */ - If (LEqual (Arg2, 3)) - { - Return (Package (2) { 0, 0 }) - } - - /* Pre-OS transition method: reboot. */ - If (LEqual (Arg2, 4)) - { - Return (2) - } - - /* Operation response: no operation executed. */ - If (LEqual (Arg2, 5)) - { - Return (Package (3) { 0, 0, 0 }) - } - - /* Set preffered user language: deprecated and must return 3 aka "not implemented". */ - If (LEqual (Arg2, 6)) - { - Return (3) - } - - /* Submit operations: deny. */ - If (LEqual (Arg2, 7)) - { - Return (3) - } - - /* All actions are forbidden. */ - If (LEqual (Arg2, 8)) - { - Return (1) - } - - Return (1) - } - - /* Memory clearing on boot: just a dummy. */ - If (LEqual (Arg0, ToUUID("376054ed-cc13-4675-901c-4756d7f2d45d"))) - { - If (LEqual (Arg2, 0)) - { - /* Function 1. */ - Return (Buffer (1) { 3 }) - } - - /* Just return success. */ - If (LEqual (Arg2, 1)) - { - Return (0) - } - - Return (1) - } - - Return (Buffer (1) { 0 }) - } -} - -#endif /* CONFIG_LPC_TPM */ diff --git a/src/drivers/pc80/tpm/tpm.c b/src/drivers/pc80/tpm/tpm.c index fbfb816cdb..57ea919ad1 100644 --- a/src/drivers/pc80/tpm/tpm.c +++ b/src/drivers/pc80/tpm/tpm.c @@ -27,6 +27,10 @@ #include <string.h> #include <delay.h> #include <arch/io.h> +#include <arch/acpi.h> +#include <arch/acpigen.h> +#include <arch/acpi_device.h> +#include <device/device.h> #include <console/console.h> #include <tpm.h> #include <arch/early_variables.h> @@ -34,7 +38,10 @@ #include "chip.h" #define PREFIX "lpc_tpm: " - +/* TCG Physical Presence Interface */ +#define TPM_PPI_UUID "3dddfaa6-361b-4eb4-a424-8d10089d1653" +/* TCG Memory Clear Interface */ +#define TPM_MCI_UUID "376054ed-cc13-4675-901c-4756d7f2d45d" /* coreboot wrapper for TPM driver (start) */ #define TPM_DEBUG(fmt, args...) \ if (IS_ENABLED(CONFIG_DEBUG_TPM)) { \ @@ -211,6 +218,13 @@ static inline void tpm_write_int_vector(int vector, int locality) write8(TIS_REG(locality, TIS_REG_INT_VECTOR), vector & 0xf); } +static inline u8 tpm_read_int_vector(int locality) +{ + u8 value = read8(TIS_REG(locality, TIS_REG_INT_VECTOR)); + TPM_DEBUG_IO_READ(TIS_REG_INT_VECTOR, value); + return value; +} + static inline void tpm_write_int_polarity(int polarity, int locality) { /* Set polarity and leave all other bits at 0 */ @@ -219,6 +233,15 @@ static inline void tpm_write_int_polarity(int polarity, int locality) write32(TIS_REG(locality, TIS_REG_INT_ENABLE), value); } +static inline u32 tpm_read_int_polarity(int locality) +{ + /* Get polarity and leave all other bits */ + u32 value = read8(TIS_REG(locality, TIS_REG_INT_ENABLE)); + value = (value >> 3) & 0x3; + TPM_DEBUG_IO_READ(TIS_REG_INT_ENABLE, value); + return value; +} + /* * tis_wait_sts() * @@ -750,9 +773,232 @@ static void lpc_tpm_set_resources(struct device *dev) } } +#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES) + +static void tpm_ppi_func0_cb(void *arg) +{ + /* Functions 1-8. */ + u8 buf[] = {0xff, 0x01}; + acpigen_write_return_byte_buffer(buf, 2); +} + +static void tpm_ppi_func1_cb(void *arg) +{ + if (IS_ENABLED(CONFIG_TPM2)) + /* Interface version: 2.0 */ + acpigen_write_return_string("2.0"); + else + /* Interface version: 1.2 */ + acpigen_write_return_string("1.2"); +} + +static void tpm_ppi_func2_cb(void *arg) +{ + /* Submit operations: drop on the floor and return success. */ + acpigen_write_return_byte(0); +} + +static void tpm_ppi_func3_cb(void *arg) +{ + /* Pending operation: none. */ + acpigen_emit_byte(RETURN_OP); + acpigen_write_package(2); + acpigen_write_byte(0); + acpigen_write_byte(0); + acpigen_pop_len(); +} +static void tpm_ppi_func4_cb(void *arg) +{ + /* Pre-OS transition method: reboot. */ + acpigen_write_return_byte(2); +} +static void tpm_ppi_func5_cb(void *arg) +{ + /* Operation response: no operation executed. */ + acpigen_emit_byte(RETURN_OP); + acpigen_write_package(3); + acpigen_write_byte(0); + acpigen_write_byte(0); + acpigen_write_byte(0); + acpigen_pop_len(); +} +static void tpm_ppi_func6_cb(void *arg) +{ + /* + * Set preferred user language: deprecated and must return 3 aka + * "not implemented". + */ + acpigen_write_return_byte(3); +} +static void tpm_ppi_func7_cb(void *arg) +{ + /* Submit operations: deny. */ + acpigen_write_return_byte(3); +} +static void tpm_ppi_func8_cb(void *arg) +{ + /* All actions are forbidden. */ + acpigen_write_return_byte(1); +} +static void (*tpm_ppi_callbacks[])(void *) = { + tpm_ppi_func0_cb, + tpm_ppi_func1_cb, + tpm_ppi_func2_cb, + tpm_ppi_func3_cb, + tpm_ppi_func4_cb, + tpm_ppi_func5_cb, + tpm_ppi_func6_cb, + tpm_ppi_func7_cb, + tpm_ppi_func8_cb, +}; + +static void tpm_mci_func0_cb(void *arg) +{ + /* Function 1. */ + acpigen_write_return_singleton_buffer(0x3); +} +static void tpm_mci_func1_cb(void *arg) +{ + /* Just return success. */ + acpigen_write_return_byte(0); +} + +static void (*tpm_mci_callbacks[])(void *) = { + tpm_mci_func0_cb, + tpm_mci_func1_cb, +}; + +static void lpc_tpm_fill_ssdt(struct device *dev) +{ + const char *path = acpi_device_path(dev->bus->dev); + u32 arg; + struct opregion opreg = OPREGION("TREG", SYSTEMMEMORY, + CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000); + + if (!path) + return; + + /* Device */ + acpigen_write_scope(path); + acpigen_write_device(acpi_device_name(dev)); + + acpigen_write_name("_HID"); + acpigen_emit_eisaid("PNP0C31"); + + acpigen_write_name("_CID"); + acpigen_emit_eisaid("PNP0C31"); + + acpigen_write_name_integer("_UID", 1); + + acpigen_write_opregion(&opreg); + + struct fieldlist tpm_field_list[] = { + /* + * TPM_INT_ENABLE_0 + * bit 0 : dataAvailIntEnable, + * bit 1 : stsValidIntEnable, + * bit 2 : localityChangeIntEnable, + * bit 3:4 typePolarity. + */ + FIELDLIST_OFFSET(0x8), + FIELDLIST_NAMESTR("INTE", 3), + FIELDLIST_NAMESTR("ITPL", 2), + + /* TPM_INT_VECTOR_0 */ + FIELDLIST_OFFSET(0xC), + FIELDLIST_NAMESTR("IVEC", 4), + + /* TPM_DID_VID */ + FIELDLIST_OFFSET(0xf00), + FIELDLIST_NAMESTR("DVID", 32), + }; + + acpigen_write_field(opreg.name, tpm_field_list, + ARRAY_SIZE(tpm_field_list), + FIELD_BYTEACC | FIELD_NOLOCK | FIELD_PRESERVE); + + u32 did_vid = tpm_read_did_vid(0); + if (did_vid > 0 && did_vid < 0xffffffff) + acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON); + else + acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_OFF); + + /* Resources */ + acpigen_write_name("_CRS"); + acpigen_write_resourcetemplate_header(); + acpigen_write_mem32fixed(1, CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000); + acpigen_write_io16(0x2e, 0x2e, 1, 2, 1); + + if (CONFIG_TPM_PIRQ) { + /* + * PIRQ: Update interrupt vector with configured PIRQ + * Active-Low Level-Triggered Shared + */ + struct acpi_irq tpm_irq_a = IRQ_LEVEL_LOW(CONFIG_TPM_PIRQ); + acpi_device_write_interrupt(&tpm_irq_a); + } else if (tpm_read_int_vector(0) > 0) { + u8 int_vec = tpm_read_int_vector(0); + u8 int_pol = tpm_read_int_polarity(0); + struct acpi_irq tpm_irq = IRQ_LEVEL_LOW(int_vec); + + if (int_pol & 1) + tpm_irq.polarity = IRQ_ACTIVE_LOW; + else + tpm_irq.polarity = IRQ_ACTIVE_HIGH; + + if (int_pol & 2) + tpm_irq.mode = IRQ_EDGE_TRIGGERED; + else + tpm_irq.mode = IRQ_LEVEL_TRIGGERED; + + acpi_device_write_interrupt(&tpm_irq); + } + + acpigen_write_resourcetemplate_footer(); + + if (!IS_ENABLED(CONFIG_CHROMEOS)) { + /* + * _DSM method + */ + struct dsm_uuid ids[] = { + /* Physical presence interface. + * This is used to submit commands like "Clear TPM" to + * be run at next reboot provided that user confirms + * them. Spec allows user to cancel all commands and/or + * configure BIOS to reject commands. So we pretend that + * user did just this: cancelled everything. If user + * really wants to clear TPM the only option now is to + * do it manually in payload. + */ + DSM_UUID(TPM_PPI_UUID, &tpm_ppi_callbacks[0], + ARRAY_SIZE(tpm_ppi_callbacks), (void *) &arg), + /* Memory clearing on boot: just a dummy. */ + DSM_UUID(TPM_MCI_UUID, &tpm_mci_callbacks[0], + ARRAY_SIZE(tpm_mci_callbacks), (void *) &arg), + }; + + acpigen_write_dsm_uuid_arr(ids, ARRAY_SIZE(ids)); + } + acpigen_pop_len(); /* Device */ + acpigen_pop_len(); /* Scope */ + + printk(BIOS_INFO, "%s.%s: %s %s\n", path, acpi_device_name(dev), + dev->chip_ops->name, dev_path(dev)); +} + +static const char *lpc_tpm_acpi_name(struct device *dev) +{ + return "TPM"; +} +#endif + static struct device_operations lpc_tpm_ops = { .read_resources = &lpc_tpm_read_resources, .set_resources = &lpc_tpm_set_resources, +#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES) + .acpi_name = &lpc_tpm_acpi_name, + .acpi_fill_ssdt_generator = &lpc_tpm_fill_ssdt, +#endif }; static struct pnp_info pnp_dev_info[] = { |