From f728643001397cee39dcfabff6458a5fbc3baecf Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Fri, 20 Nov 2015 14:32:33 +0100 Subject: tpm_tis: further simplify calculation of ordinal duration commit 07b133e6060b ("char/tpm: simplify duration calculation and eliminate smatch warning.") includes a misleading test that is always false. The tpm_ordinal_duration table is only valid for TPM_PROTECTED ordinals where the higher 16 bits are all 0, anyway. Signed-off-by: Martin Wilck Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Acked-by: Peter Huewe --- drivers/char/tpm/tpm-interface.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index c50637db3a8a..afdc83602e9f 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -310,10 +310,12 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, { int duration_idx = TPM_UNDEFINED; int duration = 0; - u8 category = (ordinal >> 24) & 0xFF; - if ((category == TPM_PROTECTED_COMMAND && ordinal < TPM_MAX_ORDINAL) || - (category == TPM_CONNECTION_COMMAND && ordinal < TSC_MAX_ORDINAL)) + /* + * We only have a duration table for protected commands, where the upper + * 16 bits are 0. For the few other ordinals the fallback will be used. + */ + if (ordinal < TPM_MAX_ORDINAL) duration_idx = tpm_ordinal_duration[ordinal]; if (duration_idx != TPM_UNDEFINED) -- cgit v1.2.3 From 727f28b8ca24a581c7bd868326b8cea1058c720a Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 25 Nov 2015 14:05:30 -0700 Subject: tpm_tis: Use devm_free_irq not free_irq The interrupt is always allocated with devm_request_irq so it must always be freed with devm_free_irq. Fixes: 448e9c55c12d ("tpm_tis: verify interrupt during init") Signed-off-by: Jason Gunthorpe Acked-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Martin Wilck Signed-off-by: Jarkko Sakkinen Acked-by: Peter Huewe --- drivers/char/tpm/tpm_tis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 65f7eecc45b0..f10a107614b4 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -401,7 +401,7 @@ static void disable_interrupts(struct tpm_chip *chip) iowrite32(intmask, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); - free_irq(chip->vendor.irq, chip); + devm_free_irq(chip->pdev, chip->vendor.irq, chip); chip->vendor.irq = 0; } -- cgit v1.2.3 From 036bb38ffb3e4c92361108f324364b0341cd9e31 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 25 Nov 2015 14:05:31 -0700 Subject: tpm_tis: Ensure interrupts are disabled when the driver starts This should be done very early, before anything could possibly cause the TPM to generate an interrupt. If the IRQ line is shared with another driver causing an interrupt before setting up our handler will be very bad. Signed-off-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Martin Wilck Tested-by: Scot Doyle Signed-off-by: Jarkko Sakkinen Acked-by: Peter Huewe --- drivers/char/tpm/tpm_tis.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index f10a107614b4..513f30efd9d3 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -677,6 +677,15 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, goto out_err; } + /* Take control of the TPM's interrupt hardware and shut it off */ + intmask = ioread32(chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | + TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; + intmask &= ~TPM_GLOBAL_INT_ENABLE; + iowrite32(intmask, + chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); + if (request_locality(chip, 0) != 0) { rc = -ENODEV; goto out_err; @@ -735,17 +744,6 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, init_waitqueue_head(&chip->vendor.read_queue); init_waitqueue_head(&chip->vendor.int_queue); - intmask = - ioread32(chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor.locality)); - - intmask |= TPM_INTF_CMD_READY_INT - | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT - | TPM_INTF_STS_VALID_INT; - - iowrite32(intmask, - chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor.locality)); if (interrupts) chip->vendor.irq = tpm_info->irq; if (interrupts && !chip->vendor.irq) { -- cgit v1.2.3 From 25112048cd59930e23775cafb88e18cfb484892c Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 25 Nov 2015 14:05:32 -0700 Subject: tpm: rework tpm_get_timeouts() IRQ probing needs to know that the TPM is working before trying to probe, so move tpm_get_timeouts() to the top of the tpm_tis_init(). This has the advantage of also getting the correct timeouts loaded before doing IRQ probing. All the timeout handling code is moved to tpm_get_timeouts() in order to remove duplicate code in tpm_tis and tpm_crb. [jarkko.sakkinen@linux.intel.com: squashed two patches together and improved the commit message.] Signed-off-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Martin Wilck Tested-by: Scot Doyle Signed-off-by: Jarkko Sakkinen Acked-by: Peter Huewe --- drivers/char/tpm/tpm-interface.c | 15 +++++++++++++++ drivers/char/tpm/tpm_crb.c | 14 +++----------- drivers/char/tpm/tpm_tis.c | 33 ++++++++++++++++----------------- 3 files changed, 34 insertions(+), 28 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index afdc83602e9f..e2fa89c88304 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -503,6 +503,21 @@ int tpm_get_timeouts(struct tpm_chip *chip) struct duration_t *duration_cap; ssize_t rc; + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + /* Fixed timeouts for TPM2 */ + chip->vendor.timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A); + chip->vendor.timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B); + chip->vendor.timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C); + chip->vendor.timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D); + chip->vendor.duration[TPM_SHORT] = + msecs_to_jiffies(TPM2_DURATION_SHORT); + chip->vendor.duration[TPM_MEDIUM] = + msecs_to_jiffies(TPM2_DURATION_MEDIUM); + chip->vendor.duration[TPM_LONG] = + msecs_to_jiffies(TPM2_DURATION_LONG); + return 0; + } + tpm_cmd.header.in = tpm_getcap_header; tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 4bb9727c1047..8342cf51ffdc 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -284,17 +284,9 @@ static int crb_acpi_add(struct acpi_device *device) chip->vendor.priv = priv; - /* Default timeouts and durations */ - chip->vendor.timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A); - chip->vendor.timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B); - chip->vendor.timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C); - chip->vendor.timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D); - chip->vendor.duration[TPM_SHORT] = - msecs_to_jiffies(TPM2_DURATION_SHORT); - chip->vendor.duration[TPM_MEDIUM] = - msecs_to_jiffies(TPM2_DURATION_MEDIUM); - chip->vendor.duration[TPM_LONG] = - msecs_to_jiffies(TPM2_DURATION_LONG); + rc = tpm_get_timeouts(chip); + if (rc) + return rc; chip->acpi_dev_handle = device->handle; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 513f30efd9d3..56a295d328c7 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -740,6 +740,16 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, if (intfcaps & TPM_INTF_DATA_AVAIL_INT) dev_dbg(dev, "\tData Avail Int Support\n"); + /* Very early on issue a command to the TPM in polling mode to make + * sure it works. May as well use that command to set the proper + * timeouts for the driver. + */ + if (tpm_get_timeouts(chip)) { + dev_err(dev, "Could not get TPM timeouts and durations\n"); + rc = -ENODEV; + goto out_err; + } + /* INTERRUPT Setup */ init_waitqueue_head(&chip->vendor.read_queue); init_waitqueue_head(&chip->vendor.int_queue); @@ -837,18 +847,13 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, iowrite8(irq_r, chip->vendor.iobase + TPM_INT_VECTOR(chip->vendor.locality)); - if (chip->flags & TPM_CHIP_FLAG_TPM2) { - chip->vendor.timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A); - chip->vendor.timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B); - chip->vendor.timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C); - chip->vendor.timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D); - chip->vendor.duration[TPM_SHORT] = - msecs_to_jiffies(TPM2_DURATION_SHORT); - chip->vendor.duration[TPM_MEDIUM] = - msecs_to_jiffies(TPM2_DURATION_MEDIUM); - chip->vendor.duration[TPM_LONG] = - msecs_to_jiffies(TPM2_DURATION_LONG); + if (tpm_get_timeouts(chip)) { + dev_err(dev, "Could not get TPM timeouts and durations\n"); + rc = -ENODEV; + goto out_err; + } + if (chip->flags & TPM_CHIP_FLAG_TPM2) { rc = tpm2_do_selftest(chip); if (rc == TPM2_RC_INITIALIZE) { dev_warn(dev, "Firmware has not started TPM\n"); @@ -864,12 +869,6 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, goto out_err; } } else { - if (tpm_get_timeouts(chip)) { - dev_err(dev, "Could not get TPM timeouts and durations\n"); - rc = -ENODEV; - goto out_err; - } - if (tpm_do_selftest(chip)) { dev_err(dev, "TPM self test failed\n"); rc = -ENODEV; -- cgit v1.2.3 From 7ab4032fa579cd54be6a986a5cfd7f374b6bf02d Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 25 Nov 2015 14:05:34 -0700 Subject: tpm_tis: Get rid of the duplicate IRQ probing code The new code that works directly in tpm_tis_send is able to handle IRQ probing duties as well, so just use it for everything. Signed-off-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Martin Wilck Tested-by: Scot Doyle Signed-off--by: Jarkko Sakkinen Acked-by: Peter Huewe --- drivers/char/tpm/tpm.h | 1 - drivers/char/tpm/tpm_tis.c | 64 +++++++++++++++------------------------------- 2 files changed, 20 insertions(+), 45 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index a4257a32964f..347fc615bcc9 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -138,7 +138,6 @@ struct tpm_vendor_specific { unsigned long base; /* TPM base address */ int irq; - int probed_irq; int region_size; int have_region; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 56a295d328c7..2580893de023 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -99,6 +99,7 @@ static struct tpm_info tis_default_info = { #define TPM_RID(l) (0x0F04 | ((l) << 12)) struct priv_data { + bool irq_probing; bool irq_tested; }; @@ -463,8 +464,9 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) msleep(1); if (!priv->irq_tested) { disable_interrupts(chip); - dev_err(chip->pdev, - FW_BUG "TPM interrupt not working, polling instead\n"); + if (!priv->irq_probing) + dev_err(chip->pdev, FW_BUG + "TPM interrupt not working, polling instead\n"); } priv->irq_tested = true; return rc; @@ -570,26 +572,6 @@ static const struct tpm_class_ops tpm_tis = { .req_canceled = tpm_tis_req_canceled, }; -static irqreturn_t tis_int_probe(int irq, void *dev_id) -{ - struct tpm_chip *chip = dev_id; - u32 interrupt; - - interrupt = ioread32(chip->vendor.iobase + - TPM_INT_STATUS(chip->vendor.locality)); - - if (interrupt == 0) - return IRQ_NONE; - - chip->vendor.probed_irq = irq; - - /* Clear interrupts handled with TPM_EOI */ - iowrite32(interrupt, - chip->vendor.iobase + - TPM_INT_STATUS(chip->vendor.locality)); - return IRQ_HANDLED; -} - static irqreturn_t tis_int_handler(int dummy, void *dev_id) { struct tpm_chip *chip = dev_id; @@ -772,13 +754,14 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, iowrite8(i, chip->vendor.iobase + TPM_INT_VECTOR(chip->vendor.locality)); if (devm_request_irq - (dev, i, tis_int_probe, IRQF_SHARED, + (dev, i, tis_int_handler, IRQF_SHARED, chip->devname, chip) != 0) { dev_info(chip->pdev, "Unable to request irq: %d for probe\n", i); continue; } + chip->vendor.irq = i; /* Clear all existing */ iowrite32(ioread32 @@ -792,7 +775,8 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); - chip->vendor.probed_irq = 0; + priv->irq_tested = false; + priv->irq_probing = true; /* Generate Interrupts */ if (chip->flags & TPM_CHIP_FLAG_TPM2) @@ -800,26 +784,20 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, else tpm_gen_interrupt(chip); - chip->vendor.irq = chip->vendor.probed_irq; - - /* free_irq will call into tis_int_probe; - clear all irqs we haven't seen while doing - tpm_gen_interrupt */ - iowrite32(ioread32 - (chip->vendor.iobase + - TPM_INT_STATUS(chip->vendor.locality)), - chip->vendor.iobase + - TPM_INT_STATUS(chip->vendor.locality)); - - /* Turn off */ - iowrite32(intmask, - chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor.locality)); + priv->irq_probing = false; - devm_free_irq(dev, i, chip); + /* tpm_tis_send will either confirm the interrupt is + * working or it will call disable_irq which undoes + * all of the above. + */ + if (chip->vendor.irq) + break; } + if (!chip->vendor.irq) + iowrite8(irq_r, chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); } - if (chip->vendor.irq) { + if (chip->vendor.irq && !priv->irq_tested) { iowrite8(chip->vendor.irq, chip->vendor.iobase + TPM_INT_VECTOR(chip->vendor.locality)); @@ -843,9 +821,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); } - } else if (irq_r != -1) - iowrite8(irq_r, chip->vendor.iobase + - TPM_INT_VECTOR(chip->vendor.locality)); + } if (tpm_get_timeouts(chip)) { dev_err(dev, "Could not get TPM timeouts and durations\n"); -- cgit v1.2.3 From e3837e74a06dc38ab79529758a3667fbff2fdc17 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 25 Nov 2015 14:05:35 -0700 Subject: tpm_tis: Refactor the interrupt setup Now that the probe and run cases are merged together we can use a much simpler setup flow where probe and normal setup are done with exactly the same code. Since the new flow always calls tpm_gen_interrupt to confirm the IRQ there is also no longer any need to call tpm_get_timeouts twice. Signed-off-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Martin Wilck Tested-by: Scot Doyle Signed-off-by: Jarkko Sakkinen Acked-by: Peter Huewe --- drivers/char/tpm/tpm_tis.c | 184 +++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 100 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 2580893de023..c29ae69f1df6 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -99,7 +99,6 @@ static struct tpm_info tis_default_info = { #define TPM_RID(l) (0x0F04 | ((l) << 12)) struct priv_data { - bool irq_probing; bool irq_tested; }; @@ -462,12 +461,8 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) chip->vendor.irq = irq; if (!priv->irq_tested) msleep(1); - if (!priv->irq_tested) { + if (!priv->irq_tested) disable_interrupts(chip); - if (!priv->irq_probing) - dev_err(chip->pdev, FW_BUG - "TPM interrupt not working, polling instead\n"); - } priv->irq_tested = true; return rc; } @@ -604,6 +599,80 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) return IRQ_HANDLED; } +/* Register the IRQ and issue a command that will cause an interrupt. If an + * irq is seen then leave the chip setup for IRQ operation, otherwise reverse + * everything and leave in polling mode. Returns 0 on success. + */ +static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, int irq) +{ + struct priv_data *priv = chip->vendor.priv; + u8 original_int_vec; + + if (devm_request_irq(chip->pdev, irq, tis_int_handler, IRQF_SHARED, + chip->devname, chip) != 0) { + dev_info(chip->pdev, "Unable to request irq: %d for probe\n", + irq); + return -1; + } + chip->vendor.irq = irq; + + original_int_vec = ioread8(chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); + iowrite8(irq, + chip->vendor.iobase + TPM_INT_VECTOR(chip->vendor.locality)); + + /* Clear all existing */ + iowrite32(ioread32(chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)), + chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality)); + + /* Turn on */ + iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, + chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); + + priv->irq_tested = false; + + /* Generate an interrupt by having the core call through to + * tpm_tis_send + */ + if (chip->flags & TPM_CHIP_FLAG_TPM2) + tpm2_gen_interrupt(chip); + else + tpm_gen_interrupt(chip); + + /* tpm_tis_send will either confirm the interrupt is working or it + * will call disable_irq which undoes all of the above. + */ + if (!chip->vendor.irq) { + iowrite8(original_int_vec, + chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); + return 1; + } + + return 0; +} + +/* Try to find the IRQ the TPM is using. This is for legacy x86 systems that + * do not have ACPI/etc. We typically expect the interrupt to be declared if + * present. + */ +static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask) +{ + u8 original_int_vec; + int i; + + original_int_vec = ioread8(chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); + + if (!original_int_vec) { + for (i = 3; i <= 15; i++) + if (!tpm_tis_probe_irq_single(chip, intmask, i)) + return; + } else if (!tpm_tis_probe_irq_single(chip, intmask, original_int_vec)) + return; +} + static bool interrupts = true; module_param(interrupts, bool, 0444); MODULE_PARM_DESC(interrupts, "Enable interrupts"); @@ -626,8 +695,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, acpi_handle acpi_dev_handle) { u32 vendor, intfcaps, intmask; - int rc, i, irq_s, irq_e, probe; - int irq_r = -1; + int rc, probe; struct tpm_chip *chip; struct priv_data *priv; @@ -735,98 +803,14 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, /* INTERRUPT Setup */ init_waitqueue_head(&chip->vendor.read_queue); init_waitqueue_head(&chip->vendor.int_queue); - - if (interrupts) - chip->vendor.irq = tpm_info->irq; - if (interrupts && !chip->vendor.irq) { - irq_s = - ioread8(chip->vendor.iobase + - TPM_INT_VECTOR(chip->vendor.locality)); - irq_r = irq_s; - if (irq_s) { - irq_e = irq_s; - } else { - irq_s = 3; - irq_e = 15; - } - - for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) { - iowrite8(i, chip->vendor.iobase + - TPM_INT_VECTOR(chip->vendor.locality)); - if (devm_request_irq - (dev, i, tis_int_handler, IRQF_SHARED, - chip->devname, chip) != 0) { - dev_info(chip->pdev, - "Unable to request irq: %d for probe\n", - i); - continue; - } - chip->vendor.irq = i; - - /* Clear all existing */ - iowrite32(ioread32 - (chip->vendor.iobase + - TPM_INT_STATUS(chip->vendor.locality)), - chip->vendor.iobase + - TPM_INT_STATUS(chip->vendor.locality)); - - /* Turn on */ - iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, - chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor.locality)); - - priv->irq_tested = false; - priv->irq_probing = true; - - /* Generate Interrupts */ - if (chip->flags & TPM_CHIP_FLAG_TPM2) - tpm2_gen_interrupt(chip); - else - tpm_gen_interrupt(chip); - - priv->irq_probing = false; - - /* tpm_tis_send will either confirm the interrupt is - * working or it will call disable_irq which undoes - * all of the above. - */ - if (chip->vendor.irq) - break; - } - if (!chip->vendor.irq) - iowrite8(irq_r, chip->vendor.iobase + - TPM_INT_VECTOR(chip->vendor.locality)); - } - if (chip->vendor.irq && !priv->irq_tested) { - iowrite8(chip->vendor.irq, - chip->vendor.iobase + - TPM_INT_VECTOR(chip->vendor.locality)); - if (devm_request_irq - (dev, chip->vendor.irq, tis_int_handler, IRQF_SHARED, - chip->devname, chip) != 0) { - dev_info(chip->pdev, - "Unable to request irq: %d for use\n", - chip->vendor.irq); - chip->vendor.irq = 0; - } else { - /* Clear all existing */ - iowrite32(ioread32 - (chip->vendor.iobase + - TPM_INT_STATUS(chip->vendor.locality)), - chip->vendor.iobase + - TPM_INT_STATUS(chip->vendor.locality)); - - /* Turn on */ - iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, - chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor.locality)); - } - } - - if (tpm_get_timeouts(chip)) { - dev_err(dev, "Could not get TPM timeouts and durations\n"); - rc = -ENODEV; - goto out_err; + if (interrupts) { + if (tpm_info->irq) { + tpm_tis_probe_irq_single(chip, intmask, tpm_info->irq); + if (!chip->vendor.irq) + dev_err(chip->pdev, FW_BUG + "TPM interrupt not working, polling instead\n"); + } else + tpm_tis_probe_irq(chip, intmask); } if (chip->flags & TPM_CHIP_FLAG_TPM2) { -- cgit v1.2.3 From b8ba1e744445d65dad7dd61db909e7f2b89df35e Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 25 Nov 2015 14:05:36 -0700 Subject: tpm_tis: Tighten IRQ auto-probing auto-probing doesn't work with shared interrupts, and the auto detection interrupt range is for x86 only. Signed-off-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Martin Wilck Tested-by: Scot Doyle Signed-off-by: Jarkko Sakkinen Acked-by: Peter Huewe --- drivers/char/tpm/tpm_tis.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index c29ae69f1df6..8a3509cb10da 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -603,12 +603,13 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) * irq is seen then leave the chip setup for IRQ operation, otherwise reverse * everything and leave in polling mode. Returns 0 on success. */ -static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, int irq) +static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, + int flags, int irq) { struct priv_data *priv = chip->vendor.priv; u8 original_int_vec; - if (devm_request_irq(chip->pdev, irq, tis_int_handler, IRQF_SHARED, + if (devm_request_irq(chip->pdev, irq, tis_int_handler, flags, chip->devname, chip) != 0) { dev_info(chip->pdev, "Unable to request irq: %d for probe\n", irq); @@ -666,10 +667,13 @@ static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask) TPM_INT_VECTOR(chip->vendor.locality)); if (!original_int_vec) { - for (i = 3; i <= 15; i++) - if (!tpm_tis_probe_irq_single(chip, intmask, i)) - return; - } else if (!tpm_tis_probe_irq_single(chip, intmask, original_int_vec)) + if (IS_ENABLED(CONFIG_X86)) + for (i = 3; i <= 15; i++) + if (!tpm_tis_probe_irq_single(chip, intmask, 0, + i)) + return; + } else if (!tpm_tis_probe_irq_single(chip, intmask, 0, + original_int_vec)) return; } @@ -805,7 +809,8 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, init_waitqueue_head(&chip->vendor.int_queue); if (interrupts) { if (tpm_info->irq) { - tpm_tis_probe_irq_single(chip, intmask, tpm_info->irq); + tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, + tpm_info->irq); if (!chip->vendor.irq) dev_err(chip->pdev, FW_BUG "TPM interrupt not working, polling instead\n"); -- cgit v1.2.3 From 6674ff145eef1f158e3d1d065cb1e19f315d909b Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 9 Dec 2015 08:52:01 -0500 Subject: tpm_ibmvtpm: properly handle interrupted packet receptions When the TPM response reception is interrupted in the wait_event_interruptable call, the TPM is still busy processing the command and will only deliver the response later. So we have to wait for an outstanding response before sending a new request to avoid trying to put a 2nd request into the CRQ. Also reset the res_len before sending a command so we will end up in that wait_event_interruptable() waiting for the response rather than reading the command packet as a response. The easiest way to trigger the problem is to run the following cd /sys/device/vio/71000004 while :; cat pcrs >/dev/null; done And press Ctrl-C. This will then display an error tpm_ibmvtpm 71000004: tpm_transmit: tpm_recv: error -4 followed by several other errors once interaction with the TPM resumes. tpm_ibmvtpm 71000004: A TPM error (101) occurred attempting to determine the number of PCRS. Signed-off-by: Stefan Berger Tested-by: Hon Ching(Vicky) Lo Reviewed-by: Jarkko Sakkinen Reviewed-by: Ashley Lai Signed-off-by: Jarkko Sakkinen Acked-by: Peter Huewe --- drivers/char/tpm/tpm_ibmvtpm.c | 22 ++++++++++++++++++++-- drivers/char/tpm/tpm_ibmvtpm.h | 1 + 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 3e6a22658b63..b0a9a9e34241 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -90,7 +90,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) return 0; } - sig = wait_event_interruptible(ibmvtpm->wq, ibmvtpm->res_len != 0); + sig = wait_event_interruptible(ibmvtpm->wq, !ibmvtpm->tpm_processing_cmd); if (sig) return -EINTR; @@ -125,7 +125,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) struct ibmvtpm_dev *ibmvtpm; struct ibmvtpm_crq crq; __be64 *word = (__be64 *)&crq; - int rc; + int rc, sig; ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip); @@ -141,18 +141,35 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) return -EIO; } + if (ibmvtpm->tpm_processing_cmd) { + dev_info(ibmvtpm->dev, + "Need to wait for TPM to finish\n"); + /* wait for previous command to finish */ + sig = wait_event_interruptible(ibmvtpm->wq, !ibmvtpm->tpm_processing_cmd); + if (sig) + return -EINTR; + } + spin_lock(&ibmvtpm->rtce_lock); + ibmvtpm->res_len = 0; memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count); crq.valid = (u8)IBMVTPM_VALID_CMD; crq.msg = (u8)VTPM_TPM_COMMAND; crq.len = cpu_to_be16(count); crq.data = cpu_to_be32(ibmvtpm->rtce_dma_handle); + /* + * set the processing flag before the Hcall, since we may get the + * result (interrupt) before even being able to check rc. + */ + ibmvtpm->tpm_processing_cmd = true; + rc = ibmvtpm_send_crq(ibmvtpm->vdev, be64_to_cpu(word[0]), be64_to_cpu(word[1])); if (rc != H_SUCCESS) { dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc); rc = 0; + ibmvtpm->tpm_processing_cmd = false; } else rc = count; @@ -515,6 +532,7 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, case VTPM_TPM_COMMAND_RES: /* len of the data in rtce buffer */ ibmvtpm->res_len = be16_to_cpu(crq->len); + ibmvtpm->tpm_processing_cmd = false; wake_up_interruptible(&ibmvtpm->wq); return; default: diff --git a/drivers/char/tpm/tpm_ibmvtpm.h b/drivers/char/tpm/tpm_ibmvtpm.h index 6af92890518f..91dfe766d080 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.h +++ b/drivers/char/tpm/tpm_ibmvtpm.h @@ -45,6 +45,7 @@ struct ibmvtpm_dev { wait_queue_head_t wq; u16 res_len; u32 vtpm_version; + bool tpm_processing_cmd; }; #define CRQ_RES_BUF_SIZE PAGE_SIZE -- cgit v1.2.3 From 5ca4c20cfd37bac6486de040e9951b3b34755238 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Thu, 5 Nov 2015 21:43:06 +0200 Subject: keys, trusted: select hash algorithm for TPM2 chips Added 'hash=' option for selecting the hash algorithm for add_key() syscall and documentation for it. Added entry for sm3-256 to the following tables in order to support TPM_ALG_SM3_256: * hash_algo_name * hash_digest_size Includes support for the following hash algorithms: * sha1 * sha256 * sha384 * sha512 * sm3-256 Signed-off-by: Jarkko Sakkinen Tested-by: Colin Ian King Reviewed-by: James Morris Reviewed-by: Mimi Zohar Acked-by: Peter Huewe --- Documentation/security/keys-trusted-encrypted.txt | 3 ++ crypto/hash_info.c | 2 ++ drivers/char/tpm/tpm.h | 10 +++++-- drivers/char/tpm/tpm2-cmd.c | 36 +++++++++++++++++++++-- include/crypto/hash_info.h | 3 ++ include/keys/trusted-type.h | 1 + include/uapi/linux/hash_info.h | 1 + security/keys/Kconfig | 1 + security/keys/trusted.c | 27 ++++++++++++++++- 9 files changed, 77 insertions(+), 7 deletions(-) (limited to 'drivers/char') diff --git a/Documentation/security/keys-trusted-encrypted.txt b/Documentation/security/keys-trusted-encrypted.txt index e105ae97a4f5..fd2565b301e8 100644 --- a/Documentation/security/keys-trusted-encrypted.txt +++ b/Documentation/security/keys-trusted-encrypted.txt @@ -38,6 +38,9 @@ Usage: pcrlock= pcr number to be extended to "lock" blob migratable= 0|1 indicating permission to reseal to new PCR values, default 1 (resealing allowed) + hash= hash algorithm name as a string. For TPM 1.x the only + allowed value is sha1. For TPM 2.x the allowed values + are sha1, sha256, sha384, sha512 and sm3-256. "keyctl print" returns an ascii hex copy of the sealed key, which is in standard TPM_STORED_DATA format. The key length for new keys are always in bytes. diff --git a/crypto/hash_info.c b/crypto/hash_info.c index 3e7ff46f26e8..7b1e0b188ce6 100644 --- a/crypto/hash_info.c +++ b/crypto/hash_info.c @@ -31,6 +31,7 @@ const char *const hash_algo_name[HASH_ALGO__LAST] = { [HASH_ALGO_TGR_128] = "tgr128", [HASH_ALGO_TGR_160] = "tgr160", [HASH_ALGO_TGR_192] = "tgr192", + [HASH_ALGO_SM3_256] = "sm3-256", }; EXPORT_SYMBOL_GPL(hash_algo_name); @@ -52,5 +53,6 @@ const int hash_digest_size[HASH_ALGO__LAST] = { [HASH_ALGO_TGR_128] = TGR128_DIGEST_SIZE, [HASH_ALGO_TGR_160] = TGR160_DIGEST_SIZE, [HASH_ALGO_TGR_192] = TGR192_DIGEST_SIZE, + [HASH_ALGO_SM3_256] = SM3256_DIGEST_SIZE, }; EXPORT_SYMBOL_GPL(hash_digest_size); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 347fc615bcc9..542a80cbfd9c 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -83,16 +83,20 @@ enum tpm2_structures { }; enum tpm2_return_codes { - TPM2_RC_INITIALIZE = 0x0100, - TPM2_RC_TESTING = 0x090A, + TPM2_RC_HASH = 0x0083, /* RC_FMT1 */ + TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */ TPM2_RC_DISABLED = 0x0120, + TPM2_RC_TESTING = 0x090A, /* RC_WARN */ }; enum tpm2_algorithms { TPM2_ALG_SHA1 = 0x0004, TPM2_ALG_KEYEDHASH = 0x0008, TPM2_ALG_SHA256 = 0x000B, - TPM2_ALG_NULL = 0x0010 + TPM2_ALG_SHA384 = 0x000C, + TPM2_ALG_SHA512 = 0x000D, + TPM2_ALG_NULL = 0x0010, + TPM2_ALG_SM3_256 = 0x0012, }; enum tpm2_command_codes { diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index c12130485fc1..d9d082206f6e 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -16,6 +16,7 @@ */ #include "tpm.h" +#include #include enum tpm2_object_attributes { @@ -104,6 +105,19 @@ struct tpm2_cmd { union tpm2_cmd_params params; } __packed; +struct tpm2_hash { + unsigned int crypto_id; + unsigned int tpm_id; +}; + +static struct tpm2_hash tpm2_hash_map[] = { + {HASH_ALGO_SHA1, TPM2_ALG_SHA1}, + {HASH_ALGO_SHA256, TPM2_ALG_SHA256}, + {HASH_ALGO_SHA384, TPM2_ALG_SHA384}, + {HASH_ALGO_SHA512, TPM2_ALG_SHA512}, + {HASH_ALGO_SM3_256, TPM2_ALG_SM3_256}, +}; + /* * Array with one entry per ordinal defining the maximum amount * of time the chip could take to return the result. The values @@ -429,8 +443,20 @@ int tpm2_seal_trusted(struct tpm_chip *chip, { unsigned int blob_len; struct tpm_buf buf; + u32 hash; + int i; int rc; + for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) { + if (options->hash == tpm2_hash_map[i].crypto_id) { + hash = tpm2_hash_map[i].tpm_id; + break; + } + } + + if (i == ARRAY_SIZE(tpm2_hash_map)) + return -EINVAL; + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); if (rc) return rc; @@ -455,7 +481,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, tpm_buf_append_u16(&buf, 14); tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH); - tpm_buf_append_u16(&buf, TPM2_ALG_SHA256); + tpm_buf_append_u16(&buf, hash); tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH); tpm_buf_append_u16(&buf, 0); /* policy digest size */ tpm_buf_append_u16(&buf, TPM2_ALG_NULL); @@ -488,8 +514,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip, out: tpm_buf_destroy(&buf); - if (rc > 0) - rc = -EPERM; + if (rc > 0) { + if ((rc & TPM2_RC_HASH) == TPM2_RC_HASH) + rc = -EINVAL; + else + rc = -EPERM; + } return rc; } diff --git a/include/crypto/hash_info.h b/include/crypto/hash_info.h index e1e5a3e5dd1b..56f217d41f12 100644 --- a/include/crypto/hash_info.h +++ b/include/crypto/hash_info.h @@ -34,6 +34,9 @@ #define TGR160_DIGEST_SIZE 20 #define TGR192_DIGEST_SIZE 24 +/* not defined in include/crypto/ */ +#define SM3256_DIGEST_SIZE 32 + extern const char *const hash_algo_name[HASH_ALGO__LAST]; extern const int hash_digest_size[HASH_ALGO__LAST]; diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index f91ecd9d1bb1..a6a100833ae9 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -36,6 +36,7 @@ struct trusted_key_options { uint32_t pcrinfo_len; unsigned char pcrinfo[MAX_PCRINFO_SIZE]; int pcrlock; + uint32_t hash; }; extern struct key_type key_type_trusted; diff --git a/include/uapi/linux/hash_info.h b/include/uapi/linux/hash_info.h index ca18c45f8304..ebf8fd885dd5 100644 --- a/include/uapi/linux/hash_info.h +++ b/include/uapi/linux/hash_info.h @@ -31,6 +31,7 @@ enum hash_algo { HASH_ALGO_TGR_128, HASH_ALGO_TGR_160, HASH_ALGO_TGR_192, + HASH_ALGO_SM3_256, HASH_ALGO__LAST }; diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 72483b8f1be5..fe4d74e126a7 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -54,6 +54,7 @@ config TRUSTED_KEYS select CRYPTO select CRYPTO_HMAC select CRYPTO_SHA1 + select CRYPTO_HASH_INFO help This option provides support for creating, sealing, and unsealing keys in the kernel. Trusted keys are random number symmetric keys, diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 7c183c767a3a..8f1300cab38e 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -11,6 +11,7 @@ * See Documentation/security/keys-trusted-encrypted.txt */ +#include #include #include #include @@ -710,7 +711,8 @@ enum { Opt_err = -1, Opt_new, Opt_load, Opt_update, Opt_keyhandle, Opt_keyauth, Opt_blobauth, - Opt_pcrinfo, Opt_pcrlock, Opt_migratable + Opt_pcrinfo, Opt_pcrlock, Opt_migratable, + Opt_hash, }; static const match_table_t key_tokens = { @@ -723,6 +725,7 @@ static const match_table_t key_tokens = { {Opt_pcrinfo, "pcrinfo=%s"}, {Opt_pcrlock, "pcrlock=%s"}, {Opt_migratable, "migratable=%s"}, + {Opt_hash, "hash=%s"}, {Opt_err, NULL} }; @@ -737,6 +740,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay, unsigned long handle; unsigned long lock; unsigned long token_mask = 0; + int i; + int tpm2; + + tpm2 = tpm_is_tpm2(TPM_ANY_NUM); + if (tpm2 < 0) + return tpm2; + + opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1; while ((p = strsep(&c, " \t"))) { if (*p == '\0' || *p == ' ' || *p == '\t') @@ -790,6 +801,20 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return -EINVAL; opt->pcrlock = lock; break; + case Opt_hash: + for (i = 0; i < HASH_ALGO__LAST; i++) { + if (!strcmp(args[0].from, hash_algo_name[i])) { + opt->hash = i; + break; + } + } + if (i == HASH_ALGO__LAST) + return -EINVAL; + if (!tpm2 && i != HASH_ALGO_SHA1) { + pr_info("trusted_key: TPM 1.x only supports SHA-1.\n"); + return -EINVAL; + } + break; default: return -EINVAL; } -- cgit v1.2.3 From 5beb0c435bdde35a09376566b0e28f7df87c9f68 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 31 Oct 2015 17:53:44 +0200 Subject: keys, trusted: seal with a TPM2 authorization policy TPM2 supports authorization policies, which are essentially combinational logic statements repsenting the conditions where the data can be unsealed based on the TPM state. This patch enables to use authorization policies to seal trusted keys. Two following new options have been added for trusted keys: * 'policydigest=': provide an auth policy digest for sealing. * 'policyhandle=': provide a policy session handle for unsealing. If 'hash=' option is supplied after 'policydigest=' option, this will result an error because the state of the option would become mixed. Signed-off-by: Jarkko Sakkinen Tested-by: Colin Ian King Reviewed-by: Mimi Zohar Acked-by: Peter Huewe --- Documentation/security/keys-trusted-encrypted.txt | 34 +++++++++++++---------- drivers/char/tpm/tpm2-cmd.c | 24 +++++++++++++--- include/keys/trusted-type.h | 4 +++ security/keys/trusted.c | 26 +++++++++++++++++ 4 files changed, 70 insertions(+), 18 deletions(-) (limited to 'drivers/char') diff --git a/Documentation/security/keys-trusted-encrypted.txt b/Documentation/security/keys-trusted-encrypted.txt index fd2565b301e8..324ddf5223b3 100644 --- a/Documentation/security/keys-trusted-encrypted.txt +++ b/Documentation/security/keys-trusted-encrypted.txt @@ -27,20 +27,26 @@ Usage: keyctl print keyid options: - keyhandle= ascii hex value of sealing key default 0x40000000 (SRK) - keyauth= ascii hex auth for sealing key default 0x00...i - (40 ascii zeros) - blobauth= ascii hex auth for sealed data default 0x00... - (40 ascii zeros) - blobauth= ascii hex auth for sealed data default 0x00... - (40 ascii zeros) - pcrinfo= ascii hex of PCR_INFO or PCR_INFO_LONG (no default) - pcrlock= pcr number to be extended to "lock" blob - migratable= 0|1 indicating permission to reseal to new PCR values, - default 1 (resealing allowed) - hash= hash algorithm name as a string. For TPM 1.x the only - allowed value is sha1. For TPM 2.x the allowed values - are sha1, sha256, sha384, sha512 and sm3-256. + keyhandle= ascii hex value of sealing key default 0x40000000 (SRK) + keyauth= ascii hex auth for sealing key default 0x00...i + (40 ascii zeros) + blobauth= ascii hex auth for sealed data default 0x00... + (40 ascii zeros) + blobauth= ascii hex auth for sealed data default 0x00... + (40 ascii zeros) + pcrinfo= ascii hex of PCR_INFO or PCR_INFO_LONG (no default) + pcrlock= pcr number to be extended to "lock" blob + migratable= 0|1 indicating permission to reseal to new PCR values, + default 1 (resealing allowed) + hash= hash algorithm name as a string. For TPM 1.x the only + allowed value is sha1. For TPM 2.x the allowed values + are sha1, sha256, sha384, sha512 and sm3-256. + policydigest= digest for the authorization policy. must be calculated + with the same hash algorithm as specified by the 'hash=' + option. + policyhandle= handle to an authorization policy session that defines the + same policy and with the same hash algorithm as was used to + seal the key. "keyctl print" returns an ascii hex copy of the sealed key, which is in standard TPM_STORED_DATA format. The key length for new keys are always in bytes. diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index d9d082206f6e..45a634016f95 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -478,12 +478,26 @@ int tpm2_seal_trusted(struct tpm_chip *chip, tpm_buf_append_u8(&buf, payload->migratable); /* public */ - tpm_buf_append_u16(&buf, 14); + if (options->policydigest) + tpm_buf_append_u16(&buf, 14 + options->digest_len); + else + tpm_buf_append_u16(&buf, 14); tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH); tpm_buf_append_u16(&buf, hash); - tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH); - tpm_buf_append_u16(&buf, 0); /* policy digest size */ + + /* policy */ + if (options->policydigest) { + tpm_buf_append_u32(&buf, 0); + tpm_buf_append_u16(&buf, options->digest_len); + tpm_buf_append(&buf, options->policydigest, + options->digest_len); + } else { + tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH); + tpm_buf_append_u16(&buf, 0); + } + + /* public parameters */ tpm_buf_append_u16(&buf, TPM2_ALG_NULL); tpm_buf_append_u16(&buf, 0); @@ -613,7 +627,9 @@ static int tpm2_unseal(struct tpm_chip *chip, return rc; tpm_buf_append_u32(&buf, blob_handle); - tpm2_buf_append_auth(&buf, TPM2_RS_PW, + tpm2_buf_append_auth(&buf, + options->policyhandle ? + options->policyhandle : TPM2_RS_PW, NULL /* nonce */, 0, 0 /* session_attributes */, options->blobauth /* hmac */, diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index a6a100833ae9..42cf2d991bf4 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -18,6 +18,7 @@ #define MAX_KEY_SIZE 128 #define MAX_BLOB_SIZE 512 #define MAX_PCRINFO_SIZE 64 +#define MAX_DIGEST_SIZE 64 struct trusted_key_payload { struct rcu_head rcu; @@ -37,6 +38,9 @@ struct trusted_key_options { unsigned char pcrinfo[MAX_PCRINFO_SIZE]; int pcrlock; uint32_t hash; + uint32_t digest_len; + unsigned char policydigest[MAX_DIGEST_SIZE]; + uint32_t policyhandle; }; extern struct key_type key_type_trusted; diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 8f1300cab38e..e15baf722ae3 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -713,6 +713,8 @@ enum { Opt_keyhandle, Opt_keyauth, Opt_blobauth, Opt_pcrinfo, Opt_pcrlock, Opt_migratable, Opt_hash, + Opt_policydigest, + Opt_policyhandle, }; static const match_table_t key_tokens = { @@ -726,6 +728,8 @@ static const match_table_t key_tokens = { {Opt_pcrlock, "pcrlock=%s"}, {Opt_migratable, "migratable=%s"}, {Opt_hash, "hash=%s"}, + {Opt_policydigest, "policydigest=%s"}, + {Opt_policyhandle, "policyhandle=%s"}, {Opt_err, NULL} }; @@ -748,6 +752,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return tpm2; opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1; + opt->digest_len = hash_digest_size[opt->hash]; while ((p = strsep(&c, " \t"))) { if (*p == '\0' || *p == ' ' || *p == '\t') @@ -802,9 +807,13 @@ static int getoptions(char *c, struct trusted_key_payload *pay, opt->pcrlock = lock; break; case Opt_hash: + if (test_bit(Opt_policydigest, &token_mask)) + return -EINVAL; for (i = 0; i < HASH_ALGO__LAST; i++) { if (!strcmp(args[0].from, hash_algo_name[i])) { opt->hash = i; + opt->digest_len = + hash_digest_size[opt->hash]; break; } } @@ -815,6 +824,23 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return -EINVAL; } break; + case Opt_policydigest: + if (!tpm2 || + strlen(args[0].from) != (2 * opt->digest_len)) + return -EINVAL; + res = hex2bin(opt->policydigest, args[0].from, + opt->digest_len); + if (res < 0) + return -EINVAL; + break; + case Opt_policyhandle: + if (!tpm2) + return -EINVAL; + res = kstrtoul(args[0].from, 16, &handle); + if (res < 0) + return -EINVAL; + opt->policyhandle = handle; + break; default: return -EINVAL; } -- cgit v1.2.3