summaryrefslogtreecommitdiffstats
path: root/EmbeddedPkg/Library/GdbSerialLib/GdbSerialLib.c
blob: 088e23e0d89f41e163a6b5cb3add5d8b2c1af670 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/** @file
  Basic serial IO abstraction for GDB

  Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#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               0x20U
#define LSR_RXDA                0x01U
#define DLAB                    0x01U
#define ENABLE_FIFO             0x01U
#define CLEAR_FIFOS             0x06U



// 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/receive 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
                           value 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 configured.

**/
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++;
  }
}