/** @file Basic serial IO abstaction for GDB Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include <Uefi.h> #include <Library/GdbSerialLib.h> #include <Library/PcdLib.h> #include <Library/IoLib.h> #include <Library/DebugLib.h> //--------------------------------------------- // UART Register Offsets //--------------------------------------------- #define BAUD_LOW_OFFSET 0x00 #define BAUD_HIGH_OFFSET 0x01 #define IER_OFFSET 0x01 #define LCR_SHADOW_OFFSET 0x01 #define FCR_SHADOW_OFFSET 0x02 #define IR_CONTROL_OFFSET 0x02 #define FCR_OFFSET 0x02 #define EIR_OFFSET 0x02 #define BSR_OFFSET 0x03 #define LCR_OFFSET 0x03 #define MCR_OFFSET 0x04 #define LSR_OFFSET 0x05 #define MSR_OFFSET 0x06 //--------------------------------------------- // UART Register Bit Defines //--------------------------------------------- #define LSR_TXRDY 0x20 #define LSR_RXDA 0x01 #define DLAB 0x01 #define ENABLE_FIFO 0x01 #define CLEAR_FIFOS 0x06 // IO Port Base for the UART UINTN gPort; /** The constructor function initializes the UART. @param ImageHandle The firmware allocated handle for the EFI image. @param SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. **/ RETURN_STATUS EFIAPI GdbSerialLibConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { UINT64 BaudRate; UINT8 DataBits; UINT8 Parity; UINT8 StopBits; gPort = (UINTN)PcdGet32 (PcdGdbUartPort); BaudRate = PcdGet64 (PcdGdbBaudRate); Parity = PcdGet8 (PcdGdbParity); DataBits = PcdGet8 (PcdGdbDataBits); StopBits = PcdGet8 (PcdGdbStopBits); return GdbSerialInit (BaudRate, Parity, DataBits, StopBits); } /** Sets the baud rate, receive FIFO depth, transmit/receice time out, parity, data buts, and stop bits on a serial device. This call is optional as the serial port will be set up with defaults base on PCD values. @param BaudRate The requested baud rate. A BaudRate value of 0 will use the the device's default interface speed. @param Parity The type of parity to use on this serial device. A Parity value of DefaultParity will use the device's default parity value. @param DataBits The number of data bits to use on the serial device. A DataBits vaule of 0 will use the device's default data bit setting. @param StopBits The number of stop bits to use on this serial device. A StopBits value of DefaultStopBits will use the device's default number of stop bits. @retval EFI_SUCCESS The device was configured. @retval EFI_DEVICE_ERROR The serial device could not be coonfigured. **/ RETURN_STATUS EFIAPI GdbSerialInit ( IN UINT64 BaudRate, IN UINT8 Parity, IN UINT8 DataBits, IN UINT8 StopBits ) { UINTN Divisor; UINT8 OutputData; UINT8 Data; UINT8 BreakSet = 0; // // We assume the UART has been turned on to decode gPort address range // // // Map 5..8 to 0..3 // Data = (UINT8) (DataBits - (UINT8)5); // // Calculate divisor for baud generator // Divisor = 115200/(UINTN)BaudRate; // // Set communications format // OutputData = (UINT8)((DLAB << 7) | ((BreakSet << 6) | ((Parity << 3) | ((StopBits << 2) | Data)))); IoWrite8 (gPort + LCR_OFFSET, OutputData); // // Configure baud rate // IoWrite8 (gPort + BAUD_HIGH_OFFSET, (UINT8)(Divisor >> 8)); IoWrite8 (gPort + BAUD_LOW_OFFSET, (UINT8)(Divisor & 0xff)); // // Switch back to bank 0 // OutputData = (UINT8)((~DLAB<<7)|((BreakSet<<6)|((Parity<<3)|((StopBits<<2)| Data)))); IoWrite8 (gPort + LCR_OFFSET, OutputData); // Not sure this is the right place to enable the FIFOs.... // We probably need the FIFO enabled to not drop input IoWrite8 (gPort + FCR_SHADOW_OFFSET, ENABLE_FIFO); // Configure the UART hardware here return RETURN_SUCCESS; } /** Check to see if a character is available from GDB. Do not read the character as that is done via GdbGetChar(). @return TRUE - Character available @return FALSE - Character not available **/ BOOLEAN EFIAPI GdbIsCharAvailable ( VOID ) { UINT8 Data; Data = IoRead8 (gPort + LSR_OFFSET); return ((Data & LSR_RXDA) == LSR_RXDA); } /** Get a character from GDB. This function must be able to run in interrupt context. @return A character from GDB **/ CHAR8 EFIAPI GdbGetChar ( VOID ) { UINT8 Data; CHAR8 Char; // Wait for the serial port to be ready do { Data = IoRead8 (gPort + LSR_OFFSET); } while ((Data & LSR_RXDA) == 0); Char = IoRead8 (gPort); // Make this an EFI_D_INFO after we get everything debugged. DEBUG ((EFI_D_ERROR, "<%c<", Char)); return Char; } /** Send a character to GDB. This function must be able to run in interrupt context. @param Char Send a character to GDB **/ VOID EFIAPI GdbPutChar ( IN CHAR8 Char ) { UINT8 Data; // Make this an EFI_D_INFO after we get everything debugged. DEBUG ((EFI_D_ERROR, ">%c>", Char)); // Wait for the serial port to be ready do { Data = IoRead8 (gPort + LSR_OFFSET); } while ((Data & LSR_TXRDY) == 0); IoWrite8 (gPort, Char); } /** Send an ASCII string to GDB. This function must be able to run in interrupt context. @param String Send a string to GDB **/ VOID GdbPutString ( IN CHAR8 *String ) { while (*String != '\0') { GdbPutChar (*String); String++; } }