]> git.proxmox.com Git - mirror_edk2.git/blob - StdLib/LibC/Uefi/Devices/Console/daConsole.c
StdLib/LibC/Uefi/Devices/Console/daConsole.c: Fix bug where unconvertable wide chara...
[mirror_edk2.git] / StdLib / LibC / Uefi / Devices / Console / daConsole.c
1 /** @file
2 Abstract device driver for the UEFI Console.
3
4 Manipulates abstractions for stdin, stdout, stderr.
5
6 Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials are licensed and made available under
8 the terms and conditions of the BSD License that accompanies this distribution.
9 The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php.
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16 #include <Uefi.h>
17 #include <Library/BaseLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Protocol/SimpleTextIn.h>
21 #include <Protocol/SimpleTextOut.h>
22
23 #include <LibConfig.h>
24
25 #include <errno.h>
26 #include <wctype.h>
27 #include <wchar.h>
28 #include <stdarg.h>
29 #include <sys/fcntl.h>
30 #include <unistd.h>
31 #include <kfile.h>
32 #include <Device/Device.h>
33 #include <MainData.h>
34
35 static const CHAR16* const
36 stdioNames[NUM_SPECIAL] = {
37 L"stdin:", L"stdout:", L"stderr:"
38 };
39
40 static const int stdioFlags[NUM_SPECIAL] = {
41 O_RDONLY, // stdin
42 O_WRONLY, // stdout
43 O_WRONLY // stderr
44 };
45
46 static DeviceNode *ConNode[NUM_SPECIAL];
47 static ConInstance *ConInstanceList;
48
49 static wchar_t *ConReadBuf;
50
51 /* Flags settable by Ioctl */
52 static BOOLEAN TtyCooked;
53 static BOOLEAN TtyEcho;
54
55 /** Convert string from MBCS to WCS and translate \n to \r\n.
56
57 It is the caller's responsibility to ensure that dest is
58 large enough to hold the converted results. It is guaranteed
59 that there will be fewer than n characters placed in dest.
60
61 @param dest WCS buffer to receive the converted string.
62 @param buf MBCS string to convert to WCS.
63 @param n Number of BYTES contained in buf.
64 @param Cs Pointer to the character state object for this stream
65
66 @return The number of BYTES consumed from buf.
67 **/
68 ssize_t
69 WideTtyCvt( CHAR16 *dest, const char *buf, ssize_t n, mbstate_t *Cs)
70 {
71 ssize_t i = 0;
72 int numB = 0;
73 wchar_t wc[2];
74
75 while(n > 0) {
76 numB = (int)mbrtowc(wc, buf, MIN(MB_LEN_MAX,n), Cs);
77 if( numB == 0) {
78 break;
79 };
80 if(numB < 0) { // If an unconvertable character, replace it.
81 wc[0] = BLOCKELEMENT_LIGHT_SHADE;
82 numB = 1;
83 }
84 if(wc[0] == L'\n') {
85 *dest++ = L'\r';
86 ++i;
87 }
88 *dest++ = (CHAR16)wc[0];
89 i += numB;
90 n -= numB;
91 buf += numB;
92 }
93 *dest = 0;
94 return i;
95 }
96
97 static
98 int
99 EFIAPI
100 da_ConClose(
101 IN struct __filedes *filp
102 )
103 {
104 ConInstance *Stream;
105
106 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
107 // Quick check to see if Stream looks reasonable
108 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
109 EFIerrno = RETURN_INVALID_PARAMETER;
110 return -1; // Looks like a bad File Descriptor pointer
111 }
112 gMD->StdIo[Stream->InstanceNum] = NULL; // Mark the stream as closed
113 return RETURN_SUCCESS;
114 }
115
116 static
117 off_t
118 EFIAPI
119 da_ConSeek(
120 struct __filedes *filp,
121 off_t Position,
122 int whence ///< Ignored by Console
123 )
124 {
125 ConInstance *Stream;
126 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
127 XY_OFFSET CursorPos;
128
129 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
130 // Quick check to see if Stream looks reasonable
131 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
132 EFIerrno = RETURN_INVALID_PARAMETER;
133 return -1; // Looks like a bad This pointer
134 }
135 if(Stream->InstanceNum == STDIN_FILENO) {
136 // Seek is not valid for stdin
137 EFIerrno = RETURN_UNSUPPORTED;
138 return -1;
139 }
140 // Everything is OK to do the final verification and "seek".
141 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
142 CursorPos.Offset = Position;
143
144 EFIerrno = Proto->SetCursorPosition(Proto,
145 (INTN)CursorPos.XYpos.Column,
146 (INTN)CursorPos.XYpos.Row);
147
148 if(RETURN_ERROR(EFIerrno)) {
149 return -1;
150 }
151 else {
152 return Position;
153 }
154 }
155
156 /* Write a NULL terminated WCS to the EFI console.
157
158 @param[in,out] BufferSize Number of bytes in Buffer. Set to zero if
159 the string couldn't be displayed.
160 @param[in] Buffer The WCS string to be displayed
161
162 @return The number of BYTES written. Because of MBCS, this may be more than number of characters.
163 */
164 static
165 ssize_t
166 EFIAPI
167 da_ConWrite(
168 IN struct __filedes *filp,
169 IN off_t *Position,
170 IN size_t BufferSize,
171 IN const void *Buffer
172 )
173 {
174 EFI_STATUS Status;
175 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
176 ConInstance *Stream;
177 ssize_t NumBytes;
178
179 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
180 // Quick check to see if Stream looks reasonable
181 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
182 EFIerrno = RETURN_INVALID_PARAMETER;
183 return -1; // Looks like a bad This pointer
184 }
185 if(Stream->InstanceNum == STDIN_FILENO) {
186 // Write is not valid for stdin
187 EFIerrno = RETURN_UNSUPPORTED;
188 return -1;
189 }
190 // Everything is OK to do the write.
191 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
192
193 // Convert string from MBCS to WCS and translate \n to \r\n.
194 NumBytes = WideTtyCvt(gMD->UString, (const char *)Buffer, (ssize_t)BufferSize, &Stream->CharState);
195 BufferSize = NumBytes;
196
197
198 // Send the Unicode buffer to the console
199 Status = Proto->OutputString( Proto, gMD->UString);
200 // Depending on status, update BufferSize and return
201 if(RETURN_ERROR(Status)) {
202 BufferSize = 0; // We don't really know how many characters made it out
203 }
204 else {
205 //BufferSize = NumBytes;
206 Stream->NumWritten += NumBytes;
207 }
208 EFIerrno = Status; // Make error reason available to caller
209 return BufferSize;
210 }
211
212 /** Read characters from the console input device.
213
214 @param[in,out] filp Pointer to file descriptor for this file.
215 @param[in,out] offset Ignored.
216 @param[in] BufferSize Buffer size, in bytes.
217 @param[out] Buffer Buffer in which to place the read characters.
218
219 @return Number of bytes actually placed into Buffer.
220
221 @todo Handle encodings other than ASCII-7 and UEFI.
222 **/
223 static
224 ssize_t
225 EFIAPI
226 da_ConRead(
227 IN OUT struct __filedes *filp,
228 IN OUT off_t *offset, // Console ignores this
229 IN size_t BufferSize,
230 OUT VOID *Buffer
231 )
232 {
233 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Proto;
234 ConInstance *Stream;
235 wchar_t *OutPtr;
236 EFI_INPUT_KEY Key;
237 UINTN NumChar;
238 UINTN Edex;
239 EFI_STATUS Status = RETURN_SUCCESS;
240 UINTN i;
241 char EchoBuff[MB_CUR_MAX + 1];
242 int NumEcho;
243
244 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
245 // Quick check to see if Stream looks reasonable
246 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
247 EFIerrno = RETURN_INVALID_PARAMETER;
248 return -1; // Looks like a bad This pointer
249 }
250 if(Stream->InstanceNum != STDIN_FILENO) {
251 // Read only valid for stdin
252 EFIerrno = RETURN_UNSUPPORTED;
253 return -1;
254 }
255 // It looks like things are OK for trying to read
256 // We will accumulate *BufferSize characters or until we encounter
257 // an "activation" character. Currently any control character.
258 Proto = (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)Stream->Dev;
259 OutPtr = ConReadBuf;
260 NumChar = (BufferSize > MAX_INPUT)? MAX_INPUT : BufferSize;
261 i = 0;
262 do {
263 if((Stream->UnGetKey.UnicodeChar == CHAR_NULL) && (Stream->UnGetKey.ScanCode == SCAN_NULL)) {
264 Status = gBS->WaitForEvent( 1, &Proto->WaitForKey, &Edex);
265 if(Status != RETURN_SUCCESS) {
266 break;
267 }
268 Status = Proto->ReadKeyStroke(Proto, &Key);
269 if(Status != RETURN_SUCCESS) {
270 break;
271 }
272 }
273 else {
274 Key.ScanCode = Stream->UnGetKey.ScanCode;
275 Key.UnicodeChar = Stream->UnGetKey.UnicodeChar;
276 Stream->UnGetKey.ScanCode = SCAN_NULL;
277 Stream->UnGetKey.UnicodeChar = CHAR_NULL;
278 }
279 if(Key.ScanCode == SCAN_NULL) {
280 NumEcho = 0;
281 if(TtyCooked && (Key.UnicodeChar == CHAR_CARRIAGE_RETURN)) {
282 *OutPtr++ = CHAR_LINEFEED;
283 NumEcho = wctomb(EchoBuff, CHAR_LINEFEED);
284 }
285 else {
286 *OutPtr++ = Key.UnicodeChar;
287 NumEcho = wctomb(EchoBuff, Key.UnicodeChar);
288 }
289 ++i;
290 EchoBuff[NumEcho] = 0; /* Terminate the Echo buffer */
291 if(TtyEcho) {
292 /* Echo the character just input */
293 da_ConWrite(&gMD->fdarray[STDOUT_FILENO], NULL, 2, EchoBuff);
294 }
295 }
296 if(iswcntrl(Key.UnicodeChar)) { // If a control character, or a scan code
297 break;
298 }
299 } while(i < NumChar);
300
301 *OutPtr = L'\0'; // Terminate the input buffer
302
303 /* Convert the input buffer and place in Buffer.
304 If the fully converted input buffer won't fit, write what will and
305 leave the rest in ConReadBuf with ConReadLeft indicating how many
306 unconverted characters remain in ConReadBuf.
307 */
308 NumEcho = (int)wcstombs(Buffer, ConReadBuf, BufferSize); /* Re-use NumEcho to hold number of bytes in Buffer */
309 /* More work needs to be done before locales other than C can be supported. */
310
311 EFIerrno = Status;
312 return (ssize_t)NumEcho; // Will be 0 if we didn't get a key
313 }
314
315 /** Console-specific helper function for the fstat() function.
316
317 st_size Set to number of characters read for stdin and number written for stdout and stderr.
318 st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
319 st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
320 st_blksize Set to 1 since this is a character device
321
322 All other members of the stat structure are left unchanged.
323 **/
324 static
325 int
326 EFIAPI
327 da_ConStat(
328 struct __filedes *filp,
329 struct stat *Buffer,
330 void *Something
331 )
332 {
333 ConInstance *Stream;
334 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Proto;
335 XY_OFFSET CursorPos;
336 INT32 OutMode;
337 UINTN ModeCol;
338 UINTN ModeRow;
339
340 // ConGetInfo
341 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
342 // Quick check to see if Stream looks reasonable
343 if ((Stream->Cookie != CON_COOKIE) || // Cookie == 'IoAb'
344 (Buffer == NULL))
345 {
346 EFIerrno = RETURN_INVALID_PARAMETER;
347 return -1;
348 }
349 // All of our parameters are correct, so fill in the information.
350 Buffer->st_blksize = 0; // Character device, not a block device
351 Buffer->st_mode = filp->f_iflags;
352
353 // ConGetPosition
354 if(Stream->InstanceNum == STDIN_FILENO) {
355 // This is stdin
356 Buffer->st_curpos = 0;
357 Buffer->st_size = (off_t)Stream->NumRead;
358 Buffer->st_physsize = 1;
359 }
360 else {
361 Proto = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)Stream->Dev;
362 CursorPos.XYpos.Column = (UINT32)Proto->Mode->CursorColumn;
363 CursorPos.XYpos.Row = (UINT32)Proto->Mode->CursorRow;
364 Buffer->st_curpos = (off_t)CursorPos.Offset;
365 Buffer->st_size = (off_t)Stream->NumWritten;
366
367 OutMode = Proto->Mode->Mode;
368 EFIerrno = Proto->QueryMode(Proto, (UINTN)OutMode, &ModeCol, &ModeRow);
369 if(RETURN_ERROR(EFIerrno)) {
370 Buffer->st_physsize = 0;
371 }
372 else {
373 CursorPos.XYpos.Column = (UINT32)ModeCol;
374 CursorPos.XYpos.Row = (UINT32)ModeRow;
375 Buffer->st_physsize = (off_t)CursorPos.Offset;
376 }
377 }
378 return 0;
379 }
380
381 static
382 int
383 EFIAPI
384 da_ConIoctl(
385 struct __filedes *filp,
386 ULONGN cmd,
387 va_list argp
388 )
389 {
390 return -EPERM;
391 }
392
393 /** Open an abstract Console Device.
394 **/
395 int
396 EFIAPI
397 da_ConOpen(
398 DeviceNode *DevNode,
399 struct __filedes *filp,
400 int DevInstance, // Not used for console devices
401 wchar_t *Path, // Not used for console devices
402 wchar_t *MPath // Not used for console devices
403 )
404 {
405 ConInstance *Stream;
406
407 if((filp == NULL) ||
408 (DevNode == NULL))
409 {
410 EFIerrno = RETURN_INVALID_PARAMETER;
411 errno = EINVAL;
412 return -1;
413 }
414 Stream = (ConInstance *)DevNode->InstanceList;
415 // Quick check to see if Stream looks reasonable
416 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
417 EFIerrno = RETURN_INVALID_PARAMETER;
418 errno = EINVAL;
419 return -1; // Looks like a bad This pointer
420 }
421 gMD->StdIo[Stream->InstanceNum] = Stream;
422 filp->f_iflags |= (S_IFREG | _S_IFCHR | _S_ICONSOLE);
423 filp->f_offset = 0;
424 filp->f_ops = &Stream->Abstraction;
425
426 return 0;
427 }
428
429 #include <sys/poll.h>
430 /* Returns a bit mask describing which operations could be completed immediately.
431
432 (POLLIN | POLLRDNORM) A Unicode character is available to read
433 (POLLIN) A ScanCode is ready.
434 (POLLOUT) The device is ready for output - always set on stdout and stderr.
435
436 */
437 static
438 short
439 EFIAPI
440 da_ConPoll(
441 struct __filedes *filp,
442 short events
443 )
444 {
445 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Proto;
446 ConInstance *Stream;
447 EFI_STATUS Status = RETURN_SUCCESS;
448 short RdyMask = 0;
449
450 Stream = BASE_CR(filp->f_ops, ConInstance, Abstraction);
451 // Quick check to see if Stream looks reasonable
452 if(Stream->Cookie != CON_COOKIE) { // Cookie == 'IoAb'
453 EFIerrno = RETURN_INVALID_PARAMETER;
454 return POLLNVAL; // Looks like a bad filp pointer
455 }
456 if(Stream->InstanceNum == 0) {
457 // Only input is supported for this device
458 Proto = (EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)Stream->Dev;
459 if((Stream->UnGetKey.UnicodeChar == CHAR_NULL) && (Stream->UnGetKey.ScanCode == SCAN_NULL)) {
460 Status = Proto->ReadKeyStroke(Proto, &Stream->UnGetKey);
461 if(Status == RETURN_SUCCESS) {
462 RdyMask = POLLIN;
463 if(Stream->UnGetKey.UnicodeChar != CHAR_NULL) {
464 RdyMask |= POLLRDNORM;
465 }
466 }
467 else {
468 Stream->UnGetKey.ScanCode = SCAN_NULL;
469 Stream->UnGetKey.UnicodeChar = CHAR_NULL;
470 }
471 }
472 }
473 else if(Stream->InstanceNum < NUM_SPECIAL) { // Not 0, is it 1 or 2?
474 // Only output is supported for this device
475 RdyMask = POLLOUT;
476 }
477 else {
478 RdyMask = POLLERR; // Not one of the standard streams
479 }
480 EFIerrno = Status;
481
482 return (RdyMask & (events | POLL_RETONLY));
483 }
484
485 /** Construct the Console stream devices: stdin, stdout, stderr.
486
487 Allocate the instance structure and populate it with the information for
488 each stream device.
489 **/
490 RETURN_STATUS
491 EFIAPI
492 __Cons_construct(
493 IN EFI_HANDLE ImageHandle,
494 IN EFI_SYSTEM_TABLE *SystemTable
495 )
496 {
497 ConInstance *Stream;
498 RETURN_STATUS Status = RETURN_SUCCESS;
499 int i;
500
501 ConInstanceList = (ConInstance *)AllocateZeroPool(NUM_SPECIAL * sizeof(ConInstance));
502 ConReadBuf = (wchar_t *)AllocateZeroPool((MAX_INPUT + 1) * sizeof(wchar_t));
503 if((ConInstanceList == NULL) || (ConReadBuf == NULL)) {
504 return RETURN_OUT_OF_RESOURCES;
505 }
506
507 for( i = 0; i < NUM_SPECIAL; ++i) {
508 // Get pointer to instance.
509 Stream = &ConInstanceList[i];
510
511 Stream->Cookie = CON_COOKIE;
512 Stream->InstanceNum = i;
513 Stream->CharState.A = 0; // Start in the initial state
514
515 switch(i) {
516 case STDIN_FILENO:
517 Stream->Dev = SystemTable->ConIn;
518 break;
519 case STDOUT_FILENO:
520 Stream->Dev = SystemTable->ConOut;
521 break;
522 case STDERR_FILENO:
523 if(SystemTable->StdErr == NULL) {
524 Stream->Dev = SystemTable->ConOut;
525 }
526 else {
527 Stream->Dev = SystemTable->StdErr;
528 }
529 break;
530 default:
531 return RETURN_VOLUME_CORRUPTED; // This is a "should never happen" case.
532 }
533
534 Stream->Abstraction.fo_close = &da_ConClose;
535 Stream->Abstraction.fo_read = &da_ConRead;
536 Stream->Abstraction.fo_write = &da_ConWrite;
537 Stream->Abstraction.fo_stat = &da_ConStat;
538 Stream->Abstraction.fo_lseek = &da_ConSeek;
539 Stream->Abstraction.fo_fcntl = &fnullop_fcntl;
540 Stream->Abstraction.fo_ioctl = &da_ConIoctl;
541 Stream->Abstraction.fo_poll = &da_ConPoll;
542 Stream->Abstraction.fo_flush = &fnullop_flush;
543 Stream->Abstraction.fo_delete = &fbadop_delete;
544 Stream->Abstraction.fo_mkdir = &fbadop_mkdir;
545 Stream->Abstraction.fo_rmdir = &fbadop_rmdir;
546 Stream->Abstraction.fo_rename = &fbadop_rename;
547
548 Stream->NumRead = 0;
549 Stream->NumWritten = 0;
550 Stream->UnGetKey.ScanCode = SCAN_NULL;
551 Stream->UnGetKey.UnicodeChar = CHAR_NULL;
552
553 if(Stream->Dev == NULL) {
554 continue; // No device for this stream.
555 }
556 ConNode[i] = __DevRegister(stdioNames[i], NULL, &da_ConOpen, Stream, 1, sizeof(ConInstance), stdioFlags[i]);
557 if(ConNode[i] == NULL) {
558 Status = EFIerrno;
559 break;
560 }
561 Stream->Parent = ConNode[i];
562 }
563 /* Initialize Ioctl flags until Ioctl is really implemented. */
564 TtyCooked = TRUE;
565 TtyEcho = TRUE;
566
567 return Status;
568 }
569
570 RETURN_STATUS
571 EFIAPI
572 __Cons_deconstruct(
573 IN EFI_HANDLE ImageHandle,
574 IN EFI_SYSTEM_TABLE *SystemTable
575 )
576 {
577 int i;
578
579 for(i = 0; i < NUM_SPECIAL; ++i) {
580 if(ConNode[i] != NULL) {
581 FreePool(ConNode[i]);
582 }
583 }
584 if(ConInstanceList != NULL) {
585 FreePool(ConInstanceList);
586 }
587 if(ConReadBuf != NULL) {
588 FreePool(ConReadBuf);
589 }
590
591 return RETURN_SUCCESS;
592 }
593
594 /* ######################################################################### */
595 #if 0 /* Not implemented for Console */
596
597 static
598 int
599 EFIAPI
600 da_ConCntl(
601 struct __filedes *filp,
602 UINT32,
603 void *,
604 void *
605 )
606 {
607 }
608
609 static
610 int
611 EFIAPI
612 da_ConFlush(
613 struct __filedes *filp
614 )
615 {
616 return 0;
617 }
618 #endif /* Not implemented for Console */