From 805e89e3f7fe5118374b5005e5c2dc214185dcac Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:41 -0600 Subject: [PATCH 01/28] alist: Mention the error condition in alist_add_placeholder() Update the function comment to note that this function can return NULL if it runs out of memory. Signed-off-by: Simon Glass --- include/alist.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/alist.h b/include/alist.h index 68d268f01af..0343946bc4a 100644 --- a/include/alist.h +++ b/include/alist.h @@ -151,8 +151,9 @@ void *alist_ensure_ptr(struct alist *lst, uint index); * alist_add_placeholder() - Add a new item to the end of the list * * @lst: alist to add to - * Return: Pointer to the newly added position. Note that this is not inited so - * the caller must copy the requested struct to the returned pointer + * Return: Pointer to the newly added position, or NULL if out of memory. Note + * that this is not inited so the caller must copy the requested struct to the + * returned pointer */ void *alist_add_placeholder(struct alist *lst); From 55c8aad164da140485f8cb146765523b3c7811ce Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:42 -0600 Subject: [PATCH 02/28] alist: Add a comment for alist_init_struct() Comment this macro so that it is clear how to use it. Signed-off-by: Simon Glass --- include/alist.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/alist.h b/include/alist.h index 0343946bc4a..a727f1c7dfa 100644 --- a/include/alist.h +++ b/include/alist.h @@ -198,6 +198,12 @@ bool alist_expand_by(struct alist *lst, uint inc_by); */ bool alist_init(struct alist *lst, uint obj_size, uint alloc_size); +/** + * alist_init_struct() - Typed version of alist_init() + * + * Use as: + * alist_init(&lst, struct my_struct); + */ #define alist_init_struct(_lst, _struct) \ alist_init(_lst, sizeof(_struct), 0) From eb6e87a7ab9f93596983b57eef08509beeedf3fb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:43 -0600 Subject: [PATCH 03/28] alist: Expand the comment for alist_get() Add a better description for this macro. Signed-off-by: Simon Glass --- include/alist.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/alist.h b/include/alist.h index a727f1c7dfa..2c78ede201e 100644 --- a/include/alist.h +++ b/include/alist.h @@ -116,7 +116,12 @@ static inline const void *alist_getd(struct alist *lst, uint index) return lst->data + index * lst->obj_size; } -/** get an entry as a constant */ +/** + * alist_get() - get an entry as a constant + * + * Use as (to obtain element 2 of the list): + * const struct my_struct *ptr = alist_get(lst, 2, struct my_struct) + */ #define alist_get(_lst, _index, _struct) \ ((const _struct *)alist_get_ptr(_lst, _index)) From 1d49f78c362981435a88d887c777ccc445f5a4e7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:44 -0600 Subject: [PATCH 04/28] alist: Add a way to get the next element Add a new function which returns the next element after the one provided, if it exists in the list. Signed-off-by: Simon Glass --- include/alist.h | 34 +++++++++++++++++++++++++++++++ lib/alist.c | 21 +++++++++++++++++++ test/lib/alist.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/include/alist.h b/include/alist.h index 2c78ede201e..97523af37a6 100644 --- a/include/alist.h +++ b/include/alist.h @@ -71,6 +71,21 @@ static inline bool alist_has(struct alist *lst, uint index) return index < lst->count; } +/** + * alist_calc_index() - Calculate the index of an item in the list + * + * The returned element number will be -1 if the list is empty or the pointer + * pointers to before the list starts. + * + * If the pointer points to after the last item, the calculated element-number + * will be returned, even though it is greater than lst->count + * + * @lst: alist to check + * @ptr: pointer to check + * Return: element number of the pointer + */ +int alist_calc_index(const struct alist *lst, const void *ptr); + /** * alist_err() - Check if the alist is still valid * @@ -190,6 +205,25 @@ bool alist_expand_by(struct alist *lst, uint inc_by); #define alist_add(_lst, _obj) \ ((typeof(_obj) *)alist_add_ptr(_lst, &(_obj))) +/** get next entry as a constant */ +#define alist_next(_lst, _objp) \ + ((const typeof(_objp))alist_next_ptrd(_lst, _objp)) + +/** get next entry, which can be written to */ +#define alist_nextw(_lst, _objp) \ + ((typeof(_objp))alist_next_ptrd(_lst, _objp)) + +/** + * alist_next_ptrd() - Get a pointer to the next list element + * + * This returns NULL if the requested element is beyond lst->count + * + * @lst: List to check + * @ptr: Pointer to current element (must be valid) + * Return: Pointer to next element, or NULL if @ptr is the last + */ +const void *alist_next_ptrd(const struct alist *lst, const void *ptr); + /** * alist_init() - Set up a new object list * diff --git a/lib/alist.c b/lib/alist.c index b7928cad520..7730fe0d473 100644 --- a/lib/alist.c +++ b/lib/alist.c @@ -106,6 +106,27 @@ const void *alist_get_ptr(const struct alist *lst, uint index) return lst->data + index * lst->obj_size; } +int alist_calc_index(const struct alist *lst, const void *ptr) +{ + uint index; + + if (!lst->count || ptr < lst->data) + return -1; + + index = (ptr - lst->data) / lst->obj_size; + + return index; +} + +const void *alist_next_ptrd(const struct alist *lst, const void *ptr) +{ + int index = alist_calc_index(lst, ptr); + + assert(index != -1); + + return alist_get_ptr(lst, index + 1); +} + void *alist_ensure_ptr(struct alist *lst, uint index) { uint minsize = index + 1; diff --git a/test/lib/alist.c b/test/lib/alist.c index d41845c7e6c..96092affec9 100644 --- a/test/lib/alist.c +++ b/test/lib/alist.c @@ -240,3 +240,55 @@ static int lib_test_alist_add(struct unit_test_state *uts) return 0; } LIB_TEST(lib_test_alist_add, 0); + +/* Test alist_next() */ +static int lib_test_alist_next(struct unit_test_state *uts) +{ + const struct my_struct *ptr; + struct my_struct data, *ptr2; + struct alist lst; + ulong start; + + start = ut_check_free(); + + ut_assert(alist_init_struct(&lst, struct my_struct)); + data.val = 123; + data.other_val = 0; + alist_add(&lst, data); + + data.val = 321; + alist_add(&lst, data); + + data.val = 789; + alist_add(&lst, data); + + ptr = alist_get(&lst, 0, struct my_struct); + ut_assertnonnull(ptr); + ut_asserteq(123, ptr->val); + + ptr = alist_next(&lst, ptr); + ut_assertnonnull(ptr); + ut_asserteq(321, ptr->val); + + ptr2 = (struct my_struct *)ptr; + ptr2 = alist_nextw(&lst, ptr2); + ut_assertnonnull(ptr2); + + ptr = alist_next(&lst, ptr); + ut_assertnonnull(ptr); + ut_asserteq(789, ptr->val); + ut_asserteq_ptr(ptr, ptr2); + ptr2->val = 89; + ut_asserteq(89, ptr->val); + + ptr = alist_next(&lst, ptr); + ut_assertnull(ptr); + + alist_uninit(&lst); + + /* Check for memory leaks */ + ut_assertok(ut_check_delta(start)); + + return 0; +} +LIB_TEST(lib_test_alist_next, 0); From d785a77d18acdf6714f4570c14e622caadf4d5e3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:45 -0600 Subject: [PATCH 05/28] alist: Add for-loop helpers Add some macros which permit easy iteration through an alist, similar to those provided by the 'list' implementation. Signed-off-by: Simon Glass --- include/alist.h | 50 ++++++++++++++++++++++++++++++++ lib/alist.c | 7 +++++ test/lib/alist.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) diff --git a/include/alist.h b/include/alist.h index 97523af37a6..0090b9c0eb1 100644 --- a/include/alist.h +++ b/include/alist.h @@ -224,6 +224,56 @@ bool alist_expand_by(struct alist *lst, uint inc_by); */ const void *alist_next_ptrd(const struct alist *lst, const void *ptr); +/** + * alist_chk_ptr() - Check whether a pointer is within a list + * + * Checks if the pointer points to an existing element of the list. The pointer + * must point to the start of an element, either in the list, or just outside of + * it. This function is only useful for handling for() loops + * + * Return: true if @ptr is within the list (0..count-1), else false + */ +bool alist_chk_ptr(const struct alist *lst, const void *ptr); + +/** + * alist_start() - Get the start of the list (first element) + * + * Note that this will always return ->data even if it is not NULL + * + * Usage: + * const struct my_struct *obj; # 'const' is optional + * + * alist_start(&lst, struct my_struct) + */ +#define alist_start(_lst, _struct) \ + ((_struct *)(_lst)->data) + +/** + * alist_end() - Get the end of the list (just after last element) + * + * Usage: + * const struct my_struct *obj; # 'const' is optional + * + * alist_end(&lst, struct my_struct) + */ +#define alist_end(_lst, _struct) \ + ((_struct *)(_lst)->data + (_lst)->count) + +/** + * alist_for_each() - Iterate over an alist (with constant pointer) + * + * Use as: + * const struct my_struct *obj; # 'const' is optional + * + * alist_for_each(obj, &lst) { + * obj->... + * } + */ +#define alist_for_each(_pos, _lst) \ + for (_pos = alist_start(_lst, typeof(*(_pos))); \ + _pos < alist_end(_lst, typeof(*(_pos))); \ + _pos++) + /** * alist_init() - Set up a new object list * diff --git a/lib/alist.c b/lib/alist.c index 7730fe0d473..1a4b4fb9c40 100644 --- a/lib/alist.c +++ b/lib/alist.c @@ -118,6 +118,13 @@ int alist_calc_index(const struct alist *lst, const void *ptr) return index; } +bool alist_chk_ptr(const struct alist *lst, const void *ptr) +{ + int index = alist_calc_index(lst, ptr); + + return index >= 0 && index < lst->count; +} + const void *alist_next_ptrd(const struct alist *lst, const void *ptr) { int index = alist_calc_index(lst, ptr); diff --git a/test/lib/alist.c b/test/lib/alist.c index 96092affec9..1715a22584c 100644 --- a/test/lib/alist.c +++ b/test/lib/alist.c @@ -292,3 +292,77 @@ static int lib_test_alist_next(struct unit_test_state *uts) return 0; } LIB_TEST(lib_test_alist_next, 0); + +/* Test alist_for_each() */ +static int lib_test_alist_for_each(struct unit_test_state *uts) +{ + const struct my_struct *ptr; + struct my_struct data, *ptr2; + struct alist lst; + ulong start; + int sum; + + start = ut_check_free(); + + ut_assert(alist_init_struct(&lst, struct my_struct)); + ut_asserteq_ptr(NULL, alist_end(&lst, struct my_struct)); + + sum = 0; + alist_for_each(ptr, &lst) + sum++; + ut_asserteq(0, sum); + + alist_for_each(ptr, &lst) + sum++; + ut_asserteq(0, sum); + + /* add three items */ + data.val = 1; + data.other_val = 0; + alist_add(&lst, data); + + ptr = lst.data; + ut_asserteq_ptr(ptr + 1, alist_end(&lst, struct my_struct)); + + data.val = 2; + alist_add(&lst, data); + ut_asserteq_ptr(ptr + 2, alist_end(&lst, struct my_struct)); + + data.val = 3; + alist_add(&lst, data); + ut_asserteq_ptr(ptr + 3, alist_end(&lst, struct my_struct)); + + /* check alist_chk_ptr() */ + ut_asserteq(true, alist_chk_ptr(&lst, ptr + 2)); + ut_asserteq(false, alist_chk_ptr(&lst, ptr + 3)); + ut_asserteq(false, alist_chk_ptr(&lst, ptr + 4)); + ut_asserteq(true, alist_chk_ptr(&lst, ptr)); + ut_asserteq(false, alist_chk_ptr(&lst, ptr - 1)); + + /* sum all items */ + sum = 0; + alist_for_each(ptr, &lst) + sum += ptr->val; + ut_asserteq(6, sum); + + /* increment all items */ + alist_for_each(ptr2, &lst) + ptr2->val += 1; + + /* sum all items again */ + sum = 0; + alist_for_each(ptr, &lst) + sum += ptr->val; + ut_asserteq(9, sum); + + ptr = lst.data; + ut_asserteq_ptr(ptr + 3, alist_end(&lst, struct my_struct)); + + alist_uninit(&lst); + + /* Check for memory leaks */ + ut_assertok(ut_check_delta(start)); + + return 0; +} +LIB_TEST(lib_test_alist_for_each, 0); From 5bd4ead8bd76c85aa599c44e8bfb12f512d8ed09 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:46 -0600 Subject: [PATCH 06/28] alist: Add a function to empty the list Sometimes it is useful to empty the list without de-allocating any of the memory used, e.g. when the list will be re-populated immediately afterwards. Add a new function for this. Signed-off-by: Simon Glass --- include/alist.h | 7 +++++++ lib/alist.c | 5 +++++ test/lib/alist.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/include/alist.h b/include/alist.h index 0090b9c0eb1..c639e42ab7d 100644 --- a/include/alist.h +++ b/include/alist.h @@ -274,6 +274,13 @@ bool alist_chk_ptr(const struct alist *lst, const void *ptr); _pos < alist_end(_lst, typeof(*(_pos))); \ _pos++) +/** + * alist_empty() - Empty an alist + * + * This removes all entries from the list, without changing the allocated size + */ +void alist_empty(struct alist *lst); + /** * alist_init() - Set up a new object list * diff --git a/lib/alist.c b/lib/alist.c index 1a4b4fb9c40..32cd45b0b01 100644 --- a/lib/alist.c +++ b/lib/alist.c @@ -41,6 +41,11 @@ void alist_uninit(struct alist *lst) memset(lst, '\0', sizeof(struct alist)); } +void alist_empty(struct alist *lst) +{ + lst->count = 0; +} + /** * alist_expand_to() - Expand a list to the given size * diff --git a/test/lib/alist.c b/test/lib/alist.c index 1715a22584c..87135bb3a51 100644 --- a/test/lib/alist.c +++ b/test/lib/alist.c @@ -358,6 +358,16 @@ static int lib_test_alist_for_each(struct unit_test_state *uts) ptr = lst.data; ut_asserteq_ptr(ptr + 3, alist_end(&lst, struct my_struct)); + /* empty the list and try again */ + alist_empty(&lst); + ut_asserteq_ptr(ptr, alist_end(&lst, struct my_struct)); + ut_assertnull(alist_get(&lst, 0, struct my_struct)); + + sum = 0; + alist_for_each(ptr, &lst) + sum += ptr->val; + ut_asserteq(0, sum); + alist_uninit(&lst); /* Check for memory leaks */ @@ -366,3 +376,35 @@ static int lib_test_alist_for_each(struct unit_test_state *uts) return 0; } LIB_TEST(lib_test_alist_for_each, 0); + +/* Test alist_empty() */ +static int lib_test_alist_empty(struct unit_test_state *uts) +{ + struct my_struct data; + struct alist lst; + ulong start; + + start = ut_check_free(); + + ut_assert(alist_init_struct(&lst, struct my_struct)); + ut_asserteq(0, lst.count); + data.val = 1; + data.other_val = 0; + alist_add(&lst, data); + ut_asserteq(1, lst.count); + ut_asserteq(4, lst.alloc); + + alist_empty(&lst); + ut_asserteq(0, lst.count); + ut_asserteq(4, lst.alloc); + ut_assertnonnull(lst.data); + ut_asserteq(sizeof(data), lst.obj_size); + + alist_uninit(&lst); + + /* Check for memory leaks */ + ut_assertok(ut_check_delta(start)); + + return 0; +} +LIB_TEST(lib_test_alist_empty, 0); From 5dfc1c8078572436fc68817d22d9e046eaa01398 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:47 -0600 Subject: [PATCH 07/28] alist: Add a way to efficiently filter an alist Unlike linked lists, it is inefficient to remove items from an alist, particularly if it is large. If most items need to be removed, then the time-complexity approaches O(n2). Provide a way to do this efficiently, by working through the alist once and copying elements down. Signed-off-by: Simon Glass --- include/alist.h | 30 +++++++++++++++++ lib/alist.c | 8 +++++ test/lib/alist.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) diff --git a/include/alist.h b/include/alist.h index c639e42ab7d..b00d9ea97d6 100644 --- a/include/alist.h +++ b/include/alist.h @@ -274,6 +274,36 @@ bool alist_chk_ptr(const struct alist *lst, const void *ptr); _pos < alist_end(_lst, typeof(*(_pos))); \ _pos++) +/** + * alist_for_each_filter() - version which sets up a 'from' pointer too + * + * This is used for filtering out information in the list. It works by iterating + * through the list, copying elements down over the top of elements to be + * deleted. + * + * In this example, 'from' iterates through the list from start to end,, 'to' + * also begins at the start, but only increments if the element at 'from' should + * be kept. This provides an O(n) filtering operation. Note that + * alist_update_end() must be called after the loop, to update the count. + * + * alist_for_each_filter(from, to, &lst) { + * if (from->val != 2) + * *to++ = *from; + * } + * alist_update_end(&lst, to); + */ +#define alist_for_each_filter(_pos, _from, _lst) \ + for (_pos = _from = alist_start(_lst, typeof(*(_pos))); \ + _pos < alist_end(_lst, typeof(*(_pos))); \ + _pos++) + +/** + * alist_update_end() - Set the element count based on a given pointer + * + * Set the given element as the final one + */ +void alist_update_end(struct alist *lst, const void *end); + /** * alist_empty() - Empty an alist * diff --git a/lib/alist.c b/lib/alist.c index 32cd45b0b01..4ce651f5c45 100644 --- a/lib/alist.c +++ b/lib/alist.c @@ -123,6 +123,14 @@ int alist_calc_index(const struct alist *lst, const void *ptr) return index; } +void alist_update_end(struct alist *lst, const void *ptr) +{ + int index; + + index = alist_calc_index(lst, ptr); + lst->count = index == -1 ? 0 : index; +} + bool alist_chk_ptr(const struct alist *lst, const void *ptr) { int index = alist_calc_index(lst, ptr); diff --git a/test/lib/alist.c b/test/lib/alist.c index 87135bb3a51..0bf24578d2e 100644 --- a/test/lib/alist.c +++ b/test/lib/alist.c @@ -408,3 +408,88 @@ static int lib_test_alist_empty(struct unit_test_state *uts) return 0; } LIB_TEST(lib_test_alist_empty, 0); + +static int lib_test_alist_filter(struct unit_test_state *uts) +{ + struct my_struct *from, *to, *ptr; + struct my_struct data; + struct alist lst; + ulong start; + int count; + + start = ut_check_free(); + + ut_assert(alist_init_struct(&lst, struct my_struct)); + data.val = 1; + data.other_val = 0; + alist_add(&lst, data); + + data.val = 2; + alist_add(&lst, data); + + data.val = 3; + alist_add(&lst, data); + ptr = lst.data; + + /* filter out all values except 2 */ + alist_for_each_filter(from, to, &lst) { + if (from->val != 2) + *to++ = *from; + } + alist_update_end(&lst, to); + + ut_asserteq(2, lst.count); + ut_assertnonnull(lst.data); + + ut_asserteq(1, alist_get(&lst, 0, struct my_struct)->val); + ut_asserteq(3, alist_get(&lst, 1, struct my_struct)->val); + ut_asserteq_ptr(ptr + 3, from); + ut_asserteq_ptr(ptr + 2, to); + + /* filter out nothing */ + alist_for_each_filter(from, to, &lst) { + if (from->val != 2) + *to++ = *from; + } + alist_update_end(&lst, to); + ut_asserteq_ptr(ptr + 2, from); + ut_asserteq_ptr(ptr + 2, to); + + ut_asserteq(2, lst.count); + ut_assertnonnull(lst.data); + + ut_asserteq(1, alist_get(&lst, 0, struct my_struct)->val); + ut_asserteq(3, alist_get(&lst, 1, struct my_struct)->val); + + /* filter out everything */ + alist_for_each_filter(from, to, &lst) { + if (from->val == 2) + *to++ = *from; + } + alist_update_end(&lst, to); + ut_asserteq_ptr(ptr + 2, from); + ut_asserteq_ptr(ptr, to); + + /* filter out everything (nop) */ + count = 0; + alist_for_each_filter(from, to, &lst) { + if (from->val == 2) + *to++ = *from; + count++; + } + alist_update_end(&lst, to); + ut_asserteq_ptr(ptr, from); + ut_asserteq_ptr(ptr, to); + ut_asserteq(0, count); + + ut_asserteq(0, lst.count); + ut_assertnonnull(lst.data); + + alist_uninit(&lst); + + /* Check for memory leaks */ + ut_assertok(ut_check_delta(start)); + + return 0; +} +LIB_TEST(lib_test_alist_filter, 0); From 2ca32cbb837ff0a1e906a99c518a03852e1d24e8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:48 -0600 Subject: [PATCH 08/28] alist: Add maintainer Add myself as maintainer of alist Signed-off-by: Simon Glass --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index df046192ea0..0399ed1dbf6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -58,6 +58,13 @@ F: cmd/acpi.c F: include/acpi/ F: lib/acpi/ +ALIST: +M: Simon Glass +S: Maintained +F: include/alist.h +F: lib/alist.c +F: test/lib/alist.c + ANDROID AB M: Igor Opaniuk M: Mattijs Korpershoek From 79b3e9d25b3f96fdf66f9d7dc6a14fdce9a15f93 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:49 -0600 Subject: [PATCH 09/28] dm: core: Add a function to see if a device exists All the uclass functions for finding a device end up creating a uclass if it doesn't exist. Add a function which instead returns NULL in this case. This is useful when in the 'unbind' path, since we don't want to undo any unbinding which has already happened. Signed-off-by: Simon Glass --- drivers/core/uclass.c | 11 +++++++++++ include/dm/uclass.h | 11 +++++++++++ test/dm/core.c | 22 ++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 7ae0884a75e..f846a35d6b2 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -304,6 +304,17 @@ int uclass_find_device_by_name(enum uclass_id id, const char *name, return uclass_find_device_by_namelen(id, name, strlen(name), devp); } +struct udevice *uclass_try_first_device(enum uclass_id id) +{ + struct uclass *uc; + + uc = uclass_find(id); + if (!uc || list_empty(&uc->dev_head)) + return NULL; + + return list_first_entry(&uc->dev_head, struct udevice, uclass_node); +} + int uclass_find_next_free_seq(struct uclass *uc) { struct udevice *dev; diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 456eef7f2f3..c2793040923 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -435,6 +435,17 @@ int uclass_next_device_check(struct udevice **devp); int uclass_first_device_drvdata(enum uclass_id id, ulong driver_data, struct udevice **devp); +/** + * uclass_try_first_device()- See if there is a device for a uclass + * + * If the uclass exists, this returns the first device on that uclass, without + * probing it. If the uclass does not exist, it gives up + * + * @id: Uclass ID to check + * Return: Pointer to device, if found, else NULL + */ +struct udevice *uclass_try_first_device(enum uclass_id id); + /** * uclass_probe_all() - Probe all devices based on an uclass ID * diff --git a/test/dm/core.c b/test/dm/core.c index e0c5b9e0017..7371d3ff426 100644 --- a/test/dm/core.c +++ b/test/dm/core.c @@ -1351,3 +1351,25 @@ static int dm_test_dev_get_mem(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_dev_get_mem, UTF_SCAN_FDT); + +/* Test uclass_try_first_device() */ +static int dm_test_try_first_device(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* Check that it doesn't create a device or uclass */ + ut_assertnull(uclass_find(UCLASS_TEST)); + ut_assertnull(uclass_try_first_device(UCLASS_TEST)); + ut_assertnull(uclass_try_first_device(UCLASS_TEST)); + ut_assertnull(uclass_find(UCLASS_TEST)); + + /* Create a test device */ + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual, + &dev)); + dev = uclass_try_first_device(UCLASS_TEST); + ut_assertnonnull(dev); + ut_asserteq(UCLASS_TEST, device_get_uclass_id(dev)); + + return 0; +} +DM_TEST(dm_test_try_first_device, 0); From 5224aa1dadee67ed4c575c4d1c1e2d4c10be0c18 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:50 -0600 Subject: [PATCH 10/28] test: boot: Use a consistent name for the script bootmeth In the bootflow tests the script bootmeth is bound with the name bootmeth_script whereas the others have a name without the bootmeth_ prefix. Adjust it to be the same. Signed-off-by: Simon Glass Reviewed-by: Mattijs Korpershoek --- test/boot/bootflow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 0d4e966892e..8762d21ef3c 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -542,7 +542,7 @@ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev, /* Enable the script bootmeth too */ ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd)); ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_2script), - "bootmeth_script", 0, ofnode_null(), &dev)); + "script", 0, ofnode_null(), &dev)); /* Enable the cros bootmeth if needed */ if (IS_ENABLED(CONFIG_BOOTMETH_CROS) && bind_cros_android) { From fbdac8155c8978e02e54fc1a5952e8d53f7a2eee Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:56 -0600 Subject: [PATCH 11/28] test: Expand implementation of ut_list_has_dm_tests() This function assumes that all tests in a suite are being run. This means that it can sometimes call dm_test_restore() when it should not. The impact of this is that it is not possible, for example, to run 'ut bootstd bootflow_cros' and then check the state of bootstd afterwards, since all devices are removed and recreated. Update the function to take account of any selected test, to avoid this problem. Add a comment for test_insert while we are here. Signed-off-by: Simon Glass --- test/test-main.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/test/test-main.c b/test/test-main.c index da5b07ce00b..ca6a073a8cb 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -240,15 +240,22 @@ static bool test_matches(const char *prefix, const char *test_name, * ut_list_has_dm_tests() - Check if a list of tests has driver model ones * * @tests: List of tests to run - * @count: Number of tests to ru + * @count: Number of tests to run + * @prefix: String prefix for the tests. Any tests that have this prefix will be + * printed without the prefix, so that it is easier to see the unique part + * of the test name. If NULL, no prefix processing is done + * @select_name: Name of a single test being run (from the list provided). If + * NULL all tests are being run * Return: true if any of the tests have the UTF_DM flag */ -static bool ut_list_has_dm_tests(struct unit_test *tests, int count) +static bool ut_list_has_dm_tests(struct unit_test *tests, int count, + const char *prefix, const char *select_name) { struct unit_test *test; for (test = tests; test < tests + count; test++) { - if (test->flags & UTF_DM) + if (test_matches(prefix, test->name, select_name) && + (test->flags & UTF_DM)) return true; } @@ -550,6 +557,9 @@ static int ut_run_test_live_flat(struct unit_test_state *uts, * @count: Number of tests to run * @select_name: Name of a single test to run (from the list provided). If NULL * then all tests are run + * @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 * Return: 0 if all tests passed, -ENOENT if test @select_name was not found, * -EBADF if any failed */ @@ -646,7 +656,7 @@ int ut_run_list(const char *category, const char *prefix, int ret; if (!CONFIG_IS_ENABLED(OF_PLATDATA) && - ut_list_has_dm_tests(tests, count)) { + ut_list_has_dm_tests(tests, count, prefix, select_name)) { has_dm_tests = true; /* * If we have no device tree, or it only has a root node, then From 3ef48a7c66eda99654aec3fa63a0465120941bb6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:57 -0600 Subject: [PATCH 12/28] test: Drop the duplicate line in setup_bootmenu_image() The mkimage call is done twice. Remove the duplicate. Signed-off-by: Simon Glass --- test/py/tests/test_ut.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py index 39aa1035e34..9166c8f6b6e 100644 --- a/test/py/tests/test_ut.py +++ b/test/py/tests/test_ut.py @@ -208,8 +208,6 @@ booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r} cons, f'echo here {kernel} {symlink}') os.symlink(kernel, symlink) - u_boot_utils.run_and_log( - cons, f'mkimage -C none -A arm -T script -d {cmd_fname} {scr_fname}') complete = True except ValueError as exc: From 5936d863cd5626acaf616822c56ac6c79c6893a4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:58 -0600 Subject: [PATCH 13/28] test: boot: Update bootflow_iter() for console checking This test checks console output so should have the UTF_CONSOLE flag. Add it. Signed-off-by: Simon Glass --- test/boot/bootflow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 8762d21ef3c..2f859c40adb 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -370,7 +370,7 @@ static int bootflow_iter(struct unit_test_state *uts) return 0; } -BOOTSTD_TEST(bootflow_iter, UTF_DM | UTF_SCAN_FDT); +BOOTSTD_TEST(bootflow_iter, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE); #if defined(CONFIG_SANDBOX) && defined(CONFIG_BOOTMETH_GLOBAL) /* Check using the system bootdev */ From bdf4269f39c2c39129fa57d2702ab4e4ee550e5e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:21:59 -0600 Subject: [PATCH 14/28] bootstd: cros: Correct the x86-setup address This should really use an address rather than the buffer. Update it in the command. Signed-off-by: Simon Glass --- cmd/bootflow.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/bootflow.c b/cmd/bootflow.c index 1588f277a4a..5b6e003df77 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -403,7 +403,8 @@ static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc, puts("(none)"); putc('\n'); if (bflow->x86_setup) - printf("X86 setup: %p\n", bflow->x86_setup); + printf("X86 setup: %lx\n", + (ulong)map_to_sysmem(bflow->x86_setup)); printf("Logo: %s\n", bflow->logo ? simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)"); if (bflow->logo) { From 482eedf9f391cab4f055f531be961376a38ae46e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Oct 2024 09:22:09 -0600 Subject: [PATCH 15/28] bootstd: Avoid showing an invalid buffer address When the buffer address is not set, say so, rather than showing an address which looks very strange, on sandbox. Signed-off-by: Simon Glass --- cmd/bootflow.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/bootflow.c b/cmd/bootflow.c index 5b6e003df77..f67948d7368 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -393,7 +393,11 @@ static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc, printf("Partition: %d\n", bflow->part); printf("Subdir: %s\n", bflow->subdir ? bflow->subdir : "(none)"); printf("Filename: %s\n", bflow->fname); - printf("Buffer: %lx\n", (ulong)map_to_sysmem(bflow->buf)); + printf("Buffer: "); + if (bflow->buf) + printf("%lx\n", (ulong)map_to_sysmem(bflow->buf)); + else + printf("(not loaded)\n"); printf("Size: %x (%d bytes)\n", bflow->size, bflow->size); printf("OS: %s\n", bflow->os_name ? bflow->os_name : "(none)"); printf("Cmdline: "); From 680dff6f924b8fe462a95418f3bc05918341399e Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Sat, 3 Aug 2024 12:41:45 +0000 Subject: [PATCH 16/28] bootstage: Do not sort records The timer counter on Rockchip SoCs may be reset in TF-A, this may cause the bootstage records to be printed out of order and with an incorrect elapsed time. Fix this by not sorting the bootstage records. Before on a Radxa ZERO 3W (RK3566) board: => bootstage report Timer summary in microseconds (12 records): Mark Elapsed Stage 0 0 reset 7,436 7,436 board_init_f 164,826 157,390 SPL 375,392 210,566 end phase 423,909 48,517 board_init_r 472,973 49,064 eth_common_init 476,848 3,875 main_loop 477,003 155 cli_loop Accumulated time: 7,181 of_live 14,739 dm_spl 15,029 dm_r 315,150 dm_f With this the records can be printed in chronological order when the counter is reset and SPL and board_init_r records show correct elapsed time. => bootstage report Timer summary in microseconds (12 records): Mark Elapsed Stage 0 0 reset 164,437 164,437 SPL 375,023 210,586 end phase 7,437 7,437 board_init_f 424,390 416,953 board_init_r 473,515 49,125 eth_common_init 477,402 3,887 main_loop 477,571 169 cli_loop Accumulated time: 14,734 dm_spl 315,646 dm_f 7,339 of_live 14,977 dm_r For the tested board external TPL and BROM take ~164 ms to initialize DRAM and load SPL, SPL take ~210ms to load images from FIT and U-Boot proper take ~477ms to reach cli prompt. Signed-off-by: Jonas Karlman --- common/bootstage.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/common/bootstage.c b/common/bootstage.c index c7bb204501a..4532100acea 100644 --- a/common/bootstage.c +++ b/common/bootstage.c @@ -249,6 +249,8 @@ static uint32_t print_time_record(struct bootstage_record *rec, uint32_t prev) printf("%11s", ""); print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS); } else { + if (prev > rec->time_us) + prev = 0; print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS); print_grouped_ull(rec->time_us - prev, BOOTSTAGE_DIGITS); } @@ -257,13 +259,6 @@ static uint32_t print_time_record(struct bootstage_record *rec, uint32_t prev) return rec->time_us; } -static int h_compare_record(const void *r1, const void *r2) -{ - const struct bootstage_record *rec1 = r1, *rec2 = r2; - - return rec1->time_us > rec2->time_us ? 1 : -1; -} - #ifdef CONFIG_OF_LIBFDT /** * Add all bootstage timings to a device tree. @@ -342,9 +337,6 @@ void bootstage_report(void) prev = print_time_record(rec, 0); - /* Sort records by increasing time */ - qsort(data->record, data->rec_count, sizeof(*rec), h_compare_record); - for (i = 1, rec++; i < data->rec_count; i++, rec++) { if (rec->id && !rec->start_us) prev = print_time_record(rec, prev); From bde86903abd6a1169f33f212ca1247d26ea11555 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Oct 2024 16:32:07 -0600 Subject: [PATCH 17/28] x86: coreboot: Add a test for cbsysinfo command Add a simple test for this command, checking that coreboot has the required features. Signed-off-by: Simon Glass --- test/cmd/Makefile | 1 + test/cmd/coreboot.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 test/cmd/coreboot.c diff --git a/test/cmd/Makefile b/test/cmd/Makefile index fe7a2165af2..0055330dbec 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -15,6 +15,7 @@ obj-y += exit.o mem.o obj-$(CONFIG_X86) += cpuid.o msr.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_BDI) += bdinfo.o +obj-$(CONFIG_COREBOOT_SYSINFO) += coreboot.o obj-$(CONFIG_CMD_FDT) += fdt.o obj-$(CONFIG_CONSOLE_TRUETYPE) += font.o obj-$(CONFIG_CMD_HISTORY) += history.o diff --git a/test/cmd/coreboot.c b/test/cmd/coreboot.c new file mode 100644 index 00000000000..4a00bad2b8f --- /dev/null +++ b/test/cmd/coreboot.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for coreboot commands + * + * Copyright 2023 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include + +/** + * test_cmd_cbsysinfo() - test the cbsysinfo command produces expected output + * + * This includes ensuring that the coreboot build has the expected options + * enabled + */ +static int test_cmd_cbsysinfo(struct unit_test_state *uts) +{ + ut_assertok(run_command("cbsysinfo", 0)); + ut_assert_nextlinen("Coreboot table at"); + + /* Make sure the linear frame buffer is enabled */ + ut_assert_skip_to_linen("Framebuffer"); + ut_assert_nextlinen(" Phys addr"); + + ut_assert_skip_to_line("Chrome OS VPD: 00000000"); + ut_assert_nextlinen("RSDP"); + ut_assert_nextlinen("Unimpl."); + ut_assert_console_end(); + + return 0; +} +CMD_TEST(test_cmd_cbsysinfo, UTF_CONSOLE); From d04c23f1c5ee68134543eaa0c5b918d7c9988fe0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Oct 2024 16:32:08 -0600 Subject: [PATCH 18/28] x86: coreboot: Show the option table Update the cbsysinfo command to show the contents of the CMOS option table. While we are here, add some example output for this command, along with mention of what the unimplemented tags are. Signed-off-by: Simon Glass --- cmd/x86/cbsysinfo.c | 73 ++++++++++++++++++++++++++- doc/usage/cmd/cbsysinfo.rst | 99 +++++++++++++++++++++++++++++++++++++ test/cmd/coreboot.c | 7 +++ 3 files changed, 178 insertions(+), 1 deletion(-) diff --git a/cmd/x86/cbsysinfo.c b/cmd/x86/cbsysinfo.c index 7ca2e13ae2f..ea4d89616f6 100644 --- a/cmd/x86/cbsysinfo.c +++ b/cmd/x86/cbsysinfo.c @@ -184,6 +184,77 @@ static const char *timestamp_name(uint32_t id) return ""; } +static void show_option_vals(const struct cb_cmos_option_table *tab, + uint id) +{ + const void *ptr, *end; + bool found = false; + + end = (void *)tab + tab->size; + for (ptr = (void *)tab + tab->header_length; ptr < end;) { + const struct cb_record *rec = ptr; + + switch (rec->tag) { + case CB_TAG_OPTION_ENUM: { + const struct cb_cmos_enums *enums = ptr; + + if (enums->config_id == id) { + if (!found) + printf(" "); + printf(" %d:%s", enums->value, enums->text); + found = true; + } + break; + } + break; + case CB_TAG_OPTION_DEFAULTS: + case CB_TAG_OPTION_CHECKSUM: + case CB_TAG_OPTION: + break; + default: + printf("tag %x\n", rec->tag); + break; + } + ptr += rec->size; + } +} + +static void show_option_table(const struct cb_cmos_option_table *tab) +{ + const void *ptr, *end; + + print_ptr("option_table", tab); + if (!tab->size) + return; + + printf(" Bit Len Cfg ID Name\n"); + end = (void *)tab + tab->size; + for (ptr = (void *)tab + tab->header_length; ptr < end;) { + const struct cb_record *rec = ptr; + + switch (rec->tag) { + case CB_TAG_OPTION: { + const struct cb_cmos_entries *entry = ptr; + + printf("%4x %4x %3c %3x %-20s", entry->bit, + entry->length, entry->config, entry->config_id, + entry->name); + show_option_vals(tab, entry->config_id); + printf("\n"); + break; + } + case CB_TAG_OPTION_ENUM: + case CB_TAG_OPTION_DEFAULTS: + case CB_TAG_OPTION_CHECKSUM: + break; + default: + printf("tag %x\n", rec->tag); + break; + } + ptr += rec->size; + } +} + static void show_table(struct sysinfo_t *info, bool verbose) { struct cb_serial *ser = info->serial; @@ -218,7 +289,7 @@ static void show_table(struct sysinfo_t *info, bool verbose) printf("%12d: %02x:%-8s %016llx %016llx\n", i, mr->type, get_mem_name(mr->type), mr->base, mr->size); } - print_ptr("option_table", info->option_table); + show_option_table(info->option_table); print_hex("CMOS start", info->cmos_range_start); if (info->cmos_range_start) { diff --git a/doc/usage/cmd/cbsysinfo.rst b/doc/usage/cmd/cbsysinfo.rst index 80d8ba1b662..28f61d9c63e 100644 --- a/doc/usage/cmd/cbsysinfo.rst +++ b/doc/usage/cmd/cbsysinfo.rst @@ -23,3 +23,102 @@ Example :: => cbsysinfo + Coreboot table at 500, size 5c4, records 1d (dec 29), decoded to 000000007dce4520, forwarded to 000000007ff9a000 + + CPU KHz : 0 + Serial I/O port: 00000000 + base : 00000000 + pointer : 000000007ff9a370 + type : 1 + base : 000003f8 + baud : 0d115200 + regwidth : 1 + input_hz : 0d1843200 + PCI addr : 00000010 + Mem ranges : 7 + id: type || base || size + 0: 10:table 0000000000000000 0000000000001000 + 1: 01:ram 0000000000001000 000000000009f000 + 2: 02:reserved 00000000000a0000 0000000000060000 + 3: 01:ram 0000000000100000 000000007fe6d000 + 4: 10:table 000000007ff6d000 0000000000093000 + 5: 02:reserved 00000000fec00000 0000000000001000 + 6: 02:reserved 00000000ff800000 0000000000800000 + option_table: 000000007ff9a018 + Bit Len Cfg ID Name + 0 180 r 0 reserved_memory + 180 1 e 4 boot_option 0:Fallback 1:Normal + 184 4 h 0 reboot_counter + 190 8 r 0 reserved_century + 1b8 8 r 0 reserved_ibm_ps2_century + 1c0 1 e 1 power_on_after_fail 0:Disable 1:Enable + 1c4 4 e 6 debug_level 5:Notice 6:Info 7:Debug 8:Spew + 1d0 80 r 0 vbnv + 3f0 10 h 0 check_sum + CMOS start : 1c0 + CMOS end : 1cf + CMOS csum loc: 3f0 + VBNV start : ffffffff + VBNV size : ffffffff + CB version : 4.21-5-g7e6eae9679e3-dirty + Extra : + Build : Thu Sep 07 14:52:41 UTC 2023 + Time : 14:52:41 + Framebuffer : 000000007ff9a410 + Phys addr : fd000000 + X res : 0d800 + X res : 0d600 + Bytes / line: c80 + Bpp : 0d32 + pos/size red 16/8, green 8/8, blue 0/8, reserved 24/8 + GPIOs : 0 + id: port polarity val name + MACs : 0d10 + 0: 12:00:00:00:28:00 + 1: 00:00:00:fd:00:00 + 2: 20:03:00:00:58:02 + 3: 80:0c:00:00:20:10 + 4: 08:00:08:18:08:00 + 5: 16:00:00:00:10:00 + 6: 00:d0:fd:7f:00:00 + 7: 17:00:00:00:10:00 + 8: 00:e0:fd:7f:00:00 + 9: 37:00:00:00:10:00 + Multiboot tab: 0000000000000000 + CB header : 000000007ff9a000 + CB mainboard: 000000007ff9a344 + vendor : 0: Emulation + part_number : 10: QEMU x86 i440fx/piix4 + vboot handoff: 0000000000000000 + size : 0 + vdat addr : 0000000000000000 + size : 0 + SMBIOS : 7ff6d000 + size : 8000 + ROM MTRR : 0 + Tstamp table: 000000007ffdd000 + CBmem cons : 000000007ffde000 + Size : 1fff8 + Cursor : 3332 + MRC cache : 0000000000000000 + ACPI GNVS : 0000000000000000 + Board ID : ffffffff + RAM code : ffffffff + WiFi calib : 0000000000000000 + Ramoops buff: 0 + size : 0 + SF size : 0 + SF sector : 0 + SF erase cmd: 0 + FMAP offset : 0 + CBFS offset : 200 + CBFS size : 3ffe00 + Boot media size: 400000 + MTC start : 0 + MTC size : 0 + Chrome OS VPD: 0000000000000000 + RSDP : 000000007ff75000 + Unimpl. : 10 37 40 + => + +Note that "Unimpl." shows tags which U-Boot does not currently implement. diff --git a/test/cmd/coreboot.c b/test/cmd/coreboot.c index 4a00bad2b8f..0fcf66ac71b 100644 --- a/test/cmd/coreboot.c +++ b/test/cmd/coreboot.c @@ -22,6 +22,13 @@ static int test_cmd_cbsysinfo(struct unit_test_state *uts) ut_assertok(run_command("cbsysinfo", 0)); ut_assert_nextlinen("Coreboot table at"); + /* Make sure CMOS options are enabled */ + ut_assert_skip_to_line( + " 1c0 1 e 1 power_on_after_fail 0:Disable 1:Enable"); + ut_assert_skip_to_line("CMOS start : 1c0"); + ut_assert_nextline(" CMOS end : 1cf"); + ut_assert_nextline(" CMOS csum loc: 3f0"); + /* Make sure the linear frame buffer is enabled */ ut_assert_skip_to_linen("Framebuffer"); ut_assert_nextlinen(" Phys addr"); From 00815be92489c0cdc740595b478436576ba23492 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Oct 2024 16:32:09 -0600 Subject: [PATCH 19/28] x86: coreboot: Enable support for the configuration editor Enable cedit support along with required options and a simple style. Signed-off-by: Simon Glass --- arch/x86/dts/coreboot.dts | 7 +++++++ configs/coreboot64_defconfig | 2 ++ configs/coreboot_defconfig | 2 ++ 3 files changed, 11 insertions(+) diff --git a/arch/x86/dts/coreboot.dts b/arch/x86/dts/coreboot.dts index b867468e16c..39d1d325db9 100644 --- a/arch/x86/dts/coreboot.dts +++ b/arch/x86/dts/coreboot.dts @@ -54,6 +54,13 @@ menu-inset = <3>; menuitem-gap-y = <1>; }; + + cedit-theme { + font-size = <30>; + menu-inset = <3>; + menuitem-gap-y = <1>; + menu-title-margin-x = <30>; + }; }; sysinfo { diff --git a/configs/coreboot64_defconfig b/configs/coreboot64_defconfig index 553f1dc83f0..706c0cd71d7 100644 --- a/configs/coreboot64_defconfig +++ b/configs/coreboot64_defconfig @@ -17,6 +17,7 @@ CONFIG_SHOW_BOOT_PROGRESS=y CONFIG_USE_BOOTARGS=y CONFIG_BOOTARGS="root=/dev/sdb3 init=/sbin/init rootwait ro" CONFIG_BOOTCOMMAND="bootflow scan -l; if bootflow menu; then cls; bootflow boot; fi" +CONFIG_CEDIT=y CONFIG_SYS_PBSIZE=532 CONFIG_PRE_CONSOLE_BUFFER=y CONFIG_SYS_CONSOLE_INFO_QUIET=y @@ -46,6 +47,7 @@ CONFIG_TFTP_TSIZE=y CONFIG_USE_ROOTPATH=y CONFIG_REGMAP=y CONFIG_SYSCON=y +CONFIG_OFNODE_MULTI_TREE=y # CONFIG_ACPIGEN is not set CONFIG_SYS_IDE_MAXDEVICE=4 CONFIG_SYS_ATA_DATA_OFFSET=0 diff --git a/configs/coreboot_defconfig b/configs/coreboot_defconfig index 881bf8dd180..bd190e40ffc 100644 --- a/configs/coreboot_defconfig +++ b/configs/coreboot_defconfig @@ -16,6 +16,7 @@ CONFIG_SHOW_BOOT_PROGRESS=y CONFIG_USE_BOOTARGS=y CONFIG_BOOTARGS="root=/dev/sdb3 init=/sbin/init rootwait ro" CONFIG_BOOTCOMMAND="bootflow scan -l; if bootflow menu; then cls; bootflow boot; fi" +CONFIG_CEDIT=y CONFIG_PRE_CONSOLE_BUFFER=y CONFIG_SYS_CONSOLE_INFO_QUIET=y CONFIG_LOG=y @@ -42,6 +43,7 @@ CONFIG_TFTP_TSIZE=y CONFIG_USE_ROOTPATH=y CONFIG_REGMAP=y CONFIG_SYSCON=y +CONFIG_OFNODE_MULTI_TREE=y # CONFIG_ACPIGEN is not set CONFIG_SYS_IDE_MAXDEVICE=4 CONFIG_SYS_ATA_DATA_OFFSET=0 From e25c34ddb524043c26ff1db820584a40c0f094b8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Oct 2024 16:32:10 -0600 Subject: [PATCH 20/28] x86: coreboot: Add a command to check and update CMOS RAM Coreboot tables provide information about the CMOS-RAM checksum. Add a command which can check and update this. With this it is possible to adjust CMOS-RAM settings and tidy up the checksum afterwards. Signed-off-by: Simon Glass --- cmd/Kconfig | 11 ++++ cmd/x86/Makefile | 1 + cmd/x86/cbcmos.c | 139 +++++++++++++++++++++++++++++++++++++++ doc/usage/cmd/cbcmos.rst | 42 ++++++++++++ doc/usage/index.rst | 1 + test/cmd/coreboot.c | 41 ++++++++++++ 6 files changed, 235 insertions(+) create mode 100644 cmd/x86/cbcmos.c create mode 100644 doc/usage/cmd/cbcmos.rst diff --git a/cmd/Kconfig b/cmd/Kconfig index 4fba9fe6703..636833646f6 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2871,6 +2871,17 @@ config CMD_CBSYSINFO memory by coreboot before jumping to U-Boot. It can be useful for debugging the beaaviour of coreboot or U-Boot. +config CMD_CBCMOS + bool "cbcmos" + depends on X86 + default y if SYS_COREBOOT + help + This provides information options to check the CMOS RAM checksum, + if present, as well as to update it. + + It is useful when coreboot CMOS-RAM settings must be examined or + updated. + config CMD_CYCLIC bool "cyclic - Show information about cyclic functions" depends on CYCLIC diff --git a/cmd/x86/Makefile b/cmd/x86/Makefile index 925215235d3..5f3f5be2882 100644 --- a/cmd/x86/Makefile +++ b/cmd/x86/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_CMD_CBSYSINFO) += cbsysinfo.o obj-y += cpuid.o msr.o mtrr.o +obj-$(CONFIG_CMD_CBCMOS) += cbcmos.o obj-$(CONFIG_CMD_EXCEPTION) += exception.o obj-$(CONFIG_USE_HOB) += hob.o obj-$(CONFIG_HAVE_FSP) += fsp.o diff --git a/cmd/x86/cbcmos.c b/cmd/x86/cbcmos.c new file mode 100644 index 00000000000..fe5582fbf51 --- /dev/null +++ b/cmd/x86/cbcmos.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Support for booting from coreboot + * + * Copyright 2021 Google LLC + */ + +#define LOG_CATEGORY UCLASS_RTC + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +const struct sysinfo_t *get_table(void) +{ + if (!gd->arch.coreboot_table) { + printf("No coreboot sysinfo table found\n"); + return NULL; + } + + return &lib_sysinfo; +} + +static int calc_sum(struct udevice *dev, uint start_bit, uint bit_count) +{ + uint start_byte = start_bit / 8; + uint byte_count = bit_count / 8; + int ret, i; + uint sum; + + log_debug("Calc sum from %x: %x bytes\n", start_byte, byte_count); + sum = 0; + for (i = 0; i < bit_count / 8; i++) { + ret = rtc_read8(dev, start_bit / 8 + i); + if (ret < 0) + return ret; + sum += ret; + } + + return (sum & 0xff) << 8 | (sum & 0xff00) >> 8; +} + +/** + * prep_cbcmos() - Prepare for a CMOS-RAM command + * + * @tab: coreboot table + * @devnum: RTC device name to use, or NULL for the first one + * @dep: Returns RTC device on success + * Return: calculated checksum for CMOS RAM or -ve on error + */ +static int prep_cbcmos(const struct sysinfo_t *tab, const char *devname, + struct udevice **devp) +{ + struct udevice *dev; + int ret; + + if (!tab) + return CMD_RET_FAILURE; + if (devname) + ret = uclass_get_device_by_name(UCLASS_RTC, devname, &dev); + else + ret = uclass_first_device_err(UCLASS_RTC, &dev); + if (ret) { + printf("Failed to get RTC device: %dE\n", ret); + return ret; + } + + ret = calc_sum(dev, tab->cmos_range_start, + tab->cmos_range_end + 1 - tab->cmos_range_start); + if (ret < 0) { + printf("Failed to read RTC device: %dE\n", ret); + return ret; + } + *devp = dev; + + return ret; +} + +static int do_cbcmos_check(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const struct sysinfo_t *tab = get_table(); + struct udevice *dev; + u16 cur, sum; + int ret; + + ret = prep_cbcmos(tab, argv[1], &dev); + if (ret < 0) + return CMD_RET_FAILURE; + sum = ret; + + ret = rtc_read16(dev, tab->cmos_checksum_location / 8, &cur); + if (ret < 0) { + printf("Failed to read RTC device: %dE\n", ret); + return CMD_RET_FAILURE; + } + if (sum != cur) { + printf("Checksum %04x error: calculated %04x\n", cur, sum); + return CMD_RET_FAILURE; + } + + return 0; +} + +static int do_cbcmos_update(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const struct sysinfo_t *tab = get_table(); + struct udevice *dev; + u16 sum; + int ret; + + ret = prep_cbcmos(tab, argv[1], &dev); + if (ret < 0) + return CMD_RET_FAILURE; + sum = ret; + + ret = rtc_write16(dev, tab->cmos_checksum_location / 8, sum); + if (ret < 0) { + printf("Failed to read RTC device: %dE\n", ret); + return CMD_RET_FAILURE; + } + printf("Checksum %04x written\n", sum); + + return 0; +} + +U_BOOT_LONGHELP(cbcmos, + "check - check CMOS RAM\n" + "cbcmos update - Update CMOS-RAM checksum"; +); + +U_BOOT_CMD_WITH_SUBCMDS(cbcmos, "coreboot CMOS RAM", cbcmos_help_text, + U_BOOT_SUBCMD_MKENT(check, 2, 1, do_cbcmos_check), + U_BOOT_SUBCMD_MKENT(update, 2, 1, do_cbcmos_update)); diff --git a/doc/usage/cmd/cbcmos.rst b/doc/usage/cmd/cbcmos.rst new file mode 100644 index 00000000000..156521dd02b --- /dev/null +++ b/doc/usage/cmd/cbcmos.rst @@ -0,0 +1,42 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +cbcmos +====== + +Synopis +------- + +:: + + cbcmos check [] + cbcmos update [] + + +Description +----------- + +This checks or updates the CMOS-RAM checksum value against the CMOS-RAM +contents. It is used with coreboot, which provides information about where to +find the checksum and what part of the CMOS RAM it covers. + +If `` is provided then the named real-time clock (RTC) device is used. +Otherwise the default RTC is used. + +Example +------- + +This shows checking and updating a checksum across bytes 38 and 39 of the +CMOS RAM:: + + => rtc read 38 2 + 00000038: 71 00 q. + => cbc check + => rtc write 38 66 + => rtc read 38 2 + 00000038: 66 00 f. + => cbc check + Checksum 7100 error: calculated 6600 + => cbc update + Checksum 6600 written + => cbc check + => diff --git a/doc/usage/index.rst b/doc/usage/index.rst index db71711c393..b7c80ef3b12 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -43,6 +43,7 @@ Shell commands cmd/bootz cmd/button cmd/cat + cmd/cbcmos cmd/cbsysinfo cmd/cedit cmd/cli diff --git a/test/cmd/coreboot.c b/test/cmd/coreboot.c index 0fcf66ac71b..e1acf8697e8 100644 --- a/test/cmd/coreboot.c +++ b/test/cmd/coreboot.c @@ -7,10 +7,16 @@ */ #include +#include +#include #include #include #include +enum { + CSUM_LOC = 0x3f0 / 8, +}; + /** * test_cmd_cbsysinfo() - test the cbsysinfo command produces expected output * @@ -41,3 +47,38 @@ static int test_cmd_cbsysinfo(struct unit_test_state *uts) return 0; } CMD_TEST(test_cmd_cbsysinfo, UTF_CONSOLE); + +/* test cbcmos command */ +static int test_cmd_cbcmos(struct unit_test_state *uts) +{ + u16 old_csum, new_csum; + struct udevice *dev; + + /* initially the checksum should be correct */ + ut_assertok(run_command("cbcmos check", 0)); + ut_assert_console_end(); + + /* make a change to the checksum */ + ut_assertok(uclass_first_device_err(UCLASS_RTC, &dev)); + ut_assertok(rtc_read16(dev, CSUM_LOC, &old_csum)); + ut_assertok(rtc_write16(dev, CSUM_LOC, old_csum + 1)); + + /* now the command should fail */ + ut_asserteq(1, run_command("cbcmos check", 0)); + ut_assert_nextline("Checksum %04x error: calculated %04x", + old_csum + 1, old_csum); + ut_assert_console_end(); + + /* now get it to fix the checksum */ + ut_assertok(run_command("cbcmos update", 0)); + ut_assert_nextline("Checksum %04x written", old_csum); + ut_assert_console_end(); + + /* check the RTC looks right */ + ut_assertok(rtc_read16(dev, CSUM_LOC, &new_csum)); + ut_asserteq(old_csum, new_csum); + ut_assert_console_end(); + + return 0; +} +CMD_TEST(test_cmd_cbcmos, UTF_CONSOLE); From ae3b5928d61190d0faef7dfb2bbfc415a25b6ca5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Oct 2024 16:32:11 -0600 Subject: [PATCH 21/28] x86: coreboot: Allow building an expo for editing CMOS config Coreboot provides the CMOS layout in the tables it passes to U-Boot. Use that to build an editor for the CMOS settings. Signed-off-by: Simon Glass --- boot/Makefile | 3 + boot/expo_build_cb.c | 245 ++++++++++++++++++++++++++++++++ cmd/cedit.c | 28 ++++ doc/board/coreboot/coreboot.rst | 6 + doc/develop/cedit.rst | 2 +- doc/usage/cmd/cbcmos.rst | 3 + doc/usage/cmd/cedit.rst | 76 ++++++++++ include/expo.h | 8 ++ test/cmd/coreboot.c | 35 +++++ 9 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 boot/expo_build_cb.c diff --git a/boot/Makefile b/boot/Makefile index 0e0afad68d1..43def7c33d7 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -59,6 +59,9 @@ obj-$(CONFIG_$(PHASE_)LOAD_FIT) += common_fit.o obj-$(CONFIG_$(PHASE_)EXPO) += expo.o scene.o expo_build.o obj-$(CONFIG_$(PHASE_)EXPO) += scene_menu.o scene_textline.o +ifdef CONFIG_COREBOOT_SYSINFO +obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo_build_cb.o +endif obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE) += vbe.o obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_REQUEST) += vbe_request.o diff --git a/boot/expo_build_cb.c b/boot/expo_build_cb.c new file mode 100644 index 00000000000..442ad760e79 --- /dev/null +++ b/boot/expo_build_cb.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Building an expo from an FDT description + * + * Copyright 2022 Google LLC + * Written by Simon Glass + */ + +#define LOG_CATEGORY LOGC_EXPO + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct build_info - Information to use when building + */ +struct build_info { + const struct cb_cmos_option_table *tab; + struct cedit_priv *priv; +}; + +/** + * convert_to_title() - Convert text to 'title' format and allocate a string + * + * Converts "this_is_a_test" to "This is a test" so it looks better + * + * @text: Text to convert + * Return: Allocated string, or NULL if out of memory + */ +static char *convert_to_title(const char *text) +{ + int len = strlen(text); + char *buf, *s; + + buf = malloc(len + 1); + if (!buf) + return NULL; + + for (s = buf; *text; s++, text++) { + if (s == buf) + *s = toupper(*text); + else if (*text == '_') + *s = ' '; + else + *s = *text; + } + *s = '\0'; + + return buf; +} + +/** + * menu_build() - Build a menu and add it to a scene + * + * See doc/developer/expo.rst for a description of the format + * + * @info: Build information + * @entry: CMOS entry to build a menu for + * @scn: Scene to add the menu to + * @objp: Returns the object pointer + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error, -ENOENT if there is a references to a non-existent string + */ +static int menu_build(struct build_info *info, + const struct cb_cmos_entries *entry, struct scene *scn, + struct scene_obj **objp) +{ + struct scene_obj_menu *menu; + const void *ptr, *end; + uint menu_id; + char *title; + int ret, i; + + ret = scene_menu(scn, entry->name, 0, &menu); + if (ret < 0) + return log_msg_ret("men", ret); + menu_id = ret; + + title = convert_to_title(entry->name); + if (!title) + return log_msg_ret("con", -ENOMEM); + + /* Set the title */ + ret = scene_txt_str(scn, "title", 0, 0, title, NULL); + if (ret < 0) + return log_msg_ret("tit", ret); + menu->title_id = ret; + + end = (void *)info->tab + info->tab->size; + for (ptr = (void *)info->tab + info->tab->header_length, i = 0; + ptr < end; i++) { + const struct cb_cmos_enums *enums = ptr; + struct scene_menitem *item; + uint label; + + ptr += enums->size; + if (enums->tag != CB_TAG_OPTION_ENUM || + enums->config_id != entry->config_id) + continue; + + ret = scene_txt_str(scn, enums->text, 0, 0, enums->text, NULL); + if (ret < 0) + return log_msg_ret("tit", ret); + label = ret; + + ret = scene_menuitem(scn, menu_id, simple_xtoa(i), 0, 0, label, + 0, 0, 0, &item); + if (ret < 0) + return log_msg_ret("mi", ret); + item->value = enums->value; + } + *objp = &menu->obj; + + return 0; +} + +/** + * scene_build() - Build a scene and all its objects + * + * See doc/developer/expo.rst for a description of the format + * + * @info: Build information + * @scn: Scene to add the object to + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format + * error, -ENOENT if there is a references to a non-existent string + */ +static int scene_build(struct build_info *info, struct expo *exp) +{ + struct scene_obj_menu *menu; + const void *ptr, *end; + struct scene_obj *obj; + struct scene *scn; + uint label, menu_id; + int ret; + + ret = scene_new(exp, "cmos", 0, &scn); + if (ret < 0) + return log_msg_ret("scn", ret); + + ret = scene_txt_str(scn, "title", 0, 0, "CMOS RAM settings", NULL); + if (ret < 0) + return log_msg_ret("add", ret); + scn->title_id = ret; + + ret = scene_txt_str(scn, "prompt", 0, 0, + "UP and DOWN to choose, ENTER to select", NULL); + if (ret < 0) + return log_msg_ret("add", ret); + + end = (void *)info->tab + info->tab->size; + for (ptr = (void *)info->tab + info->tab->header_length; ptr < end;) { + const struct cb_cmos_entries *entry; + const struct cb_record *rec = ptr; + + entry = ptr; + ptr += rec->size; + if (rec->tag != CB_TAG_OPTION) + continue; + switch (entry->config) { + case 'e': + ret = menu_build(info, entry, scn, &obj); + break; + default: + continue; + } + if (ret < 0) + return log_msg_ret("add", ret); + + obj->start_bit = entry->bit; + obj->bit_length = entry->length; + } + + ret = scene_menu(scn, "save", EXPOID_SAVE, &menu); + if (ret < 0) + return log_msg_ret("men", ret); + menu_id = ret; + + ret = scene_txt_str(scn, "save", 0, 0, "Save and exit", NULL); + if (ret < 0) + return log_msg_ret("sav", ret); + label = ret; + ret = scene_menuitem(scn, menu_id, "save", 0, 0, label, + 0, 0, 0, NULL); + if (ret < 0) + return log_msg_ret("mi", ret); + + ret = scene_menu(scn, "nosave", EXPOID_DISCARD, &menu); + if (ret < 0) + return log_msg_ret("men", ret); + menu_id = ret; + + ret = scene_txt_str(scn, "nosave", 0, 0, "Exit without saving", NULL); + if (ret < 0) + return log_msg_ret("nos", ret); + label = ret; + ret = scene_menuitem(scn, menu_id, "exit", 0, 0, label, + 0, 0, 0, NULL); + if (ret < 0) + return log_msg_ret("mi", ret); + + return 0; +} + +static int build_it(struct build_info *info, struct expo **expp) +{ + struct expo *exp; + int ret; + + ret = expo_new("coreboot", NULL, &exp); + if (ret) + return log_msg_ret("exp", ret); + expo_set_dynamic_start(exp, EXPOID_BASE_ID); + + ret = scene_build(info, exp); + if (ret < 0) + return log_msg_ret("scn", ret); + + *expp = exp; + + return 0; +} + +int cb_expo_build(struct expo **expp) +{ + struct build_info info; + struct expo *exp; + int ret; + + info.tab = lib_sysinfo.option_table; + if (!info.tab) + return log_msg_ret("tab", -ENOENT); + + ret = build_it(&info, &exp); + if (ret) + return log_msg_ret("bui", ret); + *expp = exp; + + return 0; +} diff --git a/cmd/cedit.c b/cmd/cedit.c index fec67a8e334..f696356419e 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -67,6 +67,28 @@ static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +#ifdef CONFIG_COREBOOT_SYSINFO +static int do_cedit_cb_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct expo *exp; + int ret; + + if (argc > 1) + return CMD_RET_USAGE; + + ret = cb_expo_build(&exp); + if (ret) { + printf("Failed to build expo: %dE\n", ret); + return CMD_RET_FAILURE; + } + + cur_exp = exp; + + return 0; +} +#endif /* CONFIG_COREBOOT_SYSINFO */ + static int do_cedit_write_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -271,6 +293,9 @@ static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, U_BOOT_LONGHELP(cedit, "load - load config editor\n" +#ifdef CONFIG_COREBOOT_SYSINFO + "cb_load - load coreboot CMOS editor\n" +#endif "cedit read_fdt - read settings\n" "cedit write_fdt - write settings\n" "cedit read_env [-v] - read settings from env vars\n" @@ -281,6 +306,9 @@ U_BOOT_LONGHELP(cedit, U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load), +#ifdef CONFIG_COREBOOT_SYSINFO + U_BOOT_SUBCMD_MKENT(cb_load, 5, 1, do_cedit_cb_load), +#endif U_BOOT_SUBCMD_MKENT(read_fdt, 5, 1, do_cedit_read_fdt), U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), U_BOOT_SUBCMD_MKENT(read_env, 2, 1, do_cedit_read_env), diff --git a/doc/board/coreboot/coreboot.rst b/doc/board/coreboot/coreboot.rst index a177265c16e..f52b24ff43d 100644 --- a/doc/board/coreboot/coreboot.rst +++ b/doc/board/coreboot/coreboot.rst @@ -182,3 +182,9 @@ CI runs tests using a pre-built coreboot image. This ensures that U-Boot can boot as a coreboot payload, based on a known-good build of coreboot. To update the `coreboot.rom` file which is used, see ``tools/Dockerfile`` + +Editing CMOS RAM settings +------------------------- + +U-Boot supports creating a configuration editor to edit coreboot CMOS-RAM +settings. See :ref:`cedit_cb_load`. diff --git a/doc/develop/cedit.rst b/doc/develop/cedit.rst index 310be889240..1ac55ab1219 100644 --- a/doc/develop/cedit.rst +++ b/doc/develop/cedit.rst @@ -172,4 +172,4 @@ Cedit provides several options for persistent settings: For now, reading and writing settings is not automatic. See the :doc:`../usage/cmd/cedit` for how to do this on the command line or in a -script. +script. For x86 devices, see :ref:`cedit_cb_load`. diff --git a/doc/usage/cmd/cbcmos.rst b/doc/usage/cmd/cbcmos.rst index 156521dd02b..9395cf1cbd7 100644 --- a/doc/usage/cmd/cbcmos.rst +++ b/doc/usage/cmd/cbcmos.rst @@ -40,3 +40,6 @@ CMOS RAM:: Checksum 6600 written => cbc check => + +See also :ref:`cedit_cb_load` which shows an example that includes the +configuration editor. diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index f29f1b3f388..e54ea204b9f 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -18,6 +18,7 @@ Synopsis cedit write_env [-v] cedit read_env [-v] cedit write_cmos [-v] [dev] + cedit cb_load Description ----------- @@ -92,6 +93,13 @@ updated. Normally the first RTC device is used to hold the data. You can specify a different device by name using the `dev` parameter. +.. _cedit_cb_load: + +cedit cb_load +~~~~~~~~~~~~~ + +This is supported only on x86 devices booted from coreboot. It creates a new +configuration editor which can be used to edit CMOS settings. Example ------- @@ -158,3 +166,71 @@ Here is an example with the device specified:: => cedit write_cmos rtc@43 => + +This example shows editing coreboot CMOS-RAM settings. A script could be used +to automate this:: + + => cbsysinfo + Coreboot table at 500, size 5c4, records 1d (dec 29), decoded to 000000007dce3f40, forwarded to 000000007ff9a000 + + CPU KHz : 0 + Serial I/O port: 00000000 + base : 00000000 + pointer : 000000007ff9a370 + type : 1 + base : 000003f8 + baud : 0d115200 + regwidth : 1 + input_hz : 0d1843200 + PCI addr : 00000010 + Mem ranges : 7 + id: type || base || size + 0: 10:table 0000000000000000 0000000000001000 + 1: 01:ram 0000000000001000 000000000009f000 + 2: 02:reserved 00000000000a0000 0000000000060000 + 3: 01:ram 0000000000100000 000000007fe6d000 + 4: 10:table 000000007ff6d000 0000000000093000 + 5: 02:reserved 00000000fec00000 0000000000001000 + 6: 02:reserved 00000000ff800000 0000000000800000 + option_table: 000000007ff9a018 + Bit Len Cfg ID Name + 0 180 r 0 reserved_memory + 180 1 e 4 boot_option 0:Fallback 1:Normal + 184 4 h 0 reboot_counter + 190 8 r 0 reserved_century + 1b8 8 r 0 reserved_ibm_ps2_century + 1c0 1 e 1 power_on_after_fail 0:Disable 1:Enable + 1c4 4 e 6 debug_level 5:Notice 6:Info 7:Debug 8:Spew + 1d0 80 r 0 vbnv + 3f0 10 h 0 check_sum + CMOS start : 1c0 + CMOS end : 1cf + CMOS csum loc: 3f0 + VBNV start : ffffffff + VBNV size : ffffffff + ... + Unimpl. : 10 37 40 + +Check that the CMOS RAM checksum is correct, then create a configuration editor +and load the settings from CMOS RAM:: + + => cbcmos check + => cedit cb + => cedit read_cmos + +Now run the cedit. In this case the user selected 'save' so `cedit run` returns +success:: + + => if cedit run; then cedit write_cmos -v; fi + Write 2 bytes from offset 30 to 38 + => echo $? + 0 + +Update the checksum in CMOS RAM:: + + => cbcmos check + Checksum 6100 error: calculated 7100 + => cbcmos update + Checksum 7100 written + => cbcmos check + => diff --git a/include/expo.h b/include/expo.h index 8cb37260db5..3c383d2e2ee 100644 --- a/include/expo.h +++ b/include/expo.h @@ -762,4 +762,12 @@ int expo_apply_theme(struct expo *exp, ofnode node); */ int expo_build(ofnode root, struct expo **expp); +/** + * cb_expo_build() - Build an expo for coreboot CMOS RAM + * + * @expp: Returns the expo created + * Return: 0 if OK, -ve on error + */ +int cb_expo_build(struct expo **expp); + #endif /*__EXPO_H */ diff --git a/test/cmd/coreboot.c b/test/cmd/coreboot.c index e1acf8697e8..a99898d15c4 100644 --- a/test/cmd/coreboot.c +++ b/test/cmd/coreboot.c @@ -6,12 +6,16 @@ * Written by Simon Glass */ +#include #include #include +#include #include +#include #include #include #include +#include "../../boot/scene_internal.h" enum { CSUM_LOC = 0x3f0 / 8, @@ -82,3 +86,34 @@ static int test_cmd_cbcmos(struct unit_test_state *uts) return 0; } CMD_TEST(test_cmd_cbcmos, UTF_CONSOLE); + +/* test 'cedit cb_load' command */ +static int test_cmd_cedit_cb_load(struct unit_test_state *uts) +{ + struct scene_obj_menu *menu; + struct video_priv *vid_priv; + struct scene_obj_txt *txt; + struct scene *scn; + struct expo *exp; + int scn_id; + + ut_assertok(run_command("cedit cb_load", 0)); + ut_assertok(run_command("cedit read_cmos", 0)); + ut_assert_console_end(); + + exp = cur_exp; + scn_id = cedit_prepare(exp, &vid_priv, &scn); + ut_assert(scn_id > 0); + ut_assertnonnull(scn); + + /* just do a very basic test that the first menu is present */ + menu = scene_obj_find(scn, scn->highlight_id, SCENEOBJT_NONE); + ut_assertnonnull(menu); + + txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE); + ut_assertnonnull(txt); + ut_asserteq_str("Boot option", expo_get_str(exp, txt->str_id)); + + return 0; +} +CMD_TEST(test_cmd_cedit_cb_load, UTF_CONSOLE); From 12d583b38fa570da1fc5aba6d1f5aaf2ba3d8505 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Oct 2024 13:47:52 +0100 Subject: [PATCH 22/28] log: Add a new category for tests In some core test code, no existing categories make sense. Add a new one for testing. Signed-off-by: Simon Glass --- common/log.c | 1 + include/log.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/common/log.c b/common/log.c index b83a6618900..c9fe35230d6 100644 --- a/common/log.c +++ b/common/log.c @@ -32,6 +32,7 @@ static const char *const log_cat_name[] = { "fs", "expo", "console", + "test", }; _Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE, diff --git a/include/log.h b/include/log.h index bf81a27011f..4f6d6a2c2cf 100644 --- a/include/log.h +++ b/include/log.h @@ -106,6 +106,8 @@ enum log_category_t { LOGC_EXPO, /** @LOGC_CONSOLE: Related to the console and stdio */ LOGC_CONSOLE, + /** @LOGC_TEST: Related to testing */ + LOGC_TEST, /** @LOGC_COUNT: Number of log categories */ LOGC_COUNT, /** @LOGC_END: Sentinel value for lists of log categories */ From 3f1d79932a1b3804fe108961ad95c44adbc6cb72 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Oct 2024 13:47:53 +0100 Subject: [PATCH 23/28] test: Allow saving and restoring the bloblist Tests which create a new bloblist overwrite the existing one in sandbox. Provide a flag for tests to declare this behaviour. Save and restore the bloblist pointer so that other tests remain unaffected. Note that when sandbox is running normally, the bloblist has been relocated to high in memory. The existing bloblist tests create a new bloblist low in memory, so they do not conflict. Correct a build error on coreboot by using accessors for gd->bloblist: Signed-off-by: Simon Glass --- include/asm-generic/global_data.h | 2 ++ include/test/test.h | 3 +++ test/test-main.c | 13 +++++++++++++ 3 files changed, 18 insertions(+) diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index bf593d96a84..26277b93976 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -545,8 +545,10 @@ static_assert(sizeof(struct global_data) == GD_SIZE); #if CONFIG_IS_ENABLED(BLOBLIST) #define gd_bloblist() gd->bloblist +#define gd_set_bloblist(_val) gd->bloblist = (_val) #else #define gd_bloblist() NULL +#define gd_set_bloblist(_val) #endif #if CONFIG_IS_ENABLED(BOOTSTAGE) diff --git a/include/test/test.h b/include/test/test.h index 92eec2eb6f9..21c0478befe 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -29,6 +29,7 @@ * @of_other: Live tree for the other FDT * @runs_per_test: Number of times to run each test (typically 1) * @force_run: true to run tests marked with the UTF_MANUAL flag + * @old_bloblist: stores the old gd->bloblist pointer * @expect_str: Temporary string used to hold expected string value * @actual_str: Temporary string used to hold actual string value */ @@ -50,6 +51,7 @@ struct unit_test_state { struct device_node *of_other; int runs_per_test; bool force_run; + void *old_bloblist; char expect_str[512]; char actual_str[512]; }; @@ -73,6 +75,7 @@ enum ut_flags { UTF_MANUAL = BIT(8), UTF_ETH_BOOTDEV = BIT(9), /* enable Ethernet bootdevs */ UTF_SF_BOOTDEV = BIT(10), /* enable SPI flash bootdevs */ + UFT_BLOBLIST = BIT(11), /* test changes gd->bloblist */ }; /** diff --git a/test/test-main.c b/test/test-main.c index ca6a073a8cb..7a1f74a2c84 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -4,6 +4,8 @@ * Written by Simon Glass */ +#define LOG_CATEGORY LOGC_TEST + #include #include #include @@ -386,6 +388,12 @@ static int test_pre_run(struct unit_test_state *uts, struct unit_test *test) return -EAGAIN; } } + if (test->flags & UFT_BLOBLIST) { + log_debug("save bloblist %p\n", gd_bloblist()); + uts->old_bloblist = gd_bloblist(); + gd_set_bloblist(NULL); + } + ut_silence_console(uts); return 0; @@ -409,6 +417,11 @@ static int test_post_run(struct unit_test_state *uts, struct unit_test *test) free(uts->of_other); uts->of_other = NULL; + if (test->flags & UFT_BLOBLIST) { + gd_set_bloblist(uts->old_bloblist); + log_debug("restore bloblist %p\n", gd_bloblist()); + } + blkcache_free(); return 0; From 6cdb1497f99eafe7661c4bde1dca4831bfdd7120 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Oct 2024 13:47:54 +0100 Subject: [PATCH 24/28] bloblist: test: Mark tests with UTF_BLOBLIST Mark bloblist tests with this flag so that other tests which use bloblist remain unaffected. Signed-off-by: Simon Glass --- test/bloblist.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/bloblist.c b/test/bloblist.c index fd85c7ab79e..e0ad94e77d8 100644 --- a/test/bloblist.c +++ b/test/bloblist.c @@ -94,7 +94,7 @@ static int bloblist_test_init(struct unit_test_state *uts) return 1; } -BLOBLIST_TEST(bloblist_test_init, 0); +BLOBLIST_TEST(bloblist_test_init, UFT_BLOBLIST); static int bloblist_test_blob(struct unit_test_state *uts) { @@ -134,7 +134,7 @@ static int bloblist_test_blob(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_blob, 0); +BLOBLIST_TEST(bloblist_test_blob, UFT_BLOBLIST); /* Check bloblist_ensure_size_ret() */ static int bloblist_test_blob_ensure(struct unit_test_state *uts) @@ -168,7 +168,7 @@ static int bloblist_test_blob_ensure(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_blob_ensure, 0); +BLOBLIST_TEST(bloblist_test_blob_ensure, UFT_BLOBLIST); static int bloblist_test_bad_blob(struct unit_test_state *uts) { @@ -184,7 +184,7 @@ static int bloblist_test_bad_blob(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_bad_blob, 0); +BLOBLIST_TEST(bloblist_test_bad_blob, UFT_BLOBLIST); static int bloblist_test_checksum(struct unit_test_state *uts) { @@ -257,7 +257,7 @@ static int bloblist_test_checksum(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_checksum, 0); +BLOBLIST_TEST(bloblist_test_checksum, UFT_BLOBLIST); /* Test the 'bloblist info' command */ static int bloblist_test_cmd_info(struct unit_test_state *uts) @@ -278,7 +278,7 @@ static int bloblist_test_cmd_info(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_cmd_info, UTF_CONSOLE); +BLOBLIST_TEST(bloblist_test_cmd_info, UFT_BLOBLIST | UTF_CONSOLE); /* Test the 'bloblist list' command */ static int bloblist_test_cmd_list(struct unit_test_state *uts) @@ -300,7 +300,7 @@ static int bloblist_test_cmd_list(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_cmd_list, UTF_CONSOLE); +BLOBLIST_TEST(bloblist_test_cmd_list, UFT_BLOBLIST | UTF_CONSOLE); /* Test alignment of bloblist blobs */ static int bloblist_test_align(struct unit_test_state *uts) @@ -358,7 +358,7 @@ static int bloblist_test_align(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_align, 0); +BLOBLIST_TEST(bloblist_test_align, UFT_BLOBLIST); /* Test relocation of a bloblist */ static int bloblist_test_reloc(struct unit_test_state *uts) @@ -392,7 +392,7 @@ static int bloblist_test_reloc(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_reloc, 0); +BLOBLIST_TEST(bloblist_test_reloc, UFT_BLOBLIST); /* Test expansion of a blob */ static int bloblist_test_grow(struct unit_test_state *uts) @@ -445,7 +445,7 @@ static int bloblist_test_grow(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_grow, 0); +BLOBLIST_TEST(bloblist_test_grow, UFT_BLOBLIST); /* Test shrinking of a blob */ static int bloblist_test_shrink(struct unit_test_state *uts) @@ -495,7 +495,7 @@ static int bloblist_test_shrink(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_shrink, 0); +BLOBLIST_TEST(bloblist_test_shrink, UFT_BLOBLIST); /* Test failing to adjust a blob size */ static int bloblist_test_resize_fail(struct unit_test_state *uts) @@ -530,7 +530,7 @@ static int bloblist_test_resize_fail(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_resize_fail, 0); +BLOBLIST_TEST(bloblist_test_resize_fail, UFT_BLOBLIST); /* Test expanding the last blob in a bloblist */ static int bloblist_test_resize_last(struct unit_test_state *uts) @@ -581,7 +581,7 @@ static int bloblist_test_resize_last(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_resize_last, 0); +BLOBLIST_TEST(bloblist_test_resize_last, UFT_BLOBLIST); /* Check a completely full bloblist */ static int bloblist_test_blob_maxsize(struct unit_test_state *uts) @@ -604,7 +604,7 @@ static int bloblist_test_blob_maxsize(struct unit_test_state *uts) return 0; } -BLOBLIST_TEST(bloblist_test_blob_maxsize, 0); +BLOBLIST_TEST(bloblist_test_blob_maxsize, UFT_BLOBLIST); int do_ut_bloblist(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) From d2e1c8a34876dd9e71c2a194fd83f8586716f480 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Oct 2024 13:47:55 +0100 Subject: [PATCH 25/28] sandbox: Convert sb command to use new macro Ise the new U_BOOT_CMD_WITH_SUBCMDS() macro instead of writing the code out manually. Signed-off-by: Simon Glass --- cmd/sb.c | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/cmd/sb.c b/cmd/sb.c index db485fddfca..9dbb53275b3 100644 --- a/cmd/sb.c +++ b/cmd/sb.c @@ -40,29 +40,10 @@ static int do_sb_state(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } -static struct cmd_tbl cmd_sb_sub[] = { - U_BOOT_CMD_MKENT(handoff, 1, 0, do_sb_handoff, "", ""), - U_BOOT_CMD_MKENT(state, 1, 0, do_sb_state, "", ""), -}; - -static int do_sb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) -{ - struct cmd_tbl *c; - - /* Skip past 'sb' */ - argc--; - argv++; - - c = find_cmd_tbl(argv[0], cmd_sb_sub, ARRAY_SIZE(cmd_sb_sub)); - if (c) - return c->cmd(cmdtp, flag, argc, argv); - else - return CMD_RET_USAGE; -} - -U_BOOT_CMD( - sb, 8, 1, do_sb, - "Sandbox status commands", +U_BOOT_LONGHELP(sb, "handoff - Show handoff data received from SPL\n" - "sb state - Show sandbox state" -); + "sb state - Show sandbox state"); + +U_BOOT_CMD_WITH_SUBCMDS(sb, "Sandbox status commands", sb_help_text, + U_BOOT_SUBCMD_MKENT(handoff, 1, 1, do_sb_handoff), + U_BOOT_SUBCMD_MKENT(state, 1, 1, do_sb_state)); From ec6d30649cd5026acf64301cca3d1721c16e69e7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Oct 2024 13:47:56 +0100 Subject: [PATCH 26/28] doc: sandbox: Add docs for the sb command This command has a few small features, so document it. Signed-off-by: Simon Glass --- doc/usage/cmd/sb.rst | 54 ++++++++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + 2 files changed, 55 insertions(+) create mode 100644 doc/usage/cmd/sb.rst diff --git a/doc/usage/cmd/sb.rst b/doc/usage/cmd/sb.rst new file mode 100644 index 00000000000..6f54f9d9eb7 --- /dev/null +++ b/doc/usage/cmd/sb.rst @@ -0,0 +1,54 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +.. index:: + single: sbi (command) + +sbi command +=========== + +Synopsis +-------- + +:: + + sb handoff + sb state + +Description +----------- + +The *sb* command is used to display information about sandbox's internal +operation. See :doc:`/arch/sandbox/index` for more information. + +sb handoff +~~~~~~~~~~ + +This shows information about any handoff information received from SPL. If +U-Boot is started from an SPL build, it shows a valid magic number. + +sb state +~~~~~~~~ + +This shows basic information about the sandbox state, currently just the +command-line with which sandbox was started. + +Example +------- + +This shows checking for the presence of SPL-handoff information. For this to +work, ``u-boot-spl`` must be run, with build that enables ``CONFIG_SPL``, such +as ``sandbox_spl``:: + + => sb handoff + SPL handoff magic 14f93c7b + +This shows output from the *sb state* subcommand:: + + => sb state + Arguments: + /tmp/b/sandbox/u-boot -D + +Configuration +------------- + +The *sb handoff* command is only supported if CONFIG_HANDOFF is enabled. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index b7c80ef3b12..cb7a23f1170 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -104,6 +104,7 @@ Shell commands cmd/reset cmd/rng cmd/saves + cmd/sb cmd/sbi cmd/scmi cmd/scp03 From 5400c4bc0528b1f2bebd97de1deed62cbd65039b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Oct 2024 13:47:57 +0100 Subject: [PATCH 27/28] sandbox: Add a way to show the sandbox memory-mapping This is mostly hidden in the background, but it is sometimes useful to look at it. Add a function to allow this. Signed-off-by: Simon Glass --- arch/sandbox/cpu/cpu.c | 13 +++++++++++++ arch/sandbox/include/asm/cpu.h | 3 +++ cmd/sb.c | 11 +++++++++++ doc/usage/cmd/sb.rst | 25 +++++++++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c index 06f8c13fab9..d1c4dcf0764 100644 --- a/arch/sandbox/cpu/cpu.c +++ b/arch/sandbox/cpu/cpu.c @@ -253,6 +253,19 @@ phys_addr_t map_to_sysmem(const void *ptr) return mentry->tag; } +void sandbox_map_list(void) +{ + struct sandbox_mapmem_entry *mentry; + struct sandbox_state *state = state_get_current(); + + printf("Sandbox memory-mapping\n"); + printf("%8s %16s %6s\n", "Addr", "Mapping", "Refcnt"); + list_for_each_entry(mentry, &state->mapmem_head, sibling_node) { + printf("%8lx %p %6d\n", mentry->tag, mentry->ptr, + mentry->refcnt); + } +} + unsigned long sandbox_read(const void *addr, enum sandboxio_size_t size) { struct sandbox_state *state = state_get_current(); diff --git a/arch/sandbox/include/asm/cpu.h b/arch/sandbox/include/asm/cpu.h index c97ac7ba95b..682bb3376d1 100644 --- a/arch/sandbox/include/asm/cpu.h +++ b/arch/sandbox/include/asm/cpu.h @@ -8,4 +8,7 @@ void cpu_sandbox_set_current(const char *name); +/* show the mapping of sandbox addresses to pointers */ +void sandbox_map_list(void); + #endif /* __SANDBOX_CPU_H */ diff --git a/cmd/sb.c b/cmd/sb.c index 9dbb53275b3..9245052492e 100644 --- a/cmd/sb.c +++ b/cmd/sb.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,14 @@ static int do_sb_handoff(struct cmd_tbl *cmdtp, int flag, int argc, #endif } +static int do_sb_map(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + sandbox_map_list(); + + return 0; +} + static int do_sb_state(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -42,8 +51,10 @@ static int do_sb_state(struct cmd_tbl *cmdtp, int flag, int argc, U_BOOT_LONGHELP(sb, "handoff - Show handoff data received from SPL\n" + "sb map - Show mapped memory\n" "sb state - Show sandbox state"); U_BOOT_CMD_WITH_SUBCMDS(sb, "Sandbox status commands", sb_help_text, U_BOOT_SUBCMD_MKENT(handoff, 1, 1, do_sb_handoff), + U_BOOT_SUBCMD_MKENT(map, 1, 1, do_sb_map), U_BOOT_SUBCMD_MKENT(state, 1, 1, do_sb_state)); diff --git a/doc/usage/cmd/sb.rst b/doc/usage/cmd/sb.rst index 6f54f9d9eb7..37431aff7c8 100644 --- a/doc/usage/cmd/sb.rst +++ b/doc/usage/cmd/sb.rst @@ -12,6 +12,7 @@ Synopsis :: sb handoff + sb map sb state Description @@ -26,6 +27,24 @@ sb handoff This shows information about any handoff information received from SPL. If U-Boot is started from an SPL build, it shows a valid magic number. +sb map +~~~~~~ + +This shows any mappings between sandbox's emulated RAM and the underlying host +address-space. + +Fields shown are: + +Addr + Address in emulated RAM + +Mapping + Equivalent address in the host address-space. While sandbox requests address + ``0x10000000`` from the OS, this is not always available. + +Refcnt + Shows the number of references to this mapping. + sb state ~~~~~~~~ @@ -42,6 +61,12 @@ as ``sandbox_spl``:: => sb handoff SPL handoff magic 14f93c7b +This shows output from the *sb map* subcommand, with a single mapping:: + + Sandbox memory-mapping + Addr Mapping Refcnt + ff000000 000056185b46d6d0 2 + This shows output from the *sb state* subcommand:: => sb state From dc24948a457e2f5cb7c518a9458be851b8b7e9ea Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Oct 2024 13:47:58 +0100 Subject: [PATCH 28/28] sandbox: Fix comment for nomap_sysmem() function This should say 'cast' rather than 'case', so fix it. Signed-off-by: Simon Glass --- arch/sandbox/include/asm/io.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sandbox/include/asm/io.h b/arch/sandbox/include/asm/io.h index a23bd64994a..3d09170063f 100644 --- a/arch/sandbox/include/asm/io.h +++ b/arch/sandbox/include/asm/io.h @@ -235,7 +235,7 @@ static inline void unmap_sysmem(const void *vaddr) * nomap_sysmem() - pass through an address unchanged * * This is used to indicate an address which should NOT be mapped, e.g. in - * SMBIOS tables. Using this function instead of a case shows that the sandbox + * SMBIOS tables. Using this function instead of a cast shows that the sandbox * conversion has been done */ static inline void *nomap_sysmem(phys_addr_t paddr, unsigned long len)