Extend JTAG tutorial

pull/19/head
Andre Richter 5 years ago committed by Andre Richter
parent 59c6c15c1d
commit 28cf26a28a

@ -1,9 +1,18 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "cortex-a"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"register 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel8"
version = "0.1.0"
dependencies = [
"cortex-a 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"raspi3_boot 0.1.0",
"register 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -22,6 +31,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "raspi3_boot"
version = "0.1.0"
dependencies = [
"cortex-a 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -40,6 +50,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum cortex-a 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "12425c4491f31f28f539c74382ade69ee9db4f1f597aa177f43e072595562e46"
"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum register 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a0f44a6dc9a98359515541a0c46ef4e3630a30879c1d7a4038f31dd533570bfb"

@ -6,6 +6,7 @@ edition = "2018"
[dependencies]
raspi3_boot = { path = "raspi3_boot" }
cortex-a = "2.3.1"
register = "0.3.2"
[package.metadata.cargo-xbuild]

@ -0,0 +1,98 @@
#
# MIT License
#
# Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
XRUSTC_CMD = cargo xrustc --target=$(TARGET) --release
CARGO_OUTPUT = target/$(TARGET)/release/kernel8
OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary
CONTAINER_UTILS = andrerichter/raspi3-utils
CONTAINER_OPENOCD = andrerichter/raspi3-openocd
CONTAINER_GDB = andrerichter/raspi3-gdb
DOCKER_CMD = docker run -it --rm
DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work
DOCKER_ARG_TTY = --privileged -v /dev:/dev
DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag
DOCKER_ARG_NET = --network host
DOCKER_EXEC_QEMU = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
DOCKER_EXEC_RASPBOOT = raspbootcom /dev/ttyUSB0
.PHONY: all qemu raspboot clippy clean objdump nm jtag
all: clean kernel8.img
$(CARGO_OUTPUT): $(SOURCES)
$(XRUSTC_CMD)
kernel8.img: $(CARGO_OUTPUT)
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: all
$(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \
$(DOCKER_EXEC_QEMU) -serial stdio
raspboot: all
$(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \
$(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) kernel8.img
clippy:
cargo xclippy --target=$(TARGET)
clean:
cargo clean
objdump:
cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel8
nm:
cargo nm --target $(TARGET) -- kernel8 | sort
jtagboot:
$(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \
$(DOCKER_EXEC_RASPBOOT) /jtag/jtag_boot.img
openocd:
$(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_OPENOCD)
define gen_gdb
$(XRUSTC_CMD) -- $1
cp $(CARGO_OUTPUT) kernel8_for_jtag
$(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_GDB) \
gdb-multiarch -q kernel8_for_jtag
endef
gdb: clean $(SOURCES)
$(call gen_gdb,-C debuginfo=2)
gdb-opt0: clean $(SOURCES)
$(call gen_gdb,-C debuginfo=2 -C opt-level=0)

@ -0,0 +1,260 @@
# Tutorial 0B - Hardware Debugging using JTAG
In the upcoming tutorials, we are going to touch sensitive areas of the RPi's
SoC that can make our debugging life very hard. For example, changing the
processor's `Exception Level` or introducing `virtual memory`.
A hardware based debugger can sometimes be the last resort when searching for a
tricky bug. Especially for debugging intricate, architecture-specific HW issues,
it will be handy, because in this area `QEMU` sometimes can not help, because it
abstracts certain features of our RPi's HW and doesn't simulate down to the very
last bit.
So lets introduce `JTAG` debugging. Once set up, it will allow us to single-step
through our kernel on the real HW. How cool is that?!
![JTAG live demo](../doc/jtag_demo.gif)
## Hardware
Unlike microcontroller boards like the `STM32F3DISCOVERY`, which is used in our
WG's [Embedded Rust Book](https://rust-embedded.github.io/book/start/hardware.html),
the RPi does not have an embedded debugger on it's board. Hence, you need to buy one.
For this tutorial, we will use the
[ARM-USB-TINY-H](https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/) by
OLIMEX. It has a standard
[ARM JTAG 20 connector](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0499dj/BEHEIHCE.html).
Unfortunately, the RPi does not, so we have to connect it via jumper wires.
### Wiring
<table>
<thead>
<tr>
<th>GPIO #</th>
<th>Name</th>
<th>JTAG #</th>
<th>Note</th>
<th width="60%">Diagram</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>VTREF</td>
<td>1</td>
<td>to 3.3V</td>
<td rowspan="8"><img src="../doc/wiring_jtag.png"></td>
</tr>
<tr>
<td></td>
<td>GND</td>
<td>4</td>
<td>to GND</td>
</tr>
<tr>
<td>22</td>
<td>TRST</td>
<td>3</td>
<td></td>
</tr>
<tr>
<td>26</td>
<td>TDI</td>
<td>5</td>
<td></td>
</tr>
<tr>
<td>27</td>
<td>TMS</td>
<td>7</td>
<td></td>
</tr>
<tr>
<td>25</td>
<td>TCK</td>
<td>9</td>
<td></td>
</tr>
<tr>
<td>23</td>
<td>RTCK</td>
<td>11</td>
<td></td>
</tr>
<tr>
<td>24</td>
<td>TDO</td>
<td>13</td>
<td></td>
</tr>
</tbody>
</table>
<p align="center"><img src="../doc/image_jtag_connected.jpg" width="50%"></p>
## Configuring GPIO for JTAG
Before it is possible to connect, we additionally have to configure the
respective GPIO pins for `JTAG` functionality from software. Our approach is as
allows:
Via `raspboot`, we load a tiny helper binary onto the RPi which configures the
pins respectively and then parks the executing core in an endless loop, waiting
for the `JTAG` debugger to connect. The helper binary is maintained separately
in this repository's [X1_JTAG_boot](../X1_JTAG_boot) folder, and is a
stripped-down version of the code we use in our tutorials.
This functionality is provided by the new `Makefile` target `make jtagboot`.
```console
ferris@box:~$ make jtagboot
Raspbootcom V1.0
### Listening on /dev/ttyUSB0
RBIN64
### sending kernel /jtag/jtag_boot.img [759 byte]
### finished sending
[i] JTAG is live. Please connect.
```
It is important to keep the USB serial connected and the terminal open with
`raspboot` running. When we load the actual kernel later, `UART` output will
appear here.
## OpenOCD
Next, we need to launch the [Open On-Chip Debugger](http://openocd.org/), aka
`OpenOCD` to actually connect the `JTAG`.
As always, our tutorials try to be as painless as possible regarding dev-tools,
which is why we have packaged everything into a [dedicated Docker container](../docker/raspi3-openocd) that will be provisioned automagically on the first run.
Now connect the Olimex USB JTAG debugger, open a new terminal and in the same
folder, type `make openocd` (in that order!). You will see some initial output:
```console
ferris@box:~$ make openocd
Open On-Chip Debugger 0.10.0+dev-ge243075 (2019-03-07-19:07)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
trst_and_srst separate srst_gates_jtag trst_push_pull srst_open_drain connect_deassert_srst
adapter speed: 1000 kHz
jtag_ntrst_delay: 500
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 1000 kHz
Info : JTAG tap: rpi3.tap tap/device found: 0x4ba00477 (mfg: 0x23b (ARM Ltd.), part: 0xba00, ver: 0x4)
Info : rpi3.core0: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.core1: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.core2: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.core3: hardware has 6 breakpoints, 4 watchpoints
Info : Listening on port 3333 for gdb connections
Info : Listening on port 3334 for gdb connections
Info : Listening on port 3335 for gdb connections
Info : Listening on port 3336 for gdb connections
```
`OpenOCD` has detected the four cores of the RPi, and opened four network ports
to which `gdb` can now connect to debug the respective core.
## GDB
Finally, we need an `AArch64`-capable version of `gdb`. You guessed right, we
packaged [another container for you](../docker/raspi3-gdb). It can be launched
via `make gdb`.
This Makefile target actually does a little more. It builds a special version of
our kernel with debug information included. This enables `gdb` to show the `Rust`
source code line we are currently debugging. It also launches `gdb` such
that it already loads this debug build (`kernel8_for_jtag`).
We can now use the `gdb` commandline to
1. Set breakpoints in our kernel
2. Load the kernel via JTAG in memory (remember that currently, the RPi is still executing
the minimal JTAG pin enablement binary).
3. Manipulate the program counter of the RPi to start execution at our kernel's entry point.
4. Single-step through its execution.
```shell
>>> break _boot_cores
Breakpoint 1 at 0x80000
>>> target remote :3333 # Connect to OpenOCD, raspi3.core0
>>> load kernel8_for_jtag # Load the kernel into the Raspi's DRAM over JTAG.
Loading section .text, size 0x6cc lma 0x80000
Loading section .rodata, size 0x9a lma 0x806cc
Start address 0x80000, load size 1894
Transfer rate: 66 KB/sec, 947 bytes/write.
>>> set $pc = 0x80000 # Set RPI's program counter to the start of the kernel binary.
>>> cont
Breakpoint 1, 0x0000000000080000 in _boot_cores ()
>>> step
>>> step # Single-step through the kernel
>>> ...
```
### Remarks
#### Optimization
When debugging an OS binary, you have to make a trade-off between the
granularity at which you can step through your Rust source-code and the
optimization level of the generated binary. The `make` and `make gdb` targets
produce a `--release` binary, which includes an optimization level of three
(`-opt-level=3`). However, in this case, the compiler will inline very
aggressively and pack together reads and writes where possible. As a result, it
will not always be possible to hit breakpoints exactly where you want to
regarding the line of source code file.
For this reason, the Makefile also provides the `make gdb-opt0` target, which
uses `-opt-level=0`. Hence, it will allow you to have finer debugging
granularity. However, please keep in mind that when debugging code that closely
deals with HW, a compiler optimization that squashes reads or writes to volatile
registers can make all the difference in execution. FYI, the demo gif above has
been recorded with `gdb-opt0`.
#### GDB control
At some point, you may reach delay loops or code that waits on user input from
the serial. Here, single stepping might not be feasible or work anymore. You can
jump over these roadblocks by setting other breakpoints beyond these areas, and
reach them using the `cont` command.
Pressing `ctrl+c` in `gdb` will stop execution of the RPi again in case you
continued it without further breakpoints.
## Notes on USB connection constraints
If you followed the tutorial from top to bottom, everything should be fine
regarding USB connections.
Still, please note that in its current form, our `Makefile` makes implicit
assumptions about the naming of the connected USB devices. It expects
`/dev/ttyUSB0` to be the `UART` device.
Hence, please ensure the following order of connecting the devices to your box:
1. Connect the USB serial.
2. Afterwards, the Olimex debugger.
This way, Linux enumerates the devices accordingly. This has to be done only
once. It is fine to disconnect and connect the serial multiple times, e.g. for
kicking off different `make jtagboot` runs, while keeping the debugger
connected.
## In summary
1. `make jtagboot` and keep terminal open.
2. Connect USB serial device.
3. Connect `JTAG` debugger USB device.
4. In new terminal, `make openocd`.
5. In new terminal, `make gdb` or make `make gdb-opt0`.
## Acknowledgments
Thanks to [@naotaco](https://github.com/naotaco) for laying the groundwork for
this tutorial.

Binary file not shown.

@ -5,5 +5,6 @@ authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2018"
[dependencies]
cortex-a = "2.3.1"
panic-abort = "0.3.1"
r0 = "0.2.2"

@ -26,7 +26,6 @@
#![deny(missing_docs)]
#![deny(warnings)]
#![no_std]
#![feature(global_asm)]
//! Low-level boot of the Raspberry's processor
@ -49,8 +48,7 @@ macro_rules! entry {
/// Reset function.
///
/// Initializes the bss section before calling into the user's `main()`.
#[no_mangle]
pub unsafe extern "C" fn reset() -> ! {
unsafe fn reset() -> ! {
extern "C" {
// Boundaries of the .bss section, provided by the linker script
static mut __bss_start: u64;
@ -67,5 +65,26 @@ pub unsafe extern "C" fn reset() -> ! {
main();
}
// Disable all cores except core 0, and then jump to reset()
global_asm!(include_str!("boot_cores.S"));
/// Entrypoint of the processor.
///
/// Parks all cores except core0, and then jumps to the internal
/// `reset()` function.
#[link_section = ".text.boot"]
#[no_mangle]
pub unsafe extern "C" fn _boot_cores() -> ! {
use cortex_a::{asm, regs::*};
const CORE_0: u64 = 0;
const CORE_MASK: u64 = 0x3;
const STACK_START: u64 = 0x80_000;
if CORE_0 == MPIDR_EL1.get() & CORE_MASK {
SP.set(STACK_START);
reset()
} else {
// if not core0, infinitely wait for events
loop {
asm::wfe();
}
}
}

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2019 Nao Taco <naotaco@gmail.com>
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -22,21 +22,16 @@
* SOFTWARE.
*/
#![no_std]
#![no_main]
#![feature(asm)]
use cortex_a::asm;
const MMIO_BASE: u32 = 0x3F00_0000;
mod debugger;
fn kernel_entry() -> ! {
// To change some GPIO pins to ARM debugger function.
// After this point, a debugger can attach to the CPU.
debugger::setup_debug();
// Wait to be attached.
loop {}
/*
*
* Using the CPU's cycles
*
*/
/// Wait N CPU cycles (ARM CPU only)
pub fn wait_cycles(cyc: u32) {
for _ in 0..cyc {
asm::nop();
}
}
raspi3_boot::entry!(kernel_entry);

@ -0,0 +1,121 @@
/*
* MIT License
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use super::MMIO_BASE;
use core::ops;
use register::{mmio::ReadWrite, register_bitfields};
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// GPIO Function Select 1
GPFSEL1 [
/// Pin 15
FSEL15 OFFSET(15) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
RXD0 = 0b100, // UART0 - Alternate function 0
RXD1 = 0b010 // Mini UART - Alternate function 5
],
/// Pin 14
FSEL14 OFFSET(12) NUMBITS(3) [
Input = 0b000,
Output = 0b001,
TXD0 = 0b100, // UART0 - Alternate function 0
TXD1 = 0b010 // Mini UART - Alternate function 5
]
],
/// GPIO Pull-up/down Clock Register 0
GPPUDCLK0 [
/// Pin 15
PUDCLK15 OFFSET(15) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
],
/// Pin 14
PUDCLK14 OFFSET(14) NUMBITS(1) [
NoEffect = 0,
AssertClock = 1
]
]
}
const GPIO_BASE: u32 = MMIO_BASE + 0x200_000;
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
pub GPFSEL0: ReadWrite<u32>, // 0x00
pub GPFSEL1: ReadWrite<u32, GPFSEL1::Register>, // 0x04
pub GPFSEL2: ReadWrite<u32>, // 0x08
pub GPFSEL3: ReadWrite<u32>, // 0x0C
pub GPFSEL4: ReadWrite<u32>, // 0x10
pub GPFSEL5: ReadWrite<u32>, // 0x14
__reserved_0: u32, // 0x18
GPSET0: ReadWrite<u32>, // 0x1C
GPSET1: ReadWrite<u32>, // 0x20
__reserved_1: u32, //
GPCLR0: ReadWrite<u32>, // 0x28
__reserved_2: [u32; 2], //
GPLEV0: ReadWrite<u32>, // 0x34
GPLEV1: ReadWrite<u32>, // 0x38
__reserved_3: u32, //
GPEDS0: ReadWrite<u32>, // 0x40
GPEDS1: ReadWrite<u32>, // 0x44
__reserved_4: [u32; 7], //
GPHEN0: ReadWrite<u32>, // 0x64
GPHEN1: ReadWrite<u32>, // 0x68
__reserved_5: [u32; 10], //
pub GPPUD: ReadWrite<u32>, // 0x94
pub GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>, // 0x98
pub GPPUDCLK1: ReadWrite<u32>, // 0x9C
}
/// Public interface to the GPIO MMIO area
pub struct GPIO;
impl ops::Deref for GPIO {
type Target = RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*Self::ptr() }
}
}
impl GPIO {
pub fn new() -> GPIO {
GPIO
}
/// Returns a pointer to the register block
fn ptr() -> *const RegisterBlock {
GPIO_BASE as *const _
}
}

@ -0,0 +1,71 @@
/*
* MIT License
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#![no_std]
#![no_main]
const MMIO_BASE: u32 = 0x3F00_0000;
mod delays;
mod gpio;
mod mbox;
mod power;
mod uart;
fn kernel_entry() -> ! {
let gpio = gpio::GPIO::new();
let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new();
let power = power::Power::new();
// set up serial console
match uart.init(&mut mbox, &gpio) {
Ok(_) => uart.puts("\n[0] UART is live!\n"),
Err(_) => loop {
cortex_a::asm::wfe() // If UART fails, abort early
},
}
uart.puts("[1] Press a key to continue booting... ");
uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
loop {
uart.puts("\n 1 - power off\n 2 - reset\nChoose one: ");
let c = uart.getc();
uart.send(c);
match c {
'1' => {
if power.off(&mut mbox, &gpio).is_err() {
uart.puts("Mailbox error in Power::off()");
}
}
'2' => power.reset(),
_ => {}
}
}
}
raspi3_boot::entry!(kernel_entry);

@ -0,0 +1,163 @@
/*
* MIT License
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use super::MMIO_BASE;
use core::ops;
use cortex_a::asm;
use register::{
mmio::{ReadOnly, WriteOnly},
register_bitfields,
};
register_bitfields! {
u32,
STATUS [
FULL OFFSET(31) NUMBITS(1) [],
EMPTY OFFSET(30) NUMBITS(1) []
]
}
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
READ: ReadOnly<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
__reserved_1: u32, // 0x1C
WRITE: WriteOnly<u32>, // 0x20
}
// Custom errors
pub enum MboxError {
ResponseError,
UnknownError,
}
pub type Result<T> = ::core::result::Result<T, MboxError>;
// Channels
pub mod channel {
pub const PROP: u32 = 8;
}
// Tags
pub mod tag {
pub const SETPOWER: u32 = 0x28001;
pub const SETCLKRATE: u32 = 0x38002;
pub const LAST: u32 = 0;
}
// Clocks
pub mod clock {
pub const UART: u32 = 0x0_0000_0002;
}
// Responses
mod response {
pub const SUCCESS: u32 = 0x8000_0000;
pub const ERROR: u32 = 0x8000_0001; // error parsing request buffer (partial response)
}
pub const REQUEST: u32 = 0;
// Public interface to the mailbox
#[repr(C)]
#[repr(align(16))]
pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly.
pub buffer: [u32; 36],
}
/// Deref to RegisterBlock
///
/// Allows writing
/// ```
/// self.STATUS.read()
/// ```
/// instead of something along the lines of
/// ```
/// unsafe { (*Mbox::ptr()).STATUS.read() }
/// ```
impl ops::Deref for Mbox {
type Target = RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*Self::ptr() }
}
}
impl Mbox {
pub fn new() -> Mbox {
Mbox { buffer: [0; 36] }
}
/// Returns a pointer to the register block
fn ptr() -> *const RegisterBlock {
VIDEOCORE_MBOX as *const _
}
/// Make a mailbox call. Returns Err(MboxError) on failure, Ok(()) success
pub fn call(&self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox
loop {
if !self.STATUS.is_set(STATUS::FULL) {
break;
}
asm::nop();
}
let buf_ptr = self.buffer.as_ptr() as u32;
// write the address of our message to the mailbox with channel identifier
self.WRITE.set((buf_ptr & !0xF) | (channel & 0xF));
// now wait for the response
loop {
// is there a response?
loop {
if !self.STATUS.is_set(STATUS::EMPTY) {
break;
}
asm::nop();
}
let resp: u32 = self.READ.get();
// is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == buf_ptr) {
// is it a valid successful response?
return match self.buffer[1] {
response::SUCCESS => Ok(()),
response::ERROR => Err(MboxError::ResponseError),
_ => Err(MboxError::UnknownError),
};
}
}
}
}

@ -0,0 +1,143 @@
/*
* MIT License
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use super::MMIO_BASE;
use crate::delays;
use crate::gpio;
use crate::mbox;
use core::ops;
use core::sync::atomic::{compiler_fence, Ordering};
use register::mmio::*;
const POWER_BASE: u32 = MMIO_BASE + 0x100_01C;
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
PM_RSTC: ReadWrite<u32>, // 0x1C
PM_RSTS: ReadWrite<u32>, // 0x20
PM_WDOG: ReadWrite<u32>, // 0x24
}
const PM_PASSWORD: u32 = 0x5a_000_000;
const PM_RSTC_WRCFG_CLR: u32 = 0xffff_ffcf;
const PM_RSTC_WRCFG_FULL_RESET: u32 = 0x0000_0020;
// The Raspberry Pi firmware uses the RSTS register to know which
// partition to boot from. The partition value is spread into bits 0, 2,
// 4, 6, 8, 10. Partition 63 is a special partition used by the
// firmware to indicate halt.
const PM_RSTS_RASPBERRYPI_HALT: u32 = 0x555;
pub enum PowerError {
MailboxError,
}
pub type Result<T> = ::core::result::Result<T, PowerError>;
/// Public interface to the Power subsystem
pub struct Power;
impl ops::Deref for Power {
type Target = RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*Self::ptr() }
}
}
impl Power {
pub fn new() -> Power {
Power
}
/// Returns a pointer to the register block
fn ptr() -> *const RegisterBlock {
POWER_BASE as *const _
}
/// Shutdown the board
pub fn off(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> {
// power off devices one by one
for dev_id in 0..16 {
mbox.buffer[0] = 8 * 4;
mbox.buffer[1] = mbox::REQUEST;
mbox.buffer[2] = mbox::tag::SETPOWER;
mbox.buffer[3] = 8;
mbox.buffer[4] = 8;
mbox.buffer[5] = dev_id; // device id
mbox.buffer[6] = 0; // bit 0: off, bit 1: no wait
mbox.buffer[7] = mbox::tag::LAST;
// Insert a compiler fence that ensures that all stores to the
// mbox buffer are finished before the GPU is signaled (which
// is done by a store operation as well).
compiler_fence(Ordering::Release);
if mbox.call(mbox::channel::PROP).is_err() {
return Err(PowerError::MailboxError); // Abort if UART clocks couldn't be set
};
}
// power off gpio pins (but not VCC pins)
gpio.GPFSEL0.set(0);
gpio.GPFSEL1.set(0);
gpio.GPFSEL2.set(0);
gpio.GPFSEL3.set(0);
gpio.GPFSEL4.set(0);
gpio.GPFSEL5.set(0);
gpio.GPPUD.set(0);
delays::wait_cycles(150);
gpio.GPPUDCLK0.set(0xffff_ffff);
gpio.GPPUDCLK1.set(0xffff_ffff);
delays::wait_cycles(150);
// flush GPIO setup
gpio.GPPUDCLK0.set(0);
gpio.GPPUDCLK1.set(0);
// We set the watchdog hard reset bit here to distinguish this
// reset from the normal (full) reset. bootcode.bin will not
// reboot after a hard reset.
let mut val = self.PM_RSTS.get();
val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT;
self.PM_RSTS.set(val);
// Continue with normal reset mechanism
self.reset();
}
/// Reboot
pub fn reset(&self) -> ! {
// use a timeout of 10 ticks (~150us)
self.PM_WDOG.set(PM_PASSWORD | 10);
let mut val = self.PM_RSTC.get();
val &= PM_RSTC_WRCFG_CLR;
val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
self.PM_RSTC.set(val);
loop {}
}
}

@ -0,0 +1,262 @@
/*
* MIT License
*
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use super::MMIO_BASE;
use crate::delays;
use crate::gpio;
use crate::mbox;
use core::{
ops,
sync::atomic::{compiler_fence, Ordering},
};
use cortex_a::asm;
use register::{mmio::*, register_bitfields};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
register_bitfields! {
u32,
/// Flag Register
FR [
/// Transmit FIFO full. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_ LCRH Register. If the
/// FIFO is disabled, this bit is set when the transmit
/// holding register is full. If the FIFO is enabled, the TXFF
/// bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the
/// state of the FEN bit in the UARTLCR_H Register. If the
/// FIFO is disabled, this bit is set when the receive holding
/// register is empty. If the FIFO is enabled, the RXFE bit is
/// set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
],
/// Integer Baud rate divisor
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Word length. These bits indicate the number of data bits
/// transmitted or received in a frame.
WLEN OFFSET(5) NUMBITS(2) [
FiveBit = 0b00,
SixBit = 0b01,
SevenBit = 0b10,
EightBit = 0b11
]
],
/// Control Register
CR [
/// Receive enable. If this bit is set to 1, the receive
/// section of the UART is enabled. Data reception occurs for
/// UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before
/// stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit
/// section of the UART is enabled. Data transmission occurs
/// for UART signals. When the UART is disabled in the middle
/// of transmission, it completes the current character before
/// stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission
/// or reception, it completes the current character
/// before stopping.
Disabled = 0,
Enabled = 1
]
],
/// Interupt Clear Register
ICR [
/// Meta field for all pending interrupts
ALL OFFSET(0) NUMBITS(11) []
]
}
const UART_BASE: u32 = MMIO_BASE + 0x20_1000;
#[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
DR: ReadWrite<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: ReadOnly<u32, FR::Register>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
CR: WriteOnly<u32, CR::Register>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WriteOnly<u32, ICR::Register>, // 0x44
}
pub enum UartError {
MailboxError,
}
pub type Result<T> = ::core::result::Result<T, UartError>;
pub struct Uart;
impl ops::Deref for Uart {
type Target = RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*Self::ptr() }
}
}
impl Uart {
pub fn new() -> Uart {
Uart
}
/// Returns a pointer to the register block
fn ptr() -> *const RegisterBlock {
UART_BASE as *const _
}
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> {
// turn off UART0
self.CR.set(0);
// set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4;
mbox.buffer[1] = mbox::REQUEST;
mbox.buffer[2] = mbox::tag::SETCLKRATE;
mbox.buffer[3] = 12;
mbox.buffer[4] = 8;
mbox.buffer[5] = mbox::clock::UART; // UART clock
mbox.buffer[6] = 4_000_000; // 4Mhz
mbox.buffer[7] = 0; // skip turbo setting
mbox.buffer[8] = mbox::tag::LAST;
// Insert a compiler fence that ensures that all stores to the
// mbox buffer are finished before the GPU is signaled (which
// is done by a store operation as well).
compiler_fence(Ordering::Release);
if mbox.call(mbox::channel::PROP).is_err() {
return Err(UartError::MailboxError); // Abort if UART clocks couldn't be set
};
// map UART0 to GPIO pins
gpio.GPFSEL1
.modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
gpio.GPPUD.set(0); // enable pins 14 and 15
delays::wait_cycles(150);
gpio.GPPUDCLK0.modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
delays::wait_cycles(150);
gpio.GPPUDCLK0.set(0);
self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
self.FBRD.write(FBRD::FBRD.val(0xB));
self.LCRH.write(LCRH::WLEN::EightBit); // 8N1
self.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
Ok(())
}
/// Send a character
pub fn send(&self, c: char) {
// wait until we can send
loop {
if !self.FR.is_set(FR::TXFF) {
break;
}
asm::nop();
}
// write the character to the buffer
self.DR.set(c as u32);
}
/// Receive a character
pub fn getc(&self) -> char {
// wait until something is in the buffer
loop {
if !self.FR.is_set(FR::RXFE) {
break;
}
asm::nop();
}
// read it and return
let mut ret = self.DR.get() as u8 as char;
// convert carrige return to newline
if ret == '\r' {
ret = '\n'
}
ret
}
/// Display a string
pub fn puts(&self, string: &str) {
for c in string.chars() {
// convert newline to carrige return + newline
if c == '\n' {
self.send('\r')
}
self.send(c);
}
}
}

@ -1,60 +0,0 @@
#
# MIT License
#
# Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary
UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
.PHONY: all qemu clippy clean objdump nm
all: clean kernel8.img
target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release
kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy:
cargo xclippy --target=$(TARGET)
clean:
cargo clean
objdump:
cargo objdump --target $(TARGET) -- -disassemble -print-imm-hex kernel8
nm:
cargo nm --target $(TARGET) -- kernel8 | sort

@ -1,153 +0,0 @@
# Tutorial 13 - Using debugger
Debugging with a debugger is very effective, but it's a bit difficult on our Raspberry Pi.
[The Embedded Rust Book mentions](https://rust-embedded.github.io/book/start/hardware.html) about using debugger on `STM32F3DISCOVERY`, however, there are some differences from our environment. The biggest one is lack of debugger hardware. Unlike `STM32F3DISCOVERY`, Raspberry Pi does not have embedded debugger on it's board; it means we need to get, connect, and setup it.
## Hardware debugger
A debugger `ARM-USB-TINY-H` made by OLIMEX has tested with Raspberry Pi3 and openocd.
https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/
It has standard [ARM JTAG 20 connector](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0499dj/BEHEIHCE.html), but unfortunately, Raspberry Pi doesn't; we have to connect like following:
| GPIO# | Name | JTAG# | Note |
|-------|-------|-------|---------|
| | VTREF | 1 | to 3.3V |
| | GND | 4 | to GND |
| 22 | TRST | 3 | |
| 26 | TDI | 5 | |
| 27 | TMS | 7 | |
| 25 | TCK | 9 | |
| 23 | RTCK | 11 | |
| 24 | TDO | 13 | |
![Connected debugger](doc/raspi3-arm-usb-tiny-h.jpg)
![wiring](doc/rapi3-jtag-wiring.png)
## debugger.rs
And, GPIO pins have to be changed to alternative functions. In this tutorial, `debugger.rs` sets the pins JTAG functions(all of them are assigned to Alt4) from the default.
```rust
pub fn setup_debug() {
unsafe {
(*GPFSEL2).modify(
GPFSEL2::FSEL27::Alt4
+ GPFSEL2::FSEL26::Alt4
+ GPFSEL2::FSEL25::Alt4
+ GPFSEL2::FSEL24::Alt4
+ GPFSEL2::FSEL23::Alt4
+ GPFSEL2::FSEL22::Alt4,
);
}
}
```
## main.rs
After enabling debugger, it goes empty loop to wait debugger connection.
## Running debugger on Linux with Docker
Using pre-built docker image like following command is easier way. This is tested on Ubuntu18.04.
Note that a device you have to specify in this command (`--device=XXX`) may be attached on different point on your machine. You can find it on `syslog` after you connect the debugger to your PC. It's like `/dev/ttyUSB0` on Ubuntu.
```console
$ sudo docker run -p 3333:3333 -p 4444:4444 --rm --privileged --device=/dev/ttyUSB0 naotaco/openocd:armv8 /bin/sh -c "cd openocd-armv8 && openocd -f tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f tcl/target/rpi3.cfg"
Open On-Chip Debugger 0.9.0-dev-gb796a58 (2019-02-19-01:36)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.sourceforge.net/doc/doxygen/bugs.html
trst_and_srst separate srst_gates_jtag trst_push_pull srst_open_drain connect_deassert_srst
adapter speed: 1000 kHz
jtag_ntrst_delay: 500
Info : clock speed 1000 kHz
Info : JTAG tap: rpi3.dap tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4)
Info : rpi3.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.cpu1: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.cpu2: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.cpu3: hardware has 6 breakpoints, 4 watchpoints
```
Then, from another console, use telnet to connect to openocd. Type `targets` to show status.
```console
$ telnet localhost 4444
Trying ::1...
Connection failed: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> targets
TargetName Type Endian TapName State
-- ------------------ ---------- ------ ------------------ ------------
0 rpi3.cpu aarch64 little rpi3.dap running
1 rpi3.cpu1 aarch64 little rpi3.dap running
2 rpi3.cpu2 aarch64 little rpi3.dap running
3* rpi3.cpu3 aarch64 little rpi3.dap running
```
If the Raspberry Pi is running and configured correctly, `State` will be `running`.
You can change target cpu and break it.
```console
> targets rpi3.cpu # switch to core0
> halt # stop CPU
number of cache level 2
cache l2 present :not supported
rpi3.cpu cluster 0 core 0 multi core
target state: halted
target halted in ARM64 state due to debug-request, current mode: EL2H
cpsr: 0x600003c9 pc: 0x8004c
MMU: disabled, D-Cache: disabled, I-Cache: disabled
> reg # show registers
===== arm v8 registers
(0) x0 (/64): 0x0000000000000000 (dirty)
(1) x1 (/64): 0x0000000000080000
...
(29) x29 (/64): 0xE55C2E08279A78D0
(30) x30 (/64): 0x0000000000080080
(31) sp (/64): 0x0000000000080000
(32) pc (/64): 0x000000000008004C
(33) CPSR (/32): 0x600003C9
```
In this timing, value of `pc` may point an address of the empty loop.
## Build/setup openocd directly
Alternatively, you can build openocd to install to your local machine.
### Installing Openocd on Ubuntu 18.04
Unfortunately, openocd from apt on Ubuntu 18.04 does not support ARMv8; we need to build it.
```bash
sudo apt install build-essential automake libtool libudev-dev pkg-config libusb-1.0-0-dev gcc-6
git clone https://github.com/daniel-k/openocd.git openocd-armv8
cd openocd-armv8
git checkout origin/armv8
./bootstrap
CC=gcc-6 ./configure --enable-ftdi
# Error on gcc 7.3
make
sudo make install
```
### Running debugger
```console
# go to the repository you built to find config files
$ cd openocd-armv8
$ sudo openocd -f tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f tcl/target/rpi3.cfg
```
Now it waits connection at 3333/4444; you can connect as same as when using docker.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

@ -1,48 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
.section ".text.boot"
.global _boot_cores
_boot_cores:
// read cpu id, stop slave cores
mrs x1, mpidr_el1
and x1, x1, #3
cbz x1, 2f
// cpu id > 0, stop
1: wfe
b 1b
2: // cpu id == 0
// set stack before our code
ldr x1, =_boot_cores
mov sp, x1
// jump to Rust code, should not return
bl reset
// for failsafe, halt this core too
b 1b

@ -1,120 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2019 Nao Taco <naotaco@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use super::MMIO_BASE;
// use core::ops;
use register::{mmio::ReadWrite, register_bitfields};
register_bitfields! {
u32,
/// function Select 2
GPFSEL2 [
// based on 6.2 Alternative Function Assignments in BCM2835 ARM Peripherals
FSEL27 OFFSET(21) NUMBITS(3)[ // GPIO27
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011, // JTAG ARM debug: ARM_TMS
Alt5 = 0b010
],
FSEL26 OFFSET(18) NUMBITS(3)[ // GPIO26
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011, // JTAG ARM debug: ARM_TDI
Alt5 = 0b010
],
FSEL25 OFFSET(15) NUMBITS(3)[ // GPIO25
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011, // JTAG ARM debug: ARM_TCK
Alt5 = 0b010
],
FSEL24 OFFSET(12) NUMBITS(3)[ // GPIO24
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011, // JTAG ARM debug: ARM_TDO
Alt5 = 0b010
],
FSEL23 OFFSET(9) NUMBITS(3)[ // GPIO23
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011, // JTAG ARM debug: ARM_RTCK
Alt5 = 0b010
],
FSEL22 OFFSET(6) NUMBITS(3)[ // GPIO22
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011, // JTAG ARM debug: ARM_TRST
Alt5 = 0b010
]
]
}
const GPFSEL2: *const ReadWrite<u32, GPFSEL2::Register> =
(MMIO_BASE + 0x0020_0008) as *const ReadWrite<u32, GPFSEL2::Register>;
pub fn setup_debug() {
unsafe {
(*GPFSEL2).modify(
GPFSEL2::FSEL27::Alt4
+ GPFSEL2::FSEL26::Alt4
+ GPFSEL2::FSEL25::Alt4
+ GPFSEL2::FSEL24::Alt4
+ GPFSEL2::FSEL23::Alt4
+ GPFSEL2::FSEL22::Alt4,
);
}
}

@ -1 +0,0 @@
newline_style = "Unix"

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 KiB

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Loading…
Cancel
Save