xref: /haiku/src/system/boot/platform/efi/console.cpp (revision 553f3f2309e87d95345220463fa865d5fd8cf28d)
1 /*
2  * Copyright 2014-2016 Haiku, Inc. All rights reserved.
3  * Copyright 2013 Fredrik Holmqvist, fredrik.holmqvist@gmail.com. All rights
4  * reserved.
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "console.h"
10 
11 #include <string.h>
12 
13 #include <SupportDefs.h>
14 
15 #include <boot/stage2.h>
16 #include <boot/platform.h>
17 #include <efi/protocol/console-control.h>
18 #include <util/kernel_cpp.h>
19 
20 #include "efi_platform.h"
21 
22 
23 // This likely won't work without moving things around.
24 // Too early (pre-console init)
25 //#define TRACE_CONSOLE
26 #ifdef TRACE_CONSOLE
27 #   define TRACE(x...) dprintf(x)
28 #else
29 #   define TRACE(x...)
30 #endif
31 
32 
33 class Console : public ConsoleNode {
34 	public:
35 		Console();
36 
37 		virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer,
38 			size_t bufferSize);
39 		virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer,
40 			size_t bufferSize);
41 };
42 
43 
44 static uint32 sScreenWidth;
45 static uint32 sScreenHeight;
46 static uint32 sScreenMode;
47 static Console sInput, sOutput;
48 FILE *stdin, *stdout, *stderr;
49 
50 
51 //	#pragma mark -
52 
53 
54 Console::Console()
55 	: ConsoleNode()
56 {
57 }
58 
59 
60 ssize_t
61 Console::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
62 {
63 	return B_ERROR;
64 }
65 
66 
67 ssize_t
68 Console::WriteAt(void *cookie, off_t /*pos*/, const void *buffer,
69 	size_t bufferSize)
70 {
71 	const char *string = (const char *)buffer;
72 	char16_t ucsBuffer[bufferSize + 3];
73 	uint32 j = 0;
74 
75 	for (uint32 i = 0; i < bufferSize; i++) {
76 		switch (string[i]) {
77 			case '\n': {
78 				ucsBuffer[j++] = '\r';
79 				ucsBuffer[j++] = '\n';
80 			} //fallthrough
81 			case 0 : {
82 				//Not sure if we should keep going or abort for 0.
83 				//Keep going was easy anyway.
84 				ucsBuffer[j] = 0;
85 				kSystemTable->ConOut->OutputString(kSystemTable->ConOut,
86 					ucsBuffer);
87 				j = 0;
88 				continue;
89 			}
90 			default:
91 				ucsBuffer[j++] = (char16_t)string[i];
92 		}
93 	}
94 
95 	if (j > 0) {
96 		ucsBuffer[j] = 0;
97 		kSystemTable->ConOut->OutputString(kSystemTable->ConOut, ucsBuffer);
98 	}
99 	return bufferSize;
100 }
101 
102 
103 //	#pragma mark -
104 
105 
106 void
107 console_clear_screen(void)
108 {
109 	kSystemTable->ConOut->ClearScreen(kSystemTable->ConOut);
110 }
111 
112 
113 int32
114 console_width(void)
115 {
116 	return sScreenWidth;
117 }
118 
119 
120 int32
121 console_height(void)
122 {
123 	return sScreenHeight;
124 }
125 
126 
127 void
128 console_set_cursor(int32 x, int32 y)
129 {
130 	kSystemTable->ConOut->SetCursorPosition(kSystemTable->ConOut, x, y);
131 }
132 
133 
134 void
135 console_show_cursor(void)
136 {
137 	kSystemTable->ConOut->EnableCursor(kSystemTable->ConOut, true);
138 }
139 
140 
141 void
142 console_hide_cursor(void)
143 {
144 	kSystemTable->ConOut->EnableCursor(kSystemTable->ConOut, false);
145 }
146 
147 
148 void
149 console_set_color(int32 foreground, int32 background)
150 {
151 	kSystemTable->ConOut->SetAttribute(kSystemTable->ConOut,
152 		EFI_TEXT_ATTR((foreground & 0xf), (background & 0xf)));
153 }
154 
155 
156 int
157 console_wait_for_key(void)
158 {
159 	size_t index;
160 	efi_status status;
161 	efi_input_key key;
162 	efi_event event = kSystemTable->ConIn->WaitForKey;
163 
164 	do {
165 		kBootServices->WaitForEvent(1, &event, &index);
166 		status = kSystemTable->ConIn->ReadKeyStroke(kSystemTable->ConIn, &key);
167 	} while (status == EFI_NOT_READY);
168 
169 	if (key.UnicodeChar > 0)
170 		return (int) key.UnicodeChar;
171 
172 	switch (key.ScanCode) {
173 		case SCAN_ESC:
174 			return TEXT_CONSOLE_KEY_ESCAPE;
175 		case SCAN_UP:
176 			return TEXT_CONSOLE_KEY_UP;
177 		case SCAN_DOWN:
178 			return TEXT_CONSOLE_KEY_DOWN;
179 		case SCAN_LEFT:
180 			return TEXT_CONSOLE_KEY_LEFT;
181 		case SCAN_RIGHT:
182 			return TEXT_CONSOLE_KEY_RIGHT;
183 		case SCAN_PAGE_UP:
184 			return TEXT_CONSOLE_KEY_PAGE_UP;
185 		case SCAN_PAGE_DOWN:
186 			return TEXT_CONSOLE_KEY_PAGE_DOWN;
187 		case SCAN_HOME:
188 			return TEXT_CONSOLE_KEY_HOME;
189 		case SCAN_END:
190 			return TEXT_CONSOLE_KEY_END;
191 	}
192 	return 0;
193 }
194 
195 
196 static void update_screen_size(void)
197 {
198 	size_t width, height;
199 	size_t area = 0;
200 	efi_simple_text_output_protocol *ConOut = kSystemTable->ConOut;
201 
202 	for (int mode = 0; mode < ConOut->Mode->MaxMode; ++mode) {
203 		if (ConOut->QueryMode(ConOut, mode, &width, &height) == EFI_SUCCESS) {
204 			if (width * height > area) {
205 				sScreenWidth = width;
206 				sScreenHeight = height;
207 				sScreenMode = mode;
208 			}
209 		}
210 	}
211 
212 	ConOut->SetMode(ConOut, sScreenMode);
213 }
214 
215 
216 static void
217 console_control(bool graphics)
218 {
219 	TRACE("Checking for EFI Console Control...\n");
220 	efi_guid consoleControlProtocolGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
221 	efi_console_control_protocol* consoleControl = NULL;
222 
223 	efi_status status = kSystemTable->BootServices->LocateProtocol(
224 		&consoleControlProtocolGUID, NULL, (void**)&consoleControl);
225 
226 	// Some EFI implementations boot up in an EFI graphics mode (Apple)
227 	// If this protocol doesn't exist, we can assume we're already in text mode.
228 	if (status != EFI_SUCCESS || consoleControl == NULL) {
229 		TRACE("EFI Console Control not found. Skipping.\n");
230 		return;
231 	}
232 
233 	TRACE("Located EFI Console Control. Setting EFI %s mode...\n",
234 		graphics ? "graphics" : "text");
235 
236 	if (graphics) {
237 		status = consoleControl->SetMode(consoleControl,
238 			EfiConsoleControlScreenGraphics);
239 	} else {
240 		status = consoleControl->SetMode(consoleControl,
241 			EfiConsoleControlScreenText);
242 	}
243 
244 	TRACE("Setting EFI %s mode was%s successful.\n",
245 		graphics ? "graphics" : "text", (status == EFI_SUCCESS) ? "" : " not");
246 }
247 
248 
249 status_t
250 console_init(void)
251 {
252 	console_control(true);
253 	update_screen_size();
254 	console_hide_cursor();
255 	console_clear_screen();
256 
257 	// enable stdio functionality
258 	stdin = (FILE *)&sInput;
259 	stdout = stderr = (FILE *)&sOutput;
260 
261 	return B_OK;
262 }
263 
264 
265 uint32
266 console_check_boot_keys(void)
267 {
268 	efi_status status;
269 	efi_input_key key;
270 
271 	// give the user a chance to press a key
272 	kBootServices->Stall(500000);
273 
274 	status = kSystemTable->ConIn->ReadKeyStroke(kSystemTable->ConIn, &key);
275 
276 	if (status != EFI_SUCCESS)
277 		return 0;
278 
279 	if (key.UnicodeChar == 0 && key.ScanCode == SCAN_ESC)
280 		return BOOT_OPTION_DEBUG_OUTPUT;
281 	if (key.UnicodeChar == ' ')
282 		return BOOT_OPTION_MENU;
283 
284 	return 0;
285 }
286 
287 
288 extern "C" void
289 platform_switch_to_text_mode(void)
290 {
291 	console_control(false);
292 	kSystemTable->ConOut->Reset(kSystemTable->ConOut, false);
293 	kSystemTable->ConOut->SetMode(kSystemTable->ConOut, sScreenMode);
294 }
295