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