How to debug the Linux kernel using the JTAG ICE mkII
Even though the JTAG ICE mkII is primarily meant for debugging standalone application, the Linux kernel can be considered a standalone application in its own right. This means that you can debug the kernel by using the standalone debugging toolchain.
Tools you need
- JTAG ICE mkII
- avr32gdbproxy
- avr32-gdb (the standalone debugger) or avr32-linux-gdb. Preferably the former since it has the ability to read and write system registers.
Limitations
The standalone toolchain currently doesn't fully support virtual memory; more specifically, it doesn't support dealing with paging. Paging means that the virtual-to-physical address translation is performed through a TranslationLookasideBuffer? (TLB) instead of a simple, fixed mapping. The kernel enables paging early on in the boot process, but fortunately, the kernel image itself resides in the non-paged P1 segment of the AVR32 virtual memory map. This means that it is possible to debug the kernel itself as well as all drivers, filesystems, etc. that have been linked statically without confusing the debugger.
However, there are two things that will confuse the debugger:
- Dynamically loaded kernel modules. These are loaded into the paged P3 segment of the virtual memory map, which the debugger doesn't know how to handle. If you want to debug your driver using gdb, link it into the kernel image instead of as a loadable module.
- Regular userspace processes. These are unprivileged processes running in the paged U0 segment. If you want to debug regular applications, you're much better off using RemoteDebugging with gdbserver.
Also note that when the kernel is being debugged this way, it's probably not a good idea to use other debugging tools on the target at the same time. This includes KProbes (disable it in the kernel config to be sure), strace and gdbserver. If you do this, the kernel may put the OCD system in Monitor Mode, causing avr32gdbproxy to lose contact with the target.
Booting the kernel
Although gdb does support loading program images into memory, the easiest and fastest way to load the kernel is to let u-boot do it. Here's how to do it:
- Connect the JTAGICEmkII to the target, hard reset target and start avr32gdbproxy.
- Start avr32-gdb, giving it the ELF image of the Linux kernel as parameter:
avr32-gdb vmlinux
- Tell gdb to connect to avr32gdbproxy:
target remote :4711
- Set a hard breakpoint at the kernel entry address:
hbreak *0x90000000
- Let u-boot run by typing
continue
(or just "c") on the gdb prompt.
- Do whatever you usually do to start Linux from u-boot (just letting it happen automatically usually works.) If u-boot is set to boot without any interaction then you must complete steps 1-4 before the kernel hardware breakpoint code is reached.
- The breakpoint will trigger just as the kernel is about to start executing. From here on, you can use regular, soft breakpoints.
Although you can set the first breakpoint anywhere you want (the kernel entry being the safest choice,) it is very important that it is a hard breakpoint. A soft breakpoint (i.e. replacing an instruction by a breakpoint instruction) will not work as u-boot will overwrite the breakpoint instruction when loading the kernel into memory.