Skip to content

Commit f10a895

Browse files
adamgreenfacchinm
authored andcommitted
Upgrade MRI to version 1.5
This upgrades the version of the MRI library sources used by KernelDebug and ThreadDebug to the latest that I am actively supporting. Changes that will benefit both ThreadDebug and KernelDebug include: * I added the libraries/MRI/src/variants folder to hold the definition of g_memoryMapXml specific to each Arduino variant which supports MRI. Before these definitions were duplicated between ThreadDebug and KernelDebug. * Has cache fix to enable soft breakpoints for code running out of RAM. * Fixed cases where single stepping would sometimes hang if the code being stepped over caused a crash. * I have added a DebugSerial object which can be used in place of Serial and/or SerialUSB to send output to the GDB console during the development process. MRI contains changes which make sending such output to GDB more efficient so I added this DebugSerial object to take advantage of it. The ThreadDebug example has been modified to show using this object to allow Serial.print() calls when using ThreadDebug which previously caused problems. Some of the changes included in this version update will benefit ThreadDebug: * Platform_CommSendBuffer() is now overridden by ThreadDebug to send packets to GDB in one USB call instead of sending a byte at a time as it did before. * The original RTX OS handlers like SVCall, PendSV, and SysTick only need to be saved once, during init, and not each time that ThreadDebug's versions are switched in. * When running a test pass on ThreadDebug this time, I noticed that user threads weren't being locked as expected when I used the `set scheduler-locking step` command in GDB. I have always had this case in my test pass so I don't know how I didn't notice it was broken before. Anyway it is now fixed by making sure that the RTX idle thread is never allowed to remain frozen. If it has been requested by GDB to be frozen, it is thawed at one priority level higher than normal so that it stops all other frozen threads from running. Other changes might not be a direct benefit to ThreadDebug but they will be for KernelDebug: * Faults will now pend to DebugMon so that invalid memory accesses initiated from GDB will no longer result in double fault CPU hard hangs. This pending to DebugMon for faults was actually copied from behavior first created for ThreadDebug back in 2020. * Better handling of crashes the occur when the stack pointer becomes corrupted. Previously stacking exceptions could result in CPU hard hangs due to double faulting. * Now has better code to handle single stepping through critical sections. * Can now modify the current SP pointer from GDB. Probably not something most people would want to do though. Can also update the value of the PRIMASK, BASEPRI, FAULTMASK, and CONTROL special registers. Other important updates to MRI included in version 1.5 are: * Can now step into Standard C Library calls like memset() with no weird side effects. * MRI now supports running the DebugMon interrupt handler at priorities other than 0. This allows important code such as USB or Bluetooth stacks to run in the background while GDB/MRI are debugging lower priority code. I first explored this ability for ThreadDebug but now use it in other projects to do things like communicate with GDB over BLE. * Anywhere that the application under debug might want to call into MRI (semihosting, setting debugger hooks, etc), it now uses unique hardcoded `bkpt` instructions rather than just making function calls. This allows them to be called when MRI is installed as a separate boot loader on a device. * Unescaping of binary packet data sent from GDB has been pushed lower down into the software stack, to the packet layer. This is where it should have been done in the first place. Thanks go to @xiaoxiang781216, @icecream95, and @PetteriAimonen for their MRI contributions that are included in this update.
1 parent eb65448 commit f10a895

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1906
-510
lines changed

libraries/KernelDebug/library.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=KernelDebug
2-
version=1.0.0
2+
version=1.5.0
33
author=Adam Green <[email protected]>
44
maintainer=Arduino <[email protected]>
55
sentence=The GDB compatible kernel debug monitor for Cortex-M devices.

libraries/KernelDebug/src/KernelDebug.cpp

+62-33
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2020 Adam Green (https://github.com/adamgreen/)
1+
/* Copyright 2022 Adam Green (https://github.com/adamgreen/)
22
33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ extern "C" {
3131
// Source code for armv7-m module which is included here, configured for supporting kernel mode debugging.
3232
#include <architectures/armv7-m/armv7-m.x>
3333
#include <architectures/armv7-m/debug_cm3.h>
34+
#include <variants/mri_variant.h>
3435
}
3536

3637

@@ -134,8 +135,10 @@ static void readThreadContext(MriContext* pContext, uint32_t* pSP, osRtxThread_t
134135
// Forward declaration of external functions used by KernelDebug.
135136
// Will be setting initial breakpoint on setup() routine.
136137
extern "C" void setup();
137-
// The debugger uses this handler to catch faults, debug events, etc.
138+
// The debugger uses this handler to handle debug events, etc.
138139
extern "C" void mriExceptionHandler(void);
140+
// The debugger uses this handler to handle faults.
141+
extern "C" void mriFaultHandler(void);
139142

140143

141144

@@ -241,10 +244,10 @@ static void restoreSystemHandlerPriorities(const SystemHandlerPriorities* pPrior
241244

242245
static void switchFaultHandlersToDebugger(void)
243246
{
244-
NVIC_SetVector(HardFault_IRQn, (uint32_t)&mriExceptionHandler);
245-
NVIC_SetVector(MemoryManagement_IRQn, (uint32_t)&mriExceptionHandler);
246-
NVIC_SetVector(BusFault_IRQn, (uint32_t)&mriExceptionHandler);
247-
NVIC_SetVector(UsageFault_IRQn, (uint32_t)&mriExceptionHandler);
247+
NVIC_SetVector(HardFault_IRQn, (uint32_t)&mriFaultHandler);
248+
NVIC_SetVector(MemoryManagement_IRQn, (uint32_t)&mriFaultHandler);
249+
NVIC_SetVector(BusFault_IRQn, (uint32_t)&mriFaultHandler);
250+
NVIC_SetVector(UsageFault_IRQn, (uint32_t)&mriFaultHandler);
248251
}
249252

250253

@@ -287,27 +290,6 @@ void Platform_CommSendChar(int character)
287290

288291

289292

290-
static const char g_memoryMapXml[] = "<?xml version=\"1.0\"?>"
291-
"<!DOCTYPE memory-map PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"
292-
"<memory-map>"
293-
"<memory type=\"ram\" start=\"0x00000000\" length=\"0x10000\"> </memory>"
294-
"<memory type=\"flash\" start=\"0x08000000\" length=\"0x200000\"> <property name=\"blocksize\">0x20000</property></memory>"
295-
"<memory type=\"ram\" start=\"0x10000000\" length=\"0x48000\"> </memory>"
296-
"<memory type=\"ram\" start=\"0x1ff00000\" length=\"0x20000\"> </memory>"
297-
"<memory type=\"ram\" start=\"0x20000000\" length=\"0x20000\"> </memory>"
298-
"<memory type=\"ram\" start=\"0x24000000\" length=\"0x80000\"> </memory>"
299-
"<memory type=\"ram\" start=\"0x30000000\" length=\"0x48000\"> </memory>"
300-
"<memory type=\"ram\" start=\"0x38000000\" length=\"0x10000\"> </memory>"
301-
"<memory type=\"ram\" start=\"0x38800000\" length=\"0x1000\"> </memory>"
302-
"<memory type=\"ram\" start=\"0x58020000\" length=\"0x2c00\"> </memory>"
303-
"<memory type=\"ram\" start=\"0x58024400\" length=\"0xc00\"> </memory>"
304-
"<memory type=\"ram\" start=\"0x58025400\" length=\"0x800\"> </memory>"
305-
"<memory type=\"ram\" start=\"0x58026000\" length=\"0x800\"> </memory>"
306-
"<memory type=\"ram\" start=\"0x58027000\" length=\"0x400\"> </memory>"
307-
"<memory type=\"flash\" start=\"0x90000000\" length=\"0x10000000\"> <property name=\"blocksize\">0x200</property></memory>"
308-
"<memory type=\"ram\" start=\"0x60000000\" length=\"0x800000\"> </memory>"
309-
"</memory-map>";
310-
311293
extern "C" uint32_t Platform_GetDeviceMemoryMapXmlSize(void)
312294
{
313295
return sizeof(g_memoryMapXml) - 1;
@@ -331,12 +313,7 @@ extern "C" uint32_t Platform_GetUidSize(void)
331313
return 0;
332314
}
333315

334-
extern "C" int Semihost_IsDebuggeeMakingSemihostCall(void)
335-
{
336-
return 0;
337-
}
338-
339-
int Semihost_HandleSemihostRequest(void)
316+
int Semihost_HandleMbedSemihostRequest(PlatformSemihostParameters* pParameters)
340317
{
341318
return 0;
342319
}
@@ -617,3 +594,55 @@ void Platform_RtosSetThreadState(uint32_t threadId, PlatformThreadState state)
617594
void Platform_RtosRestorePrevThreadState(void)
618595
{
619596
}
597+
598+
599+
600+
601+
602+
// This class can be used instead of Serial for sending output to the PC via GDB.
603+
DebugSerial::DebugSerial()
604+
{
605+
}
606+
607+
// Methods that must be implemented for Print subclasses.
608+
size_t DebugSerial::write(uint8_t byte)
609+
{
610+
return write(&byte, sizeof(byte));
611+
}
612+
613+
size_t DebugSerial::write(const uint8_t *pBuffer, size_t size)
614+
{
615+
const int STDOUT_FILE_NO = 1;
616+
const char* pCurr = (const char*)pBuffer;
617+
int bytesWritten = 0;
618+
619+
while (size > 0) {
620+
int result = mriNewlib_SemihostWrite(STDOUT_FILE_NO, pCurr, size);
621+
if (result == -1) {
622+
break;
623+
}
624+
size -= result;
625+
pCurr += result;
626+
bytesWritten += result;
627+
}
628+
return bytesWritten;
629+
}
630+
631+
void DebugSerial::flush()
632+
{
633+
}
634+
635+
void DebugSerial::begin(unsigned long baud, uint16_t mode)
636+
{
637+
// Silence compiler warnings about unused parameters.
638+
(void)baud;
639+
(void)mode;
640+
}
641+
642+
void DebugSerial::end()
643+
{
644+
}
645+
646+
647+
// Instantiate the single instance of this stream.
648+
class DebugSerial arduino::DebugSerial;

libraries/KernelDebug/src/KernelDebug.h

+23
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,27 @@ class KernelDebug {
6262
#define debugBreak() { __asm volatile ("bkpt #0"); }
6363
#endif
6464

65+
66+
// This class can be used instead of Serial for sending output to the PC via GDB.
67+
class DebugSerial : public Print
68+
{
69+
public:
70+
DebugSerial();
71+
72+
// Leaving out input Stream related methods so that compiler throws errors if user tries to use them.
73+
74+
// Methods that must be implemented for Print subclasses.
75+
virtual size_t write(uint8_t);
76+
virtual size_t write(const uint8_t *buffer, size_t size);
77+
virtual void flush();
78+
79+
// Additional methods defined by HardwareSerial that user might call.
80+
void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
81+
void begin(unsigned long baudrate, uint16_t config);
82+
void end();
83+
operator bool() { return true; }
84+
85+
protected:
86+
} extern DebugSerial;
87+
6588
} // namespace arduino

0 commit comments

Comments
 (0)