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