blob: 442ad760e7962317643964dd330ab79b8cf8362a [file] [log] [blame]
Simon Glass34344202024-10-14 16:32:11 -06001// 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 */
23struct 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 */
36static 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 */
70static 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 */
133static 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
210static 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
229int 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}