diff mbox series

[2/2] tpm2: Add a TPMv2 MMIO TIS driver

Message ID 20210707162604.84196-2-ilias.apalodimas@linaro.org
State Superseded
Headers show
Series [1/2,v2] tpm2: Introduce TIS tpm core | expand

Commit Message

Ilias Apalodimas July 7, 2021, 4:25 p.m. UTC
Add support for devices that expose a TPMv2 though MMIO.
Apart from those devices, we can use the driver in our QEMU setups and
test TPM related code which is difficult to achieve using the sandbox
driver (e.g test the EFI TCG2 protocol).

It's worth noting that a previous patch added TPMv2 TIS core functions,
which the current driver is consuming.

Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

---
Changes since v1:
- split off the tis core code into a different file
 drivers/tpm/Kconfig         |   9 +++
 drivers/tpm/Makefile        |   1 +
 drivers/tpm/tpm2_tis_mmio.c | 156 ++++++++++++++++++++++++++++++++++++
 3 files changed, 166 insertions(+)
 create mode 100644 drivers/tpm/tpm2_tis_mmio.c

-- 
2.32.0.rc0

Comments

Simon Glass July 11, 2021, midnight UTC | #1
Hi Ilias,

On Wed, 7 Jul 2021 at 10:26, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>

> Add support for devices that expose a TPMv2 though MMIO.

> Apart from those devices, we can use the driver in our QEMU setups and

> test TPM related code which is difficult to achieve using the sandbox

> driver (e.g test the EFI TCG2 protocol).

>

> It's worth noting that a previous patch added TPMv2 TIS core functions,

> which the current driver is consuming.

>

> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

> ---

> Changes since v1:

> - split off the tis core code into a different file

>  drivers/tpm/Kconfig         |   9 +++

>  drivers/tpm/Makefile        |   1 +

>  drivers/tpm/tpm2_tis_mmio.c | 156 ++++++++++++++++++++++++++++++++++++

>  3 files changed, 166 insertions(+)

>  create mode 100644 drivers/tpm/tpm2_tis_mmio.c


Looks good to me

>

> diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig

> index 9eebab5cfd90..406ee8716e1e 100644

> --- a/drivers/tpm/Kconfig

> +++ b/drivers/tpm/Kconfig

> @@ -161,6 +161,15 @@ config TPM2_FTPM_TEE

>         help

>           This driver supports firmware TPM running in TEE.

>

> +config TPM2_MMIO

> +       bool "MMIO based TPM2 Interface"

> +       depends on TPM_V2

> +       help

> +         This driver supports firmware TPM2.0 MMIO interface.

> +         The usual TPM operations and the 'tpm' command can be used to talk

> +         to the device using the standard TPM Interface Specification (TIS)

> +         protocol.

> +

>  endif # TPM_V2

>

>  endmenu

> diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile

> index f64d20067f88..1065c1874f58 100644

> --- a/drivers/tpm/Makefile

> +++ b/drivers/tpm/Makefile

> @@ -14,3 +14,4 @@ obj-$(CONFIG_$(SPL_TPL_)TPM2_CR50_I2C) += cr50_i2c.o

>  obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o

>  obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_spi.o

>  obj-$(CONFIG_TPM2_FTPM_TEE) += tpm2_ftpm_tee.o

> +obj-$(CONFIG_TPM2_MMIO) += tpm2_tis_core.o tpm2_tis_mmio.o

> diff --git a/drivers/tpm/tpm2_tis_mmio.c b/drivers/tpm/tpm2_tis_mmio.c

> new file mode 100644

> index 000000000000..2183a2807162

> --- /dev/null

> +++ b/drivers/tpm/tpm2_tis_mmio.c

> @@ -0,0 +1,156 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/*

> + * driver for mmio TCG/TIS TPM (trusted platform module).

> + *

> + * Specifications at www.trustedcomputinggroup.org

> + */

> +

> +#include <common.h>

> +#include <dm.h>

> +#include <log.h>

> +#include <tpm-v2.h>

> +#include <linux/bitops.h>

> +#include <linux/compiler.h>

> +#include <linux/delay.h>

> +#include <linux/errno.h>

> +#include <linux/types.h>

> +#include <linux/io.h>

> +#include <linux/unaligned/be_byteshift.h>

> +#include "tpm_tis.h"

> +#include "tpm_internal.h"

> +

> +struct tpm_tis_chip_data {

> +       unsigned int pcr_count;

> +       unsigned int pcr_select_min;

> +       unsigned int time_before_first_cmd_ms;

> +       void __iomem *iobase;

> +};


comments

> +

> +static int mmio_read_bytes(struct udevice *udev, u32 addr, u16 len,

> +                          u8 *result)

> +{

> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> +

> +       while (len--)

> +               *result++ = ioread8(drv_data->iobase + addr);


We normally put a blank line before the final return

> +       return 0;

> +}

> +

> +static int mmio_write_bytes(struct udevice *udev, u32 addr, u16 len,

> +                           const u8 *value)

> +{

> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> +

> +       while (len--)

> +               iowrite8(*value++, drv_data->iobase + addr);

> +       return 0;

> +}

> +

> +static int mmio_read16(struct udevice *udev, u32 addr, u16 *result)

> +{

> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> +

> +       *result = ioread16(drv_data->iobase + addr);

> +       return 0;

> +}

> +

> +static int mmio_read32(struct udevice *udev, u32 addr, u32 *result)

> +{

> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> +

> +       *result = ioread32(drv_data->iobase + addr);

> +       return 0;

> +}

> +

> +static int mmio_write32(struct udevice *udev, u32 addr, u32 value)

> +{

> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> +

> +       iowrite32(value, drv_data->iobase + addr);

> +       return 0;

> +}

> +

> +static struct tpm_tis_phy_ops phy_ops = {


Should be a uclass interface.

> +       .read_bytes = mmio_read_bytes,

> +       .write_bytes = mmio_write_bytes,

> +       .read16 = mmio_read16,

> +       .read32 = mmio_read32,

> +       .write32 = mmio_write32,

> +};

> +

> +static int tpm_tis_probe(struct udevice *udev)

> +{

> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> +       struct tpm_chip_priv *priv = dev_get_uclass_priv(udev);

> +       int ret = 0;

> +       fdt_addr_t ioaddr;

> +       u64 sz;

> +

> +       ioaddr = dev_read_addr(udev);

> +       if (ioaddr == FDT_ADDR_T_NONE)

> +               return -EINVAL;


consider this for easier debugging:

   return log_msg_ret("ioaddr", -EINVAL);

> +

> +       ret = dev_read_u64(udev, "reg", &sz);

> +       if (ret)

> +               return -EINVAL;

> +

> +       drv_data->iobase = ioremap(ioaddr, sz);

> +       log_info("Remapped TPM2 base: 0x%llx size: 0x%llx\n", ioaddr, sz);


log_debug() I think?

> +       tpm_tis_ops_register(udev, &phy_ops);

> +       ret = tpm_tis_init(udev);

> +       if (ret)

> +               goto iounmap;

> +

> +       priv->pcr_count = drv_data->pcr_count;

> +       priv->pcr_select_min = drv_data->pcr_select_min;

> +       /*

> +        * Although the driver probably works with a TPMv1 our Kconfig

> +        * limits the driver to TPMv2 only

> +        */

> +       priv->version = TPM_V2;

> +

> +       return ret;

> +iounmap:

> +       iounmap(drv_data->iobase);

> +       return -EINVAL;

> +}

> +

> +static int tpm_tis_remove(struct udevice *udev)

> +{

> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> +

> +       iounmap(drv_data->iobase);

> +       return tpm_tis_cleanup(udev);

> +}

> +

> +static const struct tpm_ops tpm_tis_ops = {

> +       .open           = tpm_tis_open,

> +       .close          = tpm_tis_close,

> +       .get_desc       = tpm_tis_get_desc,

> +       .send           = tpm_tis_send,

> +       .recv           = tpm_tis_recv,

> +       .cleanup        = tpm_tis_cleanup,

> +};

> +

> +static const struct tpm_tis_chip_data tpm_tis_std_chip_data = {

> +       .pcr_count = 24,

> +       .pcr_select_min = 3,

> +};

> +

> +static const struct udevice_id tpm_tis_ids[] = {

> +       {

> +               .compatible = "tcg,tpm-tis-mmio",

> +               .data = (ulong)&tpm_tis_std_chip_data,

> +       },

> +       { }

> +};

> +

> +U_BOOT_DRIVER(tpm_tis_mmio) = {

> +       .name   = "tpm_tis_mmio",

> +       .id     = UCLASS_TPM,

> +       .of_match = tpm_tis_ids,

> +       .ops    = &tpm_tis_ops,

> +       .probe  = tpm_tis_probe,

> +       .remove = tpm_tis_remove,

> +       .priv_auto      = sizeof(struct tpm_chip),

> +};

> --

> 2.32.0.rc0

>


Regards,
Simon
Ilias Apalodimas July 12, 2021, 6:22 p.m. UTC | #2
Hi Simon, 
On Sat, Jul 10, 2021 at 06:00:59PM -0600, Simon Glass wrote:
> Hi Ilias,

> 

> On Wed, 7 Jul 2021 at 10:26, Ilias Apalodimas

> <ilias.apalodimas@linaro.org> wrote:

> >

> > Add support for devices that expose a TPMv2 though MMIO.

> > Apart from those devices, we can use the driver in our QEMU setups and

> > test TPM related code which is difficult to achieve using the sandbox

> > driver (e.g test the EFI TCG2 protocol).

> >

> > It's worth noting that a previous patch added TPMv2 TIS core functions,

> > which the current driver is consuming.

> >

> > Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

> > ---

> > Changes since v1:

> > - split off the tis core code into a different file

> >  drivers/tpm/Kconfig         |   9 +++

> >  drivers/tpm/Makefile        |   1 +

> >  drivers/tpm/tpm2_tis_mmio.c | 156 ++++++++++++++++++++++++++++++++++++

> >  3 files changed, 166 insertions(+)

> >  create mode 100644 drivers/tpm/tpm2_tis_mmio.c

> 

> Looks good to me


Thanks! 

> >

> > +struct tpm_tis_chip_data {

> > +       unsigned int pcr_count;

> > +       unsigned int pcr_select_min;

> > +       unsigned int time_before_first_cmd_ms;

> > +       void __iomem *iobase;

> > +};

> 

> comments

> 


You mean on all the internal functions?
Sure I can add them

> > +

> > +static int mmio_read_bytes(struct udevice *udev, u32 addr, u16 len,

> > +                          u8 *result)

> > +{

> > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > +

> > +       while (len--)

> > +               *result++ = ioread8(drv_data->iobase + addr);

> 

> We normally put a blank line before the final return

> 


sure

> > +       return 0;

> > +}

> > +

> > +static int mmio_write_bytes(struct udevice *udev, u32 addr, u16 len,

> > +                           const u8 *value)

> > +{

> > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > +

> > +       while (len--)

> > +               iowrite8(*value++, drv_data->iobase + addr);

> > +       return 0;

> > +}

> > +

> > +static int mmio_read16(struct udevice *udev, u32 addr, u16 *result)

> > +{

> > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > +

> > +       *result = ioread16(drv_data->iobase + addr);

> > +       return 0;

> > +}

> > +

> > +static int mmio_read32(struct udevice *udev, u32 addr, u32 *result)

> > +{

> > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > +

> > +       *result = ioread32(drv_data->iobase + addr);

> > +       return 0;

> > +}

> > +

> > +static int mmio_write32(struct udevice *udev, u32 addr, u32 value)

> > +{

> > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > +

> > +       iowrite32(value, drv_data->iobase + addr);

> > +       return 0;

> > +}

> > +

> > +static struct tpm_tis_phy_ops phy_ops = {

> 

> Should be a uclass interface.

>


Why? A uclass is supposed to describe and abstract hardware.  This is just
a specific implementation of a TPM, not all TPMs are TIS compliant. We already 
have a uclass for those.

> > +       .read_bytes = mmio_read_bytes,

> > +       .write_bytes = mmio_write_bytes,

> > +       .read16 = mmio_read16,

> > +       .read32 = mmio_read32,

> > +       .write32 = mmio_write32,

> > +};

> > +

> > +static int tpm_tis_probe(struct udevice *udev)

> > +{

> > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > +       struct tpm_chip_priv *priv = dev_get_uclass_priv(udev);

> > +       int ret = 0;

> > +       fdt_addr_t ioaddr;

> > +       u64 sz;

> > +

> > +       ioaddr = dev_read_addr(udev);

> > +       if (ioaddr == FDT_ADDR_T_NONE)

> > +               return -EINVAL;

> 

> consider this for easier debugging:

> 

>    return log_msg_ret("ioaddr", -EINVAL);

> 


Sure

> > +

> > +       ret = dev_read_u64(udev, "reg", &sz);

> > +       if (ret)

> > +               return -EINVAL;

> > +

> > +       drv_data->iobase = ioremap(ioaddr, sz);

> > +       log_info("Remapped TPM2 base: 0x%llx size: 0x%llx\n", ioaddr, sz);

> 

> log_debug() I think?

>

Yea, that's a leftover of my initial code, were I needed to make sure the
ioremap worked.

> > +       tpm_tis_ops_register(udev, &phy_ops);

> > +       ret = tpm_tis_init(udev);

> > +       if (ret)

> > +               goto iounmap;

> > +

> > +       priv->pcr_count = drv_data->pcr_count;

> > +       priv->pcr_select_min = drv_data->pcr_select_min;

> > +       /*

> > +        * Although the driver probably works with a TPMv1 our Kconfig

> > +        * limits the driver to TPMv2 only

> > +        */

> > +       priv->version = TPM_V2;

> > +

> > +       return ret;

> > +iounmap:

> > +       iounmap(drv_data->iobase);

> > +       return -EINVAL;

> > +}

> > +

> > +static int tpm_tis_remove(struct udevice *udev)

> > +{

> > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > +

> > +       iounmap(drv_data->iobase);

> > +       return tpm_tis_cleanup(udev);

> > +}

> > +

> > +static const struct tpm_ops tpm_tis_ops = {

> > +       .open           = tpm_tis_open,

> > +       .close          = tpm_tis_close,

> > +       .get_desc       = tpm_tis_get_desc,

> > +       .send           = tpm_tis_send,

> > +       .recv           = tpm_tis_recv,

> > +       .cleanup        = tpm_tis_cleanup,

> > +};

> > +

> > +static const struct tpm_tis_chip_data tpm_tis_std_chip_data = {

> > +       .pcr_count = 24,

> > +       .pcr_select_min = 3,

> > +};

> > +

> > +static const struct udevice_id tpm_tis_ids[] = {

> > +       {

> > +               .compatible = "tcg,tpm-tis-mmio",

> > +               .data = (ulong)&tpm_tis_std_chip_data,

> > +       },

> > +       { }

> > +};

> > +

> > +U_BOOT_DRIVER(tpm_tis_mmio) = {

> > +       .name   = "tpm_tis_mmio",

> > +       .id     = UCLASS_TPM,

> > +       .of_match = tpm_tis_ids,

> > +       .ops    = &tpm_tis_ops,

> > +       .probe  = tpm_tis_probe,

> > +       .remove = tpm_tis_remove,

> > +       .priv_auto      = sizeof(struct tpm_chip),

> > +};

> > --

> > 2.32.0.rc0

> >

> 

> Regards,

> Simon


Thanks for looking at this
/Ilias
Heinrich Schuchardt July 12, 2021, 7:13 p.m. UTC | #3
On 7/12/21 8:22 PM, Ilias Apalodimas wrote:
> Hi Simon,

> On Sat, Jul 10, 2021 at 06:00:59PM -0600, Simon Glass wrote:

>> Hi Ilias,

>>

>> On Wed, 7 Jul 2021 at 10:26, Ilias Apalodimas

>> <ilias.apalodimas@linaro.org> wrote:

>>>

>>> Add support for devices that expose a TPMv2 though MMIO.

>>> Apart from those devices, we can use the driver in our QEMU setups and

>>> test TPM related code which is difficult to achieve using the sandbox

>>> driver (e.g test the EFI TCG2 protocol).

>>>

>>> It's worth noting that a previous patch added TPMv2 TIS core functions,

>>> which the current driver is consuming.

>>>

>>> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

>>> ---

>>> Changes since v1:

>>> - split off the tis core code into a different file

>>>   drivers/tpm/Kconfig         |   9 +++

>>>   drivers/tpm/Makefile        |   1 +

>>>   drivers/tpm/tpm2_tis_mmio.c | 156 ++++++++++++++++++++++++++++++++++++

>>>   3 files changed, 166 insertions(+)

>>>   create mode 100644 drivers/tpm/tpm2_tis_mmio.c

>>

>> Looks good to me

>

> Thanks!

>

>>>

>>> +struct tpm_tis_chip_data {

>>> +       unsigned int pcr_count;

>>> +       unsigned int pcr_select_min;

>>> +       unsigned int time_before_first_cmd_ms;

>>> +       void __iomem *iobase;

>>> +};

>>

>> comments

>>

>

> You mean on all the internal functions?

> Sure I can add them

>

>>> +

>>> +static int mmio_read_bytes(struct udevice *udev, u32 addr, u16 len,

>>> +                          u8 *result)

>>> +{

>>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

>>> +

>>> +       while (len--)

>>> +               *result++ = ioread8(drv_data->iobase + addr);

>>

>> We normally put a blank line before the final return

>>

>

> sure

>

>>> +       return 0;

>>> +}

>>> +

>>> +static int mmio_write_bytes(struct udevice *udev, u32 addr, u16 len,

>>> +                           const u8 *value)

>>> +{

>>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

>>> +

>>> +       while (len--)

>>> +               iowrite8(*value++, drv_data->iobase + addr);

>>> +       return 0;

>>> +}

>>> +

>>> +static int mmio_read16(struct udevice *udev, u32 addr, u16 *result)

>>> +{

>>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

>>> +

>>> +       *result = ioread16(drv_data->iobase + addr);

>>> +       return 0;

>>> +}

>>> +

>>> +static int mmio_read32(struct udevice *udev, u32 addr, u32 *result)

>>> +{

>>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

>>> +

>>> +       *result = ioread32(drv_data->iobase + addr);

>>> +       return 0;

>>> +}

>>> +

>>> +static int mmio_write32(struct udevice *udev, u32 addr, u32 value)

>>> +{

>>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

>>> +

>>> +       iowrite32(value, drv_data->iobase + addr);

>>> +       return 0;

>>> +}

>>> +

>>> +static struct tpm_tis_phy_ops phy_ops = {

>>

>> Should be a uclass interface.

>>

>

> Why? A uclass is supposed to describe and abstract hardware.  This is just

> a specific implementation of a TPM, not all TPMs are TIS compliant. We already

> have a uclass for those.


The design proposed by Ilias matches what we find in Linux for TPM.
Keeping the same structure should render porting of drivers for
additional devices easier.

Best regards

Heinrich

>

>>> +       .read_bytes = mmio_read_bytes,

>>> +       .write_bytes = mmio_write_bytes,

>>> +       .read16 = mmio_read16,

>>> +       .read32 = mmio_read32,

>>> +       .write32 = mmio_write32,

>>> +};

>>> +

>>> +static int tpm_tis_probe(struct udevice *udev)

>>> +{

>>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

>>> +       struct tpm_chip_priv *priv = dev_get_uclass_priv(udev);

>>> +       int ret = 0;

>>> +       fdt_addr_t ioaddr;

>>> +       u64 sz;

>>> +

>>> +       ioaddr = dev_read_addr(udev);

>>> +       if (ioaddr == FDT_ADDR_T_NONE)

>>> +               return -EINVAL;

>>

>> consider this for easier debugging:

>>

>>     return log_msg_ret("ioaddr", -EINVAL);

>>

>

> Sure

>

>>> +

>>> +       ret = dev_read_u64(udev, "reg", &sz);

>>> +       if (ret)

>>> +               return -EINVAL;

>>> +

>>> +       drv_data->iobase = ioremap(ioaddr, sz);

>>> +       log_info("Remapped TPM2 base: 0x%llx size: 0x%llx\n", ioaddr, sz);

>>

>> log_debug() I think?

>>

> Yea, that's a leftover of my initial code, were I needed to make sure the

> ioremap worked.

>

>>> +       tpm_tis_ops_register(udev, &phy_ops);

>>> +       ret = tpm_tis_init(udev);

>>> +       if (ret)

>>> +               goto iounmap;

>>> +

>>> +       priv->pcr_count = drv_data->pcr_count;

>>> +       priv->pcr_select_min = drv_data->pcr_select_min;

>>> +       /*

>>> +        * Although the driver probably works with a TPMv1 our Kconfig

>>> +        * limits the driver to TPMv2 only

>>> +        */

>>> +       priv->version = TPM_V2;

>>> +

>>> +       return ret;

>>> +iounmap:

>>> +       iounmap(drv_data->iobase);

>>> +       return -EINVAL;

>>> +}

>>> +

>>> +static int tpm_tis_remove(struct udevice *udev)

>>> +{

>>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

>>> +

>>> +       iounmap(drv_data->iobase);

>>> +       return tpm_tis_cleanup(udev);

>>> +}

>>> +

>>> +static const struct tpm_ops tpm_tis_ops = {

>>> +       .open           = tpm_tis_open,

>>> +       .close          = tpm_tis_close,

>>> +       .get_desc       = tpm_tis_get_desc,

>>> +       .send           = tpm_tis_send,

>>> +       .recv           = tpm_tis_recv,

>>> +       .cleanup        = tpm_tis_cleanup,

>>> +};

>>> +

>>> +static const struct tpm_tis_chip_data tpm_tis_std_chip_data = {

>>> +       .pcr_count = 24,

>>> +       .pcr_select_min = 3,

>>> +};

>>> +

>>> +static const struct udevice_id tpm_tis_ids[] = {

>>> +       {

>>> +               .compatible = "tcg,tpm-tis-mmio",

>>> +               .data = (ulong)&tpm_tis_std_chip_data,

>>> +       },

>>> +       { }

>>> +};

>>> +

>>> +U_BOOT_DRIVER(tpm_tis_mmio) = {

>>> +       .name   = "tpm_tis_mmio",

>>> +       .id     = UCLASS_TPM,

>>> +       .of_match = tpm_tis_ids,

>>> +       .ops    = &tpm_tis_ops,

>>> +       .probe  = tpm_tis_probe,

>>> +       .remove = tpm_tis_remove,

>>> +       .priv_auto      = sizeof(struct tpm_chip),

>>> +};

>>> --

>>> 2.32.0.rc0

>>>

>>

>> Regards,

>> Simon

>

> Thanks for looking at this

> /Ilias

>
Simon Glass July 12, 2021, 7:38 p.m. UTC | #4
Hi Ilias,

On Mon, 12 Jul 2021 at 12:22, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>

> Hi Simon,

> On Sat, Jul 10, 2021 at 06:00:59PM -0600, Simon Glass wrote:

> > Hi Ilias,

> >

> > On Wed, 7 Jul 2021 at 10:26, Ilias Apalodimas

> > <ilias.apalodimas@linaro.org> wrote:

> > >

> > > Add support for devices that expose a TPMv2 though MMIO.

> > > Apart from those devices, we can use the driver in our QEMU setups and

> > > test TPM related code which is difficult to achieve using the sandbox

> > > driver (e.g test the EFI TCG2 protocol).

> > >

> > > It's worth noting that a previous patch added TPMv2 TIS core functions,

> > > which the current driver is consuming.

> > >

> > > Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

> > > ---

> > > Changes since v1:

> > > - split off the tis core code into a different file

> > >  drivers/tpm/Kconfig         |   9 +++

> > >  drivers/tpm/Makefile        |   1 +

> > >  drivers/tpm/tpm2_tis_mmio.c | 156 ++++++++++++++++++++++++++++++++++++

> > >  3 files changed, 166 insertions(+)

> > >  create mode 100644 drivers/tpm/tpm2_tis_mmio.c

> >

> > Looks good to me

>

> Thanks!

>

> > >

> > > +struct tpm_tis_chip_data {

> > > +       unsigned int pcr_count;

> > > +       unsigned int pcr_select_min;

> > > +       unsigned int time_before_first_cmd_ms;

> > > +       void __iomem *iobase;

> > > +};

> >

> > comments

> >

>

> You mean on all the internal functions?

> Sure I can add them


I mean for struct members.

For internal functions a comment is a good idea depending on how
valuable it is - things like return values, args. But if just
implementing a fully documented API, it may not really add much.

>

> > > +

> > > +static int mmio_read_bytes(struct udevice *udev, u32 addr, u16 len,

> > > +                          u8 *result)

> > > +{

> > > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > > +

> > > +       while (len--)

> > > +               *result++ = ioread8(drv_data->iobase + addr);

> >

> > We normally put a blank line before the final return

> >

>

> sure

>

> > > +       return 0;

> > > +}

> > > +

> > > +static int mmio_write_bytes(struct udevice *udev, u32 addr, u16 len,

> > > +                           const u8 *value)

> > > +{

> > > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > > +

> > > +       while (len--)

> > > +               iowrite8(*value++, drv_data->iobase + addr);

> > > +       return 0;

> > > +}

> > > +

> > > +static int mmio_read16(struct udevice *udev, u32 addr, u16 *result)

> > > +{

> > > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > > +

> > > +       *result = ioread16(drv_data->iobase + addr);

> > > +       return 0;

> > > +}

> > > +

> > > +static int mmio_read32(struct udevice *udev, u32 addr, u32 *result)

> > > +{

> > > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > > +

> > > +       *result = ioread32(drv_data->iobase + addr);

> > > +       return 0;

> > > +}

> > > +

> > > +static int mmio_write32(struct udevice *udev, u32 addr, u32 value)

> > > +{

> > > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > > +

> > > +       iowrite32(value, drv_data->iobase + addr);

> > > +       return 0;

> > > +}

> > > +

> > > +static struct tpm_tis_phy_ops phy_ops = {

> >

> > Should be a uclass interface.

> >

>

> Why? A uclass is supposed to describe and abstract hardware.  This is just

> a specific implementation of a TPM, not all TPMs are TIS compliant. We already

> have a uclass for those.


Who told you that a uclass is supposed to describe and abstract hardware? :-)

The uclass is how driver model does APIs, so normally a uclass would
be used for any API. There are exceptions, but this one actually looks
like a useful interface we should have.

>

> > > +       .read_bytes = mmio_read_bytes,

> > > +       .write_bytes = mmio_write_bytes,

> > > +       .read16 = mmio_read16,

> > > +       .read32 = mmio_read32,

> > > +       .write32 = mmio_write32,

> > > +};

> > > +

> > > +static int tpm_tis_probe(struct udevice *udev)

> > > +{

> > > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > > +       struct tpm_chip_priv *priv = dev_get_uclass_priv(udev);

> > > +       int ret = 0;

> > > +       fdt_addr_t ioaddr;

> > > +       u64 sz;

> > > +

> > > +       ioaddr = dev_read_addr(udev);

> > > +       if (ioaddr == FDT_ADDR_T_NONE)

> > > +               return -EINVAL;

> >

> > consider this for easier debugging:

> >

> >    return log_msg_ret("ioaddr", -EINVAL);

> >

>

> Sure

>

> > > +

> > > +       ret = dev_read_u64(udev, "reg", &sz);

> > > +       if (ret)

> > > +               return -EINVAL;

> > > +

> > > +       drv_data->iobase = ioremap(ioaddr, sz);

> > > +       log_info("Remapped TPM2 base: 0x%llx size: 0x%llx\n", ioaddr, sz);

> >

> > log_debug() I think?

> >

> Yea, that's a leftover of my initial code, were I needed to make sure the

> ioremap worked.

>

> > > +       tpm_tis_ops_register(udev, &phy_ops);

> > > +       ret = tpm_tis_init(udev);

> > > +       if (ret)

> > > +               goto iounmap;

> > > +

> > > +       priv->pcr_count = drv_data->pcr_count;

> > > +       priv->pcr_select_min = drv_data->pcr_select_min;

> > > +       /*

> > > +        * Although the driver probably works with a TPMv1 our Kconfig

> > > +        * limits the driver to TPMv2 only

> > > +        */

> > > +       priv->version = TPM_V2;

> > > +

> > > +       return ret;

> > > +iounmap:

> > > +       iounmap(drv_data->iobase);

> > > +       return -EINVAL;

> > > +}

> > > +

> > > +static int tpm_tis_remove(struct udevice *udev)

> > > +{

> > > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > > +

> > > +       iounmap(drv_data->iobase);

> > > +       return tpm_tis_cleanup(udev);

> > > +}

> > > +

> > > +static const struct tpm_ops tpm_tis_ops = {

> > > +       .open           = tpm_tis_open,

> > > +       .close          = tpm_tis_close,

> > > +       .get_desc       = tpm_tis_get_desc,

> > > +       .send           = tpm_tis_send,

> > > +       .recv           = tpm_tis_recv,

> > > +       .cleanup        = tpm_tis_cleanup,

> > > +};

> > > +

> > > +static const struct tpm_tis_chip_data tpm_tis_std_chip_data = {

> > > +       .pcr_count = 24,

> > > +       .pcr_select_min = 3,

> > > +};

> > > +

> > > +static const struct udevice_id tpm_tis_ids[] = {

> > > +       {

> > > +               .compatible = "tcg,tpm-tis-mmio",

> > > +               .data = (ulong)&tpm_tis_std_chip_data,

> > > +       },

> > > +       { }

> > > +};

> > > +

> > > +U_BOOT_DRIVER(tpm_tis_mmio) = {

> > > +       .name   = "tpm_tis_mmio",

> > > +       .id     = UCLASS_TPM,

> > > +       .of_match = tpm_tis_ids,

> > > +       .ops    = &tpm_tis_ops,

> > > +       .probe  = tpm_tis_probe,

> > > +       .remove = tpm_tis_remove,

> > > +       .priv_auto      = sizeof(struct tpm_chip),

> > > +};

> > > --

> > > 2.32.0.rc0

> > >

> >

> > Regards,

> > Simon

>

> Thanks for looking at this

> /Ilias


Likewise.

Regards,
Simon
Simon Glass July 12, 2021, 7:43 p.m. UTC | #5
Hi Heinrich,

On Mon, 12 Jul 2021 at 13:19, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>

> On 7/12/21 8:22 PM, Ilias Apalodimas wrote:

> > Hi Simon,

> > On Sat, Jul 10, 2021 at 06:00:59PM -0600, Simon Glass wrote:

> >> Hi Ilias,

> >>

> >> On Wed, 7 Jul 2021 at 10:26, Ilias Apalodimas

> >> <ilias.apalodimas@linaro.org> wrote:

> >>>

> >>> Add support for devices that expose a TPMv2 though MMIO.

> >>> Apart from those devices, we can use the driver in our QEMU setups and

> >>> test TPM related code which is difficult to achieve using the sandbox

> >>> driver (e.g test the EFI TCG2 protocol).

> >>>

> >>> It's worth noting that a previous patch added TPMv2 TIS core functions,

> >>> which the current driver is consuming.

> >>>

> >>> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

> >>> ---

> >>> Changes since v1:

> >>> - split off the tis core code into a different file

> >>>   drivers/tpm/Kconfig         |   9 +++

> >>>   drivers/tpm/Makefile        |   1 +

> >>>   drivers/tpm/tpm2_tis_mmio.c | 156 ++++++++++++++++++++++++++++++++++++

> >>>   3 files changed, 166 insertions(+)

> >>>   create mode 100644 drivers/tpm/tpm2_tis_mmio.c

> >>

> >> Looks good to me

> >

> > Thanks!

> >

> >>>

> >>> +struct tpm_tis_chip_data {

> >>> +       unsigned int pcr_count;

> >>> +       unsigned int pcr_select_min;

> >>> +       unsigned int time_before_first_cmd_ms;

> >>> +       void __iomem *iobase;

> >>> +};

> >>

> >> comments

> >>

> >

> > You mean on all the internal functions?

> > Sure I can add them

> >

> >>> +

> >>> +static int mmio_read_bytes(struct udevice *udev, u32 addr, u16 len,

> >>> +                          u8 *result)

> >>> +{

> >>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> >>> +

> >>> +       while (len--)

> >>> +               *result++ = ioread8(drv_data->iobase + addr);

> >>

> >> We normally put a blank line before the final return

> >>

> >

> > sure

> >

> >>> +       return 0;

> >>> +}

> >>> +

> >>> +static int mmio_write_bytes(struct udevice *udev, u32 addr, u16 len,

> >>> +                           const u8 *value)

> >>> +{

> >>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> >>> +

> >>> +       while (len--)

> >>> +               iowrite8(*value++, drv_data->iobase + addr);

> >>> +       return 0;

> >>> +}

> >>> +

> >>> +static int mmio_read16(struct udevice *udev, u32 addr, u16 *result)

> >>> +{

> >>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> >>> +

> >>> +       *result = ioread16(drv_data->iobase + addr);

> >>> +       return 0;

> >>> +}

> >>> +

> >>> +static int mmio_read32(struct udevice *udev, u32 addr, u32 *result)

> >>> +{

> >>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> >>> +

> >>> +       *result = ioread32(drv_data->iobase + addr);

> >>> +       return 0;

> >>> +}

> >>> +

> >>> +static int mmio_write32(struct udevice *udev, u32 addr, u32 value)

> >>> +{

> >>> +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> >>> +

> >>> +       iowrite32(value, drv_data->iobase + addr);

> >>> +       return 0;

> >>> +}

> >>> +

> >>> +static struct tpm_tis_phy_ops phy_ops = {

> >>

> >> Should be a uclass interface.

> >>

> >

> > Why? A uclass is supposed to describe and abstract hardware.  This is just

> > a specific implementation of a TPM, not all TPMs are TIS compliant. We already

> > have a uclass for those.

>

> The design proposed by Ilias matches what we find in Linux for TPM.

> Keeping the same structure should render porting of drivers for

> additional devices easier.


Can you please be more specific in your comment? What change are you asking for?

Regards,
Simon
Heinrich Schuchardt July 12, 2021, 10:46 p.m. UTC | #6
Am 12. Juli 2021 21:43:53 MESZ schrieb Simon Glass <sjg@chromium.org>:
>Hi Heinrich,

>

>On Mon, 12 Jul 2021 at 13:19, Heinrich Schuchardt <xypron.glpk@gmx.de>

>wrote:

>>

>> On 7/12/21 8:22 PM, Ilias Apalodimas wrote:

>> > Hi Simon,

>> > On Sat, Jul 10, 2021 at 06:00:59PM -0600, Simon Glass wrote:

>> >> Hi Ilias,

>> >>

>> >> On Wed, 7 Jul 2021 at 10:26, Ilias Apalodimas

>> >> <ilias.apalodimas@linaro.org> wrote:

>> >>>

>> >>> Add support for devices that expose a TPMv2 though MMIO.

>> >>> Apart from those devices, we can use the driver in our QEMU

>setups and

>> >>> test TPM related code which is difficult to achieve using the

>sandbox

>> >>> driver (e.g test the EFI TCG2 protocol).

>> >>>

>> >>> It's worth noting that a previous patch added TPMv2 TIS core

>functions,

>> >>> which the current driver is consuming.

>> >>>

>> >>> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

>> >>> ---

>> >>> Changes since v1:

>> >>> - split off the tis core code into a different file

>> >>>   drivers/tpm/Kconfig         |   9 +++

>> >>>   drivers/tpm/Makefile        |   1 +

>> >>>   drivers/tpm/tpm2_tis_mmio.c | 156

>++++++++++++++++++++++++++++++++++++

>> >>>   3 files changed, 166 insertions(+)

>> >>>   create mode 100644 drivers/tpm/tpm2_tis_mmio.c

>> >>

>> >> Looks good to me

>> >

>> > Thanks!

>> >

>> >>>

>> >>> +struct tpm_tis_chip_data {

>> >>> +       unsigned int pcr_count;

>> >>> +       unsigned int pcr_select_min;

>> >>> +       unsigned int time_before_first_cmd_ms;

>> >>> +       void __iomem *iobase;

>> >>> +};

>> >>

>> >> comments

>> >>

>> >

>> > You mean on all the internal functions?

>> > Sure I can add them

>> >

>> >>> +

>> >>> +static int mmio_read_bytes(struct udevice *udev, u32 addr, u16

>len,

>> >>> +                          u8 *result)

>> >>> +{

>> >>> +       struct tpm_tis_chip_data *drv_data = (void

>*)dev_get_driver_data(udev);

>> >>> +

>> >>> +       while (len--)

>> >>> +               *result++ = ioread8(drv_data->iobase + addr);

>> >>

>> >> We normally put a blank line before the final return

>> >>

>> >

>> > sure

>> >

>> >>> +       return 0;

>> >>> +}

>> >>> +

>> >>> +static int mmio_write_bytes(struct udevice *udev, u32 addr, u16

>len,

>> >>> +                           const u8 *value)

>> >>> +{

>> >>> +       struct tpm_tis_chip_data *drv_data = (void

>*)dev_get_driver_data(udev);

>> >>> +

>> >>> +       while (len--)

>> >>> +               iowrite8(*value++, drv_data->iobase + addr);

>> >>> +       return 0;

>> >>> +}

>> >>> +

>> >>> +static int mmio_read16(struct udevice *udev, u32 addr, u16

>*result)

>> >>> +{

>> >>> +       struct tpm_tis_chip_data *drv_data = (void

>*)dev_get_driver_data(udev);

>> >>> +

>> >>> +       *result = ioread16(drv_data->iobase + addr);

>> >>> +       return 0;

>> >>> +}

>> >>> +

>> >>> +static int mmio_read32(struct udevice *udev, u32 addr, u32

>*result)

>> >>> +{

>> >>> +       struct tpm_tis_chip_data *drv_data = (void

>*)dev_get_driver_data(udev);

>> >>> +

>> >>> +       *result = ioread32(drv_data->iobase + addr);

>> >>> +       return 0;

>> >>> +}

>> >>> +

>> >>> +static int mmio_write32(struct udevice *udev, u32 addr, u32

>value)

>> >>> +{

>> >>> +       struct tpm_tis_chip_data *drv_data = (void

>*)dev_get_driver_data(udev);

>> >>> +

>> >>> +       iowrite32(value, drv_data->iobase + addr);

>> >>> +       return 0;

>> >>> +}

>> >>> +

>> >>> +static struct tpm_tis_phy_ops phy_ops = {

>> >>

>> >> Should be a uclass interface.

>> >>

>> >

>> > Why? A uclass is supposed to describe and abstract hardware.  This

>is just

>> > a specific implementation of a TPM, not all TPMs are TIS compliant.

>We already

>> > have a uclass for those.

>>

>> The design proposed by Ilias matches what we find in Linux for TPM.

>> Keeping the same structure should render porting of drivers for

>> additional devices easier.

>

>Can you please be more specific in your comment? What change are you

>asking for?

>

>Regards,

>Simon


None. I think the design is ok as is.

I was responding to your idea of adding another uclass.

Best regards

Heinrich
Simon Glass July 13, 2021, 4:52 p.m. UTC | #7
Hi Heinrich,

On Mon, 12 Jul 2021 at 16:47, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>

> Am 12. Juli 2021 21:43:53 MESZ schrieb Simon Glass <sjg@chromium.org>:

> >Hi Heinrich,

> >

> >On Mon, 12 Jul 2021 at 13:19, Heinrich Schuchardt <xypron.glpk@gmx.de>

> >wrote:

> >>

> >> On 7/12/21 8:22 PM, Ilias Apalodimas wrote:

> >> > Hi Simon,

> >> > On Sat, Jul 10, 2021 at 06:00:59PM -0600, Simon Glass wrote:

> >> >> Hi Ilias,

> >> >>

> >> >> On Wed, 7 Jul 2021 at 10:26, Ilias Apalodimas

> >> >> <ilias.apalodimas@linaro.org> wrote:

> >> >>>

> >> >>> Add support for devices that expose a TPMv2 though MMIO.

> >> >>> Apart from those devices, we can use the driver in our QEMU

> >setups and

> >> >>> test TPM related code which is difficult to achieve using the

> >sandbox

> >> >>> driver (e.g test the EFI TCG2 protocol).

> >> >>>

> >> >>> It's worth noting that a previous patch added TPMv2 TIS core

> >functions,

> >> >>> which the current driver is consuming.

> >> >>>

> >> >>> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

> >> >>> ---

> >> >>> Changes since v1:

> >> >>> - split off the tis core code into a different file

> >> >>>   drivers/tpm/Kconfig         |   9 +++

> >> >>>   drivers/tpm/Makefile        |   1 +

> >> >>>   drivers/tpm/tpm2_tis_mmio.c | 156

> >++++++++++++++++++++++++++++++++++++

> >> >>>   3 files changed, 166 insertions(+)

> >> >>>   create mode 100644 drivers/tpm/tpm2_tis_mmio.c

> >> >>

> >> >> Looks good to me

> >> >

> >> > Thanks!

> >> >

> >> >>>

> >> >>> +struct tpm_tis_chip_data {

> >> >>> +       unsigned int pcr_count;

> >> >>> +       unsigned int pcr_select_min;

> >> >>> +       unsigned int time_before_first_cmd_ms;

> >> >>> +       void __iomem *iobase;

> >> >>> +};

> >> >>

> >> >> comments

> >> >>

> >> >

> >> > You mean on all the internal functions?

> >> > Sure I can add them

> >> >

> >> >>> +

> >> >>> +static int mmio_read_bytes(struct udevice *udev, u32 addr, u16

> >len,

> >> >>> +                          u8 *result)

> >> >>> +{

> >> >>> +       struct tpm_tis_chip_data *drv_data = (void

> >*)dev_get_driver_data(udev);

> >> >>> +

> >> >>> +       while (len--)

> >> >>> +               *result++ = ioread8(drv_data->iobase + addr);

> >> >>

> >> >> We normally put a blank line before the final return

> >> >>

> >> >

> >> > sure

> >> >

> >> >>> +       return 0;

> >> >>> +}

> >> >>> +

> >> >>> +static int mmio_write_bytes(struct udevice *udev, u32 addr, u16

> >len,

> >> >>> +                           const u8 *value)

> >> >>> +{

> >> >>> +       struct tpm_tis_chip_data *drv_data = (void

> >*)dev_get_driver_data(udev);

> >> >>> +

> >> >>> +       while (len--)

> >> >>> +               iowrite8(*value++, drv_data->iobase + addr);

> >> >>> +       return 0;

> >> >>> +}

> >> >>> +

> >> >>> +static int mmio_read16(struct udevice *udev, u32 addr, u16

> >*result)

> >> >>> +{

> >> >>> +       struct tpm_tis_chip_data *drv_data = (void

> >*)dev_get_driver_data(udev);

> >> >>> +

> >> >>> +       *result = ioread16(drv_data->iobase + addr);

> >> >>> +       return 0;

> >> >>> +}

> >> >>> +

> >> >>> +static int mmio_read32(struct udevice *udev, u32 addr, u32

> >*result)

> >> >>> +{

> >> >>> +       struct tpm_tis_chip_data *drv_data = (void

> >*)dev_get_driver_data(udev);

> >> >>> +

> >> >>> +       *result = ioread32(drv_data->iobase + addr);

> >> >>> +       return 0;

> >> >>> +}

> >> >>> +

> >> >>> +static int mmio_write32(struct udevice *udev, u32 addr, u32

> >value)

> >> >>> +{

> >> >>> +       struct tpm_tis_chip_data *drv_data = (void

> >*)dev_get_driver_data(udev);

> >> >>> +

> >> >>> +       iowrite32(value, drv_data->iobase + addr);

> >> >>> +       return 0;

> >> >>> +}

> >> >>> +

> >> >>> +static struct tpm_tis_phy_ops phy_ops = {

> >> >>

> >> >> Should be a uclass interface.

> >> >>

> >> >

> >> > Why? A uclass is supposed to describe and abstract hardware.  This

> >is just

> >> > a specific implementation of a TPM, not all TPMs are TIS compliant.

> >We already

> >> > have a uclass for those.

> >>

> >> The design proposed by Ilias matches what we find in Linux for TPM.

> >> Keeping the same structure should render porting of drivers for

> >> additional devices easier.

> >

> >Can you please be more specific in your comment? What change are you

> >asking for?

> >

> >Regards,

> >Simon

>

> None. I think the design is ok as is.

>

> I was responding to your idea of adding another uclass.


It seems obvious to me that the proposed API is useful outside TPMs.
It provides an MMIO interface, after all.

Regards,
Simon
Ilias Apalodimas July 13, 2021, 8:11 p.m. UTC | #8
[...]
> > > Should be a uclass interface.

> > >

> >

> > Why? A uclass is supposed to describe and abstract hardware.  This is just

> > a specific implementation of a TPM, not all TPMs are TIS compliant. We already

> > have a uclass for those.

> 

> Who told you that a uclass is supposed to describe and abstract hardware? :-)

> 


That's what I've mostly seen it used for, maybe i got the idea wrong.

> The uclass is how driver model does APIs, so normally a uclass would

> be used for any API. There are exceptions, but this one actually looks

> like a useful interface we should have.

> 


the point is we already have a uclass for tpm devices.  So why should the
we add another one that just describes the TIS interface?

> >

> > > > +       .read_bytes = mmio_read_bytes,

> > > > +       .write_bytes = mmio_write_bytes,

> > > > +       .read16 = mmio_read16,

> > > > +       .read32 = mmio_read32,

> > > > +       .write32 = mmio_write32,

> > > > +};

> > > > +

> > > > +static int tpm_tis_probe(struct udevice *udev)

> > > > +{

> > > > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > > > +       struct tpm_chip_priv *priv = dev_get_uclass_priv(udev);

> > > > +       int ret = 0;

> > > > +       fdt_addr_t ioaddr;

> > > > +       u64 sz;

> > > > +

> > > > +       ioaddr = dev_read_addr(udev);

> > > > +       if (ioaddr == FDT_ADDR_T_NONE)

> > > > +               return -EINVAL;

> > >

> > > consider this for easier debugging:

> > >

> > >    return log_msg_ret("ioaddr", -EINVAL);

> > >

> >

> > Sure

> >

> > > > +

> > > > +       ret = dev_read_u64(udev, "reg", &sz);

> > > > +       if (ret)

> > > > +               return -EINVAL;

> > > > +

> > > > +       drv_data->iobase = ioremap(ioaddr, sz);

> > > > +       log_info("Remapped TPM2 base: 0x%llx size: 0x%llx\n", ioaddr, sz);

> > >

> > > log_debug() I think?

> > >

> > Yea, that's a leftover of my initial code, were I needed to make sure the

> > ioremap worked.

> >

> > > > +       tpm_tis_ops_register(udev, &phy_ops);

> > > > +       ret = tpm_tis_init(udev);

> > > > +       if (ret)

> > > > +               goto iounmap;

> > > > +

> > > > +       priv->pcr_count = drv_data->pcr_count;

> > > > +       priv->pcr_select_min = drv_data->pcr_select_min;

> > > > +       /*

> > > > +        * Although the driver probably works with a TPMv1 our Kconfig

> > > > +        * limits the driver to TPMv2 only

> > > > +        */

> > > > +       priv->version = TPM_V2;

> > > > +

> > > > +       return ret;

> > > > +iounmap:

> > > > +       iounmap(drv_data->iobase);

> > > > +       return -EINVAL;

> > > > +}

> > > > +

> > > > +static int tpm_tis_remove(struct udevice *udev)

> > > > +{

> > > > +       struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);

> > > > +

> > > > +       iounmap(drv_data->iobase);

> > > > +       return tpm_tis_cleanup(udev);

> > > > +}

> > > > +

> > > > +static const struct tpm_ops tpm_tis_ops = {

> > > > +       .open           = tpm_tis_open,

> > > > +       .close          = tpm_tis_close,

> > > > +       .get_desc       = tpm_tis_get_desc,

> > > > +       .send           = tpm_tis_send,

> > > > +       .recv           = tpm_tis_recv,

> > > > +       .cleanup        = tpm_tis_cleanup,

> > > > +};

> > > > +

> > > > +static const struct tpm_tis_chip_data tpm_tis_std_chip_data = {

> > > > +       .pcr_count = 24,

> > > > +       .pcr_select_min = 3,

> > > > +};

> > > > +

> > > > +static const struct udevice_id tpm_tis_ids[] = {

> > > > +       {

> > > > +               .compatible = "tcg,tpm-tis-mmio",

> > > > +               .data = (ulong)&tpm_tis_std_chip_data,

> > > > +       },

> > > > +       { }

> > > > +};

> > > > +

> > > > +U_BOOT_DRIVER(tpm_tis_mmio) = {

> > > > +       .name   = "tpm_tis_mmio",

> > > > +       .id     = UCLASS_TPM,

> > > > +       .of_match = tpm_tis_ids,

> > > > +       .ops    = &tpm_tis_ops,

> > > > +       .probe  = tpm_tis_probe,

> > > > +       .remove = tpm_tis_remove,

> > > > +       .priv_auto      = sizeof(struct tpm_chip),

> > > > +};

> > > > --

> > > > 2.32.0.rc0

> > > >

> > >

> > > Regards,

> > > Simon

> >

> > Thanks for looking at this

> > /Ilias

> 

> Likewise.

> 

> Regards,

> Simon
Simon Glass July 14, 2021, 2:49 a.m. UTC | #9
Hi Ilias,

On Tue, 13 Jul 2021 at 14:11, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>

>

> [...]

> > > > Should be a uclass interface.

> > > >

> > >

> > > Why? A uclass is supposed to describe and abstract hardware.  This is just

> > > a specific implementation of a TPM, not all TPMs are TIS compliant. We already

> > > have a uclass for those.

> >

> > Who told you that a uclass is supposed to describe and abstract hardware? :-)

> >

>

> That's what I've mostly seen it used for, maybe i got the idea wrong.


A uclass is basically a software construct. It is an interface between
clients and the driver, typically. Quite often the uclass is an
interface on top of the hardware (actually the driver). But quite
often it is not. For example, we use an GPIO uclass to access a pmic's
GPIOs, we use an I2C uclass to access the cros_ec I2C tunnel. Anywhere
where it makes sense to have an abstraction, we use a uclass.

>

> > The uclass is how driver model does APIs, so normally a uclass would

> > be used for any API. There are exceptions, but this one actually looks

> > like a useful interface we should have.

> >

>

> the point is we already have a uclass for tpm devices.  So why should the

> we add another one that just describes the TIS interface?


You have already added another API, right? All we are discussing is
whether it should be a uclass or not. Unless there is a very good
reason, we should avoid creating custom interfaces that don't use
driver model. I actually think the interface you've created (MMIO)
will be very useful as a uclass.

[..]

Regards,
Simon
Ilias Apalodimas July 14, 2021, 5:23 a.m. UTC | #10
On Tue, Jul 13, 2021 at 08:49:21PM -0600, Simon Glass wrote:
> Hi Ilias,

> 

> On Tue, 13 Jul 2021 at 14:11, Ilias Apalodimas

> <ilias.apalodimas@linaro.org> wrote:

> >

> >

> > [...]

> > > > > Should be a uclass interface.

> > > > >

> > > >

> > > > Why? A uclass is supposed to describe and abstract hardware.  This is just

> > > > a specific implementation of a TPM, not all TPMs are TIS compliant. We already

> > > > have a uclass for those.

> > >

> > > Who told you that a uclass is supposed to describe and abstract hardware? :-)

> > >

> >

> > That's what I've mostly seen it used for, maybe i got the idea wrong.

> 

> A uclass is basically a software construct. It is an interface between

> clients and the driver, typically. Quite often the uclass is an

> interface on top of the hardware (actually the driver). But quite

> often it is not. For example, we use an GPIO uclass to access a pmic's

> GPIOs, we use an I2C uclass to access the cros_ec I2C tunnel. Anywhere

> where it makes sense to have an abstraction, we use a uclass.

> 

> >

> > > The uclass is how driver model does APIs, so normally a uclass would

> > > be used for any API. There are exceptions, but this one actually looks

> > > like a useful interface we should have.

> > >

> >

> > the point is we already have a uclass for tpm devices.  So why should the

> > we add another one that just describes the TIS interface?

> 

> You have already added another API, right? All we are discussing is

> whether it should be a uclass or not. Unless there is a very good

> reason, we should avoid creating custom interfaces that don't use

> driver model. I actually think the interface you've created (MMIO)

> will be very useful as a uclass.

> 


So you are basically looking into adding something similar to
dm_i2c_read/dm_i2c_write etc?
I assume this is gong to be the default read method passed on the TIS API
when we want to support i2c TPMs.
For the MMIO case that would essentially mean, move the functions on a
different file, add them on a header and define a UCLASS_DRIVER with only
the .id and .name defined?

Thanks
/Ilias

> [..]

> 

> Regards,

> Simon
Simon Glass July 14, 2021, 7:50 p.m. UTC | #11
Hi Ilias,

On Tue, 13 Jul 2021 at 23:23, Ilias Apalodimas
<ilias.apalodimas@linaro.org> wrote:
>

> On Tue, Jul 13, 2021 at 08:49:21PM -0600, Simon Glass wrote:

> > Hi Ilias,

> >

> > On Tue, 13 Jul 2021 at 14:11, Ilias Apalodimas

> > <ilias.apalodimas@linaro.org> wrote:

> > >

> > >

> > > [...]

> > > > > > Should be a uclass interface.

> > > > > >

> > > > >

> > > > > Why? A uclass is supposed to describe and abstract hardware.  This is just

> > > > > a specific implementation of a TPM, not all TPMs are TIS compliant. We already

> > > > > have a uclass for those.

> > > >

> > > > Who told you that a uclass is supposed to describe and abstract hardware? :-)

> > > >

> > >

> > > That's what I've mostly seen it used for, maybe i got the idea wrong.

> >

> > A uclass is basically a software construct. It is an interface between

> > clients and the driver, typically. Quite often the uclass is an

> > interface on top of the hardware (actually the driver). But quite

> > often it is not. For example, we use an GPIO uclass to access a pmic's

> > GPIOs, we use an I2C uclass to access the cros_ec I2C tunnel. Anywhere

> > where it makes sense to have an abstraction, we use a uclass.

> >

> > >

> > > > The uclass is how driver model does APIs, so normally a uclass would

> > > > be used for any API. There are exceptions, but this one actually looks

> > > > like a useful interface we should have.

> > > >

> > >

> > > the point is we already have a uclass for tpm devices.  So why should the

> > > we add another one that just describes the TIS interface?

> >

> > You have already added another API, right? All we are discussing is

> > whether it should be a uclass or not. Unless there is a very good

> > reason, we should avoid creating custom interfaces that don't use

> > driver model. I actually think the interface you've created (MMIO)

> > will be very useful as a uclass.

> >

>

> So you are basically looking into adding something similar to

> dm_i2c_read/dm_i2c_write etc?

> I assume this is gong to be the default read method passed on the TIS API

> when we want to support i2c TPMs.


Well I'm not quite sure, but if MMIO can sit on top of I2C, then yes.

> For the MMIO case that would essentially mean, move the functions on a

> different file, add them on a header and define a UCLASS_DRIVER with only

> the .id and .name defined?


Yes. Something like:

- add a new uclass ID to dm/uciass-id.h
- add include/mmio.h (or whatever it is called) with operations (see
pch.h as an example)
- add drivers/misc/mmio-uclass.c with the UCLASS_DRIVER and stubs for the API
- add drivers/misc/sandbox_mmio.c with a sandbox driver that does very
little (perhaps just read canned data?)
- add test/dm/mmio.c sandbox test that calls each API function and
checks the result

As I said pch is a good example since it is fairly simple.

Regards,
Simon
diff mbox series

Patch

diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig
index 9eebab5cfd90..406ee8716e1e 100644
--- a/drivers/tpm/Kconfig
+++ b/drivers/tpm/Kconfig
@@ -161,6 +161,15 @@  config TPM2_FTPM_TEE
 	help
 	  This driver supports firmware TPM running in TEE.
 
+config TPM2_MMIO
+	bool "MMIO based TPM2 Interface"
+	depends on TPM_V2
+	help
+	  This driver supports firmware TPM2.0 MMIO interface.
+	  The usual TPM operations and the 'tpm' command can be used to talk
+	  to the device using the standard TPM Interface Specification (TIS)
+	  protocol.
+
 endif # TPM_V2
 
 endmenu
diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile
index f64d20067f88..1065c1874f58 100644
--- a/drivers/tpm/Makefile
+++ b/drivers/tpm/Makefile
@@ -14,3 +14,4 @@  obj-$(CONFIG_$(SPL_TPL_)TPM2_CR50_I2C) += cr50_i2c.o
 obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o
 obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_spi.o
 obj-$(CONFIG_TPM2_FTPM_TEE) += tpm2_ftpm_tee.o
+obj-$(CONFIG_TPM2_MMIO) += tpm2_tis_core.o tpm2_tis_mmio.o
diff --git a/drivers/tpm/tpm2_tis_mmio.c b/drivers/tpm/tpm2_tis_mmio.c
new file mode 100644
index 000000000000..2183a2807162
--- /dev/null
+++ b/drivers/tpm/tpm2_tis_mmio.c
@@ -0,0 +1,156 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * driver for mmio TCG/TIS TPM (trusted platform module).
+ *
+ * Specifications at www.trustedcomputinggroup.org
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <tpm-v2.h>
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/unaligned/be_byteshift.h>
+#include "tpm_tis.h"
+#include "tpm_internal.h"
+
+struct tpm_tis_chip_data {
+	unsigned int pcr_count;
+	unsigned int pcr_select_min;
+	unsigned int time_before_first_cmd_ms;
+	void __iomem *iobase;
+};
+
+static int mmio_read_bytes(struct udevice *udev, u32 addr, u16 len,
+			   u8 *result)
+{
+	struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);
+
+	while (len--)
+		*result++ = ioread8(drv_data->iobase + addr);
+	return 0;
+}
+
+static int mmio_write_bytes(struct udevice *udev, u32 addr, u16 len,
+			    const u8 *value)
+{
+	struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);
+
+	while (len--)
+		iowrite8(*value++, drv_data->iobase + addr);
+	return 0;
+}
+
+static int mmio_read16(struct udevice *udev, u32 addr, u16 *result)
+{
+	struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);
+
+	*result = ioread16(drv_data->iobase + addr);
+	return 0;
+}
+
+static int mmio_read32(struct udevice *udev, u32 addr, u32 *result)
+{
+	struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);
+
+	*result = ioread32(drv_data->iobase + addr);
+	return 0;
+}
+
+static int mmio_write32(struct udevice *udev, u32 addr, u32 value)
+{
+	struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);
+
+	iowrite32(value, drv_data->iobase + addr);
+	return 0;
+}
+
+static struct tpm_tis_phy_ops phy_ops = {
+	.read_bytes = mmio_read_bytes,
+	.write_bytes = mmio_write_bytes,
+	.read16 = mmio_read16,
+	.read32 = mmio_read32,
+	.write32 = mmio_write32,
+};
+
+static int tpm_tis_probe(struct udevice *udev)
+{
+	struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);
+	struct tpm_chip_priv *priv = dev_get_uclass_priv(udev);
+	int ret = 0;
+	fdt_addr_t ioaddr;
+	u64 sz;
+
+	ioaddr = dev_read_addr(udev);
+	if (ioaddr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	ret = dev_read_u64(udev, "reg", &sz);
+	if (ret)
+		return -EINVAL;
+
+	drv_data->iobase = ioremap(ioaddr, sz);
+	log_info("Remapped TPM2 base: 0x%llx size: 0x%llx\n", ioaddr, sz);
+	tpm_tis_ops_register(udev, &phy_ops);
+	ret = tpm_tis_init(udev);
+	if (ret)
+		goto iounmap;
+
+	priv->pcr_count = drv_data->pcr_count;
+	priv->pcr_select_min = drv_data->pcr_select_min;
+	/*
+	 * Although the driver probably works with a TPMv1 our Kconfig
+	 * limits the driver to TPMv2 only
+	 */
+	priv->version = TPM_V2;
+
+	return ret;
+iounmap:
+	iounmap(drv_data->iobase);
+	return -EINVAL;
+}
+
+static int tpm_tis_remove(struct udevice *udev)
+{
+	struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev);
+
+	iounmap(drv_data->iobase);
+	return tpm_tis_cleanup(udev);
+}
+
+static const struct tpm_ops tpm_tis_ops = {
+	.open		= tpm_tis_open,
+	.close		= tpm_tis_close,
+	.get_desc	= tpm_tis_get_desc,
+	.send		= tpm_tis_send,
+	.recv		= tpm_tis_recv,
+	.cleanup	= tpm_tis_cleanup,
+};
+
+static const struct tpm_tis_chip_data tpm_tis_std_chip_data = {
+	.pcr_count = 24,
+	.pcr_select_min = 3,
+};
+
+static const struct udevice_id tpm_tis_ids[] = {
+	{
+		.compatible = "tcg,tpm-tis-mmio",
+		.data = (ulong)&tpm_tis_std_chip_data,
+	},
+	{ }
+};
+
+U_BOOT_DRIVER(tpm_tis_mmio) = {
+	.name   = "tpm_tis_mmio",
+	.id     = UCLASS_TPM,
+	.of_match = tpm_tis_ids,
+	.ops    = &tpm_tis_ops,
+	.probe	= tpm_tis_probe,
+	.remove	= tpm_tis_remove,
+	.priv_auto	= sizeof(struct tpm_chip),
+};