[PATCH v5 2/3] efi_selftest: Add tests for SPI protocol support

Paul Barker
2022-11-23T17:51:00Z

These tests ensure that the new UEFI SPI I/O protocol support works as
expected. They intended to execute on the sandbox target.

Signed-off-by: Paul Barker <paul.barker@sancloud.com>
---
MAINTAINERS | 1 +
arch/sandbox/dts/test.dts | 13 +
lib/efi_selftest/Makefile | 1 +
lib/efi_selftest/efi_selftest_spi_protocol.c | 284 +++++++++++++++++++
4 files changed, 299 insertions(+)
create mode 100644 lib/efi_selftest/efi_selftest_spi_protocol.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 6a82cdbbb474..54742e759e4c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -931,6 +931,7 @@ M: Paul Barker <paul.barker@sancloud.com>
S: Maintained
F: include/efi_spi_protocol.h
F: lib/efi_loader/efi_spi_protocol.c
+F: lib/efi_selftest/efi_selftest_spi_protocol.c

ENVIRONMENT
M: Joe Hershberger <joe.hershberger@ni.com>
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index dffe10adbf47..003af61113a8 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -1253,6 +1253,13 @@
compatible = "spansion,m25p16", "jedec,spi-nor";
spi-max-frequency = <40000000>;
sandbox,filename = "spi.bin";
+
+ u-boot,uefi-spi-vendor = "spansion";
+ u-boot,uefi-spi-part-number = "mt25p16";
+
+ /* GUID in UEFI format: b881eb5d-ad92-4a48-8fdd-fa75a8e4c6b8 */
+ u-boot,uefi-spi-io-guid = [5d eb 81 b8 92 ad 48 4a
+ 8f dd fa 75 a8 e4 c6 b8];
};
spi.bin@1 {
reg = <1>;
@@ -1261,6 +1268,12 @@
sandbox,filename = "spi.bin";
spi-cpol;
spi-cpha;
+
+ u-boot,uefi-spi-vendor = "spansion";
+ u-boot,uefi-spi-part-number = "mt25p16";
+ /* GUID in UEFI format: b6b39ecd-2b1f-a643-b8d7-3192d7cf7270 */
+ u-boot,uefi-spi-io-guid = [cd 9e b3 b6 1f 2b 43 a6
+ b8 d7 31 92 d7 cf 72 70];
};
};

diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
index e4d75420bff6..e2c4fcd40795 100644
--- a/lib/efi_selftest/Makefile
+++ b/lib/efi_selftest/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_EFI_LOADER_HII) += efi_selftest_hii.o
obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_selftest_rng.o
obj-$(CONFIG_EFI_GET_TIME) += efi_selftest_rtc.o
obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_selftest_tcg2.o
+obj-$(CONFIG_EFI_SPI_PROTOCOL) += efi_selftest_spi_protocol.o

ifeq ($(CONFIG_GENERATE_ACPI_TABLE),)
obj-y += efi_selftest_fdt.o
diff --git a/lib/efi_selftest/efi_selftest_spi_protocol.c b/lib/efi_selftest/efi_selftest_spi_protocol.c
new file mode 100644
index 000000000000..946d04dbb557
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_spi_protocol.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2022 Micron Technology, Inc.
+ */
+
+#include <efi_selftest.h>
+#include <efi_spi_protocol.h>
+
+static struct efi_boot_services *boottime;
+static efi_guid_t efi_spi_configuration_guid = EFI_SPI_CONFIGURATION_GUID;
+
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ boottime = systable->boottime;
+ return EFI_ST_SUCCESS;
+}
+
+static int test_peripheral(struct efi_spi_peripheral *p, struct efi_spi_bus *bus)
+{
+ struct efi_spi_io_protocol *io_protocol;
+ u8 req[5], resp[5];
+ efi_status_t ret;
+
+ if (!p->friendly_name) {
+ efi_st_error("SPI peripheral lacks a friendly name\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!p->spi_peripheral_driver_guid) {
+ efi_st_error("SPI peripheral lacks driver GUID\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!p->spi_part) {
+ efi_st_error("SPI peripheral lacks SPI part definition\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!p->max_clock_hz) {
+ efi_st_error("SPI peripheral has a max clock rate of zero\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!p->spi_bus) {
+ efi_st_error("SPI peripheral lack pointer to SPI bus\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (p->spi_bus != bus) {
+ efi_st_error("SPI peripheral spi_bus pointer points to the wrong bus\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!p->chip_select) {
+ efi_st_error("SPI peripheral lacks chip_select function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!p->spi_part->vendor) {
+ efi_st_error("SPI part lacks vendor string\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!p->spi_part->part_number) {
+ efi_st_error("SPI part lacks part number string\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (p->spi_part->min_clock_hz > p->spi_part->max_clock_hz) {
+ efi_st_error("SPI part min clock rate is greater than max clock rate\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (p->spi_part->max_clock_hz != p->max_clock_hz) {
+ efi_st_error("SPI part max clock rate does not match peripheral max clock rate\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = boottime->locate_protocol(p->spi_peripheral_driver_guid,
+ NULL, (void **)&io_protocol);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SPI IO protocol not available\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (io_protocol->spi_peripheral != p) {
+ efi_st_error("SPI IO protocol spi_peripheral pointer points to the wrong peripheral\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (io_protocol->original_spi_peripheral != p) {
+ efi_st_error("SPI IO protocol original_spi_peripheral pointer points to the wrong peripheral\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->maximum_transfer_bytes) {
+ efi_st_error("SPI IO protocol has zero maximum transfer size\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->legacy_spi_protocol) {
+ efi_st_error("SPI IO protocol lacks legacy SPI protocol\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->transaction) {
+ efi_st_error("SPI IO protocol lacks transaction function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->update_spi_peripheral) {
+ efi_st_error("SPI IO protocol lacks update_spi_peripheral function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->legacy_spi_protocol->erase_block_opcode) {
+ efi_st_error("SPI legacy controller protocol lacks erase_block_opcode function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (io_protocol->legacy_spi_protocol->erase_block_opcode(
+ io_protocol->legacy_spi_protocol,
+ 0) != EFI_UNSUPPORTED) {
+ efi_st_error("Incorrect return value from SPI legacy controller protocol erase_block_opcode function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->legacy_spi_protocol->write_status_prefix) {
+ efi_st_error("SPI legacy controller protocol lacks write_status_prefix function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (io_protocol->legacy_spi_protocol->write_status_prefix(
+ io_protocol->legacy_spi_protocol,
+ 0) != EFI_UNSUPPORTED) {
+ efi_st_error("Incorrect return value from SPI legacy controller protocol write_status_prefix function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->legacy_spi_protocol->bios_base_address) {
+ efi_st_error("SPI legacy controller protocol lacks bios_base_address function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (io_protocol->legacy_spi_protocol->bios_base_address(
+ io_protocol->legacy_spi_protocol,
+ 0) != EFI_UNSUPPORTED) {
+ efi_st_error("Incorrect return value from SPI legacy controller protocol bios_base_address function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->legacy_spi_protocol->clear_spi_protect) {
+ efi_st_error("SPI legacy controller protocol lacks clear_spi_protect function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (io_protocol->legacy_spi_protocol->clear_spi_protect(
+ io_protocol->legacy_spi_protocol) != EFI_UNSUPPORTED) {
+ efi_st_error("Incorrect return value from SPI legacy controller protocol clear_spi_protect function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->legacy_spi_protocol->is_range_protected) {
+ efi_st_error("SPI legacy controller protocol lacks is_range_protected function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (io_protocol->legacy_spi_protocol->is_range_protected(
+ io_protocol->legacy_spi_protocol,
+ 0, 0)) {
+ efi_st_error("Incorrect return value from SPI legacy controller protocol is_range_protected function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->legacy_spi_protocol->protect_next_range) {
+ efi_st_error("SPI legacy controller protocol lacks protect_next_range function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (io_protocol->legacy_spi_protocol->protect_next_range(
+ io_protocol->legacy_spi_protocol,
+ 0, 0) != EFI_UNSUPPORTED) {
+ efi_st_error("Incorrect return value from SPI legacy controller protocol protect_next_range function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!io_protocol->legacy_spi_protocol->lock_controller) {
+ efi_st_error("SPI legacy controller protocol lacks lock_controller function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (io_protocol->legacy_spi_protocol->lock_controller(
+ io_protocol->legacy_spi_protocol) != EFI_UNSUPPORTED) {
+ efi_st_error("Incorrect return value from SPI legacy controller protocol lock_controller function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ req[0] = 0x9f;
+ ret = io_protocol->transaction(io_protocol,
+ SPI_TRANSACTION_FULL_DUPLEX,
+ false, 0, 1, 8,
+ sizeof(req), req,
+ sizeof(resp), resp);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SPI transaction failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if ((resp[0] != 0xff) || (resp[1] != 0x20) || (resp[2] != 0x20) || (resp[3] != 0x15)) {
+ efi_st_error("Incorrect response from sandbox SPI flash emulator\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int test_bus(struct efi_spi_bus *bus)
+{
+ struct efi_spi_peripheral *p;
+ int status;
+
+ if (!bus->friendly_name) {
+ efi_st_error("SPI bus lacks a friendly name\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!bus->peripheral_list) {
+ efi_st_error("SPI bus has zero peripherals\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!bus->clock) {
+ efi_st_error("SPI bus lacks clock function\n");
+ return EFI_ST_FAILURE;
+ }
+
+ for (p = bus->peripheral_list; p; p = p->next_spi_peripheral) {
+ status = test_peripheral(p, bus);
+ if (status) {
+ efi_st_error("Failed testing SPI peripheral\n");
+ return status;
+ }
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+static int execute(void)
+{
+ struct efi_spi_configuration_protocol *spi;
+ efi_status_t ret;
+ int status;
+ u32 i;
+
+ ret = boottime->locate_protocol(&efi_spi_configuration_guid,
+ NULL, (void **)&spi);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SPI configuration protocol not available\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (!spi->bus_count) {
+ efi_st_error("SPI configuration protocol has zero busses\n");
+ return EFI_ST_FAILURE;
+ }
+
+ for (i = 0; i < spi->bus_count; i++) {
+ status = test_bus(spi->bus_list[i]);
+ if (status) {
+ efi_st_error("Failed testing SPI bus %d\n", i);
+ return status;
+ }
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(spi_protocol) = {
+ .name = "SPI protocol",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
--
2.25.1