summaryrefslogtreecommitdiffstats
path: root/EmbeddedPkg/GdbStub/SerialIo.c
blob: 5afaed06b9fccfe0c3e7239040b081881b44498b (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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
/** @file
  Serial IO Abstraction for GDB stub. This allows an EFI consoles that shows up on the system 
  running GDB. One consle for error information and another console for user input/output.
  
  Basic packet format is $packet-data#checksum. So every comand has 4 bytes of overhead: $, 
  #, 0, 0. The 0 and 0 are the ascii characters for the checksum. 
  

  Copyright (c) 2008-2009, Apple Inc. All rights reserved.
  
  All rights reserved. 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 <GdbStubInternal.h>

//
// Set TRUE if F Reply package signals a ctrl-c. We can not process the Ctrl-c
// here we need to wait for the periodic callback to do this. 
//
BOOLEAN gCtrlCBreakFlag = FALSE;

//
// If the periodic callback is called while we are processing an F packet we need
// to let the callback know to not read from the serail stream as it could steal
// characters from the F reponse packet
//
BOOLEAN gProcessingFPacket = FALSE;

/**
  Process a control-C break message. 
  
  Currently a place holder, remove the ASSERT when it gets implemented. 

  @param  ErrNo   Error infomration from the F reply packet or other source

**/

VOID
GdbCtrlCBreakMessage (
  IN  UINTN ErrNo
  )
{
  // See D.10.5 of gdb.pdf
  // This should look like a break message. Should look like SIGINT

  /* TODO: Make sure if we should do anything with ErrNo */
  //Turn on the global Ctrl-C flag.
  gCtrlCBreakFlag = TRUE;
}


/**
  Parse the F reply packet and extract the return value and an ErrNo if it exists.

  @param  Packet  Packet to parse like an F reply packet
  @param  ErrNo   Buffer to hold Count bytes that were read

  @retval -1      Error, not a valid F reply packet 
  @retval other   Return the return code from the F reply packet 

**/
INTN
GdbParseFReplyPacket (
  IN  CHAR8   *Packet,
  OUT UINTN   *ErrNo   
  )
{
  INTN   RetCode;
   
  if (Packet[0] != 'F') {
    // A valid responce would be an F packet
    return -1;
  }
  
  RetCode = AsciiStrHexToUintn (&Packet[1]);
  
  // Find 1st comma 
  for (;*Packet != '\0' && *Packet != ',';  Packet++);  
  if (*Packet == '\0') {
    *ErrNo = 0;
    return RetCode;
  }
  
  *ErrNo = AsciiStrHexToUintn (++Packet);

  // Find 2nd comma 
  for (;*Packet != '\0' && *Packet != ',';  Packet++);  
  if (*Packet == '\0') {
    return RetCode;
  }
  
  if (*(++Packet) == 'C') {
    GdbCtrlCBreakMessage (*ErrNo);  
  }
  
  return RetCode;
}


/**
  Read data from a FileDescriptor. On success number of bytes read is returned. Zero indicates 
  the end of a file. On error -1 is returned. If count is zero, GdbRead returns zero.

  @param  FileDescriptor   Device to talk to.
  @param  Buffer           Buffer to hold Count bytes that were read
  @param  Count            Number of bytes to transfer. 

  @retval -1               Error
  @retval {other}          Number of bytes read.

**/
INTN
GdbRead (
  IN  INTN    FileDescriptor,
  OUT VOID    *Buffer,
  IN  UINTN   Count
  )
{
  CHAR8   Packet[128];
  UINTN   Size;
  INTN    RetCode;
  UINTN   ErrNo;
  BOOLEAN ReceiveDone = FALSE;
   
  // Send:
  // "Fread,XX,YYYYYYYY,XX
  //
  // XX - FileDescriptor in ASCII
  // YYYYYYYY - Buffer address in ASCII 
  // XX - Count in ASCII
  // SS - check sum
  //
  Size = AsciiSPrint (Packet, sizeof (Packet), "Fread,%x,%x,%x", FileDescriptor, Buffer, Count);
  // Packet array is too small if you got this ASSERT
  ASSERT (Size < sizeof (Packet));
  
  gProcessingFPacket = TRUE;
  SendPacket (Packet);
  Print ((CHAR16 *)L"Packet sent..\n");

  do {
    // Reply:
    ReceivePacket (Packet, sizeof (Packet));
    Print ((CHAR16 *)L"Command received..%c\n", Packet[0]);

    // Process GDB commands
    switch (Packet[0]) {
      //Write memory command.
      //M addr,length:XX...
      case    'M':
        WriteToMemory (Packet);
        break;

      //Fretcode, errno, Ctrl-C flag
      //retcode - Count read
      case    'F':
        //Once target receives F reply packet that means the previous
        //transactions are finished.
        ReceiveDone = TRUE;
        break;

      //Send empty buffer
      default    :
        SendNotSupported();
        break;
    }
  } while (ReceiveDone == FALSE);

  RetCode = GdbParseFReplyPacket (Packet, &ErrNo);
  Print ((CHAR16 *)L"RetCode: %x..ErrNo: %x..\n", RetCode, ErrNo);
  
  if (ErrNo > 0) {
    //Send error to the host if there is any.
    SendError ((UINT8)ErrNo);
  }
  
  gProcessingFPacket = FALSE;

  return RetCode;
}  
  

/**
  Write data to a FileDescriptor. On success number of bytes written is returned. Zero indicates 
  nothing was written. On error -1 is returned. 

  @param  FileDescriptor   Device to talk to.
  @param  Buffer           Buffer to hold Count bytes that are to be written
  @param  Count            Number of bytes to transfer. 

  @retval -1               Error
  @retval {other}          Number of bytes written.

**/
INTN
GdbWrite (
  IN  INTN          FileDescriptor,
  OUT CONST VOID    *Buffer,
  IN  UINTN         Count
  )
{
  CHAR8   Packet[128];
  UINTN   Size;
  INTN    RetCode;
  UINTN   ErrNo;
  BOOLEAN ReceiveDone = FALSE;

  // Send:
  // #Fwrite,XX,YYYYYYYY,XX$SS
  //
  // XX - FileDescriptor in ASCII
  // YYYYYYYY - Buffer address in ASCII 
  // XX - Count in ASCII
  // SS - check sum
  //
  Size = AsciiSPrint (Packet, sizeof (Packet), "Fwrite,%x,%x,%x", FileDescriptor, Buffer, Count);
  // Packet array is too small if you got this ASSERT
  ASSERT (Size < sizeof (Packet));
  
  SendPacket (Packet);
  Print ((CHAR16 *)L"Packet sent..\n");

  do {
    // Reply:
    ReceivePacket (Packet, sizeof (Packet));
    Print ((CHAR16 *)L"Command received..%c\n", Packet[0]);

    // Process GDB commands
    switch (Packet[0]) {
      //Read memory command.
      //m addr,length. 
      case    'm':
        ReadFromMemory (Packet);
        break;

      //Fretcode, errno, Ctrl-C flag
      //retcode - Count read
      case    'F':
        //Once target receives F reply packet that means the previous 
        //transactions are finished.
        ReceiveDone = TRUE;
        break;
      
      //Send empty buffer
      default    :  
        SendNotSupported();
        break;
    }
  } while (ReceiveDone == FALSE);

  RetCode = GdbParseFReplyPacket (Packet, &ErrNo);
  Print ((CHAR16 *)L"RetCode: %x..ErrNo: %x..\n", RetCode, ErrNo);

  //Send error to the host if there is any.
  if (ErrNo > 0) {
    SendError((UINT8)ErrNo);
  }
 
  return RetCode;
}


/**
  Reset the serial device.

  @param  This              Protocol instance pointer.
                            
  @retval EFI_SUCCESS       The device was reset.
  @retval EFI_DEVICE_ERROR  The serial device could not be reset.

**/
EFI_STATUS
EFIAPI
GdbSerialReset (
  IN EFI_SERIAL_IO_PROTOCOL  *This
  )
{
  return EFI_SUCCESS;
}


/**
  Sets the baud rate, receive FIFO depth, transmit/receice time out, parity, 
  data buts, and stop bits on a serial device.

  @param  This             Protocol instance pointer.
  @param  BaudRate         The requested baud rate. A BaudRate value of 0 will use the the
                           device's default interface speed.
  @param  ReveiveFifoDepth The requested depth of the FIFO on the receive side of the
                           serial interface. A ReceiveFifoDepth value of 0 will use
                           the device's dfault FIFO depth.
  @param  Timeout          The requested time out for a single character in microseconds.
                           This timeout applies to both the transmit and receive side of the
                           interface. A Timeout value of 0 will use the device's default time
                           out value.
  @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 reset.
  @retval EFI_DEVICE_ERROR The serial device could not be reset.

**/
EFI_STATUS
EFIAPI
GdbSerialSetAttributes (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN UINT64                  BaudRate,
  IN UINT32                  ReceiveFifoDepth,
  IN UINT32                  Timeout,
  IN EFI_PARITY_TYPE         Parity,
  IN UINT8                   DataBits,
  IN EFI_STOP_BITS_TYPE      StopBits
  )
{
  return EFI_UNSUPPORTED;
}


/**
  Set the control bits on a serial device

  @param  This             Protocol instance pointer.
  @param  Control          Set the bits of Control that are settable.

  @retval EFI_SUCCESS      The new control bits were set on the serial device.
  @retval EFI_UNSUPPORTED  The serial device does not support this operation.
  @retval EFI_DEVICE_ERROR The serial device is not functioning correctly.

**/
EFI_STATUS
EFIAPI
GdbSerialSetControl (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN UINT32                  Control
  )
{
  return EFI_UNSUPPORTED;
}


/**
  Retrieves the status of thecontrol bits on a serial device

  @param  This              Protocol instance pointer.
  @param  Control           A pointer to return the current Control signals from the serial device.
                            
  @retval EFI_SUCCESS       The control bits were read from the serial device.
  @retval EFI_DEVICE_ERROR  The serial device is not functioning correctly.

**/
EFI_STATUS
EFIAPI
GdbSerialGetControl (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  OUT UINT32                 *Control
  )
{
  return EFI_UNSUPPORTED;
}


/**
  Writes data to a serial device.

  @param  This              Protocol instance pointer.
  @param  BufferSize        On input, the size of the Buffer. On output, the amount of
                            data actually written.
  @param  Buffer            The buffer of data to write

  @retval EFI_SUCCESS       The data was written.
  @retval EFI_DEVICE_ERROR  The device reported an error.
  @retval EFI_TIMEOUT       The data write was stopped due to a timeout.

**/
EFI_STATUS
EFIAPI
GdbSerialWrite (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN OUT UINTN               *BufferSize,
  IN VOID                    *Buffer
  )
{
  GDB_SERIAL_DEV  *SerialDev;
  UINTN            Return;

  SerialDev = GDB_SERIAL_DEV_FROM_THIS (This);
  
  Return = GdbWrite (SerialDev->OutFileDescriptor, Buffer, *BufferSize);
  if (Return == (UINTN)-1) {
    return EFI_DEVICE_ERROR;
  }
  
  if (Return != *BufferSize) {
    *BufferSize = Return;
  }
  
  return EFI_SUCCESS;
}

/**
  Writes data to a serial device.

  @param  This              Protocol instance pointer.
  @param  BufferSize        On input, the size of the Buffer. On output, the amount of
                            data returned in Buffer.
  @param  Buffer            The buffer to return the data into.

  @retval EFI_SUCCESS       The data was read.
  @retval EFI_DEVICE_ERROR  The device reported an error.
  @retval EFI_TIMEOUT       The data write was stopped due to a timeout.

**/

EFI_STATUS
EFIAPI
GdbSerialRead (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN OUT UINTN               *BufferSize,
  OUT VOID                   *Buffer
  )
{
  GDB_SERIAL_DEV  *SerialDev;
  UINTN            Return;

  SerialDev = GDB_SERIAL_DEV_FROM_THIS (This);
  
  Return = GdbRead (SerialDev->InFileDescriptor, Buffer, *BufferSize);
  if (Return == (UINTN)-1) {
    return EFI_DEVICE_ERROR;
  }
  
  if (Return != *BufferSize) {
    *BufferSize = Return;
  }
  
  return EFI_SUCCESS;
}


// 
// Template used to initailize the GDB Serial IO protocols
//
GDB_SERIAL_DEV gdbSerialDevTemplate = {
  GDB_SERIAL_DEV_SIGNATURE,
  NULL,
  
  { // SerialIo
    SERIAL_IO_INTERFACE_REVISION,
    GdbSerialReset,
    GdbSerialSetAttributes,
    GdbSerialSetControl,
    GdbSerialGetControl,
    GdbSerialWrite,
    GdbSerialRead,
    NULL
  },
  { // SerialMode
    0,      // ControlMask
    0,      // Timeout
    0,      // BaudRate
    1,      // RceiveFifoDepth
    0,      // DataBits
    0,      // Parity
    0       // StopBits
  },
  { 
    {
      HARDWARE_DEVICE_PATH,
      HW_VENDOR_DP,
      {
        (UINT8) (sizeof (VENDOR_DEVICE_PATH) + sizeof (UINT32)),
        (UINT8) ((sizeof (VENDOR_DEVICE_PATH) + sizeof (UINT32)) >> 8)
      },
      EFI_SERIAL_IO_PROTOCOL_GUID,
    },
    0,
    {
      END_DEVICE_PATH_TYPE,
      END_ENTIRE_DEVICE_PATH_SUBTYPE,
      {
        (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),
        (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL) >> 8)
      }
    },
  },
  GDB_STDIN,
  GDB_STDOUT
};


/**
  Make two serial consoles: 1) StdIn and StdOut via GDB. 2) StdErr via GDB.
  
  These console show up on the remote system running GDB

**/
VOID
GdbInitializeSerialConsole (
  VOID
  )
{
  EFI_STATUS      Status;
  GDB_SERIAL_DEV  *StdOutSerialDev;
  GDB_SERIAL_DEV  *StdErrSerialDev;

  // Use the template to make a copy of the Serial Console private data structure.
  StdOutSerialDev = AllocateCopyPool (sizeof (GDB_SERIAL_DEV),  &gdbSerialDevTemplate);
  ASSERT (StdOutSerialDev != NULL);
  
  // Fixup pointer after the copy
  StdOutSerialDev->SerialIo.Mode = &StdOutSerialDev->SerialMode;
  
  StdErrSerialDev = AllocateCopyPool (sizeof (GDB_SERIAL_DEV),  &gdbSerialDevTemplate);
  ASSERT (StdErrSerialDev != NULL);

  // Fixup pointer and modify stuff that is different for StdError
  StdErrSerialDev->SerialIo.Mode = &StdErrSerialDev->SerialMode;  
  StdErrSerialDev->DevicePath.Index = 1;
  StdErrSerialDev->OutFileDescriptor = GDB_STDERR;
  
  // Make a new handle with Serial IO protocol and its device path on it.
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &StdOutSerialDev->Handle, 
                  &gEfiSerialIoProtocolGuid,   &StdOutSerialDev->SerialIo,
                  &gEfiDevicePathProtocolGuid, &StdOutSerialDev->DevicePath,
                  NULL
                  );
  ASSERT_EFI_ERROR (Status);

  // Make a new handle with Serial IO protocol and its device path on it.
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &StdErrSerialDev->Handle, 
                  &gEfiSerialIoProtocolGuid,   &StdErrSerialDev->SerialIo,
                  &gEfiDevicePathProtocolGuid, &StdErrSerialDev->DevicePath,
                  NULL
                  );
  ASSERT_EFI_ERROR (Status);
}