aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2023-08-25 17:52:59 -0400
committerTom Rini <trini@konsulko.com>2023-08-25 17:52:59 -0400
commit05763b71d2fcfe9729bf5ef0b14bd00c3af0c985 (patch)
tree8a62bb1cda41f5fb2ff2cb0211e927e45645e18a
parent7c6b18fb5494a1da2421b16c42d31fc466c38362 (diff)
parent84b08afcbb8f8b4402b940d87bf5822984eedb3d (diff)
downloadu-boot-05763b71d2fcfe9729bf5ef0b14bd00c3af0c985.zip
u-boot-05763b71d2fcfe9729bf5ef0b14bd00c3af0c985.tar.gz
u-boot-05763b71d2fcfe9729bf5ef0b14bd00c3af0c985.tar.bz2
Merge branch '2023-08-25-add-persistent-config-editor-via-expo' into next
To quote the author: So far cedit does not support reading and writing the configuration. This series add several features related to this: First, it adds support for using a file on a filesystem. This is in FDT format and provides enough information to reset the cedit back to the saved settings. Second, it adds support for using the U-Boot environment. Since the environment is generally saved across reboots, this feature provides an easy way of storing the state on most boards. The variables all have a 'c.' prefix to avoid confusion with other variables. Finally it adds support for using CMOS RAM. This is commonly used on x86 devices to store BIOS settings. The expo schema provides information on the register layout. Some other minor tweaks and improvements are included along the way.
-rw-r--r--arch/sandbox/dts/sandbox.dtsi12
-rw-r--r--arch/sandbox/dts/test.dts12
-rw-r--r--boot/cedit.c569
-rw-r--r--boot/expo.c15
-rw-r--r--boot/expo_build.c22
-rw-r--r--boot/scene.c18
-rw-r--r--boot/scene_internal.h50
-rw-r--r--boot/scene_menu.c20
-rw-r--r--cmd/cedit.c209
-rw-r--r--doc/develop/cedit.rst169
-rw-r--r--doc/develop/expo.rst48
-rw-r--r--doc/develop/index.rst1
-rw-r--r--doc/usage/cmd/cedit.rst117
-rw-r--r--include/abuf.h9
-rw-r--r--include/cedit.h125
-rw-r--r--include/expo.h33
-rw-r--r--lib/abuf.c5
-rw-r--r--test/boot/Makefile1
-rw-r--r--test/boot/cedit.c198
-rw-r--r--test/boot/expo.c88
-rw-r--r--test/boot/files/expo_ids.h25
-rw-r--r--test/boot/files/expo_layout.dts28
-rw-r--r--test/lib/abuf.c25
-rw-r--r--test/py/tests/test_ut.py4
-rwxr-xr-xtools/expo.py22
25 files changed, 1679 insertions, 146 deletions
diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi
index 8aaf911..ff7e558 100644
--- a/arch/sandbox/dts/sandbox.dtsi
+++ b/arch/sandbox/dts/sandbox.dtsi
@@ -16,12 +16,6 @@
stdout-path = "/serial";
};
- cedit-theme {
- font-size = <30>;
- menu-inset = <3>;
- menuitem-gap-y = <1>;
- };
-
alarm_wdt: alarm-wdt {
compatible = "sandbox,alarm-wdt";
timeout-sec = <5>;
@@ -36,6 +30,12 @@
bootstd {
compatible = "u-boot,boot-std";
filename-prefixes = "./";
+
+ cedit-theme {
+ font-size = <30>;
+ menu-inset = <3>;
+ menuitem-gap-y = <1>;
+ };
};
buttons {
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index f351d5c..084cb88 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -100,6 +100,12 @@
menuitem-gap-y = <1>;
};
+ cedit-theme {
+ font-size = <30>;
+ menu-inset = <3>;
+ menuitem-gap-y = <1>;
+ };
+
/*
* This is used for the VBE OS-request tests. A FAT filesystem
* created in a partition with the VBE information appearing
@@ -144,12 +150,6 @@
cedit: cedit {
};
- cedit-theme {
- font-size = <30>;
- menu-inset = <3>;
- menuitem-gap-y = <1>;
- };
-
fuzzing-engine {
compatible = "sandbox,fuzzing-engine";
};
diff --git a/boot/cedit.c b/boot/cedit.c
index ee24658..73645f7 100644
--- a/boot/cedit.c
+++ b/boot/cedit.c
@@ -6,15 +6,50 @@
* Written by Simon Glass <sjg@chromium.org>
*/
+#define LOG_CATEGORY LOGC_EXPO
+
#include <common.h>
+#include <abuf.h>
+#include <cedit.h>
#include <cli.h>
#include <dm.h>
+#include <env.h>
#include <expo.h>
+#include <malloc.h>
#include <menu.h>
+#include <rtc.h>
#include <video.h>
#include <linux/delay.h>
#include "scene_internal.h"
+enum {
+ CMOS_MAX_BITS = 2048,
+ CMOS_MAX_BYTES = CMOS_MAX_BITS / 8,
+};
+
+#define CMOS_BYTE(bit) ((bit) / 8)
+#define CMOS_BIT(bit) ((bit) % 8)
+
+/**
+ * struct cedit_iter_priv - private data for cedit operations
+ *
+ * @buf: Buffer to use when writing settings to the devicetree
+ * @node: Node to read from when reading settings from devicetree
+ * @verbose: true to show writing to environment variables
+ * @mask: Mask bits for the CMOS RAM. If a bit is set the byte containing it
+ * will be written
+ * @value: Value bits for CMOS RAM. This is the actual value written
+ * @dev: RTC device to write to
+ */
+struct cedit_iter_priv {
+ struct abuf *buf;
+ ofnode node;
+ bool verbose;
+ u8 *mask;
+ u8 *value;
+ struct udevice *dev;
+};
+
int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
{
struct scene_obj_txt *txt;
@@ -46,18 +81,15 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
return 0;
}
-int cedit_run(struct expo *exp)
+int cedit_prepare(struct expo *exp, struct video_priv **vid_privp,
+ struct scene **scnp)
{
- struct cli_ch_state s_cch, *cch = &s_cch;
struct video_priv *vid_priv;
- uint scene_id;
struct udevice *dev;
struct scene *scn;
- bool done;
+ uint scene_id;
int ret;
- cli_ch_init(cch);
-
/* For now we only support a video console */
ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
if (ret)
@@ -92,6 +124,27 @@ int cedit_run(struct expo *exp)
if (ret)
return log_msg_ret("dim", ret);
+ *vid_privp = vid_priv;
+ *scnp = scn;
+
+ return scene_id;
+}
+
+int cedit_run(struct expo *exp)
+{
+ struct cli_ch_state s_cch, *cch = &s_cch;
+ struct video_priv *vid_priv;
+ uint scene_id;
+ struct scene *scn;
+ bool done;
+ int ret;
+
+ cli_ch_init(cch);
+ ret = cedit_prepare(exp, &vid_priv, &scn);
+ if (ret < 0)
+ return log_msg_ret("prep", ret);
+ scene_id = ret;
+
done = false;
do {
struct expo_action act;
@@ -161,3 +214,507 @@ int cedit_run(struct expo *exp)
return 0;
}
+
+static int check_space(int ret, struct abuf *buf)
+{
+ if (ret == -FDT_ERR_NOSPACE) {
+ if (!abuf_realloc_inc(buf, CEDIT_SIZE_INC))
+ return log_msg_ret("spc", -ENOMEM);
+ ret = fdt_resize(abuf_data(buf), abuf_data(buf),
+ abuf_size(buf));
+ if (ret)
+ return log_msg_ret("res", -EFAULT);
+ }
+
+ return 0;
+}
+
+static int get_cur_menuitem_text(const struct scene_obj_menu *menu,
+ const char **strp)
+{
+ struct scene *scn = menu->obj.scene;
+ const struct scene_menitem *mi;
+ const struct scene_obj_txt *txt;
+ const char *str;
+
+ mi = scene_menuitem_find(menu, menu->cur_item_id);
+ if (!mi)
+ return log_msg_ret("mi", -ENOENT);
+
+ txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT);
+ if (!txt)
+ return log_msg_ret("txt", -ENOENT);
+
+ str = expo_get_str(scn->expo, txt->str_id);
+ if (!str)
+ return log_msg_ret("str", -ENOENT);
+ *strp = str;
+
+ return 0;
+}
+
+static int h_write_settings(struct scene_obj *obj, void *vpriv)
+{
+ struct cedit_iter_priv *priv = vpriv;
+ struct abuf *buf = priv->buf;
+
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_TEXT:
+ break;
+ case SCENEOBJT_MENU: {
+ const struct scene_obj_menu *menu;
+ const char *str;
+ char name[80];
+ int ret, i;
+
+ menu = (struct scene_obj_menu *)obj;
+ ret = -EAGAIN;
+ for (i = 0; ret && i < 2; i++) {
+ ret = fdt_property_u32(abuf_data(buf), obj->name,
+ menu->cur_item_id);
+ if (!i) {
+ ret = check_space(ret, buf);
+ if (ret)
+ return log_msg_ret("res", -ENOMEM);
+ }
+ }
+ /* this should not happen */
+ if (ret)
+ return log_msg_ret("wrt", -EFAULT);
+
+ ret = get_cur_menuitem_text(menu, &str);
+ if (ret)
+ return log_msg_ret("mis", ret);
+
+ snprintf(name, sizeof(name), "%s-str", obj->name);
+ ret = -EAGAIN;
+ for (i = 0; ret && i < 2; i++) {
+ ret = fdt_property_string(abuf_data(buf), name, str);
+ if (!i) {
+ ret = check_space(ret, buf);
+ if (ret)
+ return log_msg_ret("rs2", -ENOMEM);
+ }
+ }
+
+ /* this should not happen */
+ if (ret)
+ return log_msg_ret("wr2", -EFAULT);
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int cedit_write_settings(struct expo *exp, struct abuf *buf)
+{
+ struct cedit_iter_priv priv;
+ void *fdt;
+ int ret;
+
+ abuf_init(buf);
+ if (!abuf_realloc(buf, CEDIT_SIZE_INC))
+ return log_msg_ret("buf", -ENOMEM);
+
+ fdt = abuf_data(buf);
+ ret = fdt_create(fdt, abuf_size(buf));
+ if (!ret)
+ ret = fdt_finish_reservemap(fdt);
+ if (!ret)
+ ret = fdt_begin_node(fdt, "");
+ if (!ret)
+ ret = fdt_begin_node(fdt, CEDIT_NODE_NAME);
+ if (ret) {
+ log_debug("Failed to start FDT (err=%d)\n", ret);
+ return log_msg_ret("sta", -EINVAL);
+ }
+
+ /* write out the items */
+ priv.buf = buf;
+ ret = expo_iter_scene_objs(exp, h_write_settings, &priv);
+ if (ret) {
+ log_debug("Failed to write settings (err=%d)\n", ret);
+ return log_msg_ret("set", ret);
+ }
+
+ ret = fdt_end_node(fdt);
+ if (!ret)
+ ret = fdt_end_node(fdt);
+ if (!ret)
+ ret = fdt_finish(fdt);
+ if (ret) {
+ log_debug("Failed to finish FDT (err=%d)\n", ret);
+ return log_msg_ret("fin", -EINVAL);
+ }
+
+ return 0;
+}
+
+static int h_read_settings(struct scene_obj *obj, void *vpriv)
+{
+ struct cedit_iter_priv *priv = vpriv;
+ ofnode node = priv->node;
+
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_TEXT:
+ break;
+ case SCENEOBJT_MENU: {
+ struct scene_obj_menu *menu;
+ uint val;
+
+ if (ofnode_read_u32(node, obj->name, &val))
+ return log_msg_ret("rd", -ENOENT);
+ menu = (struct scene_obj_menu *)obj;
+ menu->cur_item_id = val;
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int cedit_read_settings(struct expo *exp, oftree tree)
+{
+ struct cedit_iter_priv priv;
+ ofnode root, node;
+ int ret;
+
+ root = oftree_root(tree);
+ if (!ofnode_valid(root))
+ return log_msg_ret("roo", -ENOENT);
+ node = ofnode_find_subnode(root, CEDIT_NODE_NAME);
+ if (!ofnode_valid(node))
+ return log_msg_ret("pat", -ENOENT);
+
+ /* read in the items */
+ priv.node = node;
+ ret = expo_iter_scene_objs(exp, h_read_settings, &priv);
+ if (ret) {
+ log_debug("Failed to read settings (err=%d)\n", ret);
+ return log_msg_ret("set", ret);
+ }
+
+ return 0;
+}
+
+static int h_write_settings_env(struct scene_obj *obj, void *vpriv)
+{
+ const struct scene_obj_menu *menu;
+ struct cedit_iter_priv *priv = vpriv;
+ char name[80], var[60];
+ const char *str;
+ int val, ret;
+
+ if (obj->type != SCENEOBJT_MENU)
+ return 0;
+
+ menu = (struct scene_obj_menu *)obj;
+ val = menu->cur_item_id;
+ snprintf(var, sizeof(var), "c.%s", obj->name);
+
+ if (priv->verbose)
+ printf("%s=%d\n", var, val);
+
+ ret = env_set_ulong(var, val);
+ if (ret)
+ return log_msg_ret("set", ret);
+
+ ret = get_cur_menuitem_text(menu, &str);
+ if (ret)
+ return log_msg_ret("mis", ret);
+
+ snprintf(name, sizeof(name), "c.%s-str", obj->name);
+ if (priv->verbose)
+ printf("%s=%s\n", name, str);
+
+ ret = env_set(name, str);
+ if (ret)
+ return log_msg_ret("st2", ret);
+
+ return 0;
+}
+
+int cedit_write_settings_env(struct expo *exp, bool verbose)
+{
+ struct cedit_iter_priv priv;
+ int ret;
+
+ /* write out the items */
+ priv.verbose = verbose;
+ ret = expo_iter_scene_objs(exp, h_write_settings_env, &priv);
+ if (ret) {
+ log_debug("Failed to write settings to env (err=%d)\n", ret);
+ return log_msg_ret("set", ret);
+ }
+
+ return 0;
+}
+
+static int h_read_settings_env(struct scene_obj *obj, void *vpriv)
+{
+ struct cedit_iter_priv *priv = vpriv;
+ struct scene_obj_menu *menu;
+ char var[60];
+ int val;
+
+ if (obj->type != SCENEOBJT_MENU)
+ return 0;
+
+ menu = (struct scene_obj_menu *)obj;
+ val = menu->cur_item_id;
+ snprintf(var, sizeof(var), "c.%s", obj->name);
+
+ val = env_get_ulong(var, 10, 0);
+ if (priv->verbose)
+ printf("%s=%d\n", var, val);
+ if (!val)
+ return log_msg_ret("get", -ENOENT);
+
+ /*
+ * note that no validation is done here, to make sure the ID is valid
+ * and actually points to a menu item
+ */
+ menu->cur_item_id = val;
+
+ return 0;
+}
+
+int cedit_read_settings_env(struct expo *exp, bool verbose)
+{
+ struct cedit_iter_priv priv;
+ int ret;
+
+ /* write out the items */
+ priv.verbose = verbose;
+ ret = expo_iter_scene_objs(exp, h_read_settings_env, &priv);
+ if (ret) {
+ log_debug("Failed to read settings from env (err=%d)\n", ret);
+ return log_msg_ret("set", ret);
+ }
+
+ return 0;
+}
+
+/**
+ * get_cur_menuitem_seq() - Get the sequence number of a menu's current item
+ *
+ * Enumerates the items of a menu (0, 1, 2) and returns the sequence number of
+ * the currently selected item. If the first item is selected, this returns 0;
+ * if the second, 1; etc.
+ *
+ * @menu: Menu to check
+ * Return: Sequence number on success, else -ve error value
+ */
+static int get_cur_menuitem_seq(const struct scene_obj_menu *menu)
+{
+ const struct scene_menitem *mi;
+ int seq, found;
+
+ seq = 0;
+ found = -1;
+ list_for_each_entry(mi, &menu->item_head, sibling) {
+ if (mi->id == menu->cur_item_id) {
+ found = seq;
+ break;
+ }
+ seq++;
+ }
+
+ if (found == -1)
+ return log_msg_ret("nf", -ENOENT);
+
+ return found;
+}
+
+static int h_write_settings_cmos(struct scene_obj *obj, void *vpriv)
+{
+ const struct scene_obj_menu *menu;
+ struct cedit_iter_priv *priv = vpriv;
+ int val, ret;
+ uint i, seq;
+
+ if (obj->type != SCENEOBJT_MENU)
+ return 0;
+
+ menu = (struct scene_obj_menu *)obj;
+ val = menu->cur_item_id;
+
+ ret = get_cur_menuitem_seq(menu);
+ if (ret < 0)
+ return log_msg_ret("cur", ret);
+ seq = ret;
+ log_debug("%s: seq=%d\n", menu->obj.name, seq);
+
+ /* figure out where to place this item */
+ if (!obj->bit_length)
+ return log_msg_ret("len", -EINVAL);
+ if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
+ return log_msg_ret("bit", -E2BIG);
+
+ for (i = 0; i < obj->bit_length; i++, seq >>= 1) {
+ uint bitnum = obj->start_bit + i;
+
+ priv->mask[CMOS_BYTE(bitnum)] |= 1 << CMOS_BIT(bitnum);
+ if (seq & 1)
+ priv->value[CMOS_BYTE(bitnum)] |= BIT(CMOS_BIT(bitnum));
+ log_debug("bit %x %x %x\n", bitnum,
+ priv->mask[CMOS_BYTE(bitnum)],
+ priv->value[CMOS_BYTE(bitnum)]);
+ }
+
+ return 0;
+}
+
+int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev,
+ bool verbose)
+{
+ struct cedit_iter_priv priv;
+ int ret, i, count, first, last;
+
+ /* write out the items */
+ priv.mask = calloc(1, CMOS_MAX_BYTES);
+ if (!priv.mask)
+ return log_msg_ret("mas", -ENOMEM);
+ priv.value = calloc(1, CMOS_MAX_BYTES);
+ if (!priv.value) {
+ free(priv.mask);
+ return log_msg_ret("val", -ENOMEM);
+ }
+
+ ret = expo_iter_scene_objs(exp, h_write_settings_cmos, &priv);
+ if (ret) {
+ log_debug("Failed to write CMOS (err=%d)\n", ret);
+ ret = log_msg_ret("set", ret);
+ goto done;
+ }
+
+ /* write the data to the RTC */
+ first = CMOS_MAX_BYTES;
+ last = -1;
+ for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
+ if (priv.mask[i]) {
+ log_debug("Write byte %x: %x\n", i, priv.value[i]);
+ ret = rtc_write8(dev, i, priv.value[i]);
+ if (ret) {
+ ret = log_msg_ret("wri", ret);
+ goto done;
+ }
+ count++;
+ first = min(first, i);
+ last = max(last, i);
+ }
+ }
+ if (verbose) {
+ printf("Write %d bytes from offset %x to %x\n", count, first,
+ last);
+ }
+
+done:
+ free(priv.mask);
+ free(priv.value);
+ return ret;
+}
+
+static int h_read_settings_cmos(struct scene_obj *obj, void *vpriv)
+{
+ struct cedit_iter_priv *priv = vpriv;
+ const struct scene_menitem *mi;
+ struct scene_obj_menu *menu;
+ int val, ret;
+ uint i;
+
+ if (obj->type != SCENEOBJT_MENU)
+ return 0;
+
+ menu = (struct scene_obj_menu *)obj;
+
+ /* figure out where to place this item */
+ if (!obj->bit_length)
+ return log_msg_ret("len", -EINVAL);
+ if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
+ return log_msg_ret("bit", -E2BIG);
+
+ val = 0;
+ for (i = 0; i < obj->bit_length; i++) {
+ uint bitnum = obj->start_bit + i;
+ uint offset = CMOS_BYTE(bitnum);
+
+ /* read the byte if not already read */
+ if (!priv->mask[offset]) {
+ ret = rtc_read8(priv->dev, offset);
+ if (ret < 0)
+ return log_msg_ret("rea", ret);
+ priv->value[offset] = ret;
+
+ /* mark it as read */
+ priv->mask[offset] = 0xff;
+ }
+
+ if (priv->value[offset] & BIT(CMOS_BIT(bitnum)))
+ val |= BIT(i);
+ log_debug("bit %x %x\n", bitnum, val);
+ }
+
+ /* update the current item */
+ mi = scene_menuitem_find_seq(menu, val);
+ if (!mi)
+ return log_msg_ret("seq", -ENOENT);
+
+ menu->cur_item_id = mi->id;
+ log_debug("Update menu %d cur_item_id %d\n", menu->obj.id, mi->id);
+
+ return 0;
+}
+
+int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev,
+ bool verbose)
+{
+ struct cedit_iter_priv priv;
+ int ret, i, count, first, last;
+
+ /* read in the items */
+ priv.mask = calloc(1, CMOS_MAX_BYTES);
+ if (!priv.mask)
+ return log_msg_ret("mas", -ENOMEM);
+ priv.value = calloc(1, CMOS_MAX_BYTES);
+ if (!priv.value) {
+ free(priv.mask);
+ return log_msg_ret("val", -ENOMEM);
+ }
+ priv.dev = dev;
+
+ ret = expo_iter_scene_objs(exp, h_read_settings_cmos, &priv);
+ if (ret) {
+ log_debug("Failed to read CMOS (err=%d)\n", ret);
+ ret = log_msg_ret("set", ret);
+ goto done;
+ }
+
+ /* read the data to the RTC */
+ first = CMOS_MAX_BYTES;
+ last = -1;
+ for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
+ if (priv.mask[i]) {
+ log_debug("Read byte %x: %x\n", i, priv.value[i]);
+ count++;
+ first = min(first, i);
+ last = max(last, i);
+ }
+ }
+ if (verbose) {
+ printf("Read %d bytes from offset %x to %x\n", count, first,
+ last);
+ }
+
+done:
+ free(priv.mask);
+ free(priv.value);
+ return ret;
+}
diff --git a/boot/expo.c b/boot/expo.c
index db837f7..139d684 100644
--- a/boot/expo.c
+++ b/boot/expo.c
@@ -266,3 +266,18 @@ int expo_apply_theme(struct expo *exp, ofnode node)
return 0;
}
+
+int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter,
+ void *priv)
+{
+ struct scene *scn;
+ int ret;
+
+ list_for_each_entry(scn, &exp->scene_head, sibling) {
+ ret = scene_iter_objs(scn, iter, priv);
+ if (ret)
+ return log_msg_ret("wr", ret);
+ }
+
+ return 0;
+}
diff --git a/boot/expo_build.c b/boot/expo_build.c
index 22f62eb..bb33cc2 100644
--- a/boot/expo_build.c
+++ b/boot/expo_build.c
@@ -214,22 +214,21 @@ static void list_strings(struct build_info *info)
* @info: Build information
* @node: Node containing the menu description
* @scn: Scene to add the menu to
+ * @id: ID for the menu
+ * @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, ofnode node, struct scene *scn)
+static int menu_build(struct build_info *info, ofnode node, struct scene *scn,
+ uint id, struct scene_obj **objp)
{
struct scene_obj_menu *menu;
uint title_id, menu_id;
const u32 *item_ids;
int ret, size, i;
const char *name;
- u32 id;
name = ofnode_get_name(node);
- ret = ofnode_read_u32(node, "id", &id);
- if (ret)
- return log_msg_ret("id", -EINVAL);
ret = scene_menu(scn, name, id, &menu);
if (ret < 0)
@@ -275,12 +274,13 @@ static int menu_build(struct build_info *info, ofnode node, struct scene *scn)
if (ret < 0)
return log_msg_ret("mi", ret);
}
+ *objp = &menu->obj;
return 0;
}
/**
- * menu_build() - Build an expo object and add it to a scene
+ * obj_build() - Build an expo object and add it to a scene
*
* See doc/developer/expo.rst for a description of the format
*
@@ -292,8 +292,9 @@ static int menu_build(struct build_info *info, ofnode node, struct scene *scn)
*/
static int obj_build(struct build_info *info, ofnode node, struct scene *scn)
{
+ struct scene_obj *obj;
const char *type;
- u32 id;
+ u32 id, val;
int ret;
log_debug("- object %s\n", ofnode_get_name(node));
@@ -306,12 +307,17 @@ static int obj_build(struct build_info *info, ofnode node, struct scene *scn)
return log_msg_ret("typ", -EINVAL);
if (!strcmp("menu", type))
- ret = menu_build(info, node, scn);
+ ret = menu_build(info, node, scn, id, &obj);
else
ret = -EINVAL;
if (ret)
return log_msg_ret("bld", ret);
+ if (!ofnode_read_u32(node, "start-bit", &val))
+ obj->start_bit = val;
+ if (!ofnode_read_u32(node, "bit-length", &val))
+ obj->bit_length = val;
+
return 0;
}
diff --git a/boot/scene.c b/boot/scene.c
index e523333..6c52948 100644
--- a/boot/scene.c
+++ b/boot/scene.c
@@ -79,7 +79,7 @@ int scene_obj_count(struct scene *scn)
return count;
}
-void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type)
+void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
{
struct scene_obj *obj;
@@ -681,3 +681,19 @@ int scene_set_open(struct scene *scn, uint id, bool open)
return 0;
}
+
+int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
+ void *priv)
+{
+ struct scene_obj *obj;
+
+ list_for_each_entry(obj, &scn->obj_head, sibling) {
+ int ret;
+
+ ret = iter(obj, priv);
+ if (ret)
+ return log_msg_ret("itr", ret);
+ }
+
+ return 0;
+}
diff --git a/boot/scene_internal.h b/boot/scene_internal.h
index fb1ea55..695a907 100644
--- a/boot/scene_internal.h
+++ b/boot/scene_internal.h
@@ -9,6 +9,8 @@
#ifndef __SCENE_INTERNAL_H
#define __SCENE_INTERNAL_H
+typedef int (*expo_scene_obj_iterator)(struct scene_obj *obj, void *priv);
+
/**
* expo_lookup_scene_id() - Look up a scene ID
*
@@ -38,7 +40,7 @@ uint resolve_id(struct expo *exp, uint id);
* @type: Type of the object, or SCENEOBJT_NONE to match any type
* Returns: Object found, or NULL if not found
*/
-void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type);
+void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type);
/**
* scene_obj_find_by_name() - Find an object in a scene by name
@@ -198,4 +200,50 @@ int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu);
*/
int scene_menu_calc_dims(struct scene_obj_menu *menu);
+/**
+ * scene_iter_objs() - Iterate through all scene objects
+ *
+ * @scn: Scene to process
+ * @iter: Iterator to call on each object
+ * @priv: Private data to pass to the iterator, in addition to the object
+ * Return: 0 if OK, -ve on error
+ */
+int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
+ void *priv);
+
+/**
+ * expo_iter_scene_objects() - Iterate through all scene objects
+ *
+ * @exp: Expo to process
+ * @iter: Iterator to call on each object
+ * @priv: Private data to pass to the iterator, in addition to the object
+ * Return: 0 if OK, -ve on error
+ */
+int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter,
+ void *priv);
+
+/**
+ * scene_menuitem_find() - Find the menu item for an ID
+ *
+ * Looks up the menu to find the item with the given ID
+ *
+ * @menu: Menu to check
+ * @id: ID to look for
+ * Return: Menu item, or NULL if not found
+ */
+struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu,
+ int id);
+
+/**
+ * scene_menuitem_find_seq() - Find the menu item at a sequential position
+ *
+ * This numbers the items from 0 and returns the seq'th one
+ *
+ * @menu: Menu to check
+ * @seq: Sequence number to look for
+ * Return: menu item if found, else NULL
+ */
+struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu,
+ uint seq);
+
#endif /* __SCENE_INTERNAL_H */
diff --git a/boot/scene_menu.c b/boot/scene_menu.c
index 57ffb52..e0dcd0a 100644
--- a/boot/scene_menu.c
+++ b/boot/scene_menu.c
@@ -33,8 +33,8 @@ void scene_menu_destroy(struct scene_obj_menu *menu)
scene_menuitem_destroy(item);
}
-static struct scene_menitem *scene_menuitem_find(struct scene_obj_menu *menu,
- int id)
+struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu,
+ int id)
{
struct scene_menitem *item;
@@ -46,6 +46,22 @@ static struct scene_menitem *scene_menuitem_find(struct scene_obj_menu *menu,
return NULL;
}
+struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu,
+ uint seq)
+{
+ struct scene_menitem *item;
+ uint i;
+
+ i = 0;
+ list_for_each_entry(item, &menu->item_head, sibling) {
+ if (i == seq)
+ return item;
+ i++;
+ }
+
+ return NULL;
+}
+
/**
* update_pointers() - Update the pointer object and handle highlights
*
diff --git a/cmd/cedit.c b/cmd/cedit.c
index 0cae304..2ff284f 100644
--- a/cmd/cedit.c
+++ b/cmd/cedit.c
@@ -7,14 +7,29 @@
*/
#include <common.h>
+#include <abuf.h>
+#include <cedit.h>
#include <command.h>
+#include <dm.h>
#include <expo.h>
#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
#include <dm/ofnode.h>
#include <linux/sizes.h>
struct expo *cur_exp;
+static int check_cur_expo(void)
+{
+ if (!cur_exp) {
+ printf("No expo loaded\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
@@ -53,18 +68,192 @@ static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc,
return 0;
}
+static int do_cedit_write_fdt(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *fname;
+ struct abuf buf;
+ loff_t bytes;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+ fname = argv[3];
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ ret = cedit_write_settings(cur_exp, &buf);
+ if (ret) {
+ printf("Failed to write settings: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY))
+ return CMD_RET_FAILURE;
+
+ ret = fs_write(fname, map_to_sysmem(abuf_data(&buf)), 0,
+ abuf_size(&buf), &bytes);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ return 0;
+}
+
+static int do_cedit_read_fdt(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *fname;
+ void *buf;
+ oftree tree;
+ ulong size;
+ int ret;
+
+ if (argc < 4)
+ return CMD_RET_USAGE;
+ fname = argv[3];
+
+ ret = fs_load_alloc(argv[1], argv[2], argv[3], SZ_1M, 0, &buf, &size);
+ if (ret) {
+ printf("File not found\n");
+ return CMD_RET_FAILURE;
+ }
+
+ tree = oftree_from_fdt(buf);
+ if (!oftree_valid(tree)) {
+ free(buf);
+ printf("Cannot create oftree\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = cedit_read_settings(cur_exp, tree);
+ oftree_dispose(tree);
+ free(buf);
+ if (ret) {
+ printf("Failed to read settings: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_cedit_write_env(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool verbose;
+ int ret;
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ verbose = argc > 1 && !strcmp(argv[1], "-v");
+
+ ret = cedit_write_settings_env(cur_exp, verbose);
+ if (ret) {
+ printf("Failed to write settings to environment: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_cedit_read_env(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ bool verbose;
+ int ret;
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ verbose = argc > 1 && !strcmp(argv[1], "-v");
+
+ ret = cedit_read_settings_env(cur_exp, verbose);
+ if (ret) {
+ printf("Failed to read settings from environment: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_cedit_write_cmos(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ bool verbose = false;
+ int ret;
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ if (argc > 1 && !strcmp(argv[1], "-v")) {
+ verbose = true;
+ argc--;
+ argv++;
+ }
+
+ if (argc > 1)
+ ret = uclass_get_device_by_name(UCLASS_RTC, argv[1], &dev);
+ else
+ ret = uclass_first_device_err(UCLASS_RTC, &dev);
+ if (ret) {
+ printf("Failed to get RTC device: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ if (cedit_write_settings_cmos(cur_exp, dev, verbose)) {
+ printf("Failed to write settings to CMOS\n");
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_cedit_read_cmos(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ bool verbose = false;
+ int ret;
+
+ if (check_cur_expo())
+ return CMD_RET_FAILURE;
+
+ if (argc > 1 && !strcmp(argv[1], "-v")) {
+ verbose = true;
+ argc--;
+ argv++;
+ }
+
+ if (argc > 1)
+ ret = uclass_get_device_by_name(UCLASS_RTC, argv[1], &dev);
+ else
+ ret = uclass_first_device_err(UCLASS_RTC, &dev);
+ if (ret) {
+ printf("Failed to get RTC device: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = cedit_read_settings_cmos(cur_exp, dev, verbose);
+ if (ret) {
+ printf("Failed to read settings from CMOS: %dE\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
ofnode node;
int ret;
- if (!cur_exp) {
- printf("No expo loaded\n");
+ if (check_cur_expo())
return CMD_RET_FAILURE;
- }
- node = ofnode_path("/cedit-theme");
+ node = ofnode_path("/bootstd/cedit-theme");
if (ofnode_valid(node)) {
ret = expo_apply_theme(cur_exp, node);
if (ret)
@@ -84,10 +273,22 @@ static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc,
#ifdef CONFIG_SYS_LONGHELP
static char cedit_help_text[] =
"load <interface> <dev[:part]> <filename> - load config editor\n"
+ "cedit read_fdt <i/f> <dev[:part]> <filename> - read settings\n"
+ "cedit write_fdt <i/f> <dev[:part]> <filename> - write settings\n"
+ "cedit read_env [-v] - read settings from env vars\n"
+ "cedit write_env [-v] - write settings to env vars\n"
+ "cedit read_cmos [-v] [dev] - read settings from CMOS RAM\n"
+ "cedit write_cmos [-v] [dev] - write settings to CMOS RAM\n"
"cedit run - run config editor";
#endif /* CONFIG_SYS_LONGHELP */
U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text,
U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load),
+ 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),
+ U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env),
+ U_BOOT_SUBCMD_MKENT(read_cmos, 2, 1, do_cedit_read_cmos),
+ U_BOOT_SUBCMD_MKENT(write_cmos, 2, 1, do_cedit_write_cmos),
U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run),
);
diff --git a/doc/develop/cedit.rst b/doc/develop/cedit.rst
new file mode 100644
index 0000000..63dff9d
--- /dev/null
+++ b/doc/develop/cedit.rst
@@ -0,0 +1,169 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Configuration Editor
+====================
+
+Introduction
+------------
+
+U-Boot provides a configuration editor which allows settings to be changed in
+a GUI or text environment.
+
+
+This feature is still in development and has a number of limitations. For
+example, cedit only supports menu items (there is no numeric or text entry),
+provides no support for colour text and does not support scrolling. Still it is
+possible to use it for simple applications.
+
+
+Overview
+--------
+
+The configuration editor makes use of :doc:`expo` to build a description of the
+configuration screens and allow user to interact with it.
+
+To create a single-scene cedit for your application:
+
+#. Design the scene, i.e. the objects that need to be present and what their
+ possible values are
+
+#. Enter this in .dts format
+
+#. Create a header file containing the IDs
+
+#. Run the 'expo.py' tool to generate a .dtb file containing the layout, which
+ can be used by U-Boot
+
+#. Use the :doc:`../usage/cmd/cedit` to create the cedit, read the settings,
+ present the cedit to the user and save the settings afterwards.
+
+Each of these is described in a separate section. See :ref:`expo_example` for
+an example file.
+
+
+Design a scene
+--------------
+
+Using a piece of paper or a drawing tool, lay out the objects you want in your
+scene. Typically you will use the default layout engine, which simply puts items
+one after the other from top to bottom. So use a single column and show the
+prompt and value for each object.
+
+For menu items, show one of the values, but keep in mind what else you need.
+
+
+Create an expo-format file
+--------------------------
+
+The description is in the form of a devicetree file, as documented at
+:ref:`expo_format`. Since everything in an expo has an ID number (an integer
+greater than 1) the description is written terms of these IDs. They each have
+an enum value. which is typically taken care of by the `expo.py` tool.
+
+The expo should have a `scenes` node with a named scene as a subnode. Within the
+scene, add properties for the scene, then a subnode for each object in the
+scene.
+
+All object nodes require an `id` value and a `type` property. Other properties
+depend on the type. For example, a menu has a `title` and an `item-label` list
+proving the text for the menu items, as well as an `item-id` list providing the
+ID of each menu item, so it can be selected.
+
+Text properties may have two variants. For example `title` specifies the title
+of a menu, but you can instead use `title-id` to specify the string ID to use as
+the title. String are defined in a separate area, common to the whole expo,
+which contains a subnode for each string. Within that subnode are the ID and the
+`value` (i.e. the text). For now only English is supported, but in future it may
+be possible to append a language identifier to provide other values (e.g.
+'value-es' for Spanish).
+
+
+Create an ID header-file
+------------------------
+
+Expo needs to know the integer value to use for every ID referenced in your
+expo-format file. For example, if you have defined a `cpu-speed` node with an
+id of `ID_CPU_SPEED`, then Expo needs to know the value of `ID_CPU_SPEED`.
+
+When you write C code to use the expo, you may need to know the IDs. For
+example, to find which value the user selected in `cpu-speed` menu, you must
+use the `ID_CPU_SPEED` ID. The ID is the only way to refer to anything in Expo.
+
+Since we need a shared set of IDs, it is best to have a header file containing
+them. Expo supports doing this with an enum, where every ID is listed in the
+enum::
+
+ enum {
+ ZERO,
+
+ ID_PROMPT,
+
+ ID_SCENE1,
+ ID_SCENE1_TITLE,
+ ...
+ };
+
+The C compiler can parse this directly. The `expo.py` tool parses it for expo.
+
+Create a header file containing every ID mentioned in your expo. Try to group
+related things together.
+
+
+Build the expo layout
+---------------------
+
+Use the `expo.py` tool to build a .dtb for your expo::
+
+ ./tools/expo.py -e expo_ids.h -l expo_layout.dts -o expo.dtb
+
+This uses the enum in the provided header file to get the ID numbers, grabs
+the `.dts` file, inserts the ID numbers and then uses the devicetree compiler to
+build a `.dtb` file.
+
+If you get an error::
+
+ Devicetree compiler error:
+ Error: <stdin>:9.19-20 syntax error
+ FATAL ERROR: Unable to parse input tree
+
+that means that something is wrong with your syntax, or perhaps you have an ID
+in the `.dts` file that is not mentioned in your enum. Check both files and try
+again.
+
+
+Use the command interface
+-------------------------
+
+See the :doc:`../usage/cmd/cedit` command for information on available commands.
+Typically you will use `cedit load` to load the `.dtb` file and `cedit run` to
+let the user interact with it.
+
+
+Multiple scenes
+---------------
+
+Expo supports multiple scenes but has no pre-determined way of moving between
+them. You could use selection of a menu item as a signal to change the scene,
+but this is not currently implemented in the cedit code (see `cedit_run()`).
+
+
+Themes
+------
+
+The configuration editor uses simple expo themes. The theme is read from
+`/bootstd/cedit-theme` in the devicetree.
+
+
+Reading and writing settings
+----------------------------
+
+Cedit provides several options for persistent settings:
+
+- Writing an FDT file to a filesystem
+- Writing to U-Boot's environment variables, which are then typically stored in
+ a persistent manner
+- Writing to CMOS RAM registers (common on x86 machines)
+
+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.
diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst
index 2ac4af2..f137619 100644
--- a/doc/develop/expo.rst
+++ b/doc/develop/expo.rst
@@ -317,6 +317,18 @@ id
Specifies the ID of the object. This is used when referring to the object.
+Where CMOS RAM is used for reading and writing settings, the following
+additional properties are required:
+
+start-bit
+ Specifies the first bit in the CMOS RAM to use for this setting. For a RAM
+ with 0x100 bytes, there are 0x800 bit locations. For example, register 0x80
+ holds bits 0x400 to 0x407.
+
+bit-length
+ Specifies the number of CMOS RAM bits to use for this setting. The bits
+ extend from `start-bit` to `start-bit + bit-length - 1`. Note that the bits
+ must be contiguous.
Menu nodes have the following additional properties:
@@ -358,6 +370,9 @@ The `expo_arrange()` function can be called to arrange the expo objects in a
suitable manner. For each scene it puts the title at the top, the prompt at the
bottom and the objects in order from top to bottom.
+
+.. _expo_example:
+
Expo format example
~~~~~~~~~~~~~~~~~~~
@@ -367,22 +382,27 @@ strings are provided inline in the nodes where they are used.
::
- #define ID_PROMPT 1
- #define ID_SCENE1 2
- #define ID_SCENE1_TITLE 3
+ /* this comment is parsed by the expo.py tool to insert the values below
+
+ enum {
+ ZERO,
+ ID_PROMPT,
+ ID_SCENE1,
+ ID_SCENE1_TITLE,
- #define ID_CPU_SPEED 4
- #define ID_CPU_SPEED_TITLE 5
- #define ID_CPU_SPEED_1 6
- #define ID_CPU_SPEED_2 7
- #define ID_CPU_SPEED_3 8
+ ID_CPU_SPEED,
+ ID_CPU_SPEED_TITLE,
+ ID_CPU_SPEED_1,
+ ID_CPU_SPEED_2,
+ ID_CPU_SPEED_3,
- #define ID_POWER_LOSS 9
- #define ID_AC_OFF 10
- #define ID_AC_ON 11
- #define ID_AC_MEMORY 12
+ ID_POWER_LOSS,
+ ID_AC_OFF,
+ ID_AC_ON,
+ ID_AC_MEMORY,
- #define ID_DYNAMIC_START 13
+ ID_DYNAMIC_START,
+ */
&cedit {
dynamic-start = <ID_DYNAMIC_START>;
@@ -465,7 +485,7 @@ Some ideas for future work:
- Support unicode
- Support curses for proper serial-terminal menus
- Add support for large menus which need to scroll
-- Add support for reading and writing configuration settings with cedit
+- Update expo.py tool to check for overlapping names and CMOS locations
.. Simon Glass <sjg@chromium.org>
.. 7-Oct-22
diff --git a/doc/develop/index.rst b/doc/develop/index.rst
index 5b230d0..0d12484 100644
--- a/doc/develop/index.rst
+++ b/doc/develop/index.rst
@@ -38,6 +38,7 @@ Implementation
driver-model/index
environment
expo
+ cedit
event
global_data
logging
diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst
index 8e1110c..f415b48 100644
--- a/doc/usage/cmd/cedit.rst
+++ b/doc/usage/cmd/cedit.rst
@@ -10,6 +10,11 @@ Synopis
cedit load <interface> <dev[:part]> <filename>
cedit run
+ cedit write_fdt <dev[:part]> <filename>
+ cedit read_fdt <dev[:part]> <filename>
+ cedit write_env [-v]
+ cedit read_env [-v]
+ cedit write_cmos [-v] [dev]
Description
-----------
@@ -22,6 +27,69 @@ It makes use of the expo subsystem.
The description is in the form of a devicetree file, as documented at
:ref:`expo_format`.
+See :doc:`../../develop/cedit` for information about the configuration editor.
+
+cedit load
+~~~~~~~~~~
+
+Loads a configuration-editor description from a file. It creates a new cedit
+structure ready for use. Initially no settings are read, so default values are
+used for each object.
+
+cedit run
+~~~~~~~~~
+
+Runs the default configuration-editor event loop. This is very simple, just
+accepting character input and moving through the objects under user control.
+The implementation is at `cedit_run()`.
+
+cedit write_fdt
+~~~~~~~~~~~~~~~
+
+Writes the current user settings to a devicetree file. For each menu item the
+selected ID and its text string are written.
+
+cedit read_fdt
+~~~~~~~~~~~~~~
+
+Reads the user settings from a devicetree file and updates the cedit with those
+settings.
+
+cedit read_env
+~~~~~~~~~~~~~~
+
+Reads the settings from the environment variables. For each menu item `<name>`,
+cedit looks for a variable called `c.<name>` with the ID of the selected menu
+item.
+
+The `-v` flag enables verbose mode, where each variable is printed after it is
+read.
+
+cedit write_env
+~~~~~~~~~~~~~~~
+
+Writes the settings to environment variables. For each menu item the selected
+ID and its text string are written, similar to:
+
+ setenv c.<name> <selected_id>
+ setenv c.<name>-str <selected_id's text string>
+
+The `-v` flag enables verbose mode, where each variable is printed before it is
+set.
+
+cedit write_cmos
+~~~~~~~~~~~~~~~~
+
+Writes the settings to locations in the CMOS RAM. The locations used are
+specified by the schema. See `expo_format_`.
+
+The `-v` flag enables verbose mode, which shows which CMOS locations were
+updated.
+
+Normally the first RTC device is used to hold the data. You can specify a
+different device by name using the `dev` parameter.
+
+
Example
-------
@@ -29,3 +97,52 @@ Example
=> cedit load hostfs - fred.dtb
=> cedit run
+ => cedit write_fdt hostfs - settings.dtb
+
+That results in::
+
+ / {
+ cedit-values {
+ cpu-speed = <0x00000006>;
+ cpu-speed-str = "2 GHz";
+ power-loss = <0x0000000a>;
+ power-loss-str = "Always Off";
+ };
+ }
+
+ => cedit read_fdt hostfs - settings.dtb
+
+This shows settings being stored in the environment::
+
+ => cedit write_env -v
+ c.cpu-speed=7
+ c.cpu-speed-str=2.5 GHz
+ c.power-loss=12
+ c.power-loss-str=Memory
+ => print
+ ...
+ c.cpu-speed=6
+ c.cpu-speed-str=2 GHz
+ c.power-loss=10
+ c.power-loss-str=Always Off
+ ...
+
+ => cedit read_env -v
+ c.cpu-speed=7
+ c.power-loss=12
+
+This shows writing to CMOS RAM. Notice that the bytes at 80 and 84 change::
+
+ => rtc read 80 8
+ 00000080: 00 00 00 00 00 2f 2a 08 ...../*.
+ => cedit write_cmos -v
+ Write 2 bytes from offset 80 to 84
+ => rtc read 80 8
+ 00000080: 01 00 00 00 08 2f 2a 08 ...../*.
+ => cedit read_cmos -v
+ Read 2 bytes from offset 80 to 84
+
+Here is an example with the device specified::
+
+ => cedit write_cmos rtc@43
+ =>
diff --git a/include/abuf.h b/include/abuf.h
index 9badda6..be98ec7 100644
--- a/include/abuf.h
+++ b/include/abuf.h
@@ -91,6 +91,15 @@ void abuf_map_sysmem(struct abuf *abuf, ulong addr, size_t size);
bool abuf_realloc(struct abuf *abuf, size_t new_size);
/**
+ * abuf_realloc_inc() - Increment abuf size by a given amount
+ *
+ * @abuf: abuf to adjust
+ * @inc: Size incrmement to use (the buffer size will be increased by this much)
+ * Return: true if OK, false if out of memory
+ */
+bool abuf_realloc_inc(struct abuf *abuf, size_t inc);
+
+/**
* abuf_uninit_move() - Return the allocated contents and uninit the abuf
*
* This returns the abuf data to the caller, allocating it if necessary, so that
diff --git a/include/cedit.h b/include/cedit.h
new file mode 100644
index 0000000..f43cafa
--- /dev/null
+++ b/include/cedit.h
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __CEDIT_H
+#define __CEDIT_H
+
+#include <dm/ofnode_decl.h>
+
+struct abuf;
+struct expo;
+struct scene;
+struct video_priv;
+
+enum {
+ /* size increment for writing FDT */
+ CEDIT_SIZE_INC = 1024,
+};
+
+/* Name of the cedit node in the devicetree */
+#define CEDIT_NODE_NAME "cedit-values"
+
+extern struct expo *cur_exp;
+
+/**
+ * cedit_arange() - Arrange objects in a configuration-editor scene
+ *
+ * @exp: Expo to update
+ * @vid_priv: Private info of the video device
+ * @scene_id: scene ID to arrange
+ * Returns: 0 if OK, -ve on error
+ */
+int cedit_arange(struct expo *exp, struct video_priv *vid_priv, uint scene_id);
+
+/**
+ * cedit_run() - Run a configuration editor
+ *
+ * This accepts input until the user quits with Escape
+ *
+ * @exp: Expo to use
+ * Returns: 0 if OK, -ve on error
+ */
+int cedit_run(struct expo *exp);
+
+/**
+ * cedit_prepare() - Prepare to run a cedit
+ *
+ * Set up the video device, select the first scene and highlight the first item.
+ * This ensures that all menus have a selected item.
+ *
+ * @exp: Expo to use
+ * @vid_privp: Set to private data for the video device
+ * @scnp: Set to the first scene
+ * Return: scene ID of first scene if OK, -ve on error
+ */
+int cedit_prepare(struct expo *exp, struct video_priv **vid_privp,
+ struct scene **scnp);
+
+/**
+ * cedit_write_settings() - Write settings in FDT format
+ *
+ * Sets up an FDT with the settings
+ *
+ * @exp: Expo to write settings from
+ * @buf: Returns abuf containing the settings FDT (inited by this function)
+ * Return: 0 if OK, -ve on error
+ */
+int cedit_write_settings(struct expo *exp, struct abuf *buf);
+
+/**
+ * cedit_read_settings() - Read settings in FDT format
+ *
+ * Read an FDT with the settings
+ *
+ * @exp: Expo to read settings into
+ * @tree: Tree to read from
+ * Return: 0 if OK, -ve on error
+ */
+int cedit_read_settings(struct expo *exp, oftree tree);
+
+/**
+ * cedit_write_settings_env() - Write settings to envrionment variables
+ *
+ * @exp: Expo to write settings from
+ * @verbose: true to print each var as it is set
+ * Return: 0 if OK, -ve on error
+ */
+int cedit_write_settings_env(struct expo *exp, bool verbose);
+
+/*
+ * cedit_read_settings_env() - Read settings from the environment
+ *
+ * @exp: Expo to read settings into
+ * @verbose: true to print each var before it is read
+ */
+int cedit_read_settings_env(struct expo *exp, bool verbose);
+
+/**
+ * cedit_write_settings_cmos() - Write settings to CMOS RAM
+ *
+ * Write settings to the defined places in CMOS RAM
+ *
+ * @exp: Expo to write settings from
+ * @dev: UCLASS_RTC device containing space for this information
+ * Returns 0 if OK, -ve on error
+ * @verbose: true to print a summary at the end
+ */
+int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev,
+ bool verbose);
+
+/**
+ * cedit_read_settings_cmos() - Read settings from CMOS RAM
+ *
+ * Read settings from the defined places in CMO RAM
+ *
+ * @exp: Expo to read settings into
+ * @dev: RTC device to read settings from
+ * @verbose: true to print a summary at the end
+ */
+int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev,
+ bool verbose);
+
+#endif /* __CEDIT_H */
diff --git a/include/expo.h b/include/expo.h
index 0b1d944..a2b3a71 100644
--- a/include/expo.h
+++ b/include/expo.h
@@ -4,14 +4,13 @@
* Written by Simon Glass <sjg@chromium.org>
*/
-#ifndef __SCENE_H
-#define __SCENE_H
+#ifndef __EXPO_H
+#define __EXPO_H
#include <dm/ofnode_decl.h>
#include <linux/list.h>
struct udevice;
-struct video_priv;
/**
* enum expoact_type - types of actions reported by the expo
@@ -188,6 +187,8 @@ enum scene_obj_flags_t {
* @type: Type of this object
* @dim: Dimensions for this object
* @flags: Flags for this object
+ * @bit_length: Number of bits used for this object in CMOS RAM
+ * @start_bit: Start bit to use for this object in CMOS RAM
* @sibling: Node to link this object to its siblings
*/
struct scene_obj {
@@ -196,7 +197,9 @@ struct scene_obj {
uint id;
enum scene_obj_t type;
struct scene_dim dim;
- int flags;
+ u8 flags;
+ u8 bit_length;
+ u16 start_bit;
struct list_head sibling;
};
@@ -676,24 +679,4 @@ int expo_apply_theme(struct expo *exp, ofnode node);
*/
int expo_build(ofnode root, struct expo **expp);
-/**
- * cedit_arange() - Arrange objects in a configuration-editor scene
- *
- * @exp: Expo to update
- * @vid_priv: Private info of the video device
- * @scene_id: scene ID to arrange
- * Returns: 0 if OK, -ve on error
- */
-int cedit_arange(struct expo *exp, struct video_priv *vid_priv, uint scene_id);
-
-/**
- * cedit_run() - Run a configuration editor
- *
- * This accepts input until the user quits with Escape
- *
- * @exp: Expo to use
- * Returns: 0 if OK, -ve on error
- */
-int cedit_run(struct expo *exp);
-
-#endif /*__SCENE_H */
+#endif /*__EXPO_H */
diff --git a/lib/abuf.c b/lib/abuf.c
index bd27046..ce2cff5 100644
--- a/lib/abuf.c
+++ b/lib/abuf.c
@@ -82,6 +82,11 @@ bool abuf_realloc(struct abuf *abuf, size_t new_size)
}
}
+bool abuf_realloc_inc(struct abuf *abuf, size_t inc)
+{
+ return abuf_realloc(abuf, abuf->size + inc);
+}
+
void *abuf_uninit_move(struct abuf *abuf, size_t *sizep)
{
void *ptr;
diff --git a/test/boot/Makefile b/test/boot/Makefile
index 22ed61c..5294758 100644
--- a/test/boot/Makefile
+++ b/test/boot/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o
obj-$(CONFIG_FIT) += image.o
obj-$(CONFIG_EXPO) += expo.o
+obj-$(CONFIG_CEDIT) += cedit.o
ifdef CONFIG_OF_LIVE
obj-$(CONFIG_BOOTMETH_VBE_SIMPLE) += vbe_simple.o
diff --git a/test/boot/cedit.c b/test/boot/cedit.c
new file mode 100644
index 0000000..ab2b8a1
--- /dev/null
+++ b/test/boot/cedit.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <cedit.h>
+#include <env.h>
+#include <expo.h>
+#include <mapmem.h>
+#include <dm/ofnode.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+#include <test/cedit-test.h>
+#include "../../boot/scene_internal.h"
+
+/* Check the cedit command */
+static int cedit_base(struct unit_test_state *uts)
+{
+ extern struct expo *cur_exp;
+ struct scene_obj_menu *menu;
+ struct scene_obj_txt *txt;
+ struct expo *exp;
+ struct scene *scn;
+
+ ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
+
+ console_record_reset_enable();
+
+ /*
+ * ^N Move down to second menu
+ * ^M Open menu
+ * ^N Move down to second item
+ * ^M Select item
+ * \e Quit
+ */
+ console_in_puts("\x0e\x0d\x0e\x0d\e");
+ ut_assertok(run_command("cedit run", 0));
+
+ exp = cur_exp;
+ scn = expo_lookup_scene_id(exp, exp->scene_id);
+ ut_assertnonnull(scn);
+
+ 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("AC Power", expo_get_str(exp, txt->str_id));
+
+ ut_asserteq(ID_AC_ON, menu->cur_item_id);
+
+ return 0;
+}
+BOOTSTD_TEST(cedit_base, 0);
+
+/* Check the cedit write_fdt and read_fdt commands */
+static int cedit_fdt(struct unit_test_state *uts)
+{
+ struct video_priv *vid_priv;
+ extern struct expo *cur_exp;
+ struct scene_obj_menu *menu;
+ ulong addr = 0x1000;
+ struct ofprop prop;
+ struct scene *scn;
+ oftree tree;
+ ofnode node;
+ void *fdt;
+ int i;
+
+ console_record_reset_enable();
+ ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
+
+ ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn));
+
+ /* get a menu to fiddle with */
+ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU);
+ ut_assertnonnull(menu);
+ menu->cur_item_id = ID_CPU_SPEED_2;
+
+ ut_assertok(run_command("cedit write_fdt hostfs - settings.dtb", 0));
+ ut_assertok(run_commandf("load hostfs - %lx settings.dtb", addr));
+ ut_assert_nextlinen("1024 bytes read");
+
+ fdt = map_sysmem(addr, 1024);
+ tree = oftree_from_fdt(fdt);
+ node = ofnode_find_subnode(oftree_root(tree), CEDIT_NODE_NAME);
+
+ ut_asserteq(ID_CPU_SPEED_2,
+ ofnode_read_u32_default(node, "cpu-speed", 0));
+ ut_asserteq_str("2.5 GHz", ofnode_read_string(node, "cpu-speed-str"));
+ ut_assert(ofnode_valid(node));
+
+ /* There should only be 4 properties */
+ for (i = 0, ofnode_first_property(node, &prop); ofprop_valid(&prop);
+ i++, ofnode_next_property(&prop))
+ ;
+ ut_asserteq(4, i);
+
+ ut_assert_console_end();
+
+ /* reset the expo */
+ menu->cur_item_id = ID_CPU_SPEED_1;
+
+ /* load in the settings and make sure they update */
+ ut_assertok(run_command("cedit read_fdt hostfs - settings.dtb", 0));
+ ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
+
+ ut_assertnonnull(menu);
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(cedit_fdt, 0);
+
+/* Check the cedit write_env and read_env commands */
+static int cedit_env(struct unit_test_state *uts)
+{
+ struct video_priv *vid_priv;
+ extern struct expo *cur_exp;
+ struct scene_obj_menu *menu;
+ struct scene *scn;
+
+ console_record_reset_enable();
+ ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
+
+ ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn));
+
+ /* get a menu to fiddle with */
+ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU);
+ ut_assertnonnull(menu);
+ menu->cur_item_id = ID_CPU_SPEED_2;
+
+ ut_assertok(run_command("cedit write_env -v", 0));
+ ut_assert_nextlinen("c.cpu-speed=7");
+ ut_assert_nextlinen("c.cpu-speed-str=2.5 GHz");
+ ut_assert_nextlinen("c.power-loss=10");
+ ut_assert_nextlinen("c.power-loss-str=Always Off");
+ ut_assert_console_end();
+
+ ut_asserteq(7, env_get_ulong("c.cpu-speed", 10, 0));
+ ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str"));
+
+ /* reset the expo */
+ menu->cur_item_id = ID_CPU_SPEED_1;
+
+ ut_assertok(run_command("cedit read_env -v", 0));
+ ut_assert_nextlinen("c.cpu-speed=7");
+ ut_assert_nextlinen("c.power-loss=10");
+ ut_assert_console_end();
+
+ ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
+
+ return 0;
+}
+BOOTSTD_TEST(cedit_env, 0);
+
+/* Check the cedit write_cmos and read_cmos commands */
+static int cedit_cmos(struct unit_test_state *uts)
+{
+ struct scene_obj_menu *menu, *menu2;
+ struct video_priv *vid_priv;
+ extern struct expo *cur_exp;
+ struct scene *scn;
+
+ console_record_reset_enable();
+ ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
+
+ ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn));
+
+ /* get the menus to fiddle with */
+ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU);
+ ut_assertnonnull(menu);
+ menu->cur_item_id = ID_CPU_SPEED_2;
+
+ menu2 = scene_obj_find(scn, ID_POWER_LOSS, SCENEOBJT_MENU);
+ ut_assertnonnull(menu2);
+ menu2->cur_item_id = ID_AC_MEMORY;
+
+ ut_assertok(run_command("cedit write_cmos -v", 0));
+ ut_assert_nextlinen("Write 2 bytes from offset 80 to 84");
+ ut_assert_console_end();
+
+ /* reset the expo */
+ menu->cur_item_id = ID_CPU_SPEED_1;
+ menu2->cur_item_id = ID_AC_OFF;
+
+ ut_assertok(run_command("cedit read_cmos -v", 0));
+ ut_assert_nextlinen("Read 2 bytes from offset 80 to 84");
+ ut_assert_console_end();
+
+ ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
+ ut_asserteq(ID_AC_MEMORY, menu2->cur_item_id);
+
+ return 0;
+}
+BOOTSTD_TEST(cedit_cmos, 0);
diff --git a/test/boot/expo.c b/test/boot/expo.c
index 3898f85..9002740 100644
--- a/test/boot/expo.c
+++ b/test/boot/expo.c
@@ -289,6 +289,33 @@ static int expo_object_attr(struct unit_test_state *uts)
}
BOOTSTD_TEST(expo_object_attr, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+/**
+ * struct test_iter_priv - private data for expo-iterator test
+ *
+ * @count: number of scene objects
+ * @menu_count: number of menus
+ * @fail_at: item ID at which to return an error
+ */
+struct test_iter_priv {
+ int count;
+ int menu_count;
+ int fail_at;
+};
+
+int h_test_iter(struct scene_obj *obj, void *vpriv)
+{
+ struct test_iter_priv *priv = vpriv;
+
+ if (priv->fail_at == obj->id)
+ return -EINVAL;
+
+ priv->count++;
+ if (obj->type == SCENEOBJT_MENU)
+ priv->menu_count++;
+
+ return 0;
+}
+
/* Check creating a scene with a menu */
static int expo_object_menu(struct unit_test_state *uts)
{
@@ -296,6 +323,7 @@ static int expo_object_menu(struct unit_test_state *uts)
struct scene_menitem *item;
int id, label_id, desc_id, key_id, pointer_id, preview_id;
struct scene_obj_txt *ptr, *name1, *desc1, *key1, *tit, *prev1;
+ struct test_iter_priv priv;
struct scene *scn;
struct expo *exp;
ulong start_mem;
@@ -382,6 +410,23 @@ static int expo_object_menu(struct unit_test_state *uts)
ut_asserteq(menu->obj.dim.y + 32, prev1->obj.dim.y);
ut_asserteq(true, prev1->obj.flags & SCENEOF_HIDE);
+ /* check iterating through scene items */
+ memset(&priv, '\0', sizeof(priv));
+ ut_assertok(expo_iter_scene_objs(exp, h_test_iter, &priv));
+ ut_asserteq(7, priv.count);
+ ut_asserteq(1, priv.menu_count);
+
+ /* check the iterator failing part way through iteration */
+ memset(&priv, '\0', sizeof(priv));
+ priv.fail_at = key_id;
+ ut_asserteq(-EINVAL, expo_iter_scene_objs(exp, h_test_iter, &priv));
+
+ /* 2 items (preview_id and the menuitem) are after key_id, 7 - 2 = 5 */
+ ut_asserteq(5, priv.count);
+
+ /* menu is first, so is still processed */
+ ut_asserteq(1, priv.menu_count);
+
expo_destroy(exp);
ut_assertok(ut_check_delta(start_mem));
@@ -669,46 +714,3 @@ static int expo_test_build(struct unit_test_state *uts)
return 0;
}
BOOTSTD_TEST(expo_test_build, UT_TESTF_DM);
-
-/* Check the cedit command */
-static int expo_cedit(struct unit_test_state *uts)
-{
- extern struct expo *cur_exp;
- struct scene_obj_menu *menu;
- struct scene_obj_txt *txt;
- struct expo *exp;
- struct scene *scn;
-
- if (!IS_ENABLED(CONFIG_CMD_CEDIT))
- return -EAGAIN;
-
- ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
-
- console_record_reset_enable();
-
- /*
- * ^N Move down to second menu
- * ^M Open menu
- * ^N Move down to second item
- * ^M Select item
- * \e Quit
- */
- console_in_puts("\x0e\x0d\x0e\x0d\e");
- ut_assertok(run_command("cedit run", 0));
-
- exp = cur_exp;
- scn = expo_lookup_scene_id(exp, exp->scene_id);
- ut_assertnonnull(scn);
-
- 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("AC Power", expo_get_str(exp, txt->str_id));
-
- ut_asserteq(ID_AC_ON, menu->cur_item_id);
-
- return 0;
-}
-BOOTSTD_TEST(expo_cedit, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
diff --git a/test/boot/files/expo_ids.h b/test/boot/files/expo_ids.h
new file mode 100644
index 0000000..027d44b
--- /dev/null
+++ b/test/boot/files/expo_ids.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Sample expo screen layout (ID numbers)
+ */
+
+enum {
+ ZERO,
+ ID_PROMPT,
+
+ ID_SCENE1,
+ ID_SCENE1_TITLE,
+
+ ID_CPU_SPEED,
+ ID_CPU_SPEED_TITLE,
+ ID_CPU_SPEED_1,
+ ID_CPU_SPEED_2,
+ ID_CPU_SPEED_3,
+
+ ID_POWER_LOSS,
+ ID_AC_OFF,
+ ID_AC_ON,
+ ID_AC_MEMORY,
+
+ ID_DYNAMIC_START,
+};
diff --git a/test/boot/files/expo_layout.dts b/test/boot/files/expo_layout.dts
index 55d5c91..cb2a674 100644
--- a/test/boot/files/expo_layout.dts
+++ b/test/boot/files/expo_layout.dts
@@ -5,28 +5,7 @@
/dts-v1/;
-/*
-enum {
- ZERO,
- ID_PROMPT,
-
- ID_SCENE1,
- ID_SCENE1_TITLE,
-
- ID_CPU_SPEED,
- ID_CPU_SPEED_TITLE,
- ID_CPU_SPEED_1,
- ID_CPU_SPEED_2,
- ID_CPU_SPEED_3,
-
- ID_POWER_LOSS,
- ID_AC_OFF,
- ID_AC_ON,
- ID_AC_MEMORY,
-
- ID_DYNAMIC_START,
-};
-*/
+/* see expo_ids.h for the IDs */
/ {
dynamic-start = <ID_DYNAMIC_START>;
@@ -59,6 +38,9 @@ enum {
/* IDs for the menu items */
item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2
ID_CPU_SPEED_3>;
+
+ start-bit = <0x400>;
+ bit-length = <2>;
};
power-loss {
@@ -70,6 +52,8 @@ enum {
"Memory";
item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>;
+ start-bit = <0x422>;
+ bit-length = <2>;
};
};
};
diff --git a/test/lib/abuf.c b/test/lib/abuf.c
index 42ee4c1..42803b2 100644
--- a/test/lib/abuf.c
+++ b/test/lib/abuf.c
@@ -155,6 +155,31 @@ static int lib_test_abuf_realloc_size(struct unit_test_state *uts)
}
LIB_TEST(lib_test_abuf_realloc_size, 0);
+/* Test abuf_realloc_inc() */
+static int lib_test_abuf_realloc_inc(struct unit_test_state *uts)
+{
+ struct abuf buf;
+ ulong start;
+
+ start = ut_check_free();
+
+ abuf_init(&buf);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ abuf_realloc_inc(&buf, 20);
+ ut_asserteq(20, buf.size);
+ ut_asserteq(true, buf.alloced);
+
+ abuf_uninit(&buf);
+
+ /* Check for memory leaks */
+ ut_assertok(ut_check_delta(start));
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_realloc_inc, 0);
+
/* Test handling of buffers that are too large */
static int lib_test_abuf_large(struct unit_test_state *uts)
{
diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
index aa1d477..6a59c30 100644
--- a/test/py/tests/test_ut.py
+++ b/test/py/tests/test_ut.py
@@ -285,10 +285,12 @@ label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
def setup_cedit_file(cons):
infname = os.path.join(cons.config.source_dir,
'test/boot/files/expo_layout.dts')
+ inhname = os.path.join(cons.config.source_dir,
+ 'test/boot/files/expo_ids.h')
expo_tool = os.path.join(cons.config.source_dir, 'tools/expo.py')
outfname = 'cedit.dtb'
u_boot_utils.run_and_log(
- cons, f'{expo_tool} -e {infname} -l {infname} -o {outfname}')
+ cons, f'{expo_tool} -e {inhname} -l {infname} -o {outfname}')
@pytest.mark.buildconfigspec('ut_dm')
diff --git a/tools/expo.py b/tools/expo.py
index c6eb87a..ea80c70 100755
--- a/tools/expo.py
+++ b/tools/expo.py
@@ -69,7 +69,10 @@ def calc_ids(fname):
def run_expo(args):
"""Run the expo program"""
- ids = calc_ids(args.enum_fname)
+ fname = args.enum_fname or args.layout
+ ids = calc_ids(fname)
+ if not ids:
+ print(f"Warning: No enum ID values found in file '{fname}'")
indata = tools.read_file(args.layout)
@@ -88,10 +91,10 @@ def run_expo(args):
with open('/tmp/asc', 'wb') as outf:
outf.write(data)
- proc = subprocess.run('dtc', input=data, capture_output=True, check=True)
+ proc = subprocess.run('dtc', input=data, capture_output=True)
edtb = proc.stdout
if proc.stderr:
- print(proc.stderr)
+ print(f"Devicetree compiler error:\n{proc.stderr.decode('utf-8')}")
return 1
tools.write_file(args.outfile, edtb)
return 0
@@ -109,11 +112,13 @@ def parse_args(argv):
args is a list of string arguments
"""
parser = argparse.ArgumentParser()
+ parser.add_argument('-D', '--debug', action='store_true',
+ help='Enable full debug traceback')
parser.add_argument('-e', '--enum-fname', type=str,
- help='C file containing enum declaration for expo items')
- parser.add_argument('-l', '--layout', type=str,
- help='Devicetree file source .dts for expo layout')
- parser.add_argument('-o', '--outfile', type=str,
+ help='.dts or C file containing enum declaration for expo items')
+ parser.add_argument('-l', '--layout', type=str, required=True,
+ help='Devicetree file source .dts for expo layout (and perhaps enums)')
+ parser.add_argument('-o', '--outfile', type=str, required=True,
help='Filename to write expo layout dtb')
return parser.parse_args(argv)
@@ -122,6 +127,9 @@ def start_expo():
"""Start the expo program"""
args = parse_args(sys.argv[1:])
+ if not args.debug:
+ sys.tracebacklimit = 0
+
ret_code = run_expo(args)
sys.exit(ret_code)