From 404cdf0cdaacfa8595f8ec268709612d48335f01 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 7 Feb 2025 11:31:00 -0700 Subject: test: Update documentation Update documentation for how to write tests and the 'ut' command. Signed-off-by: Simon Glass --- doc/usage/cmd/ut.rst | 145 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 113 insertions(+), 32 deletions(-) (limited to 'doc/usage/cmd') diff --git a/doc/usage/cmd/ut.rst b/doc/usage/cmd/ut.rst index e794922c806..1acf3126680 100644 --- a/doc/usage/cmd/ut.rst +++ b/doc/usage/cmd/ut.rst @@ -11,34 +11,44 @@ Synopsis :: - ut [-r] [-fs] [-I:] [ []] - - Number of times to run each test - -f Force 'manual' tests to run as well - Run after other tests have run - Name of the 'one' test to run - Test suite to run, or `all` - Name of single test to run + ut [-r] [-f] [-I:] [-r] [ | 'all' []] + ut [-s] info Description ----------- The ut command runs unit tests written in C. +suite + Specifies the suite to run, This can be a single suite, or a comma-separated + list + +test + Speciifes a particular test to run, within a suite, or all suites + +-f + Forces running of a manual test. + +-r + Specifies the number of types to run each test + +-I : + Test to run after other tests have run. This is used to find which test + causes another test to fail. If the one test fails, testing stops + immediately. + 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 info -s`. - Each test is normally run once, although those marked with `UTF_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. +:ref:`develop/tests_writing:mixing python and c` for more information on manual +tests. 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 @@ -50,9 +60,22 @@ the problem. Generally all tests in the suite are run. To run just a single test from the suite, provide the argument. +To specify a list of suites to run, can also be a comma-separated list. + See :ref:`develop/tests_writing:writing c tests` for more information on how to write unit tests. +ut all +~~~~~~ + +Instead of a suite name 'all' may be used to run all tests. + +ut info +~~~~~~~ + +This provides information about the total number of suites and tests. Use the +`-s` flag to show a detailed list of suites. + Example ------- @@ -97,26 +120,84 @@ List available unit-test suites:: 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 + => ut common + Running 14 common tests + Test: cli_ch_test: cread.c + Test: cread_test: cread.c + Test: dm_test_cyclic_running: cyclic.c + Test: print_display_buffer: print.c + Test: print_do_hex_dump: print.c + Test: print_efi_ut: print.c + Test: print_guid: print.c + Test: print_hexdump_line: print.c + Test: print_printf: print.c + Test: snprint: print.c + Test: test_autoboot: test_autoboot.c + Enter password "a" in 1 seconds to stop autoboot + Enter password "a" in 1 seconds to stop autoboot + Enter password "a" in 1 seconds to stop autoboot + Enter password "a" in 1 seconds to stop autoboot + Enter password "a" in 1 seconds to stop autoboot + Enter password "a" in 1 seconds to stop autoboot + Autoboot password unlock not successful + Test: test_event_base: event.c + Test: test_event_probe: event.c + Test: test_event_probe: event.c (flat tree) + Test: test_event_simple: event.c + Tests run: 14, 2611 ms, average 186 ms, skipped: 2, failures: 0 Run just a single test in a suite:: - => ut bloblist bloblist_test_grow - Test: bloblist_test_grow: bloblist.c - Failures: 0 + => ut fdt_overlay change_int_property + Test: fdt_overlay_init: cmd_ut_fdt_overlay.c + Test: change_int_property: cmd_ut_fdt_overlay.c + Tests run: 2, 0 ms, average 0 ms, failures: 0 + +Run a selection of three suites:: + + => ut bloblist,mem,fdt_overlay + Running 14 bloblist tests + Test: align: bloblist.c + Test: bad_blob: bloblist.c + Test: blob: bloblist.c + Test: blob_ensure: bloblist.c + Test: blob_maxsize: bloblist.c + Test: checksum: bloblist.c + Test: cmd_info: bloblist.c + Test: cmd_list: bloblist.c + Test: grow: bloblist.c + Test: init: bloblist.c + Test: reloc: bloblist.c + Test: resize_fail: bloblist.c + Test: resize_last: bloblist.c + Test: shrink: bloblist.c + Tests run: 14, 1 ms, average: 0 ms, failures: 0 + Running 13 mem tests + Test: cp_b: mem_copy.c + Test: cp_l: mem_copy.c + Test: cp_q: mem_copy.c + Test: cp_w: mem_copy.c + Test: ms_b: mem_search.c + Test: ms_cont: mem_search.c + Test: ms_cont_end: mem_search.c + Test: ms_l: mem_search.c + Test: ms_limit: mem_search.c + Test: ms_mult: mem_search.c + Test: ms_quiet: mem_search.c + Test: ms_s: mem_search.c + Test: ms_w: mem_search.c + Tests run: 13, 13 ms, average: 1 ms, failures: 0 + Running 10 fdt_overlay tests + Test: fdt_overlay_init: cmd_ut_fdt_overlay.c + Test: add_node_by_path: cmd_ut_fdt_overlay.c + Test: add_node_by_phandle: cmd_ut_fdt_overlay.c + Test: add_str_property: cmd_ut_fdt_overlay.c + Test: add_subnode_property: cmd_ut_fdt_overlay.c + Test: change_int_property: cmd_ut_fdt_overlay.c + Test: change_str_property: cmd_ut_fdt_overlay.c + Test: local_phandle: cmd_ut_fdt_overlay.c + Test: local_phandles: cmd_ut_fdt_overlay.c + Test: stacked: cmd_ut_fdt_overlay.c + Tests run: 10, 12 ms, average: 1 ms, failures: 0 + Suites run: 3, total tests run: 37, 26 ms, average: 0 ms, failures: 0 + Average test time: 0 ms, worst case 'mem' took 1 ms -- cgit v1.3.1 From 06159a1465fc97d8d7b72b9bea39a396f6e7057c Mon Sep 17 00:00:00 2001 From: Gabriel Dalimonte Date: Mon, 17 Feb 2025 13:26:44 -0500 Subject: fs: fat: add rename The implementation roughly follows the POSIX specification for rename() [1]. The ordering of operations attempting to minimize the chance for data loss in unexpected circumstances. The 'mv' command was implemented as a front end for the rename operation as that is what most users are likely familiar with in terms of behavior. The 'FAT_RENAME' Kconfig option was added to prevent code size increase on size-oriented builds like SPL. [1] https://pubs.opengroup.org/onlinepubs/9799919799/functions/rename.html Signed-off-by: Gabriel Dalimonte --- cmd/fs.c | 14 ++ doc/usage/cmd/mv.rst | 61 ++++++ fs/fat/Kconfig | 7 + fs/fat/fat_write.c | 285 ++++++++++++++++++++++++ fs/fs.c | 65 +++++- include/fat.h | 1 + include/fs.h | 2 + lib/efi_loader/Kconfig | 1 + test/py/tests/test_fs/conftest.py | 121 +++++++++++ test/py/tests/test_fs/fstest_helpers.py | 2 + test/py/tests/test_fs/test_rename.py | 372 ++++++++++++++++++++++++++++++++ 11 files changed, 930 insertions(+), 1 deletion(-) create mode 100644 doc/usage/cmd/mv.rst create mode 100644 test/py/tests/test_fs/test_rename.py (limited to 'doc/usage/cmd') diff --git a/cmd/fs.c b/cmd/fs.c index 3d7e06d6f1e..3faf7627447 100644 --- a/cmd/fs.c +++ b/cmd/fs.c @@ -110,3 +110,17 @@ U_BOOT_CMD( fstypes, 1, 1, do_fstypes_wrapper, "List supported filesystem types", "" ); + +static int do_mv_wrapper(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_mv(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + mv, 5, 1, do_mv_wrapper, + "rename/move a file/directory", + " [] \n" + " - renames/moves a file/directory in 'dev' on 'interface' from\n" + " 'old_path' to 'new_path'" +); diff --git a/doc/usage/cmd/mv.rst b/doc/usage/cmd/mv.rst new file mode 100644 index 00000000000..99864371038 --- /dev/null +++ b/doc/usage/cmd/mv.rst @@ -0,0 +1,61 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +.. index:: + single: mv (command) + +mv command +========== + +Synopsis +-------- + +:: + + mv [] + +Description +----------- + +The mv command renames/moves a file or directory within a filesystem. + +interface + interface for accessing the block device (mmc, sata, scsi, usb, ....) + +dev + device number + +part + partition number, defaults to 0 (whole device) + +old_path + existing path to file/directory + +new_path + new path/name for the rename/move + + +Example +------- + + # Rename file 'foo' in directory 'dir' to 'bar' + mv mmc 0:0 dir/foo dir/bar + + # Move file 'f' from directory 'foo' to existing directory 'bar' renaming + # 'f' to 'g' + mv mmc 0:0 foo/f bar/g + + # Move directory 'abc' in directory 'dir1' into existing directory 'dir2' + mv mmc 0:0 dir1/abc dir2 + +Configuration +------------- + +The mv command is only available if CONFIG_CMD_FS_GENERIC=y. + +Return value +------------ + +The return value $? is set to 0 (true) if the file was successfully +renamed/moved. + +If an error occurs, the return value $? is set to 1 (false). diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig index 9bb11eac9f7..19d52238713 100644 --- a/fs/fat/Kconfig +++ b/fs/fat/Kconfig @@ -13,6 +13,13 @@ config FAT_WRITE This provides support for creating and writing new files to an existing FAT filesystem partition. +config FAT_RENAME + bool "Enable filesystem rename support" + depends on FAT_WRITE + help + This provides support for renaming and moving files within a + FAT filesystem partition. + config FS_FAT_MAX_CLUSTSIZE int "Set maximum possible clustersize" default 65536 diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 86366d03853..d4952e259ff 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -1215,6 +1215,28 @@ static void fill_dentry(fsdata *mydata, dir_entry *dentptr, memcpy(&dentptr->nameext, shortname, SHORT_NAME_SIZE); } +/** + * fat_itr_parent() - modifies the iterator to the parent directory of the + * current iterator. + * + * @itr: iterator positioned anywhere in a directory + * @Return: 0 if the iterator is in the parent directory, -errno otherwise + */ +static int fat_itr_parent(fat_itr *itr) +{ + int ret; + + if (itr->is_root) + return -EIO; + + /* ensure iterator is at the first directory entry */ + ret = fat_move_to_cluster(itr, itr->start_clust); + if (ret) + return ret; + + return fat_itr_resolve(itr, "..", TYPE_DIR); +} + /** * create_link() - inserts a directory entry for a file or directory * @@ -1822,3 +1844,266 @@ exit: free(dotdent); return ret; } + +/** + * check_path_prefix() - ensures one path does not contains another path as a + * prefix. + * + * for example: path foo/bar/baz/qux contains the path prefix foo/bar/baz + * + * note: the iterator may be pointing to any directory entry in the directory + * + * @prefix_clust: start cluster of the final directory in the prefix path + * (the start cluster of 'baz' in the above example) + * @path_itr: iterator of the path to check (an iterator pointing to any + * direntry in 'qux' in the above example) + * Return: -errno on error, 0 if path_itr does not have the directory + * at prefix_clust as an ancestor. + */ +static int check_path_prefix(loff_t prefix_clust, fat_itr *path_itr) +{ + fat_itr itr; + fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata; + int ret; + + /* duplicate fsdata */ + itr = *path_itr; + fsdata = *itr.fsdata; + + /* allocate local fat buffer */ + fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE); + if (!fsdata.fatbuf) { + log_debug("Error: allocating memory\n"); + ret = -ENOMEM; + goto exit; + } + + fsdata.fatbufnum = -1; + itr.fsdata = &fsdata; + + /* ensure iterator is at the first directory entry */ + ret = fat_move_to_cluster(&itr, itr.start_clust); + if (ret) + goto exit; + + while (1) { + if (prefix_clust == itr.start_clust) { + ret = -EINVAL; + goto exit; + } + + if (itr.is_root) { + ret = 0; + goto exit; + } + + /* Should not occur in a well-formed FAT filesystem besides the root */ + if (fat_itr_parent(&itr)) { + log_debug("FAT filesystem corrupt!\n"); + log_debug("dir @ clust %u has no parent direntry\n", + itr.start_clust); + ret = -EIO; + goto exit; + } + } + +exit: + free(fsdata.fatbuf); + return ret; +} + +/** + * fat_rename - rename/move a file or directory + * + * @old_path: path to the existing file/directory + * @new_path: new path/name for the rename/move + * Return: 0 on success, -errno otherwise + */ +int fat_rename(const char *old_path, const char *new_path) +{ + fat_itr *old_itr = NULL, *new_itr = NULL; + fsdata old_datablock = { .fatbuf = NULL, }; + fsdata new_datablock = { .fatbuf = NULL, }; + /* used for START macro */ + fsdata *mydata = &old_datablock; + int ret = -EIO, is_old_dir; + char *old_path_copy, *old_dirname, *old_basename; + char *new_path_copy, *new_dirname, *new_basename; + char l_new_basename[VFAT_MAXLEN_BYTES]; + __u32 old_clust; + dir_entry *found_existing; + /* only set if found_existing != NULL */ + __u32 new_clust; + + old_path_copy = strdup(old_path); + new_path_copy = strdup(new_path); + old_itr = malloc_cache_aligned(sizeof(fat_itr)); + new_itr = malloc_cache_aligned(sizeof(fat_itr)); + if (!old_path_copy || !new_path_copy || !old_itr || !new_itr) { + log_debug("Error: out of memory\n"); + ret = -ENOMEM; + goto exit; + } + split_filename(old_path_copy, &old_dirname, &old_basename); + split_filename(new_path_copy, &new_dirname, &new_basename); + + if (normalize_longname(l_new_basename, new_basename)) { + log_debug("FAT: illegal filename (%s)\n", new_basename); + ret = -EINVAL; + goto exit; + } + + if (!strcmp(old_basename, ".") || !strcmp(old_basename, "..") || + !strcmp(old_basename, "") || !strcmp(l_new_basename, ".") || + !strcmp(l_new_basename, "..") || !strcmp(l_new_basename, "")) { + ret = -EINVAL; + goto exit; + } + + /* checking for old_path == new_path is deferred until they're resolved */ + + /* resolve old_path */ + ret = fat_itr_root(old_itr, &old_datablock); + if (ret) + goto exit; + + ret = fat_itr_resolve(old_itr, old_dirname, TYPE_DIR); + if (ret) { + log_debug("%s doesn't exist (%d)\n", old_dirname, ret); + ret = -ENOENT; + goto exit; + } + + if (!find_directory_entry(old_itr, old_basename)) { + log_debug("%s doesn't exist (%d)\n", old_basename, -ENOENT); + ret = -ENOENT; + goto exit; + } + + /* store clust old_path points to, to relink later */ + total_sector = old_datablock.total_sect; + old_clust = START(old_itr->dent); + is_old_dir = fat_itr_isdir(old_itr); + + /* resolve new_path*/ + ret = fat_itr_root(new_itr, &new_datablock); + if (ret) + goto exit; + + ret = fat_itr_resolve(new_itr, new_dirname, TYPE_DIR); + if (ret) { + log_debug("%s doesn't exist (%d)\n", new_dirname, ret); + ret = -ENOENT; + goto exit; + } + + found_existing = find_directory_entry(new_itr, l_new_basename); + + if (found_existing) { + /* store cluster of new_path since it may need to be deleted */ + new_clust = START(new_itr->dent); + + /* old_path is new_path, noop */ + if (old_clust == new_clust) { + ret = 0; + goto exit; + } + + if (fat_itr_isdir(new_itr) != is_old_dir) { + if (is_old_dir) + ret = -ENOTDIR; + else + ret = -EISDIR; + goto exit; + } + } + + if (is_old_dir) { + ret = check_path_prefix(old_clust, new_itr); + if (ret) + goto exit; + } + + /* create/update dentry to point to old_path's data cluster */ + if (found_existing) { + struct nameext new_name = new_itr->dent->nameext; + __u8 lcase = new_itr->dent->lcase; + + if (is_old_dir) { + int n_entries = fat_dir_entries(new_itr); + + if (n_entries < 0) { + ret = n_entries; + goto exit; + } + if (n_entries > 2) { + log_debug("Error: directory is not empty: %d\n", + n_entries); + ret = -ENOTEMPTY; + goto exit; + } + } + + *new_itr->dent = *old_itr->dent; + new_itr->dent->nameext = new_name; + new_itr->dent->lcase = lcase; + } else { + /* reset iterator to the start of the directory */ + ret = fat_move_to_cluster(new_itr, new_itr->start_clust); + if (ret) + goto exit; + + ret = create_link(new_itr, l_new_basename, old_clust, + old_itr->dent->size, + old_itr->dent->attr | ATTR_ARCH); + if (ret) + goto exit; + } + + ret = flush_dir(new_itr); + if (ret) + goto exit; + + /* with new_path data cluster unreferenced, clear it */ + if (found_existing) { + ret = clear_fatent(&new_datablock, new_clust); + if (ret) + goto exit; + } + + /* update moved directory so the parent is new_path */ + if (is_old_dir) { + __u32 clust = new_itr->start_clust; + dir_entry *dent; + + fat_itr_child(new_itr, new_itr); + dent = find_directory_entry(new_itr, ".."); + if (!dent) { + log_debug("FAT filesystem corrupt!\n"); + log_debug("dir %s has no parent direntry\n", + l_new_basename); + ret = -EIO; + goto exit; + } + set_start_cluster(&new_datablock, dent, clust); + ret = flush_dir(new_itr); + if (ret) + goto exit; + } + + /* refresh old in case write happened to the same block. */ + ret = fat_move_to_cluster(old_itr, old_itr->dent_clust); + if (ret) + goto exit; + + ret = delete_dentry_link(old_itr); +exit: + free(new_datablock.fatbuf); + free(old_datablock.fatbuf); + free(new_itr); + free(old_itr); + free(new_path_copy); + free(old_path_copy); + + return ret; +} diff --git a/fs/fs.c b/fs/fs.c index fdff83719b1..30a8e5010f2 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -213,12 +213,16 @@ static struct fstype_info fstypes[] = { .unlink = fs_unlink_unsupported, .mkdir = fs_mkdir_unsupported, #endif - .rename = fs_rename_unsupported, .uuid = fat_uuid, .opendir = fat_opendir, .readdir = fat_readdir, .closedir = fat_closedir, .ln = fs_ln_unsupported, +#if CONFIG_IS_ENABLED(FAT_RENAME) && !IS_ENABLED(CONFIG_XPL_BUILD) + .rename = fat_rename, +#else + .rename = fs_rename_unsupported, +#endif }, #endif @@ -1007,6 +1011,65 @@ int do_ln(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[], return 0; } +int do_mv(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[], + int fstype) +{ + struct fs_dir_stream *dirs; + char *src = argv[3]; + char *dst = argv[4]; + char *new_dst = NULL; + int ret = 1; + + if (argc != 5) { + ret = CMD_RET_USAGE; + goto exit; + } + + if (fs_set_blk_dev(argv[1], argv[2], fstype)) + goto exit; + + dirs = fs_opendir(dst); + /* dirs being valid means dst points to an existing directory. + * mv should copy the file/dir (keeping the same name) into the + * directory + */ + if (dirs) { + char *src_name = strrchr(src, '/'); + int dst_len; + + if (src_name) + src_name += 1; + else + src_name = src; + + dst_len = strlen(dst); + new_dst = calloc(1, dst_len + strlen(src_name) + 2); + strcpy(new_dst, dst); + + /* If there is already a trailing slash, don't add another */ + if (new_dst[dst_len - 1] != '/') { + new_dst[dst_len] = '/'; + dst_len += 1; + } + + strcpy(new_dst + dst_len, src_name); + dst = new_dst; + } + fs_closedir(dirs); + + if (fs_set_blk_dev(argv[1], argv[2], fstype)) + goto exit; + + if (fs_rename(src, dst)) + goto exit; + + ret = 0; + +exit: + free(new_dst); + return ret; +} + int do_fs_types(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) { struct fstype_info *drv = fstypes; diff --git a/include/fat.h b/include/fat.h index 3dce99a23cf..ca97880de12 100644 --- a/include/fat.h +++ b/include/fat.h @@ -206,6 +206,7 @@ int fat_opendir(const char *filename, struct fs_dir_stream **dirsp); int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp); void fat_closedir(struct fs_dir_stream *dirs); int fat_unlink(const char *filename); +int fat_rename(const char *old_path, const char *new_path); int fat_mkdir(const char *dirname); void fat_close(void); void *fat_next_cluster(fat_itr *itr, unsigned int *nbytes); diff --git a/include/fs.h b/include/fs.h index 5b272eb9f5e..54449faf2e5 100644 --- a/include/fs.h +++ b/include/fs.h @@ -302,6 +302,8 @@ int do_mkdir(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[], int fstype); int do_ln(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[], int fstype); +int do_mv(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[], + int fstype); /* * Determine the UUID of the specified filesystem and print it. Optionally it is diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index d4f6b56afaa..6130af14337 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -27,6 +27,7 @@ config EFI_LOADER select REGEX imply FAT imply FAT_WRITE + imply FAT_RENAME imply USB_KEYBOARD_FN_KEYS imply VIDEO_ANSI help diff --git a/test/py/tests/test_fs/conftest.py b/test/py/tests/test_fs/conftest.py index af2adaf1645..7bfcf41ed6f 100644 --- a/test/py/tests/test_fs/conftest.py +++ b/test/py/tests/test_fs/conftest.py @@ -18,6 +18,7 @@ supported_fs_fat = ['fat12', 'fat16'] supported_fs_mkdir = ['fat12', 'fat16', 'fat32'] supported_fs_unlink = ['fat12', 'fat16', 'fat32'] supported_fs_symlink = ['ext4'] +supported_fs_rename = ['fat12', 'fat16', 'fat32'] # # Filesystem test specific setup @@ -55,6 +56,7 @@ def pytest_configure(config): global supported_fs_mkdir global supported_fs_unlink global supported_fs_symlink + global supported_fs_rename def intersect(listA, listB): return [x for x in listA if x in listB] @@ -68,6 +70,7 @@ def pytest_configure(config): supported_fs_mkdir = intersect(supported_fs, supported_fs_mkdir) supported_fs_unlink = intersect(supported_fs, supported_fs_unlink) supported_fs_symlink = intersect(supported_fs, supported_fs_symlink) + supported_fs_rename = intersect(supported_fs, supported_fs_rename) def pytest_generate_tests(metafunc): """Parametrize fixtures, fs_obj_xxx @@ -99,6 +102,9 @@ def pytest_generate_tests(metafunc): if 'fs_obj_symlink' in metafunc.fixturenames: metafunc.parametrize('fs_obj_symlink', supported_fs_symlink, indirect=True, scope='module') + if 'fs_obj_rename' in metafunc.fixturenames: + metafunc.parametrize('fs_obj_rename', supported_fs_rename, + indirect=True, scope='module') # # Helper functions @@ -527,6 +533,121 @@ def fs_obj_symlink(request, u_boot_config): call('rm -rf %s' % scratch_dir, shell=True) call('rm -f %s' % fs_img, shell=True) +# +# Fixture for rename test +# +@pytest.fixture() +def fs_obj_rename(request, u_boot_config): + """Set up a file system to be used in rename tests. + + Args: + request: Pytest request object. + u_boot_config: U-Boot configuration. + + Return: + A fixture for rename tests, i.e. a triplet of file system type, + volume file name, and dictionary of test identifier and md5val. + """ + def new_rand_file(path): + check_call('dd if=/dev/urandom of=%s bs=1K count=1' % path, shell=True) + + def file_hash(path): + out = check_output( + 'dd if=%s bs=1K skip=0 count=1 2> /dev/null | md5sum' % path, + shell=True + ) + return out.decode().split()[0] + + fs_type = request.param + fs_img = '' + + fs_ubtype = fstype_to_ubname(fs_type) + check_ubconfig(u_boot_config, fs_ubtype) + + mount_dir = u_boot_config.persistent_data_dir + '/scratch' + + try: + check_call('mkdir -p %s' % mount_dir, shell=True) + except CalledProcessError as err: + pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err)) + call('rm -f %s' % fs_img, shell=True) + return + + try: + md5val = {} + # Test Case 1 + check_call('mkdir %s/test1' % mount_dir, shell=True) + new_rand_file('%s/test1/file1' % mount_dir) + md5val['test1'] = file_hash('%s/test1/file1' % mount_dir) + + # Test Case 2 + check_call('mkdir %s/test2' % mount_dir, shell=True) + new_rand_file('%s/test2/file1' % mount_dir) + new_rand_file('%s/test2/file_exist' % mount_dir) + md5val['test2'] = file_hash('%s/test2/file1' % mount_dir) + + # Test Case 3 + check_call('mkdir -p %s/test3/dir1' % mount_dir, shell=True) + new_rand_file('%s/test3/dir1/file1' % mount_dir) + md5val['test3'] = file_hash('%s/test3/dir1/file1' % mount_dir) + + # Test Case 4 + check_call('mkdir -p %s/test4/dir1' % mount_dir, shell=True) + check_call('mkdir -p %s/test4/dir2/dir1' % mount_dir, shell=True) + new_rand_file('%s/test4/dir1/file1' % mount_dir) + md5val['test4'] = file_hash('%s/test4/dir1/file1' % mount_dir) + + # Test Case 5 + check_call('mkdir -p %s/test5/dir1' % mount_dir, shell=True) + new_rand_file('%s/test5/file2' % mount_dir) + md5val['test5'] = file_hash('%s/test5/file2' % mount_dir) + + # Test Case 6 + check_call('mkdir -p %s/test6/dir2/existing' % mount_dir, shell=True) + new_rand_file('%s/test6/existing' % mount_dir) + md5val['test6'] = file_hash('%s/test6/existing' % mount_dir) + + # Test Case 7 + check_call('mkdir -p %s/test7/dir1' % mount_dir, shell=True) + check_call('mkdir -p %s/test7/dir2/dir1' % mount_dir, shell=True) + new_rand_file('%s/test7/dir2/dir1/file1' % mount_dir) + md5val['test7'] = file_hash('%s/test7/dir2/dir1/file1' % mount_dir) + + # Test Case 8 + check_call('mkdir -p %s/test8/dir1' % mount_dir, shell=True) + new_rand_file('%s/test8/dir1/file1' % mount_dir) + md5val['test8'] = file_hash('%s/test8/dir1/file1' % mount_dir) + + # Test Case 9 + check_call('mkdir -p %s/test9/dir1/nested/inner' % mount_dir, shell=True) + new_rand_file('%s/test9/dir1/nested/inner/file1' % mount_dir) + + # Test Case 10 + check_call('mkdir -p %s/test10' % mount_dir, shell=True) + new_rand_file('%s/test10/file1' % mount_dir) + md5val['test10'] = file_hash('%s/test10/file1' % mount_dir) + + # Test Case 11 + check_call('mkdir -p %s/test11/dir1' % mount_dir, shell=True) + new_rand_file('%s/test11/dir1/file1' % mount_dir) + md5val['test11'] = file_hash('%s/test11/dir1/file1' % mount_dir) + + try: + # 128MiB volume + fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB', mount_dir) + except CalledProcessError as err: + pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err)) + return + + except CalledProcessError: + pytest.skip('Setup failed for filesystem: ' + fs_type) + return + else: + yield [fs_ubtype, fs_img, md5val] + finally: + call('rm -rf %s' % mount_dir, shell=True) + call('rm -f %s' % fs_img, shell=True) + # # Fixture for fat test # diff --git a/test/py/tests/test_fs/fstest_helpers.py b/test/py/tests/test_fs/fstest_helpers.py index faec2982489..c1447b4d43e 100644 --- a/test/py/tests/test_fs/fstest_helpers.py +++ b/test/py/tests/test_fs/fstest_helpers.py @@ -9,5 +9,7 @@ def assert_fs_integrity(fs_type, fs_img): try: if fs_type == 'ext4': check_call('fsck.ext4 -n -f %s' % fs_img, shell=True) + elif fs_type in ['fat12', 'fat16', 'fat32']: + check_call('fsck.fat -n %s' % fs_img, shell=True) except CalledProcessError: raise diff --git a/test/py/tests/test_fs/test_rename.py b/test/py/tests/test_fs/test_rename.py new file mode 100644 index 00000000000..df2b2fd2945 --- /dev/null +++ b/test/py/tests/test_fs/test_rename.py @@ -0,0 +1,372 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2025 Gabriel Dalimonte +# +# U-Boot File System:rename Test + + +import pytest + +from fstest_defs import * +from fstest_helpers import assert_fs_integrity + +@pytest.mark.boardspec('sandbox') +@pytest.mark.slow +class TestRename(object): + def test_rename1(self, u_boot_console, fs_obj_rename): + """ + Test Case 1 - rename a file (successful mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 1 - rename a file'): + d = 'test1' + src = '%s/file1' % d + dst = '%s/file2' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, dst), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'load host 0:0 %x /%s' % (ADDR, dst), + 'printenv filesize']) + assert('filesize=400' in output) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (d), + ]) + assert('file1' not in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val['test1'] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_rename2(self, u_boot_console, fs_obj_rename): + """ + Test Case 2 - rename a file to an existing file (successful mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 2 - rename a file to an existing file'): + d = 'test2' + src = '%s/file1' % d + dst = '%s/file_exist' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, dst), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'load host 0:0 %x /%s' % (ADDR, dst), + 'printenv filesize']) + assert('filesize=400' in output) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (d), + ]) + assert('file1' not in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val['test2'] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_rename3(self, u_boot_console, fs_obj_rename): + """ + Test Case 3 - rename a directory (successful mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 3 - rename a directory'): + d = 'test3' + src = '%s/dir1' % d + dst = '%s/dir2' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, dst), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'load host 0:0 %x /%s/file1' % (ADDR, dst), + 'printenv filesize']) + assert('filesize=400' in output) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (d), + ]) + assert('dir1' not in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val['test3'] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_rename4(self, u_boot_console, fs_obj_rename): + """ + Test Case 4 - rename a directory to an existing directory (successful + mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 4 - rename a directory to an existing directory'): + d = 'test4' + src = '%s/dir1' % d + dst = '%s/dir2' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, dst), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'load host 0:0 %x /%s/dir1/file1' % (ADDR, dst), + 'printenv filesize']) + assert('filesize=400' in output) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (d), + ]) + assert('dir1' not in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val['test4'] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_rename5(self, u_boot_console, fs_obj_rename): + """ + Test Case 5 - rename a directory to an existing file (failed mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 5 - rename a directory to an existing file'): + d = 'test5' + src = '%s/dir1' % d + dst = '%s/file2' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, dst), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (d), + ]) + assert('dir1' in ''.join(output)) + assert('file2' in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'load host 0:0 %x /%s' % (ADDR, dst), + 'printenv filesize']) + assert('filesize=400' in output) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val['test5'] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_rename6(self, u_boot_console, fs_obj_rename): + """ + Test Case 6 - rename a file to an existing empty directory (failed mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 6 - rename a file to an existing empty directory'): + d = 'test6' + src = '%s/existing' % d + dst = '%s/dir2' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, dst), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'load host 0:0 %x /%s' % (ADDR, src), + 'printenv filesize']) + assert('filesize=400' in output) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (d), + ]) + assert('dir2' in ''.join(output)) + assert('existing' in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val['test6'] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_rename7(self, u_boot_console, fs_obj_rename): + """ + Test Case 7 - rename a directory to a non-empty directory (failed mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 7 - rename a directory to a non-empty directory'): + d = 'test7' + src = '%s/dir1' % d + dst = '%s/dir2' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, dst), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'load host 0:0 %x /%s/dir1/file1' % (ADDR, dst), + 'printenv filesize']) + assert('filesize=400' in output) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (d), + ]) + assert('dir1' in ''.join(output)) + assert('dir2' in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val['test7'] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_rename8(self, u_boot_console, fs_obj_rename): + """ + Test Case 8 - rename a directory inside itself (failed mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 8 - rename a directory inside itself'): + d = 'test8' + src = '%s/dir1' % d + dst = '%s/dir1/dir1' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, dst), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'load host 0:0 %x /%s/file1' % (ADDR, src), + 'printenv filesize']) + assert('filesize=400' in output) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (d), + ]) + assert('dir1' in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (src), + ]) + assert('file1' in ''.join(output)) + assert('dir1' not in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val['test8'] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_rename9(self, u_boot_console, fs_obj_rename): + """ + Test Case 9 - rename a directory inside itself with backtracks (failed + mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 9 - rename a directory inside itself with backtracks'): + d = 'test9' + src = '%s/dir1/nested' % d + dst = '%s/dir1/nested/inner/./../../../dir1/nested/inner/another' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, dst), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s/dir1' % (d), + ]) + assert('nested' in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (src), + ]) + assert('inner' in ''.join(output)) + assert('nested' not in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_rename10(self, u_boot_console, fs_obj_rename): + """ + Test Case 10 - rename a file to itself (successful mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 10 - rename a file to itself'): + d = 'test10' + src = '%s/file1' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, src), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'load host 0:0 %x /%s' % (ADDR, src), + 'printenv filesize']) + assert('filesize=400' in output) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (d), + ]) + assert('file1' in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val['test10'] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_rename11(self, u_boot_console, fs_obj_rename): + """ + Test Case 11 - rename a directory to itself (successful mv) + """ + fs_type, fs_img, md5val = fs_obj_rename + with u_boot_console.log.section('Test Case 11 - rename a directory to itself'): + # / at the end here is intentional. Ensures trailing / doesn't + # affect mv producing an updated dst path for fs_rename + d = 'test11/' + src = '%sdir1' % d + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'mv host 0:0 %s %s' % (src, d), + ]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command_list([ + 'load host 0:0 %x /%s/file1' % (ADDR, src), + 'printenv filesize']) + assert('filesize=400' in output) + + output = u_boot_console.run_command_list([ + 'ls host 0:0 %s' % (d), + ]) + assert('dir1' in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val['test11'] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) -- cgit v1.3.1 From c6862debd2292b9a382f9cb92d4f2e51820702b5 Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Wed, 5 Mar 2025 15:26:46 +0100 Subject: doc: cmd: wget: document cacert subcommand Document the 'wget cacert' subcommand which allows to configure root (CA) certificates for HTTPS. Signed-off-by: Jerome Forissier Acked-by: Ilias Apalodimas --- doc/usage/cmd/wget.rst | 82 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 2 deletions(-) (limited to 'doc/usage/cmd') diff --git a/doc/usage/cmd/wget.rst b/doc/usage/cmd/wget.rst index 48bedf1e845..cc82e495a29 100644 --- a/doc/usage/cmd/wget.rst +++ b/doc/usage/cmd/wget.rst @@ -12,7 +12,9 @@ Synopsis :: wget [address] [host:]path - wget [address] url # lwIP only + wget [address] url # lwIP only + wget cacert none|optional|required # lwIP only + wget cacert
# lwIP only Description @@ -54,6 +56,32 @@ address url HTTP or HTTPS URL, that is: http[s]://[:]/. +The cacert (stands for 'Certification Authority certificates') subcommand is +used to provide root certificates for the purpose of HTTPS authentication. It +also allows to enable or disable authentication. + +wget cacert
+ +address + memory address of the root certificates in X509 DER format + +size + the size of the root certificates + +wget cacert none|optional|required + +none + certificate verification is disabled. HTTPS is used without any server + authentication (unsafe) +optional + certificate verification is enabled provided root certificates have been + provided via wget cacert or wget cacert builtin. Otherwise + HTTPS is used without any server authentication (unsafe). +required + certificate verification is mandatory. If no root certificates have been + configured, HTTPS transfers will fail. + + Examples -------- @@ -97,11 +125,61 @@ In the example the following steps are executed: 1694892032 bytes transferred in 492181 ms (3.3 MiB/s) Bytes transferred = 1694892032 (65060000 hex) +Here is an example showing how to configure built-in root certificates as +well as providing some at run time. In this example it is assumed that +CONFIG_WGET_BUILTIN_CACERT_PATH=DigiCertTLSRSA4096RootG5.crt downloaded from +https://cacerts.digicert.com/DigiCertTLSRSA4096RootG5.crt. + +:: + + # Make sure IP is configured + => dhcp + # When built-in certificates are configured, authentication is mandatory + # (i.e., "wget cacert required"). Use a test server... + => wget https://digicert-tls-rsa4096-root-g5.chain-demos.digicert.com/ + 1864 bytes transferred in 1 ms (1.8 MiB/s) + Bytes transferred = 1864 (748 hex) + # Another server not signed against Digicert will fail + => wget https://www.google.com/ + Certificate verification failed + + HTTP client error 4 + # Disable authentication to allow the command to proceed anyways + => wget cacert none + => wget https://www.google.com/ + WARNING: no CA certificates, HTTPS connections not authenticated + 16683 bytes transferred in 15 ms (1.1 MiB/s) + Bytes transferred = 16683 (412b hex) + # Force verification but unregister the CA certificates + => wget cacert required + => wget cacert 0 0 + # Unsurprisingly, download fails + => wget https://digicert-tls-rsa4096-root-g5.chain-demos.digicert.com/ + Error: cacert authentication mode is 'required' but no CA certificates given + # Get the same certificates as above from the network + => wget cacert none + => wget https://cacerts.digicert.com/DigiCertTLSRSA4096RootG5.crt + WARNING: no CA certificates, HTTPS connections not authenticated + 1386 bytes transferred in 1 ms (1.3 MiB/s) + Bytes transferred = 1386 (56a hex) + # Register them and force authentication + => wget cacert $fileaddr $filesize + => wget cacert required + # Authentication is operational again + => wget https://digicert-tls-rsa4096-root-g5.chain-demos.digicert.com/ + 1864 bytes transferred in 1 ms (1.8 MiB/s) + Bytes transferred = 1864 (748 hex) + # The builtin certificates can be restored at any time + => wget cacert builtin + Configuration ------------- The command is only available if CONFIG_CMD_WGET=y. -To enable lwIP support set CONFIG_NET_LWIP=y. +To enable lwIP support set CONFIG_NET_LWIP=y. In this case, root certificates +support can be enabled via CONFIG_WGET_BUILTIN_CACERT=y +CONFIG_WGET_BUILTIN_CACERT_PATH= (for built-in certificates) and/or +CONFIG_WGET_CACERT=y (for the wget cacert command). TCP Selective Acknowledgments in the legacy network stack can be enabled via CONFIG_PROT_TCP_SACK=y. This will improve the download speed. Selective -- cgit v1.3.1 From 62b2d933cf7a486d34e379aa11076c5b62a7a239 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 20 Feb 2025 15:54:39 +0200 Subject: doc: update meminfo with arch specific information Since we added support in meminfo to dump live page tables, describe the only working architecture for now (aarch64) and add links to public documentation for further reading. Signed-off-by: Ilias Apalodimas --- doc/usage/cmd/meminfo.rst | 71 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 18 deletions(-) (limited to 'doc/usage/cmd') diff --git a/doc/usage/cmd/meminfo.rst b/doc/usage/cmd/meminfo.rst index 6c94493cccc..e10bdc6832c 100644 --- a/doc/usage/cmd/meminfo.rst +++ b/doc/usage/cmd/meminfo.rst @@ -18,7 +18,8 @@ Description The meminfo command shows the amount of memory. If ``CONFIG_CMD_MEMINFO_MAP`` is enabled, then it also shows the layout of memory used by U-Boot and the region -which is free for use by images. +which is free for use by images. In architectures that support it, it also prints +the mapped pages and their permissions. The latter is architecture specific. The layout of memory is set up before relocation, within the init sequence in ``board_init_f()``, specifically the various ``reserve_...()`` functions. This @@ -26,8 +27,9 @@ The layout of memory is set up before relocation, within the init sequence in ending with the stack. This results in the maximum possible amount of memory being left free for image-loading. -The meminfo command writes the DRAM size, then the rest of its outputs in 5 -columns: +The meminfo command writes the DRAM size. If the architecture also supports it, +page table entries will be shown next. Finally the rest of the outputs are +printed in 5 columns: Region Name of the region @@ -99,28 +101,61 @@ free Free memory, which is available for loading images. The base address of this is ``gd->ram_base`` which is generally set by ``CFG_SYS_SDRAM_BASE``. +Aarch64 specific flags +---------------------- + +More information on the output can be found +Chapter D8 - The AArch64 Virtual Memory System Architecture at +https://developer.arm.com/documentation/ddi0487/latest/ + +In short, for a stage 1 translation regime the following apply: + +* RWX: Pages mapped with Read, Write and Execute permissions +* RO: Pages mapped with Read-Only permissions +* PXN: PXN (Privileged Execute Never) applies to execution at EL1 and above +* UXN: UXN (Unprivileged Execute Never) applies to EL0 + Example ------- This example shows output with both ``CONFIG_CMD_MEMINFO`` and -``CONFIG_CMD_MEMINFO_MAP`` enabled:: - - => meminfo - DRAM: 256 MiB +``CONFIG_CMD_MEMINFO_MAP`` enabled for aarch64 qemu:: + + DRAM: 8 GiB + Walking pagetable at 000000023ffe0000, va_bits: 40. Using 4 levels + [0x0000023ffe1000] | Table | | | + [0x0000023ffe2000] | Table | | | + [0x00000000000000 - 0x00000008000000] | Block | RWX | Normal | Inner-shareable + [0x00000008000000 - 0x00000040000000] | Block | PXN UXN | Device-nGnRnE | Non-shareable + [0x00000040000000 - 0x00000200000000] | Block | RWX | Normal | Inner-shareable + [0x0000023ffea000] | Table | | | + [0x00000200000000 - 0x0000023f600000] | Block | RWX | Normal | Inner-shareable + [0x0000023ffeb000] | Table | | | + [0x0000023f600000 - 0x0000023f68c000] | Pages | RWX | Normal | Inner-shareable + [0x0000023f68c000 - 0x0000023f74f000] | Pages | RO | Normal | Inner-shareable + [0x0000023f74f000 - 0x0000023f794000] | Pages | PXN UXN RO | Normal | Inner-shareable + [0x0000023f794000 - 0x0000023f79d000] | Pages | PXN UXN | Normal | Inner-shareable + [0x0000023f79d000 - 0x0000023f800000] | Pages | RWX | Normal | Inner-shareable + [0x0000023f800000 - 0x00000240000000] | Block | RWX | Normal | Inner-shareable + [0x00000240000000 - 0x00004000000000] | Block | RWX | Normal | Inner-shareable + [0x0000023ffe3000] | Table | | | + [0x00004010000000 - 0x00004020000000] | Block | PXN UXN | Device-nGnRnE | Non-shareable + [0x0000023ffe4000] | Table | | | + [0x00008000000000 - 0x00010000000000] | Block | PXN UXN | Device-nGnRnE | Non-shareable Region Base Size End Gap ------------------------------------------------ - video f000000 1000000 10000000 - code ec3a000 3c5d28 efffd28 2d8 - malloc 8c38000 6002000 ec3a000 0 - board_info 8c37f90 68 8c37ff8 8 - global_data 8c37d80 208 8c37f88 8 - devicetree 8c33000 4d7d 8c37d7d 3 - bootstage 8c32c20 3c8 8c32fe8 18 - bloblist 8c32000 400 8c32400 820 - stack 7c31ff0 1000000 8c31ff0 10 - free 0 7c31ff0 7c31ff0 0 - + video 23f7e0000 800000 23ffe0000 + code 23f68a000 156000 23f7e0000 0 + malloc 23e64a000 1040000 23f68a000 0 + board_info 23e649f80 78 23e649ff8 8 + global_data 23e649df0 188 23e649f78 8 + devicetree 23e549df0 100000 23e649df0 0 + bloblist 23e547000 2000 23e549000 df0 + stack 23d546ff0 1000000 23e546ff0 10 + lmb 23d546ff0 0 23d546ff0 0 + lmb 23d543000 3ff0 23d546ff0 0 + free 40000000 23d543000 27d543000 ffffffffc0000000 Return value ------------ -- cgit v1.3.1 From 59001e758ae85f3226f7fab97027d6bff7baa00b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 15 Mar 2025 14:26:05 +0000 Subject: acpi: Support checking checksums When the ACPI tables come from an earlier bootloader it is helpful to see whether the checksums are correct or not. Add a -c flag to the 'acpi list' command to support that. Signed-off-by: Simon Glass --- cmd/acpi.c | 57 ++++++++++++++++++++++++++++++++------------------ doc/usage/cmd/acpi.rst | 20 +++++++++++++++++- test/dm/acpi.c | 46 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 21 deletions(-) (limited to 'doc/usage/cmd') diff --git a/cmd/acpi.c b/cmd/acpi.c index 2273176f106..bb243202009 100644 --- a/cmd/acpi.c +++ b/cmd/acpi.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,17 @@ DECLARE_GLOBAL_DATA_PTR; +static const char *show_checksum(void *ptr, uint size, bool chksums) +{ + uint checksum; + + if (!chksums) + return ""; + checksum = table_compute_checksum(ptr, size); + + return checksum ? " bad" : " OK"; +} + /** * dump_hdr() - Dump an ACPI header * @@ -23,16 +35,17 @@ DECLARE_GLOBAL_DATA_PTR; * * @hdr: ACPI header to dump */ -static void dump_hdr(struct acpi_table_header *hdr) +static void dump_hdr(struct acpi_table_header *hdr, bool chksums) { bool has_hdr = memcmp(hdr->signature, "FACS", ACPI_NAME_LEN); printf("%.*s %16lx %5x", ACPI_NAME_LEN, hdr->signature, (ulong)map_to_sysmem(hdr), hdr->length); if (has_hdr) { - printf(" v%02d %.6s %.8s %x %.4s %x\n", hdr->revision, + printf(" v%02d %.6s %.8s %x %.4s %x%s\n", hdr->revision, hdr->oem_id, hdr->oem_table_id, hdr->oem_revision, - hdr->creator_id, hdr->creator_revision); + hdr->creator_id, hdr->creator_revision, + show_checksum(hdr, hdr->length, chksums)); } else { printf("\n"); } @@ -52,22 +65,22 @@ static int dump_table_name(const char *sig) return 0; } -static void list_fadt(struct acpi_fadt *fadt) +static void list_fadt(struct acpi_fadt *fadt, bool chksums) { if (fadt->header.revision >= 3 && fadt->x_dsdt) - dump_hdr(nomap_sysmem(fadt->x_dsdt, 0)); + dump_hdr(nomap_sysmem(fadt->x_dsdt, 0), chksums); else if (fadt->dsdt) - dump_hdr(nomap_sysmem(fadt->dsdt, 0)); + dump_hdr(nomap_sysmem(fadt->dsdt, 0), chksums); if (!IS_ENABLED(CONFIG_X86) && !IS_ENABLED(CONFIG_SANDBOX) && !(fadt->flags & ACPI_FADT_HW_REDUCED_ACPI)) log_err("FADT not ACPI-hardware-reduced-compliant\n"); if (fadt->header.revision >= 3 && fadt->x_firmware_ctrl) - dump_hdr(nomap_sysmem(fadt->x_firmware_ctrl, 0)); + dump_hdr(nomap_sysmem(fadt->x_firmware_ctrl, 0), chksums); else if (fadt->firmware_ctrl) - dump_hdr(nomap_sysmem(fadt->firmware_ctrl, 0)); + dump_hdr(nomap_sysmem(fadt->firmware_ctrl, 0), chksums); } -static void list_rsdt(struct acpi_rsdp *rsdp) +static void list_rsdt(struct acpi_rsdp *rsdp, bool chksums) { int len, i, count; struct acpi_rsdt *rsdt; @@ -75,11 +88,11 @@ static void list_rsdt(struct acpi_rsdp *rsdp) if (rsdp->rsdt_address) { rsdt = nomap_sysmem(rsdp->rsdt_address, 0); - dump_hdr(&rsdt->header); + dump_hdr(&rsdt->header, chksums); } if (rsdp->xsdt_address) { xsdt = nomap_sysmem(rsdp->xsdt_address, 0); - dump_hdr(&xsdt->header); + dump_hdr(&xsdt->header, chksums); len = xsdt->header.length - sizeof(xsdt->header); count = len / sizeof(u64); } else if (rsdp->rsdt_address) { @@ -100,24 +113,28 @@ static void list_rsdt(struct acpi_rsdp *rsdp) if (!entry) break; hdr = nomap_sysmem(entry, 0); - dump_hdr(hdr); + dump_hdr(hdr, chksums); if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN)) - list_fadt((struct acpi_fadt *)hdr); + list_fadt((struct acpi_fadt *)hdr, chksums); } } -static void list_rsdp(struct acpi_rsdp *rsdp) +static void list_rsdp(struct acpi_rsdp *rsdp, bool chksums) { - printf("RSDP %16lx %5x v%02d %.6s\n", (ulong)map_to_sysmem(rsdp), - rsdp->length, rsdp->revision, rsdp->oem_id); - list_rsdt(rsdp); + printf("RSDP %16lx %5x v%02d %.6s%s%s\n", + (ulong)map_to_sysmem(rsdp), rsdp->length, rsdp->revision, + rsdp->oem_id, show_checksum(rsdp, 0x14, chksums), + show_checksum(rsdp, rsdp->length, chksums)); + list_rsdt(rsdp, chksums); } static int do_acpi_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct acpi_rsdp *rsdp; + bool chksums; + chksums = argc >= 2 && !strcmp("-c", argv[1]); rsdp = map_sysmem(gd_acpi_start(), 0); if (!rsdp) { printf("No ACPI tables present\n"); @@ -125,7 +142,7 @@ static int do_acpi_list(struct cmd_tbl *cmdtp, int flag, int argc, } printf("Name Base Size Detail\n" "---- ---------------- ----- ----------------------------\n"); - list_rsdp(rsdp); + list_rsdp(rsdp, chksums); return 0; } @@ -187,13 +204,13 @@ static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc, } U_BOOT_LONGHELP(acpi, - "list - list ACPI tables\n" + "list [-c] - list ACPI tables [check checksums]\n" "acpi items [-d] - List/dump each piece of ACPI data from devices\n" "acpi set [] - Set or show address of ACPI tables\n" "acpi dump - Dump ACPI table"); U_BOOT_CMD_WITH_SUBCMDS(acpi, "ACPI tables", acpi_help_text, - U_BOOT_SUBCMD_MKENT(list, 1, 1, do_acpi_list), + U_BOOT_SUBCMD_MKENT(list, 2, 1, do_acpi_list), U_BOOT_SUBCMD_MKENT(items, 2, 1, do_acpi_items), U_BOOT_SUBCMD_MKENT(set, 2, 1, do_acpi_set), U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_acpi_dump)); diff --git a/doc/usage/cmd/acpi.rst b/doc/usage/cmd/acpi.rst index 9f30972fe53..e652968d584 100644 --- a/doc/usage/cmd/acpi.rst +++ b/doc/usage/cmd/acpi.rst @@ -11,7 +11,7 @@ Synopsis :: - acpi list + acpi list [-c] acpi items [-d] acpi dump acpi set
@@ -38,6 +38,9 @@ List the ACPI tables that have been generated. Each table has a 4-character table name (e.g. SSDT, FACS) and has a format defined by the `ACPI specification`_. +The `-c` flag tells U-Boot to verify the checksums and print 'OK' or 'BAD' next +to each table. + U-Boot does not currently support decoding the tables. Unlike devicetree, ACPI tables have no regular schema and also some include bytecode, so decoding the tables requires a lot of code. @@ -259,5 +262,20 @@ pointer:: WAET bff76a3b 28 v01 BOCHS BXPC 1 BXPC 1 SSDT bff95040 c5 v02 COREv4 COREBOOT 2a CORE 20221020 +This shows checking that the checksums are correct for each table:: + + => acpi list -c + Name Base Size Detail + ---- ---------------- ----- ---------------------------- + RSDP bec9a000 24 v00 BOCHS OK OK + RSDT bec9bd4a 38 v01 BOCHS BXPC 1 BXPC 1 OK + FACP bec9bb46 74 v01 BOCHS BXPC 1 BXPC 1 OK + DSDT bec9a080 1ac6 v01 BOCHS BXPC 1 BXPC 1 OK + FACS bec9a040 40 + APIC bec9bbba 78 v03 BOCHS BXPC 1 BXPC 1 OK + HPET bec9bc32 38 v01 BOCHS BXPC 1 BXPC 1 OK + SRAT bec9bc6a b8 v01 BOCHS BXPC 1 BXPC 1 OK + WAET bec9bd22 28 v01 BOCHS BXPC 1 BXPC 1 OK + .. _`ACPI specification`: https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf diff --git a/test/dm/acpi.c b/test/dm/acpi.c index f98f9d1e74b..db012b6d2f1 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -439,6 +439,52 @@ static int dm_test_acpi_cmd_list(struct unit_test_state *uts) } DM_TEST(dm_test_acpi_cmd_list, UTF_SCAN_PDATA | UTF_SCAN_FDT | UTF_CONSOLE); +/* Test 'acpi list -c' command */ +static int dm_test_acpi_cmd_list_chksum(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + ulong addr; + void *buf; + + buf = memalign(16, BUF_SIZE); + ut_assertnonnull(buf); + addr = map_to_sysmem(buf); + ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr)); + + ut_assertok(acpi_write_dev_tables(&ctx)); + + run_command("acpi list -c", 0); + ut_assert_nextline("Name Base Size Detail"); + ut_assert_nextline("---- ---------------- ----- ----------------------------"); + ut_assert_nextline("RSDP %16lx %5zx v02 U-BOOT OK OK", addr, + sizeof(struct acpi_rsdp)); + addr = ALIGN(addr + sizeof(struct acpi_rsdp), 16); + ut_assert_nextline("RSDT %16lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0 OK", + addr, sizeof(struct acpi_table_header) + + 3 * sizeof(u32), OEM_REVISION); + addr = ALIGN(addr + sizeof(struct acpi_rsdt), 16); + ut_assert_nextline("XSDT %16lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0 OK", + addr, sizeof(struct acpi_table_header) + + 3 * sizeof(u64), OEM_REVISION); + addr = ALIGN(addr + sizeof(struct acpi_xsdt), 64); + ut_assert_nextline("DMAR %16lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0 OK", + addr, sizeof(struct acpi_dmar), OEM_REVISION); + addr = ALIGN(addr + sizeof(struct acpi_dmar), 16); + ut_assert_nextline("DMAR %16lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0 OK", + addr, sizeof(struct acpi_dmar), OEM_REVISION); + addr = ALIGN(addr + sizeof(struct acpi_dmar), 16); + ut_assert_nextline("DMAR %16lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0 OK", + addr, sizeof(struct acpi_dmar), OEM_REVISION); + ut_assert_console_end(); + ut_assert_console_end(); + unmap_sysmem(buf); + free(buf); + + return 0; +} +DM_TEST(dm_test_acpi_cmd_list_chksum, + UTF_SCAN_PDATA | UTF_SCAN_FDT | UTF_CONSOLE); + /* Test 'acpi dump' command */ static int dm_test_acpi_cmd_dump(struct unit_test_state *uts) { -- cgit v1.3.1 From 833c05ea272a086b5428166552ceb021b50c4cda Mon Sep 17 00:00:00 2001 From: Harsha Vardhan V M Date: Wed, 19 Mar 2025 14:17:11 +0530 Subject: doc: cmd: Add documentation for fuse command Add documentation for the 'fuse' sub-system commands in doc/usage/cmd/fuse.rst file. Remove doc/README.fuse file. Signed-off-by: Harsha Vardhan V M --- doc/README.fuse | 67 --------------------- doc/usage/cmd/fuse.rst | 156 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + 3 files changed, 157 insertions(+), 67 deletions(-) delete mode 100644 doc/README.fuse create mode 100644 doc/usage/cmd/fuse.rst (limited to 'doc/usage/cmd') diff --git a/doc/README.fuse b/doc/README.fuse deleted file mode 100644 index 1bc91c44a6a..00000000000 --- a/doc/README.fuse +++ /dev/null @@ -1,67 +0,0 @@ -Fuse API functions and commands - -The fuse API allows to control a fusebox and how it is used by the upper -hardware layers. - -A fuse corresponds to a single non-volatile memory bit that can be programmed -(i.e. blown, set to 1) only once. The programming operation is irreversible. A -fuse that has not been programmed reads 0. - -Fuses can be used by SoCs to store various permanent configuration and data, -e.g. boot configuration, security configuration, MAC addresses, etc. - -A fuse word is the smallest group of fuses that can be read at once from the -fusebox control IP registers. This is limited to 32 bits with the current API. - -A fuse bank is the smallest group of fuse words having a common ID, as defined -by each SoC. - -Upon startup, the fusebox control IP reads the fuse values and stores them to a -volatile shadow cache. - -See the README files of the drivers implementing this API in order to know the -SoC- and implementation-specific details. - -Functions / commands: - - int fuse_read(u32 bank, u32 word, u32 *val); - fuse read [] - Read fuse words from the shadow cache. - - int fuse_sense(u32 bank, u32 word, u32 *val); - fuse sense [] - Sense - i.e. read directly from the fusebox, skipping the shadow cache - - fuse words. This operation does not update the shadow cache. - - This is useful to know the true value of fuses if an override has been - performed (see below). - - int fuse_prog(u32 bank, u32 word, u32 val); - fuse prog [-y] [...] - Program fuse words. This operation directly affects the fusebox and is - irreversible. The shadow cache is updated accordingly or not, depending on - each IP. - - Only the bits to be programmed should be set in the input value (i.e. for - fuse bits that have already been programmed and hence should be left - unchanged by a further programming, it is preferable to clear the - corresponding bits in the input value in order not to perform a new - hardware programming operation on these fuse bits). - - int fuse_override(u32 bank, u32 word, u32 val); - fuse override [...] - Override fuse words in the shadow cache. - - The fusebox is unaffected, so following this operation, the shadow cache - may differ from the fusebox values. Read or sense operations can then be - used to get the values from the shadow cache or from the fusebox. - - This is useful to change the behaviors linked to some cached fuse values, - either because this is needed only temporarily, or because some of the - fuses have already been programmed or are locked (if the SoC allows to - override a locked fuse). - -Configuration: - - CONFIG_CMD_FUSE - Define this to enable the fuse commands. diff --git a/doc/usage/cmd/fuse.rst b/doc/usage/cmd/fuse.rst new file mode 100644 index 00000000000..6e330eddfea --- /dev/null +++ b/doc/usage/cmd/fuse.rst @@ -0,0 +1,156 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +.. index:: + single: fuse (command) + +fuse command +============ + +Synopsis +-------- + +:: + + fuse read [] + fuse cmp + fuse readm [] + fuse sense [] + fuse prog [-y] [...] + fuse override [...] + +Description +----------- + +The fuse API allows to control a fusebox and how it is used by the upper +hardware layers. + +A fuse corresponds to a single non-volatile memory bit that can be programmed +(i.e., blown, set to 1) only once. The programming operation is irreversible. +A fuse that has not been programmed reads as 0. + +Fuses can be used by SoCs to store various permanent configurations and data, +such as boot configurations, security settings, MAC addresses, etc. + +A fuse 'word' is the smallest group of fuses that can be read at once from +the fusebox control IP registers. In the current API, this is limited to 32 bits. + +A fuse 'bank' is the smallest group of fuse words having a common ID, as +defined by each SoC. + +Upon startup, the fusebox control IP reads the fuse values and stores them in a +volatile shadow cache. + +Commands +-------- + +- **fuse read []** + Reads 1 or 'cnt' fuse words, starting at 'word' from the shadow cache. + +- **fuse cmp ** + Compares 'hexval' to fuse at 'word'. + +- **fuse readm []** + Reads 1 or 'cnt' fuse words, starting at 'word' into memory at 'addr'. + +- **fuse sense []** + Sense 1 or 'cnt' fuse words, starting at 'word'. + Sense - i.e. read directly from the fusebox, skipping the shadow cache - + fuse words. This operation does not update the shadow cache. This is + useful to know the true value of fuses if an override has been + performed (see below). + +- **fuse prog [-y] [...]** + Permanently programs 1 or several fuse words, starting at 'word'. + This operation directly affects the fusebox and is irreversible. The + shadow cache is updated accordingly or not, depending on each IP. + Only the bits to be programmed should be set in the input value (i.e. + for fuse bits that have already been programmed and hence should be + left unchanged by a further programming, it is preferable to clear + the corresponding bits in the input value in order not to perform a + new hardware programming operation on these fuse bits). + +- **fuse override [...]** + Override 1 or several fuse words, starting at 'word' in the shadow cache. + The fusebox is unaffected, so following this operation, the shadow cache + may differ from the fusebox values. Read or sense operations can then be + used to get the values from the shadow cache or from the fusebox. + This is useful to change the behaviours linked to some cached fuse values, + either because this is needed only temporarily, or because some of the + fuses have already been programmed or are locked (if the SoC allows to + override a locked fuse). + +Examples +-------- + +fuse read +~~~~~~~~~ + +:: + + u-boot=> fuse read 0 1 + Reading bank 0: + + Word 0x00000001: 00000001 + +fuse cmp +~~~~~~~~ + +:: + + u-boot=> fuse cmp 0 1 0x1 + Comparing bank 0: + + Word 0x00000001: + Value 0x00000001:0x00000001 + passed + +fuse readm +~~~~~~~~~~ + +:: + + u-boot=> fuse readm 0 1 0x83000000 + Reading bank 0 len 1 to 0x83000000 + +fuse sense +~~~~~~~~~~ + +:: + + u-boot=> fuse sense 0 1 + Sensing bank 0: + + Word 0x00000001: 00000001 + +fuse prog +~~~~~~~~~ + +:: + + u-boot=> fuse prog 0 1 0x00000002 + Programming bank 0 word 0x00000001 to 0x00000002... + Warning: Programming fuses is an irreversible operation! + This may brick your system. + Use this command only if you are sure of what you are doing! + + Really perform this fuse programming? + y + +fuse override +~~~~~~~~~~~~~ + +:: + + u-boot=> fuse override 0 1 0x00000003 + Overriding bank 0 word 0x00000001 with 0x00000003... + +Configuration +------------- + +The fuse commands are available if CONFIG_CMD_FUSE=y. + +Return code +----------- + +The return value $? is set to 0 (true) if the command is successful, +1 (false) otherwise. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index fc058476f39..372ef56c967 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -72,6 +72,7 @@ Shell commands cmd/fdt cmd/font cmd/for + cmd/fuse cmd/fwu_mdata cmd/gpio cmd/gpt -- cgit v1.3.1 From 9f6b1ff8d08a92bbddf3c6226b8c2fdaf8df9515 Mon Sep 17 00:00:00 2001 From: Harsha Vardhan V M Date: Wed, 19 Mar 2025 14:17:14 +0530 Subject: doc: cmd: Add fuse writebuff cmd documentation Add fuse writebuff sub-system command documentation. Signed-off-by: Harsha Vardhan V M --- doc/usage/cmd/fuse.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'doc/usage/cmd') diff --git a/doc/usage/cmd/fuse.rst b/doc/usage/cmd/fuse.rst index 6e330eddfea..42c6e209f99 100644 --- a/doc/usage/cmd/fuse.rst +++ b/doc/usage/cmd/fuse.rst @@ -17,6 +17,7 @@ Synopsis fuse sense [] fuse prog [-y] [...] fuse override [...] + fuse writebuff [-y] Description ----------- @@ -79,6 +80,16 @@ Commands fuses have already been programmed or are locked (if the SoC allows to override a locked fuse). +- **fuse writebuff [-y] ** + Programs fuse data using a structured buffer in memory starting at 'addr'. + This operation directly affects the fusebox and is irreversible. + + The structure of the buffer should contain all necessary details for + programming fuses, such as the values to be written to the fuse, optional + metadata for validation or programming constraints and any configuration + data required for the operation. Define CONFIG_CMD_FUSE_WRITEBUFF to + enable the fuse writebuff command. + Examples -------- @@ -144,10 +155,19 @@ fuse override u-boot=> fuse override 0 1 0x00000003 Overriding bank 0 word 0x00000001 with 0x00000003... +fuse writebuff +~~~~~~~~~~~~~~ + +:: + + u-boot=> fuse writebuff -y 0x84000000 + Programming fuses with buffer at addr 0x84000000 + Configuration ------------- The fuse commands are available if CONFIG_CMD_FUSE=y. +The fuse writebuff command is available if CONFIG_CMD_FUSE_WRITEBUFF=y. Return code ----------- -- cgit v1.3.1