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