1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Building an expo from an FDT description
4  *
5  * Copyright 2022 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8 
9 #define LOG_CATEGORY	LOGC_EXPO
10 
11 #include <cedit.h>
12 #include <ctype.h>
13 #include <errno.h>
14 #include <expo.h>
15 #include <log.h>
16 #include <malloc.h>
17 #include <vsprintf.h>
18 #include <asm/cb_sysinfo.h>
19 
20 /**
21  * struct build_info - Information to use when building
22  */
23 struct build_info {
24 	const struct cb_cmos_option_table *tab;
25 	struct cedit_priv *priv;
26 };
27 
28 /**
29  * convert_to_title() - Convert text to 'title' format and allocate a string
30  *
31  * Converts "this_is_a_test" to "This is a test" so it looks better
32  *
33  * @text: Text to convert
34  * Return: Allocated string, or NULL if out of memory
35  */
convert_to_title(const char * text)36 static char *convert_to_title(const char *text)
37 {
38 	int len = strlen(text);
39 	char *buf, *s;
40 
41 	buf = malloc(len + 1);
42 	if (!buf)
43 		return NULL;
44 
45 	for (s = buf; *text; s++, text++) {
46 		if (s == buf)
47 			*s = toupper(*text);
48 		else if (*text == '_')
49 			*s = ' ';
50 		else
51 			*s = *text;
52 	}
53 	*s = '\0';
54 
55 	return buf;
56 }
57 
58 /**
59  * menu_build() - Build a menu and add it to a scene
60  *
61  * See doc/developer/expo.rst for a description of the format
62  *
63  * @info: Build information
64  * @entry: CMOS entry to build a menu for
65  * @scn: Scene to add the menu to
66  * @objp: Returns the object pointer
67  * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
68  * error, -ENOENT if there is a references to a non-existent string
69  */
menu_build(struct build_info * info,const struct cb_cmos_entries * entry,struct scene * scn,struct scene_obj ** objp)70 static int menu_build(struct build_info *info,
71 		      const struct cb_cmos_entries *entry, struct scene *scn,
72 		      struct scene_obj **objp)
73 {
74 	struct scene_obj_menu *menu;
75 	const void *ptr, *end;
76 	uint menu_id;
77 	char *title;
78 	int ret, i;
79 
80 	ret = scene_menu(scn, entry->name, 0, &menu);
81 	if (ret < 0)
82 		return log_msg_ret("men", ret);
83 	menu_id = ret;
84 
85 	title = convert_to_title(entry->name);
86 	if (!title)
87 		return log_msg_ret("con", -ENOMEM);
88 
89 	/* Set the title */
90 	ret = scene_txt_str(scn, "title", 0, 0, title, NULL);
91 	if (ret < 0)
92 		return log_msg_ret("tit", ret);
93 	menu->title_id = ret;
94 
95 	end = (void *)info->tab + info->tab->size;
96 	for (ptr = (void *)info->tab + info->tab->header_length, i = 0;
97 	     ptr < end; i++) {
98 		const struct cb_cmos_enums *enums = ptr;
99 		struct scene_menitem *item;
100 		uint label;
101 
102 		ptr += enums->size;
103 		if (enums->tag != CB_TAG_OPTION_ENUM ||
104 		    enums->config_id != entry->config_id)
105 			continue;
106 
107 		ret = scene_txt_str(scn, enums->text, 0, 0, enums->text, NULL);
108 		if (ret < 0)
109 			return log_msg_ret("tit", ret);
110 		label = ret;
111 
112 		ret = scene_menuitem(scn, menu_id, simple_xtoa(i), 0, 0, label,
113 				     0, 0, 0, &item);
114 		if (ret < 0)
115 			return log_msg_ret("mi", ret);
116 		item->value = enums->value;
117 	}
118 	*objp = &menu->obj;
119 
120 	return 0;
121 }
122 
123 /**
124  * scene_build() - Build a scene and all its objects
125  *
126  * See doc/developer/expo.rst for a description of the format
127  *
128  * @info: Build information
129  * @scn: Scene to add the object to
130  * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format
131  * error, -ENOENT if there is a references to a non-existent string
132  */
scene_build(struct build_info * info,struct expo * exp)133 static int scene_build(struct build_info *info, struct expo *exp)
134 {
135 	struct scene_obj_menu *menu;
136 	const void *ptr, *end;
137 	struct scene_obj *obj;
138 	struct scene *scn;
139 	uint label, menu_id;
140 	int ret;
141 
142 	ret = scene_new(exp, "cmos", 0, &scn);
143 	if (ret < 0)
144 		return log_msg_ret("scn", ret);
145 
146 	ret = scene_txt_str(scn, "title", 0, 0, "CMOS RAM settings", NULL);
147 	if (ret < 0)
148 		return log_msg_ret("add", ret);
149 	scn->title_id = ret;
150 
151 	ret = scene_txt_str(scn, "prompt", 0, 0,
152 			    "UP and DOWN to choose, ENTER to select", NULL);
153 	if (ret < 0)
154 		return log_msg_ret("add", ret);
155 
156 	end = (void *)info->tab + info->tab->size;
157 	for (ptr = (void *)info->tab + info->tab->header_length; ptr < end;) {
158 		const struct cb_cmos_entries *entry;
159 		const struct cb_record *rec = ptr;
160 
161 		entry = ptr;
162 		ptr += rec->size;
163 		if (rec->tag != CB_TAG_OPTION)
164 			continue;
165 		switch (entry->config) {
166 		case 'e':
167 			ret = menu_build(info, entry, scn, &obj);
168 			break;
169 		default:
170 			continue;
171 		}
172 		if (ret < 0)
173 			return log_msg_ret("add", ret);
174 
175 		obj->start_bit = entry->bit;
176 		obj->bit_length = entry->length;
177 	}
178 
179 	ret = scene_menu(scn, "save", EXPOID_SAVE, &menu);
180 	if (ret < 0)
181 		return log_msg_ret("men", ret);
182 	menu_id = ret;
183 
184 	ret = scene_txt_str(scn, "save", 0, 0, "Save and exit", NULL);
185 	if (ret < 0)
186 		return log_msg_ret("sav", ret);
187 	label = ret;
188 	ret = scene_menuitem(scn, menu_id, "save", 0, 0, label,
189 			     0, 0, 0, NULL);
190 	if (ret < 0)
191 		return log_msg_ret("mi", ret);
192 
193 	ret = scene_menu(scn, "nosave", EXPOID_DISCARD, &menu);
194 	if (ret < 0)
195 		return log_msg_ret("men", ret);
196 	menu_id = ret;
197 
198 	ret = scene_txt_str(scn, "nosave", 0, 0, "Exit without saving", NULL);
199 	if (ret < 0)
200 		return log_msg_ret("nos", ret);
201 	label = ret;
202 	ret = scene_menuitem(scn, menu_id, "exit", 0, 0, label,
203 			     0, 0, 0, NULL);
204 	if (ret < 0)
205 		return log_msg_ret("mi", ret);
206 
207 	return 0;
208 }
209 
build_it(struct build_info * info,struct expo ** expp)210 static int build_it(struct build_info *info, struct expo **expp)
211 {
212 	struct expo *exp;
213 	int ret;
214 
215 	ret = expo_new("coreboot", NULL, &exp);
216 	if (ret)
217 		return log_msg_ret("exp", ret);
218 	expo_set_dynamic_start(exp, EXPOID_BASE_ID);
219 
220 	ret = scene_build(info, exp);
221 	if (ret < 0)
222 		return log_msg_ret("scn", ret);
223 
224 	*expp = exp;
225 
226 	return 0;
227 }
228 
cb_expo_build(struct expo ** expp)229 int cb_expo_build(struct expo **expp)
230 {
231 	struct build_info info;
232 	struct expo *exp;
233 	int ret;
234 
235 	info.tab = lib_sysinfo.option_table;
236 	if (!info.tab)
237 		return log_msg_ret("tab", -ENOENT);
238 
239 	ret = build_it(&info, &exp);
240 	if (ret)
241 		return log_msg_ret("bui", ret);
242 	*expp = exp;
243 
244 	return 0;
245 }
246