avr_progmem/raw.rs
1//! Raw direct progmem access
2//!
3//! This module provides functions to directly access the progmem, such as
4//! [read_value].
5//!
6//! It is recommended to use best-effort wrappers in [wrapper](crate::wrapper)
7//! and [string](crate::string), which use these functions internally.
8//! This is in particular, because having a raw `static` that is stored in the
9//! progmem is very hazardous since Rust does not understand the difference
10//! between the normal data memory domain and the program memory domain, and
11//! allows safe code to directly access those raw progmem statics, which is
12//! **undefined behavior**.
13//! The wrapper types in [wrapper](crate::wrapper) and [string](crate::string),
14//! prevent safe code from directly accessing these statics and only offer
15//! dedicated accessor methods that first load the data into the normal data
16//! memory domain via the function of this module.
17
18
19#[cfg(all(target_arch = "avr", not(doc)))]
20use core::arch::asm;
21use core::mem::size_of;
22use core::mem::MaybeUninit;
23
24use cfg_if::cfg_if;
25
26
27
28/// Read a single byte from the progmem.
29///
30/// This function reads just a single byte from the program code memory domain.
31/// Thus this is essentially a Rust function around the AVR `lpm` instruction.
32///
33/// If you need to read from an array you might use [`read_slice`] or
34/// just generally for any value (including arrays) [`read_value`].
35///
36/// ## Example
37///
38/// ```
39/// use avr_progmem::raw::read_byte;
40/// use core::ptr::addr_of;
41///
42/// // This static must never be directly dereferenced/accessed!
43/// // So a `let data: u8 = P_BYTE;` is Undefined Behavior!!!
44/// /// Static byte stored in progmem!
45/// #[link_section = ".progmem.data"]
46/// static P_BYTE: u8 = b'A';
47///
48/// // Load the byte from progmem
49/// // Here, it is sound, because due to the link_section it is indeed in the
50/// // program code memory.
51/// let data: u8 = unsafe { read_byte(addr_of!(P_BYTE)) };
52/// assert_eq!(b'A', data);
53/// ```
54///
55///
56/// # Safety
57///
58/// The given point must be valid in the program domain.
59/// Notice that in AVR normal pointers (to data) are into the data domain,
60/// NOT the program domain.
61///
62/// Typically only function pointers (which make no sense here) and pointer to
63/// or into statics that are defined to be stored into progmem are valid.
64/// For instance, a valid progmem statics would be one, that is attributed with
65/// `#[link_section = ".progmem.data"]`.
66///
67/// Also general Rust pointer dereferencing constraints apply (see [`core::ptr::read`]).
68///
69/// [`read_slice`]: fn.read_slice.html
70/// [`read_value`]: fn.read_value.html
71///
72pub unsafe fn read_byte(p_addr: *const u8) -> u8 {
73 cfg_if! {
74 if #[cfg(all(target_arch = "avr", not(doc)))] {
75 // Only addresses below the 64 KiB limit are supported!
76 // Apparently this is of no concern for architectures with true
77 // 16-bit pointers.
78 // TODO: switch to use the extended lpm instruction if >64k
79 assert!(p_addr as usize <= u16::MAX as usize);
80
81 // Allocate a byte for the output (actually a single register r0
82 // will be used).
83 let res: u8;
84
85 // The inline assembly to read a single byte from given address
86 unsafe {
87 asm!(
88 // Just issue the single `lpm` assembly instruction, which reads
89 // implicitly indirectly the address from the Z register, and
90 // stores implicitly the read value in the register 0.
91 "lpm {}, Z",
92 // Output is in a register
93 out(reg) res,
94 // Input the program memory address to read from
95 in("Z") p_addr,
96 // No clobber list.
97 );
98 }
99
100 // Just output the read value
101 res
102
103 } else if #[cfg(not(target_arch = "avr"))] {
104 // This is the non-AVR dummy.
105 // We have to assume that otherwise a normal data or text segment
106 // would be used, and thus that it is actually save to access it
107 // directly!
108
109 unsafe {
110 // SAFETY: we are not on AVR, thus all data must be in some
111 // sort of data domain, because we only support the special
112 // program domain on AVR.
113 //
114 // Consequently, it is sound to just dereference the pointer
115 // to data.
116 *p_addr
117 }
118 } else {
119 // Special case, this neither possibly documentation on AVR, any
120 // it case is problematic, so if we reach this, we just abort via
121 // panic.
122 unreachable!("You should not execute code, compiled in `doc` mode");
123 }
124 }
125}
126
127
128
129/// Read an array of type `T` from progmem into data array.
130///
131/// This function uses the optimized `read_asm_loop_raw` with a looped
132/// assembly instead of byte-wise `read_byte` function.
133///
134///
135/// # Safety
136///
137/// This call is analog to `core::ptr::copy(p_addr, out, len as usize)` thus it
138/// has the same basic requirements such as both pointers must be valid for
139/// dereferencing i.e. not dangling and both pointers must
140/// be valid to read or write, respectively, of `len` many elements of type `T`,
141/// i.e. `len * size_of::<T>()` bytes.
142///
143/// Additionally, `p_addr` must be a valid pointer into the program memory
144/// domain. And `out` must be valid point to a writable location in the data
145/// memory.
146///
147/// However alignment is not strictly required for AVR, since the read/write is
148/// done byte-wise, but the non-AVR fallback dose actually use
149/// `core::ptr::copy` and therefore the pointers must be aligned.
150///
151#[cfg_attr(feature = "dev", inline(never))]
152unsafe fn read_asm_loop_raw<T>(p_addr: *const T, out: *mut T, len: u8) {
153 // Here are the general requirements essentially required by the AVR-impl
154 // However, assume, the non-AVR version is only used in tests, it makes a
155 // lot of sens to ensure the AVR requirements are held up.
156
157 // Loop head check, just return for zero iterations
158 if len == 0 || size_of::<T>() == 0 {
159 return;
160 }
161
162 // Get size in bytes of T
163 let size_type = size_of::<T>();
164 // Must not exceed 256 byte
165 assert!(size_type <= u8::MAX as usize);
166
167 // Multiply with the given length
168 let size_bytes = size_type * len as usize;
169 // Must still not exceed 256 byte
170 assert!(size_bytes <= u8::MAX as usize);
171 // Now its fine to cast down to u8
172 let size_bytes = size_bytes as u8;
173
174
175 cfg_if! {
176 if #[cfg(all(target_arch = "avr", not(doc)))] {
177 // Only addresses below the 64 KiB limit are supported
178 // Apparently this is of no concern for architectures with true
179 // 16-bit pointers.
180 // TODO: switch to use the extended lpm instruction if >64k
181 assert!(p_addr as usize <= u16::MAX as usize);
182
183 // Some dummy variables so we can define "output" for our assembly.
184 // In fact, we do not have outputs, but need to modify the
185 // registers, thus we just mark them as "outputs".
186 let mut _a: u8;
187 let mut _b: *const ();
188 let mut _c: *mut ();
189 let mut _d: u8;
190
191 // A loop to read a slice of T from prog memory
192 // The prog memory address (addr) is stored in the 16-bit address
193 // register Z (since this is the default register for the `lpm`
194 // instruction).
195 // The output data memory address (out) is stored in the 16-bit
196 // address register X, because Z is already used and Y seams to be
197 // used otherwise or is callee-save, whatever, it emits more
198 // instructions by llvm.
199 //
200 // This loop appears in the assembly, because it allows to exploit
201 // `lpm 0, Z+` instruction that simultaneously increments the
202 // pointer, and allows to write a very compact loop.
203 unsafe {
204 asm!(
205 "
206 // load value from program memory at indirect Z into temp
207 // register $3 and post-increment Z by one
208 lpm {1}, Z+
209
210 // write register $3 to data memory at indirect X
211 // and post-increment X by one
212 st X+, {1}
213
214 // Decrement the loop counter in register $0 (size_bytes).
215 // If zero has been reached the equality flag is set.
216 subi {0}, 1
217
218 // Check whether the end has not been reached and if so jump back.
219 // The end is reached if $0 (size_bytes) == 0, i.e. equality flag
220 // is set.
221 // Thus if equality flag is NOT set (brNE) jump back by 4
222 // instruction, that are all instructions in this assembly.
223 // Notice: 4 instructions = 8 Byte
224 brne -8
225 ",
226 // Some register for counting the number of bytes, gets modified
227 inout(reg) size_bytes => _,
228 // Some scratch register, just clobber
229 out(reg) _,
230 // Input address in Z, gets modified
231 inout("Z") p_addr => _,
232 // Output address in X, gets modified
233 inout("X") out => _,
234 );
235 }
236
237 } else if #[cfg(not(target_arch = "avr"))] {
238 // This is the non-AVR dummy.
239 // We have to assume that otherwise a normal data or text segment
240 // would be used, and thus that it is actually save to access it
241 // directly!
242
243 // Ignore the unused vars:
244 let _ = size_bytes;
245
246 unsafe {
247 // SAFETY: we are not on AVR, thus all data must be in some
248 // sort of data domain, because we only support the special
249 // program domain on AVR.
250 //
251 // Consequently, it is sound to just dereference the pointers
252 // to data.
253 core::ptr::copy(p_addr, out, len as usize);
254 }
255 } else {
256 // Special case, this neither possibly documentation on AVR, any
257 // it case is problematic, so if we reach this, we just abort via
258 // panic.
259 unreachable!("You should not execute code, compiled in `doc` mode");
260 }
261 }
262}
263
264
265/// Read an array of type `T` from progmem into data array.
266///
267/// This function uses either the optimized `read_asm_loop_raw` with a
268/// looped assembly instead of byte-wise `read_byte` function depending
269/// whether the `lpm-asm-loop` crate feature is set.
270///
271///
272/// # Safety
273///
274/// This call is analog to `core::ptr::copy(p_addr, out, len as usize)` thus it
275/// has the same basic requirements such as both pointers must be valid for
276/// dereferencing i.e. not dangling and both pointers must
277/// be valid to read or write, respectively, of `len` many elements of type `T`,
278/// i.e. `len * size_of::<T>()` bytes.
279///
280/// Additionally, `p_addr` must be a valid pointer into the program memory
281/// domain. And `out` must be valid point to a writable location in the data
282/// memory.
283///
284/// While the alignment is not strictly required for AVR, the non-AVR fallback
285/// might be done actually use `core::ptr::copy` and therefore the pointers
286/// must be aligned.
287///
288unsafe fn read_value_raw<T>(p_addr: *const T, out: *mut T, len: u8)
289where
290 T: Sized + Copy,
291{
292 unsafe {
293 // SAFETY: The caller must ensure the validity of the pointers
294 // and their domains.
295 read_asm_loop_raw(p_addr, out, len)
296 }
297}
298
299
300/// Read a single `T` from progmem and return it by value.
301///
302/// This function uses either a optimized assembly with loop or just a
303/// byte-wise assembly function which is looped outside depending on
304/// whether the `lpm-asm-loop` crate feature is set or not.
305///
306/// Notice that `T` might be also something like `[T, N]` so that in fact
307/// entire arrays can be loaded using this function.
308///
309/// If you need to read just a single byte you might use [`read_byte`].
310///
311/// ## Example
312///
313/// ```
314/// use avr_progmem::raw::read_value;
315/// use core::ptr::addr_of;
316///
317/// // This static must never be directly dereferenced/accessed!
318/// // So a `let data: [u8;11] = P_ARRAY;` is Undefined Behavior!!!
319/// // Also notice the `*` in front of the string, because we want to store the
320/// // data, not just a reference!
321/// /// Static bytes stored in progmem!
322/// #[link_section = ".progmem.data"]
323/// static P_ARRAY: [u8;11] = *b"Hello World";
324///
325/// // Load the bytes from progmem
326/// // Here, it is sound, because due to the link_section it is indeed in the
327/// // program code memory.
328/// let data: [u8;11] = unsafe { read_value(addr_of!(P_ARRAY)) };
329/// assert_eq!(b"Hello World", &data);
330/// ```
331///
332/// Also statically sized sub-arrays can be loaded using this function:
333///
334/// ```
335/// use std::convert::TryInto;
336/// use avr_progmem::raw::read_value;
337///
338/// /// Static bytes stored in progmem!
339/// #[link_section = ".progmem.data"]
340/// static P_ARRAY: [u8;11] = *b"Hello World";
341///
342/// // Get a sub-array reference without dereferencing it
343///
344/// // Make sure that we convert from &[T] directly to &[T;M] without
345/// // constructing an actual [T;M], because we MAY NOT LOAD THE DATA!
346/// // Also notice, that this sub-slicing does ensure that the bound are
347/// // correct.
348/// let slice: &[u8] = &P_ARRAY[6..];
349/// let array: &[u8;5] = slice.try_into().unwrap();
350///
351/// // Load the bytes from progmem
352/// // Here, it is sound, because due to the link_section it is indeed in the
353/// // program code memory.
354/// let data: [u8;5] = unsafe { read_value(array) };
355/// assert_eq!(b"World", &data);
356/// ```
357///
358/// # Panics
359///
360/// This function panics, if the size of the value (i.e. `size_of::<T>()`)
361/// is beyond 255 bytes.
362/// However, this is currently just a implementation limitation, which may
363/// be lifted in the future.
364///
365///
366/// # Safety
367///
368/// This call is analog to [`core::ptr::copy`] thus it
369/// has the same basic requirements such as the pointer must be valid for
370/// dereferencing i.e. not dangling and the pointer must
371/// be valid to read one entire value of type `T`,
372/// i.e. `size_of::<T>()` bytes.
373///
374/// Additionally, `p_addr` must be a valid pointer into the program memory
375/// domain.
376///
377/// While the alignment is not strictly required for AVR, the non-AVR fallback
378/// might be actually using `core::ptr::copy` and therefore the pointers
379/// must be aligned.
380///
381/// [`read_byte`]: fn.read_byte.html
382/// [`read_slice`]: fn.read_slice.html
383///
384#[cfg_attr(feature = "dev", inline(never))]
385pub unsafe fn read_value<T>(p_addr: *const T) -> T
386where
387 T: Sized + Copy,
388{
389 // The use of an MaybeUninit allows us to correctly allocate the space
390 // required to hold one `T`, whereas we correctly communicate that it is
391 // uninitialized to the compiler.
392 //
393 // The alternative of using a [0u8; size_of::<T>()] is actually much more
394 // cumbersome as it also removes the type inference of `read_value_raw` and
395 // still requires a `transmute` in the end.
396 let mut buffer = MaybeUninit::<T>::uninit();
397
398 let size = size_of::<T>();
399 // TODO add a local loop to process bigger chunks in 256 Byte blocks
400 assert!(size <= u8::MAX as usize);
401
402 let res: *mut T = buffer.as_mut_ptr();
403
404 unsafe {
405 // SAFETY: The soundness of this call is directly derived from the
406 // prerequisite as defined by the Safety section of this function.
407 //
408 // Additionally, the use of the MaybeUninit there is also sound, because it
409 // only written to and never read and not even a Rust reference is created
410 // to it.
411 read_value_raw(p_addr, res, 1);
412 }
413
414 unsafe {
415 // SAFETY: After `read_value_raw` returned, it wrote an entire `T` into
416 // the `res` pointer, which is baked by this `buffer`.
417 // Thus it is now properly initialized, and this call is sound.
418 buffer.assume_init()
419 }
420}