~kameliya/u-boot

7ab3ecde2695f34d4a2b778ce386c1cdb5d837de — Asherah Connor 2 months ago 9d18215
test: qemu: add qfw sandbox driver, dm tests, qemu tests

A sandbox driver and test are added for the qfw uclass, and a test in
QEMU added for qfw functionality to confirm it doesn't break in real
world use.

Signed-off-by: Asherah Connor <ashe@kivikakk.ee>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

Series-changes: 8
* Removed extraneous newline at EOF.

Series-changes: 7
* Make the qfw tests agnostic to running against a real QEMU instance or
  in the sandbox.
* Remove CONFIG_CMD_QFW=y from sandbox_spl_defconfig, per trini's
  request.
M configs/sandbox_spl_defconfig => configs/sandbox_spl_defconfig +0 -1
@@ 72,7 72,6 @@ CONFIG_CMD_EFIDEBUG=y
CONFIG_CMD_TIME=y
CONFIG_CMD_TIMER=y
CONFIG_CMD_SOUND=y
CONFIG_CMD_QFW=y
CONFIG_CMD_BOOTSTAGE=y
CONFIG_CMD_PMIC=y
CONFIG_CMD_REGULATOR=y

M drivers/misc/Makefile => drivers/misc/Makefile +1 -0
@@ 58,6 58,7 @@ obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o
ifdef CONFIG_QFW
obj-y += qfw.o
obj-$(CONFIG_QFW_PIO) += qfw_pio.o
obj-$(CONFIG_SANDBOX) += qfw_sandbox.o
endif
obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o
obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o

A drivers/misc/qfw_sandbox.c => drivers/misc/qfw_sandbox.c +127 -0
@@ 0,0 1,127 @@
// SPDX-License-Identifier: GPL-2.0+
/*
 * Sandbox interface for QFW
 *
 * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
 * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee>
 */

#define LOG_CATEGORY UCLASS_QFW

#include <asm/types.h>
#include <asm/io.h>
#include <dm.h>
#include <dm/device.h>
#include <qfw.h>

struct qfw_sandbox_plat {
	u8 file_dir_offset;
};

static void qfw_sandbox_read_entry_io(struct udevice *dev, u16 entry, u32 size,
				      void *address)
{
	debug("%s: entry 0x%x size %u address %p\n", __func__, entry, size,
	      address);

	switch (entry) {
	case FW_CFG_SIGNATURE:
		if (size == 4)
			*((u32 *)address) = cpu_to_be32(QEMU_FW_CFG_SIGNATURE);
		break;
	case FW_CFG_ID:
		/* Advertise DMA support */
		if (size == 1)
			*((u8 *)address) = FW_CFG_DMA_ENABLED;
		break;
	default:
		debug("%s got unsupported entry 0x%x\n", __func__, entry);
	/*
	 * Sandbox driver doesn't support other entries here, assume we use DMA
	 * to read them -- the uclass driver will exclusively use it when
	 * advertised.
	 */
	}
}

static void qfw_sandbox_read_entry_dma(struct udevice *dev, struct qfw_dma *dma)
{
	u16 entry;
	u32 control = be32_to_cpu(dma->control);
	void *address = (void *)be64_to_cpu(dma->address);
	u32 length = be32_to_cpu(dma->length);
	struct qfw_sandbox_plat *plat = dev_get_plat(dev);
	struct fw_cfg_file *file;

	debug("%s\n", __func__);

	if (!(control & FW_CFG_DMA_READ))
		return;

	if (control & FW_CFG_DMA_SELECT) {
		/* Start new read. */
		entry = control >> 16;

		/* Arbitrary values to be used by tests. */
		switch (entry) {
		case FW_CFG_NB_CPUS:
			if (length == 2)
				*((u16 *)address) = cpu_to_le16(5);
			break;
		case FW_CFG_FILE_DIR:
			if (length == 4) {
				*((u32 *)address) = cpu_to_be32(2);
				plat->file_dir_offset = 1;
			}
			break;
		default:
			debug("%s got unsupported entry 0x%x\n", __func__,
			      entry);
		}
	} else if (plat->file_dir_offset && length == 64) {
		file = address;
		switch (plat->file_dir_offset) {
		case 1:
			file->size = cpu_to_be32(8);
			file->select = cpu_to_be16(FW_CFG_FILE_FIRST);
			strcpy(file->name, "test-one");
			plat->file_dir_offset++;
			break;
		case 2:
			file->size = cpu_to_be32(8);
			file->select = cpu_to_be16(FW_CFG_FILE_FIRST + 1);
			strcpy(file->name, "test-two");
			plat->file_dir_offset++;
			break;
		}
	}

	/*
	 * Signal that we are finished. No-one checks this in sandbox --
	 * normally the platform-specific driver looks for it -- but let's
	 * replicate the behaviour in case someone relies on it later.
	 */
	dma->control = 0;
}

static int qfw_sandbox_probe(struct udevice *dev)
{
	return qfw_register(dev);
}

static struct dm_qfw_ops qfw_sandbox_ops = {
	.read_entry_io = qfw_sandbox_read_entry_io,
	.read_entry_dma = qfw_sandbox_read_entry_dma,
};

U_BOOT_DRIVER(qfw_sandbox) = {
	.name	= "qfw_sandbox",
	.id	= UCLASS_QFW,
	.plat_auto	= sizeof(struct qfw_sandbox_plat),
	.probe	= qfw_sandbox_probe,
	.ops	= &qfw_sandbox_ops,
};

U_BOOT_DRVINFO(qfw_sandbox) = {
	.name = "qfw_sandbox",
};

M test/dm/Makefile => test/dm/Makefile +1 -0
@@ 98,5 98,6 @@ endif
ifneq ($(CONFIG_EFI_PARTITION),)
obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fastboot.o
endif
obj-$(CONFIG_QFW) += qfw.o
endif
endif # !SPL

A test/dm/qfw.c => test/dm/qfw.c +42 -0
@@ 0,0 1,42 @@
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2021 Asherah Connor <ashe@kivikakk.ee>
 */

#include <common.h>
#include <qfw.h>
#include <dm.h>
#include <asm/test.h>
#include <dm/test.h>
#include <test/ut.h>

/*
 * Exercise the device enough to be satisfied the initialisation and DMA
 * interfaces work.
 */

static int dm_test_qfw_cpus(struct unit_test_state *uts)
{
	struct udevice *dev;

	ut_assertok(uclass_first_device_err(UCLASS_QFW, &dev));
	ut_asserteq(5, qfw_online_cpus(dev));

	return 0;
}

DM_TEST(dm_test_qfw_cpus, UT_TESTF_SCAN_PDATA);

static int dm_test_qfw_firmware_list(struct unit_test_state *uts)
{
	struct udevice *dev;
	struct fw_file *file;

	ut_assertok(uclass_first_device_err(UCLASS_QFW, &dev));
	ut_assertok(qfw_read_firmware_list(dev));
	ut_assertok_ptr((file = qfw_find_file(dev, "test-one")));

	return 0;
}

DM_TEST(dm_test_qfw_firmware_list, UT_TESTF_SCAN_PDATA);

A test/py/tests/test_qfw.py => test/py/tests/test_qfw.py +26 -0
@@ 0,0 1,26 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2021, Asherah Connor <ashe@kivikakk.ee>

# Test qfw command implementation

import pytest

@pytest.mark.buildconfigspec('cmd_qfw')
def test_qfw_cpus(u_boot_console):
    "Test QEMU firmware config reports the CPU count."

    output = u_boot_console.run_command('qfw cpus')
    # The actual number varies depending on the board under test, so only
    # assert a non-zero output.
    assert 'cpu(s) online' in output
    assert '0 cpu(s) online' not in output

@pytest.mark.buildconfigspec('cmd_qfw')
def test_qfw_list(u_boot_console):
    "Test QEMU firmware config lists devices."

    output = u_boot_console.run_command('qfw list')
    # Assert either:
    # 1) 'test-one', from the sandbox driver, or
    # 2) 'bootorder', found in every real QEMU implementation.
    assert ("bootorder" in output) or ("test-one" in output)