The GPU loads kernel8.img
from the first available FAT partition on the SD card or a USB mass storage device and the CPU jumps to 0x80000
to begin execution in 64-bit mode.
It is highly recommended that you compile Goswin von Brederlow's raspbootcom.cc, a kernel image server that will listen for a connection over a serial line (115200/8/N/1) and send a kernel image when connected.
$ gcc -o raspbootcom raspbootcom.cc
For the target board to be able to listen for a kernel over the mini-UART, you should follow bzt's instructions to build a binutils/GCC toolchain targeting aarch64-elf
. (We've tested with GCC 8.3.0.) Note that if you're building the toolchain on a Raspberry Pi 3 B+ host running Raspbian, you may want to edit /etc/dphys-swapfile
to increase your swap file to a more reasonable value (such as 1024 MB), and limit the build to two simultaneous jobs (make -j2 all-gcc
) to avoid GCC running out of memory or making your system unusable. Ideally, you would move this swap to a USB hard drive to avoid SD card wear. Be prepared for this step to take several hours on a Raspberry Pi 3 B+ host.
Next, build bzt's raspbootin64 and copy its kernel8.img
to your target SD card. You'll also need bootcode.bin
, fixup.dat
, and start.elf
from the Raspberry Pi Foundation's firmware repository. Finally, you'll need an appropriate config.txt
:
arm_control=0x200 enable_uart=1
Wire up your USB-UART adapter (we've tested with the WINGONEER CP2102 adapter on a Raspberry Pi 3 B+ running Raspbian using CuteCom) to the target board's mini-UART (on pins 8 and 10; don't forget a GND pin). Start raspbootcom
by passing it the serial device path of your adapter and the path of the kernel image you'd like to run on the target board, e.g., the kernel from bzt's first UART tutorial, which echoes back your input.
$ raspbootcom /dev/ttyUSB0 kernel8.img
Unmount the SD card, sync
to be sure the files have been flushed, and put the SD card in the target board. Power up the target board, and you should see confirmation from raspbootcom
that your kernel has been loaded. Now, you can kill raspbootcom
and work with the target board, e.g., by talking to the target board via the now available UART.
Following that, you should work through bzt's Bare Metal Raspberry Pi 3 Tutorials.
See OpenOCD for instructions on compiling and installing the latest OpenOCD source. You will need a USB-to-JTAG adapter (SEGGER J-Link EDU and Olimex ARM-USB-TINY-H have been tried) and board configuration, raspi.cfg
. To connect to a board with JTAG enabled on the ALT4 pins (e.g., by a devicetree overlay for Linux), you might do:
$ openocd -d3 -f interface/jlink.cfg -f raspi.cfg
Note that in order to run OpenOCD as a regular user on a typical Linux system, you'll need to install a rules file for your JTAG adapter in /etc/udev/rules.d
and reboot. The order of the configuration options is important: the interface adapter config must be specified before the board config. The -d3
option provides a more useful level of insight into OpenOCD operation.
Warning: This is a work-in-progress, and segfaults as soon as you try to connect to the GDB server instance. You need a copy of the latest OpenOCD (0.10+dev) built from the source repository for this configuration.
transport select jtag #reset_config trst_and_srst reset_config srst_only #jtag_rclk 4000 adapter_khz 1000 jtag_ntrst_delay 500 if { [info exists CHIPNAME] } { set _CHIPNAME $CHIPNAME } else { set _CHIPNAME rpi3 } if { [info exists DAP_TAPID] } { set _DAP_TAPID $DAP_TAPID } else { set _DAP_TAPID 0x4ba00477 } set _DAPNAME $_CHIPNAME.dap set _TARGETNAME_0 $_CHIPNAME.cpu0 set _TARGETNAME_1 $_CHIPNAME.cpu1 set _TARGETNAME_2 $_CHIPNAME.cpu2 set _TARGETNAME_3 $_CHIPNAME.cpu3 set _CTINAME_0 $_CHIPNAME.cti0 set _CTINAME_1 $_CHIPNAME.cti1 set _CTINAME_2 $_CHIPNAME.cti2 set _CTINAME_3 $_CHIPNAME.cti3 jtag newtap $_CHIPNAME dap -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_DAP_TAPID -enable dap create $_CHIPNAME -chain-position $_DAPNAME target create $_TARGETNAME_0 aarch64 -dap $_CHIPNAME -coreid 0 -dbgbase 0x80010000 target create $_TARGETNAME_1 aarch64 -dap $_CHIPNAME -coreid 1 -dbgbase 0x80012000 target create $_TARGETNAME_2 aarch64 -dap $_CHIPNAME -coreid 2 -dbgbase 0x80014000 target create $_TARGETNAME_3 aarch64 -dap $_CHIPNAME -coreid 3 -dbgbase 0x80016000 cti create $_CTINAME_0 -dap $_CHIPNAME -ap-num 0 -ctibase 0x80018000 cti create $_CTINAME_1 -dap $_CHIPNAME -ap-num 0 -ctibase 0x80019000 cti create $_CTINAME_2 -dap $_CHIPNAME -ap-num 0 -ctibase 0x8001A000 cti create $_CTINAME_3 -dap $_CHIPNAME -ap-num 0 -ctibase 0x8001B000 $_TARGETNAME_0 configure -event reset-assert-post "aarch64 dbginit" $_TARGETNAME_0 configure -event gdb-attach { halt } $_TARGETNAME_1 configure -event reset-assert-post "aarch64 dbginit" $_TARGETNAME_1 configure -event gdb-attach { halt } $_TARGETNAME_2 configure -event reset-assert-post "aarch64 dbginit" $_TARGETNAME_2 configure -event gdb-attach { halt } $_TARGETNAME_3 configure -event reset-assert-post "aarch64 dbginit" $_TARGETNAME_3 configure -event gdb-attach { halt }