]> git.proxmox.com Git - efi-boot-shim.git/blob - lib/console.c
Merge upstream version 0.7
[efi-boot-shim.git] / lib / console.c
1 /*
2 * Copyright 2012 <James.Bottomley@HansenPartnership.com>
3 * Copyright 2013 Red Hat Inc. <pjones@redhat.com>
4 *
5 * see COPYING file
6 */
7 #include <efi/efi.h>
8 #include <efi/efilib.h>
9
10 #include <console.h>
11 #include <variables.h>
12 #include <errors.h>
13
14 static EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} };
15
16 static int min(int a, int b)
17 {
18 if (a < b)
19 return a;
20 return b;
21 }
22
23 static int
24 count_lines(CHAR16 *str_arr[])
25 {
26 int i = 0;
27
28 while (str_arr[i])
29 i++;
30 return i;
31 }
32
33 static void
34 SetMem16(CHAR16 *dst, UINT32 n, CHAR16 c)
35 {
36 int i;
37
38 for (i = 0; i < n/2; i++) {
39 dst[i] = c;
40 }
41 }
42
43 EFI_INPUT_KEY
44 console_get_keystroke(void)
45 {
46 EFI_INPUT_KEY key;
47 UINTN EventIndex;
48
49 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &EventIndex);
50 uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
51
52 return key;
53 }
54
55 void
56 console_print_box_at(CHAR16 *str_arr[], int highlight, int start_col, int start_row, int size_cols, int size_rows, int offset, int lines)
57 {
58 int i;
59 SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut;
60 UINTN rows, cols;
61 CHAR16 *Line;
62
63 if (lines == 0)
64 return;
65
66 uefi_call_wrapper(co->QueryMode, 4, co, co->Mode->Mode, &cols, &rows);
67
68 /* last row on screen is unusable without scrolling, so ignore it */
69 rows--;
70
71 if (size_rows < 0)
72 size_rows = rows + size_rows + 1;
73 if (size_cols < 0)
74 size_cols = cols + size_cols + 1;
75
76 if (start_col < 0)
77 start_col = (cols + start_col + 2)/2;
78 if (start_row < 0)
79 start_row = (rows + start_row + 2)/2;
80 if (start_col < 0)
81 start_col = 0;
82 if (start_row < 0)
83 start_row = 0;
84
85 if (start_col > cols || start_row > rows) {
86 Print(L"Starting Position (%d,%d) is off screen\n",
87 start_col, start_row);
88 return;
89 }
90 if (size_cols + start_col > cols)
91 size_cols = cols - start_col;
92 if (size_rows + start_row > rows)
93 size_rows = rows - start_row;
94
95 if (lines > size_rows - 2)
96 lines = size_rows - 2;
97
98 Line = AllocatePool((size_cols+1)*sizeof(CHAR16));
99 if (!Line) {
100 Print(L"Failed Allocation\n");
101 return;
102 }
103
104 SetMem16 (Line, size_cols * 2, BOXDRAW_HORIZONTAL);
105
106 Line[0] = BOXDRAW_DOWN_RIGHT;
107 Line[size_cols - 1] = BOXDRAW_DOWN_LEFT;
108 Line[size_cols] = L'\0';
109 uefi_call_wrapper(co->SetCursorPosition, 3, co, start_col, start_row);
110 uefi_call_wrapper(co->OutputString, 2, co, Line);
111
112 int start;
113 if (offset == 0)
114 /* middle */
115 start = (size_rows - lines)/2 + start_row + offset;
116 else if (offset < 0)
117 /* from bottom */
118 start = start_row + size_rows - lines + offset - 1;
119 else
120 /* from top */
121 start = start_row + offset;
122
123
124 for (i = start_row + 1; i < size_rows + start_row - 1; i++) {
125 int line = i - start;
126
127 SetMem16 (Line, size_cols*2, L' ');
128 Line[0] = BOXDRAW_VERTICAL;
129 Line[size_cols - 1] = BOXDRAW_VERTICAL;
130 Line[size_cols] = L'\0';
131 if (line >= 0 && line < lines) {
132 CHAR16 *s = str_arr[line];
133 int len = StrLen(s);
134 int col = (size_cols - 2 - len)/2;
135
136 if (col < 0)
137 col = 0;
138
139 CopyMem(Line + col + 1, s, min(len, size_cols - 2)*2);
140 }
141 if (line >= 0 && line == highlight)
142 uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
143 uefi_call_wrapper(co->SetCursorPosition, 3, co, start_col, i);
144 uefi_call_wrapper(co->OutputString, 2, co, Line);
145 if (line >= 0 && line == highlight)
146 uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE);
147
148 }
149 SetMem16 (Line, size_cols * 2, BOXDRAW_HORIZONTAL);
150 Line[0] = BOXDRAW_UP_RIGHT;
151 Line[size_cols - 1] = BOXDRAW_UP_LEFT;
152 Line[size_cols] = L'\0';
153 uefi_call_wrapper(co->SetCursorPosition, 3, co, start_col, i);
154 uefi_call_wrapper(co->OutputString, 2, co, Line);
155
156 FreePool (Line);
157
158 }
159
160 void
161 console_print_box(CHAR16 *str_arr[], int highlight)
162 {
163 SIMPLE_TEXT_OUTPUT_MODE SavedConsoleMode;
164 SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut;
165 CopyMem(&SavedConsoleMode, co->Mode, sizeof(SavedConsoleMode));
166 uefi_call_wrapper(co->EnableCursor, 2, co, FALSE);
167 uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE);
168
169 console_print_box_at(str_arr, highlight, 0, 0, -1, -1, 0,
170 count_lines(str_arr));
171
172 console_get_keystroke();
173
174 uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible);
175
176 uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible);
177 uefi_call_wrapper(co->SetCursorPosition, 3, co, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow);
178 uefi_call_wrapper(co->SetAttribute, 2, co, SavedConsoleMode.Attribute);
179 }
180
181 int
182 console_select(CHAR16 *title[], CHAR16* selectors[], int start)
183 {
184 SIMPLE_TEXT_OUTPUT_MODE SavedConsoleMode;
185 SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut;
186 EFI_INPUT_KEY k;
187 int selector;
188 int selector_lines = count_lines(selectors);
189 int selector_max_cols = 0;
190 int i, offs_col, offs_row, size_cols, size_rows, lines;
191 int selector_offset;
192 UINTN cols, rows;
193
194 uefi_call_wrapper(co->QueryMode, 4, co, co->Mode->Mode, &cols, &rows);
195
196 for (i = 0; i < selector_lines; i++) {
197 int len = StrLen(selectors[i]);
198
199 if (len > selector_max_cols)
200 selector_max_cols = len;
201 }
202
203 if (start < 0)
204 start = 0;
205 if (start >= selector_lines)
206 start = selector_lines - 1;
207
208 offs_col = - selector_max_cols - 4;
209 size_cols = selector_max_cols + 4;
210
211 if (selector_lines > rows - 10) {
212 int title_lines = count_lines(title);
213 offs_row = title_lines + 1;
214 size_rows = rows - 3 - title_lines;
215 lines = size_rows - 2;
216 } else {
217 offs_row = - selector_lines - 4;
218 size_rows = selector_lines + 2;
219 lines = selector_lines;
220 }
221
222 if (start > lines) {
223 selector = lines;
224 selector_offset = start - lines;
225 } else {
226 selector = start;
227 selector_offset = 0;
228 }
229
230 CopyMem(&SavedConsoleMode, co->Mode, sizeof(SavedConsoleMode));
231 uefi_call_wrapper(co->EnableCursor, 2, co, FALSE);
232 uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE);
233
234 console_print_box_at(title, -1, 0, 0, -1, -1, 1, count_lines(title));
235
236 console_print_box_at(selectors, selector, offs_col, offs_row,
237 size_cols, size_rows, 0, lines);
238
239 do {
240 k = console_get_keystroke();
241
242 if (k.ScanCode == SCAN_ESC) {
243 selector = -1;
244 break;
245 }
246
247 if (k.ScanCode == SCAN_UP) {
248 if (selector > 0)
249 selector--;
250 else if (selector_offset > 0)
251 selector_offset--;
252 } else if (k.ScanCode == SCAN_DOWN) {
253 if (selector < lines - 1)
254 selector++;
255 else if (selector_offset < (selector_lines - lines))
256 selector_offset++;
257 }
258
259 console_print_box_at(&selectors[selector_offset], selector,
260 offs_col, offs_row,
261 size_cols, size_rows, 0, lines);
262 } while (!(k.ScanCode == SCAN_NULL
263 && k.UnicodeChar == CHAR_CARRIAGE_RETURN));
264
265 uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible);
266
267 uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible);
268 uefi_call_wrapper(co->SetCursorPosition, 3, co, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow);
269 uefi_call_wrapper(co->SetAttribute, 2, co, SavedConsoleMode.Attribute);
270
271 if (selector < 0)
272 /* ESC pressed */
273 return selector;
274 return selector + selector_offset;
275 }
276
277
278 int
279 console_yes_no(CHAR16 *str_arr[])
280 {
281 return console_select(str_arr, (CHAR16 *[]){ L"No", L"Yes", NULL }, 0);
282 }
283
284 void
285 console_alertbox(CHAR16 **title)
286 {
287 console_select(title, (CHAR16 *[]){ L"OK", 0 }, 0);
288 }
289
290 void
291 console_errorbox(CHAR16 *err)
292 {
293 CHAR16 **err_arr = (CHAR16 *[]){
294 L"ERROR",
295 L"",
296 0,
297 0,
298 };
299
300 err_arr[2] = err;
301
302 console_alertbox(err_arr);
303 }
304
305 void
306 console_notify(CHAR16 *string)
307 {
308 CHAR16 **str_arr = (CHAR16 *[]){
309 0,
310 0,
311 };
312
313 str_arr[0] = string;
314
315 console_alertbox(str_arr);
316 }
317
318 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
319
320 /* Copy of gnu-efi-3.0 with the added secure boot strings */
321 static struct {
322 EFI_STATUS Code;
323 WCHAR *Desc;
324 } error_table[] = {
325 { EFI_SUCCESS, L"Success"},
326 { EFI_LOAD_ERROR, L"Load Error"},
327 { EFI_INVALID_PARAMETER, L"Invalid Parameter"},
328 { EFI_UNSUPPORTED, L"Unsupported"},
329 { EFI_BAD_BUFFER_SIZE, L"Bad Buffer Size"},
330 { EFI_BUFFER_TOO_SMALL, L"Buffer Too Small"},
331 { EFI_NOT_READY, L"Not Ready"},
332 { EFI_DEVICE_ERROR, L"Device Error"},
333 { EFI_WRITE_PROTECTED, L"Write Protected"},
334 { EFI_OUT_OF_RESOURCES, L"Out of Resources"},
335 { EFI_VOLUME_CORRUPTED, L"Volume Corrupt"},
336 { EFI_VOLUME_FULL, L"Volume Full"},
337 { EFI_NO_MEDIA, L"No Media"},
338 { EFI_MEDIA_CHANGED, L"Media changed"},
339 { EFI_NOT_FOUND, L"Not Found"},
340 { EFI_ACCESS_DENIED, L"Access Denied"},
341 { EFI_NO_RESPONSE, L"No Response"},
342 { EFI_NO_MAPPING, L"No mapping"},
343 { EFI_TIMEOUT, L"Time out"},
344 { EFI_NOT_STARTED, L"Not started"},
345 { EFI_ALREADY_STARTED, L"Already started"},
346 { EFI_ABORTED, L"Aborted"},
347 { EFI_ICMP_ERROR, L"ICMP Error"},
348 { EFI_TFTP_ERROR, L"TFTP Error"},
349 { EFI_PROTOCOL_ERROR, L"Protocol Error"},
350 { EFI_INCOMPATIBLE_VERSION, L"Incompatible Version"},
351 { EFI_SECURITY_VIOLATION, L"Security Violation"},
352
353 // warnings
354 { EFI_WARN_UNKOWN_GLYPH, L"Warning Unknown Glyph"},
355 { EFI_WARN_DELETE_FAILURE, L"Warning Delete Failure"},
356 { EFI_WARN_WRITE_FAILURE, L"Warning Write Failure"},
357 { EFI_WARN_BUFFER_TOO_SMALL, L"Warning Buffer Too Small"},
358 { 0, NULL}
359 } ;
360
361
362 static CHAR16 *
363 err_string (
364 IN EFI_STATUS Status
365 )
366 {
367 UINTN Index;
368
369 for (Index = 0; error_table[Index].Desc; Index +=1) {
370 if (error_table[Index].Code == Status) {
371 return error_table[Index].Desc;
372 }
373 }
374
375 return L"";
376 }
377
378
379 void
380 console_error(CHAR16 *err, EFI_STATUS status)
381 {
382 CHAR16 **err_arr = (CHAR16 *[]){
383 L"ERROR",
384 L"",
385 0,
386 0,
387 };
388 CHAR16 str[512];
389
390 SPrint(str, sizeof(str), L"%s: (%d) %s", err, status, err_string(status));
391
392 err_arr[2] = str;
393
394 console_alertbox(err_arr);
395 }
396
397 void
398 console_reset(void)
399 {
400 SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut;
401
402 uefi_call_wrapper(co->Reset, 2, co, TRUE);
403 /* set mode 0 - required to be 80x25 */
404 uefi_call_wrapper(co->SetMode, 2, co, 0);
405 uefi_call_wrapper(co->ClearScreen, 1, co);
406 }
407
408 UINT8 verbose;
409
410 VOID
411 setup_verbosity(VOID)
412 {
413 EFI_STATUS status;
414 EFI_GUID guid = SHIM_LOCK_GUID;
415 UINT8 verbose_check;
416 UINTN verbose_check_size;
417
418 verbose_check_size = 1;
419 status = get_variable(L"SHIM_VERBOSE", (void *)&verbose_check,
420 &verbose_check_size, guid);
421 verbose = 0;
422 if (!EFI_ERROR(status))
423 verbose = verbose_check;
424 }
425
426 VOID setup_console (int text)
427 {
428 EFI_STATUS status;
429 EFI_GUID console_control_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
430 EFI_CONSOLE_CONTROL_PROTOCOL *concon;
431 static EFI_CONSOLE_CONTROL_SCREEN_MODE mode =
432 EfiConsoleControlScreenGraphics;
433 EFI_CONSOLE_CONTROL_SCREEN_MODE new_mode;
434
435 status = LibLocateProtocol(&console_control_guid, (VOID **)&concon);
436 if (status != EFI_SUCCESS)
437 return;
438
439 if (text) {
440 new_mode = EfiConsoleControlScreenText;
441
442 status = uefi_call_wrapper(concon->GetMode, 4, concon, &mode,
443 0, 0);
444 /* If that didn't work, assume it's graphics */
445 if (status != EFI_SUCCESS)
446 mode = EfiConsoleControlScreenGraphics;
447 } else {
448 new_mode = mode;
449 }
450
451 uefi_call_wrapper(concon->SetMode, 2, concon, new_mode);
452 }