Merge git://git.denx.de/u-boot-dm
commit
8a36287a01
11
README
11
README
|
@ -1423,6 +1423,17 @@ The following options need to be configured:
|
||||||
CONFIG_TPM_TIS_I2C_BURST_LIMITATION
|
CONFIG_TPM_TIS_I2C_BURST_LIMITATION
|
||||||
Define the burst count bytes upper limit
|
Define the burst count bytes upper limit
|
||||||
|
|
||||||
|
CONFIG_TPM_ST33ZP24
|
||||||
|
Support for STMicroelectronics TPM devices. Requires DM_TPM support.
|
||||||
|
|
||||||
|
CONFIG_TPM_ST33ZP24_I2C
|
||||||
|
Support for STMicroelectronics ST33ZP24 I2C devices.
|
||||||
|
Requires TPM_ST33ZP24 and I2C.
|
||||||
|
|
||||||
|
CONFIG_TPM_ST33ZP24_SPI
|
||||||
|
Support for STMicroelectronics ST33ZP24 SPI devices.
|
||||||
|
Requires TPM_ST33ZP24 and SPI.
|
||||||
|
|
||||||
CONFIG_TPM_ATMEL_TWI
|
CONFIG_TPM_ATMEL_TWI
|
||||||
Support for Atmel TWI TPM device. Requires I2C support.
|
Support for Atmel TWI TPM device. Requires I2C support.
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,19 @@ config TEGRA210
|
||||||
|
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
|
config TEGRA_DISCONNECT_UDC_ON_BOOT
|
||||||
|
bool "Disconnect USB device mode controller on boot"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
When loading U-Boot into RAM over USB protocols using tools such as
|
||||||
|
tegrarcm or L4T's exec-uboot.sh/tegraflash.py, Tegra's USB device
|
||||||
|
mode controller is initialized and enumerated by the host PC running
|
||||||
|
the tool. Unfortunately, these tools do not shut down the USB
|
||||||
|
controller before executing the downloaded code, and so the host PC
|
||||||
|
does not "de-enumerate" the USB device. This option shuts down the
|
||||||
|
USB controller when U-Boot boots to avoid leaving a stale USB device
|
||||||
|
present.
|
||||||
|
|
||||||
config SYS_MALLOC_F_LEN
|
config SYS_MALLOC_F_LEN
|
||||||
default 0x1800
|
default 0x1800
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,8 @@
|
||||||
#ifdef CONFIG_TEGRA_CLOCK_SCALING
|
#ifdef CONFIG_TEGRA_CLOCK_SCALING
|
||||||
#include <asm/arch/emc.h>
|
#include <asm/arch/emc.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_USB_EHCI_TEGRA
|
|
||||||
#include <asm/arch-tegra/usb.h>
|
#include <asm/arch-tegra/usb.h>
|
||||||
|
#ifdef CONFIG_USB_EHCI_TEGRA
|
||||||
#include <usb.h>
|
#include <usb.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_TEGRA_MMC
|
#ifdef CONFIG_TEGRA_MMC
|
||||||
|
@ -201,6 +201,14 @@ void gpio_early_init(void) __attribute__((weak, alias("__gpio_early_init")));
|
||||||
|
|
||||||
int board_early_init_f(void)
|
int board_early_init_f(void)
|
||||||
{
|
{
|
||||||
|
#if defined(CONFIG_TEGRA_DISCONNECT_UDC_ON_BOOT)
|
||||||
|
#define USBCMD_FS2 (1 << 15)
|
||||||
|
{
|
||||||
|
struct usb_ctlr *usbctlr = (struct usb_ctlr *)0x7d000000;
|
||||||
|
writel(USBCMD_FS2, &usbctlr->usb_cmd);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Do any special system timer/TSC setup */
|
/* Do any special system timer/TSC setup */
|
||||||
#if defined(CONFIG_TEGRA_SUPPORT_NON_SECURE)
|
#if defined(CONFIG_TEGRA_SUPPORT_NON_SECURE)
|
||||||
if (!tegra_cpu_is_non_secure())
|
if (!tegra_cpu_is_non_secure())
|
||||||
|
|
18
cmd/pci.c
18
cmd/pci.c
|
@ -578,9 +578,10 @@ static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||||
if ((bdf = get_pci_dev(argv[2])) == -1)
|
if ((bdf = get_pci_dev(argv[2])) == -1)
|
||||||
return 1;
|
return 1;
|
||||||
break;
|
break;
|
||||||
#ifdef CONFIG_CMD_PCI_ENUM
|
#if defined(CONFIG_CMD_PCI_ENUM) || defined(CONFIG_DM_PCI)
|
||||||
case 'e':
|
case 'e':
|
||||||
break;
|
pci_init();
|
||||||
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
default: /* scan bus */
|
default: /* scan bus */
|
||||||
value = 1; /* short listing */
|
value = 1; /* short listing */
|
||||||
|
@ -621,15 +622,6 @@ static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||||
break;
|
break;
|
||||||
case 'd': /* display */
|
case 'd': /* display */
|
||||||
return pci_cfg_display(dev, addr, size, value);
|
return pci_cfg_display(dev, addr, size, value);
|
||||||
#ifdef CONFIG_CMD_PCI_ENUM
|
|
||||||
case 'e':
|
|
||||||
# ifdef CONFIG_DM_PCI
|
|
||||||
printf("This command is not yet supported with driver model\n");
|
|
||||||
# else
|
|
||||||
pci_init();
|
|
||||||
# endif
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case 'n': /* next */
|
case 'n': /* next */
|
||||||
if (argc < 4)
|
if (argc < 4)
|
||||||
goto usage;
|
goto usage;
|
||||||
|
@ -665,9 +657,9 @@ static int do_pci(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||||
static char pci_help_text[] =
|
static char pci_help_text[] =
|
||||||
"[bus] [long]\n"
|
"[bus] [long]\n"
|
||||||
" - short or long list of PCI devices on bus 'bus'\n"
|
" - short or long list of PCI devices on bus 'bus'\n"
|
||||||
#ifdef CONFIG_CMD_PCI_ENUM
|
#if defined(CONFIG_CMD_PCI_ENUM) || defined(CONFIG_DM_PCI)
|
||||||
"pci enum\n"
|
"pci enum\n"
|
||||||
" - re-enumerate PCI buses\n"
|
" - Enumerate PCI buses\n"
|
||||||
#endif
|
#endif
|
||||||
"pci header b.d.f\n"
|
"pci header b.d.f\n"
|
||||||
" - show header of PCI device 'bus.device.function'\n"
|
" - show header of PCI device 'bus.device.function'\n"
|
||||||
|
|
|
@ -448,7 +448,7 @@ static int get_tpm(struct udevice **devp)
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = uclass_first_device(UCLASS_TPM, devp);
|
rc = uclass_first_device(UCLASS_TPM, devp);
|
||||||
if (rc) {
|
if (rc || !*devp) {
|
||||||
printf("Could not find TPM (ret=%d)\n", rc);
|
printf("Could not find TPM (ret=%d)\n", rc);
|
||||||
return CMD_RET_FAILURE;
|
return CMD_RET_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -341,6 +341,21 @@ scan_dev_for_scripts:
|
||||||
If you want to disable boot.scr on all disks, set the value to something
|
If you want to disable boot.scr on all disks, set the value to something
|
||||||
innocuous, e.g. setenv scan_dev_for_scripts true.
|
innocuous, e.g. setenv scan_dev_for_scripts true.
|
||||||
|
|
||||||
|
boot_net_usb_start:
|
||||||
|
|
||||||
|
If you want to prevent USB enumeration by distro boot commands which execute
|
||||||
|
network operations, set the value to something innocuous, e.g. setenv
|
||||||
|
boot_net_usb_start true. This would be useful if you know your Ethernet
|
||||||
|
device is not attached to USB, and you wish to increase boot speed by
|
||||||
|
avoiding unnecessary actions.
|
||||||
|
|
||||||
|
boot_net_pci_enum:
|
||||||
|
|
||||||
|
If you want to prevent PCI enumeration by distro boot commands which execute
|
||||||
|
network operations, set the value to something innocuous, e.g. setenv
|
||||||
|
boot_net_pci_enum true. This would be useful if you know your Ethernet
|
||||||
|
device is not attached to PCI, and you wish to increase boot speed by
|
||||||
|
avoiding unnecessary actions.
|
||||||
|
|
||||||
Interactively booting from a specific device at the u-boot prompt
|
Interactively booting from a specific device at the u-boot prompt
|
||||||
=================================================================
|
=================================================================
|
||||||
|
|
|
@ -223,7 +223,7 @@ static void *alloc_priv(int size, uint flags)
|
||||||
return priv;
|
return priv;
|
||||||
}
|
}
|
||||||
|
|
||||||
int device_probe_child(struct udevice *dev, void *parent_priv)
|
int device_probe(struct udevice *dev)
|
||||||
{
|
{
|
||||||
const struct driver *drv;
|
const struct driver *drv;
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
@ -270,8 +270,6 @@ int device_probe_child(struct udevice *dev, void *parent_priv)
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (parent_priv)
|
|
||||||
memcpy(dev->parent_priv, parent_priv, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = device_probe(dev->parent);
|
ret = device_probe(dev->parent);
|
||||||
|
@ -349,11 +347,6 @@ fail:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int device_probe(struct udevice *dev)
|
|
||||||
{
|
|
||||||
return device_probe_child(dev, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *dev_get_platdata(struct udevice *dev)
|
void *dev_get_platdata(struct udevice *dev)
|
||||||
{
|
{
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
|
|
|
@ -1241,3 +1241,18 @@ U_BOOT_DRIVER(pci_generic_drv) = {
|
||||||
.id = UCLASS_PCI_GENERIC,
|
.id = UCLASS_PCI_GENERIC,
|
||||||
.of_match = pci_generic_ids,
|
.of_match = pci_generic_ids,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void pci_init(void)
|
||||||
|
{
|
||||||
|
struct udevice *bus;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enumerate all known controller devices. Enumeration has the side-
|
||||||
|
* effect of probing them, so PCIe devices will be enumerated too.
|
||||||
|
*/
|
||||||
|
for (uclass_first_device(UCLASS_PCI, &bus);
|
||||||
|
bus;
|
||||||
|
uclass_next_device(&bus)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ config TPM_TIS_LPC
|
||||||
bool "Enable support for Infineon SLB9635/45 TPMs on LPC"
|
bool "Enable support for Infineon SLB9635/45 TPMs on LPC"
|
||||||
depends on TPM && X86
|
depends on TPM && X86
|
||||||
help
|
help
|
||||||
This driver supports Infineon TPM devices connected on the I2C bus.
|
This driver supports Infineon TPM devices connected on the LPC bus.
|
||||||
The usual tpm operations and the 'tpm' command can be used to talk
|
The usual tpm operations and the 'tpm' command can be used to talk
|
||||||
to the device using the standard TPM Interface Specification (TIS)
|
to the device using the standard TPM Interface Specification (TIS)
|
||||||
protocol
|
protocol
|
||||||
|
@ -64,4 +64,22 @@ config TPM_AUTH_SESSIONS
|
||||||
TPM_LoadKey2 and TPM_GetPubKey are provided. Both features are
|
TPM_LoadKey2 and TPM_GetPubKey are provided. Both features are
|
||||||
available using the 'tpm' command, too.
|
available using the 'tpm' command, too.
|
||||||
|
|
||||||
|
config TPM_ST33ZP24_I2C
|
||||||
|
bool "STMicroelectronics ST33ZP24 I2C TPM"
|
||||||
|
depends on TPM && DM_I2C
|
||||||
|
---help---
|
||||||
|
This driver supports STMicroelectronics TPM devices connected on the I2C bus.
|
||||||
|
The usual tpm operations and the 'tpm' command can be used to talk
|
||||||
|
to the device using the standard TPM Interface Specification (TIS)
|
||||||
|
protocol
|
||||||
|
|
||||||
|
config TPM_ST33ZP24_SPI
|
||||||
|
bool "STMicroelectronics ST33ZP24 SPI TPM"
|
||||||
|
depends on TPM && DM_SPI
|
||||||
|
---help---
|
||||||
|
This driver supports STMicroelectronics TPM devices connected on the SPI bus.
|
||||||
|
The usual tpm operations and the 'tpm' command can be used to talk
|
||||||
|
to the device using the standard TPM Interface Specification (TIS)
|
||||||
|
protocol
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -9,3 +9,5 @@ obj-$(CONFIG_TPM_ATMEL_TWI) += tpm_atmel_twi.o
|
||||||
obj-$(CONFIG_TPM_TIS_INFINEON) += tpm_tis_infineon.o
|
obj-$(CONFIG_TPM_TIS_INFINEON) += tpm_tis_infineon.o
|
||||||
obj-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o
|
obj-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o
|
||||||
obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o
|
obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o
|
||||||
|
obj-$(CONFIG_TPM_ST33ZP24_I2C) += tpm_tis_st33zp24_i2c.o
|
||||||
|
obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o
|
||||||
|
|
|
@ -37,18 +37,12 @@ enum tpm_timeout {
|
||||||
#define TPM_RSP_SIZE_BYTE 2
|
#define TPM_RSP_SIZE_BYTE 2
|
||||||
#define TPM_RSP_RC_BYTE 6
|
#define TPM_RSP_RC_BYTE 6
|
||||||
|
|
||||||
enum i2c_chip_type {
|
|
||||||
SLB9635,
|
|
||||||
SLB9645,
|
|
||||||
UNKNOWN,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tpm_chip {
|
struct tpm_chip {
|
||||||
int is_open;
|
int is_open;
|
||||||
int locality;
|
int locality;
|
||||||
u32 vend_dev;
|
u32 vend_dev;
|
||||||
unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */
|
unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */
|
||||||
enum i2c_chip_type chip_type;
|
ulong chip_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tpm_input_header {
|
struct tpm_input_header {
|
||||||
|
@ -134,13 +128,4 @@ enum tis_status {
|
||||||
TPM_STS_DATA_EXPECT = 0x08,
|
TPM_STS_DATA_EXPECT = 0x08,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* expected value for DIDVID register */
|
|
||||||
#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L
|
|
||||||
#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
|
|
||||||
|
|
||||||
#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
|
|
||||||
#define TPM_STS(l) (0x0001 | ((l) << 4))
|
|
||||||
#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
|
|
||||||
#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
|
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -30,17 +30,32 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/unaligned/be_byteshift.h>
|
#include <linux/unaligned/be_byteshift.h>
|
||||||
|
|
||||||
#include "tpm_tis_infineon.h"
|
#include "tpm_tis.h"
|
||||||
#include "tpm_internal.h"
|
#include "tpm_internal.h"
|
||||||
|
|
||||||
DECLARE_GLOBAL_DATA_PTR;
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
|
enum i2c_chip_type {
|
||||||
|
SLB9635,
|
||||||
|
SLB9645,
|
||||||
|
UNKNOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* expected value for DIDVID register */
|
||||||
|
#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L
|
||||||
|
#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
|
||||||
|
|
||||||
static const char * const chip_name[] = {
|
static const char * const chip_name[] = {
|
||||||
[SLB9635] = "slb9635tt",
|
[SLB9635] = "slb9635tt",
|
||||||
[SLB9645] = "slb9645tt",
|
[SLB9645] = "slb9645tt",
|
||||||
[UNKNOWN] = "unknown/fallback to slb9635",
|
[UNKNOWN] = "unknown/fallback to slb9635",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
|
||||||
|
#define TPM_STS(l) (0x0001 | ((l) << 4))
|
||||||
|
#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
|
||||||
|
#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tpm_tis_i2c_read() - read from TPM register
|
* tpm_tis_i2c_read() - read from TPM register
|
||||||
* @addr: register address to read from
|
* @addr: register address to read from
|
||||||
|
|
|
@ -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),
|
||||||
|
};
|
|
@ -139,16 +139,26 @@
|
||||||
BOOT_TARGET_DEVICES_references_IDE_without_CONFIG_CMD_IDE
|
BOOT_TARGET_DEVICES_references_IDE_without_CONFIG_CMD_IDE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_CMD_PCI_ENUM) || defined(CONFIG_DM_PCI)
|
||||||
|
#define BOOTENV_RUN_NET_PCI_ENUM "run boot_net_pci_enum; "
|
||||||
|
#define BOOTENV_SHARED_PCI \
|
||||||
|
"boot_net_pci_enum=pci enum\0"
|
||||||
|
#else
|
||||||
|
#define BOOTENV_RUN_NET_PCI_ENUM
|
||||||
|
#define BOOTENV_SHARED_PCI
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_CMD_USB
|
#ifdef CONFIG_CMD_USB
|
||||||
#define BOOTENV_RUN_USB_INIT "usb start; "
|
#define BOOTENV_RUN_NET_USB_START "run boot_net_usb_start; "
|
||||||
#define BOOTENV_SHARED_USB \
|
#define BOOTENV_SHARED_USB \
|
||||||
|
"boot_net_usb_start=usb start\0" \
|
||||||
"usb_boot=" \
|
"usb_boot=" \
|
||||||
BOOTENV_RUN_USB_INIT \
|
"usb start; " \
|
||||||
BOOTENV_SHARED_BLKDEV_BODY(usb)
|
BOOTENV_SHARED_BLKDEV_BODY(usb)
|
||||||
#define BOOTENV_DEV_USB BOOTENV_DEV_BLKDEV
|
#define BOOTENV_DEV_USB BOOTENV_DEV_BLKDEV
|
||||||
#define BOOTENV_DEV_NAME_USB BOOTENV_DEV_NAME_BLKDEV
|
#define BOOTENV_DEV_NAME_USB BOOTENV_DEV_NAME_BLKDEV
|
||||||
#else
|
#else
|
||||||
#define BOOTENV_RUN_USB_INIT
|
#define BOOTENV_RUN_NET_USB_START
|
||||||
#define BOOTENV_SHARED_USB
|
#define BOOTENV_SHARED_USB
|
||||||
#define BOOTENV_DEV_USB \
|
#define BOOTENV_DEV_USB \
|
||||||
BOOT_TARGET_DEVICES_references_USB_without_CONFIG_CMD_USB
|
BOOT_TARGET_DEVICES_references_USB_without_CONFIG_CMD_USB
|
||||||
|
@ -159,7 +169,8 @@
|
||||||
#if defined(CONFIG_CMD_DHCP)
|
#if defined(CONFIG_CMD_DHCP)
|
||||||
#define BOOTENV_DEV_DHCP(devtypeu, devtypel, instance) \
|
#define BOOTENV_DEV_DHCP(devtypeu, devtypel, instance) \
|
||||||
"bootcmd_dhcp=" \
|
"bootcmd_dhcp=" \
|
||||||
BOOTENV_RUN_USB_INIT \
|
BOOTENV_RUN_NET_USB_START \
|
||||||
|
BOOTENV_RUN_NET_PCI_ENUM \
|
||||||
"if dhcp ${scriptaddr} ${boot_script_dhcp}; then " \
|
"if dhcp ${scriptaddr} ${boot_script_dhcp}; then " \
|
||||||
"source ${scriptaddr}; " \
|
"source ${scriptaddr}; " \
|
||||||
"fi\0"
|
"fi\0"
|
||||||
|
@ -175,7 +186,8 @@
|
||||||
#if defined(CONFIG_CMD_DHCP) && defined(CONFIG_CMD_PXE)
|
#if defined(CONFIG_CMD_DHCP) && defined(CONFIG_CMD_PXE)
|
||||||
#define BOOTENV_DEV_PXE(devtypeu, devtypel, instance) \
|
#define BOOTENV_DEV_PXE(devtypeu, devtypel, instance) \
|
||||||
"bootcmd_pxe=" \
|
"bootcmd_pxe=" \
|
||||||
BOOTENV_RUN_USB_INIT \
|
BOOTENV_RUN_NET_USB_START \
|
||||||
|
BOOTENV_RUN_NET_PCI_ENUM \
|
||||||
"dhcp; " \
|
"dhcp; " \
|
||||||
"if pxe get; then " \
|
"if pxe get; then " \
|
||||||
"pxe boot; " \
|
"pxe boot; " \
|
||||||
|
@ -199,6 +211,7 @@
|
||||||
#define BOOTENV \
|
#define BOOTENV \
|
||||||
BOOTENV_SHARED_HOST \
|
BOOTENV_SHARED_HOST \
|
||||||
BOOTENV_SHARED_MMC \
|
BOOTENV_SHARED_MMC \
|
||||||
|
BOOTENV_SHARED_PCI \
|
||||||
BOOTENV_SHARED_USB \
|
BOOTENV_SHARED_USB \
|
||||||
BOOTENV_SHARED_SATA \
|
BOOTENV_SHARED_SATA \
|
||||||
BOOTENV_SHARED_SCSI \
|
BOOTENV_SHARED_SCSI \
|
||||||
|
|
|
@ -65,19 +65,6 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
|
||||||
*/
|
*/
|
||||||
int device_probe(struct udevice *dev);
|
int device_probe(struct udevice *dev);
|
||||||
|
|
||||||
/**
|
|
||||||
* device_probe() - Probe a child device, activating it
|
|
||||||
*
|
|
||||||
* Activate a device so that it is ready for use. All its parents are probed
|
|
||||||
* first. The child is provided with parent data if parent_priv is not NULL.
|
|
||||||
*
|
|
||||||
* @dev: Pointer to device to probe
|
|
||||||
* @parent_priv: Pointer to parent data. If non-NULL then this is provided to
|
|
||||||
* the child.
|
|
||||||
* @return 0 if OK, -ve on error
|
|
||||||
*/
|
|
||||||
int device_probe_child(struct udevice *dev, void *parent_priv);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* device_remove() - Remove a device, de-activating it
|
* device_remove() - Remove a device, de-activating it
|
||||||
*
|
*
|
||||||
|
|
|
@ -262,7 +262,7 @@ int tpm_init(void)
|
||||||
struct udevice *dev;
|
struct udevice *dev;
|
||||||
|
|
||||||
err = uclass_first_device(UCLASS_TPM, &dev);
|
err = uclass_first_device(UCLASS_TPM, &dev);
|
||||||
if (err)
|
if (err || !dev)
|
||||||
return err;
|
return err;
|
||||||
return tpm_open(dev);
|
return tpm_open(dev);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
|
@ -81,6 +81,8 @@ static int dm_test_main(const char *test_name)
|
||||||
struct unit_test *test;
|
struct unit_test *test;
|
||||||
int run_count;
|
int run_count;
|
||||||
|
|
||||||
|
uts->fail_count = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we have no device tree, or it only has a root node, then these
|
* If we have no device tree, or it only has a root node, then these
|
||||||
* tests clearly aren't going to work...
|
* tests clearly aren't going to work...
|
||||||
|
|
|
@ -29,7 +29,7 @@ log = None
|
||||||
console = None
|
console = None
|
||||||
|
|
||||||
def mkdir_p(path):
|
def mkdir_p(path):
|
||||||
'''Create a directory path.
|
"""Create a directory path.
|
||||||
|
|
||||||
This includes creating any intermediate/parent directories. Any errors
|
This includes creating any intermediate/parent directories. Any errors
|
||||||
caused due to already extant directories are ignored.
|
caused due to already extant directories are ignored.
|
||||||
|
@ -39,7 +39,7 @@ def mkdir_p(path):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
|
@ -50,14 +50,14 @@ def mkdir_p(path):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
'''pytest hook: Add custom command-line options to the cmdline parser.
|
"""pytest hook: Add custom command-line options to the cmdline parser.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
parser: The pytest command-line parser.
|
parser: The pytest command-line parser.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
parser.addoption('--build-dir', default=None,
|
parser.addoption('--build-dir', default=None,
|
||||||
help='U-Boot build directory (O=)')
|
help='U-Boot build directory (O=)')
|
||||||
|
@ -73,14 +73,14 @@ def pytest_addoption(parser):
|
||||||
help='Compile U-Boot before running tests')
|
help='Compile U-Boot before running tests')
|
||||||
|
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
'''pytest hook: Perform custom initialization at startup time.
|
"""pytest hook: Perform custom initialization at startup time.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config: The pytest configuration.
|
config: The pytest configuration.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
global log
|
global log
|
||||||
global console
|
global console
|
||||||
|
@ -190,7 +190,7 @@ def pytest_configure(config):
|
||||||
console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig)
|
console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig)
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
'''pytest hook: parameterize test functions based on custom rules.
|
"""pytest hook: parameterize test functions based on custom rules.
|
||||||
|
|
||||||
If a test function takes parameter(s) (fixture names) of the form brd__xxx
|
If a test function takes parameter(s) (fixture names) of the form brd__xxx
|
||||||
or env__xxx, the brd and env configuration dictionaries are consulted to
|
or env__xxx, the brd and env configuration dictionaries are consulted to
|
||||||
|
@ -202,7 +202,7 @@ def pytest_generate_tests(metafunc):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
subconfigs = {
|
subconfigs = {
|
||||||
'brd': console.config.brd,
|
'brd': console.config.brd,
|
||||||
|
@ -225,28 +225,37 @@ def pytest_generate_tests(metafunc):
|
||||||
# ... otherwise, see if there's a key that contains a list of
|
# ... otherwise, see if there's a key that contains a list of
|
||||||
# values to use instead.
|
# values to use instead.
|
||||||
vals = subconfig.get(fn + 's', [])
|
vals = subconfig.get(fn + 's', [])
|
||||||
metafunc.parametrize(fn, vals)
|
def fixture_id(index, val):
|
||||||
|
try:
|
||||||
|
return val["fixture_id"]
|
||||||
|
except:
|
||||||
|
return fn + str(index)
|
||||||
|
ids = [fixture_id(index, val) for (index, val) in enumerate(vals)]
|
||||||
|
metafunc.parametrize(fn, vals, ids=ids)
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='function')
|
||||||
def u_boot_console(request):
|
def u_boot_console(request):
|
||||||
'''Generate the value of a test's u_boot_console fixture.
|
"""Generate the value of a test's u_boot_console fixture.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request: The pytest request.
|
request: The pytest request.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The fixture value.
|
The fixture value.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
console.ensure_spawned()
|
||||||
return console
|
return console
|
||||||
|
|
||||||
tests_not_run = set()
|
tests_not_run = set()
|
||||||
tests_failed = set()
|
tests_failed = set()
|
||||||
|
tests_xpassed = set()
|
||||||
|
tests_xfailed = set()
|
||||||
tests_skipped = set()
|
tests_skipped = set()
|
||||||
tests_passed = set()
|
tests_passed = set()
|
||||||
|
|
||||||
def pytest_itemcollected(item):
|
def pytest_itemcollected(item):
|
||||||
'''pytest hook: Called once for each test found during collection.
|
"""pytest hook: Called once for each test found during collection.
|
||||||
|
|
||||||
This enables our custom result analysis code to see the list of all tests
|
This enables our custom result analysis code to see the list of all tests
|
||||||
that should eventually be run.
|
that should eventually be run.
|
||||||
|
@ -256,12 +265,12 @@ def pytest_itemcollected(item):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
tests_not_run.add(item.name)
|
tests_not_run.add(item.name)
|
||||||
|
|
||||||
def cleanup():
|
def cleanup():
|
||||||
'''Clean up all global state.
|
"""Clean up all global state.
|
||||||
|
|
||||||
Executed (via atexit) once the entire test process is complete. This
|
Executed (via atexit) once the entire test process is complete. This
|
||||||
includes logging the status of all tests, and the identity of any failed
|
includes logging the status of all tests, and the identity of any failed
|
||||||
|
@ -272,7 +281,7 @@ def cleanup():
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if console:
|
if console:
|
||||||
console.close()
|
console.close()
|
||||||
|
@ -282,6 +291,14 @@ def cleanup():
|
||||||
log.status_skipped('%d skipped' % len(tests_skipped))
|
log.status_skipped('%d skipped' % len(tests_skipped))
|
||||||
for test in tests_skipped:
|
for test in tests_skipped:
|
||||||
log.status_skipped('... ' + test)
|
log.status_skipped('... ' + test)
|
||||||
|
if tests_xpassed:
|
||||||
|
log.status_xpass('%d xpass' % len(tests_xpassed))
|
||||||
|
for test in tests_xpassed:
|
||||||
|
log.status_xpass('... ' + test)
|
||||||
|
if tests_xfailed:
|
||||||
|
log.status_xfail('%d xfail' % len(tests_xfailed))
|
||||||
|
for test in tests_xfailed:
|
||||||
|
log.status_xfail('... ' + test)
|
||||||
if tests_failed:
|
if tests_failed:
|
||||||
log.status_fail('%d failed' % len(tests_failed))
|
log.status_fail('%d failed' % len(tests_failed))
|
||||||
for test in tests_failed:
|
for test in tests_failed:
|
||||||
|
@ -294,7 +311,7 @@ def cleanup():
|
||||||
atexit.register(cleanup)
|
atexit.register(cleanup)
|
||||||
|
|
||||||
def setup_boardspec(item):
|
def setup_boardspec(item):
|
||||||
'''Process any 'boardspec' marker for a test.
|
"""Process any 'boardspec' marker for a test.
|
||||||
|
|
||||||
Such a marker lists the set of board types that a test does/doesn't
|
Such a marker lists the set of board types that a test does/doesn't
|
||||||
support. If tests are being executed on an unsupported board, the test is
|
support. If tests are being executed on an unsupported board, the test is
|
||||||
|
@ -305,7 +322,7 @@ def setup_boardspec(item):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
mark = item.get_marker('boardspec')
|
mark = item.get_marker('boardspec')
|
||||||
if not mark:
|
if not mark:
|
||||||
|
@ -322,7 +339,7 @@ def setup_boardspec(item):
|
||||||
pytest.skip('board not supported')
|
pytest.skip('board not supported')
|
||||||
|
|
||||||
def setup_buildconfigspec(item):
|
def setup_buildconfigspec(item):
|
||||||
'''Process any 'buildconfigspec' marker for a test.
|
"""Process any 'buildconfigspec' marker for a test.
|
||||||
|
|
||||||
Such a marker lists some U-Boot configuration feature that the test
|
Such a marker lists some U-Boot configuration feature that the test
|
||||||
requires. If tests are being executed on an U-Boot build that doesn't
|
requires. If tests are being executed on an U-Boot build that doesn't
|
||||||
|
@ -333,7 +350,7 @@ def setup_buildconfigspec(item):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
mark = item.get_marker('buildconfigspec')
|
mark = item.get_marker('buildconfigspec')
|
||||||
if not mark:
|
if not mark:
|
||||||
|
@ -343,7 +360,7 @@ def setup_buildconfigspec(item):
|
||||||
pytest.skip('.config feature not enabled')
|
pytest.skip('.config feature not enabled')
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
'''pytest hook: Configure (set up) a test item.
|
"""pytest hook: Configure (set up) a test item.
|
||||||
|
|
||||||
Called once for each test to perform any custom configuration. This hook
|
Called once for each test to perform any custom configuration. This hook
|
||||||
is used to skip the test if certain conditions apply.
|
is used to skip the test if certain conditions apply.
|
||||||
|
@ -353,14 +370,14 @@ def pytest_runtest_setup(item):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
log.start_section(item.name)
|
log.start_section(item.name)
|
||||||
setup_boardspec(item)
|
setup_boardspec(item)
|
||||||
setup_buildconfigspec(item)
|
setup_buildconfigspec(item)
|
||||||
|
|
||||||
def pytest_runtest_protocol(item, nextitem):
|
def pytest_runtest_protocol(item, nextitem):
|
||||||
'''pytest hook: Called to execute a test.
|
"""pytest hook: Called to execute a test.
|
||||||
|
|
||||||
This hook wraps the standard pytest runtestprotocol() function in order
|
This hook wraps the standard pytest runtestprotocol() function in order
|
||||||
to acquire visibility into, and record, each test function's result.
|
to acquire visibility into, and record, each test function's result.
|
||||||
|
@ -371,36 +388,45 @@ def pytest_runtest_protocol(item, nextitem):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A list of pytest reports (test result data).
|
A list of pytest reports (test result data).
|
||||||
'''
|
"""
|
||||||
|
|
||||||
reports = runtestprotocol(item, nextitem=nextitem)
|
reports = runtestprotocol(item, nextitem=nextitem)
|
||||||
failed = None
|
|
||||||
skipped = None
|
failure_cleanup = False
|
||||||
|
test_list = tests_passed
|
||||||
|
msg = 'OK'
|
||||||
|
msg_log = log.status_pass
|
||||||
for report in reports:
|
for report in reports:
|
||||||
if report.outcome == 'failed':
|
if report.outcome == 'failed':
|
||||||
failed = report
|
if hasattr(report, 'wasxfail'):
|
||||||
|
test_list = tests_xpassed
|
||||||
|
msg = 'XPASSED'
|
||||||
|
msg_log = log.status_xpass
|
||||||
|
else:
|
||||||
|
failure_cleanup = True
|
||||||
|
test_list = tests_failed
|
||||||
|
msg = 'FAILED:\n' + str(report.longrepr)
|
||||||
|
msg_log = log.status_fail
|
||||||
break
|
break
|
||||||
if report.outcome == 'skipped':
|
if report.outcome == 'skipped':
|
||||||
if not skipped:
|
if hasattr(report, 'wasxfail'):
|
||||||
skipped = report
|
failure_cleanup = True
|
||||||
|
test_list = tests_xfailed
|
||||||
|
msg = 'XFAILED:\n' + str(report.longrepr)
|
||||||
|
msg_log = log.status_xfail
|
||||||
|
break
|
||||||
|
test_list = tests_skipped
|
||||||
|
msg = 'SKIPPED:\n' + str(report.longrepr)
|
||||||
|
msg_log = log.status_skipped
|
||||||
|
|
||||||
if failed:
|
if failure_cleanup:
|
||||||
tests_failed.add(item.name)
|
console.drain_console()
|
||||||
elif skipped:
|
|
||||||
tests_skipped.add(item.name)
|
test_list.add(item.name)
|
||||||
else:
|
|
||||||
tests_passed.add(item.name)
|
|
||||||
tests_not_run.remove(item.name)
|
tests_not_run.remove(item.name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if failed:
|
msg_log(msg)
|
||||||
msg = 'FAILED:\n' + str(failed.longrepr)
|
|
||||||
log.status_fail(msg)
|
|
||||||
elif skipped:
|
|
||||||
msg = 'SKIPPED:\n' + str(skipped.longrepr)
|
|
||||||
log.status_skipped(msg)
|
|
||||||
else:
|
|
||||||
log.status_pass('OK')
|
|
||||||
except:
|
except:
|
||||||
# If something went wrong with logging, it's better to let the test
|
# If something went wrong with logging, it's better to let the test
|
||||||
# process continue, which may report other exceptions that triggered
|
# process continue, which may report other exceptions that triggered
|
||||||
|
@ -416,7 +442,7 @@ def pytest_runtest_protocol(item, nextitem):
|
||||||
|
|
||||||
log.end_section(item.name)
|
log.end_section(item.name)
|
||||||
|
|
||||||
if failed:
|
if failure_cleanup:
|
||||||
console.cleanup_spawn()
|
console.cleanup_spawn()
|
||||||
|
|
||||||
return reports
|
return reports
|
||||||
|
|
|
@ -83,6 +83,14 @@ pre {
|
||||||
color: #ffff00
|
color: #ffff00
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-xfail {
|
||||||
|
color: #ff7f00
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-xpass {
|
||||||
|
color: #ff7f00
|
||||||
|
}
|
||||||
|
|
||||||
.status-fail {
|
.status-fail {
|
||||||
color: #ff0000
|
color: #ff0000
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,12 @@ import subprocess
|
||||||
mod_dir = os.path.dirname(os.path.abspath(__file__))
|
mod_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
class LogfileStream(object):
|
class LogfileStream(object):
|
||||||
'''A file-like object used to write a single logical stream of data into
|
"""A file-like object used to write a single logical stream of data into
|
||||||
a multiplexed log file. Objects of this type should be created by factory
|
a multiplexed log file. Objects of this type should be created by factory
|
||||||
functions in the Logfile class rather than directly.'''
|
functions in the Logfile class rather than directly."""
|
||||||
|
|
||||||
def __init__(self, logfile, name, chained_file):
|
def __init__(self, logfile, name, chained_file):
|
||||||
'''Initialize a new object.
|
"""Initialize a new object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
logfile: The Logfile object to log to.
|
logfile: The Logfile object to log to.
|
||||||
|
@ -29,26 +29,26 @@ class LogfileStream(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.logfile = logfile
|
self.logfile = logfile
|
||||||
self.name = name
|
self.name = name
|
||||||
self.chained_file = chained_file
|
self.chained_file = chained_file
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
'''Dummy function so that this class is "file-like".
|
"""Dummy function so that this class is "file-like".
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
None.
|
None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def write(self, data, implicit=False):
|
def write(self, data, implicit=False):
|
||||||
'''Write data to the log stream.
|
"""Write data to the log stream.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data: The data to write tot he file.
|
data: The data to write tot he file.
|
||||||
|
@ -60,33 +60,33 @@ class LogfileStream(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.logfile.write(self, data, implicit)
|
self.logfile.write(self, data, implicit)
|
||||||
if self.chained_file:
|
if self.chained_file:
|
||||||
self.chained_file.write(data)
|
self.chained_file.write(data)
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
'''Flush the log stream, to ensure correct log interleaving.
|
"""Flush the log stream, to ensure correct log interleaving.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
None.
|
None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.logfile.flush()
|
self.logfile.flush()
|
||||||
if self.chained_file:
|
if self.chained_file:
|
||||||
self.chained_file.flush()
|
self.chained_file.flush()
|
||||||
|
|
||||||
class RunAndLog(object):
|
class RunAndLog(object):
|
||||||
'''A utility object used to execute sub-processes and log their output to
|
"""A utility object used to execute sub-processes and log their output to
|
||||||
a multiplexed log file. Objects of this type should be created by factory
|
a multiplexed log file. Objects of this type should be created by factory
|
||||||
functions in the Logfile class rather than directly.'''
|
functions in the Logfile class rather than directly."""
|
||||||
|
|
||||||
def __init__(self, logfile, name, chained_file):
|
def __init__(self, logfile, name, chained_file):
|
||||||
'''Initialize a new object.
|
"""Initialize a new object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
logfile: The Logfile object to log to.
|
logfile: The Logfile object to log to.
|
||||||
|
@ -96,29 +96,33 @@ class RunAndLog(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.logfile = logfile
|
self.logfile = logfile
|
||||||
self.name = name
|
self.name = name
|
||||||
self.chained_file = chained_file
|
self.chained_file = chained_file
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
'''Clean up any resources managed by this object.'''
|
"""Clean up any resources managed by this object."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def run(self, cmd, cwd=None):
|
def run(self, cmd, cwd=None, ignore_errors=False):
|
||||||
'''Run a command as a sub-process, and log the results.
|
"""Run a command as a sub-process, and log the results.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cmd: The command to execute.
|
cmd: The command to execute.
|
||||||
cwd: The directory to run the command in. Can be None to use the
|
cwd: The directory to run the command in. Can be None to use the
|
||||||
current directory.
|
current directory.
|
||||||
|
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:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
msg = "+" + " ".join(cmd) + "\n"
|
msg = '+' + ' '.join(cmd) + '\n'
|
||||||
if self.chained_file:
|
if self.chained_file:
|
||||||
self.chained_file.write(msg)
|
self.chained_file.write(msg)
|
||||||
self.logfile.write(self, msg)
|
self.logfile.write(self, msg)
|
||||||
|
@ -148,7 +152,7 @@ class RunAndLog(object):
|
||||||
exception = e
|
exception = e
|
||||||
if output and not output.endswith('\n'):
|
if output and not output.endswith('\n'):
|
||||||
output += '\n'
|
output += '\n'
|
||||||
if exit_status and not exception:
|
if exit_status and not exception and not ignore_errors:
|
||||||
exception = Exception('Exit code: ' + str(exit_status))
|
exception = Exception('Exit code: ' + str(exit_status))
|
||||||
if exception:
|
if exception:
|
||||||
output += str(exception) + '\n'
|
output += str(exception) + '\n'
|
||||||
|
@ -159,13 +163,13 @@ class RunAndLog(object):
|
||||||
raise exception
|
raise exception
|
||||||
|
|
||||||
class SectionCtxMgr(object):
|
class SectionCtxMgr(object):
|
||||||
'''A context manager for Python's "with" statement, which allows a certain
|
"""A context manager for Python's "with" statement, which allows a certain
|
||||||
portion of test code to be logged to a separate section of the log file.
|
portion of test code to be logged to a separate section of the log file.
|
||||||
Objects of this type should be created by factory functions in the Logfile
|
Objects of this type should be created by factory functions in the Logfile
|
||||||
class rather than directly.'''
|
class rather than directly."""
|
||||||
|
|
||||||
def __init__(self, log, marker):
|
def __init__(self, log, marker):
|
||||||
'''Initialize a new object.
|
"""Initialize a new object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
log: The Logfile object to log to.
|
log: The Logfile object to log to.
|
||||||
|
@ -173,7 +177,7 @@ class SectionCtxMgr(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.log = log
|
self.log = log
|
||||||
self.marker = marker
|
self.marker = marker
|
||||||
|
@ -185,35 +189,35 @@ class SectionCtxMgr(object):
|
||||||
self.log.end_section(self.marker)
|
self.log.end_section(self.marker)
|
||||||
|
|
||||||
class Logfile(object):
|
class Logfile(object):
|
||||||
'''Generates an HTML-formatted log file containing multiple streams of
|
"""Generates an HTML-formatted log file containing multiple streams of
|
||||||
data, each represented in a well-delineated/-structured fashion.'''
|
data, each represented in a well-delineated/-structured fashion."""
|
||||||
|
|
||||||
def __init__(self, fn):
|
def __init__(self, fn):
|
||||||
'''Initialize a new object.
|
"""Initialize a new object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
fn: The filename to write to.
|
fn: The filename to write to.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.f = open(fn, "wt")
|
self.f = open(fn, 'wt')
|
||||||
self.last_stream = None
|
self.last_stream = None
|
||||||
self.blocks = []
|
self.blocks = []
|
||||||
self.cur_evt = 1
|
self.cur_evt = 1
|
||||||
shutil.copy(mod_dir + "/multiplexed_log.css", os.path.dirname(fn))
|
shutil.copy(mod_dir + '/multiplexed_log.css', os.path.dirname(fn))
|
||||||
self.f.write("""\
|
self.f.write('''\
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" type="text/css" href="multiplexed_log.css">
|
<link rel="stylesheet" type="text/css" href="multiplexed_log.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<tt>
|
<tt>
|
||||||
""")
|
''')
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
'''Close the log file.
|
"""Close the log file.
|
||||||
|
|
||||||
After calling this function, no more data may be written to the log.
|
After calling this function, no more data may be written to the log.
|
||||||
|
|
||||||
|
@ -222,22 +226,22 @@ class Logfile(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.f.write("""\
|
self.f.write('''\
|
||||||
</tt>
|
</tt>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
""")
|
''')
|
||||||
self.f.close()
|
self.f.close()
|
||||||
|
|
||||||
# The set of characters that should be represented as hexadecimal codes in
|
# The set of characters that should be represented as hexadecimal codes in
|
||||||
# the log file.
|
# the log file.
|
||||||
_nonprint = ("%" + "".join(chr(c) for c in range(0, 32) if c not in (9, 10)) +
|
_nonprint = ('%' + ''.join(chr(c) for c in range(0, 32) if c not in (9, 10)) +
|
||||||
"".join(chr(c) for c in range(127, 256)))
|
''.join(chr(c) for c in range(127, 256)))
|
||||||
|
|
||||||
def _escape(self, data):
|
def _escape(self, data):
|
||||||
'''Render data format suitable for inclusion in an HTML document.
|
"""Render data format suitable for inclusion in an HTML document.
|
||||||
|
|
||||||
This includes HTML-escaping certain characters, and translating
|
This includes HTML-escaping certain characters, and translating
|
||||||
control characters to a hexadecimal representation.
|
control characters to a hexadecimal representation.
|
||||||
|
@ -247,36 +251,36 @@ class Logfile(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An escaped version of the data.
|
An escaped version of the data.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
data = data.replace(chr(13), "")
|
data = data.replace(chr(13), '')
|
||||||
data = "".join((c in self._nonprint) and ("%%%02x" % ord(c)) or
|
data = ''.join((c in self._nonprint) and ('%%%02x' % ord(c)) or
|
||||||
c for c in data)
|
c for c in data)
|
||||||
data = cgi.escape(data)
|
data = cgi.escape(data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def _terminate_stream(self):
|
def _terminate_stream(self):
|
||||||
'''Write HTML to the log file to terminate the current stream's data.
|
"""Write HTML to the log file to terminate the current stream's data.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
None.
|
None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.cur_evt += 1
|
self.cur_evt += 1
|
||||||
if not self.last_stream:
|
if not self.last_stream:
|
||||||
return
|
return
|
||||||
self.f.write("</pre>\n")
|
self.f.write('</pre>\n')
|
||||||
self.f.write("<div class=\"stream-trailer\" id=\"" +
|
self.f.write('<div class="stream-trailer" id="' +
|
||||||
self.last_stream.name + "\">End stream: " +
|
self.last_stream.name + '">End stream: ' +
|
||||||
self.last_stream.name + "</div>\n")
|
self.last_stream.name + '</div>\n')
|
||||||
self.f.write("</div>\n")
|
self.f.write('</div>\n')
|
||||||
self.last_stream = None
|
self.last_stream = None
|
||||||
|
|
||||||
def _note(self, note_type, msg):
|
def _note(self, note_type, msg):
|
||||||
'''Write a note or one-off message to the log file.
|
"""Write a note or one-off message to the log file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
note_type: The type of note. This must be a value supported by the
|
note_type: The type of note. This must be a value supported by the
|
||||||
|
@ -285,32 +289,32 @@ class Logfile(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self._terminate_stream()
|
self._terminate_stream()
|
||||||
self.f.write("<div class=\"" + note_type + "\">\n<pre>")
|
self.f.write('<div class="' + note_type + '">\n<pre>')
|
||||||
self.f.write(self._escape(msg))
|
self.f.write(self._escape(msg))
|
||||||
self.f.write("\n</pre></div>\n")
|
self.f.write('\n</pre></div>\n')
|
||||||
|
|
||||||
def start_section(self, marker):
|
def start_section(self, marker):
|
||||||
'''Begin a new nested section in the log file.
|
"""Begin a new nested section in the log file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
marker: The name of the section that is starting.
|
marker: The name of the section that is starting.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self._terminate_stream()
|
self._terminate_stream()
|
||||||
self.blocks.append(marker)
|
self.blocks.append(marker)
|
||||||
blk_path = "/".join(self.blocks)
|
blk_path = '/'.join(self.blocks)
|
||||||
self.f.write("<div class=\"section\" id=\"" + blk_path + "\">\n")
|
self.f.write('<div class="section" id="' + blk_path + '">\n')
|
||||||
self.f.write("<div class=\"section-header\" id=\"" + blk_path +
|
self.f.write('<div class="section-header" id="' + blk_path +
|
||||||
"\">Section: " + blk_path + "</div>\n")
|
'">Section: ' + blk_path + '</div>\n')
|
||||||
|
|
||||||
def end_section(self, marker):
|
def end_section(self, marker):
|
||||||
'''Terminate the current nested section in the log file.
|
"""Terminate the current nested section in the log file.
|
||||||
|
|
||||||
This function validates proper nesting of start_section() and
|
This function validates proper nesting of start_section() and
|
||||||
end_section() calls. If a mismatch is found, an exception is raised.
|
end_section() calls. If a mismatch is found, an exception is raised.
|
||||||
|
@ -320,20 +324,20 @@ class Logfile(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if (not self.blocks) or (marker != self.blocks[-1]):
|
if (not self.blocks) or (marker != self.blocks[-1]):
|
||||||
raise Exception("Block nesting mismatch: \"%s\" \"%s\"" %
|
raise Exception('Block nesting mismatch: "%s" "%s"' %
|
||||||
(marker, "/".join(self.blocks)))
|
(marker, '/'.join(self.blocks)))
|
||||||
self._terminate_stream()
|
self._terminate_stream()
|
||||||
blk_path = "/".join(self.blocks)
|
blk_path = '/'.join(self.blocks)
|
||||||
self.f.write("<div class=\"section-trailer\" id=\"section-trailer-" +
|
self.f.write('<div class="section-trailer" id="section-trailer-' +
|
||||||
blk_path + "\">End section: " + blk_path + "</div>\n")
|
blk_path + '">End section: ' + blk_path + '</div>\n')
|
||||||
self.f.write("</div>\n")
|
self.f.write('</div>\n')
|
||||||
self.blocks.pop()
|
self.blocks.pop()
|
||||||
|
|
||||||
def section(self, marker):
|
def section(self, marker):
|
||||||
'''Create a temporary section in the log file.
|
"""Create a temporary section in the log file.
|
||||||
|
|
||||||
This function creates a context manager for Python's "with" statement,
|
This function creates a context manager for Python's "with" statement,
|
||||||
which allows a certain portion of test code to be logged to a separate
|
which allows a certain portion of test code to be logged to a separate
|
||||||
|
@ -348,96 +352,120 @@ class Logfile(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A context manager object.
|
A context manager object.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return SectionCtxMgr(self, marker)
|
return SectionCtxMgr(self, marker)
|
||||||
|
|
||||||
def error(self, msg):
|
def error(self, msg):
|
||||||
'''Write an error note to the log file.
|
"""Write an error note to the log file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg: A message describing the error.
|
msg: A message describing the error.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self._note("error", msg)
|
self._note("error", msg)
|
||||||
|
|
||||||
def warning(self, msg):
|
def warning(self, msg):
|
||||||
'''Write an warning note to the log file.
|
"""Write an warning note to the log file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg: A message describing the warning.
|
msg: A message describing the warning.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self._note("warning", msg)
|
self._note("warning", msg)
|
||||||
|
|
||||||
def info(self, msg):
|
def info(self, msg):
|
||||||
'''Write an informational note to the log file.
|
"""Write an informational note to the log file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg: An informational message.
|
msg: An informational message.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self._note("info", msg)
|
self._note("info", msg)
|
||||||
|
|
||||||
def action(self, msg):
|
def action(self, msg):
|
||||||
'''Write an action note to the log file.
|
"""Write an action note to the log file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg: A message describing the action that is being logged.
|
msg: A message describing the action that is being logged.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self._note("action", msg)
|
self._note("action", msg)
|
||||||
|
|
||||||
def status_pass(self, msg):
|
def status_pass(self, msg):
|
||||||
'''Write a note to the log file describing test(s) which passed.
|
"""Write a note to the log file describing test(s) which passed.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg: A message describing passed test(s).
|
msg: A message describing the passed test(s).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self._note("status-pass", msg)
|
self._note("status-pass", msg)
|
||||||
|
|
||||||
def status_skipped(self, msg):
|
def status_skipped(self, msg):
|
||||||
'''Write a note to the log file describing skipped test(s).
|
"""Write a note to the log file describing skipped test(s).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg: A message describing passed test(s).
|
msg: A message describing the skipped test(s).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self._note("status-skipped", msg)
|
self._note("status-skipped", msg)
|
||||||
|
|
||||||
def status_fail(self, msg):
|
def status_xfail(self, msg):
|
||||||
'''Write a note to the log file describing failed test(s).
|
"""Write a note to the log file describing xfailed test(s).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg: A message describing passed test(s).
|
msg: A message describing the xfailed test(s).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
self._note("status-xfail", msg)
|
||||||
|
|
||||||
|
def status_xpass(self, msg):
|
||||||
|
"""Write a note to the log file describing xpassed test(s).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg: A message describing the xpassed test(s).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._note("status-xpass", msg)
|
||||||
|
|
||||||
|
def status_fail(self, msg):
|
||||||
|
"""Write a note to the log file describing failed test(s).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg: A message describing the failed test(s).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing.
|
||||||
|
"""
|
||||||
|
|
||||||
self._note("status-fail", msg)
|
self._note("status-fail", msg)
|
||||||
|
|
||||||
def get_stream(self, name, chained_file=None):
|
def get_stream(self, name, chained_file=None):
|
||||||
'''Create an object to log a single stream's data into the log file.
|
"""Create an object to log a single stream's data into the log file.
|
||||||
|
|
||||||
This creates a "file-like" object that can be written to in order to
|
This creates a "file-like" object that can be written to in order to
|
||||||
write a single stream's data to the log file. The implementation will
|
write a single stream's data to the log file. The implementation will
|
||||||
|
@ -452,12 +480,12 @@ class Logfile(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A file-like object.
|
A file-like object.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return LogfileStream(self, name, chained_file)
|
return LogfileStream(self, name, chained_file)
|
||||||
|
|
||||||
def get_runner(self, name, chained_file=None):
|
def get_runner(self, name, chained_file=None):
|
||||||
'''Create an object that executes processes and logs their output.
|
"""Create an object that executes processes and logs their output.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: The name of this sub-process.
|
name: The name of this sub-process.
|
||||||
|
@ -466,12 +494,12 @@ class Logfile(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A RunAndLog object.
|
A RunAndLog object.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return RunAndLog(self, name, chained_file)
|
return RunAndLog(self, name, chained_file)
|
||||||
|
|
||||||
def write(self, stream, data, implicit=False):
|
def write(self, stream, data, implicit=False):
|
||||||
'''Write stream data into the log file.
|
"""Write stream data into the log file.
|
||||||
|
|
||||||
This function should only be used by instances of LogfileStream or
|
This function should only be used by instances of LogfileStream or
|
||||||
RunAndLog.
|
RunAndLog.
|
||||||
|
@ -487,29 +515,29 @@ class Logfile(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if stream != self.last_stream:
|
if stream != self.last_stream:
|
||||||
self._terminate_stream()
|
self._terminate_stream()
|
||||||
self.f.write("<div class=\"stream\" id=\"%s\">\n" % stream.name)
|
self.f.write('<div class="stream" id="%s">\n' % stream.name)
|
||||||
self.f.write("<div class=\"stream-header\" id=\"" + stream.name +
|
self.f.write('<div class="stream-header" id="' + stream.name +
|
||||||
"\">Stream: " + stream.name + "</div>\n")
|
'">Stream: ' + stream.name + '</div>\n')
|
||||||
self.f.write("<pre>")
|
self.f.write('<pre>')
|
||||||
if implicit:
|
if implicit:
|
||||||
self.f.write("<span class=\"implicit\">")
|
self.f.write('<span class="implicit">')
|
||||||
self.f.write(self._escape(data))
|
self.f.write(self._escape(data))
|
||||||
if implicit:
|
if implicit:
|
||||||
self.f.write("</span>")
|
self.f.write('</span>')
|
||||||
self.last_stream = stream
|
self.last_stream = stream
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
'''Flush the log stream, to ensure correct log interleaving.
|
"""Flush the log stream, to ensure correct log interleaving.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
None.
|
None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.f.flush()
|
self.f.flush()
|
||||||
|
|
|
@ -16,17 +16,17 @@ import sys
|
||||||
sys.argv.pop(0)
|
sys.argv.pop(0)
|
||||||
|
|
||||||
# argv; py.test test_directory_name user-supplied-arguments
|
# argv; py.test test_directory_name user-supplied-arguments
|
||||||
args = ["py.test", os.path.dirname(__file__) + "/tests"]
|
args = ['py.test', os.path.dirname(__file__) + '/tests']
|
||||||
args.extend(sys.argv)
|
args.extend(sys.argv)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.execvp("py.test", args)
|
os.execvp('py.test', args)
|
||||||
except:
|
except:
|
||||||
# Log full details of any exception for detailed analysis
|
# Log full details of any exception for detailed analysis
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
# Hint to the user that they likely simply haven't installed the required
|
# Hint to the user that they likely simply haven't installed the required
|
||||||
# dependencies.
|
# dependencies.
|
||||||
print >>sys.stderr, """
|
print >>sys.stderr, '''
|
||||||
exec(py.test) failed; perhaps you are missing some dependencies?
|
exec(py.test) failed; perhaps you are missing some dependencies?
|
||||||
See test/py/README.md for the list."""
|
See test/py/README.md for the list.'''
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# command prompt.
|
# command prompt.
|
||||||
|
|
||||||
def test_version(u_boot_console):
|
def test_version(u_boot_console):
|
||||||
'''Test that the "version" command prints the U-Boot version.'''
|
"""Test that the "version" command prints the U-Boot version."""
|
||||||
|
|
||||||
# "version" prints the U-Boot sign-on message. This is usually considered
|
# "version" prints the U-Boot sign-on message. This is usually considered
|
||||||
# an error, so that any unexpected reboot causes an error. Here, this
|
# an error, so that any unexpected reboot causes an error. Here, this
|
||||||
|
|
|
@ -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)
|
|
@ -10,34 +10,34 @@ import pytest
|
||||||
# FIXME: This might be useful for other tests;
|
# FIXME: This might be useful for other tests;
|
||||||
# perhaps refactor it into ConsoleBase or some other state object?
|
# perhaps refactor it into ConsoleBase or some other state object?
|
||||||
class StateTestEnv(object):
|
class StateTestEnv(object):
|
||||||
'''Container that represents the state of all U-Boot environment variables.
|
"""Container that represents the state of all U-Boot environment variables.
|
||||||
This enables quick determination of existant/non-existant variable
|
This enables quick determination of existant/non-existant variable
|
||||||
names.
|
names.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, u_boot_console):
|
def __init__(self, u_boot_console):
|
||||||
'''Initialize a new StateTestEnv object.
|
"""Initialize a new StateTestEnv object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
u_boot_console: A U-Boot console.
|
u_boot_console: A U-Boot console.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.u_boot_console = u_boot_console
|
self.u_boot_console = u_boot_console
|
||||||
self.get_env()
|
self.get_env()
|
||||||
self.set_var = self.get_non_existent_var()
|
self.set_var = self.get_non_existent_var()
|
||||||
|
|
||||||
def get_env(self):
|
def get_env(self):
|
||||||
'''Read all current environment variables from U-Boot.
|
"""Read all current environment variables from U-Boot.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
None.
|
None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
response = self.u_boot_console.run_command('printenv')
|
response = self.u_boot_console.run_command('printenv')
|
||||||
self.env = {}
|
self.env = {}
|
||||||
|
@ -48,27 +48,27 @@ class StateTestEnv(object):
|
||||||
self.env[var] = value
|
self.env[var] = value
|
||||||
|
|
||||||
def get_existent_var(self):
|
def get_existent_var(self):
|
||||||
'''Return the name of an environment variable that exists.
|
"""Return the name of an environment variable that exists.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
None.
|
None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The name of an environment variable.
|
The name of an environment variable.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
for var in self.env:
|
for var in self.env:
|
||||||
return var
|
return var
|
||||||
|
|
||||||
def get_non_existent_var(self):
|
def get_non_existent_var(self):
|
||||||
'''Return the name of an environment variable that does not exist.
|
"""Return the name of an environment variable that does not exist.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
None.
|
None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The name of an environment variable.
|
The name of an environment variable.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
while True:
|
while True:
|
||||||
|
@ -77,63 +77,67 @@ class StateTestEnv(object):
|
||||||
return var
|
return var
|
||||||
n += 1
|
n += 1
|
||||||
|
|
||||||
@pytest.fixture(scope='module')
|
ste = None
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
def state_test_env(u_boot_console):
|
def state_test_env(u_boot_console):
|
||||||
'''pytest fixture to provide a StateTestEnv object to tests.'''
|
"""pytest fixture to provide a StateTestEnv object to tests."""
|
||||||
|
|
||||||
return StateTestEnv(u_boot_console)
|
global ste
|
||||||
|
if not ste:
|
||||||
|
ste = StateTestEnv(u_boot_console)
|
||||||
|
return ste
|
||||||
|
|
||||||
def unset_var(state_test_env, var):
|
def unset_var(state_test_env, var):
|
||||||
'''Unset an environment variable.
|
"""Unset an environment variable.
|
||||||
|
|
||||||
This both executes a U-Boot shell command and updates a StateTestEnv
|
This both executes a U-Boot shell command and updates a StateTestEnv
|
||||||
object.
|
object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
state_test_env: The StateTestEnv object to updata.
|
state_test_env: The StateTestEnv object to update.
|
||||||
var: The variable name to unset.
|
var: The variable name to unset.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
state_test_env.u_boot_console.run_command('setenv %s' % var)
|
state_test_env.u_boot_console.run_command('setenv %s' % var)
|
||||||
if var in state_test_env.env:
|
if var in state_test_env.env:
|
||||||
del state_test_env.env[var]
|
del state_test_env.env[var]
|
||||||
|
|
||||||
def set_var(state_test_env, var, value):
|
def set_var(state_test_env, var, value):
|
||||||
'''Set an environment variable.
|
"""Set an environment variable.
|
||||||
|
|
||||||
This both executes a U-Boot shell command and updates a StateTestEnv
|
This both executes a U-Boot shell command and updates a StateTestEnv
|
||||||
object.
|
object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
state_test_env: The StateTestEnv object to updata.
|
state_test_env: The StateTestEnv object to update.
|
||||||
var: The variable name to set.
|
var: The variable name to set.
|
||||||
value: The value to set the variable to.
|
value: The value to set the variable to.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
state_test_env.u_boot_console.run_command('setenv %s "%s"' % (var, value))
|
state_test_env.u_boot_console.run_command('setenv %s "%s"' % (var, value))
|
||||||
state_test_env.env[var] = value
|
state_test_env.env[var] = value
|
||||||
|
|
||||||
def validate_empty(state_test_env, var):
|
def validate_empty(state_test_env, var):
|
||||||
'''Validate that a variable is not set, using U-Boot shell commands.
|
"""Validate that a variable is not set, using U-Boot shell commands.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
var: The variable name to test.
|
var: The variable name to test.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
response = state_test_env.u_boot_console.run_command('echo $%s' % var)
|
response = state_test_env.u_boot_console.run_command('echo $%s' % var)
|
||||||
assert response == ''
|
assert response == ''
|
||||||
|
|
||||||
def validate_set(state_test_env, var, value):
|
def validate_set(state_test_env, var, value):
|
||||||
'''Validate that a variable is set, using U-Boot shell commands.
|
"""Validate that a variable is set, using U-Boot shell commands.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
var: The variable name to test.
|
var: The variable name to test.
|
||||||
|
@ -141,7 +145,7 @@ def validate_set(state_test_env, var, value):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# echo does not preserve leading, internal, or trailing whitespace in the
|
# echo does not preserve leading, internal, or trailing whitespace in the
|
||||||
# value. printenv does, and hence allows more complete testing.
|
# value. printenv does, and hence allows more complete testing.
|
||||||
|
@ -149,20 +153,20 @@ def validate_set(state_test_env, var, value):
|
||||||
assert response == ('%s=%s' % (var, value))
|
assert response == ('%s=%s' % (var, value))
|
||||||
|
|
||||||
def test_env_echo_exists(state_test_env):
|
def test_env_echo_exists(state_test_env):
|
||||||
'''Test echoing a variable that exists.'''
|
"""Test echoing a variable that exists."""
|
||||||
|
|
||||||
var = state_test_env.get_existent_var()
|
var = state_test_env.get_existent_var()
|
||||||
value = state_test_env.env[var]
|
value = state_test_env.env[var]
|
||||||
validate_set(state_test_env, var, value)
|
validate_set(state_test_env, var, value)
|
||||||
|
|
||||||
def test_env_echo_non_existent(state_test_env):
|
def test_env_echo_non_existent(state_test_env):
|
||||||
'''Test echoing a variable that doesn't exist.'''
|
"""Test echoing a variable that doesn't exist."""
|
||||||
|
|
||||||
var = state_test_env.set_var
|
var = state_test_env.set_var
|
||||||
validate_empty(state_test_env, var)
|
validate_empty(state_test_env, var)
|
||||||
|
|
||||||
def test_env_printenv_non_existent(state_test_env):
|
def test_env_printenv_non_existent(state_test_env):
|
||||||
'''Test printenv error message for non-existant variables.'''
|
"""Test printenv error message for non-existant variables."""
|
||||||
|
|
||||||
var = state_test_env.set_var
|
var = state_test_env.set_var
|
||||||
c = state_test_env.u_boot_console
|
c = state_test_env.u_boot_console
|
||||||
|
@ -171,14 +175,14 @@ def test_env_printenv_non_existent(state_test_env):
|
||||||
assert(response == '## Error: "%s" not defined' % var)
|
assert(response == '## Error: "%s" not defined' % var)
|
||||||
|
|
||||||
def test_env_unset_non_existent(state_test_env):
|
def test_env_unset_non_existent(state_test_env):
|
||||||
'''Test unsetting a nonexistent variable.'''
|
"""Test unsetting a nonexistent variable."""
|
||||||
|
|
||||||
var = state_test_env.get_non_existent_var()
|
var = state_test_env.get_non_existent_var()
|
||||||
unset_var(state_test_env, var)
|
unset_var(state_test_env, var)
|
||||||
validate_empty(state_test_env, var)
|
validate_empty(state_test_env, var)
|
||||||
|
|
||||||
def test_env_set_non_existent(state_test_env):
|
def test_env_set_non_existent(state_test_env):
|
||||||
'''Test set a non-existant variable.'''
|
"""Test set a non-existant variable."""
|
||||||
|
|
||||||
var = state_test_env.set_var
|
var = state_test_env.set_var
|
||||||
value = 'foo'
|
value = 'foo'
|
||||||
|
@ -186,7 +190,7 @@ def test_env_set_non_existent(state_test_env):
|
||||||
validate_set(state_test_env, var, value)
|
validate_set(state_test_env, var, value)
|
||||||
|
|
||||||
def test_env_set_existing(state_test_env):
|
def test_env_set_existing(state_test_env):
|
||||||
'''Test setting an existant variable.'''
|
"""Test setting an existant variable."""
|
||||||
|
|
||||||
var = state_test_env.set_var
|
var = state_test_env.set_var
|
||||||
value = 'bar'
|
value = 'bar'
|
||||||
|
@ -194,14 +198,14 @@ def test_env_set_existing(state_test_env):
|
||||||
validate_set(state_test_env, var, value)
|
validate_set(state_test_env, var, value)
|
||||||
|
|
||||||
def test_env_unset_existing(state_test_env):
|
def test_env_unset_existing(state_test_env):
|
||||||
'''Test unsetting a variable.'''
|
"""Test unsetting a variable."""
|
||||||
|
|
||||||
var = state_test_env.set_var
|
var = state_test_env.set_var
|
||||||
unset_var(state_test_env, var)
|
unset_var(state_test_env, var)
|
||||||
validate_empty(state_test_env, var)
|
validate_empty(state_test_env, var)
|
||||||
|
|
||||||
def test_env_expansion_spaces(state_test_env):
|
def test_env_expansion_spaces(state_test_env):
|
||||||
'''Test expanding a variable that contains a space in its value.'''
|
"""Test expanding a variable that contains a space in its value."""
|
||||||
|
|
||||||
var_space = None
|
var_space = None
|
||||||
var_test = None
|
var_test = None
|
||||||
|
|
|
@ -4,6 +4,6 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
def test_help(u_boot_console):
|
def test_help(u_boot_console):
|
||||||
'''Test that the "help" command can be executed.'''
|
"""Test that the "help" command can be executed."""
|
||||||
|
|
||||||
u_boot_console.run_command('help')
|
u_boot_console.run_command('help')
|
||||||
|
|
|
@ -95,7 +95,7 @@ subtests = (
|
||||||
)
|
)
|
||||||
|
|
||||||
def exec_hush_if(u_boot_console, expr, result):
|
def exec_hush_if(u_boot_console, expr, result):
|
||||||
'''Execute a shell "if" command, and validate its result.'''
|
"""Execute a shell "if" command, and validate its result."""
|
||||||
|
|
||||||
cmd = 'if ' + expr + '; then echo true; else echo false; fi'
|
cmd = 'if ' + expr + '; then echo true; else echo false; fi'
|
||||||
response = u_boot_console.run_command(cmd)
|
response = u_boot_console.run_command(cmd)
|
||||||
|
@ -103,7 +103,7 @@ def exec_hush_if(u_boot_console, expr, result):
|
||||||
|
|
||||||
@pytest.mark.buildconfigspec('sys_hush_parser')
|
@pytest.mark.buildconfigspec('sys_hush_parser')
|
||||||
def test_hush_if_test_setup(u_boot_console):
|
def test_hush_if_test_setup(u_boot_console):
|
||||||
'''Set up environment variables used during the "if" tests.'''
|
"""Set up environment variables used during the "if" tests."""
|
||||||
|
|
||||||
u_boot_console.run_command('setenv ut_var_nonexistent')
|
u_boot_console.run_command('setenv ut_var_nonexistent')
|
||||||
u_boot_console.run_command('setenv ut_var_exists 1')
|
u_boot_console.run_command('setenv ut_var_exists 1')
|
||||||
|
@ -111,13 +111,13 @@ def test_hush_if_test_setup(u_boot_console):
|
||||||
@pytest.mark.buildconfigspec('sys_hush_parser')
|
@pytest.mark.buildconfigspec('sys_hush_parser')
|
||||||
@pytest.mark.parametrize('expr,result', subtests)
|
@pytest.mark.parametrize('expr,result', subtests)
|
||||||
def test_hush_if_test(u_boot_console, expr, result):
|
def test_hush_if_test(u_boot_console, expr, result):
|
||||||
'''Test a single "if test" condition.'''
|
"""Test a single "if test" condition."""
|
||||||
|
|
||||||
exec_hush_if(u_boot_console, expr, result)
|
exec_hush_if(u_boot_console, expr, result)
|
||||||
|
|
||||||
@pytest.mark.buildconfigspec('sys_hush_parser')
|
@pytest.mark.buildconfigspec('sys_hush_parser')
|
||||||
def test_hush_if_test_teardown(u_boot_console):
|
def test_hush_if_test_teardown(u_boot_console):
|
||||||
'''Clean up environment variables used during the "if" tests.'''
|
"""Clean up environment variables used during the "if" tests."""
|
||||||
|
|
||||||
u_boot_console.run_command('setenv ut_var_exists')
|
u_boot_console.run_command('setenv ut_var_exists')
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ def test_hush_if_test_teardown(u_boot_console):
|
||||||
# Of those, only UMS currently allows file removal though.
|
# Of those, only UMS currently allows file removal though.
|
||||||
@pytest.mark.boardspec('sandbox')
|
@pytest.mark.boardspec('sandbox')
|
||||||
def test_hush_if_test_host_file_exists(u_boot_console):
|
def test_hush_if_test_host_file_exists(u_boot_console):
|
||||||
'''Test the "if test -e" shell command.'''
|
"""Test the "if test -e" shell command."""
|
||||||
|
|
||||||
test_file = u_boot_console.config.result_dir + \
|
test_file = u_boot_console.config.result_dir + \
|
||||||
'/creating_this_file_breaks_u_boot_tests'
|
'/creating_this_file_breaks_u_boot_tests'
|
||||||
|
|
|
@ -4,13 +4,14 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import u_boot_utils
|
||||||
|
|
||||||
@pytest.mark.buildconfigspec('cmd_memory')
|
@pytest.mark.buildconfigspec('cmd_memory')
|
||||||
def test_md(u_boot_console):
|
def test_md(u_boot_console):
|
||||||
'''Test that md reads memory as expected, and that memory can be modified
|
"""Test that md reads memory as expected, and that memory can be modified
|
||||||
using the mw command.'''
|
using the mw command."""
|
||||||
|
|
||||||
ram_base = u_boot_console.find_ram_base()
|
ram_base = u_boot_utils.find_ram_base(u_boot_console)
|
||||||
addr = '%08x' % ram_base
|
addr = '%08x' % ram_base
|
||||||
val = 'a5f09876'
|
val = 'a5f09876'
|
||||||
expected_response = addr + ': ' + val
|
expected_response = addr + ': ' + val
|
||||||
|
@ -23,10 +24,10 @@ def test_md(u_boot_console):
|
||||||
|
|
||||||
@pytest.mark.buildconfigspec('cmd_memory')
|
@pytest.mark.buildconfigspec('cmd_memory')
|
||||||
def test_md_repeat(u_boot_console):
|
def test_md_repeat(u_boot_console):
|
||||||
'''Test command repeat (via executing an empty command) operates correctly
|
"""Test command repeat (via executing an empty command) operates correctly
|
||||||
for "md"; the command must repeat and dump an incrementing address.'''
|
for "md"; the command must repeat and dump an incrementing address."""
|
||||||
|
|
||||||
ram_base = u_boot_console.find_ram_base()
|
ram_base = u_boot_utils.find_ram_base(u_boot_console)
|
||||||
addr_base = '%08x' % ram_base
|
addr_base = '%08x' % ram_base
|
||||||
words = 0x10
|
words = 0x10
|
||||||
addr_repeat = '%08x' % (ram_base + (words * 4))
|
addr_repeat = '%08x' % (ram_base + (words * 4))
|
||||||
|
|
|
@ -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
|
|
@ -9,16 +9,14 @@ import signal
|
||||||
@pytest.mark.boardspec('sandbox')
|
@pytest.mark.boardspec('sandbox')
|
||||||
@pytest.mark.buildconfigspec('reset')
|
@pytest.mark.buildconfigspec('reset')
|
||||||
def test_reset(u_boot_console):
|
def test_reset(u_boot_console):
|
||||||
'''Test that the "reset" command exits sandbox process.'''
|
"""Test that the "reset" command exits sandbox process."""
|
||||||
|
|
||||||
u_boot_console.run_command('reset', wait_for_prompt=False)
|
u_boot_console.run_command('reset', wait_for_prompt=False)
|
||||||
assert(u_boot_console.validate_exited())
|
assert(u_boot_console.validate_exited())
|
||||||
u_boot_console.ensure_spawned()
|
|
||||||
|
|
||||||
@pytest.mark.boardspec('sandbox')
|
@pytest.mark.boardspec('sandbox')
|
||||||
def test_ctrl_c(u_boot_console):
|
def test_ctrl_c(u_boot_console):
|
||||||
'''Test that sending SIGINT to sandbox causes it to exit.'''
|
"""Test that sending SIGINT to sandbox causes it to exit."""
|
||||||
|
|
||||||
u_boot_console.kill(signal.SIGINT)
|
u_boot_console.kill(signal.SIGINT)
|
||||||
assert(u_boot_console.validate_exited())
|
assert(u_boot_console.validate_exited())
|
||||||
u_boot_console.ensure_spawned()
|
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
# Test basic shell functionality, such as commands separate by semi-colons.
|
# Test basic shell functionality, such as commands separate by semi-colons.
|
||||||
|
|
||||||
def test_shell_execute(u_boot_console):
|
def test_shell_execute(u_boot_console):
|
||||||
'''Test any shell command.'''
|
"""Test any shell command."""
|
||||||
|
|
||||||
response = u_boot_console.run_command('echo hello')
|
response = u_boot_console.run_command('echo hello')
|
||||||
assert response.strip() == 'hello'
|
assert response.strip() == 'hello'
|
||||||
|
|
||||||
def test_shell_semicolon_two(u_boot_console):
|
def test_shell_semicolon_two(u_boot_console):
|
||||||
'''Test two shell commands separate by a semi-colon.'''
|
"""Test two shell commands separate by a semi-colon."""
|
||||||
|
|
||||||
cmd = 'echo hello; echo world'
|
cmd = 'echo hello; echo world'
|
||||||
response = u_boot_console.run_command(cmd)
|
response = u_boot_console.run_command(cmd)
|
||||||
|
@ -19,8 +19,8 @@ def test_shell_semicolon_two(u_boot_console):
|
||||||
assert response.index('hello') < response.index('world')
|
assert response.index('hello') < response.index('world')
|
||||||
|
|
||||||
def test_shell_semicolon_three(u_boot_console):
|
def test_shell_semicolon_three(u_boot_console):
|
||||||
'''Test three shell commands separate by a semi-colon, with variable
|
"""Test three shell commands separate by a semi-colon, with variable
|
||||||
expansion dependencies between them.'''
|
expansion dependencies between them."""
|
||||||
|
|
||||||
cmd = 'setenv list 1; setenv list ${list}2; setenv list ${list}3; ' + \
|
cmd = 'setenv list 1; setenv list ${list}2; setenv list ${list}3; ' + \
|
||||||
'echo ${list}'
|
'echo ${list}'
|
||||||
|
@ -29,9 +29,9 @@ def test_shell_semicolon_three(u_boot_console):
|
||||||
u_boot_console.run_command('setenv list')
|
u_boot_console.run_command('setenv list')
|
||||||
|
|
||||||
def test_shell_run(u_boot_console):
|
def test_shell_run(u_boot_console):
|
||||||
'''Test the "run" shell command.'''
|
"""Test the "run" shell command."""
|
||||||
|
|
||||||
u_boot_console.run_command('setenv foo \"setenv monty 1; setenv python 2\"')
|
u_boot_console.run_command('setenv foo "setenv monty 1; setenv python 2"')
|
||||||
u_boot_console.run_command('run foo')
|
u_boot_console.run_command('run foo')
|
||||||
response = u_boot_console.run_command('echo $monty')
|
response = u_boot_console.run_command('echo $monty')
|
||||||
assert response.strip() == '1'
|
assert response.strip() == '1'
|
||||||
|
|
|
@ -6,12 +6,8 @@ import pytest
|
||||||
import time
|
import time
|
||||||
|
|
||||||
def test_sleep(u_boot_console):
|
def test_sleep(u_boot_console):
|
||||||
'''Test the sleep command, and validate that it sleeps for approximately
|
"""Test the sleep command, and validate that it sleeps for approximately
|
||||||
the correct amount of time.'''
|
the correct amount of time."""
|
||||||
|
|
||||||
# Do this before we time anything, to make sure U-Boot is already running.
|
|
||||||
# Otherwise, the system boot time is included in the time measurement.
|
|
||||||
u_boot_console.ensure_spawned()
|
|
||||||
|
|
||||||
# 3s isn't too long, but is enough to cross a few second boundaries.
|
# 3s isn't too long, but is enough to cross a few second boundaries.
|
||||||
sleep_time = 3
|
sleep_time = 3
|
||||||
|
|
|
@ -2,28 +2,58 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
# Test U-Boot's "ums" command. At present, this test only ensures that a UMS
|
# Test U-Boot's "ums" command. The test starts UMS in U-Boot, waits for USB
|
||||||
# device can be enumerated by the host/test machine. In the future, this test
|
# device enumeration on the host, reads a small block of data from the UMS
|
||||||
# should be enhanced to validate disk IO.
|
# block device, optionally mounts a partition and performs filesystem-based
|
||||||
|
# read/write tests, and finally aborts the "ums" command in U-Boot.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import os.path
|
||||||
import pytest
|
import pytest
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
|
import u_boot_utils
|
||||||
|
|
||||||
'''
|
"""
|
||||||
Note: This test relies on:
|
Note: This test relies on:
|
||||||
|
|
||||||
a) boardenv_* to contain configuration values to define which USB ports are
|
a) boardenv_* to contain configuration values to define which USB ports are
|
||||||
available for testing. Without this, this test will be automatically skipped.
|
available for testing. Without this, this test will be automatically skipped.
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
# Leave this list empty if you have no block_devs below with writable
|
||||||
|
# partitions defined.
|
||||||
|
env__mount_points = (
|
||||||
|
"/mnt/ubtest-mnt-p2371-2180-na",
|
||||||
|
)
|
||||||
|
|
||||||
env__usb_dev_ports = (
|
env__usb_dev_ports = (
|
||||||
{'tgt_usb_ctlr': '0', 'host_ums_dev_node': '/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0'},
|
{
|
||||||
|
"fixture_id": "micro_b",
|
||||||
|
"tgt_usb_ctlr": "0",
|
||||||
|
"host_ums_dev_node": "/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
env__block_devs = (
|
env__block_devs = (
|
||||||
{'type': 'mmc', 'id': '0'}, # eMMC; always present
|
# eMMC; always present
|
||||||
{'type': 'mmc', 'id': '1'}, # SD card; present since I plugged one in
|
{
|
||||||
|
"fixture_id": "emmc",
|
||||||
|
"type": "mmc",
|
||||||
|
"id": "0",
|
||||||
|
# The following two properties are optional.
|
||||||
|
# If present, the partition will be mounted and a file written-to and
|
||||||
|
# read-from it. If missing, only a simple block read test will be
|
||||||
|
# performed.
|
||||||
|
"writable_fs_partition": 1,
|
||||||
|
"writable_fs_subdir": "tmp/",
|
||||||
|
},
|
||||||
|
# SD card; present since I plugged one in
|
||||||
|
{
|
||||||
|
"fixture_id": "sd",
|
||||||
|
"type": "mmc",
|
||||||
|
"id": "1"
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
b) udev rules to set permissions on devices nodes, so that sudo is not
|
b) udev rules to set permissions on devices nodes, so that sudo is not
|
||||||
|
@ -34,47 +64,42 @@ ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="66
|
||||||
(You may wish to change the group ID instead of setting the permissions wide
|
(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
|
open. All that matters is that the user ID running the test can access the
|
||||||
device.)
|
device.)
|
||||||
'''
|
|
||||||
|
|
||||||
def open_ums_device(host_ums_dev_node):
|
c) /etc/fstab entries to allow the block device to be mounted without requiring
|
||||||
'''Attempt to open a device node, returning either the opened file handle,
|
root permissions. For example:
|
||||||
or None on any error.'''
|
|
||||||
|
|
||||||
try:
|
/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0-part1 /mnt/ubtest-mnt-p2371-2180-na ext4 noauto,user,nosuid,nodev
|
||||||
return open(host_ums_dev_node, 'rb')
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def wait_for_ums_device(host_ums_dev_node):
|
This entry is only needed if any block_devs above contain a
|
||||||
'''Continually attempt to open the device node exported by the "ums"
|
writable_fs_partition value.
|
||||||
command, and either return the opened file handle, or raise an exception
|
"""
|
||||||
after a timeout.'''
|
|
||||||
|
|
||||||
for i in xrange(100):
|
|
||||||
fh = open_ums_device(host_ums_dev_node)
|
|
||||||
if fh:
|
|
||||||
return fh
|
|
||||||
time.sleep(0.1)
|
|
||||||
raise Exception('UMS device did not appear')
|
|
||||||
|
|
||||||
def wait_for_ums_device_gone(host_ums_dev_node):
|
|
||||||
'''Continually attempt to open the device node exported by the "ums"
|
|
||||||
command, and either return once the device has disappeared, or raise an
|
|
||||||
exception if it does not before a timeout occurs.'''
|
|
||||||
|
|
||||||
for i in xrange(100):
|
|
||||||
fh = open_ums_device(host_ums_dev_node)
|
|
||||||
if not fh:
|
|
||||||
return
|
|
||||||
fh.close()
|
|
||||||
time.sleep(0.1)
|
|
||||||
raise Exception('UMS device did not disappear')
|
|
||||||
|
|
||||||
@pytest.mark.buildconfigspec('cmd_usb_mass_storage')
|
@pytest.mark.buildconfigspec('cmd_usb_mass_storage')
|
||||||
def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
|
def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
|
||||||
'''Test the "ums" command; the host system must be able to enumerate a UMS
|
"""Test the "ums" command; the host system must be able to enumerate a UMS
|
||||||
device when "ums" is running, and this device must disappear when "ums" is
|
device when "ums" is running, block and optionally file I/O are tested,
|
||||||
aborted.'''
|
and this device must disappear when "ums" 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__block_devs: The list of block devices that the target U-Boot
|
||||||
|
device has attached. See the file-level comment above for details
|
||||||
|
of the format.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
have_writable_fs_partition = 'writable_fs_partition' in env__block_devs[0]
|
||||||
|
if not have_writable_fs_partition:
|
||||||
|
# If 'writable_fs_subdir' is missing, we'll skip all parts of the
|
||||||
|
# testing which mount filesystems.
|
||||||
|
u_boot_console.log.warning(
|
||||||
|
'boardenv missing "writable_fs_partition"; ' +
|
||||||
|
'UMS testing will be limited.')
|
||||||
|
|
||||||
tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr']
|
tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr']
|
||||||
host_ums_dev_node = env__usb_dev_port['host_ums_dev_node']
|
host_ums_dev_node = env__usb_dev_port['host_ums_dev_node']
|
||||||
|
@ -84,11 +109,129 @@ def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
|
||||||
# device list here. We'll test each block device somewhere else.
|
# device list here. We'll test each block device somewhere else.
|
||||||
tgt_dev_type = env__block_devs[0]['type']
|
tgt_dev_type = env__block_devs[0]['type']
|
||||||
tgt_dev_id = env__block_devs[0]['id']
|
tgt_dev_id = env__block_devs[0]['id']
|
||||||
|
if have_writable_fs_partition:
|
||||||
|
mount_point = u_boot_console.config.env['env__mount_points'][0]
|
||||||
|
mount_subdir = env__block_devs[0]['writable_fs_subdir']
|
||||||
|
part_num = env__block_devs[0]['writable_fs_partition']
|
||||||
|
host_ums_part_node = '%s-part%d' % (host_ums_dev_node, part_num)
|
||||||
|
else:
|
||||||
|
host_ums_part_node = host_ums_dev_node
|
||||||
|
|
||||||
cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id)
|
test_f = u_boot_utils.PersistentRandomFile(u_boot_console, 'ums.bin',
|
||||||
u_boot_console.run_command('ums 0 mmc 0', wait_for_prompt=False)
|
1024 * 1024);
|
||||||
fh = wait_for_ums_device(host_ums_dev_node)
|
if have_writable_fs_partition:
|
||||||
fh.read(4096)
|
mounted_test_fn = mount_point + '/' + mount_subdir + test_f.fn
|
||||||
fh.close()
|
|
||||||
u_boot_console.ctrlc()
|
def start_ums():
|
||||||
wait_for_ums_device_gone(host_ums_dev_node)
|
"""Start U-Boot's ums shell command.
|
||||||
|
|
||||||
|
This also waits for the host-side USB enumeration process to complete.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
u_boot_console.log.action(
|
||||||
|
'Starting long-running U-Boot ums shell command')
|
||||||
|
cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id)
|
||||||
|
u_boot_console.run_command(cmd, wait_for_prompt=False)
|
||||||
|
u_boot_console.wait_for(re.compile('UMS: LUN.*[\r\n]'))
|
||||||
|
fh = u_boot_utils.wait_until_open_succeeds(host_ums_part_node)
|
||||||
|
u_boot_console.log.action('Reading raw data from UMS device')
|
||||||
|
fh.read(4096)
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
def mount():
|
||||||
|
"""Mount the block device that U-Boot exports.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
u_boot_console.log.action('Mounting exported UMS device')
|
||||||
|
cmd = ('/bin/mount', host_ums_part_node)
|
||||||
|
u_boot_utils.run_and_log(u_boot_console, cmd)
|
||||||
|
|
||||||
|
def umount(ignore_errors):
|
||||||
|
"""Unmount the block device that U-Boot exports.
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
u_boot_console.log.action('Unmounting UMS device')
|
||||||
|
cmd = ('/bin/umount', host_ums_part_node)
|
||||||
|
u_boot_utils.run_and_log(u_boot_console, cmd, ignore_errors)
|
||||||
|
|
||||||
|
def stop_ums(ignore_errors):
|
||||||
|
"""Stop U-Boot's ums 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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
u_boot_console.log.action(
|
||||||
|
'Stopping long-running U-Boot ums shell command')
|
||||||
|
u_boot_console.ctrlc()
|
||||||
|
u_boot_utils.wait_until_file_open_fails(host_ums_part_node,
|
||||||
|
ignore_errors)
|
||||||
|
|
||||||
|
ignore_cleanup_errors = True
|
||||||
|
try:
|
||||||
|
start_ums()
|
||||||
|
if not have_writable_fs_partition:
|
||||||
|
# Skip filesystem-based testing if not configured
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
mount()
|
||||||
|
u_boot_console.log.action('Writing test file via UMS')
|
||||||
|
cmd = ('rm', '-f', mounted_test_fn)
|
||||||
|
u_boot_utils.run_and_log(u_boot_console, cmd)
|
||||||
|
if os.path.exists(mounted_test_fn):
|
||||||
|
raise Exception('Could not rm target UMS test file')
|
||||||
|
cmd = ('cp', test_f.abs_fn, mounted_test_fn)
|
||||||
|
u_boot_utils.run_and_log(u_boot_console, cmd)
|
||||||
|
ignore_cleanup_errors = False
|
||||||
|
finally:
|
||||||
|
umount(ignore_errors=ignore_cleanup_errors)
|
||||||
|
finally:
|
||||||
|
stop_ums(ignore_errors=ignore_cleanup_errors)
|
||||||
|
|
||||||
|
ignore_cleanup_errors = True
|
||||||
|
try:
|
||||||
|
start_ums()
|
||||||
|
try:
|
||||||
|
mount()
|
||||||
|
u_boot_console.log.action('Reading test file back via UMS')
|
||||||
|
read_back_hash = u_boot_utils.md5sum_file(mounted_test_fn)
|
||||||
|
cmd = ('rm', '-f', mounted_test_fn)
|
||||||
|
u_boot_utils.run_and_log(u_boot_console, cmd)
|
||||||
|
ignore_cleanup_errors = False
|
||||||
|
finally:
|
||||||
|
umount(ignore_errors=ignore_cleanup_errors)
|
||||||
|
finally:
|
||||||
|
stop_ums(ignore_errors=ignore_cleanup_errors)
|
||||||
|
|
||||||
|
written_hash = test_f.content_hash
|
||||||
|
assert(written_hash == read_back_hash)
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
def test_unknown_command(u_boot_console):
|
def test_unknown_command(u_boot_console):
|
||||||
'''Test that executing an unknown command causes U-Boot to print an
|
"""Test that executing an unknown command causes U-Boot to print an
|
||||||
error.'''
|
error."""
|
||||||
|
|
||||||
# The "unknown command" error is actively expected here,
|
# The "unknown command" error is actively expected here,
|
||||||
# so error detection for it is disabled.
|
# so error detection for it is disabled.
|
||||||
|
|
|
@ -14,6 +14,7 @@ import os
|
||||||
import pytest
|
import pytest
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import u_boot_spawn
|
||||||
|
|
||||||
# Regexes for text we expect U-Boot to send to the console.
|
# Regexes for text we expect U-Boot to send to the console.
|
||||||
pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}-[^\r\n]*)')
|
pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}-[^\r\n]*)')
|
||||||
|
@ -21,14 +22,27 @@ pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}-[^\r\n]*)')
|
||||||
pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
|
pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
|
||||||
pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
|
pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
|
||||||
pattern_error_notification = re.compile('## Error: ')
|
pattern_error_notification = re.compile('## Error: ')
|
||||||
|
pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###')
|
||||||
|
|
||||||
|
PAT_ID = 0
|
||||||
|
PAT_RE = 1
|
||||||
|
|
||||||
|
bad_pattern_defs = (
|
||||||
|
('spl_signon', pattern_u_boot_spl_signon),
|
||||||
|
('main_signon', pattern_u_boot_main_signon),
|
||||||
|
('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
|
||||||
|
('unknown_command', pattern_unknown_command),
|
||||||
|
('error_notification', pattern_error_notification),
|
||||||
|
('error_please_reset', pattern_error_please_reset),
|
||||||
|
)
|
||||||
|
|
||||||
class ConsoleDisableCheck(object):
|
class ConsoleDisableCheck(object):
|
||||||
'''Context manager (for Python's with statement) that temporarily disables
|
"""Context manager (for Python's with statement) that temporarily disables
|
||||||
the specified console output error check. This is useful when deliberately
|
the specified console output error check. This is useful when deliberately
|
||||||
executing a command that is known to trigger one of the error checks, in
|
executing a command that is known to trigger one of the error checks, in
|
||||||
order to test that the error condition is actually raised. This class is
|
order to test that the error condition is actually raised. This class is
|
||||||
used internally by ConsoleBase::disable_check(); it is not intended for
|
used internally by ConsoleBase::disable_check(); it is not intended for
|
||||||
direct usage.'''
|
direct usage."""
|
||||||
|
|
||||||
def __init__(self, console, check_type):
|
def __init__(self, console, check_type):
|
||||||
self.console = console
|
self.console = console
|
||||||
|
@ -36,18 +50,20 @@ class ConsoleDisableCheck(object):
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.console.disable_check_count[self.check_type] += 1
|
self.console.disable_check_count[self.check_type] += 1
|
||||||
|
self.console.eval_bad_patterns()
|
||||||
|
|
||||||
def __exit__(self, extype, value, traceback):
|
def __exit__(self, extype, value, traceback):
|
||||||
self.console.disable_check_count[self.check_type] -= 1
|
self.console.disable_check_count[self.check_type] -= 1
|
||||||
|
self.console.eval_bad_patterns()
|
||||||
|
|
||||||
class ConsoleBase(object):
|
class ConsoleBase(object):
|
||||||
'''The interface through which test functions interact with the U-Boot
|
"""The interface through which test functions interact with the U-Boot
|
||||||
console. This primarily involves executing shell commands, capturing their
|
console. This primarily involves executing shell commands, capturing their
|
||||||
results, and checking for common error conditions. Some common utilities
|
results, and checking for common error conditions. Some common utilities
|
||||||
are also provided too.'''
|
are also provided too."""
|
||||||
|
|
||||||
def __init__(self, log, config, max_fifo_fill):
|
def __init__(self, log, config, max_fifo_fill):
|
||||||
'''Initialize a U-Boot console connection.
|
"""Initialize a U-Boot console connection.
|
||||||
|
|
||||||
Can only usefully be called by sub-classes.
|
Can only usefully be called by sub-classes.
|
||||||
|
|
||||||
|
@ -64,7 +80,7 @@ class ConsoleBase(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.log = log
|
self.log = log
|
||||||
self.config = config
|
self.config = config
|
||||||
|
@ -76,19 +92,20 @@ class ConsoleBase(object):
|
||||||
self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
|
self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
|
||||||
self.prompt_escaped = re.escape(self.prompt)
|
self.prompt_escaped = re.escape(self.prompt)
|
||||||
self.p = None
|
self.p = None
|
||||||
self.disable_check_count = {
|
self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
|
||||||
'spl_signon': 0,
|
self.eval_bad_patterns()
|
||||||
'main_signon': 0,
|
|
||||||
'unknown_command': 0,
|
|
||||||
'error_notification': 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.at_prompt = False
|
self.at_prompt = False
|
||||||
self.at_prompt_logevt = None
|
self.at_prompt_logevt = None
|
||||||
self.ram_base = None
|
|
||||||
|
def eval_bad_patterns(self):
|
||||||
|
self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
|
||||||
|
if self.disable_check_count[pat[PAT_ID]] == 0]
|
||||||
|
self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
|
||||||
|
if self.disable_check_count[pat[PAT_ID]] == 0]
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
'''Terminate the connection to the U-Boot console.
|
"""Terminate the connection to the U-Boot console.
|
||||||
|
|
||||||
This function is only useful once all interaction with U-Boot is
|
This function is only useful once all interaction with U-Boot is
|
||||||
complete. Once this function is called, data cannot be sent to or
|
complete. Once this function is called, data cannot be sent to or
|
||||||
|
@ -99,7 +116,7 @@ class ConsoleBase(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if self.p:
|
if self.p:
|
||||||
self.p.close()
|
self.p.close()
|
||||||
|
@ -107,7 +124,7 @@ class ConsoleBase(object):
|
||||||
|
|
||||||
def run_command(self, cmd, wait_for_echo=True, send_nl=True,
|
def run_command(self, cmd, wait_for_echo=True, send_nl=True,
|
||||||
wait_for_prompt=True):
|
wait_for_prompt=True):
|
||||||
'''Execute a command via the U-Boot console.
|
"""Execute a command via the U-Boot console.
|
||||||
|
|
||||||
The command is always sent to U-Boot.
|
The command is always sent to U-Boot.
|
||||||
|
|
||||||
|
@ -142,29 +159,12 @@ class ConsoleBase(object):
|
||||||
The output from U-Boot during command execution. In other
|
The output from U-Boot during command execution. In other
|
||||||
words, the text U-Boot emitted between the point it echod the
|
words, the text U-Boot emitted between the point it echod the
|
||||||
command string and emitted the subsequent command prompts.
|
command string and emitted the subsequent command prompts.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.ensure_spawned()
|
|
||||||
|
|
||||||
if self.at_prompt and \
|
if self.at_prompt and \
|
||||||
self.at_prompt_logevt != self.logstream.logfile.cur_evt:
|
self.at_prompt_logevt != self.logstream.logfile.cur_evt:
|
||||||
self.logstream.write(self.prompt, implicit=True)
|
self.logstream.write(self.prompt, implicit=True)
|
||||||
|
|
||||||
bad_patterns = []
|
|
||||||
bad_pattern_ids = []
|
|
||||||
if (self.disable_check_count['spl_signon'] == 0 and
|
|
||||||
self.u_boot_spl_signon):
|
|
||||||
bad_patterns.append(self.u_boot_spl_signon_escaped)
|
|
||||||
bad_pattern_ids.append('SPL signon')
|
|
||||||
if self.disable_check_count['main_signon'] == 0:
|
|
||||||
bad_patterns.append(self.u_boot_main_signon_escaped)
|
|
||||||
bad_pattern_ids.append('U-Boot main signon')
|
|
||||||
if self.disable_check_count['unknown_command'] == 0:
|
|
||||||
bad_patterns.append(pattern_unknown_command)
|
|
||||||
bad_pattern_ids.append('Unknown command')
|
|
||||||
if self.disable_check_count['error_notification'] == 0:
|
|
||||||
bad_patterns.append(pattern_error_notification)
|
|
||||||
bad_pattern_ids.append('Error notification')
|
|
||||||
try:
|
try:
|
||||||
self.at_prompt = False
|
self.at_prompt = False
|
||||||
if send_nl:
|
if send_nl:
|
||||||
|
@ -178,18 +178,18 @@ class ConsoleBase(object):
|
||||||
continue
|
continue
|
||||||
chunk = re.escape(chunk)
|
chunk = re.escape(chunk)
|
||||||
chunk = chunk.replace('\\\n', '[\r\n]')
|
chunk = chunk.replace('\\\n', '[\r\n]')
|
||||||
m = self.p.expect([chunk] + bad_patterns)
|
m = self.p.expect([chunk] + self.bad_patterns)
|
||||||
if m != 0:
|
if m != 0:
|
||||||
self.at_prompt = False
|
self.at_prompt = False
|
||||||
raise Exception('Bad pattern found on console: ' +
|
raise Exception('Bad pattern found on console: ' +
|
||||||
bad_pattern_ids[m - 1])
|
self.bad_pattern_ids[m - 1])
|
||||||
if not wait_for_prompt:
|
if not wait_for_prompt:
|
||||||
return
|
return
|
||||||
m = self.p.expect([self.prompt_escaped] + bad_patterns)
|
m = self.p.expect([self.prompt_escaped] + self.bad_patterns)
|
||||||
if m != 0:
|
if m != 0:
|
||||||
self.at_prompt = False
|
self.at_prompt = False
|
||||||
raise Exception('Bad pattern found on console: ' +
|
raise Exception('Bad pattern found on console: ' +
|
||||||
bad_pattern_ids[m - 1])
|
self.bad_pattern_ids[m - 1])
|
||||||
self.at_prompt = True
|
self.at_prompt = True
|
||||||
self.at_prompt_logevt = self.logstream.logfile.cur_evt
|
self.at_prompt_logevt = self.logstream.logfile.cur_evt
|
||||||
# Only strip \r\n; space/TAB might be significant if testing
|
# Only strip \r\n; space/TAB might be significant if testing
|
||||||
|
@ -201,7 +201,7 @@ class ConsoleBase(object):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def ctrlc(self):
|
def ctrlc(self):
|
||||||
'''Send a CTRL-C character to U-Boot.
|
"""Send a CTRL-C character to U-Boot.
|
||||||
|
|
||||||
This is useful in order to stop execution of long-running synchronous
|
This is useful in order to stop execution of long-running synchronous
|
||||||
commands such as "ums".
|
commands such as "ums".
|
||||||
|
@ -211,12 +211,72 @@ class ConsoleBase(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
self.log.action('Sending Ctrl-C')
|
||||||
self.run_command(chr(3), wait_for_echo=False, send_nl=False)
|
self.run_command(chr(3), wait_for_echo=False, send_nl=False)
|
||||||
|
|
||||||
|
def wait_for(self, text):
|
||||||
|
"""Wait for a pattern to be emitted by U-Boot.
|
||||||
|
|
||||||
|
This is useful when a long-running command such as "dfu" is executing,
|
||||||
|
and it periodically emits some text that should show up at a specific
|
||||||
|
location in the log file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: The text to wait for; either a string (containing raw text,
|
||||||
|
not a regular expression) or an re object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if type(text) == type(''):
|
||||||
|
text = re.escape(text)
|
||||||
|
m = self.p.expect([text] + self.bad_patterns)
|
||||||
|
if m != 0:
|
||||||
|
raise Exception('Bad pattern found on console: ' +
|
||||||
|
self.bad_pattern_ids[m - 1])
|
||||||
|
|
||||||
|
def drain_console(self):
|
||||||
|
"""Read from and log the U-Boot console for a short time.
|
||||||
|
|
||||||
|
U-Boot's console output is only logged when the test code actively
|
||||||
|
waits for U-Boot to emit specific data. There are cases where tests
|
||||||
|
can fail without doing this. For example, if a test asks U-Boot to
|
||||||
|
enable USB device mode, then polls until a host-side device node
|
||||||
|
exists. In such a case, it is useful to log U-Boot's console output
|
||||||
|
in case U-Boot printed clues as to why the host-side even did not
|
||||||
|
occur. This function will do that.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# If we are already not connected to U-Boot, there's nothing to drain.
|
||||||
|
# This should only happen when a previous call to run_command() or
|
||||||
|
# wait_for() failed (and hence the output has already been logged), or
|
||||||
|
# the system is shutting down.
|
||||||
|
if not self.p:
|
||||||
|
return
|
||||||
|
|
||||||
|
orig_timeout = self.p.timeout
|
||||||
|
try:
|
||||||
|
# Drain the log for a relatively short time.
|
||||||
|
self.p.timeout = 1000
|
||||||
|
# Wait for something U-Boot will likely never send. This will
|
||||||
|
# cause the console output to be read and logged.
|
||||||
|
self.p.expect(['This should never match U-Boot output'])
|
||||||
|
except u_boot_spawn.Timeout:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
self.p.timeout = orig_timeout
|
||||||
|
|
||||||
def ensure_spawned(self):
|
def ensure_spawned(self):
|
||||||
'''Ensure a connection to a correctly running U-Boot instance.
|
"""Ensure a connection to a correctly running U-Boot instance.
|
||||||
|
|
||||||
This may require spawning a new Sandbox process or resetting target
|
This may require spawning a new Sandbox process or resetting target
|
||||||
hardware, as defined by the implementation sub-class.
|
hardware, as defined by the implementation sub-class.
|
||||||
|
@ -228,7 +288,7 @@ class ConsoleBase(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if self.p:
|
if self.p:
|
||||||
return
|
return
|
||||||
|
@ -243,26 +303,30 @@ class ConsoleBase(object):
|
||||||
self.p.timeout = 30000
|
self.p.timeout = 30000
|
||||||
self.p.logfile_read = self.logstream
|
self.p.logfile_read = self.logstream
|
||||||
if self.config.buildconfig.get('CONFIG_SPL', False) == 'y':
|
if self.config.buildconfig.get('CONFIG_SPL', False) == 'y':
|
||||||
self.p.expect([pattern_u_boot_spl_signon])
|
m = self.p.expect([pattern_u_boot_spl_signon] + self.bad_patterns)
|
||||||
self.u_boot_spl_signon = self.p.after
|
if m != 0:
|
||||||
self.u_boot_spl_signon_escaped = re.escape(self.p.after)
|
raise Exception('Bad pattern found on console: ' +
|
||||||
else:
|
self.bad_pattern_ids[m - 1])
|
||||||
self.u_boot_spl_signon = None
|
m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns)
|
||||||
self.p.expect([pattern_u_boot_main_signon])
|
if m != 0:
|
||||||
self.u_boot_main_signon = self.p.after
|
raise Exception('Bad pattern found on console: ' +
|
||||||
self.u_boot_main_signon_escaped = re.escape(self.p.after)
|
self.bad_pattern_ids[m - 1])
|
||||||
build_idx = self.u_boot_main_signon.find(', Build:')
|
signon = self.p.after
|
||||||
|
build_idx = signon.find(', Build:')
|
||||||
if build_idx == -1:
|
if build_idx == -1:
|
||||||
self.u_boot_version_string = self.u_boot_main_signon
|
self.u_boot_version_string = signon
|
||||||
else:
|
else:
|
||||||
self.u_boot_version_string = self.u_boot_main_signon[:build_idx]
|
self.u_boot_version_string = signon[:build_idx]
|
||||||
while True:
|
while True:
|
||||||
match = self.p.expect([self.prompt_escaped,
|
m = self.p.expect([self.prompt_escaped,
|
||||||
pattern_stop_autoboot_prompt])
|
pattern_stop_autoboot_prompt] + self.bad_patterns)
|
||||||
if match == 1:
|
if m == 0:
|
||||||
|
break
|
||||||
|
if m == 1:
|
||||||
self.p.send(chr(3)) # CTRL-C
|
self.p.send(chr(3)) # CTRL-C
|
||||||
continue
|
continue
|
||||||
break
|
raise Exception('Bad pattern found on console: ' +
|
||||||
|
self.bad_pattern_ids[m - 2])
|
||||||
self.at_prompt = True
|
self.at_prompt = True
|
||||||
self.at_prompt_logevt = self.logstream.logfile.cur_evt
|
self.at_prompt_logevt = self.logstream.logfile.cur_evt
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
@ -271,7 +335,7 @@ class ConsoleBase(object):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def cleanup_spawn(self):
|
def cleanup_spawn(self):
|
||||||
'''Shut down all interaction with the U-Boot instance.
|
"""Shut down all interaction with the U-Boot instance.
|
||||||
|
|
||||||
This is used when an error is detected prior to re-establishing a
|
This is used when an error is detected prior to re-establishing a
|
||||||
connection with a fresh U-Boot instance.
|
connection with a fresh U-Boot instance.
|
||||||
|
@ -283,7 +347,7 @@ class ConsoleBase(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.p:
|
if self.p:
|
||||||
|
@ -293,7 +357,7 @@ class ConsoleBase(object):
|
||||||
self.p = None
|
self.p = None
|
||||||
|
|
||||||
def validate_version_string_in_text(self, text):
|
def validate_version_string_in_text(self, text):
|
||||||
'''Assert that a command's output includes the U-Boot signon message.
|
"""Assert that a command's output includes the U-Boot signon message.
|
||||||
|
|
||||||
This is primarily useful for validating the "version" command without
|
This is primarily useful for validating the "version" command without
|
||||||
duplicating the signon text regex in a test function.
|
duplicating the signon text regex in a test function.
|
||||||
|
@ -303,12 +367,12 @@ class ConsoleBase(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing. An exception is raised if the validation fails.
|
Nothing. An exception is raised if the validation fails.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
assert(self.u_boot_version_string in text)
|
assert(self.u_boot_version_string in text)
|
||||||
|
|
||||||
def disable_check(self, check_type):
|
def disable_check(self, check_type):
|
||||||
'''Temporarily disable an error check of U-Boot's output.
|
"""Temporarily disable an error check of U-Boot's output.
|
||||||
|
|
||||||
Create a new context manager (for use with the "with" statement) which
|
Create a new context manager (for use with the "with" statement) which
|
||||||
temporarily disables a particular console output error check.
|
temporarily disables a particular console output error check.
|
||||||
|
@ -319,42 +383,6 @@ class ConsoleBase(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A context manager object.
|
A context manager object.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return ConsoleDisableCheck(self, check_type)
|
return ConsoleDisableCheck(self, check_type)
|
||||||
|
|
||||||
def find_ram_base(self):
|
|
||||||
'''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:
|
|
||||||
None.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The address of U-Boot's first RAM bank, as an integer.
|
|
||||||
'''
|
|
||||||
|
|
||||||
if self.config.buildconfig.get('config_cmd_bdi', 'n') != 'y':
|
|
||||||
pytest.skip('bdinfo command not supported')
|
|
||||||
if self.ram_base == -1:
|
|
||||||
pytest.skip('Previously failed to find RAM bank start')
|
|
||||||
if self.ram_base is not None:
|
|
||||||
return self.ram_base
|
|
||||||
|
|
||||||
with self.log.section('find_ram_base'):
|
|
||||||
response = self.run_command('bdinfo')
|
|
||||||
for l in response.split('\n'):
|
|
||||||
if '-> start' in l:
|
|
||||||
self.ram_base = int(l.split('=')[1].strip(), 16)
|
|
||||||
break
|
|
||||||
if self.ram_base is None:
|
|
||||||
self.ram_base = -1
|
|
||||||
raise Exception('Failed to find RAM bank start in `bdinfo`')
|
|
||||||
|
|
||||||
return self.ram_base
|
|
||||||
|
|
|
@ -11,15 +11,15 @@ from u_boot_spawn import Spawn
|
||||||
from u_boot_console_base import ConsoleBase
|
from u_boot_console_base import ConsoleBase
|
||||||
|
|
||||||
class ConsoleExecAttach(ConsoleBase):
|
class ConsoleExecAttach(ConsoleBase):
|
||||||
'''Represents a physical connection to a U-Boot console, typically via a
|
"""Represents a physical connection to a U-Boot console, typically via a
|
||||||
serial port. This implementation executes a sub-process to attach to the
|
serial port. This implementation executes a sub-process to attach to the
|
||||||
console, expecting that the stdin/out of the sub-process will be forwarded
|
console, expecting that the stdin/out of the sub-process will be forwarded
|
||||||
to/from the physical hardware. This approach isolates the test infra-
|
to/from the physical hardware. This approach isolates the test infra-
|
||||||
structure from the user-/installation-specific details of how to
|
structure from the user-/installation-specific details of how to
|
||||||
communicate with, and the identity of, serial ports etc.'''
|
communicate with, and the identity of, serial ports etc."""
|
||||||
|
|
||||||
def __init__(self, log, config):
|
def __init__(self, log, config):
|
||||||
'''Initialize a U-Boot console connection.
|
"""Initialize a U-Boot console connection.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
log: A multiplexed_log.Logfile instance.
|
log: A multiplexed_log.Logfile instance.
|
||||||
|
@ -27,7 +27,7 @@ class ConsoleExecAttach(ConsoleBase):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# The max_fifo_fill value might need tweaking per-board/-SoC?
|
# The max_fifo_fill value might need tweaking per-board/-SoC?
|
||||||
# 1 would be safe anywhere, but is very slow (a pexpect issue?).
|
# 1 would be safe anywhere, but is very slow (a pexpect issue?).
|
||||||
|
@ -42,7 +42,7 @@ class ConsoleExecAttach(ConsoleBase):
|
||||||
runner.close()
|
runner.close()
|
||||||
|
|
||||||
def get_spawn(self):
|
def get_spawn(self):
|
||||||
'''Connect to a fresh U-Boot instance.
|
"""Connect to a fresh U-Boot instance.
|
||||||
|
|
||||||
The target board is reset, so that U-Boot begins running from scratch.
|
The target board is reset, so that U-Boot begins running from scratch.
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class ConsoleExecAttach(ConsoleBase):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A u_boot_spawn.Spawn object that is attached to U-Boot.
|
A u_boot_spawn.Spawn object that is attached to U-Boot.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
args = [self.config.board_type, self.config.board_identity]
|
args = [self.config.board_type, self.config.board_identity]
|
||||||
s = Spawn(['u-boot-test-console'] + args)
|
s = Spawn(['u-boot-test-console'] + args)
|
||||||
|
|
|
@ -10,11 +10,11 @@ from u_boot_spawn import Spawn
|
||||||
from u_boot_console_base import ConsoleBase
|
from u_boot_console_base import ConsoleBase
|
||||||
|
|
||||||
class ConsoleSandbox(ConsoleBase):
|
class ConsoleSandbox(ConsoleBase):
|
||||||
'''Represents a connection to a sandbox U-Boot console, executed as a sub-
|
"""Represents a connection to a sandbox U-Boot console, executed as a sub-
|
||||||
process.'''
|
process."""
|
||||||
|
|
||||||
def __init__(self, log, config):
|
def __init__(self, log, config):
|
||||||
'''Initialize a U-Boot console connection.
|
"""Initialize a U-Boot console connection.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
log: A multiplexed_log.Logfile instance.
|
log: A multiplexed_log.Logfile instance.
|
||||||
|
@ -22,12 +22,12 @@ class ConsoleSandbox(ConsoleBase):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
super(ConsoleSandbox, self).__init__(log, config, max_fifo_fill=1024)
|
super(ConsoleSandbox, self).__init__(log, config, max_fifo_fill=1024)
|
||||||
|
|
||||||
def get_spawn(self):
|
def get_spawn(self):
|
||||||
'''Connect to a fresh U-Boot instance.
|
"""Connect to a fresh U-Boot instance.
|
||||||
|
|
||||||
A new sandbox process is created, so that U-Boot begins running from
|
A new sandbox process is created, so that U-Boot begins running from
|
||||||
scratch.
|
scratch.
|
||||||
|
@ -37,26 +37,30 @@ class ConsoleSandbox(ConsoleBase):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A u_boot_spawn.Spawn object that is attached to U-Boot.
|
A u_boot_spawn.Spawn object that is attached to U-Boot.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return Spawn([self.config.build_dir + '/u-boot'])
|
cmd = [
|
||||||
|
self.config.build_dir + '/u-boot',
|
||||||
|
'-d',
|
||||||
|
self.config.build_dir + '/arch/sandbox/dts/test.dtb'
|
||||||
|
]
|
||||||
|
return Spawn(cmd, cwd=self.config.source_dir)
|
||||||
|
|
||||||
def kill(self, sig):
|
def kill(self, sig):
|
||||||
'''Send a specific Unix signal to the sandbox process.
|
"""Send a specific Unix signal to the sandbox process.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sig: The Unix signal to send to the process.
|
sig: The Unix signal to send to the process.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.ensure_spawned()
|
|
||||||
self.log.action('kill %d' % sig)
|
self.log.action('kill %d' % sig)
|
||||||
self.p.kill(sig)
|
self.p.kill(sig)
|
||||||
|
|
||||||
def validate_exited(self):
|
def validate_exited(self):
|
||||||
'''Determine whether the sandbox process has exited.
|
"""Determine whether the sandbox process has exited.
|
||||||
|
|
||||||
If required, this function waits a reasonable time for the process to
|
If required, this function waits a reasonable time for the process to
|
||||||
exit.
|
exit.
|
||||||
|
@ -66,7 +70,7 @@ class ConsoleSandbox(ConsoleBase):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Boolean indicating whether the process has exited.
|
Boolean indicating whether the process has exited.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
p = self.p
|
p = self.p
|
||||||
self.p = None
|
self.p = None
|
||||||
|
|
|
@ -12,23 +12,25 @@ import select
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class Timeout(Exception):
|
class Timeout(Exception):
|
||||||
'''An exception sub-class that indicates that a timeout occurred.'''
|
"""An exception sub-class that indicates that a timeout occurred."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Spawn(object):
|
class Spawn(object):
|
||||||
'''Represents the stdio of a freshly created sub-process. Commands may be
|
"""Represents the stdio of a freshly created sub-process. Commands may be
|
||||||
sent to the process, and responses waited for.
|
sent to the process, and responses waited for.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, args):
|
def __init__(self, args, cwd=None):
|
||||||
'''Spawn (fork/exec) the sub-process.
|
"""Spawn (fork/exec) the sub-process.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
args: array of processs arguments. argv[0] is the command to execute.
|
args: array of processs arguments. argv[0] is the command to
|
||||||
|
execute.
|
||||||
|
cwd: the directory to run the process in, or None for no change.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.waited = False
|
self.waited = False
|
||||||
self.buf = ''
|
self.buf = ''
|
||||||
|
@ -44,6 +46,8 @@ class Spawn(object):
|
||||||
# run under "go" (www.go.cd). Perhaps this happens under any
|
# run under "go" (www.go.cd). Perhaps this happens under any
|
||||||
# background (non-interactive) system?
|
# background (non-interactive) system?
|
||||||
signal.signal(signal.SIGHUP, signal.SIG_DFL)
|
signal.signal(signal.SIGHUP, signal.SIG_DFL)
|
||||||
|
if cwd:
|
||||||
|
os.chdir(cwd)
|
||||||
os.execvp(args[0], args)
|
os.execvp(args[0], args)
|
||||||
except:
|
except:
|
||||||
print 'CHILD EXECEPTION:'
|
print 'CHILD EXECEPTION:'
|
||||||
|
@ -56,26 +60,26 @@ class Spawn(object):
|
||||||
self.poll.register(self.fd, select.POLLIN | select.POLLPRI | select.POLLERR | select.POLLHUP | select.POLLNVAL)
|
self.poll.register(self.fd, select.POLLIN | select.POLLPRI | select.POLLERR | select.POLLHUP | select.POLLNVAL)
|
||||||
|
|
||||||
def kill(self, sig):
|
def kill(self, sig):
|
||||||
'''Send unix signal "sig" to the child process.
|
"""Send unix signal "sig" to the child process.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sig: The signal number to send.
|
sig: The signal number to send.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
os.kill(self.pid, sig)
|
os.kill(self.pid, sig)
|
||||||
|
|
||||||
def isalive(self):
|
def isalive(self):
|
||||||
'''Determine whether the child process is still running.
|
"""Determine whether the child process is still running.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
None.
|
None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Boolean indicating whether process is alive.
|
Boolean indicating whether process is alive.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if self.waited:
|
if self.waited:
|
||||||
return False
|
return False
|
||||||
|
@ -88,19 +92,19 @@ class Spawn(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def send(self, data):
|
def send(self, data):
|
||||||
'''Send data to the sub-process's stdin.
|
"""Send data to the sub-process's stdin.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data: The data to send to the process.
|
data: The data to send to the process.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
os.write(self.fd, data)
|
os.write(self.fd, data)
|
||||||
|
|
||||||
def expect(self, patterns):
|
def expect(self, patterns):
|
||||||
'''Wait for the sub-process to emit specific data.
|
"""Wait for the sub-process to emit specific data.
|
||||||
|
|
||||||
This function waits for the process to emit one pattern from the
|
This function waits for the process to emit one pattern from the
|
||||||
supplied list of patterns, or for a timeout to occur.
|
supplied list of patterns, or for a timeout to occur.
|
||||||
|
@ -116,12 +120,13 @@ class Spawn(object):
|
||||||
Notable exceptions:
|
Notable exceptions:
|
||||||
Timeout, if the process did not emit any of the patterns within
|
Timeout, if the process did not emit any of the patterns within
|
||||||
the expected time.
|
the expected time.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
for pi in xrange(len(patterns)):
|
for pi in xrange(len(patterns)):
|
||||||
if type(patterns[pi]) == type(''):
|
if type(patterns[pi]) == type(''):
|
||||||
patterns[pi] = re.compile(patterns[pi])
|
patterns[pi] = re.compile(patterns[pi])
|
||||||
|
|
||||||
|
tstart_s = time.time()
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
earliest_m = None
|
earliest_m = None
|
||||||
|
@ -131,7 +136,7 @@ class Spawn(object):
|
||||||
m = pattern.search(self.buf)
|
m = pattern.search(self.buf)
|
||||||
if not m:
|
if not m:
|
||||||
continue
|
continue
|
||||||
if earliest_m and m.start() > earliest_m.start():
|
if earliest_m and m.start() >= earliest_m.start():
|
||||||
continue
|
continue
|
||||||
earliest_m = m
|
earliest_m = m
|
||||||
earliest_pi = pi
|
earliest_pi = pi
|
||||||
|
@ -142,7 +147,11 @@ class Spawn(object):
|
||||||
self.after = self.buf[pos:posafter]
|
self.after = self.buf[pos:posafter]
|
||||||
self.buf = self.buf[posafter:]
|
self.buf = self.buf[posafter:]
|
||||||
return earliest_pi
|
return earliest_pi
|
||||||
events = self.poll.poll(self.timeout)
|
tnow_s = time.time()
|
||||||
|
tdelta_ms = (tnow_s - tstart_s) * 1000
|
||||||
|
if tdelta_ms > self.timeout:
|
||||||
|
raise Timeout()
|
||||||
|
events = self.poll.poll(self.timeout - tdelta_ms)
|
||||||
if not events:
|
if not events:
|
||||||
raise Timeout()
|
raise Timeout()
|
||||||
c = os.read(self.fd, 1024)
|
c = os.read(self.fd, 1024)
|
||||||
|
@ -156,7 +165,7 @@ class Spawn(object):
|
||||||
self.logfile_read.flush()
|
self.logfile_read.flush()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
'''Close the stdio connection to the sub-process.
|
"""Close the stdio connection to the sub-process.
|
||||||
|
|
||||||
This also waits a reasonable time for the sub-process to stop running.
|
This also waits a reasonable time for the sub-process to stop running.
|
||||||
|
|
||||||
|
@ -165,7 +174,7 @@ class Spawn(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
os.close(self.fd)
|
os.close(self.fd)
|
||||||
for i in xrange(100):
|
for i in xrange(100):
|
||||||
|
|
|
@ -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