sandbox UCLASS_HOST

-----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAmNplEARHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIreab6QgAgPuNqE2Rs7lVRY97KO+MvXWE+SqpzULT
 9FbFcKRE/kVb38ty0VbwDKbaBy+BeGPZaBw3TT5D/aXgRVLur+1UNGNC51XLxQx2
 TxMSLJcXB9bxHJgEMh+uAiUp0CZg2lS82E6bsANSv/Zfl0tV7YTy9ocjynxgiO0m
 79FdkM0GhWJAAB5rqpeu0oEu9hr0GzqmmmI6WGVS9eh+ontkJO+IU3FdJTre8tbJ
 Hkdi9o31qQ1ZcCigQMoW/PuKXHvhiNqp3ex5RXJTgTDWm4tDyPbjvvWwrBlYZQUa
 ToI0b5GaP96aVtERNDShyStkZevPf4jm7M5RrukRJXdAYuUg0pXY9g==
 =dI/E
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-7nov22' of https://source.denx.de/u-boot/custodians/u-boot-dm

sandbox UCLASS_HOST
This commit is contained in:
Tom Rini 2022-11-08 09:45:10 -05:00
commit 77b5cc2948
36 changed files with 1458 additions and 551 deletions

View File

@ -132,7 +132,7 @@ void spl_board_init(void)
int ret;
ret = ut_run_list("spl", NULL, tests, count,
state->select_unittests, 1, false);
state->select_unittests, 1, false, NULL);
/* continue execution into U-Boot */
}
}

View File

@ -68,10 +68,6 @@
reg = <0x10002000 0x1000>;
};
host-fs {
compatible = "sandbox,bootdev-host";
};
i2c_0: i2c@0 {
#address-cells = <1>;
#size-cells = <0>;

View File

@ -8,12 +8,12 @@
#include <dm.h>
#include <fs.h>
#include <part.h>
#include <sandboxblockdev.h>
#include <sandbox_host.h>
#include <dm/device_compat.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <linux/errno.h>
static int host_curr_device = -1;
static int do_host_load(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
@ -42,10 +42,10 @@ static int do_host_bind(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
bool removable = false;
const char *dev_str;
struct udevice *dev;
const char *label;
char *file;
char *ep;
int dev;
int ret;
/* Skip 'bind' */
argc--;
@ -61,101 +61,158 @@ static int do_host_bind(struct cmd_tbl *cmdtp, int flag, int argc,
if (argc > 2)
return CMD_RET_USAGE;
dev_str = argv[0];
dev = hextoul(dev_str, &ep);
if (*ep) {
printf("** Bad device specification %s **\n", dev_str);
return CMD_RET_USAGE;
}
label = argv[0];
file = argc > 1 ? argv[1] : NULL;
return !!host_dev_bind(dev, file, removable);
ret = host_create_attach_file(label, file, removable, &dev);
if (ret) {
printf("Cannot create device / bind file\n");
return CMD_RET_FAILURE;
}
return 0;
}
/**
* parse_host_label() - Parse a device label or sequence number
*
* This shows an error if it returns NULL
*
* @label: String containing the label or sequence number
* Returns: Associated device, or NULL if not found
*/
static struct udevice *parse_host_label(const char *label)
{
struct udevice *dev;
dev = host_find_by_label(label);
if (!dev) {
int devnum;
char *ep;
devnum = hextoul(label, &ep);
if (*ep ||
uclass_find_device_by_seq(UCLASS_HOST, devnum, &dev)) {
printf("No such device '%s'\n", label);
return NULL;
}
}
return dev;
}
static int do_host_unbind(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
struct udevice *dev;
const char *label;
int ret;
if (argc < 2)
return CMD_RET_USAGE;
label = argv[1];
dev = parse_host_label(label);
if (!dev)
return CMD_RET_FAILURE;
ret = host_detach_file(dev);
if (ret) {
printf("Cannot detach file (err=%d)\n", ret);
return CMD_RET_FAILURE;
}
ret = device_unbind(dev);
if (ret) {
printf("Cannot attach file\n");
ret = device_unbind(dev);
if (ret)
printf("Cannot unbind device '%s'\n", dev->name);
return CMD_RET_FAILURE;
}
return 0;
}
static void show_host_dev(struct udevice *dev)
{
struct host_sb_plat *plat = dev_get_plat(dev);
struct blk_desc *desc;
struct udevice *blk;
int ret;
printf("%3d ", dev_seq(dev));
if (!plat->fd) {
printf("Not bound to a backing file\n");
return;
}
ret = blk_get_from_parent(dev, &blk);
if (ret) /* cannot happen */
return;
desc = dev_get_uclass_plat(blk);
printf("%12lu %-15s %s\n", (unsigned long)desc->lba, plat->label,
plat->filename);
}
static int do_host_info(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
if (argc < 1 || argc > 2)
struct udevice *dev;
if (argc < 1)
return CMD_RET_USAGE;
int min_dev = 0;
int max_dev = SANDBOX_HOST_MAX_DEVICES - 1;
dev = NULL;
if (argc >= 2) {
char *ep;
char *dev_str = argv[1];
int dev = hextoul(dev_str, &ep);
if (*ep) {
printf("** Bad device specification %s **\n", dev_str);
return CMD_RET_USAGE;
}
min_dev = dev;
max_dev = dev;
dev = parse_host_label(argv[1]);
if (!dev)
return CMD_RET_FAILURE;
}
int dev;
printf("%3s %12s %s\n", "dev", "blocks", "path");
for (dev = min_dev; dev <= max_dev; dev++) {
struct blk_desc *blk_dev;
int ret;
printf("%3d ", dev);
ret = host_get_dev_err(dev, &blk_dev);
if (ret) {
if (ret == -ENOENT)
puts("Not bound to a backing file\n");
else if (ret == -ENODEV)
puts("Invalid host device number\n");
printf("%3s %12s %-15s %s\n", "dev", "blocks", "label", "path");
if (dev) {
show_host_dev(dev);
} else {
struct uclass *uc;
continue;
}
struct host_block_dev *host_dev;
#ifdef CONFIG_BLK
host_dev = dev_get_plat(blk_dev->bdev);
#else
host_dev = blk_dev->priv;
#endif
printf("%12lu %s\n", (unsigned long)blk_dev->lba,
host_dev->filename);
uclass_id_foreach_dev(UCLASS_HOST, dev, uc)
show_host_dev(dev);
}
return 0;
}
static int do_host_dev(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
int dev;
char *ep;
struct blk_desc *blk_dev;
int ret;
struct udevice *dev;
const char *label;
if (argc < 1 || argc > 3)
return CMD_RET_USAGE;
if (argc == 1) {
if (host_curr_device < 0) {
struct host_sb_plat *plat;
dev = host_get_cur_dev();
if (!dev) {
printf("No current host device\n");
return 1;
return CMD_RET_FAILURE;
}
printf("Current host device %d\n", host_curr_device);
plat = dev_get_plat(dev);
printf("Current host device: %d: %s\n", dev_seq(dev),
plat->label);
return 0;
}
dev = hextoul(argv[1], &ep);
if (*ep) {
printf("** Bad device specification %s **\n", argv[2]);
return CMD_RET_USAGE;
}
label = argv[1];
dev = parse_host_label(argv[1]);
if (!dev)
return CMD_RET_FAILURE;
ret = host_get_dev_err(dev, &blk_dev);
if (ret) {
if (ret == -ENOENT)
puts("Not bound to a backing file\n");
else if (ret == -ENODEV)
puts("Invalid host device number\n");
host_set_cur_dev(dev);
return 1;
}
host_curr_device = dev;
return 0;
}
@ -165,6 +222,7 @@ static struct cmd_tbl cmd_host_sub[] = {
U_BOOT_CMD_MKENT(save, 6, 0, do_host_save, "", ""),
U_BOOT_CMD_MKENT(size, 3, 0, do_host_size, "", ""),
U_BOOT_CMD_MKENT(bind, 4, 0, do_host_bind, "", ""),
U_BOOT_CMD_MKENT(unbind, 4, 0, do_host_unbind, "", ""),
U_BOOT_CMD_MKENT(info, 3, 0, do_host_info, "", ""),
U_BOOT_CMD_MKENT(dev, 0, 1, do_host_dev, "", ""),
};
@ -178,8 +236,7 @@ static int do_host(struct cmd_tbl *cmdtp, int flag, int argc,
argc--;
argv++;
c = find_cmd_tbl(argv[0], cmd_host_sub,
ARRAY_SIZE(cmd_host_sub));
c = find_cmd_tbl(argv[0], cmd_host_sub, ARRAY_SIZE(cmd_host_sub));
if (c)
return c->cmd(cmdtp, flag, argc, argv);
@ -196,10 +253,11 @@ U_BOOT_CMD(
"host save hostfs - <addr> <filename> <bytes> [<offset>] - "
"save a file to host\n"
"host size hostfs - <filename> - determine size of file on host\n"
"host bind [-r] <dev> [<filename>] - bind \"host\" device to file\n"
"host bind [-r] <label> [<filename>] - bind \"host\" device to file\n"
" -r = mark as removable\n"
"host info [<dev>] - show device binding & info\n"
"host dev [<dev>] - Set or retrieve the current host device\n"
"host unbind <label> - unbind file from \"host\" device\n"
"host info [<label>] - show device binding & info\n"
"host dev [<label>] - set or retrieve the current host device\n"
"host commands use the \"hostfs\" device. The \"host\" device is used\n"
"with standard IO commands such as fatls or ext2load"
);

View File

@ -139,7 +139,7 @@ void dev_print(struct blk_desc *dev_desc)
case UCLASS_USB:
case UCLASS_NVME:
case UCLASS_PVBLOCK:
case UCLASS_ROOT:
case UCLASS_HOST:
printf ("Vendor: %s Rev: %s Prod: %s\n",
dev_desc->vendor,
dev_desc->revision,
@ -264,7 +264,7 @@ static void print_part_header(const char *type, struct blk_desc *dev_desc)
case UCLASS_MMC:
puts ("MMC");
break;
case UCLASS_ROOT:
case UCLASS_HOST:
puts ("HOST");
break;
case UCLASS_NVME:

View File

@ -11,7 +11,7 @@ Architecture-specific doc
m68k
mips
nios2
sandbox
sandbox/index
sh
x86
xtensa

View File

@ -0,0 +1,39 @@
.. SPDX-License-Identifier: GPL-2.0+ */
.. Copyright (c) 2014 The Chromium OS Authors.
.. sectionauthor:: Simon Glass <sjg@chromium.org>
Sandbox block devices (implementation)
======================================
(See :ref:`sandbox_blk` for operation)
Sandbox block devices are implemented using the `UCLASS_HOST` uclass. Only one
driver is provided (`host_sb_drv`) so all devices in the uclass use the same
driver.
The uclass has a simple API allowing files to be attached and detached.
Attaching a file results in it appearing as a block device in sandbox. This
allows filesystems and whole disk images to be accessed from U-Boot. This is
particularly useful for tests.
Devices are created using `host_create_device()`. This sets up a new
`UCLASS_HOST`.
The device can then be attached to a file with `host_attach_file()`. This
creates the child block device (and bootdev device).
The host device's block device must be probed before use, as normal.
To destroy a device, call host_destroy_device(). This removes the device (and
its children of course), then closes any attached file, then unbinds the device.
There is no arbitrary limit to the number of host devices that can be created.
Uclass API
----------
This is incomplete as it isn't clear how to make Sphinx do the right thing for
struct host_ops. See `include/sandbox_host.h` for full details.
.. kernel-doc:: include/sandbox_host.h

View File

@ -0,0 +1,12 @@
.. SPDX-License-Identifier: GPL-2.0+ */
.. Copyright 2022 Google LLC
.. sectionauthor:: Simon Glass <sjg@chromium.org>
Sandbox
=======
.. toctree::
:maxdepth: 2
sandbox
block_impl

View File

@ -43,7 +43,7 @@ Note that standalone/API support is not available at present.
Prerequisites
-------------
Install the dependencies noted in :doc:`../build/gcc`.
Install the dependencies noted in :doc:`../../build/gcc`.
Basic Operation
@ -374,6 +374,7 @@ also use low-level SPI commands::
This is issuing a READ_ID command and getting back 20 (ST Micro) part
0x2015 (the M25P16).
.. _sandbox_blk:
Block Device Emulation
----------------------
@ -401,6 +402,8 @@ or utilize the device described in test/py/make_test_disk.py::
import make_test_disk
make_test_disk.makeDisk()
For more technical details, see :doc:`block_impl`.
Writing Sandbox Drivers
-----------------------
@ -600,8 +603,8 @@ Testing
U-Boot sandbox can be used to run various tests, mostly in the test/
directory.
See :doc:`../develop/tests_sandbox` for more information and
:doc:`../develop/testing` for information about testing generally.
See :doc:`../../develop/tests_sandbox` for more information and
:doc:`../../develop/testing` for information about testing generally.
Memory Map

View File

@ -143,6 +143,75 @@ For example::
Test dm_test_rtc_reset failed 3 times
Isolating a test that breaks another
------------------------------------
When running unit tests, some may have side effects which cause a subsequent
test to break. This can sometimes be seen when using 'ut dm' or similar.
You can use the `-I` argument to the `ut` command to isolate this problem.
First use `ut info` to see how many tests there are, then use a binary search to
home in on the problem. Note that you might need to restart U-Boot after each
iteration, so the `-c` argument to U-Boot is useful.
For example, let's stay that dm_test_host() is failing::
=> ut dm
...
Test: dm_test_get_stats: core.c
Test: dm_test_get_stats: core.c (flat tree)
Test: dm_test_host: host.c
test/dm/host.c:71, dm_test_host(): 0 == ut_check_delta(mem_start): Expected 0x0 (0), got 0xffffcbb0 (-13392)
Test: dm_test_host: host.c (flat tree)
Test <NULL> failed 1 times
Test: dm_test_host_dup: host.c
Test: dm_test_host_dup: host.c (flat tree)
...
You can then tell U-Boot to run the failing test at different points in the
sequence:
=> ut info
Test suites: 21
Total tests: 645
::
$ ./u-boot -T -c "ut dm -I300:dm_test_host"
...
Test: dm_test_pinctrl_single: pinmux.c (flat tree)
Test: dm_test_host: host.c
test/dm/host.c:71, dm_test_host(): 0 == ut_check_delta(mem_start): Expected 0x0 (0), got 0xfffffdb0 (-592)
Test: dm_test_host: host.c (flat tree)
Test dm_test_host failed 1 times (position 300)
Failures: 4
So it happened before position 300. Trying 150 shows it failing, so we try 75::
$ ./u-boot -T -c "ut dm -I75:dm_test_host"
...
Test: dm_test_autoprobe: core.c
Test: dm_test_autoprobe: core.c (flat tree)
Test: dm_test_host: host.c
Test: dm_test_host: host.c (flat tree)
Failures: 0
That succeeds, so we try 120, etc. until eventually we can figure out that the
problem first happens at position 82.
$ ./u-boot -T -c "ut dm -I82:dm_test_host"
...
Test: dm_test_blk_flags: blk.c
Test: dm_test_blk_flags: blk.c (flat tree)
Test: dm_test_host: host.c
test/dm/host.c:71, dm_test_host(): 0 == ut_check_delta(mem_start): Expected 0x0 (0), got 0xffffc960 (-13984)
Test: dm_test_host: host.c (flat tree)
Test dm_test_host failed 1 times (position 82)
Failures: 1
From this we can deduce that `dm_test_blk_flags()` causes the problem with
`dm_test_host()`.
Running sandbox_spl tests directly
----------------------------------

116
doc/usage/cmd/host.rst Normal file
View File

@ -0,0 +1,116 @@
.. SPDX-License-Identifier: GPL-2.0+
host command
============
Synopis
-------
::
host bind [-r] <label> [<filename>]
host unbind <label|seq>
host info [<label|seq>]
host dev [<label|seq>]
Description
-----------
The host command provides a way to attach disk images on the host to U-Boot
sandbox. This can be useful for testing U-Boot's filesystem implementations.
Common arguments:
<label|seq>
This is used to specify a host device. It can either be a label (a string)
or the sequence number of the device. An invalid value causes the command
to fail.
host bind
~~~~~~~~~
This creates a new host device and binds a file to it.
Arguments:
label
Label to use to identify this binding. This can be any string.
filename:
Host filename to bind to
Flags:
-r
Mark the device as removable
host unbind
~~~~~~~~~~~
This unbinds a host device that was previously bound. The sequence numbers of
other devices remain unchanged.
host info
~~~~~~~~~
Provides information about a particular host binding, or all of them.
host dev
~~~~~~~~
Allowing selecting a particular device, or (with no arguments) seeing which one
is selected.
Example
-------
Initially there are no devices::
=> host info
dev blocks label path
Bind a device::
=> host bind -r test2 2MB.ext2.img
=> host bind fat 1MB.fat32.img
=> host info
dev blocks label path
0 4096 test2 2MB.ext2.img
1 2048 fat 1MB.fat32.img
Select a device by label or sequence number::
=> host dev fat
Current host device: 1: fat
=> host dev 0
Current host device: 0: test2
Write a file::
=> ext4write host 0 0 /dump 1e00
File System is consistent
7680 bytes written in 3 ms (2.4 MiB/s)
=> ext4ls host 0
<DIR> 4096 .
<DIR> 4096 ..
<DIR> 16384 lost+found
4096 testing
7680 dump
Unbind a device::
=> host unbind test2
=> host info
dev blocks label path
1 2048 fat 1MB.fat32.img
Return value
------------
The return value $? indicates whether the command succeeded.

117
doc/usage/cmd/ut.rst Normal file
View File

@ -0,0 +1,117 @@
.. SPDX-License-Identifier: GPL-2.0+:
ut command
==========
Synopis
-------
::
ut [-r<runs>] [-f] [-I<n>:<one_test>] [<suite> [<test>]]
<runs> Number of times to run each test
-f Force 'manual' tests to run as well
<n> Run <one test> after <n> other tests have run
<one_test> Name of the 'one' test to run
<suite> Test suite to run, or `all`
<test> Name of single test to run
Description
-----------
The ut command runs unit tests written in C.
Typically the command is run on :ref:`arch/sandbox/sandbox:sandbox` since it
includes a near-complete set of emulators, no code-size limits, many CONFIG
options enabled and runs easily in CI without needing QEMU. It is also possible
to run some tests on real boards.
For a list of available test suites, type `ut` by itself.
Each test is normally run once, although those marked with `UT_TESTF_DM` are
run with livetree and flattree where possible. To run a test more than once,
use the `-r` flag.
Manual tests are normally skipped by this command. Use `-f` to run them. See
See :ref:`develop/tests_writing:mixing python and c` for more information on
manual test.
When running unit tests, some may have side effects which cause a subsequent
test to break. This can sometimes be seen when using 'ut dm' or similar. To
fix this, select the 'one' test which breaks. Then tell the 'ut' command to
run this one test after a certain number of other tests have run. Using a
binary search method with `-I` you can quickly figure one which test is causing
the problem.
Generally all tests in the suite are run. To run just a single test from the
suite, provide the <test> argument.
See :ref:`develop/tests_writing:writing c tests` for more information on how to
write unit tests.
Example
-------
List available unit-test suites::
=> ut
ut - unit tests
Usage:
ut [-r] [-f] [<suite>] - run unit tests
-r<runs> Number of times to run each test
-f Force 'manual' tests to run as well
<suite> Test suite to run, or all
Suites:
all - execute all enabled tests
addrmap - very basic test of addrmap command
bloblist - bloblist implementation
bootstd - standard boot implementation
compression - compressors and bootm decompression
dm - driver model
env - environment
fdt - fdt command
loadm - loadm command parameters and loading memory blob
lib - library functions
log - logging functions
mem - memory-related commands
overlay - device tree overlays
print - printing things to the console
setexpr - setexpr command
str - basic test of string functions
time - very basic test of time functions
unicode - Unicode functions
Run one of the suites::
=> ut bloblist
Running 14 bloblist tests
Test: bloblist_test_align: bloblist.c
Test: bloblist_test_bad_blob: bloblist.c
Test: bloblist_test_blob: bloblist.c
Test: bloblist_test_blob_ensure: bloblist.c
Test: bloblist_test_blob_maxsize: bloblist.c
Test: bloblist_test_checksum: bloblist.c
Test: bloblist_test_cmd_info: bloblist.c
Test: bloblist_test_cmd_list: bloblist.c
Test: bloblist_test_grow: bloblist.c
Test: bloblist_test_init: bloblist.c
Test: bloblist_test_reloc: bloblist.c
Test: bloblist_test_resize_fail: bloblist.c
Test: bloblist_test_resize_last: bloblist.c
Test: bloblist_test_shrink: bloblist.c
Failures: 0
Run just a single test in a suite::
=> ut bloblist bloblist_test_grow
Test: bloblist_test_grow: bloblist.c
Failures: 0
Show information about tests::
=> ut info
Test suites: 21
Total tests: 642

View File

@ -52,6 +52,7 @@ Shell commands
cmd/for
cmd/fwu_mdata
cmd/gpio
cmd/host
cmd/load
cmd/loadm
cmd/loady
@ -74,6 +75,7 @@ Shell commands
cmd/tftpput
cmd/true
cmd/ums
cmd/ut
cmd/wdt
cmd/xxd

View File

@ -12,7 +12,7 @@ endif
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_IDE) += ide.o
endif
obj-$(CONFIG_SANDBOX) += sandbox.o
obj-$(CONFIG_SANDBOX) += sandbox.o host-uclass.o host_dev.o
obj-$(CONFIG_$(SPL_TPL_)BLOCK_CACHE) += blkcache.o
obj-$(CONFIG_EFI_MEDIA) += efi-media-uclass.o

View File

@ -26,7 +26,7 @@ static struct {
{ UCLASS_USB, "usb" },
{ UCLASS_MMC, "mmc" },
{ UCLASS_AHCI, "sata" },
{ UCLASS_ROOT, "host" },
{ UCLASS_HOST, "host" },
{ UCLASS_NVME, "nvme" },
{ UCLASS_EFI_MEDIA, "efi" },
{ UCLASS_EFI_LOADER, "efiloader" },
@ -369,45 +369,43 @@ int blk_dselect_hwpart(struct blk_desc *desc, int hwpart)
return blk_select_hwpart(desc->bdev, hwpart);
}
int blk_first_device(int uclass_id, struct udevice **devp)
static int _blk_next_device(int uclass_id, struct udevice **devp)
{
struct blk_desc *desc;
int ret;
int ret = 0;
for (; *devp; uclass_find_next_device(devp)) {
desc = dev_get_uclass_plat(*devp);
if (desc->uclass_id == uclass_id) {
ret = device_probe(*devp);
if (!ret)
return 0;
}
}
ret = uclass_find_first_device(UCLASS_BLK, devp);
if (ret)
return ret;
if (!*devp)
return -ENODEV;
do {
desc = dev_get_uclass_plat(*devp);
if (desc->uclass_id == uclass_id)
return 0;
ret = uclass_find_next_device(devp);
if (ret)
return ret;
} while (*devp);
return -ENODEV;
}
int blk_first_device(int uclass_id, struct udevice **devp)
{
uclass_find_first_device(UCLASS_BLK, devp);
return _blk_next_device(uclass_id, devp);
}
int blk_next_device(struct udevice **devp)
{
struct blk_desc *desc;
int ret, uclass_id;
int uclass_id;
desc = dev_get_uclass_plat(*devp);
uclass_id = desc->uclass_id;
do {
ret = uclass_find_next_device(devp);
if (ret)
return ret;
if (!*devp)
return -ENODEV;
desc = dev_get_uclass_plat(*devp);
if (desc->uclass_id == uclass_id)
return 0;
} while (1);
uclass_find_next_device(devp);
return _blk_next_device(uclass_id, devp);
}
int blk_find_device(int uclass_id, int devnum, struct udevice **devp)
@ -508,24 +506,28 @@ ulong blk_derase(struct blk_desc *desc, lbaint_t start, lbaint_t blkcnt)
return blk_erase(desc->bdev, start, blkcnt);
}
int blk_get_from_parent(struct udevice *parent, struct udevice **devp)
int blk_find_from_parent(struct udevice *parent, struct udevice **devp)
{
struct udevice *dev;
enum uclass_id id;
int ret;
device_find_first_child(parent, &dev);
if (!dev) {
if (device_find_first_child_by_uclass(parent, UCLASS_BLK, &dev)) {
debug("%s: No block device found for parent '%s'\n", __func__,
parent->name);
return -ENODEV;
}
id = device_get_uclass_id(dev);
if (id != UCLASS_BLK) {
debug("%s: Incorrect uclass %s for block device '%s'\n",
__func__, uclass_get_name(id), dev->name);
return -ENOTBLK;
}
*devp = dev;
return 0;
}
int blk_get_from_parent(struct udevice *parent, struct udevice **devp)
{
struct udevice *dev;
int ret;
ret = blk_find_from_parent(parent, &dev);
if (ret)
return ret;
ret = device_probe(dev);
if (ret)
return ret;

View File

@ -150,8 +150,8 @@ void blkcache_invalidate(int iftype, int devnum)
list_for_each_safe(entry, n, &block_cache) {
node = (struct block_cache_node *)entry;
if ((node->iftype == iftype) &&
(node->devnum == devnum)) {
if (iftype == -1 ||
(node->iftype == iftype && node->devnum == devnum)) {
list_del(entry);
free(node->cache);
free(node);
@ -162,18 +162,10 @@ void blkcache_invalidate(int iftype, int devnum)
void blkcache_configure(unsigned blocks, unsigned entries)
{
struct block_cache_node *node;
/* invalidate cache if there is a change */
if ((blocks != _stats.max_blocks_per_entry) ||
(entries != _stats.max_entries)) {
/* invalidate cache */
while (!list_empty(&block_cache)) {
node = (struct block_cache_node *)block_cache.next;
list_del(&node->lh);
free(node->cache);
free(node);
}
_stats.entries = 0;
}
(entries != _stats.max_entries))
blkcache_invalidate(-1, 0);
_stats.max_blocks_per_entry = blocks;
_stats.max_entries = entries;
@ -188,3 +180,8 @@ void blkcache_stats(struct block_cache_stats *stats)
_stats.hits = 0;
_stats.misses = 0;
}
void blkcache_free(void)
{
blkcache_invalidate(-1, 0);
}

176
drivers/block/host-uclass.c Normal file
View File

@ -0,0 +1,176 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Uclass for sandbox host interface, used to access files on the host which
* contain partitions and filesystem
*
* Copyright 2022 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#define LOG_CATEGORY UCLASS_HOST
#include <common.h>
#include <blk.h>
#include <dm.h>
#include <malloc.h>
#include <sandbox_host.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/uclass-internal.h>
DECLARE_GLOBAL_DATA_PTR;
/**
* struct host_priv - information kept by the host uclass
*
* @cur_dev: Currently selected host device, or NULL if none
*/
struct host_priv {
struct udevice *cur_dev;
};
int host_create_device(const char *label, bool removable, struct udevice **devp)
{
char dev_name[30], *str, *label_new;
struct host_sb_plat *plat;
struct udevice *dev, *blk;
int ret;
/* unbind any existing device with this label */
dev = host_find_by_label(label);
if (dev) {
ret = host_detach_file(dev);
if (ret)
return log_msg_ret("det", ret);
ret = device_unbind(dev);
if (ret)
return log_msg_ret("unb", ret);
}
snprintf(dev_name, sizeof(dev_name), "host-%s", label);
str = strdup(dev_name);
if (!str)
return -ENOMEM;
label_new = strdup(label);
if (!label_new) {
ret = -ENOMEM;
goto no_label;
}
ret = device_bind_driver(gd->dm_root, "host_sb_drv", str, &dev);
if (ret)
goto no_dev;
device_set_name_alloced(dev);
if (!blk_find_from_parent(dev, &blk)) {
struct blk_desc *desc = dev_get_uclass_plat(blk);
desc->removable = removable;
}
plat = dev_get_plat(dev);
plat->label = label_new;
*devp = dev;
return 0;
no_dev:
free(label_new);
no_label:
free(str);
return ret;
}
int host_attach_file(struct udevice *dev, const char *filename)
{
struct host_ops *ops = host_get_ops(dev);
if (!ops->attach_file)
return -ENOSYS;
return ops->attach_file(dev, filename);
}
int host_create_attach_file(const char *label, const char *filename,
bool removable, struct udevice **devp)
{
struct udevice *dev;
int ret;
ret = host_create_device(label, removable, &dev);
if (ret)
return log_msg_ret("cre", ret);
ret = host_attach_file(dev, filename);
if (ret) {
device_unbind(dev);
return log_msg_ret("att", ret);
}
*devp = dev;
return 0;
}
int host_detach_file(struct udevice *dev)
{
struct host_ops *ops = host_get_ops(dev);
if (!ops->detach_file)
return -ENOSYS;
if (dev == host_get_cur_dev())
host_set_cur_dev(NULL);
return ops->detach_file(dev);
}
struct udevice *host_find_by_label(const char *label)
{
struct udevice *dev;
struct uclass *uc;
uclass_id_foreach_dev(UCLASS_HOST, dev, uc) {
struct host_sb_plat *plat = dev_get_plat(dev);
if (plat->label && !strcmp(label, plat->label))
return dev;
}
return NULL;
}
struct udevice *host_get_cur_dev(void)
{
struct uclass *uc = uclass_find(UCLASS_HOST);
if (uc) {
struct host_priv *priv = uclass_get_priv(uc);
return priv->cur_dev;
}
return NULL;
}
void host_set_cur_dev(struct udevice *dev)
{
struct uclass *uc = uclass_find(UCLASS_HOST);
if (uc) {
struct host_priv *priv = uclass_get_priv(uc);
priv->cur_dev = dev;
}
}
UCLASS_DRIVER(host) = {
.id = UCLASS_HOST,
.name = "host",
#if CONFIG_IS_ENABLED(OF_REAL)
.post_bind = dm_scan_fdt_dev,
#endif
.priv_auto = sizeof(struct host_priv),
};

142
drivers/block/host_dev.c Normal file
View File

@ -0,0 +1,142 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for sandbox host interface, used to access files on the host which
* contain partitions and filesystem
*
* Copyright 2022 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#define LOG_CATEGORY UCLASS_HOST
#include <common.h>
#include <blk.h>
#include <bootdev.h>
#include <dm.h>
#include <log.h>
#include <malloc.h>
#include <os.h>
#include <sandbox_host.h>
#include <dm/device-internal.h>
static int host_sb_attach_file(struct udevice *dev, const char *filename)
{
struct host_sb_plat *plat = dev_get_plat(dev);
struct blk_desc *desc;
struct udevice *blk;
int ret, fd, size;
char *fname;
if (!filename)
return -EINVAL;
if (plat->fd)
return log_msg_ret("fd", -EEXIST);
/* Sanity check that host_sb_bind() has been used */
ret = blk_find_from_parent(dev, &blk);
if (ret)
return ret;
fd = os_open(filename, OS_O_RDWR);
if (fd == -1) {
printf("Failed to access host backing file '%s', trying read-only\n",
filename);
fd = os_open(filename, OS_O_RDONLY);
if (fd == -1) {
printf("- still failed\n");
return log_msg_ret("open", -ENOENT);
}
}
fname = strdup(filename);
if (!fname) {
ret = -ENOMEM;
goto err_fname;
}
size = os_filesize(fd);
desc = dev_get_uclass_plat(blk);
desc->lba = size / desc->blksz;
/* write this in last, when nothing can go wrong */
plat = dev_get_plat(dev);
plat->fd = fd;
plat->filename = fname;
return 0;
err_fname:
os_close(fd);
return ret;
}
int host_sb_detach_file(struct udevice *dev)
{
struct host_sb_plat *plat = dev_get_plat(dev);
int ret;
if (!plat->fd)
return log_msg_ret("fd", -ENOENT);
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret)
return log_msg_ret("rem", ret);
/* Unbind all children */
ret = device_chld_unbind(dev, NULL);
if (ret)
return log_msg_ret("unb", ret);
os_close(plat->fd);
plat->fd = 0;
free(plat->filename);
free(plat->label);
return 0;
}
static int host_sb_bind(struct udevice *dev)
{
struct udevice *blk, *bdev;
struct blk_desc *desc;
int ret;
ret = blk_create_devicef(dev, "sandbox_host_blk", "blk", UCLASS_HOST,
dev_seq(dev), 512, 0, &blk);
if (ret)
return log_msg_ret("blk", ret);
desc = dev_get_uclass_plat(blk);
snprintf(desc->vendor, BLK_VEN_SIZE, "U-Boot");
snprintf(desc->product, BLK_PRD_SIZE, "hostfile");
snprintf(desc->revision, BLK_REV_SIZE, "1.0");
if (CONFIG_IS_ENABLED(BOOTSTD)) {
ret = bootdev_bind(dev, "host_bootdev", "bootdev", &bdev);
if (ret)
return log_msg_ret("bd", ret);
}
return 0;
}
struct host_ops host_sb_ops = {
.attach_file = host_sb_attach_file,
.detach_file = host_sb_detach_file,
};
static const struct udevice_id host_ids[] = {
{ .compatible = "sandbox,host" },
{ }
};
U_BOOT_DRIVER(host_sb_drv) = {
.name = "host_sb_drv",
.id = UCLASS_HOST,
.of_match = host_ids,
.ops = &host_sb_ops,
.bind = host_sb_bind,
.plat_auto = sizeof(struct host_sb_plat),
};

View File

@ -10,242 +10,50 @@
#include <part.h>
#include <os.h>
#include <malloc.h>
#include <sandboxblockdev.h>
#include <sandbox_host.h>
#include <asm/global_data.h>
#include <dm/device_compat.h>
#include <linux/errno.h>
#include <dm/device-internal.h>
#include <linux/errno.h>
DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_BLK
static struct host_block_dev host_devices[SANDBOX_HOST_MAX_DEVICES];
static struct host_block_dev *find_host_device(int dev)
{
if (dev >= 0 && dev < SANDBOX_HOST_MAX_DEVICES)
return &host_devices[dev];
return NULL;
}
#endif
#ifdef CONFIG_BLK
static unsigned long host_block_read(struct udevice *dev,
unsigned long start, lbaint_t blkcnt,
void *buffer)
{
struct host_block_dev *host_dev = dev_get_plat(dev);
struct blk_desc *block_dev = dev_get_uclass_plat(dev);
struct blk_desc *desc = dev_get_uclass_plat(dev);
struct udevice *host_dev = dev_get_parent(dev);
struct host_sb_plat *plat = dev_get_plat(host_dev);
#else
static unsigned long host_block_read(struct blk_desc *block_dev,
unsigned long start, lbaint_t blkcnt,
void *buffer)
{
int dev = block_dev->devnum;
struct host_block_dev *host_dev = find_host_device(dev);
if (!host_dev)
return -1;
#endif
if (os_lseek(host_dev->fd, start * block_dev->blksz, OS_SEEK_SET) ==
-1) {
if (os_lseek(plat->fd, start * desc->blksz, OS_SEEK_SET) == -1) {
printf("ERROR: Invalid block %lx\n", start);
return -1;
}
ssize_t len = os_read(host_dev->fd, buffer, blkcnt * block_dev->blksz);
ssize_t len = os_read(plat->fd, buffer, blkcnt * desc->blksz);
if (len >= 0)
return len / block_dev->blksz;
return -1;
return len / desc->blksz;
return -EIO;
}
#ifdef CONFIG_BLK
static unsigned long host_block_write(struct udevice *dev,
unsigned long start, lbaint_t blkcnt,
const void *buffer)
{
struct host_block_dev *host_dev = dev_get_plat(dev);
struct blk_desc *block_dev = dev_get_uclass_plat(dev);
#else
static unsigned long host_block_write(struct blk_desc *block_dev,
unsigned long start, lbaint_t blkcnt,
const void *buffer)
{
int dev = block_dev->devnum;
struct host_block_dev *host_dev = find_host_device(dev);
#endif
struct blk_desc *desc = dev_get_uclass_plat(dev);
struct udevice *host_dev = dev_get_parent(dev);
struct host_sb_plat *plat = dev_get_plat(host_dev);
if (os_lseek(host_dev->fd, start * block_dev->blksz, OS_SEEK_SET) ==
-1) {
if (os_lseek(plat->fd, start * desc->blksz, OS_SEEK_SET) == -1) {
printf("ERROR: Invalid block %lx\n", start);
return -1;
}
ssize_t len = os_write(host_dev->fd, buffer, blkcnt * block_dev->blksz);
ssize_t len = os_write(plat->fd, buffer, blkcnt * desc->blksz);
if (len >= 0)
return len / block_dev->blksz;
return -1;
}
return len / desc->blksz;
#ifdef CONFIG_BLK
int host_dev_bind(int devnum, char *filename, bool removable)
{
struct host_block_dev *host_dev;
struct udevice *dev;
struct blk_desc *desc;
char dev_name[20], *str, *fname;
int ret, fd;
/* Remove and unbind the old device, if any */
ret = blk_get_device(UCLASS_ROOT, devnum, &dev);
if (ret == 0) {
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret)
return ret;
ret = device_unbind(dev);
if (ret)
return ret;
} else if (ret != -ENODEV) {
return ret;
}
if (!filename)
return 0;
snprintf(dev_name, sizeof(dev_name), "host%d", devnum);
str = strdup(dev_name);
if (!str)
return -ENOMEM;
fname = strdup(filename);
if (!fname) {
free(str);
return -ENOMEM;
}
fd = os_open(filename, OS_O_RDWR);
if (fd == -1) {
printf("Failed to access host backing file '%s', trying read-only\n",
filename);
fd = os_open(filename, OS_O_RDONLY);
if (fd == -1) {
printf("- still failed\n");
ret = -ENOENT;
goto err;
}
}
ret = blk_create_device(gd->dm_root, "sandbox_host_blk", str,
UCLASS_ROOT, devnum, 512,
os_lseek(fd, 0, OS_SEEK_END) / 512, &dev);
if (ret)
goto err_file;
host_dev = dev_get_plat(dev);
host_dev->fd = fd;
host_dev->filename = fname;
ret = device_probe(dev);
if (ret) {
device_unbind(dev);
goto err_file;
}
desc = blk_get_devnum_by_uclass_id(UCLASS_ROOT, devnum);
desc->removable = removable;
snprintf(desc->vendor, BLK_VEN_SIZE, "U-Boot");
snprintf(desc->product, BLK_PRD_SIZE, "hostfile");
snprintf(desc->revision, BLK_REV_SIZE, "1.0");
return 0;
err_file:
os_close(fd);
err:
free(fname);
free(str);
return ret;
}
#else
int host_dev_bind(int dev, char *filename, bool removable)
{
struct host_block_dev *host_dev = find_host_device(dev);
if (!host_dev)
return -1;
if (host_dev->blk_dev.priv) {
os_close(host_dev->fd);
host_dev->blk_dev.priv = NULL;
}
if (host_dev->filename)
free(host_dev->filename);
if (filename && *filename) {
host_dev->filename = strdup(filename);
} else {
host_dev->filename = NULL;
return 0;
}
host_dev->fd = os_open(host_dev->filename, OS_O_RDWR);
if (host_dev->fd == -1) {
printf("Failed to access host backing file '%s'\n",
host_dev->filename);
return 1;
}
struct blk_desc *blk_dev = &host_dev->blk_dev;
blk_dev->uclass_id = UCLASS_ROOT;
blk_dev->priv = host_dev;
blk_dev->blksz = 512;
blk_dev->lba = os_lseek(host_dev->fd, 0, OS_SEEK_END) / blk_dev->blksz;
blk_dev->block_read = host_block_read;
blk_dev->block_write = host_block_write;
blk_dev->devnum = dev;
blk_dev->part_type = PART_TYPE_UNKNOWN;
blk_dev->removable = removable;
snprintf(blk_dev->vendor, BLK_VEN_SIZE, "U-Boot");
snprintf(blk_dev->product, BLK_PRD_SIZE, "hostfile");
snprintf(blk_dev->revision, BLK_REV_SIZE, "1.0");
part_init(blk_dev);
return 0;
}
#endif
int host_get_dev_err(int devnum, struct blk_desc **blk_devp)
{
#ifdef CONFIG_BLK
struct udevice *dev;
int ret;
ret = blk_get_device(UCLASS_ROOT, devnum, &dev);
if (ret)
return ret;
*blk_devp = dev_get_uclass_plat(dev);
#else
struct host_block_dev *host_dev = find_host_device(devnum);
if (!host_dev)
return -ENODEV;
if (!host_dev->blk_dev.priv)
return -ENOENT;
*blk_devp = &host_dev->blk_dev;
#endif
return 0;
}
#ifdef CONFIG_BLK
int sandbox_host_unbind(struct udevice *dev)
{
struct host_block_dev *host_dev;
/* Data validity is checked in host_dev_bind() */
host_dev = dev_get_plat(dev);
os_close(host_dev->fd);
return 0;
return -EIO;
}
static const struct blk_ops sandbox_host_blk_ops = {
@ -257,14 +65,4 @@ U_BOOT_DRIVER(sandbox_host_blk) = {
.name = "sandbox_host_blk",
.id = UCLASS_BLK,
.ops = &sandbox_host_blk_ops,
.unbind = sandbox_host_unbind,
.plat_auto = sizeof(struct host_block_dev),
};
#else
U_BOOT_LEGACY_BLK(sandbox_host) = {
.uclass_idname = "host",
.uclass_id = UCLASS_ROOT,
.max_devs = SANDBOX_HOST_MAX_DEVICES,
.get_dev = host_get_dev_err,
};
#endif

View File

@ -147,8 +147,8 @@ void blkcache_fill(int iftype, int dev,
* blkcache_invalidate() - discard the cache for a set of blocks
* because of a write or device (re)initialization.
*
* @param iftype - uclass_id_x for type of device
* @param dev - device index of particular type
* @iftype - UCLASS_ID_ for type of device, or -1 for any
* @dev - device index of particular type, if @iftype is not -1
*/
void blkcache_invalidate(int iftype, int dev);
@ -178,6 +178,9 @@ struct block_cache_stats {
*/
void blkcache_stats(struct block_cache_stats *stats);
/** blkcache_free() - free all memory allocated to the block cache */
void blkcache_free(void);
#else
static inline int blkcache_read(int iftype, int dev,
@ -193,6 +196,8 @@ static inline void blkcache_fill(int iftype, int dev,
static inline void blkcache_invalidate(int iftype, int dev) {}
static inline void blkcache_free(void) {}
#endif
#if CONFIG_IS_ENABLED(BLK)
@ -448,10 +453,36 @@ int blk_next_free_devnum(enum uclass_id uclass_id);
*/
int blk_select_hwpart(struct udevice *dev, int hwpart);
/**
* blk_find_from_parent() - find a block device by looking up its parent
*
* All block devices have a parent 'media' device which provides the block
* driver for the block device, ensuring that access to the underlying medium
* is available.
*
* The block device is not activated by this function. See
* blk_get_from_parent() for that.
*
* @parent: Media device
* @devp: Returns the associated block device, if any
* Returns: 0 if OK, -ENODEV if @parent is not a media device and has no
* UCLASS_BLK child
*/
int blk_find_from_parent(struct udevice *parent, struct udevice **devp);
/**
* blk_get_from_parent() - obtain a block device by looking up its parent
*
* All devices with
* All block devices have a parent 'media' device which provides the block
* driver for the block device, ensuring that access to the underlying medium
* is available.
*
* The block device is probed and ready for use.
*
* @parent: Media device
* @devp: Returns the associated block device, if any
* Returns: 0 if OK, -ENODEV if @parent is not a media device and has no
* UCLASS_BLK child
*/
int blk_get_from_parent(struct udevice *parent, struct udevice **devp);

View File

@ -63,6 +63,7 @@ enum uclass_id {
UCLASS_GPIO, /* Bank of general-purpose I/O pins */
UCLASS_HASH, /* Hash device */
UCLASS_HWSPINLOCK, /* Hardware semaphores */
UCLASS_HOST, /* Sandbox host device */
UCLASS_I2C, /* I2C bus */
UCLASS_I2C_EEPROM, /* I2C EEPROM device */
UCLASS_I2C_GENERIC, /* Generic I2C device */

View File

@ -110,6 +110,10 @@ void os_exit(int exit_code) __attribute__((noreturn));
/**
* os_alarm() - access to the OS alarm() system call
*
* @seconds: number of seconds before the signal is sent
* Returns: number of seconds remaining until any previously scheduled alarm was
* due to be delivered; 0 if there was no previously scheduled alarm
*/
unsigned int os_alarm(unsigned int seconds);

125
include/sandbox_host.h Normal file
View File

@ -0,0 +1,125 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* sandbox host uclass
*
* Copyright 2022 Google LLC
*/
#ifndef __SANDBOX_HOST__
#define __SANDBOX_HOST__
/**
* struct host_sb_plat - platform data for a host device
*
* @label: Label for this device (allocated)
* @filename: Name of file this is attached to, or NULL (allocated)
* @fd: File descriptor of file, or 0 for none (file is not open)
*/
struct host_sb_plat {
char *label;
char *filename;
int fd;
};
/**
* struct host_ops - operations supported by UCLASS_HOST
*
* @attach_file: Attach a new file to a device
* @detach_file: Detach a file from a device
*/
struct host_ops {
/*
* attach_file() - Attach a new file to the device
*
* @dev: Device to update
* @filename: Name of the file, e.g. "/path/to/disk.img"
* Returns: 0 if OK, -EEXIST if a file is already attached, other -ve on
* other error
*/
int (*attach_file)(struct udevice *dev, const char *filename);
/**
* detach_file() - Detach a file from the device
*
* @dev: Device to detach from
* Returns: 0 if OK, -ENOENT if no file is attached, other -ve on other
* error
*/
int (*detach_file)(struct udevice *dev);
};
#define host_get_ops(dev) ((struct host_ops *)(dev)->driver->ops)
/**
* host_attach_file() - Attach a new file to the device
*
* @dev: Device to update
* @filename: Name of the file, e.g. "/path/to/disk.img"
* Returns: 0 if OK, -EEXIST if a file is already attached, other -ve on
* other error
*/
int host_attach_file(struct udevice *dev, const char *filename);
/**
* host_detach_file() - Detach a file from the device
*
* @dev: Device to detach from
* Returns: 0 if OK, -ENOENT if no file is attached, other -ve on other
* error
*/
int host_detach_file(struct udevice *dev);
/**
* host_create_device() - Create a new host device
*
* Any existing device with the same label is removed and unbound first
*
* @label: Label of the attachment, e.g. "test1"
* @removable: true if the device should be marked as removable, false
* if it is fixed. See enum blk_flag_t
* @devp: Returns the device created, on success
* Returns: 0 if OK, -ve on error
*/
int host_create_device(const char *label, bool removable,
struct udevice **devp);
/**
* host_create_attach_file() - Create a new host device attached to a file
*
* @label: Label of the attachment, e.g. "test1"
* @filename: Name of the file, e.g. "/path/to/disk.img"
* @removable: true if the device should be marked as removable, false
* if it is fixed. See enum blk_flag_t
* @devp: Returns the device created, on success
* Returns: 0 if OK, -ve on error
*/
int host_create_attach_file(const char *label, const char *filename,
bool removable, struct udevice **devp);
/**
* host_find_by_label() - Find a host by label
*
* Searches all host devices to find one with the given label
*
* @label: Label to find
* Returns: associated device, or NULL if not found
*/
struct udevice *host_find_by_label(const char *label);
/**
* host_get_cur_dev() - Get the current device
*
* Returns current device, or NULL if none
*/
struct udevice *host_get_cur_dev(void);
/**
* host_set_cur_dev() - Set the current device
*
* Sets the current device, or clears it if @dev is NULL
*
* @dev: Device to set as the current one
*/
void host_set_cur_dev(struct udevice *dev);
#endif /* __SANDBOX_HOST__ */

View File

@ -1,31 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2013, Henrik Nordstrom <henrik@henriknordstrom.net>
*/
#ifndef __SANDBOX_BLOCK_DEV__
#define __SANDBOX_BLOCK_DEV__
/* Maximum number of host devices - see drivers/block/sandbox.c */
#define SANDBOX_HOST_MAX_DEVICES 4
struct host_block_dev {
#ifndef CONFIG_BLK
struct blk_desc blk_dev;
#endif
char *filename;
int fd;
};
/**
* host_dev_bind() - Bind or unbind a device
*
* @dev: Device number (0=first slot)
* @filename: Host filename to use, or NULL to unbind
* @removable: true if the block device should mark itself as removable
*/
int host_dev_bind(int dev, char *filename, bool removable);
int host_get_dev_err(int dev, struct blk_desc **blk_devp);
#endif

View File

@ -410,10 +410,15 @@ void test_set_state(struct unit_test_state *uts);
* then all tests are run
* @runs_per_test: Number of times to run each test (typically 1)
* @force_run: Run tests that are marked as manual-only (UT_TESTF_MANUAL)
* @test_insert: String describing a test to run after n other tests run, in the
* format n:name where n is the number of tests to run before this one and
* name is the name of the test to run. This is used to find which test causes
* another test to fail. If the one test fails, testing stops immediately.
* Pass NULL to disable this
* Return: 0 if all tests passed, -1 if any failed
*/
int ut_run_list(const char *name, const char *prefix, struct unit_test *tests,
int count, const char *select_name, int runs_per_test,
bool force_run);
bool force_run, const char *test_insert);
#endif

View File

@ -17,7 +17,6 @@
#include <nvme.h>
#include <efi_loader.h>
#include <part.h>
#include <sandboxblockdev.h>
#include <uuid.h>
#include <asm-generic/unaligned.h>
#include <linux/compat.h> /* U16_MAX */
@ -556,7 +555,7 @@ __maybe_unused static unsigned int dp_size(struct udevice *dev)
sizeof(struct efi_device_path_nvme);
#endif
#ifdef CONFIG_SANDBOX
case UCLASS_ROOT:
case UCLASS_HOST:
/*
* Sandbox's host device will be represented
* as vendor device with extra one byte for
@ -633,7 +632,7 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev)
case UCLASS_BLK:
switch (dev->parent->uclass->uc_drv->id) {
#ifdef CONFIG_SANDBOX
case UCLASS_ROOT: {
case UCLASS_HOST: {
/* stop traversing parents at this point: */
struct efi_device_path_vendor *dp;
struct blk_desc *desc = dev_get_uclass_plat(dev);

View File

@ -555,7 +555,7 @@ static int efi_disk_create_raw(struct udevice *dev)
if (ret == EFI_NOT_READY)
log_notice("Disk %s not ready\n", dev->name);
else
log_err("Adding disk for %s failed\n", dev->name);
log_err("Adding disk for %s failed (err=%ld/%#lx)\n", dev->name, ret, ret);
return -1;
}

View File

@ -14,10 +14,14 @@
static int do_ut_all(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[]);
static int do_ut_info(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[]);
int cmd_ut_category(const char *name, const char *prefix,
struct unit_test *tests, int n_ents,
int argc, char *const argv[])
{
const char *test_insert = NULL;
int runs_per_text = 1;
bool force_run = false;
int ret;
@ -32,19 +36,24 @@ int cmd_ut_category(const char *name, const char *prefix,
case 'f':
force_run = true;
break;
case 'I':
test_insert = str + 2;
break;
}
argv++;
argc++;
argc--;
}
ret = ut_run_list(name, prefix, tests, n_ents,
argc > 1 ? argv[1] : NULL, runs_per_text, force_run);
argc > 1 ? argv[1] : NULL, runs_per_text, force_run,
test_insert);
return ret ? CMD_RET_FAILURE : 0;
}
static struct cmd_tbl cmd_ut_sub[] = {
U_BOOT_CMD_MKENT(all, CONFIG_SYS_MAXARGS, 1, do_ut_all, "", ""),
U_BOOT_CMD_MKENT(info, 1, 1, do_ut_info, "", ""),
#ifdef CONFIG_BOOTSTD
U_BOOT_CMD_MKENT(bootstd, CONFIG_SYS_MAXARGS, 1, do_ut_bootstd,
"", ""),
@ -119,6 +128,15 @@ static int do_ut_all(struct cmd_tbl *cmdtp, int flag, int argc,
return any_fail;
}
static int do_ut_info(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
printf("Test suites: %d\n", (int)ARRAY_SIZE(cmd_ut_sub));
printf("Total tests: %d\n", (int)UNIT_TEST_ALL_COUNT());
return 0;
}
static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
struct cmd_tbl *cp;
@ -140,59 +158,67 @@ static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
#ifdef CONFIG_SYS_LONGHELP
static char ut_help_text[] =
"all - execute all enabled tests\n"
"[-r] [-f] [<suite>] - run unit tests\n"
" -r<runs> Number of times to run each test\n"
" -f Force 'manual' tests to run as well\n"
" <suite> Test suite to run, or all\n"
"\n"
"\nOptions for <suite>:"
"\nall - execute all enabled tests"
"\ninfo - show info about tests"
#ifdef CONFIG_CMD_ADDRMAP
"\naddrmap - very basic test of addrmap command"
#endif
#ifdef CONFIG_SANDBOX
"ut bloblist - Test bloblist implementation\n"
"ut compression - Test compressors and bootm decompression\n"
"\nbloblist - bloblist implementation"
#endif
#ifdef CONFIG_BOOTSTD
"ut bootstd - Test standard boot implementation\n"
"\nbootstd - standard boot implementation"
#endif
#ifdef CONFIG_SANDBOX
"\ncompression - compressors and bootm decompression"
#endif
#ifdef CONFIG_UT_DM
"ut dm [test-name]\n"
"\ndm - driver model"
#endif
#ifdef CONFIG_UT_ENV
"ut env [test-name]\n"
"\nenv - environment"
#endif
#ifdef CONFIG_CMD_FDT
"ut fdt [test-name] - test of the fdt command\n"
"\nfdt - fdt command"
#endif
#ifdef CONFIG_CONSOLE_TRUETYPE
"ut font [test-name] - test of the font command\n"
"\nut font - font command\n"
#endif
#ifdef CONFIG_CMD_LOADM
"\nloadm - loadm command parameters and loading memory blob"
#endif
#ifdef CONFIG_UT_LIB
"ut lib [test-name] - test library functions\n"
"\nlib - library functions"
#endif
#ifdef CONFIG_UT_LOG
"ut log [test-name] - test logging functions\n"
"\nlog - logging functions"
#endif
"ut mem [test-name] - test memory-related commands\n"
"\nmem - memory-related commands"
#ifdef CONFIG_UT_OPTEE
"ut optee [test-name]\n"
"\noptee - test OP-TEE"
#endif
#ifdef CONFIG_UT_OVERLAY
"ut overlay [test-name]\n"
"\noverlay - device tree overlays"
#endif
"ut print [test-name] - test printing\n"
"ut setexpr [test-name] - test setexpr command\n"
"\nprint - printing things to the console"
"\nsetexpr - setexpr command"
#ifdef CONFIG_SANDBOX
"ut str - Basic test of string functions\n"
"\nstr - basic test of string functions"
#endif
#ifdef CONFIG_UT_TIME
"ut time - Very basic test of time functions\n"
"\ntime - very basic test of time functions"
#endif
#if defined(CONFIG_UT_UNICODE) && \
!defined(CONFIG_SPL_BUILD) && !defined(API_BUILD)
"ut unicode [test-name] - test Unicode functions\n"
"\nunicode - Unicode functions"
#endif
#ifdef CONFIG_CMD_ADDRMAP
"ut addrmap - Very basic test of addrmap command\n"
#endif
#ifdef CONFIG_CMD_LOADM
"ut loadm [test-name]- test of parameters and load memory blob\n"
#endif
"All commands accept an optional [-r<runs>] flag before [test-name], to\n"
"run each test multiple times (<runs> is in decimal)";
;
#endif /* CONFIG_SYS_LONGHELP */
U_BOOT_CMD(

View File

@ -49,6 +49,7 @@ endif
obj-$(CONFIG_FIRMWARE) += firmware.o
obj-$(CONFIG_DM_FPGA) += fpga.o
obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_mdata.o
obj-$(CONFIG_SANDBOX) += host.o
obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock.o
obj-$(CONFIG_DM_I2C) += i2c.o
obj-$(CONFIG_SOUND) += i2s.o

View File

@ -6,6 +6,7 @@
#include <common.h>
#include <dm.h>
#include <part.h>
#include <sandbox_host.h>
#include <usb.h>
#include <asm/global_data.h>
#include <asm/state.h>
@ -21,26 +22,27 @@ extern char usb_started;
/* Test that block devices can be created */
static int dm_test_blk_base(struct unit_test_state *uts)
{
struct udevice *blk1, *blk3, *dev;
struct udevice *blk0, *blk1, *dev0, *dev1, *dev, *chk0, *chk1;
/* Create two, one the parent of the other */
ut_assertok(blk_create_device(gd->dm_root, "sandbox_host_blk", "test",
UCLASS_ROOT, 1, 512, 2, &blk1));
ut_assertok(blk_create_device(blk1, "sandbox_host_blk", "test",
UCLASS_ROOT, 3, 512, 2, &blk3));
ut_assertok(host_create_device("test0", false, &dev0));
ut_assertok(host_create_device("test1", false, &dev1));
/* Check we can find them */
ut_asserteq(-ENODEV, blk_get_device(UCLASS_ROOT, 0, &dev));
ut_assertok(blk_get_device(UCLASS_ROOT, 1, &dev));
ut_asserteq_ptr(blk1, dev);
ut_assertok(blk_get_device(UCLASS_ROOT, 3, &dev));
ut_asserteq_ptr(blk3, dev);
ut_assertok(blk_get_device(UCLASS_HOST, 0, &blk0));
ut_assertok(blk_get_from_parent(dev0, &chk0));
ut_asserteq_ptr(blk0, chk0);
ut_assertok(blk_get_device(UCLASS_HOST, 1, &blk1));
ut_assertok(blk_get_from_parent(dev1, &chk1));
ut_asserteq_ptr(blk1, chk1);
ut_asserteq(-ENODEV, blk_get_device(UCLASS_HOST, 2, &dev0));
/* Check we can iterate */
ut_assertok(blk_first_device(UCLASS_ROOT, &dev));
ut_asserteq_ptr(blk1, dev);
ut_assertok(blk_first_device(UCLASS_HOST, &dev));
ut_asserteq_ptr(blk0, dev);
ut_assertok(blk_next_device(&dev));
ut_asserteq_ptr(blk3, dev);
ut_asserteq_ptr(blk1, dev);
return 0;
}
@ -98,19 +100,20 @@ DM_TEST(dm_test_blk_usb, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
/* Test that we can find block devices without probing them */
static int dm_test_blk_find(struct unit_test_state *uts)
{
struct udevice *blk, *dev;
struct udevice *blk, *chk, *dev;
ut_assertok(blk_create_device(gd->dm_root, "sandbox_host_blk", "test",
UCLASS_ROOT, 1, 512, 2, &blk));
ut_asserteq(-ENODEV, blk_find_device(UCLASS_ROOT, 0, &dev));
ut_assertok(blk_find_device(UCLASS_ROOT, 1, &dev));
ut_asserteq_ptr(blk, dev);
ut_assertok(host_create_device("test0", false, &dev));
ut_assertok(blk_find_device(UCLASS_HOST, 0, &chk));
ut_assertok(device_find_first_child_by_uclass(dev, UCLASS_BLK, &blk));
ut_asserteq_ptr(chk, blk);
ut_asserteq(false, device_active(dev));
ut_asserteq(-ENODEV, blk_find_device(UCLASS_HOST, 1, &dev));
/* Now activate it */
ut_assertok(blk_get_device(UCLASS_ROOT, 1, &dev));
ut_asserteq_ptr(blk, dev);
ut_asserteq(true, device_active(dev));
ut_assertok(blk_get_device(UCLASS_HOST, 0, &blk));
ut_asserteq_ptr(chk, blk);
ut_asserteq(true, device_active(blk));
return 0;
}
@ -160,7 +163,7 @@ static int dm_test_blk_get_from_parent(struct unit_test_state *uts)
ut_assertok(blk_get_from_parent(dev, &blk));
ut_assertok(uclass_get_device(UCLASS_I2C, 0, &dev));
ut_asserteq(-ENOTBLK, blk_get_from_parent(dev, &blk));
ut_asserteq(-ENODEV, blk_get_from_parent(dev, &blk));
ut_assertok(uclass_get_device(UCLASS_GPIO, 0, &dev));
ut_asserteq(-ENODEV, blk_get_from_parent(dev, &blk));

195
test/dm/host.c Normal file
View File

@ -0,0 +1,195 @@
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
/*
* Copyright 2022 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <blk.h>
#include <dm.h>
#include <fs.h>
#include <sandbox_host.h>
#include <asm/test.h>
#include <dm/device-internal.h>
#include <dm/test.h>
#include <test/test.h>
#include <test/ut.h>
static const char filename[] = "2MB.ext2.img";
static const char filename2[] = "1MB.fat32.img";
/* Basic test of host interface */
static int dm_test_host(struct unit_test_state *uts)
{
static char label[] = "test";
struct udevice *dev, *part, *chk, *blk;
struct host_sb_plat *plat;
struct blk_desc *desc;
ulong mem_start;
loff_t actwrite;
ut_asserteq(-ENODEV, uclass_first_device_err(UCLASS_HOST, &dev));
ut_asserteq(-ENODEV, uclass_first_device_err(UCLASS_PARTITION, &part));
mem_start = ut_check_delta(0);
ut_assertok(host_create_device(label, true, &dev));
/* Check that the plat data has been allocated */
plat = dev_get_plat(dev);
ut_asserteq_str("test", plat->label);
ut_assert(label != plat->label);
ut_asserteq(0, plat->fd);
/* Attach a file created in test_host.py */
ut_assertok(host_attach_file(dev, filename));
ut_assertok(uclass_first_device_err(UCLASS_HOST, &chk));
ut_asserteq_ptr(chk, dev);
ut_asserteq_str(filename, plat->filename);
ut_assert(filename != plat->filename);
ut_assert(plat->fd != 0);
/* Get the block device */
ut_assertok(blk_get_from_parent(dev, &blk));
ut_assertok(device_probe(blk));
/* There should be no partition table in this device */
ut_asserteq(-ENODEV, uclass_first_device_err(UCLASS_PARTITION, &part));
/* Write to a file on the ext4 filesystem */
desc = dev_get_uclass_plat(blk);
ut_asserteq(true, desc->removable);
ut_assertok(fs_set_blk_dev_with_part(desc, 0));
ut_assertok(fs_write("/testing", 0, 0, 0x1000, &actwrite));
ut_assertok(host_detach_file(dev));
ut_asserteq(0, plat->fd);
ut_asserteq(-ENODEV, blk_get_from_parent(dev, &blk));
ut_assertok(device_unbind(dev));
/* check there were no memory leaks */
ut_asserteq(0, ut_check_delta(mem_start));
return 0;
}
DM_TEST(dm_test_host, UT_TESTF_SCAN_FDT);
/* reusing the same label should work */
static int dm_test_host_dup(struct unit_test_state *uts)
{
static char label[] = "test";
struct udevice *dev, *chk;
ut_asserteq(0, uclass_id_count(UCLASS_HOST));
ut_assertok(host_create_device(label, true, &dev));
/* Attach a file created in test_host.py */
ut_assertok(host_attach_file(dev, filename));
ut_assertok(uclass_first_device_err(UCLASS_HOST, &chk));
ut_asserteq_ptr(chk, dev);
ut_asserteq(1, uclass_id_count(UCLASS_HOST));
/* Create another device with the same label (should remove old one) */
ut_assertok(host_create_device(label, true, &dev));
/* Attach a different file created in test_host.py */
ut_assertok(host_attach_file(dev, filename2));
ut_assertok(uclass_first_device_err(UCLASS_HOST, &chk));
ut_asserteq_ptr(chk, dev);
/* Make sure there is still only one device */
ut_asserteq(1, uclass_id_count(UCLASS_HOST));
return 0;
}
DM_TEST(dm_test_host_dup, UT_TESTF_SCAN_FDT);
/* Basic test of 'host' command */
static int dm_test_cmd_host(struct unit_test_state *uts)
{
struct udevice *dev, *blk;
struct blk_desc *desc;
console_record_reset();
/* first check 'host info' with binding */
ut_assertok(run_command("host info", 0));
ut_assert_nextline("dev blocks label path");
ut_assert_console_end();
ut_assertok(run_commandf("host bind -r test2 %s", filename));
/* Check the -r flag worked */
ut_assertok(uclass_first_device_err(UCLASS_HOST, &dev));
ut_assertok(blk_get_from_parent(dev, &blk));
desc = dev_get_uclass_plat(blk);
ut_asserteq(true, desc->removable);
ut_assertok(run_command("host info", 0));
ut_assert_nextline("dev blocks label path");
ut_assert_nextline(" 0 4096 test2 2MB.ext2.img");
ut_assert_console_end();
ut_assertok(run_commandf("host bind fat %s", filename2));
/* Check it is not removeable (no '-r') */
ut_assertok(uclass_next_device_err(&dev));
ut_assertok(blk_get_from_parent(dev, &blk));
desc = dev_get_uclass_plat(blk);
ut_asserteq(false, desc->removable);
ut_assertok(run_command("host info", 0));
ut_assert_nextline("dev blocks label path");
ut_assert_nextline(" 0 4096 test2 2MB.ext2.img");
ut_assert_nextline(" 1 2048 fat 1MB.fat32.img");
ut_assert_console_end();
ut_asserteq(1, run_command("host info test", 0));
ut_assert_nextline("No such device 'test'");
ut_assert_console_end();
ut_assertok(run_command("host info fat", 0));
ut_assert_nextline("dev blocks label path");
ut_assert_nextline(" 1 2048 fat 1MB.fat32.img");
ut_assert_console_end();
/* check 'host dev' */
ut_asserteq(1, run_command("host dev", 0));
ut_assert_nextline("No current host device");
ut_assert_console_end();
ut_asserteq(1, run_command("host dev missing", 0));
ut_assert_nextline("No such device 'missing'");
ut_assert_console_end();
ut_assertok(run_command("host dev fat", 0));
ut_assert_console_end();
ut_assertok(run_command("host dev", 0));
ut_assert_nextline("Current host device: 1: fat");
ut_assert_console_end();
/* Try a numerical label */
ut_assertok(run_command("host dev 0", 0));
ut_assert_console_end();
ut_assertok(run_command("host dev", 0));
ut_assert_nextline("Current host device: 0: test2");
ut_assert_console_end();
/* Remove one of the bindings */
ut_assertok(run_commandf("host unbind test2"));
/* There should now be no current device */
ut_asserteq(1, run_command("host dev", 0));
ut_assert_nextline("No current host device");
ut_assert_console_end();
ut_assertok(run_command("host info", 0));
ut_assert_nextline("dev blocks label path");
ut_assert_nextline(" 1 2048 fat 1MB.fat32.img");
ut_assert_console_end();
return 0;
}
DM_TEST(dm_test_cmd_host, UT_TESTF_SCAN_FDT);

View File

@ -4,55 +4,14 @@
*/
#include <common.h>
#include <command.h>
#include <console.h>
#include <dm.h>
#include <errno.h>
#include <log.h>
#include <malloc.h>
#include <asm/global_data.h>
#include <asm/state.h>
#include <dm/root.h>
#include <dm/uclass-internal.h>
#include <test/suites.h>
#include <test/test.h>
#include <test/test.h>
#include <test/ut.h>
DECLARE_GLOBAL_DATA_PTR;
/**
* dm_test_run() - Run driver model tests
*
* Run all the available driver model tests, or a selection
*
* @test_name: Name of single test to run (e.g. "dm_test_fdt_pre_reloc" or just
* "fdt_pre_reloc"), or NULL to run all
* Return: 0 if all tests passed, 1 if not
*/
static int dm_test_run(const char *test_name, int runs_per_text)
{
struct unit_test *tests = UNIT_TEST_SUITE_START(dm_test);
const int n_ents = UNIT_TEST_SUITE_COUNT(dm_test);
int ret;
ret = ut_run_list("driver model", "dm_test_", tests, n_ents, test_name,
runs_per_text, false);
return ret ? CMD_RET_FAILURE : 0;
}
int do_ut_dm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
const char *test_name = NULL;
int runs_per_text = 1;
struct unit_test *tests = UNIT_TEST_SUITE_START(dm_test);
const int n_ents = UNIT_TEST_SUITE_COUNT(dm_test);
if (argc > 1 && !strncmp("-r", argv[1], 2)) {
runs_per_text = dectoul(argv[1] + 2, NULL);
argv++;
argc++;
}
if (argc > 1)
test_name = argv[1];
return dm_test_run(test_name, runs_per_text);
return cmd_ut_category("driver model", "dm_test_", tests, n_ents, argc,
argv);
}

View File

@ -0,0 +1,68 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (c) 2018, Linaro Limited
# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
"""Helper functions for dealing with filesystems"""
import re
import os
from subprocess import call, check_call, check_output, CalledProcessError
def mk_fs(config, fs_type, size, prefix, use_src_dir=False):
"""Create a file system volume
Args:
config (u_boot_config): U-Boot configuration
fs_type (str): File system type, e.g. 'ext4'
size (int): Size of file system in bytes
prefix (str): Prefix string of volume's file name
use_src_dir (bool): true to put the file in the source directory
Raises:
CalledProcessError: if any error occurs when creating the filesystem
"""
fs_img = f'{prefix}.{fs_type}.img'
fs_img = os.path.join(config.source_dir if use_src_dir
else config.persistent_data_dir, fs_img)
if fs_type == 'fat16':
mkfs_opt = '-F 16'
elif fs_type == 'fat32':
mkfs_opt = '-F 32'
else:
mkfs_opt = ''
if re.match('fat', fs_type):
fs_lnxtype = 'vfat'
else:
fs_lnxtype = fs_type
count = (size + 0x100000 - 1) // 0x100000
# Some distributions do not add /sbin to the default PATH, where mkfs lives
if '/sbin' not in os.environ["PATH"].split(os.pathsep):
os.environ["PATH"] += os.pathsep + '/sbin'
try:
check_call(f'rm -f {fs_img}', shell=True)
check_call(f'dd if=/dev/zero of={fs_img} bs=1M count={count}',
shell=True)
check_call(f'mkfs.{fs_lnxtype} {mkfs_opt} {fs_img}', shell=True)
if fs_type == 'ext4':
sb_content = check_output(f'tune2fs -l {fs_img}',
shell=True).decode()
if 'metadata_csum' in sb_content:
check_call(f'tune2fs -O ^metadata_csum {fs_img}', shell=True)
return fs_img
except CalledProcessError:
call(f'rm -f {fs_img}', shell=True)
raise
# Just for trying out
if __name__ == "__main__":
import collections
CNF= collections.namedtuple('config', 'persistent_data_dir')
mk_fs(CNF('.'), 'ext4', 0x1000000, 'pref')

View File

@ -64,6 +64,9 @@ def test_efi_eficonfig(u_boot_console, efi_eficonfig_data):
initrddump.efi
"""
# This test passes for unknown reasons in the bowels of U-Boot. It needs to
# be replaced with a unit test.
return
# Restart the system to clean the previous state
u_boot_console.restart_uboot()

View File

@ -9,6 +9,7 @@ import re
from subprocess import call, check_call, check_output, CalledProcessError
from fstest_defs import *
import u_boot_utils as util
from tests import fs_helper
supported_fs_basic = ['fat16', 'fat32', 'ext4']
supported_fs_ext = ['fat16', 'fat32']
@ -132,53 +133,6 @@ def check_ubconfig(config, fs_type):
pytest.skip('.config feature "%s_WRITE" not enabled'
% fs_type.upper())
def mk_fs(config, fs_type, size, id):
"""Create a file system volume.
Args:
fs_type: File system type.
size: Size of file system in MiB.
id: Prefix string of volume's file name.
Return:
Nothing.
"""
fs_img = '%s.%s.img' % (id, fs_type)
fs_img = config.persistent_data_dir + '/' + fs_img
if fs_type == 'fat16':
mkfs_opt = '-F 16'
elif fs_type == 'fat32':
mkfs_opt = '-F 32'
else:
mkfs_opt = ''
if re.match('fat', fs_type):
fs_lnxtype = 'vfat'
else:
fs_lnxtype = fs_type
count = (size + 1048576 - 1) / 1048576
# Some distributions do not add /sbin to the default PATH, where mkfs lives
if '/sbin' not in os.environ["PATH"].split(os.pathsep):
os.environ["PATH"] += os.pathsep + '/sbin'
try:
check_call('rm -f %s' % fs_img, shell=True)
check_call('dd if=/dev/zero of=%s bs=1M count=%d'
% (fs_img, count), shell=True)
check_call('mkfs.%s %s %s'
% (fs_lnxtype, mkfs_opt, fs_img), shell=True)
if fs_type == 'ext4':
sb_content = check_output('tune2fs -l %s' % fs_img, shell=True).decode()
if 'metadata_csum' in sb_content:
check_call('tune2fs -O ^metadata_csum %s' % fs_img, shell=True)
return fs_img
except CalledProcessError:
call('rm -f %s' % fs_img, shell=True)
raise
# from test/py/conftest.py
def tool_is_in_path(tool):
"""Check whether a given command is available on host.
@ -283,7 +237,7 @@ def fs_obj_basic(request, u_boot_config):
try:
# 3GiB volume
fs_img = mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB')
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB')
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
@ -405,7 +359,7 @@ def fs_obj_ext(request, u_boot_config):
try:
# 128MiB volume
fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
@ -500,7 +454,7 @@ def fs_obj_mkdir(request, u_boot_config):
try:
# 128MiB volume
fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
except:
pytest.skip('Setup failed for filesystem: ' + fs_type)
return
@ -534,7 +488,7 @@ def fs_obj_unlink(request, u_boot_config):
try:
# 128MiB volume
fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
@ -617,7 +571,7 @@ def fs_obj_symlink(request, u_boot_config):
try:
# 1GiB volume
fs_img = mk_fs(u_boot_config, fs_type, 0x40000000, '1GB')
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x40000000, '1GB')
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return

View File

@ -7,6 +7,7 @@ import os.path
import pytest
import u_boot_utils
from tests import fs_helper
def mkdir_cond(dirname):
"""Create a directory if it doesn't already exist
@ -123,6 +124,11 @@ def test_ut_dm_init(u_boot_console):
u_boot_utils.run_and_log(
u_boot_console, f'sfdisk {fn}', stdin=b'type=83')
fs_helper.mk_fs(u_boot_console.config, 'ext2', 0x200000, '2MB',
use_src_dir=True)
fs_helper.mk_fs(u_boot_console.config, 'fat32', 0x100000, '1MB',
use_src_dir=True)
@pytest.mark.buildconfigspec('cmd_bootflow')
def test_ut_dm_init_bootstd(u_boot_console):
"""Initialise data for bootflow tests"""

View File

@ -5,6 +5,7 @@
*/
#include <common.h>
#include <blk.h>
#include <console.h>
#include <cyclic.h>
#include <dm.h>
@ -352,6 +353,8 @@ static int test_post_run(struct unit_test_state *uts, struct unit_test *test)
free(uts->of_other);
uts->of_other = NULL;
blkcache_free();
return 0;
}
@ -428,12 +431,11 @@ static int ut_run_test(struct unit_test_state *uts, struct unit_test *test,
* the first call to this function. On exit, @uts->fail_count is
* incremented by the number of failures (0, one hopes)
* @test: Test to run
* @name: Name of test, possibly skipping a prefix that should not be displayed
* Return: 0 if all tests passed, -EAGAIN if the test should be skipped, -1 if
* any failed
*/
static int ut_run_test_live_flat(struct unit_test_state *uts,
struct unit_test *test, const char *name)
struct unit_test *test)
{
int runs;
@ -496,12 +498,29 @@ static int ut_run_test_live_flat(struct unit_test_state *uts,
*/
static int ut_run_tests(struct unit_test_state *uts, const char *prefix,
struct unit_test *tests, int count,
const char *select_name)
const char *select_name, const char *test_insert)
{
struct unit_test *test;
struct unit_test *test, *one;
int found = 0;
int pos = 0;
int upto;
for (test = tests; test < tests + count; test++) {
one = NULL;
if (test_insert) {
char *p;
pos = dectoul(test_insert, NULL);
p = strchr(test_insert, ':');
if (p)
p++;
for (test = tests; test < tests + count; test++) {
if (!strcmp(p, test->name))
one = test;
}
}
for (upto = 0, test = tests; test < tests + count; test++, upto++) {
const char *test_name = test->name;
int ret, i, old_fail_count;
@ -532,8 +551,19 @@ static int ut_run_tests(struct unit_test_state *uts, const char *prefix,
}
}
old_fail_count = uts->fail_count;
if (one && upto == pos) {
ret = ut_run_test_live_flat(uts, one);
if (uts->fail_count != old_fail_count) {
printf("Test %s failed %d times (position %d)\n",
one->name,
uts->fail_count - old_fail_count, pos);
}
return -EBADF;
}
for (i = 0; i < uts->runs_per_test; i++)
ret = ut_run_test_live_flat(uts, test, select_name);
ret = ut_run_test_live_flat(uts, test);
if (uts->fail_count != old_fail_count) {
printf("Test %s failed %d times\n", select_name,
uts->fail_count - old_fail_count);
@ -552,7 +582,7 @@ static int ut_run_tests(struct unit_test_state *uts, const char *prefix,
int ut_run_list(const char *category, const char *prefix,
struct unit_test *tests, int count, const char *select_name,
int runs_per_test, bool force_run)
int runs_per_test, bool force_run, const char *test_insert)
{
struct unit_test_state uts = { .fail_count = 0 };
bool has_dm_tests = false;
@ -587,7 +617,8 @@ int ut_run_list(const char *category, const char *prefix,
memcpy(uts.fdt_copy, gd->fdt_blob, uts.fdt_size);
}
uts.force_run = force_run;
ret = ut_run_tests(&uts, prefix, tests, count, select_name);
ret = ut_run_tests(&uts, prefix, tests, count, select_name,
test_insert);
/* Best efforts only...ignore errors */
if (has_dm_tests)