commit
8a36287a01
@ -0,0 +1,543 @@ |
||||
/*
|
||||
* STMicroelectronics TPM ST33ZP24 I2C UBOOT driver |
||||
* |
||||
* Copyright (C) 2016 STMicroelectronics |
||||
* |
||||
* Description: Device driver for ST33ZP24 I2C TPM TCG. |
||||
* |
||||
* This device driver implements the TPM interface as defined in |
||||
* the TCG TPM Interface Spec version 1.21, revision 1.0 and the |
||||
* STMicroelectronics Protocol Stack Specification version 1.2.0. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <fdtdec.h> |
||||
#include <i2c.h> |
||||
#include <tpm.h> |
||||
#include <errno.h> |
||||
#include <linux/types.h> |
||||
#include <asm/unaligned.h> |
||||
|
||||
#include "tpm_tis.h" |
||||
#include "tpm_internal.h" |
||||
|
||||
#define TPM_ACCESS 0x0 |
||||
#define TPM_STS 0x18 |
||||
#define TPM_DATA_FIFO 0x24 |
||||
|
||||
#define LOCALITY0 0 |
||||
|
||||
#define TPM_DUMMY_BYTE 0xAA |
||||
#define TPM_ST33ZP24_I2C_SLAVE_ADDR 0x13 |
||||
|
||||
#define TPM_WRITE_DIRECTION 0x80 |
||||
|
||||
/*
|
||||
* st33zp24_i2c_write8_reg |
||||
* Send byte to the TIS register according to the ST33ZP24 I2C protocol. |
||||
* @param: tpm_register, the tpm tis register where the data should be written |
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register |
||||
* @param: tpm_size, The length of the data |
||||
* @return: Number of byte written successfully else an error code. |
||||
*/ |
||||
static int st33zp24_i2c_write8_reg(struct udevice *dev, u8 tpm_register, |
||||
const u8 *tpm_data, size_t tpm_size) |
||||
{ |
||||
struct tpm_chip_priv *chip_priv = dev_get_uclass_priv(dev); |
||||
|
||||
chip_priv->buf[0] = tpm_register; |
||||
memcpy(chip_priv->buf + 1, tpm_data, tpm_size); |
||||
|
||||
return dm_i2c_write(dev, 0, chip_priv->buf, tpm_size + 1); |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_read8_reg |
||||
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol. |
||||
* @param: tpm_register, the tpm tis register where the data should be read |
||||
* @param: tpm_data, the TPM response |
||||
* @param: tpm_size, tpm TPM response size to read. |
||||
* @return: Number of byte read successfully else an error code. |
||||
*/ |
||||
static int st33zp24_i2c_read8_reg(struct udevice *dev, u8 tpm_register, |
||||
u8 *tpm_data, size_t tpm_size) |
||||
{ |
||||
int status; |
||||
u8 data; |
||||
|
||||
data = TPM_DUMMY_BYTE; |
||||
status = st33zp24_i2c_write8_reg(dev, tpm_register, &data, 1); |
||||
if (status < 0) |
||||
return status; |
||||
|
||||
return dm_i2c_read(dev, 0, tpm_data, tpm_size); |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_write |
||||
* Send byte to the TIS register according to the ST33ZP24 I2C protocol. |
||||
* @param: phy_id, the phy description |
||||
* @param: tpm_register, the tpm tis register where the data should be written |
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register |
||||
* @param: tpm_size, the length of the data |
||||
* @return: number of byte written successfully: should be one if success. |
||||
*/ |
||||
static int st33zp24_i2c_write(struct udevice *dev, u8 tpm_register, |
||||
const u8 *tpm_data, size_t tpm_size) |
||||
{ |
||||
return st33zp24_i2c_write8_reg(dev, tpm_register | TPM_WRITE_DIRECTION, |
||||
tpm_data, tpm_size); |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_read |
||||
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol. |
||||
* @param: phy_id, the phy description |
||||
* @param: tpm_register, the tpm tis register where the data should be read |
||||
* @param: tpm_data, the TPM response |
||||
* @param: tpm_size, tpm TPM response size to read. |
||||
* @return: number of byte read successfully: should be one if success. |
||||
*/ |
||||
static int st33zp24_i2c_read(struct udevice *dev, u8 tpm_register, |
||||
u8 *tpm_data, size_t tpm_size) |
||||
{ |
||||
return st33zp24_i2c_read8_reg(dev, tpm_register, tpm_data, tpm_size); |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_release_locality release the active locality |
||||
* @param: chip, the tpm chip description. |
||||
*/ |
||||
static void st33zp24_i2c_release_locality(struct udevice *dev) |
||||
{ |
||||
u8 data = TPM_ACCESS_ACTIVE_LOCALITY; |
||||
|
||||
st33zp24_i2c_write(dev, TPM_ACCESS, &data, 1); |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_check_locality if the locality is active |
||||
* @param: chip, the tpm chip description |
||||
* @return: the active locality or -EACCES. |
||||
*/ |
||||
static int st33zp24_i2c_check_locality(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
u8 data; |
||||
u8 status; |
||||
|
||||
status = st33zp24_i2c_read(dev, TPM_ACCESS, &data, 1); |
||||
if (!status && (data & |
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == |
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) |
||||
return chip->locality; |
||||
|
||||
return -EACCES; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_request_locality request the TPM locality |
||||
* @param: chip, the chip description |
||||
* @return: the active locality or negative value. |
||||
*/ |
||||
static int st33zp24_i2c_request_locality(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
unsigned long start, stop; |
||||
long ret; |
||||
u8 data; |
||||
|
||||
if (st33zp24_i2c_check_locality(dev) == chip->locality) |
||||
return chip->locality; |
||||
|
||||
data = TPM_ACCESS_REQUEST_USE; |
||||
ret = st33zp24_i2c_write(dev, TPM_ACCESS, &data, 1); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
/* wait for locality activated */ |
||||
start = get_timer(0); |
||||
stop = chip->timeout_a; |
||||
do { |
||||
if (st33zp24_i2c_check_locality(dev) >= 0) |
||||
return chip->locality; |
||||
udelay(TPM_TIMEOUT_MS * 1000); |
||||
} while (get_timer(start) < stop); |
||||
|
||||
return -EACCES; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_status return the TPM_STS register |
||||
* @param: chip, the tpm chip description |
||||
* @return: the TPM_STS register value. |
||||
*/ |
||||
static u8 st33zp24_i2c_status(struct udevice *dev) |
||||
{ |
||||
u8 data; |
||||
|
||||
st33zp24_i2c_read(dev, TPM_STS, &data, 1); |
||||
|
||||
return data; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_get_burstcount return the burstcount address 0x19 0x1A |
||||
* @param: chip, the chip description |
||||
* return: the burstcount or -TPM_DRIVER_ERR in case of error. |
||||
*/ |
||||
static int st33zp24_i2c_get_burstcount(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
unsigned long start, stop; |
||||
int burstcnt, status; |
||||
u8 tpm_reg, temp; |
||||
|
||||
/* wait for burstcount */ |
||||
start = get_timer(0); |
||||
stop = chip->timeout_d; |
||||
do { |
||||
tpm_reg = TPM_STS + 1; |
||||
status = st33zp24_i2c_read(dev, tpm_reg, &temp, 1); |
||||
if (status < 0) |
||||
return -EBUSY; |
||||
|
||||
tpm_reg = TPM_STS + 2; |
||||
burstcnt = temp; |
||||
status = st33zp24_i2c_read(dev, tpm_reg, &temp, 1); |
||||
if (status < 0) |
||||
return -EBUSY; |
||||
|
||||
burstcnt |= temp << 8; |
||||
if (burstcnt) |
||||
return burstcnt; |
||||
udelay(TIS_SHORT_TIMEOUT_MS * 1000); |
||||
} while (get_timer(start) < stop); |
||||
|
||||
return -EBUSY; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_cancel, cancel the current command execution or |
||||
* set STS to COMMAND READY. |
||||
* @param: chip, tpm_chip description. |
||||
*/ |
||||
static void st33zp24_i2c_cancel(struct udevice *dev) |
||||
{ |
||||
u8 data; |
||||
|
||||
data = TPM_STS_COMMAND_READY; |
||||
st33zp24_i2c_write(dev, TPM_STS, &data, 1); |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_wait_for_stat wait for a TPM_STS value |
||||
* @param: chip, the tpm chip description |
||||
* @param: mask, the value mask to wait |
||||
* @param: timeout, the timeout |
||||
* @param: status, |
||||
* @return: the tpm status, 0 if success, -ETIME if timeout is reached. |
||||
*/ |
||||
static int st33zp24_i2c_wait_for_stat(struct udevice *dev, u8 mask, |
||||
unsigned long timeout, int *status) |
||||
{ |
||||
unsigned long start, stop; |
||||
|
||||
/* Check current status */ |
||||
*status = st33zp24_i2c_status(dev); |
||||
if ((*status & mask) == mask) |
||||
return 0; |
||||
|
||||
start = get_timer(0); |
||||
stop = timeout; |
||||
do { |
||||
udelay(TPM_TIMEOUT_MS * 1000); |
||||
*status = st33zp24_i2c_status(dev); |
||||
if ((*status & mask) == mask) |
||||
return 0; |
||||
} while (get_timer(start) < stop); |
||||
|
||||
return -ETIME; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_recv_data receive data |
||||
* @param: chip, the tpm chip description |
||||
* @param: buf, the buffer where the data are received |
||||
* @param: count, the number of data to receive |
||||
* @return: the number of bytes read from TPM FIFO. |
||||
*/ |
||||
static int st33zp24_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
int size = 0, burstcnt, len, ret, status; |
||||
|
||||
while (size < count && |
||||
st33zp24_i2c_wait_for_stat(dev, TPM_STS_DATA_AVAIL | TPM_STS_VALID, |
||||
chip->timeout_c, &status) == 0) { |
||||
burstcnt = st33zp24_i2c_get_burstcount(dev); |
||||
if (burstcnt < 0) |
||||
return burstcnt; |
||||
len = min_t(int, burstcnt, count - size); |
||||
ret = st33zp24_i2c_read(dev, TPM_DATA_FIFO, buf + size, len); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
size += len; |
||||
} |
||||
|
||||
return size; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_recv received TPM response through TPM phy. |
||||
* @param: chip, tpm_chip description. |
||||
* @param: buf, the buffer to store data. |
||||
* @param: count, the number of bytes that can received (sizeof buf). |
||||
* @return: Returns zero in case of success else -EIO. |
||||
*/ |
||||
static int st33zp24_i2c_recv(struct udevice *dev, u8 *buf, size_t count) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
int size, expected; |
||||
|
||||
if (!chip) |
||||
return -ENODEV; |
||||
|
||||
if (count < TPM_HEADER_SIZE) { |
||||
size = -EIO; |
||||
goto out; |
||||
} |
||||
|
||||
size = st33zp24_i2c_recv_data(dev, buf, TPM_HEADER_SIZE); |
||||
if (size < TPM_HEADER_SIZE) { |
||||
debug("TPM error, unable to read header\n"); |
||||
goto out; |
||||
} |
||||
|
||||
expected = get_unaligned_be32(buf + 2); |
||||
if (expected > count) { |
||||
size = -EIO; |
||||
goto out; |
||||
} |
||||
|
||||
size += st33zp24_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE], |
||||
expected - TPM_HEADER_SIZE); |
||||
if (size < expected) { |
||||
debug("TPM error, unable to read remaining bytes of result\n"); |
||||
size = -EIO; |
||||
goto out; |
||||
} |
||||
|
||||
out: |
||||
st33zp24_i2c_cancel(dev); |
||||
st33zp24_i2c_release_locality(dev); |
||||
|
||||
return size; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_i2c_send send TPM commands through TPM phy. |
||||
* @param: chip, tpm_chip description. |
||||
* @param: buf, the buffer to send. |
||||
* @param: len, the number of bytes to send. |
||||
* @return: Returns zero in case of success else the negative error code. |
||||
*/ |
||||
static int st33zp24_i2c_send(struct udevice *dev, const u8 *buf, size_t len) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
u32 i, size; |
||||
int burstcnt, ret, status; |
||||
u8 data, tpm_stat; |
||||
|
||||
if (!chip) |
||||
return -ENODEV; |
||||
if (len < TPM_HEADER_SIZE) |
||||
return -EIO; |
||||
|
||||
ret = st33zp24_i2c_request_locality(dev); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
tpm_stat = st33zp24_i2c_status(dev); |
||||
if ((tpm_stat & TPM_STS_COMMAND_READY) == 0) { |
||||
st33zp24_i2c_cancel(dev); |
||||
if (st33zp24_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY, |
||||
chip->timeout_b, &status) < 0) { |
||||
ret = -ETIME; |
||||
goto out_err; |
||||
} |
||||
} |
||||
|
||||
for (i = 0; i < len - 1;) { |
||||
burstcnt = st33zp24_i2c_get_burstcount(dev); |
||||
if (burstcnt < 0) |
||||
return burstcnt; |
||||
|
||||
size = min_t(int, len - i - 1, burstcnt); |
||||
ret = st33zp24_i2c_write(dev, TPM_DATA_FIFO, buf + i, size); |
||||
if (ret < 0) |
||||
goto out_err; |
||||
|
||||
i += size; |
||||
} |
||||
|
||||
tpm_stat = st33zp24_i2c_status(dev); |
||||
if ((tpm_stat & TPM_STS_DATA_EXPECT) == 0) { |
||||
ret = -EIO; |
||||
goto out_err; |
||||
} |
||||
|
||||
ret = st33zp24_i2c_write(dev, TPM_DATA_FIFO, buf + len - 1, 1); |
||||
if (ret < 0) |
||||
goto out_err; |
||||
|
||||
tpm_stat = st33zp24_i2c_status(dev); |
||||
if ((tpm_stat & TPM_STS_DATA_EXPECT) != 0) { |
||||
ret = -EIO; |
||||
goto out_err; |
||||
} |
||||
|
||||
data = TPM_STS_GO; |
||||
ret = st33zp24_i2c_write(dev, TPM_STS, &data, 1); |
||||
if (ret < 0) |
||||
goto out_err; |
||||
|
||||
return len; |
||||
|
||||
out_err: |
||||
st33zp24_i2c_cancel(dev); |
||||
st33zp24_i2c_release_locality(dev); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int st33zp24_i2c_cleanup(struct udevice *dev) |
||||
{ |
||||
st33zp24_i2c_cancel(dev); |
||||
/*
|
||||
* The TPM needs some time to clean up here, |
||||
* so we sleep rather than keeping the bus busy |
||||
*/ |
||||
mdelay(2); |
||||
st33zp24_i2c_release_locality(dev); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int st33zp24_i2c_init(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
|
||||
chip->is_open = 1; |
||||
|
||||
/* Default timeouts - these could move to the device tree */ |
||||
chip->timeout_a = TIS_SHORT_TIMEOUT_MS; |
||||
chip->timeout_b = TIS_LONG_TIMEOUT_MS; |
||||
chip->timeout_c = TIS_SHORT_TIMEOUT_MS; |
||||
chip->timeout_d = TIS_SHORT_TIMEOUT_MS; |
||||
|
||||
chip->locality = LOCALITY0; |
||||
|
||||
/*
|
||||
* A timeout query to TPM can be placed here. |
||||
* Standard timeout values are used so far |
||||
*/ |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int st33zp24_i2c_open(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
int rc; |
||||
|
||||
debug("%s: start\n", __func__); |
||||
if (chip->is_open) |
||||
return -EBUSY; |
||||
|
||||
rc = st33zp24_i2c_init(dev); |
||||
if (rc < 0) |
||||
chip->is_open = 0; |
||||
|
||||
return rc; |
||||
} |
||||
|
||||
static int st33zp24_i2c_close(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
|
||||
if (chip->is_open) { |
||||
st33zp24_i2c_release_locality(dev); |
||||
chip->is_open = 0; |
||||
chip->vend_dev = 0; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int st33zp24_i2c_get_desc(struct udevice *dev, char *buf, int size) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
|
||||
if (size < 50) |
||||
return -ENOSPC; |
||||
|
||||
return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)", |
||||
chip->is_open ? "open" : "closed", |
||||
dev->name, |
||||
chip->vend_dev >> 16); |
||||
} |
||||
|
||||
static const struct tpm_ops st33zp24_i2c_tpm_ops = { |
||||
.open = st33zp24_i2c_open, |
||||
.close = st33zp24_i2c_close, |
||||
.recv = st33zp24_i2c_recv, |
||||
.send = st33zp24_i2c_send, |
||||
.cleanup = st33zp24_i2c_cleanup, |
||||
.get_desc = st33zp24_i2c_get_desc, |
||||
}; |
||||
|
||||
static int st33zp24_i2c_probe(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
|
||||
/* Default timeouts */ |
||||
chip->timeout_a = TIS_SHORT_TIMEOUT_MS; |
||||
chip->timeout_b = TIS_LONG_TIMEOUT_MS; |
||||
chip->timeout_c = TIS_SHORT_TIMEOUT_MS; |
||||
chip->timeout_d = TIS_SHORT_TIMEOUT_MS; |
||||
|
||||
chip->locality = LOCALITY0; |
||||
|
||||
i2c_set_chip_offset_len(dev, 0); |
||||
|
||||
debug("ST33ZP24 I2C TPM from STMicroelectronics found\n"); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int st33zp24_i2c_remove(struct udevice *dev) |
||||
{ |
||||
st33zp24_i2c_release_locality(dev); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct udevice_id st33zp24_i2c_ids[] = { |
||||
{ .compatible = "st,st33zp24-i2c" }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(st33zp24_i2c) = { |
||||
.name = "st33zp24-i2c", |
||||
.id = UCLASS_TPM, |
||||
.of_match = of_match_ptr(st33zp24_i2c_ids), |
||||
.probe = st33zp24_i2c_probe, |
||||
.remove = st33zp24_i2c_remove, |
||||
.ops = &st33zp24_i2c_tpm_ops, |
||||
.priv_auto_alloc_size = sizeof(struct tpm_chip), |
||||
}; |
@ -0,0 +1,672 @@ |
||||
/*
|
||||
* STMicroelectronics TPM ST33ZP24 SPI UBOOT driver |
||||
* |
||||
* Copyright (C) 2016 STMicroelectronics |
||||
* |
||||
* Description: Device driver for ST33ZP24 SPI TPM TCG. |
||||
* |
||||
* This device driver implements the TPM interface as defined in |
||||
* the TCG TPM Interface Spec version 1.21, revision 1.0 and the |
||||
* STMicroelectronics Protocol Stack Specification version 1.2.0. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <fdtdec.h> |
||||
#include <spi.h> |
||||
#include <tpm.h> |
||||
#include <errno.h> |
||||
#include <linux/types.h> |
||||
#include <asm/unaligned.h> |
||||
#include <linux/compat.h> |
||||
|
||||
#include "tpm_tis.h" |
||||
#include "tpm_internal.h" |
||||
|
||||
#define TPM_ACCESS 0x0 |
||||
#define TPM_STS 0x18 |
||||
#define TPM_DATA_FIFO 0x24 |
||||
|
||||
#define LOCALITY0 0 |
||||
|
||||
#define TPM_DATA_FIFO 0x24 |
||||
#define TPM_INTF_CAPABILITY 0x14 |
||||
|
||||
#define TPM_DUMMY_BYTE 0x00 |
||||
#define TPM_WRITE_DIRECTION 0x80 |
||||
|
||||
#define MAX_SPI_LATENCY 15 |
||||
#define LOCALITY0 0 |
||||
|
||||
#define ST33ZP24_OK 0x5A |
||||
#define ST33ZP24_UNDEFINED_ERR 0x80 |
||||
#define ST33ZP24_BADLOCALITY 0x81 |
||||
#define ST33ZP24_TISREGISTER_UKNOWN 0x82 |
||||
#define ST33ZP24_LOCALITY_NOT_ACTIVATED 0x83 |
||||
#define ST33ZP24_HASH_END_BEFORE_HASH_START 0x84 |
||||
#define ST33ZP24_BAD_COMMAND_ORDER 0x85 |
||||
#define ST33ZP24_INCORECT_RECEIVED_LENGTH 0x86 |
||||
#define ST33ZP24_TPM_FIFO_OVERFLOW 0x89 |
||||
#define ST33ZP24_UNEXPECTED_READ_FIFO 0x8A |
||||
#define ST33ZP24_UNEXPECTED_WRITE_FIFO 0x8B |
||||
#define ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END 0x90 |
||||
#define ST33ZP24_DUMMY_BYTES 0x00 |
||||
|
||||
/*
|
||||
* TPM command can be up to 2048 byte, A TPM response can be up to |
||||
* 1024 byte. |
||||
* Between command and response, there are latency byte (up to 15 |
||||
* usually on st33zp24 2 are enough). |
||||
* |
||||
* Overall when sending a command and expecting an answer we need if |
||||
* worst case: |
||||
* 2048 (for the TPM command) + 1024 (for the TPM answer). We need |
||||
* some latency byte before the answer is available (max 15). |
||||
* We have 2048 + 1024 + 15. |
||||
*/ |
||||
#define ST33ZP24_SPI_BUFFER_SIZE (TPM_BUFSIZE + (TPM_BUFSIZE / 2) +\ |
||||
MAX_SPI_LATENCY) |
||||
|
||||
struct st33zp24_spi_phy { |
||||
int latency; |
||||
|
||||
u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE]; |
||||
u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE]; |
||||
}; |
||||
|
||||
static int st33zp24_spi_status_to_errno(u8 code) |
||||
{ |
||||
switch (code) { |
||||
case ST33ZP24_OK: |
||||
return 0; |
||||
case ST33ZP24_UNDEFINED_ERR: |
||||
case ST33ZP24_BADLOCALITY: |
||||
case ST33ZP24_TISREGISTER_UKNOWN: |
||||
case ST33ZP24_LOCALITY_NOT_ACTIVATED: |
||||
case ST33ZP24_HASH_END_BEFORE_HASH_START: |
||||
case ST33ZP24_BAD_COMMAND_ORDER: |
||||
case ST33ZP24_UNEXPECTED_READ_FIFO: |
||||
case ST33ZP24_UNEXPECTED_WRITE_FIFO: |
||||
case ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END: |
||||
return -EPROTO; |
||||
case ST33ZP24_INCORECT_RECEIVED_LENGTH: |
||||
case ST33ZP24_TPM_FIFO_OVERFLOW: |
||||
return -EMSGSIZE; |
||||
case ST33ZP24_DUMMY_BYTES: |
||||
return -ENOSYS; |
||||
} |
||||
return code; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_send |
||||
* Send byte to TPM register according to the ST33ZP24 SPI protocol. |
||||
* @param: tpm, the chip description |
||||
* @param: tpm_register, the tpm tis register where the data should be written |
||||
* @param: tpm_data, the tpm_data to write inside the tpm_register |
||||
* @param: tpm_size, The length of the data |
||||
* @return: should be zero if success else a negative error code. |
||||
*/ |
||||
static int st33zp24_spi_write(struct udevice *dev, u8 tpm_register, |
||||
const u8 *tpm_data, size_t tpm_size) |
||||
{ |
||||
int total_length = 0, ret; |
||||
struct spi_slave *slave = dev_get_parent_priv(dev); |
||||
struct st33zp24_spi_phy *phy = dev_get_platdata(dev); |
||||
|
||||
u8 *tx_buf = (u8 *)phy->tx_buf; |
||||
u8 *rx_buf = phy->rx_buf; |
||||
|
||||
tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0; |
||||
tx_buf[total_length++] = tpm_register; |
||||
|
||||
if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) { |
||||
tx_buf[total_length++] = tpm_size >> 8; |
||||
tx_buf[total_length++] = tpm_size; |
||||
} |
||||
memcpy(tx_buf + total_length, tpm_data, tpm_size); |
||||
total_length += tpm_size; |
||||
|
||||
memset(tx_buf + total_length, TPM_DUMMY_BYTE, phy->latency); |
||||
|
||||
total_length += phy->latency; |
||||
|
||||
ret = spi_claim_bus(slave); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
ret = spi_xfer(slave, total_length * 8, tx_buf, rx_buf, |
||||
SPI_XFER_BEGIN | SPI_XFER_END); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
spi_release_bus(slave); |
||||
|
||||
if (ret == 0) |
||||
ret = rx_buf[total_length - 1]; |
||||
|
||||
return st33zp24_spi_status_to_errno(ret); |
||||
} |
||||
|
||||
/*
|
||||
* spi_st33zp24_spi_read8_reg |
||||
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol. |
||||
* @param: tpm, the chip description |
||||
* @param: tpm_loc, the locality to read register from |
||||
* @param: tpm_register, the tpm tis register where the data should be read |
||||
* @param: tpm_data, the TPM response |
||||
* @param: tpm_size, tpm TPM response size to read. |
||||
* @return: should be zero if success else a negative error code. |
||||
*/ |
||||
static u8 st33zp24_spi_read8_reg(struct udevice *dev, u8 tpm_register, |
||||
u8 *tpm_data, size_t tpm_size) |
||||
{ |
||||
int total_length = 0, ret; |
||||
struct spi_slave *slave = dev_get_parent_priv(dev); |
||||
struct st33zp24_spi_phy *phy = dev_get_platdata(dev); |
||||
|
||||
u8 *tx_buf = (u8 *)phy->tx_buf; |
||||
u8 *rx_buf = phy->rx_buf; |
||||
|
||||
/* Pre-Header */ |
||||
tx_buf[total_length++] = LOCALITY0; |
||||
tx_buf[total_length++] = tpm_register; |
||||
|
||||
memset(&tx_buf[total_length], TPM_DUMMY_BYTE, |
||||
phy->latency + tpm_size); |
||||
total_length += phy->latency + tpm_size; |
||||
|
||||
ret = spi_claim_bus(slave); |
||||
if (ret < 0) |
||||
return 0; |
||||
|
||||
ret = spi_xfer(slave, total_length * 8, tx_buf, rx_buf, |
||||
SPI_XFER_BEGIN | SPI_XFER_END); |
||||
if (ret < 0) |
||||
return 0; |
||||
|
||||
spi_release_bus(slave); |
||||
|
||||
if (tpm_size > 0 && ret == 0) { |
||||
ret = rx_buf[total_length - tpm_size - 1]; |
||||
memcpy(tpm_data, rx_buf + total_length - tpm_size, tpm_size); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_recv |
||||
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol. |
||||
* @param: phy_id, the phy description |
||||
* @param: tpm_register, the tpm tis register where the data should be read |
||||
* @param: tpm_data, the TPM response |
||||
* @param: tpm_size, tpm TPM response size to read. |
||||
* @return: number of byte read successfully: should be one if success. |
||||
*/ |
||||
static int st33zp24_spi_read(struct udevice *dev, u8 tpm_register, |
||||
u8 *tpm_data, size_t tpm_size) |
||||
{ |
||||
int ret; |
||||
|
||||
ret = st33zp24_spi_read8_reg(dev, tpm_register, tpm_data, tpm_size); |
||||
if (!st33zp24_spi_status_to_errno(ret)) |
||||
return tpm_size; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int st33zp24_spi_evaluate_latency(struct udevice *dev) |
||||
{ |
||||
int latency = 1, status = 0; |
||||
u8 data = 0; |
||||
struct st33zp24_spi_phy *phy = dev_get_platdata(dev); |
||||
|
||||
while (!status && latency < MAX_SPI_LATENCY) { |
||||
phy->latency = latency; |
||||
status = st33zp24_spi_read8_reg(dev, TPM_INTF_CAPABILITY, |
||||
&data, 1); |
||||
latency++; |
||||
} |
||||
if (status < 0) |
||||
return status; |
||||
if (latency == MAX_SPI_LATENCY) |
||||
return -ENODEV; |
||||
|
||||
return latency - 1; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_release_locality release the active locality |
||||
* @param: chip, the tpm chip description. |
||||
*/ |
||||
static void st33zp24_spi_release_locality(struct udevice *dev) |
||||
{ |
||||
u8 data = TPM_ACCESS_ACTIVE_LOCALITY; |
||||
|
||||
st33zp24_spi_write(dev, TPM_ACCESS, &data, 1); |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_check_locality if the locality is active |
||||
* @param: chip, the tpm chip description |
||||
* @return: the active locality or -EACCES. |
||||
*/ |
||||
static int st33zp24_spi_check_locality(struct udevice *dev) |
||||
{ |
||||
u8 data; |
||||
u8 status; |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
|
||||
status = st33zp24_spi_read(dev, TPM_ACCESS, &data, 1); |
||||
if (status && (data & |
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == |
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) |
||||
return chip->locality; |
||||
|
||||
return -EACCES; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_request_locality request the TPM locality |
||||
* @param: chip, the chip description |
||||
* @return: the active locality or negative value. |
||||
*/ |
||||
static int st33zp24_spi_request_locality(struct udevice *dev) |
||||
{ |
||||
unsigned long start, stop; |
||||
long ret; |
||||
u8 data; |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
|
||||
if (st33zp24_spi_check_locality(dev) == chip->locality) |
||||
return chip->locality; |
||||
|
||||
data = TPM_ACCESS_REQUEST_USE; |
||||
ret = st33zp24_spi_write(dev, TPM_ACCESS, &data, 1); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
/* wait for locality activated */ |
||||
start = get_timer(0); |
||||
stop = chip->timeout_a; |
||||
do { |
||||
if (st33zp24_spi_check_locality(dev) >= 0) |
||||
return chip->locality; |
||||
udelay(TPM_TIMEOUT_MS * 1000); |
||||
} while (get_timer(start) < stop); |
||||
|
||||
return -EACCES; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_status return the TPM_STS register |
||||
* @param: chip, the tpm chip description |
||||
* @return: the TPM_STS register value. |
||||
*/ |
||||
static u8 st33zp24_spi_status(struct udevice *dev) |
||||
{ |
||||
u8 data; |
||||
|
||||
st33zp24_spi_read(dev, TPM_STS, &data, 1); |
||||
return data; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_get_burstcount return the burstcount address 0x19 0x1A |
||||
* @param: chip, the chip description |
||||
* return: the burstcount or -TPM_DRIVER_ERR in case of error. |
||||
*/ |
||||
static int st33zp24_spi_get_burstcount(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
unsigned long start, stop; |
||||
int burstcnt, status; |
||||
u8 tpm_reg, temp; |
||||
|
||||
/* wait for burstcount */ |
||||
start = get_timer(0); |
||||
stop = chip->timeout_d; |
||||
do { |
||||
tpm_reg = TPM_STS + 1; |
||||
status = st33zp24_spi_read(dev, tpm_reg, &temp, 1); |
||||
if (status < 0) |
||||
return -EBUSY; |
||||
|
||||
tpm_reg = TPM_STS + 2; |
||||
burstcnt = temp; |
||||
status = st33zp24_spi_read(dev, tpm_reg, &temp, 1); |
||||
if (status < 0) |
||||
return -EBUSY; |
||||
|
||||
burstcnt |= temp << 8; |
||||
if (burstcnt) |
||||
return burstcnt; |
||||
udelay(TIS_SHORT_TIMEOUT_MS * 1000); |
||||
} while (get_timer(start) < stop); |
||||
|
||||
return -EBUSY; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_cancel, cancel the current command execution or |
||||
* set STS to COMMAND READY. |
||||
* @param: chip, tpm_chip description. |
||||
*/ |
||||
static void st33zp24_spi_cancel(struct udevice *dev) |
||||
{ |
||||
u8 data; |
||||
|
||||
data = TPM_STS_COMMAND_READY; |
||||
st33zp24_spi_write(dev, TPM_STS, &data, 1); |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_wait_for_stat wait for a TPM_STS value |
||||
* @param: chip, the tpm chip description |
||||
* @param: mask, the value mask to wait |
||||
* @param: timeout, the timeout |
||||
* @param: status, |
||||
* @return: the tpm status, 0 if success, -ETIME if timeout is reached. |
||||
*/ |
||||
static int st33zp24_spi_wait_for_stat(struct udevice *dev, u8 mask, |
||||
unsigned long timeout, int *status) |
||||
{ |
||||
unsigned long start, stop; |
||||
|
||||
/* Check current status */ |
||||
*status = st33zp24_spi_status(dev); |
||||
if ((*status & mask) == mask) |
||||
return 0; |
||||
|
||||
start = get_timer(0); |
||||
stop = timeout; |
||||
do { |
||||
udelay(TPM_TIMEOUT_MS * 1000); |
||||
*status = st33zp24_spi_status(dev); |
||||
if ((*status & mask) == mask) |
||||
return 0; |
||||
} while (get_timer(start) < stop); |
||||
|
||||
return -ETIME; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_recv_data receive data |
||||
* @param: chip, the tpm chip description |
||||
* @param: buf, the buffer where the data are received |
||||
* @param: count, the number of data to receive |
||||
* @return: the number of bytes read from TPM FIFO. |
||||
*/ |
||||
static int st33zp24_spi_recv_data(struct udevice *dev, u8 *buf, size_t count) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
int size = 0, burstcnt, len, ret, status; |
||||
|
||||
while (size < count && |
||||
st33zp24_spi_wait_for_stat(dev, TPM_STS_DATA_AVAIL | TPM_STS_VALID, |
||||
chip->timeout_c, &status) == 0) { |
||||
burstcnt = st33zp24_spi_get_burstcount(dev); |
||||
if (burstcnt < 0) |
||||
return burstcnt; |
||||
len = min_t(int, burstcnt, count - size); |
||||
ret = st33zp24_spi_read(dev, TPM_DATA_FIFO, buf + size, len); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
size += len; |
||||
} |
||||
return size; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_recv received TPM response through TPM phy. |
||||
* @param: chip, tpm_chip description. |
||||
* @param: buf, the buffer to store data. |
||||
* @param: count, the number of bytes that can received (sizeof buf). |
||||
* @return: Returns zero in case of success else -EIO. |
||||
*/ |
||||
static int st33zp24_spi_recv(struct udevice *dev, u8 *buf, size_t count) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
int size, expected; |
||||
|
||||
if (!chip) |
||||
return -ENODEV; |
||||
|
||||
if (count < TPM_HEADER_SIZE) { |
||||
size = -EIO; |
||||
goto out; |
||||
} |
||||
|
||||
size = st33zp24_spi_recv_data(dev, buf, TPM_HEADER_SIZE); |
||||
if (size < TPM_HEADER_SIZE) { |
||||
debug("TPM error, unable to read header\n"); |
||||
goto out; |
||||
} |
||||
|
||||
expected = get_unaligned_be32(buf + 2); |
||||
if (expected > count) { |
||||
size = -EIO; |
||||
goto out; |
||||
} |
||||
|
||||
size += st33zp24_spi_recv_data(dev, &buf[TPM_HEADER_SIZE], |
||||
expected - TPM_HEADER_SIZE); |
||||
if (size < expected) { |
||||
debug("TPM error, unable to read remaining bytes of result\n"); |
||||
size = -EIO; |
||||
goto out; |
||||
} |
||||
|
||||
out: |
||||
st33zp24_spi_cancel(dev); |
||||
st33zp24_spi_release_locality(dev); |
||||
|
||||
return size; |
||||
} |
||||
|
||||
/*
|
||||
* st33zp24_spi_send send TPM commands through TPM phy. |
||||
* @param: chip, tpm_chip description. |
||||
* @param: buf, the buffer to send. |
||||
* @param: len, the number of bytes to send. |
||||
* @return: Returns zero in case of success else the negative error code. |
||||
*/ |
||||
static int st33zp24_spi_send(struct udevice *dev, const u8 *buf, size_t len) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
u32 i, size; |
||||
int burstcnt, ret, status; |
||||
u8 data, tpm_stat; |
||||
|
||||
if (!chip) |
||||
return -ENODEV; |
||||
if (len < TPM_HEADER_SIZE) |
||||
return -EIO; |
||||
|
||||
ret = st33zp24_spi_request_locality(dev); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
tpm_stat = st33zp24_spi_status(dev); |
||||
if ((tpm_stat & TPM_STS_COMMAND_READY) == 0) { |
||||
st33zp24_spi_cancel(dev); |
||||
if (st33zp24_spi_wait_for_stat(dev, TPM_STS_COMMAND_READY, |
||||
chip->timeout_b, &status) < 0) { |
||||
ret = -ETIME; |
||||
goto out_err; |
||||
} |
||||
} |
||||
|
||||
for (i = 0; i < len - 1;) { |
||||
burstcnt = st33zp24_spi_get_burstcount(dev); |
||||
if (burstcnt < 0) |
||||
return burstcnt; |
||||
|
||||
size = min_t(int, len - i - 1, burstcnt); |
||||
ret = st33zp24_spi_write(dev, TPM_DATA_FIFO, buf + i, size); |
||||
if (ret < 0) |
||||
goto out_err; |
||||
|
||||
i += size; |
||||
} |
||||
|
||||
tpm_stat = st33zp24_spi_status(dev); |
||||
if ((tpm_stat & TPM_STS_DATA_EXPECT) == 0) { |
||||
ret = -EIO; |
||||
goto out_err; |
||||
} |
||||
|
||||
ret = st33zp24_spi_write(dev, TPM_DATA_FIFO, buf + len - 1, 1); |
||||
if (ret < 0) |
||||
goto out_err; |
||||
|
||||
tpm_stat = st33zp24_spi_status(dev); |
||||
if ((tpm_stat & TPM_STS_DATA_EXPECT) != 0) { |
||||
ret = -EIO; |
||||
goto out_err; |
||||
} |
||||
|
||||
data = TPM_STS_GO; |
||||
ret = st33zp24_spi_write(dev, TPM_STS, &data, 1); |
||||
if (ret < 0) |
||||
goto out_err; |
||||
|
||||
return len; |
||||
|
||||
out_err: |
||||
st33zp24_spi_cancel(dev); |
||||
st33zp24_spi_release_locality(dev); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int st33zp24_spi_cleanup(struct udevice *dev) |
||||
{ |
||||
st33zp24_spi_cancel(dev); |
||||
/*
|
||||
* The TPM needs some time to clean up here, |
||||
* so we sleep rather than keeping the bus busy |
||||
*/ |
||||
mdelay(2); |
||||
st33zp24_spi_release_locality(dev); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int st33zp24_spi_init(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
struct st33zp24_spi_phy *phy = dev_get_platdata(dev); |
||||
|
||||
chip->is_open = 1; |
||||
|
||||
/* Default timeouts - these could move to the device tree */ |
||||
chip->timeout_a = TIS_SHORT_TIMEOUT_MS; |
||||
chip->timeout_b = TIS_LONG_TIMEOUT_MS; |
||||
chip->timeout_c = TIS_SHORT_TIMEOUT_MS; |
||||
chip->timeout_d = TIS_SHORT_TIMEOUT_MS; |
||||
|
||||
chip->locality = LOCALITY0; |
||||
|
||||
phy->latency = st33zp24_spi_evaluate_latency(dev); |
||||
if (phy->latency <= 0) |
||||
return -ENODEV; |
||||
|
||||
/*
|
||||
* A timeout query to TPM can be placed here. |
||||
* Standard timeout values are used so far |
||||
*/ |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int st33zp24_spi_open(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
int rc; |
||||
|
||||
debug("%s: start\n", __func__); |
||||
if (chip->is_open) |
||||
return -EBUSY; |
||||
|
||||
rc = st33zp24_spi_init(dev); |
||||
if (rc < 0) |
||||
chip->is_open = 0; |
||||
|
||||
return rc; |
||||
} |
||||
|
||||
static int st33zp24_spi_close(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
|
||||
if (chip->is_open) { |
||||
st33zp24_spi_release_locality(dev); |
||||
chip->is_open = 0; |
||||
chip->vend_dev = 0; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int st33zp24_spi_get_desc(struct udevice *dev, char *buf, int size) |
||||
{ |
||||
struct tpm_chip *chip = dev_get_priv(dev); |
||||
|
||||
if (size < 50) |
||||
return -ENOSPC; |
||||
|
||||
return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)", |
||||
chip->is_open ? "open" : "closed", |
||||
dev->name, |
||||
chip->vend_dev >> 16); |
||||
} |
||||
|
||||
const struct tpm_ops st33zp24_spi_tpm_ops = { |
||||
.open = st33zp24_spi_open, |
||||
.close = st33zp24_spi_close, |
||||
.recv = st33zp24_spi_recv, |
||||
.send = st33zp24_spi_send, |
||||
.cleanup = st33zp24_spi_cleanup, |
||||
.get_desc = st33zp24_spi_get_desc, |
||||
}; |
||||
|
||||
static int st33zp24_spi_probe(struct udevice *dev) |
||||
{ |
||||
struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev); |
||||
|
||||
uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS; |
||||
uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS; |
||||
uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS; |
||||
uc_priv->retry_time_ms = TPM_TIMEOUT_MS; |
||||
|
||||
debug("ST33ZP24 SPI TPM from STMicroelectronics found\n"); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int st33zp24_spi_remove(struct udevice *dev) |
||||
{ |
||||
st33zp24_spi_release_locality(dev); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct udevice_id st33zp24_spi_ids[] = { |
||||
{ .compatible = "st,st33zp24-spi" }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(st33zp24_spi_spi) = { |
||||
.name = "st33zp24-spi", |
||||
.id = UCLASS_TPM, |
||||
.of_match = of_match_ptr(st33zp24_spi_ids), |
||||
.probe = st33zp24_spi_probe, |
||||
.remove = st33zp24_spi_remove, |
||||
.ops = &st33zp24_spi_tpm_ops, |
||||
.priv_auto_alloc_size = sizeof(struct tpm_chip), |
||||
.platdata_auto_alloc_size = sizeof(struct st33zp24_spi_phy), |
||||
}; |
@ -1,44 +0,0 @@ |
||||
DFU TEST CASE DESCRIPTION: |
||||
|
||||
The prerequisites for running this script are assured by |
||||
dfu_gadget_test_init.sh, which is automatically invoked by dfu_gadget_test.sh. |
||||
In this file user is able to generate their own set of test files by altering |
||||
the default set of TEST_FILES_SIZES variable. |
||||
The dfu_gadget_test_init.sh would generate test images only if they are not |
||||
already generated. |
||||
|
||||
On the target device, environment variable "dfu_alt_info" must contain at |
||||
least: |
||||
|
||||
dfu_test.bin fat 0 6;dfudummy.bin fat 0 6 |
||||
|
||||
Depending on your device, you may need to replace "fat" with |
||||
"ext4", and "6" with the relevant partition number. For reference please |
||||
consult the config file for TRATS/TRATS2 devices |
||||
(../../include/configs/trats{2}.h) |
||||
|
||||
One can use fat, ext4 or any other supported file system supported by U-Boot. |
||||
These can be created by exporting storage devices via UMS (ums 0 mmc 0) and |
||||
using standard tools on host (like mkfs.ext4). |
||||
|
||||
Example usage: |
||||
1. On the target: |
||||
setenv dfu_alt_info dfu_test.bin fat 0 6\;dfudummy.bin fat 0 6 |
||||
dfu 0 mmc 0 |
||||
2. On the host: |
||||
test/dfu/dfu_gadget_test.sh X Y [test file name] [usb device vendor:product] |
||||
e.g. test/dfu/dfu_gadget_test.sh 0 1 |
||||
or |
||||
e.g. test/dfu/dfu_gadget_test.sh 0 1 ./dat_960.img |
||||
or |
||||
e.g. test/dfu/dfu_gadget_test.sh 0 1 0451:d022 |
||||
or |
||||
e.g. test/dfu/dfu_gadget_test.sh 0 1 ./dat_960.img 0451:d022 |
||||
|
||||
... where X and Y are dfu_test.bin's and dfudummy.bin's alt setting numbers. |
||||
They can be obtained from dfu-util -l or $dfu_alt_info. |
||||
It is also possible to pass optional [test file name] to force the script to |
||||
test one particular file. |
||||
If many DFU devices are connected, it may be useful to filter on USB |
||||
vendor/product ID (0451:d022). |
||||
One can get them by running "lsusb" command on a host PC. |
@ -1,108 +0,0 @@ |
||||
#! /bin/bash |
||||
|
||||
# Copyright (C) 2014 Samsung Electronics |
||||
# Lukasz Majewski <l.majewski@samsung.com> |
||||
# |
||||
# Script fixes, enhancements and testing: |
||||
# Stephen Warren <swarren@nvidia.com> |
||||
# |
||||
# DFU operation test script |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
|
||||
set -e # any command return if not equal to zero |
||||
clear |
||||
|
||||
COLOUR_RED="\33[31m" |
||||
COLOUR_GREEN="\33[32m" |
||||
COLOUR_DEFAULT="\33[0m" |
||||
|
||||
DIR=./ |
||||
SUFFIX=img |
||||
RCV_DIR=rcv/ |
||||
LOG_FILE=./log/log-`date +%d-%m-%Y_%H-%M-%S` |
||||
|
||||
cd `dirname $0` |
||||
./dfu_gadget_test_init.sh |
||||
|
||||
cleanup () { |
||||
rm -rf $DIR$RCV_DIR |
||||
} |
||||
|
||||
die () { |
||||
printf " $COLOUR_RED FAILED $COLOUR_DEFAULT \n" |
||||
cleanup |
||||
exit 1 |
||||
} |
||||
|
||||
calculate_md5sum () { |
||||
MD5SUM=`md5sum $1` |
||||
MD5SUM=`echo $MD5SUM | cut -d ' ' -f1` |
||||
echo "md5sum:"$MD5SUM |
||||
} |
||||
|
||||
dfu_test_file () { |
||||
printf "$COLOUR_GREEN ========================================================================================= $COLOUR_DEFAULT\n" |
||||
printf "File:$COLOUR_GREEN %s $COLOUR_DEFAULT\n" $1 |
||||
|
||||
dfu-util $USB_DEV -D $1 -a $TARGET_ALT_SETTING >> $LOG_FILE 2>&1 || die $? |
||||
|
||||
echo -n "TX: " |
||||
calculate_md5sum $1 |
||||
|
||||
MD5_TX=$MD5SUM |
||||
|
||||
dfu-util $USB_DEV -D ${DIR}/dfudummy.bin -a $TARGET_ALT_SETTING_B >> $LOG_FILE 2>&1 || die $? |
||||
|
||||
N_FILE=$DIR$RCV_DIR${1:2}"_rcv" |
||||
|
||||
dfu-util $USB_DEV -U $N_FILE -a $TARGET_ALT_SETTING >> $LOG_FILE 2>&1 || die $? |
||||
|
||||
echo -n "RX: " |
||||
calculate_md5sum $N_FILE |
||||
MD5_RX=$MD5SUM |
||||
|
||||
if [ "$MD5_TX" == "$MD5_RX" ]; then |
||||
printf " $COLOUR_GREEN -------> OK $COLOUR_DEFAULT \n" |
||||
else |
||||
printf " $COLOUR_RED -------> FAILED $COLOUR_DEFAULT \n" |
||||
cleanup |
||||
exit 1 |
||||
fi |
||||
|
||||
} |
||||
|
||||
printf "$COLOUR_GREEN========================================================================================= $COLOUR_DEFAULT\n" |
||||
echo "DFU EP0 transmission test program" |
||||
echo "Trouble shoot -> disable DBG (even the KERN_DEBUG) in the UDC driver" |
||||
echo "@ -> TRATS2 # dfu 0 mmc 0" |
||||
cleanup |
||||
mkdir -p $DIR$RCV_DIR |
||||
touch $LOG_FILE |
||||
|
||||
if [ $# -eq 0 ] |
||||
then |
||||
printf " $COLOUR_RED Please pass alt setting number!! $COLOUR_DEFAULT \n" |
||||
exit 0 |
||||
fi |
||||
|
||||
TARGET_ALT_SETTING=$1 |
||||
TARGET_ALT_SETTING_B=$2 |
||||
|
||||
file=$3 |
||||
[[ $3 == *':'* ]] && USB_DEV="-d $3" && file="" |
||||
[ $# -eq 4 ] && USB_DEV="-d $4" |
||||
|
||||
if [ -n "$file" ] |
||||
then |
||||
dfu_test_file $file |
||||
else |
||||
for f in $DIR*.$SUFFIX |
||||
do |
||||
dfu_test_file $f |
||||
done |
||||
fi |
||||
|
||||
cleanup |
||||
|
||||
exit 0 |
@ -1,45 +0,0 @@ |
||||
#! /bin/bash |
||||
|
||||
# Copyright (C) 2014 Samsung Electronics |
||||
# Lukasz Majewski <l.majewski@samsung.com> |
||||
# |
||||
# Script fixes, enhancements and testing: |
||||
# Stephen Warren <swarren@nvidia.com> |
||||
# |
||||
# Script for test files generation |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
|
||||
set -e # any command return if not equal to zero |
||||
clear |
||||
|
||||
COLOUR_RED="\33[31m" |
||||
COLOUR_GREEN="\33[32m" |
||||
COLOUR_DEFAULT="\33[0m" |
||||
|
||||
LOG_DIR="./log" |
||||
|
||||
if [ $# -eq 0 ]; then |
||||
TEST_FILES_SIZES="63 64 65 127 128 129 4095 4096 4097 959 960 961 1048575 1048576 8M" |
||||
else |
||||
TEST_FILES_SIZES=$@ |
||||
fi |
||||
|
||||
printf "Init script for generating data necessary for DFU test script" |
||||
|
||||
if [ ! -d $LOG_DIR ]; then |
||||
`mkdir $LOG_DIR` |
||||
fi |
||||
|
||||
for size in $TEST_FILES_SIZES |
||||
do |
||||
FILE="./dat_$size.img" |
||||
if [ ! -f $FILE ]; then |
||||
dd if=/dev/urandom of="./dat_$size.img" bs=$size count=1 > /dev/null 2>&1 || exit $? |
||||
fi |
||||
done |
||||
dd if=/dev/urandom of="./dfudummy.bin" bs=1024 count=1 > /dev/null 2>&1 || exit $? |
||||
|
||||
printf "$COLOUR_GREEN OK $COLOUR_DEFAULT \n" |
||||
|
||||
exit 0 |
@ -0,0 +1,279 @@ |
||||
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0 |
||||
|
||||
# Test U-Boot's "dfu" command. The test starts DFU in U-Boot, waits for USB |
||||
# device enumeration on the host, executes dfu-util multiple times to test |
||||
# various transfer sizes, many of which trigger USB driver edge cases, and |
||||
# finally aborts the "dfu" command in U-Boot. |
||||
|
||||
import os |
||||
import os.path |
||||
import pytest |
||||
import u_boot_utils |
||||
|
||||
""" |
||||
Note: This test relies on: |
||||
|
||||
a) boardenv_* to contain configuration values to define which USB ports are |
||||
available for testing. Without this, this test will be automatically skipped. |
||||
For example: |
||||
|
||||
env__usb_dev_ports = ( |
||||
{ |
||||
"fixture_id": "micro_b", |
||||
"tgt_usb_ctlr": "0", |
||||
"host_usb_dev_node": "/dev/usbdev-p2371-2180", |
||||
# This parameter is optional /if/ you only have a single board |
||||
# attached to your host at a time. |
||||
"host_usb_port_path": "3-13", |
||||
}, |
||||
) |
||||
|
||||
env__dfu_configs = ( |
||||
# eMMC, partition 1 |
||||
{ |
||||
"fixture_id": "emmc", |
||||
"alt_info": "/dfu_test.bin ext4 0 1;/dfu_dummy.bin ext4 0 1", |
||||
"cmd_params": "mmc 0", |
||||
# This value is optional. |
||||
# If present, it specified the set of transfer sizes tested. |
||||
# If missing, a default list of sizes will be used, which covers |
||||
# various useful corner cases. |
||||
# Manually specifying test sizes is useful if you wish to test 4 DFU |
||||
# configurations, but don't want to test every single transfer size |
||||
# on each, to avoid bloating the overall time taken by testing. |
||||
"test_sizes": (63, 64, 65), |
||||
}, |
||||
) |
||||
|
||||
b) udev rules to set permissions on devices nodes, so that sudo is not |
||||
required. For example: |
||||
|
||||
ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666" |
||||
|
||||
(You may wish to change the group ID instead of setting the permissions wide |
||||
open. All that matters is that the user ID running the test can access the |
||||
device.) |
||||
""" |
||||
|
||||
# The set of file sizes to test. These values trigger various edge-cases such |
||||
# as one less than, equal to, and one greater than typical USB max packet |
||||
# sizes, and similar boundary conditions. |
||||
test_sizes_default = ( |
||||
64 - 1, |
||||
64, |
||||
64 + 1, |
||||
128 - 1, |
||||
128, |
||||
128 + 1, |
||||
960 - 1, |
||||
960, |
||||
960 + 1, |
||||
4096 - 1, |
||||
4096, |
||||
4096 + 1, |
||||
1024 * 1024 - 1, |
||||
1024 * 1024, |
||||
8 * 1024 * 1024, |
||||
) |
||||
|
||||
first_usb_dev_port = None |
||||
|
||||
@pytest.mark.buildconfigspec('cmd_dfu') |
||||
def test_dfu(u_boot_console, env__usb_dev_port, env__dfu_config): |
||||
"""Test the "dfu" command; the host system must be able to enumerate a USB |
||||
device when "dfu" is running, various DFU transfers are tested, and the |
||||
USB device must disappear when "dfu" is aborted. |
||||
|
||||
Args: |
||||
u_boot_console: A U-Boot console connection. |
||||
env__usb_dev_port: The single USB device-mode port specification on |
||||
which to run the test. See the file-level comment above for |
||||
details of the format. |
||||
env__dfu_config: The single DFU (memory region) configuration on which |
||||
to run the test. See the file-level comment above for details |
||||
of the format. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
""" |
||||
|
||||
def start_dfu(): |
||||
"""Start U-Boot's dfu shell command. |
||||
|
||||
This also waits for the host-side USB enumeration process to complete. |
||||
|
||||
Args: |
||||
None. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
""" |
||||
|
||||
fh = u_boot_utils.attempt_to_open_file( |
||||
env__usb_dev_port['host_usb_dev_node']) |
||||
if fh: |
||||
fh.close() |
||||
raise Exception('USB device present before dfu command invoked') |
||||
|
||||
u_boot_console.log.action( |
||||
'Starting long-running U-Boot dfu shell command') |
||||
|
||||
cmd = 'setenv dfu_alt_info "%s"' % env__dfu_config['alt_info'] |
||||
u_boot_console.run_command(cmd) |
||||
|
||||
cmd = 'dfu 0 ' + env__dfu_config['cmd_params'] |
||||
u_boot_console.run_command(cmd, wait_for_prompt=False) |
||||
u_boot_console.log.action('Waiting for DFU USB device to appear') |
||||
fh = u_boot_utils.wait_until_open_succeeds( |
||||
env__usb_dev_port['host_usb_dev_node']) |
||||
fh.close() |
||||
|
||||
def stop_dfu(ignore_errors): |
||||
"""Stop U-Boot's dfu shell command from executing. |
||||
|
||||
This also waits for the host-side USB de-enumeration process to |
||||
complete. |
||||
|
||||
Args: |
||||
ignore_errors: Ignore any errors. This is useful if an error has |
||||
already been detected, and the code is performing best-effort |
||||
cleanup. In this case, we do not want to mask the original |
||||
error by "honoring" any new errors. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
""" |
||||
|
||||
try: |
||||
u_boot_console.log.action( |
||||
'Stopping long-running U-Boot dfu shell command') |
||||
u_boot_console.ctrlc() |
||||
u_boot_console.log.action( |
||||
'Waiting for DFU USB device to disappear') |
||||
u_boot_utils.wait_until_file_open_fails( |
||||
env__usb_dev_port['host_usb_dev_node'], ignore_errors) |
||||
except: |
||||
if not ignore_errors: |
||||
raise |
||||
|
||||
def run_dfu_util(alt_setting, fn, up_dn_load_arg): |
||||
"""Invoke dfu-util on the host. |
||||
|
||||
Args: |
||||
alt_setting: The DFU "alternate setting" identifier to interact |
||||
with. |
||||
fn: The host-side file name to transfer. |
||||
up_dn_load_arg: '-U' or '-D' depending on whether a DFU upload or |
||||
download operation should be performed. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
""" |
||||
|
||||
cmd = ['dfu-util', '-a', str(alt_setting), up_dn_load_arg, fn] |
||||
if 'host_usb_port_path' in env__usb_dev_port: |
||||
cmd += ['-p', env__usb_dev_port['host_usb_port_path']] |
||||
u_boot_utils.run_and_log(u_boot_console, cmd) |
||||
u_boot_console.wait_for('Ctrl+C to exit ...') |
||||
|
||||
def dfu_write(alt_setting, fn): |
||||
"""Write a file to the target board using DFU. |
||||
|
||||
Args: |
||||
alt_setting: The DFU "alternate setting" identifier to interact |
||||
with. |
||||
fn: The host-side file name to transfer. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
""" |
||||
|
||||
run_dfu_util(alt_setting, fn, '-D') |
||||
|
||||
def dfu_read(alt_setting, fn): |
||||
"""Read a file from the target board using DFU. |
||||
|
||||
Args: |
||||
alt_setting: The DFU "alternate setting" identifier to interact |
||||
with. |
||||
fn: The host-side file name to transfer. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
""" |
||||
|
||||
# dfu-util fails reads/uploads if the host file already exists |
||||
if os.path.exists(fn): |
||||
os.remove(fn) |
||||
run_dfu_util(alt_setting, fn, '-U') |
||||
|
||||
def dfu_write_read_check(size): |
||||
"""Test DFU transfers of a specific size of data |
||||
|
||||
This function first writes data to the board then reads it back and |
||||
compares the written and read back data. Measures are taken to avoid |
||||
certain types of false positives. |
||||
|
||||
Args: |
||||
size: The data size to test. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
""" |
||||
|
||||
test_f = u_boot_utils.PersistentRandomFile(u_boot_console, |
||||
'dfu_%d.bin' % size, size) |
||||
readback_fn = u_boot_console.config.result_dir + '/dfu_readback.bin' |
||||
|
||||
u_boot_console.log.action('Writing test data to DFU primary ' + |
||||
'altsetting') |
||||
dfu_write(0, test_f.abs_fn) |
||||
|
||||
u_boot_console.log.action('Writing dummy data to DFU secondary ' + |
||||
'altsetting to clear DFU buffers') |
||||
dfu_write(1, dummy_f.abs_fn) |
||||
|
||||
u_boot_console.log.action('Reading DFU primary altsetting for ' + |
||||
'comparison') |
||||
dfu_read(0, readback_fn) |
||||
|
||||
u_boot_console.log.action('Comparing written and read data') |
||||
written_hash = test_f.content_hash |
||||
read_back_hash = u_boot_utils.md5sum_file(readback_fn, size) |
||||
assert(written_hash == read_back_hash) |
||||
|
||||
# This test may be executed against multiple USB ports. The test takes a |
||||
# long time, so we don't want to do the whole thing each time. Instead, |
||||
# execute the full test on the first USB port, and perform a very limited |
||||
# test on other ports. In the limited case, we solely validate that the |
||||
# host PC can enumerate the U-Boot USB device. |
||||
global first_usb_dev_port |
||||
if not first_usb_dev_port: |
||||
first_usb_dev_port = env__usb_dev_port |
||||
if env__usb_dev_port == first_usb_dev_port: |
||||
sizes = env__dfu_config.get('test_sizes', test_sizes_default) |
||||
else: |
||||
sizes = [] |
||||
|
||||
dummy_f = u_boot_utils.PersistentRandomFile(u_boot_console, |
||||
'dfu_dummy.bin', 1024) |
||||
|
||||
ignore_cleanup_errors = True |
||||
try: |
||||
start_dfu() |
||||
|
||||
u_boot_console.log.action( |
||||
'Overwriting DFU primary altsetting with dummy data') |
||||
dfu_write(0, dummy_f.abs_fn) |
||||
|
||||
for size in sizes: |
||||
with u_boot_console.log.section('Data size %d' % size): |
||||
dfu_write_read_check(size) |
||||
# Make the status of each sub-test obvious. If the test didn't |
||||
# pass, an exception was thrown so this code isn't executed. |
||||
u_boot_console.log.status_pass('OK') |
||||
ignore_cleanup_errors = False |
||||
finally: |
||||
stop_dfu(ignore_cleanup_errors) |
@ -0,0 +1,155 @@ |
||||
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0 |
||||
|
||||
# Test various network-related functionality, such as the dhcp, ping, and |
||||
# tftpboot commands. |
||||
|
||||
import pytest |
||||
import u_boot_utils |
||||
|
||||
""" |
||||
Note: This test relies on boardenv_* containing configuration values to define |
||||
which the network environment available for testing. Without this, this test |
||||
will be automatically skipped. |
||||
|
||||
For example: |
||||
|
||||
# Boolean indicating whether the Ethernet device is attached to USB, and hence |
||||
# USB enumeration needs to be performed prior to network tests. |
||||
# This variable may be omitted if its value is False. |
||||
env__net_uses_usb = False |
||||
|
||||
# Boolean indicating whether the Ethernet device is attached to PCI, and hence |
||||
# PCI enumeration needs to be performed prior to network tests. |
||||
# This variable may be omitted if its value is False. |
||||
env__net_uses_pci = True |
||||
|
||||
# True if a DHCP server is attached to the network, and should be tested. |
||||
# If DHCP testing is not possible or desired, this variable may be omitted or |
||||
# set to False. |
||||
env__net_dhcp_server = True |
||||
|
||||
# A list of environment variables that should be set in order to configure a |
||||
# static IP. If solely relying on DHCP, this variable may be omitted or set to |
||||
# an empty list. |
||||
env__net_static_env_vars = [ |
||||
("ipaddr", "10.0.0.100"), |
||||
("netmask", "255.255.255.0"), |
||||
("serverip", "10.0.0.1"), |
||||
] |
||||
|
||||
# Details regarding a file that may be read from a TFTP server. This variable |
||||
# may be omitted or set to None if TFTP testing is not possible or desired. |
||||
env__net_tftp_readable_file = { |
||||
"fn": "ubtest-readable.bin", |
||||
"size": 5058624, |
||||
"crc32": "c2244b26", |
||||
} |
||||
""" |
||||
|
||||
net_set_up = False |
||||
|
||||
def test_net_pre_commands(u_boot_console): |
||||
"""Execute any commands required to enable network hardware. |
||||
|
||||
These commands are provided by the boardenv_* file; see the comment at the |
||||
beginning of this file. |
||||
""" |
||||
|
||||
init_usb = u_boot_console.config.env.get('env__net_uses_usb', False) |
||||
if init_usb: |
||||
u_boot_console.run_command('usb start') |
||||
|
||||
init_pci = u_boot_console.config.env.get('env__net_uses_pci', False) |
||||
if init_pci: |
||||
u_boot_console.run_command('pci enum') |
||||
|
||||
@pytest.mark.buildconfigspec('cmd_dhcp') |
||||
def test_net_dhcp(u_boot_console): |
||||
"""Test the dhcp command. |
||||
|
||||
The boardenv_* file may be used to enable/disable this test; see the |
||||
comment at the beginning of this file. |
||||
""" |
||||
|
||||
test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False) |
||||
if not test_dhcp: |
||||
pytest.skip('No DHCP server available') |
||||
|
||||
u_boot_console.run_command('setenv autoload no') |
||||
output = u_boot_console.run_command('dhcp') |
||||
assert 'DHCP client bound to address ' in output |
||||
|
||||
global net_set_up |
||||
net_set_up = True |
||||
|
||||
@pytest.mark.buildconfigspec('net') |
||||
def test_net_setup_static(u_boot_console): |
||||
"""Set up a static IP configuration. |
||||
|
||||
The configuration is provided by the boardenv_* file; see the comment at |
||||
the beginning of this file. |
||||
""" |
||||
|
||||
env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None) |
||||
if not env_vars: |
||||
pytest.skip('No static network configuration is defined') |
||||
|
||||
for (var, val) in env_vars: |
||||
u_boot_console.run_command('setenv %s %s' % (var, val)) |
||||
|
||||
global net_set_up |
||||
net_set_up = True |
||||
|
||||
@pytest.mark.buildconfigspec('cmd_ping') |
||||
def test_net_ping(u_boot_console): |
||||
"""Test the ping command. |
||||
|
||||
The $serverip (as set up by either test_net_dhcp or test_net_setup_static) |
||||
is pinged. The test validates that the host is alive, as reported by the |
||||
ping command's output. |
||||
""" |
||||
|
||||
if not net_set_up: |
||||
pytest.skip('Network not initialized') |
||||
|
||||
output = u_boot_console.run_command('ping $serverip') |
||||
assert 'is alive' in output |
||||
|
||||
@pytest.mark.buildconfigspec('cmd_net') |
||||
def test_net_tftpboot(u_boot_console): |
||||
"""Test the tftpboot command. |
||||
|
||||
A file is downloaded from the TFTP server, its size and optionally its |
||||
CRC32 are validated. |
||||
|
||||
The details of the file to download are provided by the boardenv_* file; |
||||
see the comment at the beginning of this file. |
||||
""" |
||||
|
||||
if not net_set_up: |
||||
pytest.skip('Network not initialized') |
||||
|
||||
f = u_boot_console.config.env.get('env__net_tftp_readable_file', None) |
||||
if not f: |
||||
pytest.skip('No TFTP readable file to read') |
||||
|
||||
addr = u_boot_utils.find_ram_base(u_boot_console) |
||||
fn = f['fn'] |
||||
output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn)) |
||||
expected_text = 'Bytes transferred = ' |
||||
sz = f.get('size', None) |
||||
if sz: |
||||
expected_text += '%d' % sz |
||||
assert expected_text in output |
||||
|
||||
expected_crc = f.get('crc32', None) |
||||
if not expected_crc: |
||||
return |
||||
|
||||
if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y': |
||||
return |
||||
|
||||
output = u_boot_console.run_command('crc32 %x $filesize' % addr) |
||||
assert expected_crc in output |
@ -0,0 +1,209 @@ |
||||
# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0 |
||||
|
||||
# Utility code shared across multiple tests. |
||||
|
||||
import hashlib |
||||
import os |
||||
import os.path |
||||
import sys |
||||
import time |
||||
|
||||
def md5sum_data(data): |
||||
"""Calculate the MD5 hash of some data. |
||||
|
||||
Args: |
||||
data: The data to hash. |
||||
|
||||
Returns: |
||||
The hash of the data, as a binary string. |
||||
""" |
||||
|
||||
h = hashlib.md5() |
||||
h.update(data) |
||||
return h.digest() |
||||
|
||||
def md5sum_file(fn, max_length=None): |
||||
"""Calculate the MD5 hash of the contents of a file. |
||||
|
||||
Args: |
||||
fn: The filename of the file to hash. |
||||
max_length: The number of bytes to hash. If the file has more |
||||
bytes than this, they will be ignored. If None or omitted, the |
||||
entire file will be hashed. |
||||
|
||||
Returns: |
||||
The hash of the file content, as a binary string. |
||||
""" |
||||
|
||||
with open(fn, 'rb') as fh: |
||||
if max_length: |
||||
params = [max_length] |
||||
else: |
||||
params = [] |
||||
data = fh.read(*params) |
||||
return md5sum_data(data) |
||||
|
||||
class PersistentRandomFile(object): |
||||
"""Generate and store information about a persistent file containing |
||||
random data.""" |
||||
|
||||
def __init__(self, u_boot_console, fn, size): |
||||
"""Create or process the persistent file. |
||||
|
||||
If the file does not exist, it is generated. |
||||
|
||||
If the file does exist, its content is hashed for later comparison. |
||||
|
||||
These files are always located in the "persistent data directory" of |
||||
the current test run. |
||||
|
||||
Args: |
||||
u_boot_console: A console connection to U-Boot. |
||||
fn: The filename (without path) to create. |
||||
size: The desired size of the file in bytes. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
""" |
||||
|
||||
self.fn = fn |
||||
|
||||
self.abs_fn = u_boot_console.config.persistent_data_dir + '/' + fn |
||||
|
||||
if os.path.exists(self.abs_fn): |
||||
u_boot_console.log.action('Persistent data file ' + self.abs_fn + |
||||
' already exists') |
||||
self.content_hash = md5sum_file(self.abs_fn) |
||||
else: |
||||
u_boot_console.log.action('Generating ' + self.abs_fn + |
||||
' (random, persistent, %d bytes)' % size) |
||||
data = os.urandom(size) |
||||
with open(self.abs_fn, 'wb') as fh: |
||||
fh.write(data) |
||||
self.content_hash = md5sum_data(data) |
||||
|
||||
def attempt_to_open_file(fn): |
||||
"""Attempt to open a file, without throwing exceptions. |
||||
|
||||
Any errors (exceptions) that occur during the attempt to open the file |
||||
are ignored. This is useful in order to test whether a file (in |
||||
particular, a device node) exists and can be successfully opened, in order |
||||
to poll for e.g. USB enumeration completion. |
||||
|
||||
Args: |
||||
fn: The filename to attempt to open. |
||||
|
||||
Returns: |
||||
An open file handle to the file, or None if the file could not be |
||||
opened. |
||||
""" |
||||
|
||||
try: |
||||
return open(fn, 'rb') |
||||
except: |
||||
return None |
||||
|
||||
def wait_until_open_succeeds(fn): |
||||
"""Poll until a file can be opened, or a timeout occurs. |
||||
|
||||
Continually attempt to open a file, and return when this succeeds, or |
||||
raise an exception after a timeout. |
||||
|
||||
Args: |
||||
fn: The filename to attempt to open. |
||||
|
||||
Returns: |
||||
An open file handle to the file. |
||||
""" |
||||
|
||||
for i in xrange(100): |
||||
fh = attempt_to_open_file(fn) |
||||
if fh: |
||||
return fh |
||||
time.sleep(0.1) |
||||
raise Exception('File could not be opened') |
||||
|
||||
def wait_until_file_open_fails(fn, ignore_errors): |
||||
"""Poll until a file cannot be opened, or a timeout occurs. |
||||
|
||||
Continually attempt to open a file, and return when this fails, or |
||||
raise an exception after a timeout. |
||||
|
||||
Args: |
||||
fn: The filename to attempt to open. |
||||
ignore_errors: Indicate whether to ignore timeout errors. If True, the |
||||
function will simply return if a timeout occurs, otherwise an |
||||
exception will be raised. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
""" |
||||
|
||||
for i in xrange(100): |
||||
fh = attempt_to_open_file(fn) |
||||
if not fh: |
||||
return |
||||
fh.close() |
||||
time.sleep(0.1) |
||||
if ignore_errors: |
||||
return |
||||
raise Exception('File can still be opened') |
||||
|
||||
def run_and_log(u_boot_console, cmd, ignore_errors=False): |
||||
"""Run a command and log its output. |
||||
|
||||
Args: |
||||
u_boot_console: A console connection to U-Boot. |
||||
cmd: The command to run, as an array of argv[]. |
||||
ignore_errors: Indicate whether to ignore errors. If True, the function |
||||
will simply return if the command cannot be executed or exits with |
||||
an error code, otherwise an exception will be raised if such |
||||
problems occur. |
||||
|
||||
Returns: |
||||
Nothing. |
||||
""" |
||||
|
||||
runner = u_boot_console.log.get_runner(cmd[0], sys.stdout) |
||||
runner.run(cmd, ignore_errors=ignore_errors) |
||||
runner.close() |
||||
|
||||
ram_base = None |
||||
def find_ram_base(u_boot_console): |
||||
"""Find the running U-Boot's RAM location. |
||||
|
||||
Probe the running U-Boot to determine the address of the first bank |
||||
of RAM. This is useful for tests that test reading/writing RAM, or |
||||
load/save files that aren't associated with some standard address |
||||
typically represented in an environment variable such as |
||||
${kernel_addr_r}. The value is cached so that it only needs to be |
||||
actively read once. |
||||
|
||||
Args: |
||||
u_boot_console: A console connection to U-Boot. |
||||
|
||||
Returns: |
||||
The address of U-Boot's first RAM bank, as an integer. |
||||
""" |
||||
|
||||
global ram_base |
||||
if u_boot_console.config.buildconfig.get('config_cmd_bdi', 'n') != 'y': |
||||
pytest.skip('bdinfo command not supported') |
||||
if ram_base == -1: |
||||
pytest.skip('Previously failed to find RAM bank start') |
||||
if ram_base is not None: |
||||
return ram_base |
||||
|
||||
with u_boot_console.log.section('find_ram_base'): |
||||
response = u_boot_console.run_command('bdinfo') |
||||
for l in response.split('\n'): |
||||
if '-> start' in l: |
||||
ram_base = int(l.split('=')[1].strip(), 16) |
||||
break |
||||
if ram_base is None: |
||||
ram_base = -1 |
||||
raise Exception('Failed to find RAM bank start in `bdinfo`') |
||||
|
||||
return ram_base |
@ -1,30 +0,0 @@ |
||||
UMS test script. |
||||
|
||||
ums_gadget_test.sh |
||||
================== |
||||
|
||||
Example usage: |
||||
1. On the target: |
||||
create UMS exportable partitions (with e.g. gpt write), or specify a |
||||
partition number (PART_NUM) as "-" to use the entire device |
||||
ums 0 mmc 0 |
||||
2. On the host: |
||||
sudo test/ums/ums_gadget_test.sh VID PID PART_NUM [-f FILE_SYSTEM] [test_file] |
||||
e.g. sudo test/ums/ums_gadget_test.sh 0525 a4a5 6 -f vfat ./dat_14M.img |
||||
|
||||
... where: |
||||
VID - UMS device USB Vendor ID |
||||
PID - UMS device USB Product ID |
||||
PART_NUM - is the partition number on which UMS operates or "-" to use the |
||||
whole device |
||||
|
||||
Information about available partitions on the target one can read with using |
||||
the 'mmc part' or 'part list' commands. |
||||
|
||||
The partition num (PART_NUM) can be specified as '-' for using the whole device. |
||||
|
||||
The [-f FILE_SYSTEM] optional switch allows for formatting target partition to |
||||
FILE_SYSTEM. |
||||
|
||||
The last, optional [test_file] parameter is for specifying the exact test file |
||||
to use. |
@ -1,183 +0,0 @@ |
||||
#! /bin/bash |
||||
|
||||
# Copyright (C) 2014 Samsung Electronics |
||||
# Lukasz Majewski <l.majewski@samsung.com> |
||||
# |
||||
# UMS operation test script |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
|
||||
clear |
||||
|
||||
COLOUR_RED="\33[31m" |
||||
COLOUR_GREEN="\33[32m" |
||||
COLOUR_ORANGE="\33[33m" |
||||
COLOUR_DEFAULT="\33[0m" |
||||
|
||||
DIR=./ |
||||
SUFFIX=img |
||||
RCV_DIR=rcv/ |
||||
LOG_FILE=./log/log-`date +%d-%m-%Y_%H-%M-%S` |
||||
|
||||
cd `dirname $0` |
||||
../dfu/dfu_gadget_test_init.sh 33M 97M |
||||
|
||||
cleanup () { |
||||
rm -rf $RCV_DIR $MNT_DIR |
||||
} |
||||
|
||||
control_c() |
||||
# run if user hits control-c |
||||
{ |
||||
echo -en "\n*** CTRL+C ***\n" |
||||
umount $MNT_DIR |
||||
cleanup |
||||
exit 0 |
||||
} |
||||
|
||||
# trap keyboard interrupt (control-c) |
||||
trap control_c SIGINT |
||||
|
||||
die () { |
||||
printf " $COLOUR_RED FAILED $COLOUR_DEFAULT \n" |
||||
cleanup |
||||
exit 1 |
||||
} |
||||
|
||||
calculate_md5sum () { |
||||
MD5SUM=`md5sum $1` |
||||
MD5SUM=`echo $MD5SUM | cut -d ' ' -f1` |
||||
echo "md5sum:"$MD5SUM |
||||
} |
||||
|
||||
ums_test_file () { |
||||
printf "$COLOUR_GREEN========================================================================================= $COLOUR_DEFAULT\n" |
||||
printf "File:$COLOUR_GREEN %s $COLOUR_DEFAULT\n" $1 |
||||
|
||||
mount /dev/$MEM_DEV $MNT_DIR |
||||
if [ -f $MNT_DIR/dat_* ]; then |
||||
rm $MNT_DIR/dat_* |
||||
fi |
||||
|
||||
cp ./$1 $MNT_DIR |
||||
|
||||
while true; do |
||||
umount $MNT_DIR > /dev/null 2>&1 |
||||
if [ $? -eq 0 ]; then |
||||
break |
||||
fi |
||||
printf "$COLOUR_ORANGE\tSleeping to wait for umount...$COLOUR_DEFAULT\n" |
||||
sleep 1 |
||||
done |
||||
|
||||
echo -n "TX: " |
||||
calculate_md5sum $1 |
||||
|
||||
MD5_TX=$MD5SUM |
||||
sleep 1 |
||||
N_FILE=$DIR$RCV_DIR${1:2}"_rcv" |
||||
|
||||
mount /dev/$MEM_DEV $MNT_DIR |
||||
cp $MNT_DIR/$1 $N_FILE || die $? |
||||
rm $MNT_DIR/$1 |
||||
umount $MNT_DIR |
||||
|
||||
echo -n "RX: " |
||||
calculate_md5sum $N_FILE |
||||
MD5_RX=$MD5SUM |
||||
|
||||
if [ "$MD5_TX" == "$MD5_RX" ]; then |
||||
printf " $COLOUR_GREEN -------> OK $COLOUR_DEFAULT \n" |
||||
else |
||||
printf " $COLOUR_RED -------> FAILED $COLOUR_DEFAULT \n" |
||||
cleanup |
||||
exit 1 |
||||
fi |
||||
} |
||||
|
||||
printf "$COLOUR_GREEN========================================================================================= $COLOUR_DEFAULT\n" |
||||
echo "U-boot UMS test program" |
||||
|
||||
if [ $EUID -ne 0 ]; then |
||||
echo "You must be root to do this." 1>&2 |
||||
exit 100 |
||||
fi |
||||
|
||||
if [ $# -lt 3 ]; then |
||||
echo "Wrong number of arguments" |
||||
echo "Example:" |
||||
echo "sudo ./ums_gadget_test.sh VID PID PART_NUM [-f ext4] [test_file]" |
||||
die |
||||
fi |
||||
|
||||
MNT_DIR="/mnt/tmp-ums-test" |
||||
|
||||
VID=$1; shift |
||||
PID=$1; shift |
||||
PART_NUM=$1; shift |
||||
|
||||
if [ "$1" == "-f" ]; then |
||||
shift |
||||
FS_TO_FORMAT=$1; shift |
||||
fi |
||||
|
||||
TEST_FILE=$1 |
||||
|
||||
for f in `find /sys -type f -name idProduct`; do |
||||
d=`dirname ${f}` |
||||
if [ `cat ${d}/idVendor` != "${VID}" ]; then |
||||
continue |
||||
fi |
||||
if [ `cat ${d}/idProduct` != "${PID}" ]; then |
||||
continue |
||||
fi |
||||
USB_DEV=${d} |
||||
break |
||||
done |
||||
|
||||
if [ -z "${USB_DEV}" ]; then |
||||
echo "Connect target" |
||||
echo "e.g. ums 0 mmc 0" |
||||
exit 1 |
||||
fi |
||||
|
||||
MEM_DEV=`find $USB_DEV -type d -name "sd[a-z]" | awk -F/ '{print $(NF)}' -` |
||||
|
||||
mkdir -p $RCV_DIR |
||||
if [ ! -d $MNT_DIR ]; then |
||||
mkdir -p $MNT_DIR |
||||
fi |
||||
|
||||
if [ "$PART_NUM" == "-" ]; then |
||||
PART_NUM="" |
||||
fi |
||||
MEM_DEV=$MEM_DEV$PART_NUM |
||||
|
||||
if [ -n "$FS_TO_FORMAT" ]; then |
||||
echo -n "Formatting partition /dev/$MEM_DEV to $FS_TO_FORMAT" |
||||
mkfs -t $FS_TO_FORMAT /dev/$MEM_DEV > /dev/null 2>&1 |
||||
if [ $? -eq 0 ]; then |
||||
printf " $COLOUR_GREEN DONE $COLOUR_DEFAULT \n" |
||||
else |
||||
die |
||||
fi |
||||
fi |
||||
|
||||
printf "Mount: /dev/$MEM_DEV \n" |
||||
|
||||
if [ -n "$TEST_FILE" ]; then |
||||
if [ ! -e $TEST_FILE ]; then |
||||
echo "No file: $TEST_FILE" |
||||
die |
||||
fi |
||||
ums_test_file $TEST_FILE |
||||
else |
||||
for file in $DIR*.$SUFFIX |
||||
do |
||||
ums_test_file $file |
||||
done |
||||
fi |
||||
|
||||
cleanup |
||||
|
||||
exit 0 |
Loading…
Reference in new issue