xref: /haiku/src/system/boot/platform/efi/console.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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 <boot/platform/generic/video.h>
18 #include <efi/protocol/console-control.h>
19 #include <util/kernel_cpp.h>
20 
21 #include "efi_platform.h"
22 
23 
24 // This likely won't work without moving things around.
25 // Too early (pre-console init)
26 //#define TRACE_CONSOLE
27 #ifdef TRACE_CONSOLE
28 #   define TRACE(x...) dprintf(x)
29 #else
30 #   define TRACE(x...)
31 #endif
32 
33 
34 class EFITextConsole : public ConsoleNode {
35 	public:
36 		EFITextConsole();
37 
38 		virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer,
39 			size_t bufferSize);
40 		virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer,
41 			size_t bufferSize);
42 
43 		virtual void	ClearScreen();
44 		virtual int32	Width();
45 		virtual int32	Height();
46 		virtual void	SetCursor(int32 x, int32 y);
47 		virtual void	SetCursorVisible(bool visible);
48 		virtual void	SetColors(int32 foreground, int32 background);
49 
50 	public:
51 		uint32 fScreenWidth, fScreenHeight;
52 };
53 
54 
55 extern ConsoleNode* gConsoleNode;
56 static uint32 sScreenMode;
57 static EFITextConsole sConsole;
58 FILE *stdin, *stdout, *stderr;
59 
60 
61 //	#pragma mark -
62 
63 
64 EFITextConsole::EFITextConsole()
65 	: ConsoleNode()
66 {
67 }
68 
69 
70 ssize_t
71 EFITextConsole::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
72 {
73 	return B_ERROR;
74 }
75 
76 
77 ssize_t
78 EFITextConsole::WriteAt(void *cookie, off_t /*pos*/, const void *buffer,
79 	size_t bufferSize)
80 {
81 	const char *string = (const char *)buffer;
82 	char16_t ucsBuffer[bufferSize + 3];
83 	uint32 j = 0;
84 
85 	for (uint32 i = 0; i < bufferSize; i++) {
86 		switch (string[i]) {
87 			case '\n': {
88 				ucsBuffer[j++] = '\r';
89 				ucsBuffer[j++] = '\n';
90 			} //fallthrough
91 			case 0 : {
92 				//Not sure if we should keep going or abort for 0.
93 				//Keep going was easy anyway.
94 				ucsBuffer[j] = 0;
95 				kSystemTable->ConOut->OutputString(kSystemTable->ConOut,
96 					ucsBuffer);
97 				j = 0;
98 				continue;
99 			}
100 			default:
101 				ucsBuffer[j++] = (char16_t)string[i];
102 		}
103 	}
104 
105 	if (j > 0) {
106 		ucsBuffer[j] = 0;
107 		kSystemTable->ConOut->OutputString(kSystemTable->ConOut, ucsBuffer);
108 	}
109 	return bufferSize;
110 }
111 
112 
113 void
114 EFITextConsole::ClearScreen()
115 {
116 	kSystemTable->ConOut->ClearScreen(kSystemTable->ConOut);
117 }
118 
119 
120 int32
121 EFITextConsole::Width()
122 {
123 	return fScreenWidth;
124 }
125 
126 
127 int32
128 EFITextConsole::Height()
129 {
130 	return fScreenHeight;
131 }
132 
133 
134 void
135 EFITextConsole::SetCursor(int32 x, int32 y)
136 {
137 	kSystemTable->ConOut->SetCursorPosition(kSystemTable->ConOut, x, y);
138 }
139 
140 
141 void
142 EFITextConsole::SetCursorVisible(bool visible)
143 {
144 	kSystemTable->ConOut->EnableCursor(kSystemTable->ConOut, visible);
145 }
146 
147 
148 void
149 EFITextConsole::SetColors(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 				sConsole.fScreenWidth = width;
206 				sConsole.fScreenHeight = height;
207 				sScreenMode = mode;
208 			}
209 		}
210 	}
211 
212 	ConOut->SetMode(ConOut, sScreenMode);
213 }
214 
215 
216 status_t
217 console_init(void)
218 {
219 #if 1
220 	gConsoleNode = &sConsole;
221 
222 	update_screen_size();
223 	console_hide_cursor();
224 	console_clear_screen();
225 #else
226 	// FIXME: This does not work because we cannot initialize video before VFS, as it
227 	// needs to read the driver settings before setting a mode; and also because the
228 	// heap does not yet exist.
229 	platform_init_video();
230 	platform_switch_to_logo();
231 	gConsoleNode = video_text_console_init(gKernelArgs.frame_buffer.physical_buffer.start);
232 #endif
233 
234 	// enable stdio functionality
235 	stdin = (FILE *)gConsoleNode;
236 	stdout = stderr = (FILE *)gConsoleNode;
237 
238 	return B_OK;
239 }
240 
241 
242 uint32
243 console_check_boot_keys(void)
244 {
245 	efi_input_key key;
246 
247 	for (int i = 0; i < 3; i++) {
248 		// give the user a chance to press a key
249 		kBootServices->Stall(100000);
250 
251 		efi_status status = kSystemTable->ConIn->ReadKeyStroke(
252 			kSystemTable->ConIn, &key);
253 
254 		if (status != EFI_SUCCESS)
255 			continue;
256 
257 		if (key.UnicodeChar == 0 && key.ScanCode == SCAN_ESC)
258 			return BOOT_OPTION_DEBUG_OUTPUT;
259 		if (key.UnicodeChar == ' ')
260 			return BOOT_OPTION_MENU;
261 	}
262 	return 0;
263 }
264 
265 
266 extern "C" void
267 platform_switch_to_text_mode(void)
268 {
269 	kSystemTable->ConOut->Reset(kSystemTable->ConOut, false);
270 	kSystemTable->ConOut->SetMode(kSystemTable->ConOut, sScreenMode);
271 }
272