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}