blob: 675e00292fc0f60605d9d11cebcc2edeef166d23 [file] [log] [blame]
Patrick Georgi4b62ebe2012-11-16 15:05:11 +01001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2012 secunet Security Networks AG
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010014 */
15
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010016#include <coreboot_tables.h>
Martin Rothf62065f2016-04-23 17:24:57 -060017#include <libpayload.h>
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010018
19#include <curses.h>
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010020#include <form.h>
Martin Rothf62065f2016-04-23 17:24:57 -060021#include <menu.h>
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010022
23#ifndef HOSTED
24#define HOSTED 0
25#endif
26
27static int min(int x, int y)
28{
29 if (x < y)
30 return x;
31 return y;
32}
33
34static int max(int x, int y)
35{
36 if (x > y)
37 return x;
38 return y;
39}
40
41void render_form(FORM *form)
42{
43 int y, x, line;
44 WINDOW *w = form_win(form);
45 WINDOW *inner_w = form_sub(form);
Martin Rothf62065f2016-04-23 17:24:57 -060046 int numlines = getmaxy(w) - 2;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010047 getyx(inner_w, y, x);
48 line = y - (y % numlines);
Martin Rothf62065f2016-04-23 17:24:57 -060049 WINDOW *der = derwin(w, getmaxy(w) - 2, getmaxx(w) - 2, 1, 1);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010050 wclear(der);
51 wrefresh(der);
52 delwin(der);
Martin Rothf62065f2016-04-23 17:24:57 -060053 copywin(inner_w, w, line, 0, 1, 1,
54 min(numlines, getmaxy(inner_w) - line), 68, 0);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010055 wmove(w, y + 1 - line, x + 1);
56 wrefresh(w);
57}
58
Antonello Dettori904dd302016-08-18 10:32:27 +020059/* determine number of options, and maximum option name length */
60static int count_cmos_options(struct cb_cmos_entries *option, int *numopts,
61 int *maxlength)
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010062{
Antonello Dettori904dd302016-08-18 10:32:27 +020063 int n_opts = 0;
64 int max_l = 0;
Martin Rothf62065f2016-04-23 17:24:57 -060065
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010066 while (option) {
Martin Rothf62065f2016-04-23 17:24:57 -060067 if ((option->config != 'r') &&
Martin Roth0cf7acc2016-04-26 12:33:44 -060068 (strcmp("check_sum", (char *)option->name) != 0)) {
Antonello Dettori904dd302016-08-18 10:32:27 +020069 max_l = max(max_l, strlen((char *)option->name));
70 n_opts++;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010071 }
Antonello Dettori904dd302016-08-18 10:32:27 +020072
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010073 option = next_cmos_entry(option);
74 }
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010075
Antonello Dettori904dd302016-08-18 10:32:27 +020076 if (n_opts == 0) {
77 printf("NO CMOS OPTIONS FOUND. EXITING!!!");
78 return -1;
79 }
80
81 *numopts = n_opts;
82 *maxlength = max_l;
83
84 return 0;
85
86}
87
88/* walk over options, fetch details */
89static void cmos_walk_options(struct cb_cmos_option_table *opttbl,
90 FIELD **fields, int numopts, int maxlength)
91{
92 struct cb_cmos_entries *option = first_cmos_entry(opttbl);
Paul Menzele7385d12017-01-20 23:29:21 +010093 int i;
Antonello Dettori904dd302016-08-18 10:32:27 +020094
Paul Menzele7385d12017-01-20 23:29:21 +010095 for (i = 0; i < numopts; i++) {
Martin Rothf62065f2016-04-23 17:24:57 -060096 while ((option->config == 'r') ||
Martin Roth0cf7acc2016-04-26 12:33:44 -060097 (strcmp("check_sum", (char *)option->name) == 0)) {
Patrick Georgi4b62ebe2012-11-16 15:05:11 +010098 option = next_cmos_entry(option);
99 }
Martin Rothf62065f2016-04-23 17:24:57 -0600100 fields[2 * i] =
Martin Roth0cf7acc2016-04-26 12:33:44 -0600101 new_field(1, strlen((char *)option->name), i * 2, 1, 0, 0);
102 set_field_buffer(fields[2 * i], 0, (char *)option->name);
Martin Rothf62065f2016-04-23 17:24:57 -0600103 field_opts_off(fields[2 * i], O_ACTIVE);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100104
Martin Rothf62065f2016-04-23 17:24:57 -0600105 fields[2 * i + 1] =
106 new_field(1, 40, i * 2, maxlength + 2, 0, 0);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100107 char *buf = NULL;
Martin Rothf62065f2016-04-23 17:24:57 -0600108 int fail =
Martin Roth0cf7acc2016-04-26 12:33:44 -0600109 get_option_as_string(use_nvram, opttbl, &buf, (char *)option->name);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100110 switch (option->config) {
111 case 'h': {
Martin Rothf62065f2016-04-23 17:24:57 -0600112 set_field_type(fields[2 * i + 1], TYPE_INTEGER, 0, 0,
113 (1 << option->length) - 1);
114 field_opts_on(fields[2 * i + 1], O_BLANK);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100115 break;
Martin Rothf62065f2016-04-23 17:24:57 -0600116 }
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100117 case 's': {
Martin Rothf62065f2016-04-23 17:24:57 -0600118 set_max_field(fields[2 * i + 1], option->length / 8);
119 field_opts_off(fields[2 * i + 1], O_STATIC);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100120 break;
Martin Rothf62065f2016-04-23 17:24:57 -0600121 }
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100122 case 'e': {
123 int numvals = 0;
Martin Rothf62065f2016-04-23 17:24:57 -0600124 struct cb_cmos_enums *cmos_enum =
125 first_cmos_enum_of_id(opttbl, option->config_id);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100126
127 /* if invalid data in CMOS, set buf to first enum */
128 if (fail && cmos_enum) {
Martin Roth0cf7acc2016-04-26 12:33:44 -0600129 buf = (char *)cmos_enum->text;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100130 }
131
132 while (cmos_enum) {
133 numvals++;
Martin Rothf62065f2016-04-23 17:24:57 -0600134 cmos_enum = next_cmos_enum_of_id(
135 cmos_enum, option->config_id);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100136 }
137
Martin Rothf62065f2016-04-23 17:24:57 -0600138 char **values = malloc(sizeof(char *) * (numvals + 1));
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100139 int cnt = 0;
140
Martin Rothf62065f2016-04-23 17:24:57 -0600141 cmos_enum =
142 first_cmos_enum_of_id(opttbl, option->config_id);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100143 while (cmos_enum) {
Martin Roth0cf7acc2016-04-26 12:33:44 -0600144 values[cnt] = (char *)cmos_enum->text;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100145 cnt++;
Martin Rothf62065f2016-04-23 17:24:57 -0600146 cmos_enum = next_cmos_enum_of_id(
147 cmos_enum, option->config_id);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100148 }
149 values[cnt] = NULL;
Martin Rothf62065f2016-04-23 17:24:57 -0600150 field_opts_off(fields[2 * i + 1], O_EDIT);
151 set_field_type(fields[2 * i + 1], TYPE_ENUM, values, 1,
152 1);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100153 free(values); // copied by set_field_type
154 break;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100155 }
Martin Rothf62065f2016-04-23 17:24:57 -0600156 default:
157 break;
158 }
159 if (buf)
160 set_field_buffer(fields[2 * i + 1], 0, buf);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100161#if HOSTED
Martin Rothf62065f2016-04-23 17:24:57 -0600162 // underline is non-trivial on VGA text
163 set_field_back(fields[2 * i + 1], A_UNDERLINE);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100164#endif
Martin Rothf62065f2016-04-23 17:24:57 -0600165 field_opts_off(fields[2 * i + 1],
166 O_BLANK | O_AUTOSKIP | O_NULLOK);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100167
168 option = next_cmos_entry(option);
169 }
Antonello Dettori904dd302016-08-18 10:32:27 +0200170
Martin Rothf62065f2016-04-23 17:24:57 -0600171 fields[2 * numopts] = NULL;
Antonello Dettori904dd302016-08-18 10:32:27 +0200172}
173
174int main(void)
175{
176 int ch, done;
Paul Menzele7385d12017-01-20 23:29:21 +0100177 int i;
Antonello Dettori904dd302016-08-18 10:32:27 +0200178
Paul Menzelafbc2c92017-03-13 17:56:17 +0100179 if (IS_ENABLED(CONFIG_LP_USB))
180 usb_initialize();
Nicola Cornafece39b2017-03-10 17:34:03 +0100181
Antonello Dettori904dd302016-08-18 10:32:27 +0200182 /* coreboot data structures */
183 lib_get_sysinfo();
184
185 struct cb_cmos_option_table *opttbl = get_system_option_table();
186
Antonello Dettori079feec2016-08-18 10:32:27 +0200187 if (opttbl == NULL) {
188 printf("Could not find coreboot option table.\n");
Antonello Dettori904dd302016-08-18 10:32:27 +0200189 halt();
190 }
191
192 /* prep CMOS layout into libcurses data structures */
193
194 struct cb_cmos_entries *option = first_cmos_entry(opttbl);
195 int numopts = 0;
196 int maxlength = 0;
197
198 count_cmos_options(option, &numopts, &maxlength);
199
200 FIELD **fields = malloc(sizeof(FIELD *) * (2 * numopts + 1));
201
202 cmos_walk_options(opttbl, fields, numopts, maxlength);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100203
Lubomir Rintel1ecc8af2015-02-24 20:33:35 +0100204 /* display initialization */
205 initscr();
206 keypad(stdscr, TRUE);
207 cbreak();
208 noecho();
209
210 if (start_color()) {
Martin Rothf62065f2016-04-23 17:24:57 -0600211 assume_default_colors(COLOR_BLUE, COLOR_CYAN);
Lubomir Rintel1ecc8af2015-02-24 20:33:35 +0100212 }
213 leaveok(stdscr, TRUE);
214 curs_set(1);
215
216 erase();
217 box(stdscr, 0, 0);
218 mvaddstr(0, 2, "coreboot configuration utility");
219 refresh();
220
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100221 FORM *form = new_form(fields);
Martin Rothf62065f2016-04-23 17:24:57 -0600222 int numlines = min(numopts * 2, 16);
223 WINDOW *w = newwin(numlines + 2, 70, 2, 1);
224 WINDOW *inner_w = newpad(numopts * 2, 68);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100225 box(w, 0, 0);
226 mvwaddstr(w, 0, 2, "Press F1 when done");
227 set_form_win(form, w);
228 set_form_sub(form, inner_w);
229 post_form(form);
230
231 done = 0;
Martin Rothf62065f2016-04-23 17:24:57 -0600232 while (!done) {
Lubomir Rintel68009e92015-02-01 15:24:43 +0100233 render_form(form);
Martin Rothf62065f2016-04-23 17:24:57 -0600234 ch = getch();
235 if (ch == ERR)
236 continue;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100237 switch (ch) {
238 case KEY_DOWN:
239 form_driver(form, REQ_NEXT_FIELD);
240 break;
241 case KEY_UP:
242 form_driver(form, REQ_PREV_FIELD);
243 break;
244 case KEY_LEFT:
245 if (field_type(current_field(form)) == TYPE_ENUM) {
246 form_driver(form, REQ_PREV_CHOICE);
247 } else {
248 form_driver(form, REQ_LEFT_CHAR);
249 }
250 break;
251 case KEY_RIGHT:
252 if (field_type(current_field(form)) == TYPE_ENUM) {
253 form_driver(form, REQ_NEXT_CHOICE);
254 } else {
255 form_driver(form, REQ_RIGHT_CHAR);
256 }
257 break;
258 case KEY_BACKSPACE:
259 case '\b':
260 form_driver(form, REQ_DEL_PREV);
261 break;
262 case KEY_DC:
263 form_driver(form, REQ_DEL_CHAR);
264 break;
265 case KEY_F(1):
Martin Rothf62065f2016-04-23 17:24:57 -0600266 done = 1;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100267 break;
268 default:
269 form_driver(form, ch);
270 break;
271 }
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100272 }
273
Lubomir Rintel1ecc8af2015-02-24 20:33:35 +0100274 endwin();
275
Paul Menzele7385d12017-01-20 23:29:21 +0100276 for (i = 0; i < numopts; i++) {
Martin Rothf62065f2016-04-23 17:24:57 -0600277 char *name = field_buffer(fields[2 * i], 0);
278 char *value = field_buffer(fields[2 * i + 1], 0);
Vladimir Serbinenkoc9babb22014-01-15 22:07:52 +0100279 char *ptr;
Martin Rothf62065f2016-04-23 17:24:57 -0600280 for (ptr = value + strlen(value) - 1;
281 ptr >= value && *ptr == ' '; ptr--)
282 ;
Vladimir Serbinenkoc9babb22014-01-15 22:07:52 +0100283 ptr[1] = '\0';
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100284 set_option_from_string(use_nvram, opttbl, value, name);
285 }
286
287 unpost_form(form);
288 free_form(form);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100289
Vladimir Serbinenkoc7282882014-01-15 22:09:25 +0100290 /* reboot */
Martin Rothf62065f2016-04-23 17:24:57 -0600291 outb(0x6, 0xcf9);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100292 halt();
293}