1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use color_eyre::eyre::Result;
use object::read::{File as ElfFile, Object, ObjectSymbol};
use postform_decoder::{Decoder, ElfMetadata, LogLevel};
use probe_rs::{
    flashing::{download_file, Format},
    MemoryInterface, Session,
};
use probe_rs_rtt::{Rtt, ScanRegion};
use std::{
    fs,
    path::PathBuf,
    sync::{Arc, Mutex},
    time::Duration,
};
use termion::color;

/// RTT Errors for Postform Rtt
#[derive(Debug, thiserror::Error)]
pub enum RttError {
    #[error("Missing symbol {0}")]
    MissingSymbol(&'static str),
}

/// Downloads a FW ELF to the target in the associated session, halting the core at main.
pub fn download_firmware(session: &Arc<Mutex<Session>>, elf_path: &PathBuf) -> Result<()> {
    let mut mutex_guard = session.lock().unwrap();
    println!("Loading FW to target");
    download_file(&mut mutex_guard, &elf_path, Format::Elf)?;

    let file_contents = fs::read(elf_path)?;
    let elf_file = ElfFile::parse(&file_contents[..])?;
    let main = elf_file
        .symbols()
        .find(|s| s.name().unwrap() == "main")
        .ok_or(RttError::MissingSymbol("main"))?;

    let mut core = mutex_guard.core(0).unwrap();
    let _ = core.reset_and_halt(Duration::from_millis(10))?;
    core.set_hw_breakpoint(main.address() as u32)?;
    core.run()?;
    core.wait_for_core_halted(Duration::from_secs(1))?;
    println!("Download complete!");

    Ok(())
}

/// Selects the execution mode for RTT in the target.
#[derive(Debug)]
pub enum RttMode {
    /// The target does not block when the buffer is full, overflowing it.
    NonBlocking = 1,
    /// The target blocks until the buffer is ready to receive more data.
    Blocking = 2,
}

/// Configures the selected RTT mode in the RTT control block at the given address.
pub fn configure_rtt_mode(
    session: &Arc<Mutex<Session>>,
    rtt_addr: u64,
    mode: RttMode,
) -> Result<()> {
    let mut session_lock = session.lock().unwrap();
    let mut core = session_lock.core(0)?;
    let mode_flags_addr = rtt_addr as u32 + 44u32;
    println!("Setting mode to {:?}", mode);
    core.write_word_32(mode_flags_addr, mode as u32)?;

    Ok(())
}

/// Runs the core and clears all breakpoints
pub fn run_core(session: Arc<Mutex<Session>>) -> Result<()> {
    let mut mutex_guard = session.lock().unwrap();
    let mut core = mutex_guard.core(0)?;
    core.clear_all_hw_breakpoints()?;
    core.run()?;
    Ok(())
}

fn color_for_level(level: LogLevel) -> String {
    match level {
        LogLevel::Debug => String::from(color::Green.fg_str()),
        LogLevel::Info => String::from(color::Yellow.fg_str()),
        LogLevel::Warning => color::Rgb(255u8, 0xA5u8, 0u8).fg_string(),
        LogLevel::Error => String::from(color::Red.fg_str()),
        LogLevel::Unknown => color::Rgb(255u8, 0u8, 0u8).fg_string(),
    }
}

/// Decodes a log from the buffer and prints it to stdout.
pub fn handle_log(elf_metadata: &ElfMetadata, buffer: &[u8]) {
    let mut decoder = Decoder::new(&elf_metadata);
    match decoder.decode(buffer) {
        Ok(log) => {
            println!(
                "{timestamp:<12.6} {color}{level:<11}{reset_color}: {msg}",
                timestamp = log.timestamp,
                color = color_for_level(log.level),
                level = log.level.to_string(),
                reset_color = color::Fg(color::Reset),
                msg = log.message
            );
            println!(
                "{color}└── File: {file_name}, Line number: {line_number}{reset}",
                color = color::Fg(color::LightBlack),
                file_name = log.file_name,
                line_number = log.line_number,
                reset = color::Fg(color::Reset)
            );
        }
        Err(error) => {
            println!(
                "{color}Error parsing log:{reset_color} {error}.",
                color = color::Fg(color::Red),
                error = error,
                reset_color = color::Fg(color::Reset)
            );
        }
    }
}

/// Attaches to RTT at the address of the `_SEGGER_RTT` symbol
pub fn attach_rtt(session: Arc<Mutex<Session>>, elf_file: &ElfFile) -> Result<Rtt> {
    let segger_rtt = elf_file
        .symbols()
        .find(|s| s.name().unwrap() == "_SEGGER_RTT")
        .ok_or(RttError::MissingSymbol("_SEGGER_RTT"))?;
    println!("Attaching RTT to address 0x{:x}", segger_rtt.address());
    let scan_region = ScanRegion::Exact(segger_rtt.address() as u32);
    Ok(Rtt::attach_region(session, &scan_region)?)
}