diff options
-rw-r--r-- | arch/arm/include/asm/arch-meson/gxbb.h | 1 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-meson/i2c.h | 11 | ||||
-rw-r--r-- | arch/sandbox/include/asm/io.h | 57 | ||||
-rw-r--r-- | board/amlogic/odroid-c2/odroid-c2.c | 1 | ||||
-rw-r--r-- | cmd/fdt.c | 2 | ||||
-rw-r--r-- | common/spl/spl.c | 2 | ||||
-rw-r--r-- | configs/odroid-c2_defconfig | 3 | ||||
-rw-r--r-- | drivers/core/read.c | 3 | ||||
-rw-r--r-- | drivers/i2c/Kconfig | 6 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/meson_i2c.c | 263 | ||||
-rw-r--r-- | include/dm/ofnode.h | 2 | ||||
-rw-r--r-- | include/tpm.h | 15 | ||||
-rw-r--r-- | lib/tpm.c | 60 |
14 files changed, 421 insertions, 6 deletions
diff --git a/arch/arm/include/asm/arch-meson/gxbb.h b/arch/arm/include/asm/arch-meson/gxbb.h index 74d52903406..95a6fe6998e 100644 --- a/arch/arm/include/asm/arch-meson/gxbb.h +++ b/arch/arm/include/asm/arch-meson/gxbb.h @@ -47,6 +47,7 @@ #define GXBB_GCLK_MPEG_OTHER GXBB_HIU_ADDR(0x53) #define GXBB_GCLK_MPEG_AO GXBB_HIU_ADDR(0x54) +#define GXBB_GCLK_MPEG_0_I2C BIT(9) #define GXBB_GCLK_MPEG_1_ETH BIT(3) #endif /* __GXBB_H__ */ diff --git a/arch/arm/include/asm/arch-meson/i2c.h b/arch/arm/include/asm/arch-meson/i2c.h new file mode 100644 index 00000000000..783bc3786f8 --- /dev/null +++ b/arch/arm/include/asm/arch-meson/i2c.h @@ -0,0 +1,11 @@ +/* + * Copyright 2017 - Beniamino Galvani <b.galvani@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _MESON_I2C_H_ +#define _MESON_I2C_H_ + +#define MESON_I2C_CLK_RATE 167000000 + +#endif diff --git a/arch/sandbox/include/asm/io.h b/arch/sandbox/include/asm/io.h index ae6883f33df..59729f5635e 100644 --- a/arch/sandbox/include/asm/io.h +++ b/arch/sandbox/include/asm/io.h @@ -47,6 +47,63 @@ phys_addr_t map_to_sysmem(const void *ptr); #define writew(v, addr) ((void)addr) #define writel(v, addr) ((void)addr) +/* + * Clear and set bits in one shot. These macros can be used to clear and + * set multiple bits in a register using a single call. These macros can + * also be used to set a multiple-bit bit pattern using a mask, by + * specifying the mask in the 'clear' parameter and the new bit pattern + * in the 'set' parameter. + */ + +#define out_arch(type,endian,a,v) write##type(cpu_to_##endian(v),a) +#define in_arch(type,endian,a) endian##_to_cpu(read##type(a)) + +#define out_le64(a,v) out_arch(q,le64,a,v) +#define out_le32(a,v) out_arch(l,le32,a,v) +#define out_le16(a,v) out_arch(w,le16,a,v) + +#define in_le64(a) in_arch(q,le64,a) +#define in_le32(a) in_arch(l,le32,a) +#define in_le16(a) in_arch(w,le16,a) + +#define out_be32(a,v) out_arch(l,be32,a,v) +#define out_be16(a,v) out_arch(w,be16,a,v) + +#define in_be32(a) in_arch(l,be32,a) +#define in_be16(a) in_arch(w,be16,a) + +#define out_8(a,v) writeb(v,a) +#define in_8(a) readb(a) + +#define clrbits(type, addr, clear) \ + out_##type((addr), in_##type(addr) & ~(clear)) + +#define setbits(type, addr, set) \ + out_##type((addr), in_##type(addr) | (set)) + +#define clrsetbits(type, addr, clear, set) \ + out_##type((addr), (in_##type(addr) & ~(clear)) | (set)) + +#define clrbits_be32(addr, clear) clrbits(be32, addr, clear) +#define setbits_be32(addr, set) setbits(be32, addr, set) +#define clrsetbits_be32(addr, clear, set) clrsetbits(be32, addr, clear, set) + +#define clrbits_le32(addr, clear) clrbits(le32, addr, clear) +#define setbits_le32(addr, set) setbits(le32, addr, set) +#define clrsetbits_le32(addr, clear, set) clrsetbits(le32, addr, clear, set) + +#define clrbits_be16(addr, clear) clrbits(be16, addr, clear) +#define setbits_be16(addr, set) setbits(be16, addr, set) +#define clrsetbits_be16(addr, clear, set) clrsetbits(be16, addr, clear, set) + +#define clrbits_le16(addr, clear) clrbits(le16, addr, clear) +#define setbits_le16(addr, set) setbits(le16, addr, set) +#define clrsetbits_le16(addr, clear, set) clrsetbits(le16, addr, clear, set) + +#define clrbits_8(addr, clear) clrbits(8, addr, clear) +#define setbits_8(addr, set) setbits(8, addr, set) +#define clrsetbits_8(addr, clear, set) clrsetbits(8, addr, clear, set) + /* I/O access functions */ int inl(unsigned int addr); int inw(unsigned int addr); diff --git a/board/amlogic/odroid-c2/odroid-c2.c b/board/amlogic/odroid-c2/odroid-c2.c index eac04d8178d..a5ea8dc5af2 100644 --- a/board/amlogic/odroid-c2/odroid-c2.c +++ b/board/amlogic/odroid-c2/odroid-c2.c @@ -35,6 +35,7 @@ int misc_init_r(void) GXBB_ETH_REG_0_CLK_EN); /* Enable power and clock gate */ + setbits_le32(GXBB_GCLK_MPEG_0, GXBB_GCLK_MPEG_0_I2C); setbits_le32(GXBB_GCLK_MPEG_1, GXBB_GCLK_MPEG_1_ETH); clrbits_le32(GXBB_MEM_PD_REG_0, GXBB_MEM_PD_REG_0_ETH_MASK); diff --git a/cmd/fdt.c b/cmd/fdt.c index 955a0088c6e..b783b0df427 100644 --- a/cmd/fdt.c +++ b/cmd/fdt.c @@ -256,7 +256,7 @@ static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) char *pathp; /* path */ char *prop; /* property */ int nodeoffset; /* node offset from libfdt */ - static char data[SCRATCHPAD]; /* storage for the property */ + static char data[SCRATCHPAD] __aligned(4);/* property storage */ const void *ptmp; int len; /* new length of the property */ int ret; /* return value */ diff --git a/common/spl/spl.c b/common/spl/spl.c index aaddddd9958..d232f67ba95 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -154,7 +154,7 @@ int spl_parse_image_header(struct spl_image_info *spl_image, spl_image->os = image_get_os(header); spl_image->name = image_get_name(header); debug("spl: payload image: %.*s load addr: 0x%lx size: %d\n", - (int)sizeof(spl_image->name), spl_image->name, + IH_NMLEN, spl_image->name, spl_image->load_addr, spl_image->size); #else /* LEGACY image not supported */ diff --git a/configs/odroid-c2_defconfig b/configs/odroid-c2_defconfig index f7f80166440..1afd2fc1113 100644 --- a/configs/odroid-c2_defconfig +++ b/configs/odroid-c2_defconfig @@ -11,12 +11,15 @@ CONFIG_DEBUG_UART=y # CONFIG_CMD_IMI is not set # CONFIG_CMD_FPGA is not set CONFIG_CMD_GPIO=y +CONFIG_CMD_I2C=y # CONFIG_CMD_LOADS is not set CONFIG_CMD_MMC=y # CONFIG_CMD_SETEXPR is not set CONFIG_OF_CONTROL=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_DM_GPIO=y +CONFIG_DM_I2C=y +CONFIG_SYS_I2C_MESON=y CONFIG_DM_MMC=y CONFIG_MMC_MESON_GX=y CONFIG_DM_ETH=y diff --git a/drivers/core/read.c b/drivers/core/read.c index eacf1716fd7..5d440cee721 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -7,6 +7,7 @@ #include <common.h> #include <dm.h> +#include <mapmem.h> #include <dm/of_access.h> int dev_read_u32_default(struct udevice *dev, const char *propname, int def) @@ -61,7 +62,7 @@ void *dev_read_addr_ptr(struct udevice *dev) { fdt_addr_t addr = dev_read_addr(dev); - return (addr == FDT_ADDR_T_NONE) ? NULL : (void *)addr; + return (addr == FDT_ADDR_T_NONE) ? NULL : map_sysmem(addr, 0); } fdt_addr_t dev_read_addr_size(struct udevice *dev, const char *property, diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index c296985d9ba..1989f8eb572 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -137,6 +137,12 @@ config SYS_I2C_IMX_LPI2C help Add support for the NXP i.MX LPI2C driver. +config SYS_I2C_MESON + bool "Amlogic Meson I2C driver" + depends on DM_I2C && ARCH_MESON + help + Add support for the Amlogic Meson I2C driver. + config SYS_I2C_MXC bool "NXP i.MX I2C driver" depends on MX6 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 3a8c61b4850..733cd3e92fa 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SYS_I2C_INTEL) += intel_i2c.o obj-$(CONFIG_SYS_I2C_IMX_LPI2C) += imx_lpi2c.o obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o +obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o obj-$(CONFIG_SYS_I2C_MXS) += mxs_i2c.o diff --git a/drivers/i2c/meson_i2c.c b/drivers/i2c/meson_i2c.c new file mode 100644 index 00000000000..2434d9ed538 --- /dev/null +++ b/drivers/i2c/meson_i2c.c @@ -0,0 +1,263 @@ +/* + * (C) Copyright 2017 - Beniamino Galvani <b.galvani@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <asm/arch/i2c.h> +#include <asm/io.h> +#include <dm.h> +#include <i2c.h> + +#define I2C_TIMEOUT_MS 500 + +/* Control register fields */ +#define REG_CTRL_START BIT(0) +#define REG_CTRL_ACK_IGNORE BIT(1) +#define REG_CTRL_STATUS BIT(2) +#define REG_CTRL_ERROR BIT(3) +#define REG_CTRL_CLKDIV_SHIFT 12 +#define REG_CTRL_CLKDIV_MASK GENMASK(21, 12) +#define REG_CTRL_CLKDIVEXT_SHIFT 28 +#define REG_CTRL_CLKDIVEXT_MASK GENMASK(29, 28) + +enum { + TOKEN_END = 0, + TOKEN_START, + TOKEN_SLAVE_ADDR_WRITE, + TOKEN_SLAVE_ADDR_READ, + TOKEN_DATA, + TOKEN_DATA_LAST, + TOKEN_STOP, +}; + +struct i2c_regs { + u32 ctrl; + u32 slave_addr; + u32 tok_list0; + u32 tok_list1; + u32 tok_wdata0; + u32 tok_wdata1; + u32 tok_rdata0; + u32 tok_rdata1; +}; + +struct meson_i2c { + struct i2c_regs *regs; + struct i2c_msg *msg; + bool last; + uint count; + uint pos; + u32 tokens[2]; + uint num_tokens; +}; + +static void meson_i2c_reset_tokens(struct meson_i2c *i2c) +{ + i2c->tokens[0] = 0; + i2c->tokens[1] = 0; + i2c->num_tokens = 0; +} + +static void meson_i2c_add_token(struct meson_i2c *i2c, int token) +{ + if (i2c->num_tokens < 8) + i2c->tokens[0] |= (token & 0xf) << (i2c->num_tokens * 4); + else + i2c->tokens[1] |= (token & 0xf) << ((i2c->num_tokens % 8) * 4); + + i2c->num_tokens++; +} + +static void meson_i2c_get_data(struct meson_i2c *i2c, u8 *buf, int len) +{ + u32 rdata0, rdata1; + int i; + + rdata0 = readl(&i2c->regs->tok_rdata0); + rdata1 = readl(&i2c->regs->tok_rdata1); + + debug("meson i2c: read data %08x %08x len %d\n", rdata0, rdata1, len); + + for (i = 0; i < min(4, len); i++) + *buf++ = (rdata0 >> i * 8) & 0xff; + + for (i = 4; i < min(8, len); i++) + *buf++ = (rdata1 >> (i - 4) * 8) & 0xff; +} + +static void meson_i2c_put_data(struct meson_i2c *i2c, u8 *buf, int len) +{ + u32 wdata0 = 0, wdata1 = 0; + int i; + + for (i = 0; i < min(4, len); i++) + wdata0 |= *buf++ << (i * 8); + + for (i = 4; i < min(8, len); i++) + wdata1 |= *buf++ << ((i - 4) * 8); + + writel(wdata0, &i2c->regs->tok_wdata0); + writel(wdata1, &i2c->regs->tok_wdata1); + + debug("meson i2c: write data %08x %08x len %d\n", wdata0, wdata1, len); +} + +static void meson_i2c_prepare_xfer(struct meson_i2c *i2c) +{ + bool write = !(i2c->msg->flags & I2C_M_RD); + int i; + + i2c->count = min(i2c->msg->len - i2c->pos, 8u); + + for (i = 0; i + 1 < i2c->count; i++) + meson_i2c_add_token(i2c, TOKEN_DATA); + + if (i2c->count) { + if (write || i2c->pos + i2c->count < i2c->msg->len) + meson_i2c_add_token(i2c, TOKEN_DATA); + else + meson_i2c_add_token(i2c, TOKEN_DATA_LAST); + } + + if (write) + meson_i2c_put_data(i2c, i2c->msg->buf + i2c->pos, i2c->count); + + if (i2c->last && i2c->pos + i2c->count >= i2c->msg->len) + meson_i2c_add_token(i2c, TOKEN_STOP); + + writel(i2c->tokens[0], &i2c->regs->tok_list0); + writel(i2c->tokens[1], &i2c->regs->tok_list1); +} + +static void meson_i2c_do_start(struct meson_i2c *i2c, struct i2c_msg *msg) +{ + int token; + + token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ : + TOKEN_SLAVE_ADDR_WRITE; + + writel(msg->addr << 1, &i2c->regs->slave_addr); + meson_i2c_add_token(i2c, TOKEN_START); + meson_i2c_add_token(i2c, token); +} + +static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg, + int last) +{ + ulong start; + + debug("meson i2c: %s addr %u len %u\n", + (msg->flags & I2C_M_RD) ? "read" : "write", + msg->addr, msg->len); + + i2c->msg = msg; + i2c->last = last; + i2c->pos = 0; + i2c->count = 0; + + meson_i2c_reset_tokens(i2c); + meson_i2c_do_start(i2c, msg); + + do { + meson_i2c_prepare_xfer(i2c); + + /* start the transfer */ + setbits_le32(&i2c->regs->ctrl, REG_CTRL_START); + start = get_timer(0); + while (readl(&i2c->regs->ctrl) & REG_CTRL_STATUS) { + if (get_timer(start) > I2C_TIMEOUT_MS) { + clrbits_le32(&i2c->regs->ctrl, REG_CTRL_START); + debug("meson i2c: timeout\n"); + return -ETIMEDOUT; + } + udelay(1); + } + meson_i2c_reset_tokens(i2c); + clrbits_le32(&i2c->regs->ctrl, REG_CTRL_START); + + if (readl(&i2c->regs->ctrl) & REG_CTRL_ERROR) { + debug("meson i2c: error\n"); + return -ENXIO; + } + + if ((msg->flags & I2C_M_RD) && i2c->count) { + meson_i2c_get_data(i2c, i2c->msg->buf + i2c->pos, + i2c->count); + } + i2c->pos += i2c->count; + } while (i2c->pos < msg->len); + + return 0; +} + +static int meson_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, + int nmsgs) +{ + struct meson_i2c *i2c = dev_get_priv(bus); + int i, ret = 0; + + for (i = 0; i < nmsgs; i++) { + ret = meson_i2c_xfer_msg(i2c, msg + i, i == nmsgs - 1); + if (ret) + return -EREMOTEIO; + } + + return 0; +} + +static int meson_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + struct meson_i2c *i2c = dev_get_priv(bus); + unsigned int clk_rate = MESON_I2C_CLK_RATE; + unsigned int div; + + div = DIV_ROUND_UP(clk_rate, speed * 4); + + /* clock divider has 12 bits */ + if (div >= (1 << 12)) { + debug("meson i2c: requested bus frequency too low\n"); + div = (1 << 12) - 1; + } + + clrsetbits_le32(&i2c->regs->ctrl, REG_CTRL_CLKDIV_MASK, + (div & GENMASK(9, 0)) << REG_CTRL_CLKDIV_SHIFT); + + clrsetbits_le32(&i2c->regs->ctrl, REG_CTRL_CLKDIVEXT_MASK, + (div >> 10) << REG_CTRL_CLKDIVEXT_SHIFT); + + debug("meson i2c: set clk %u, src %u, div %u\n", speed, clk_rate, div); + + return 0; +} + +static int meson_i2c_probe(struct udevice *bus) +{ + struct meson_i2c *i2c = dev_get_priv(bus); + + i2c->regs = dev_read_addr_ptr(bus); + clrbits_le32(&i2c->regs->ctrl, REG_CTRL_START); + + return 0; +} + +static const struct dm_i2c_ops meson_i2c_ops = { + .xfer = meson_i2c_xfer, + .set_bus_speed = meson_i2c_set_bus_speed, +}; + +static const struct udevice_id meson_i2c_ids[] = { + { .compatible = "amlogic,meson6-i2c" }, + { .compatible = "amlogic,meson-gx-i2c" }, + { .compatible = "amlogic,meson-gxbb-i2c" }, + { } +}; + +U_BOOT_DRIVER(i2c_meson) = { + .name = "i2c_meson", + .id = UCLASS_I2C, + .of_match = meson_i2c_ids, + .probe = meson_i2c_probe, + .priv_auto_alloc_size = sizeof(struct meson_i2c), + .ops = &meson_i2c_ops, +}; diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index 79374b8f91a..8b9932a569c 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -45,7 +45,7 @@ struct resource; * the DT. * * @np: Pointer to device node, used for live tree - * @flat_ptr: Pointer into flat device tree, used for flat tree. Note that this + * @of_offset: Pointer into flat device tree, used for flat tree. Note that this * is not a really a pointer to a node: it is an offset value. See above. */ typedef union ofnode_union { diff --git a/include/tpm.h b/include/tpm.h index f88388f3530..760d94865c3 100644 --- a/include/tpm.h +++ b/include/tpm.h @@ -84,9 +84,12 @@ enum tpm_capability_areas { }; #define TPM_NV_PER_GLOBALLOCK (1U << 15) +#define TPM_NV_PER_PPREAD (1U << 16) #define TPM_NV_PER_PPWRITE (1U << 0) #define TPM_NV_PER_READ_STCLEAR (1U << 31) #define TPM_NV_PER_WRITE_STCLEAR (1U << 14) +#define TPM_NV_PER_WRITEDEFINE (1U << 13) +#define TPM_NV_PER_WRITEALL (1U << 12) enum { TPM_PUBEK_SIZE = 256, @@ -651,4 +654,16 @@ uint32_t tpm_flush_specific(uint32_t key_handle, uint32_t resource_type); uint32_t tpm_find_key_sha1(const uint8_t auth[20], const uint8_t pubkey_digest[20], uint32_t *handle); #endif /* CONFIG_TPM_LOAD_KEY_BY_SHA1 */ + +/** + * Read random bytes from the TPM RNG. The implementation deals with the fact + * that the TPM may legally return fewer bytes than requested by retrying + * until @p count bytes have been received. + * + * @param data output buffer for the random bytes + * @param count size of output buffer + * @return return code of the operation + */ +uint32_t tpm_get_random(void *data, uint32_t count); + #endif /* __TPM_H */ diff --git a/lib/tpm.c b/lib/tpm.c index d1cf5a8a167..c8bf06178f1 100644 --- a/lib/tpm.c +++ b/lib/tpm.c @@ -92,6 +92,7 @@ int pack_byte_string(uint8_t *str, size_t size, const char *format, ...) break; default: debug("Couldn't recognize format string\n"); + va_end(args); return -1; } @@ -170,8 +171,10 @@ int unpack_byte_string(const uint8_t *str, size_t size, const char *format, ...) return -1; } - if (offset + length > size) + if (offset + length > size) { + va_end(args); return -1; + } switch (*format) { case 'b': @@ -607,14 +610,24 @@ uint32_t tpm_get_permanent_flags(struct tpm_permanent_flags *pflags) 0x0, 0x0, 0x0, 0x4, /* subcap size */ 0x0, 0x0, 0x1, 0x8, /* subcap value */ }; + const size_t data_size_offset = TPM_HEADER_SIZE; + const size_t data_offset = TPM_HEADER_SIZE + sizeof (uint32_t); uint8_t response[COMMAND_BUFFER_SIZE]; size_t response_length = sizeof(response); uint32_t err; + uint32_t data_size; err = tpm_sendrecv_command(command, response, &response_length); if (err) return err; - memcpy(pflags, response + TPM_HEADER_SIZE, sizeof(*pflags)); + if (unpack_byte_string(response, response_length, "d", + data_size_offset, &data_size)) + return TPM_LIB_ERROR; + if (data_size < sizeof(*pflags)) + return TPM_LIB_ERROR; + if (unpack_byte_string(response, response_length, "s", + data_offset, pflags, sizeof(*pflags))) + return TPM_LIB_ERROR; return 0; } @@ -1039,3 +1052,46 @@ uint32_t tpm_find_key_sha1(const uint8_t auth[20], const uint8_t #endif /* CONFIG_TPM_LOAD_KEY_BY_SHA1 */ #endif /* CONFIG_TPM_AUTH_SESSIONS */ + +uint32_t tpm_get_random(void *data, uint32_t count) +{ + const uint8_t command[14] = { + 0x0, 0xc1, /* TPM_TAG */ + 0x0, 0x0, 0x0, 0xe, /* parameter size */ + 0x0, 0x0, 0x0, 0x46, /* TPM_COMMAND_CODE */ + }; + const size_t length_offset = 10; + const size_t data_size_offset = 10; + const size_t data_offset = 14; + uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE]; + size_t response_length = sizeof(response); + uint32_t data_size; + uint8_t *out = data; + + while (count > 0) { + uint32_t this_bytes = min((size_t)count, + sizeof (response) - data_offset); + uint32_t err; + + if (pack_byte_string(buf, sizeof(buf), "sd", + 0, command, sizeof(command), + length_offset, this_bytes)) + return TPM_LIB_ERROR; + err = tpm_sendrecv_command(buf, response, &response_length); + if (err) + return err; + if (unpack_byte_string(response, response_length, "d", + data_size_offset, &data_size)) + return TPM_LIB_ERROR; + if (data_size > count) + return TPM_LIB_ERROR; + if (unpack_byte_string(response, response_length, "s", + data_offset, out, data_size)) + return TPM_LIB_ERROR; + + count -= data_size; + out += data_size; + } + + return 0; +} |