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
EFITextConsole()64 EFITextConsole::EFITextConsole()
65 : ConsoleNode()
66 {
67 }
68
69
70 ssize_t
ReadAt(void * cookie,off_t pos,void * buffer,size_t bufferSize)71 EFITextConsole::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
72 {
73 return B_ERROR;
74 }
75
76
77 ssize_t
WriteAt(void * cookie,off_t,const void * buffer,size_t bufferSize)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
ClearScreen()114 EFITextConsole::ClearScreen()
115 {
116 kSystemTable->ConOut->ClearScreen(kSystemTable->ConOut);
117 }
118
119
120 int32
Width()121 EFITextConsole::Width()
122 {
123 return fScreenWidth;
124 }
125
126
127 int32
Height()128 EFITextConsole::Height()
129 {
130 return fScreenHeight;
131 }
132
133
134 void
SetCursor(int32 x,int32 y)135 EFITextConsole::SetCursor(int32 x, int32 y)
136 {
137 kSystemTable->ConOut->SetCursorPosition(kSystemTable->ConOut, x, y);
138 }
139
140
141 void
SetCursorVisible(bool visible)142 EFITextConsole::SetCursorVisible(bool visible)
143 {
144 kSystemTable->ConOut->EnableCursor(kSystemTable->ConOut, visible);
145 }
146
147
148 void
SetColors(int32 foreground,int32 background)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
console_wait_for_key(void)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
update_screen_size(void)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
console_init(void)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
console_check_boot_keys(void)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
platform_switch_to_text_mode(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