.. SPDX-License-Identifier: GPL-2.0+
.. Copyright (c) 2024 Alexander Dahl

Debugging U-Boot with GDB
=========================

Using a JTAG adapter it is possible to debug a running U-Boot with GDB.
A common way is to connect a debug adapter to the JTAG connector of your
board, run a GDB server, connect GDB to the GDB server, and use GDB as usual.

Similarly QEMU can provide a GDB server.

Preparing build
---------------

Building U-Boot with with reduced optimization (-Og) and without link time
optimization is recommended for easier debugging::

    CONFIG_CC_OPTIMIZE_FOR_DEBUG=y
    CONFIG_LTO=n

Otherwise build, install, and run U-Boot as usual.

Using OpenOCD as GDB server
---------------------------

`OpenOCD <https://openocd.org/>`_ is an open source tool supporting hardware
debug probes, and providing a GDB server. It is readily available in major Linux
distributions or you can build it from source.

Here is example of starting OpenOCD on Debian using a J-Link adapter and a
board with an AT91 SAMA5D2 SoC:

.. code-block:: console

    $ openocd -f interface/jlink.cfg -f target/at91sama5d2.cfg -c 'adapter speed 4000'
    Open On-Chip Debugger 0.12.0
    Licensed under GNU GPL v2
    For bug reports, read
            http://openocd.org/doc/doxygen/bugs.html
    Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'.
    adapter speed: 4000 kHz

    Info : Listening on port 6666 for tcl connections
    Info : Listening on port 4444 for telnet connections
    Info : J-Link V10 compiled Jan 30 2023 11:28:07
    Info : Hardware version: 10.10
    Info : VTarget = 3.244 V
    Info : clock speed 4000 kHz
    Info : JTAG tap: at91sama5d2.cpu tap/device found: 0x5ba00477 (mfg: 0x23b (ARM Ltd), part: 0xba00, ver: 0x5)
    Info : at91sama5d2.cpu_a5.0: hardware has 3 breakpoints, 2 watchpoints
    Info : at91sama5d2.cpu_a5.0: MPIDR level2 0, cluster 0, core 0, mono core, no SMT
    Info : starting gdb server for at91sama5d2.cpu_a5.0 on 3333
    Info : Listening on port 3333 for gdb connections

Notice that OpenOCD is listening on port 3333 for GDB connections.

Using QEMU as GDB server
------------------------

When running U-Boot on QEMU you can used the '-gdb' parameter to provide a
GDB server:

     qemu-system-riscv64 -M virt -nographic -gdb tcp::3333 -kernel u-boot

Running a GDB session
----------------------

You need a GDB suited for your target. This can be the GDB coming with your
toolchain or *gdb-multiarch* available in your Linux distribution.

.. prompt:: bash $

    gdb-multiarch u-boot

In the above command-line *u-boot* is the U-boot binary in your build
directory. You may need to adjust the path when calling GDB.

Connect to the GDB server like this:

.. code-block:: console

    (gdb) target extended-remote :3333
    Remote debugging using :3333
    0x27fa9ac6 in ?? ()
    (gdb)

This is fine for debugging before U-Boot relocates itself.

For debugging U-Boot after relocation you need to indicate the relocation
address to GDB. You can retrieve the relocation address from the U-Boot shell
with the command *bdinfo*:

.. code-block:: console

    U-Boot> bdinfo
    boot_params = 0x20000100
    DRAM bank   = 0x00000000
    -> start    = 0x20000000
    -> size     = 0x08000000
    flashstart  = 0x00000000
    flashsize   = 0x00000000
    flashoffset = 0x00000000
    baudrate    = 115200 bps
    relocaddr   = 0x27f7a000
    reloc off   = 0x0607a000
    Build       = 32-bit
    current eth = ethernet@f8008000
    ethaddr     = 00:50:c2:31:58:d4
    IP addr     = <NULL>
    fdt_blob    = 0x27b36060
    new_fdt     = 0x27b36060
    fdt_size    = 0x00003e40
    lmb_dump_all:
     memory.cnt = 0x1 / max = 0x10
     memory[0]      [0x20000000-0x27ffffff], 0x08000000 bytes flags: 0
     reserved.cnt = 0x1 / max = 0x10
     reserved[0]    [0x27b31d00-0x27ffffff], 0x004ce300 bytes flags: 0
    devicetree  = separate
    arch_number = 0x00000000
    TLB addr    = 0x27ff0000
    irq_sp      = 0x27b36050
    sp start    = 0x27b36040
    Early malloc usage: cd8 / 2000

Look out for the line starting with *relocaddr* which has the address
you need, ``0x27f7a000`` in this case.

On most architectures (not sandbox, x86, Xtensa) the global data pointer is
stored in a fixed register:

============ ========
Architecture Register
============ ========
arc          r25
arm          r9
arm64        x18
m68k         d7
microblaze   r31
mips         k0
nios2        gp
powerpc      r2
riscv        gp
sh           r13
============ ========

On these architecture the relocation address cat be determined by
dereferencing the global data pointer stored in register, *r9* in the example:

.. code-block:: console

     (gdb) p/x (*(struct global_data*)$r9)->relocaddr
     $1 = 0x27f7a000

In the GDB shell discard the previously loaded symbol file and add it once
again with the relocation address like this:

.. code-block:: console

    (gdb) symbol-file
    Discard symbol table from `/home/adahl/build/u-boot/v2024.04.x/u-boot'? (y or n) y
    No symbol file now.
    (gdb) add-symbol-file u-boot 0x27f7a000
    add symbol table from file "u-boot" at
            .text_addr = 0x27f7a000
    (y or n) y
    Reading symbols from u-boot...
    (gdb)

You can now use GDB as usual, setting breakpoints, printing backtraces,
inspecting variables, stepping through the code, etc.
