blob: 97be9df2fe79a3369c20ba974805303eba7767e8 [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
179 /* coreboot data structures */
180 lib_get_sysinfo();
181
182 struct cb_cmos_option_table *opttbl = get_system_option_table();
183
Antonello Dettori079feec2016-08-18 10:32:27 +0200184 if (opttbl == NULL) {
185 printf("Could not find coreboot option table.\n");
Antonello Dettori904dd302016-08-18 10:32:27 +0200186 halt();
187 }
188
189 /* prep CMOS layout into libcurses data structures */
190
191 struct cb_cmos_entries *option = first_cmos_entry(opttbl);
192 int numopts = 0;
193 int maxlength = 0;
194
195 count_cmos_options(option, &numopts, &maxlength);
196
197 FIELD **fields = malloc(sizeof(FIELD *) * (2 * numopts + 1));
198
199 cmos_walk_options(opttbl, fields, numopts, maxlength);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100200
Lubomir Rintel1ecc8af2015-02-24 20:33:35 +0100201 /* display initialization */
202 initscr();
203 keypad(stdscr, TRUE);
204 cbreak();
205 noecho();
206
207 if (start_color()) {
Martin Rothf62065f2016-04-23 17:24:57 -0600208 assume_default_colors(COLOR_BLUE, COLOR_CYAN);
Lubomir Rintel1ecc8af2015-02-24 20:33:35 +0100209 }
210 leaveok(stdscr, TRUE);
211 curs_set(1);
212
213 erase();
214 box(stdscr, 0, 0);
215 mvaddstr(0, 2, "coreboot configuration utility");
216 refresh();
217
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100218 FORM *form = new_form(fields);
Martin Rothf62065f2016-04-23 17:24:57 -0600219 int numlines = min(numopts * 2, 16);
220 WINDOW *w = newwin(numlines + 2, 70, 2, 1);
221 WINDOW *inner_w = newpad(numopts * 2, 68);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100222 box(w, 0, 0);
223 mvwaddstr(w, 0, 2, "Press F1 when done");
224 set_form_win(form, w);
225 set_form_sub(form, inner_w);
226 post_form(form);
227
228 done = 0;
Martin Rothf62065f2016-04-23 17:24:57 -0600229 while (!done) {
Lubomir Rintel68009e92015-02-01 15:24:43 +0100230 render_form(form);
Martin Rothf62065f2016-04-23 17:24:57 -0600231 ch = getch();
232 if (ch == ERR)
233 continue;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100234 switch (ch) {
235 case KEY_DOWN:
236 form_driver(form, REQ_NEXT_FIELD);
237 break;
238 case KEY_UP:
239 form_driver(form, REQ_PREV_FIELD);
240 break;
241 case KEY_LEFT:
242 if (field_type(current_field(form)) == TYPE_ENUM) {
243 form_driver(form, REQ_PREV_CHOICE);
244 } else {
245 form_driver(form, REQ_LEFT_CHAR);
246 }
247 break;
248 case KEY_RIGHT:
249 if (field_type(current_field(form)) == TYPE_ENUM) {
250 form_driver(form, REQ_NEXT_CHOICE);
251 } else {
252 form_driver(form, REQ_RIGHT_CHAR);
253 }
254 break;
255 case KEY_BACKSPACE:
256 case '\b':
257 form_driver(form, REQ_DEL_PREV);
258 break;
259 case KEY_DC:
260 form_driver(form, REQ_DEL_CHAR);
261 break;
262 case KEY_F(1):
Martin Rothf62065f2016-04-23 17:24:57 -0600263 done = 1;
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100264 break;
265 default:
266 form_driver(form, ch);
267 break;
268 }
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100269 }
270
Lubomir Rintel1ecc8af2015-02-24 20:33:35 +0100271 endwin();
272
Paul Menzele7385d12017-01-20 23:29:21 +0100273 for (i = 0; i < numopts; i++) {
Martin Rothf62065f2016-04-23 17:24:57 -0600274 char *name = field_buffer(fields[2 * i], 0);
275 char *value = field_buffer(fields[2 * i + 1], 0);
Vladimir Serbinenkoc9babb22014-01-15 22:07:52 +0100276 char *ptr;
Martin Rothf62065f2016-04-23 17:24:57 -0600277 for (ptr = value + strlen(value) - 1;
278 ptr >= value && *ptr == ' '; ptr--)
279 ;
Vladimir Serbinenkoc9babb22014-01-15 22:07:52 +0100280 ptr[1] = '\0';
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100281 set_option_from_string(use_nvram, opttbl, value, name);
282 }
283
284 unpost_form(form);
285 free_form(form);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100286
Vladimir Serbinenkoc7282882014-01-15 22:09:25 +0100287 /* reboot */
Martin Rothf62065f2016-04-23 17:24:57 -0600288 outb(0x6, 0xcf9);
Patrick Georgi4b62ebe2012-11-16 15:05:11 +0100289 halt();
290}