blob: 9e12b9763b7e41065580977ba4152d100dca6e8d [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
Nicola Cornafece39b2017-03-10 17:34:03 +0100179#if IS_ENABLED(CONFIG_LP_USB)
180 usb_initialize();
181#endif
182
Antonello Dettori904dd302016-08-18 10:32:27 +0200183 /* coreboot data structures */
184 lib_get_sysinfo();
185
186 struct cb_cmos_option_table *opttbl = get_system_option_table();
187
Antonello Dettori079feec2016-08-18 10:32:27 +0200188 if (opttbl == NULL) {
189 printf("Could not find coreboot option table.\n");
Antonello Dettori904dd302016-08-18 10:32:27 +0200190 halt();
191 }
192
193 /* prep CMOS layout into libcurses data structures */
194
195 struct cb_cmos_entries *option = first_cmos_entry(opttbl);
196 int numopts = 0;
197 int maxlength = 0;
198
199 count_cmos_options(option, &numopts, &maxlength);
200
201 FIELD **fields = malloc(sizeof(FIELD *) * (2 * numopts + 1));
202
203 cmos_walk_options(opttbl, fields, numopts, maxlength);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100204
Lubomir Rintel1ecc8af2015-02-24 20:33:35 +0100205 /* display initialization */
206 initscr();
207 keypad(stdscr, TRUE);
208 cbreak();
209 noecho();
210
211 if (start_color()) {
Martin Rothf62065f2016-04-23 17:24:57 -0600212 assume_default_colors(COLOR_BLUE, COLOR_CYAN);
Lubomir Rintel1ecc8af2015-02-24 20:33:35 +0100213 }
214 leaveok(stdscr, TRUE);
215 curs_set(1);
216
217 erase();
218 box(stdscr, 0, 0);
219 mvaddstr(0, 2, "coreboot configuration utility");
220 refresh();
221
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100222 FORM *form = new_form(fields);
Martin Rothf62065f2016-04-23 17:24:57 -0600223 int numlines = min(numopts * 2, 16);
224 WINDOW *w = newwin(numlines + 2, 70, 2, 1);
225 WINDOW *inner_w = newpad(numopts * 2, 68);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100226 box(w, 0, 0);
227 mvwaddstr(w, 0, 2, "Press F1 when done");
228 set_form_win(form, w);
229 set_form_sub(form, inner_w);
230 post_form(form);
231
232 done = 0;
Martin Rothf62065f2016-04-23 17:24:57 -0600233 while (!done) {
Lubomir Rintel68009e92015-02-01 15:24:43 +0100234 render_form(form);
Martin Rothf62065f2016-04-23 17:24:57 -0600235 ch = getch();
236 if (ch == ERR)
237 continue;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100238 switch (ch) {
239 case KEY_DOWN:
240 form_driver(form, REQ_NEXT_FIELD);
241 break;
242 case KEY_UP:
243 form_driver(form, REQ_PREV_FIELD);
244 break;
245 case KEY_LEFT:
246 if (field_type(current_field(form)) == TYPE_ENUM) {
247 form_driver(form, REQ_PREV_CHOICE);
248 } else {
249 form_driver(form, REQ_LEFT_CHAR);
250 }
251 break;
252 case KEY_RIGHT:
253 if (field_type(current_field(form)) == TYPE_ENUM) {
254 form_driver(form, REQ_NEXT_CHOICE);
255 } else {
256 form_driver(form, REQ_RIGHT_CHAR);
257 }
258 break;
259 case KEY_BACKSPACE:
260 case '\b':
261 form_driver(form, REQ_DEL_PREV);
262 break;
263 case KEY_DC:
264 form_driver(form, REQ_DEL_CHAR);
265 break;
266 case KEY_F(1):
Martin Rothf62065f2016-04-23 17:24:57 -0600267 done = 1;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100268 break;
269 default:
270 form_driver(form, ch);
271 break;
272 }
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100273 }
274
Lubomir Rintel1ecc8af2015-02-24 20:33:35 +0100275 endwin();
276
Paul Menzele7385d12017-01-20 23:29:21 +0100277 for (i = 0; i < numopts; i++) {
Martin Rothf62065f2016-04-23 17:24:57 -0600278 char *name = field_buffer(fields[2 * i], 0);
279 char *value = field_buffer(fields[2 * i + 1], 0);
Vladimir Serbinenkoc9babb22014-01-15 22:07:52 +0100280 char *ptr;
Martin Rothf62065f2016-04-23 17:24:57 -0600281 for (ptr = value + strlen(value) - 1;
282 ptr >= value && *ptr == ' '; ptr--)
283 ;
Vladimir Serbinenkoc9babb22014-01-15 22:07:52 +0100284 ptr[1] = '\0';
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100285 set_option_from_string(use_nvram, opttbl, value, name);
286 }
287
288 unpost_form(form);
289 free_form(form);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100290
Vladimir Serbinenkoc7282882014-01-15 22:09:25 +0100291 /* reboot */
Martin Rothf62065f2016-04-23 17:24:57 -0600292 outb(0x6, 0xcf9);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100293 halt();
294}