1 /** @file
2   Basic serial IO abstraction for GDB
3 
4   Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
5 
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include <Uefi.h>
11 #include <Library/GdbSerialLib.h>
12 #include <Library/PcdLib.h>
13 #include <Library/IoLib.h>
14 #include <Library/DebugLib.h>
15 
16 
17 //---------------------------------------------
18 // UART Register Offsets
19 //---------------------------------------------
20 #define BAUD_LOW_OFFSET         0x00
21 #define BAUD_HIGH_OFFSET        0x01
22 #define IER_OFFSET              0x01
23 #define LCR_SHADOW_OFFSET       0x01
24 #define FCR_SHADOW_OFFSET       0x02
25 #define IR_CONTROL_OFFSET       0x02
26 #define FCR_OFFSET              0x02
27 #define EIR_OFFSET              0x02
28 #define BSR_OFFSET              0x03
29 #define LCR_OFFSET              0x03
30 #define MCR_OFFSET              0x04
31 #define LSR_OFFSET              0x05
32 #define MSR_OFFSET              0x06
33 
34 //---------------------------------------------
35 // UART Register Bit Defines
36 //---------------------------------------------
37 #define LSR_TXRDY               0x20U
38 #define LSR_RXDA                0x01U
39 #define DLAB                    0x01U
40 #define ENABLE_FIFO             0x01U
41 #define CLEAR_FIFOS             0x06U
42 
43 
44 
45 // IO Port Base for the UART
46 UINTN gPort;
47 
48 
49 /**
50   The constructor function initializes the UART.
51 
52   @param  ImageHandle   The firmware allocated handle for the EFI image.
53   @param  SystemTable   A pointer to the EFI System Table.
54 
55   @retval EFI_SUCCESS   The constructor always returns EFI_SUCCESS.
56 
57 **/
58 RETURN_STATUS
59 EFIAPI
GdbSerialLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)60 GdbSerialLibConstructor (
61   IN EFI_HANDLE        ImageHandle,
62   IN EFI_SYSTEM_TABLE  *SystemTable
63   )
64 {
65   UINT64    BaudRate;
66   UINT8     DataBits;
67   UINT8     Parity;
68   UINT8     StopBits;
69 
70   gPort = (UINTN)PcdGet32 (PcdGdbUartPort);
71 
72   BaudRate = PcdGet64 (PcdGdbBaudRate);
73   Parity   = PcdGet8 (PcdGdbParity);
74   DataBits = PcdGet8 (PcdGdbDataBits);
75   StopBits = PcdGet8 (PcdGdbStopBits);
76 
77   return GdbSerialInit (BaudRate, Parity, DataBits, StopBits);
78 }
79 
80 
81 
82 /**
83   Sets the baud rate, receive FIFO depth, transmit/receive time out, parity,
84   data buts, and stop bits on a serial device. This call is optional as the serial
85   port will be set up with defaults base on PCD values.
86 
87   @param  BaudRate         The requested baud rate. A BaudRate value of 0 will use the the
88                            device's default interface speed.
89   @param  Parity           The type of parity to use on this serial device. A Parity value of
90                            DefaultParity will use the device's default parity value.
91   @param  DataBits         The number of data bits to use on the serial device. A DataBits
92                            value of 0 will use the device's default data bit setting.
93   @param  StopBits         The number of stop bits to use on this serial device. A StopBits
94                            value of DefaultStopBits will use the device's default number of
95                            stop bits.
96 
97   @retval EFI_SUCCESS      The device was configured.
98   @retval EFI_DEVICE_ERROR The serial device could not be configured.
99 
100 **/
101 RETURN_STATUS
102 EFIAPI
GdbSerialInit(IN UINT64 BaudRate,IN UINT8 Parity,IN UINT8 DataBits,IN UINT8 StopBits)103 GdbSerialInit (
104   IN UINT64     BaudRate,
105   IN UINT8      Parity,
106   IN UINT8      DataBits,
107   IN UINT8      StopBits
108   )
109 {
110   UINTN           Divisor;
111   UINT8           OutputData;
112   UINT8           Data;
113   UINT8           BreakSet = 0;
114 
115   //
116   // We assume the UART has been turned on to decode gPort address range
117   //
118 
119   //
120   // Map 5..8 to 0..3
121   //
122   Data = (UINT8) (DataBits - (UINT8)5);
123 
124   //
125   // Calculate divisor for baud generator
126   //
127   Divisor = 115200/(UINTN)BaudRate;
128 
129   //
130   // Set communications format
131   //
132   OutputData = (UINT8)((DLAB << 7) | ((BreakSet << 6) | ((Parity << 3) | ((StopBits << 2) | Data))));
133   IoWrite8 (gPort + LCR_OFFSET, OutputData);
134 
135   //
136   // Configure baud rate
137   //
138   IoWrite8 (gPort + BAUD_HIGH_OFFSET, (UINT8)(Divisor >> 8));
139   IoWrite8 (gPort + BAUD_LOW_OFFSET, (UINT8)(Divisor & 0xff));
140 
141 
142   //
143   // Switch back to bank 0
144   //
145   OutputData = (UINT8)((~DLAB<<7)|((BreakSet<<6)|((Parity<<3)|((StopBits<<2)| Data))));
146   IoWrite8 (gPort + LCR_OFFSET, OutputData);
147 
148   // Not sure this is the right place to enable the FIFOs....
149   // We probably need the FIFO enabled to not drop input
150   IoWrite8 (gPort + FCR_SHADOW_OFFSET, ENABLE_FIFO);
151 
152 
153   // Configure the UART hardware here
154   return RETURN_SUCCESS;
155 }
156 
157 
158 /**
159   Check to see if a character is available from GDB. Do not read the character as that is
160   done via GdbGetChar().
161 
162   @return TRUE  - Character available
163   @return FALSE - Character not available
164 
165 **/
166 BOOLEAN
167 EFIAPI
GdbIsCharAvailable(VOID)168 GdbIsCharAvailable (
169   VOID
170   )
171 {
172   UINT8   Data;
173 
174   Data = IoRead8 (gPort + LSR_OFFSET);
175 
176   return ((Data & LSR_RXDA) == LSR_RXDA);
177 }
178 
179 
180 /**
181   Get a character from GDB. This function must be able to run in interrupt context.
182 
183   @return A character from GDB
184 
185 **/
186 CHAR8
187 EFIAPI
GdbGetChar(VOID)188 GdbGetChar (
189   VOID
190   )
191 {
192   UINT8   Data;
193   CHAR8   Char;
194 
195   // Wait for the serial port to be ready
196   do {
197     Data = IoRead8 (gPort + LSR_OFFSET);
198   } while ((Data & LSR_RXDA) == 0);
199 
200   Char = IoRead8 (gPort);
201 
202   // Make this an EFI_D_INFO after we get everything debugged.
203   DEBUG ((EFI_D_ERROR, "<%c<", Char));
204   return Char;
205 }
206 
207 
208 /**
209   Send a character to GDB. This function must be able to run in interrupt context.
210 
211 
212   @param  Char    Send a character to GDB
213 
214 **/
215 
216 VOID
217 EFIAPI
GdbPutChar(IN CHAR8 Char)218 GdbPutChar (
219   IN  CHAR8   Char
220   )
221 {
222   UINT8   Data;
223 
224   // Make this an EFI_D_INFO after we get everything debugged.
225   DEBUG ((EFI_D_ERROR, ">%c>", Char));
226 
227   // Wait for the serial port to be ready
228   do {
229     Data = IoRead8 (gPort + LSR_OFFSET);
230   } while ((Data & LSR_TXRDY) == 0);
231 
232   IoWrite8 (gPort, Char);
233 }
234 
235 /**
236   Send an ASCII string to GDB. This function must be able to run in interrupt context.
237 
238 
239   @param  String    Send a string to GDB
240 
241 **/
242 
243 VOID
GdbPutString(IN CHAR8 * String)244 GdbPutString (
245   IN CHAR8  *String
246   )
247 {
248   while (*String != '\0') {
249     GdbPutChar (*String);
250     String++;
251   }
252 }
253 
254 
255 
256 
257