mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-05-05 20:56:12 +02:00
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:
commit
77b5cc2948
@ -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 */
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,10 +68,6 @@
|
||||
reg = <0x10002000 0x1000>;
|
||||
};
|
||||
|
||||
host-fs {
|
||||
compatible = "sandbox,bootdev-host";
|
||||
};
|
||||
|
||||
i2c_0: i2c@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
208
cmd/host.c
208
cmd/host.c
@ -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"
|
||||
);
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -11,7 +11,7 @@ Architecture-specific doc
|
||||
m68k
|
||||
mips
|
||||
nios2
|
||||
sandbox
|
||||
sandbox/index
|
||||
sh
|
||||
x86
|
||||
xtensa
|
||||
|
||||
39
doc/arch/sandbox/block_impl.rst
Normal file
39
doc/arch/sandbox/block_impl.rst
Normal 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
|
||||
12
doc/arch/sandbox/index.rst
Normal file
12
doc/arch/sandbox/index.rst
Normal 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
|
||||
@ -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
|
||||
@ -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
116
doc/usage/cmd/host.rst
Normal 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
117
doc/usage/cmd/ut.rst
Normal 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
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
176
drivers/block/host-uclass.c
Normal 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
142
drivers/block/host_dev.c
Normal 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),
|
||||
};
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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
125
include/sandbox_host.h
Normal 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__ */
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
195
test/dm/host.c
Normal 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);
|
||||
@ -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);
|
||||
}
|
||||
|
||||
68
test/py/tests/fs_helper.py
Normal file
68
test/py/tests/fs_helper.py
Normal 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')
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"""
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user