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
use super::*;
use std::alloc;
use std::ffi::CString;
use std::path::Path;
use std::ptr::NonNull;
pub struct Firmware {
ptr: NonNull<ffi::elf_firmware_t>,
}
impl Firmware {
pub fn new() -> Self {
// Safety: We know that `elf_firmware_t`'s layout has a non-zero size.
//
// (we also use `alloc_zeroed`, because that's what simavr's docs
// suggest to do.)
let ptr = unsafe {
alloc::alloc_zeroed(alloc::Layout::new::<ffi::elf_firmware_t>())
as *mut ffi::elf_firmware_t
};
// Unwrap-safety: This can fail only if the underlying allocator failed
// to find enough memory to allocate the chunk - in that case panicking
// is the best we can afford anyway
let ptr = NonNull::new(ptr).unwrap();
Self { ptr }
}
/// Load a firmware file, ELF or HEX format, from filename.
/// Progname is the current program name for error messages
/// included here as it mostly specific to HEX files.
///
/// MCU type and frequency is intended to be used when the
/// hex file format is used because the hex format does not
/// contain the CPU and MHz of the target. It is not an error
/// to provide it when using elf file format, but the values
/// will be overwritten by the parameters loaded from elf file.
///
/// file_path - Firmware file
/// load_base - Base of load region
/// mmcu_type - MCU type (mmcu in elf_firmware_t) e.g. atmega2560
/// frequency - MCU frequency in Hz, e.g. 16000000
/// prog_name - For error messages
pub fn load(
mut self,
file_path: impl AsRef<Path>,
load_base: Option<u32>,
mmcu_type: Option<&str>,
frequency: Option<u32>,
prog_name: Option<&str>,
) -> Self {
let file_path = file_path.as_ref();
let load_base = load_base.unwrap_or(0);
let prog_name = match prog_name {
Some(prog_name) => prog_name.to_string(),
None => match file_path.file_name() {
Some(file_name) => file_name.to_string_lossy().to_string(),
None => "UNKNOWN".to_string(),
},
};
let file_path = file_path.display().to_string();
// Unwrap-safety: Paths cannot contain null-terminators, so a string
// we've got from `.display().to_string()` cannot either
let c_path = CString::new(file_path).unwrap();
let c_name = CString::new(prog_name).unwrap();
// Safety:
// 1. self.ptr is NonNull<T>
// 2. self is mut
let firmware = unsafe { self.ptr.as_mut() };
// Set the MCU frequency because it's not in the .hex file
if let Some(f) = frequency {
firmware.frequency = f;
}
// Set the MCU name because it's not in the .hex file
if let Some(m) = mmcu_type {
let mut mmcu = m.as_bytes().iter();
for c in firmware.mmcu.iter_mut() {
*c = (*mmcu.next().unwrap_or(&0_u8)) as i8;
}
if let Some(c) = firmware.mmcu.last_mut() {
*c = '\0' as i8;
}
}
// Safety: `self.ptr` points at a valid, zeroed instance of
// `elf_firmware_t`; `c_path` points at a valid `CString`
unsafe {
// WARNING:
// This function does not return a result.
// Upon errors it calls `exit(1)` inside.
// And there is nothing we can do about it.
// (Beside running as a consumable process)
ffi::sim_setup_firmware(
c_path.as_ptr(), // Firmware file
load_base, // Base of load region
firmware, // Data returned here
c_name.as_ptr(), // For error messages.
);
};
self
}
pub fn flash_to(self, avr: &mut Avr) {
avr.load_firmware(self.ptr);
}
}
impl Drop for Firmware {
fn drop(&mut self) {
unsafe {
alloc::dealloc(
self.ptr.as_ptr() as *mut u8,
alloc::Layout::new::<ffi::elf_firmware_t>(),
);
}
}
}