2 Abstract device driver for the UEFI Console.
4 Manipulates abstractions for stdin, stdout, stderr.
6 This device is a WIDE device and this driver returns WIDE
7 characters. It this the responsibility of the caller to convert between
8 narrow and wide characters in order to perform the desired operations.
10 The devices status as a wide device is indicatd by _S_IWTTY being set in
13 Copyright (c) 2016, Daryl McDaniel. All rights reserved.<BR>
14 Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
15 This program and the accompanying materials are licensed and made available under
16 the terms and conditions of the BSD License that accompanies this distribution.
17 The full text of the license may be found at
18 http://opensource.org/licenses/bsd-license.php.
20 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
21 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
25 #include <Library/BaseLib.h>
26 #include <Library/MemoryAllocationLib.h>
27 #include <Library/UefiBootServicesTableLib.h>
28 #include <Library/DebugLib.h>
29 #include <Protocol/SimpleTextIn.h>
30 #include <Protocol/SimpleTextOut.h>
32 #include <LibConfig.h>
38 #include <sys/fcntl.h>
40 #include <sys/termios.h>
41 #include <Efi/SysEfi.h>
43 #include <Device/Device.h>
44 #include <Device/IIO.h>
47 static const CHAR16
* const
48 stdioNames
[NUM_SPECIAL
] = {
49 L
"stdin:", L
"stdout:", L
"stderr:"
52 static const int stdioFlags
[NUM_SPECIAL
] = {
58 static DeviceNode
*ConNode
[NUM_SPECIAL
];
59 static ConInstance
*ConInstanceList
;
63 /* Flags settable by Ioctl */
64 static BOOLEAN TtyCooked
;
65 static BOOLEAN TtyEcho
;
67 /** Convert string from MBCS to WCS and translate \n to \r\n.
69 It is the caller's responsibility to ensure that dest is
70 large enough to hold the converted results. It is guaranteed
71 that there will be fewer than n characters placed in dest.
73 @param[out] dest WCS buffer to receive the converted string.
74 @param[in] buf MBCS string to convert to WCS.
75 @param[in] n Number of BYTES contained in buf.
76 @param[in,out] Cs Pointer to the character state object for this stream
78 @return The number of BYTES consumed from buf.
81 WideTtyCvt( CHAR16
*dest
, const char *buf
, ssize_t n
, mbstate_t *Cs
)
88 numB
= (int)mbrtowc(wc
, buf
, MIN(MB_LEN_MAX
,n
), Cs
);
92 if(numB
< 0) { // If an unconvertable character, replace it.
93 wc
[0] = BLOCKELEMENT_LIGHT_SHADE
;
100 *dest
++ = (CHAR16
)wc
[0];
109 /** Position the console cursor to the coordinates specified by Position.
111 @param[in] filp Pointer to the file descriptor structure for this file.
112 @param[in] Position A value containing the target X and Y coordinates.
113 @param[in] whence Ignored by the Console device.
115 @retval Position Success. Returns a copy of the Position argument.
116 @retval -1 filp is not associated with a valid console stream.
117 @retval -1 This console stream is attached to stdin.
118 @retval -1 The SetCursorPosition operation failed.
124 struct __filedes
*filp
,
126 int whence
///< Ignored by Console
130 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
133 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
134 // Quick check to see if Stream looks reasonable
135 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
136 EFIerrno
= RETURN_INVALID_PARAMETER
;
137 return -1; // Looks like a bad This pointer
139 if(Stream
->InstanceNum
== STDIN_FILENO
) {
140 // Seek is not valid for stdin
141 EFIerrno
= RETURN_UNSUPPORTED
;
144 // Everything is OK to do the final verification and "seek".
145 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
146 CursorPos
.Offset
= Position
;
148 EFIerrno
= Proto
->SetCursorPosition(Proto
,
149 (INTN
)CursorPos
.XYpos
.Column
,
150 (INTN
)CursorPos
.XYpos
.Row
);
152 if(RETURN_ERROR(EFIerrno
)) {
160 /* Write a NULL terminated WCS to the EFI console.
162 NOTE: The UEFI Console is a wide device, _S_IWTTY, so characters received
163 by da_ConWrite are WIDE characters. It is the responsibility of the
164 higher-level function(s) to perform any necessary conversions.
166 @param[in,out] BufferSize Number of characters in Buffer.
167 @param[in] Buffer The WCS string to be displayed
169 @return The number of Characters written.
175 IN
struct __filedes
*filp
,
177 IN
size_t BufferSize
,
178 IN
const void *Buffer
182 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
188 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
189 // Quick check to see if Stream looks reasonable
190 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
191 EFIerrno
= RETURN_INVALID_PARAMETER
;
192 return -1; // Looks like a bad This pointer
194 if(Stream
->InstanceNum
== STDIN_FILENO
) {
195 // Write is not valid for stdin
196 EFIerrno
= RETURN_UNSUPPORTED
;
199 // Everything is OK to do the write.
200 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
202 Status
= EFI_SUCCESS
;
203 if(Position
!= NULL
) {
204 CursorPos
.Offset
= *Position
;
206 Status
= Proto
->SetCursorPosition(Proto
,
207 (INTN
)CursorPos
.XYpos
.Column
,
208 (INTN
)CursorPos
.XYpos
.Row
);
211 if(!RETURN_ERROR(Status
)) {
212 // Send the Unicode buffer to the console
213 Status
= Proto
->OutputString( Proto
, (CHAR16
*)Buffer
);
216 // Depending on status, update BufferSize and return
217 if(!RETURN_ERROR(Status
)) {
218 NumChar
= BufferSize
;
219 Stream
->NumWritten
+= NumChar
;
221 EFIerrno
= Status
; // Make error reason available to caller
225 /** Read a wide character from the console input device.
227 Returns NUL or a translated input character.
229 @param[in] filp Pointer to file descriptor for this file.
230 @param[out] Buffer Buffer in which to place the read character.
232 @retval EFI_DEVICE_ERROR A hardware error has occurred.
233 @retval EFI_NOT_READY No data is available. Try again later.
234 @retval EFI_SUCCESS One wide character has been placed in Character
235 - 0x0000 NUL, ignore this
236 - Otherwise, should be a good wide character in Character
241 IN OUT
struct __filedes
*filp
,
242 OUT
wchar_t *Character
245 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
249 EFI_INPUT_KEY Key
= {0,0};
252 Self
= (cIIO
*)filp
->devdata
;
253 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
254 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
256 if(Stream
->UnGetKey
== CHAR_NULL
) {
257 Status
= Proto
->ReadKeyStroke(Proto
, &Key
);
260 Status
= EFI_SUCCESS
;
261 // Use the data in the Un-get buffer
262 // Guaranteed that ScanCode and UnicodeChar are not both NUL
263 Key
.ScanCode
= SCAN_NULL
;
264 Key
.UnicodeChar
= Stream
->UnGetKey
;
265 Stream
->UnGetKey
= CHAR_NULL
;
267 if(Status
== EFI_SUCCESS
) {
268 // Translate the Escape Scan Code to an ESC character
269 if (Key
.ScanCode
!= 0) {
270 if (Key
.ScanCode
== SCAN_ESC
) {
273 else if((Self
->Termio
.c_iflag
& IGNSPEC
) != 0) {
274 // If we are ignoring special characters, return a NUL
278 // Must be a control, function, or other non-printable key.
279 // Map it into the Platform portion of the Unicode private use area
280 RetChar
= TtyFunKeyMax
- Key
.ScanCode
;
284 RetChar
= Key
.UnicodeChar
;
286 *Character
= RetChar
;
294 /** Read a wide character from the console input device.
296 NOTE: The UEFI Console is a wide device, _S_IWTTY, so characters returned
297 by da_ConRead are WIDE characters. It is the responsibility of the
298 higher-level function(s) to perform any necessary conversions.
300 A NUL character, 0x0000, is never returned. In the event that such a character
301 is encountered, the read is either retried or -1 is returned with errno set
304 @param[in] filp Pointer to file descriptor for this file.
305 @param[in] offset Ignored.
306 @param[in] BufferSize Buffer size, in bytes.
307 @param[out] Buffer Buffer in which to place the read characters.
309 @retval -1 An error has occurred. Reason in errno and EFIerrno.
310 @retval -1 No data is available. errno is set to EAGAIN
311 @retval 1 One wide character has been placed in Buffer
317 IN OUT
struct __filedes
*filp
,
318 IN OUT off_t
*offset
, // Console ignores this
319 IN
size_t BufferSize
,
323 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*Proto
;
329 BOOLEAN BlockingMode
;
333 if(BufferSize
< sizeof(wchar_t)) {
334 errno
= EINVAL
; // Buffer is too small to hold one character
337 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
338 Proto
= (EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*)Stream
->Dev
;
339 BlockingMode
= (BOOLEAN
)((filp
->Oflags
& O_NONBLOCK
) == 0);
342 Status
= EFI_SUCCESS
;
344 // Read a byte in Blocking mode
345 Status
= gBS
->WaitForEvent( 1, &Proto
->WaitForKey
, &Edex
);
348 /* WaitForEvent should not be able to fail since
349 NumberOfEvents is set to constant 1 so is never 0
350 Event is set by the Simple Text Input protocol so should never be EVT_NOTIFY_SIGNAL
351 Current TPL should be TPL_APPLICATION.
352 ASSERT so that we catch any problems during development.
354 ASSERT(Status
== EFI_SUCCESS
);
356 Status
= da_ConRawRead (filp
, &RetChar
);
357 } while ( BlockingMode
&&
359 (Status
!= EFI_DEVICE_ERROR
));
362 if(Status
== EFI_SUCCESS
) {
364 NumRead
= 1; // Indicate that Key holds the data
366 else if(Status
== EFI_NOT_READY
) {
367 // Keystroke data is not available
379 *((wchar_t *)Buffer
) = RetChar
;
385 /** Console-specific helper function for the fstat() function.
387 st_size Set to number of characters read for stdin and number written for stdout and stderr.
388 st_physsize 1 for stdin, 0 if QueryMode error, else max X and Y coordinates for the current mode.
389 st_curpos 0 for stdin, current X & Y coordinates for stdout and stderr
390 st_blksize Set to 1 since this is a character device
392 All other members of the stat structure are left unchanged.
394 @param[in] filp Pointer to file descriptor for this file.
395 @param[out] Buffer Pointer to a stat structure to receive the information.
396 @param[in,out] Something Ignored.
398 @retval 0 Successful completion.
399 @retval -1 Either filp is not associated with a console stream, or
400 Buffer is NULL. errno is set to EINVAL.
406 struct __filedes
*filp
,
412 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*Proto
;
419 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
420 // Quick check to see if Stream looks reasonable
421 if ((Stream
->Cookie
!= CON_COOKIE
) || // Cookie == 'IoAb'
425 EFIerrno
= RETURN_INVALID_PARAMETER
;
428 // All of our parameters are correct, so fill in the information.
429 Buffer
->st_blksize
= 0; // Character device, not a block device
430 Buffer
->st_mode
= filp
->f_iflags
;
433 if(Stream
->InstanceNum
== STDIN_FILENO
) {
435 Buffer
->st_curpos
= 0;
436 Buffer
->st_size
= (off_t
)Stream
->NumRead
;
437 Buffer
->st_physsize
= 1;
440 Proto
= (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*)Stream
->Dev
;
441 CursorPos
.XYpos
.Column
= (UINT32
)Proto
->Mode
->CursorColumn
;
442 CursorPos
.XYpos
.Row
= (UINT32
)Proto
->Mode
->CursorRow
;
443 Buffer
->st_curpos
= (off_t
)CursorPos
.Offset
;
444 Buffer
->st_size
= (off_t
)Stream
->NumWritten
;
446 OutMode
= Proto
->Mode
->Mode
;
447 EFIerrno
= Proto
->QueryMode(Proto
, (UINTN
)OutMode
, &ModeCol
, &ModeRow
);
448 if(RETURN_ERROR(EFIerrno
)) {
449 Buffer
->st_physsize
= 0;
452 CursorPos
.XYpos
.Column
= (UINT32
)ModeCol
;
453 CursorPos
.XYpos
.Row
= (UINT32
)ModeRow
;
454 Buffer
->st_physsize
= (off_t
)CursorPos
.Offset
;
460 /** Console-specific helper for the ioctl system call.
462 The console device does not directly participate in ioctl operations.
463 This function completes the device abstraction and returns an error value
464 to indicate that the function is not supported for this device.
466 @retval -1 Function is not supported for this device.
472 struct __filedes
*filp
,
481 /** Open an abstract Console Device.
483 @param[in] DevNode Pointer to the Device control structure for this stream.
484 @param[in] filp Pointer to the new file control structure for this stream.
485 @param[in] DevInstance Not used for the console device.
486 @param[in] Path Not used for the console device.
487 @param[in] MPath Not used for the console device.
489 @retval 0 This console stream has been successfully opened.
490 @retval -1 The DevNode or filp pointer is NULL.
491 @retval -1 DevNode does not point to a valid console stream device.
497 struct __filedes
*filp
,
498 int DevInstance
, // Not used for console devices
499 wchar_t *Path
, // Not used for console devices
500 wchar_t *MPath
// Not used for console devices
510 Stream
= (ConInstance
*)DevNode
->InstanceList
;
511 // Quick check to see if Stream looks reasonable
512 if(Stream
->Cookie
== CON_COOKIE
)
514 Instance
= Stream
->InstanceNum
;
515 if(Instance
< NUM_SPECIAL
) {
516 gMD
->StdIo
[Instance
] = Stream
;
517 filp
->f_iflags
|= (_S_IFCHR
| _S_ITTY
| _S_IWTTY
| _S_ICONSOLE
);
519 filp
->f_ops
= &Stream
->Abstraction
;
520 filp
->devdata
= (void *)IIO
;
526 EFIerrno
= RETURN_INVALID_PARAMETER
;
533 /** Flush a console device's IIO buffers.
535 Flush the IIO Input or Output buffers associated with the specified file.
537 If the console is open for output, write any unwritten data in the associated
538 output buffer (stdout or stderr) to the console.
540 If the console is open for input, discard any remaining data
543 @param[in] filp Pointer to the target file's descriptor structure.
545 @retval 0 Always succeeds
551 struct __filedes
*filp
559 if(filp
->MyFD
== STDERR_FILENO
) {
560 OutBuf
= IIO
->ErrBuf
;
563 OutBuf
= IIO
->OutBuf
;
566 Flags
= filp
->Oflags
& O_ACCMODE
; // Get the device's open mode
567 if (Flags
!= O_WRONLY
) { // (Flags == O_RDONLY) || (Flags == O_RDWR)
568 // Readable so discard the contents of the input buffer
569 IIO
->InBuf
->Flush(IIO
->InBuf
, UNICODE_STRING_MAX
);
571 if (Flags
!= O_RDONLY
) { // (Flags == O_WRONLY) || (Flags == O_RDWR)
572 // Writable so flush the output buffer
573 // At this point, the characters to write are in OutBuf
574 // First, linearize and consume the buffer
575 NumProc
= OutBuf
->Read(OutBuf
, gMD
->UString
, UNICODE_STRING_MAX
-1);
576 if (NumProc
> 0) { // Optimization -- Nothing to do if no characters
577 gMD
->UString
[NumProc
] = 0; // Ensure that the buffer is terminated
579 /* OutBuf always contains wide characters.
580 The UEFI Console (this device) always expects wide characters.
581 There is no need to handle devices that expect narrow characters
582 like the device-independent functions do.
584 // Do the actual write of the data to the console
585 (void) da_ConWrite(filp
, NULL
, NumProc
, gMD
->UString
);
586 // Paranoia -- Make absolutely sure that OutBuf is empty in case fo_write
587 // wasn't able to consume everything.
588 OutBuf
->Flush(OutBuf
, UNICODE_STRING_MAX
);
594 /** Close an open file.
596 @param[in] filp Pointer to the file descriptor structure for this file.
598 @retval 0 The file has been successfully closed.
599 @retval -1 filp does not point to a valid console descriptor.
605 IN
struct __filedes
*filp
610 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
611 // Quick check to see if Stream looks reasonable
612 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
614 EFIerrno
= RETURN_INVALID_PARAMETER
;
615 return -1; // Looks like a bad File Descriptor pointer
617 // Stream and filp look OK, so continue.
618 // Flush the I/O buffers
619 (void) da_ConFlush(filp
);
621 // Break the connection to IIO
622 filp
->devdata
= NULL
;
624 gMD
->StdIo
[Stream
->InstanceNum
] = NULL
; // Mark the stream as closed
628 #include <sys/poll.h>
629 /* Returns a bit mask describing which operations could be completed immediately.
631 Testable Events for this device are:
632 (POLLIN | POLLRDNORM) A Unicode character is available to read
633 (POLLIN) A ScanCode is ready.
634 (POLLOUT) The device is ready for output - always set on stdout and stderr.
636 Non-testable Events which are only valid in return values are:
637 POLLERR The specified device is not one of stdin, stdout, or stderr.
638 POLLHUP The specified stream has been disconnected
639 POLLNVAL da_ConPoll was called with an invalid parameter.
641 NOTE: The "Events" handled by this function are not UEFI events.
643 @param[in] filp Pointer to the file control structure for this stream.
644 @param[in] events A bit mask identifying the events to be examined
647 @return Returns a bit mask comprised of both testable and non-testable
648 event codes indicating both the state of the operation and the
649 status of the device.
655 struct __filedes
*filp
,
660 EFI_STATUS Status
= RETURN_SUCCESS
;
663 Stream
= BASE_CR(filp
->f_ops
, ConInstance
, Abstraction
);
664 // Quick check to see if Stream looks reasonable
665 if(Stream
->Cookie
!= CON_COOKIE
) { // Cookie == 'IoAb'
667 EFIerrno
= RETURN_INVALID_PARAMETER
;
668 return POLLNVAL
; // Looks like a bad filp pointer
670 if(Stream
->InstanceNum
== 0) {
671 // STDIN: Only input is supported for this device
672 Status
= da_ConRawRead (filp
, &Stream
->UnGetKey
);
673 if(Status
== RETURN_SUCCESS
) {
675 if ((Stream
->UnGetKey
< TtyFunKeyMin
) ||
676 (Stream
->UnGetKey
>= TtyFunKeyMax
))
678 RdyMask
|= POLLRDNORM
;
682 Stream
->UnGetKey
= CHAR_NULL
;
685 else if(Stream
->InstanceNum
< NUM_SPECIAL
) { // Not 0, is it 1 or 2?
686 // (STDOUT || STDERR): Only output is supported for this device
690 RdyMask
= POLLERR
; // Not one of the standard streams
694 return (RdyMask
& (events
| POLL_RETONLY
));
697 /** Construct the Console stream devices: stdin, stdout, stderr.
699 Allocate the instance structure and populate it with the information for
705 IN EFI_HANDLE ImageHandle
,
706 IN EFI_SYSTEM_TABLE
*SystemTable
710 RETURN_STATUS Status
;
713 Status
= RETURN_OUT_OF_RESOURCES
;
714 ConInstanceList
= (ConInstance
*)AllocateZeroPool(NUM_SPECIAL
* sizeof(ConInstance
));
715 if(ConInstanceList
!= NULL
) {
718 FreePool(ConInstanceList
);
721 Status
= RETURN_SUCCESS
;
722 for( i
= 0; i
< NUM_SPECIAL
; ++i
) {
723 // Get pointer to instance.
724 Stream
= &ConInstanceList
[i
];
726 Stream
->Cookie
= CON_COOKIE
;
727 Stream
->InstanceNum
= i
;
728 Stream
->CharState
.A
= 0; // Start in the initial state
732 Stream
->Dev
= SystemTable
->ConIn
;
735 Stream
->Dev
= SystemTable
->ConOut
;
738 if(SystemTable
->StdErr
== NULL
) {
739 Stream
->Dev
= SystemTable
->ConOut
;
742 Stream
->Dev
= SystemTable
->StdErr
;
746 return RETURN_VOLUME_CORRUPTED
; // This is a "should never happen" case.
749 Stream
->Abstraction
.fo_close
= &da_ConClose
;
750 Stream
->Abstraction
.fo_read
= &da_ConRead
;
751 Stream
->Abstraction
.fo_write
= &da_ConWrite
;
752 Stream
->Abstraction
.fo_stat
= &da_ConStat
;
753 Stream
->Abstraction
.fo_lseek
= &da_ConSeek
;
754 Stream
->Abstraction
.fo_fcntl
= &fnullop_fcntl
;
755 Stream
->Abstraction
.fo_ioctl
= &da_ConIoctl
;
756 Stream
->Abstraction
.fo_poll
= &da_ConPoll
;
757 Stream
->Abstraction
.fo_flush
= &da_ConFlush
;
758 Stream
->Abstraction
.fo_delete
= &fbadop_delete
;
759 Stream
->Abstraction
.fo_mkdir
= &fbadop_mkdir
;
760 Stream
->Abstraction
.fo_rmdir
= &fbadop_rmdir
;
761 Stream
->Abstraction
.fo_rename
= &fbadop_rename
;
764 Stream
->NumWritten
= 0;
765 Stream
->UnGetKey
= CHAR_NULL
;
767 if(Stream
->Dev
== NULL
) {
768 continue; // No device for this stream.
770 ConNode
[i
] = __DevRegister(stdioNames
[i
], NULL
, &da_ConOpen
, Stream
,
771 1, sizeof(ConInstance
), stdioFlags
[i
]);
772 if(ConNode
[i
] == NULL
) {
773 Status
= EFIerrno
; // Grab error code that DevRegister produced.
776 Stream
->Parent
= ConNode
[i
];
778 /* Initialize Ioctl flags until Ioctl is really implemented. */
789 IN EFI_HANDLE ImageHandle
,
790 IN EFI_SYSTEM_TABLE
*SystemTable
795 for(i
= 0; i
< NUM_SPECIAL
; ++i
) {
796 if(ConNode
[i
] != NULL
) {
797 FreePool(ConNode
[i
]);
800 if(ConInstanceList
!= NULL
) {
801 FreePool(ConInstanceList
);
808 return RETURN_SUCCESS
;
811 /* ######################################################################### */
812 #if 0 /* Not implemented (yet?) for Console */
818 struct __filedes
*filp
,
825 #endif /* Not implemented for Console */