Re: [Sony Vaio TX3] TPM chip prevents machine from suspending a secondtime

From: Stefan Berger
Date: Sun Jan 29 2012 - 13:22:37 EST


On 01/29/2012 05:49 AM, John Hughes wrote:
On 23/01/12 21:52, Stefan Berger wrote:

Can you apply the patch below to your tpm_tis.c (or somewhere else in the kernel) and let me/us know what it reports in 'dmesg' upon a 'modprobe tpm_tis'?

[ 48.826151] tpm_tis 00:07: dmi: 0: (null)
[ 48.826157] tpm_tis 00:07: dmi: 1: Phoenix Technologies LTD
[ 48.826161] tpm_tis 00:07: dmi: 2: R0021N3
[ 48.826165] tpm_tis 00:07: dmi: 3: 05/09/2006
[ 48.826169] tpm_tis 00:07: dmi: 4: Sony Corporation
[ 48.826173] tpm_tis 00:07: dmi: 5: VGN-TX3XP_B
[ 48.826177] tpm_tis 00:07: dmi: 6: J001QHZL
[ 48.826181] tpm_tis 00:07: dmi: 7: 28244651-5000176
[ 48.826186] tpm_tis 00:07: dmi: 8: DC744BA0-5B3C-11D9-8822-0013A93CCD4D
[ 48.826190] tpm_tis 00:07: dmi: 9: Sony Corporation
[ 48.826195] tpm_tis 00:07: dmi: 10: VAIO
[ 48.826199] tpm_tis 00:07: dmi: 11: N/A
[ 48.826204] tpm_tis 00:07: dmi: 12: N/A
[ 48.826208] tpm_tis 00:07: dmi: 13: N/A
[ 48.826213] tpm_tis 00:07: dmi: 14: Sony Corporation
[ 48.826217] tpm_tis 00:07: dmi: 15: 10
[ 48.826221] tpm_tis 00:07: dmi: 16: N/A
[ 48.826225] tpm_tis 00:07: dmi: 17: J001QHZL
[ 48.826229] tpm_tis 00:07: dmi: 18:
[ 48.826235] tpm_tis 00:07: 1.2 TPM (device-id 0xB, rev-id 16)

Thanks. Please try the attached patch.

I am not sure whether this is the right way to go, though: Is it a problem particular to this BIOS + Board or a general problem of this BIOS? Is it specific to the TX3 or to all VAIOs?

Stefan



Add a TPM resume quirk for machines where the BIOS doesn't send the
TPM_Startup(ST_STATE) to the TPM and subsequent suspends fail due
to this. Identify machines by their SMBIOS data and only apply the quirk
on those known to need it.

Signed-off-by: Stefan Berger <stefanb@xxxxxxxxxxxxxxxxxx>

---
drivers/char/tpm/tpm.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++
drivers/char/tpm/tpm.h | 8 ++
2 files changed, 161 insertions(+)

Index: linux-2.6/drivers/char/tpm/tpm.c
===================================================================
--- linux-2.6.orig/drivers/char/tpm/tpm.c
+++ linux-2.6/drivers/char/tpm/tpm.c
@@ -28,6 +28,7 @@
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/freezer.h>
+#include <linux/dmi.h>

#include "tpm.h"

@@ -62,6 +63,131 @@ static LIST_HEAD(tpm_chip_list);
static DEFINE_SPINLOCK(driver_lock);
static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);

+/***** tpm quirks *****/
+
+#ifdef CONFIG_DMI
+
+struct tpm_dmi_entry {
+ enum dmi_field id;
+ const char **value;
+};
+
+struct tpm_resume_quirks {
+ const char *quirk_name;
+ const struct tpm_dmi_entry *dmi_entry;
+};
+
+#define TPM_DMI_ENTRY_LAST \
+ { .id = DMI_NONE, .value = (const char*[]) { NULL }, }
+
+static const struct tpm_resume_quirks tpm_resume_quirks[] = {
+ {
+ .quirk_name = "Sony Vaio TX3",
+ .dmi_entry = (struct tpm_dmi_entry[]) {
+ {
+ .id = DMI_BIOS_VENDOR,
+ .value = (const char*[]) {
+ "Phoenix Technologies LTD",
+ NULL,
+ },
+ },
+ {
+ .id = DMI_SYS_VENDOR,
+ .value = (const char*[]) {
+ "Sony Corporation",
+ NULL,
+ },
+ },
+ {
+ .id = DMI_PRODUCT_NAME,
+ .value = (const char*[]) {
+ "VGN-TX3XP_B",
+ NULL,
+ },
+ },
+ {
+ .id = DMI_BOARD_NAME,
+ .value = (const char*[]) {
+ "VAIO",
+ NULL,
+ },
+ },
+ TPM_DMI_ENTRY_LAST
+ }
+ }
+};
+
+bool find_str_in_array(const char **array, const char *val)
+{
+ int i = 0;
+
+ while (array[i]) {
+ if (!strcmp(array[i++], val))
+ return true;
+ }
+
+ return false;
+}
+
+static bool tpm_resume_quirk_dmi(struct tpm_chip *chip)
+{
+ int i, j, rc;
+ bool found = false, handled = false;
+ const char *val;
+ const struct tpm_dmi_entry *dmi_entry;
+
+ for (i = 0; i < ARRAY_SIZE(tpm_resume_quirks) && !handled; i++) {
+ j = 0;
+ found = true;
+
+ while (true) {
+ dmi_entry = &tpm_resume_quirks[i].dmi_entry[j];
+
+ if (dmi_entry->id == DMI_NONE)
+ break;
+
+ val = dmi_get_system_info(dmi_entry->id);
+ if (!val) {
+ found = false;
+ break;
+ }
+
+ found = find_str_in_array(dmi_entry->value, val);
+ if (!found)
+ break;
+ j++;
+ }
+
+ if (found) {
+ dev_info(chip->dev,
+ "Using tpm resume quirk '%s'.\n",
+ tpm_resume_quirks[i].quirk_name);
+ rc = tpm_startup_ststate(chip);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "quirk: TPM startup(ST_STATE) "
+ "failed.\n");
+ handled = true;
+ break;
+ }
+ }
+ return handled;
+}
+
+#else
+
+static bool tpm_resume_quirk_dmi(struct tpm_chip *tpm_chip)
+{
+ return false;
+}
+
+#endif
+
+static void tpm_resume_quirk(struct tpm_chip *tpm_chip)
+{
+ tpm_resume_quirk_dmi(tpm_chip);
+}
+
/*
* Array with one entry per ordinal defining the maximum amount
* of time the chip could take to return the result. The ordinal
@@ -864,6 +990,31 @@ int tpm_do_selftest(struct tpm_chip *chi
}
EXPORT_SYMBOL_GPL(tpm_do_selftest);

+/**
+ * tpm_startup_ststate - send a ststartup to the TPM
+ */
+#define TPM_ORD_STARTUP 153
+#define STARTUP_RESULT_SIZE 10
+
+static struct tpm_input_header startup_ststate_header = {
+ .tag = TPM_TAG_RQU_COMMAND,
+ .length = cpu_to_be32(10),
+ .ordinal = cpu_to_be32(TPM_ORD_STARTUP),
+};
+
+int tpm_startup_ststate(struct tpm_chip *chip)
+{
+ int rc;
+ struct tpm_cmd_t cmd;
+
+ cmd.header.in = startup_ststate_header;
+ cmd.params.startup_in.state = cpu_to_be16(TPM_ST_STATE);
+ rc = transmit_cmd(chip, &cmd, STARTUP_RESULT_SIZE,
+ "startup(st_state)");
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_startup_ststate);
+
int tpm_send(u32 chip_num, void *cmd, size_t buflen)
{
struct tpm_chip *chip;
@@ -1313,6 +1464,8 @@ int tpm_pm_resume(struct device *dev)
if (chip == NULL)
return -ENODEV;

+ tpm_resume_quirk(chip);
+
return 0;
}
EXPORT_SYMBOL_GPL(tpm_pm_resume);
Index: linux-2.6/drivers/char/tpm/tpm.h
===================================================================
--- linux-2.6.orig/drivers/char/tpm/tpm.h
+++ linux-2.6/drivers/char/tpm/tpm.h
@@ -42,6 +42,8 @@ enum tpm_addr {
#define TPM_ERR_DEACTIVATED 0x6
#define TPM_ERR_DISABLED 0x7

+#define TPM_ST_STATE 0x2
+
#define TPM_HEADER_SIZE 10
extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr,
char *);
@@ -269,6 +271,10 @@ struct tpm_pcrextend_in {
u8 hash[TPM_DIGEST_SIZE];
}__attribute__((packed));

+struct tpm_startup_in {
+ __be16 state;
+} __packed;
+
typedef union {
struct tpm_getcap_params_out getcap_out;
struct tpm_readpubek_params_out readpubek_out;
@@ -277,6 +283,7 @@ typedef union {
struct tpm_pcrread_in pcrread_in;
struct tpm_pcrread_out pcrread_out;
struct tpm_pcrextend_in pcrextend_in;
+ struct tpm_startup_in startup_in;
} tpm_cmd_params;

struct tpm_cmd_t {
@@ -289,6 +296,7 @@ ssize_t tpm_getcap(struct device *, __be
extern int tpm_get_timeouts(struct tpm_chip *);
extern void tpm_gen_interrupt(struct tpm_chip *);
extern int tpm_do_selftest(struct tpm_chip *);
+extern int tpm_startup_ststate(struct tpm_chip *);
extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32);
extern struct tpm_chip* tpm_register_hardware(struct device *,
const struct tpm_vendor_specific *);