esp_hal/soc/
mod.rs

1#![cfg_attr(not(feature = "rt"), expect(unused))]
2
3use core::ops::Range;
4
5use portable_atomic::{AtomicU8, Ordering};
6
7pub use self::implementation::*;
8
9#[cfg_attr(esp32, path = "esp32/mod.rs")]
10#[cfg_attr(esp32c2, path = "esp32c2/mod.rs")]
11#[cfg_attr(esp32c3, path = "esp32c3/mod.rs")]
12#[cfg_attr(esp32c6, path = "esp32c6/mod.rs")]
13#[cfg_attr(esp32h2, path = "esp32h2/mod.rs")]
14#[cfg_attr(esp32s2, path = "esp32s2/mod.rs")]
15#[cfg_attr(esp32s3, path = "esp32s3/mod.rs")]
16mod implementation;
17
18mod efuse_field;
19
20#[cfg(feature = "psram")]
21mod psram_common;
22
23// Using static mut should be fine since we are only writing to it once during
24// initialization. As other tasks and interrupts are not running yet, the worst
25// that can happen is, that the user creates a DMA buffer before initializing
26// the HAL. This will access the PSRAM range, returning an empty range - which
27// is, at that point, true. The user has no (safe) means to allocate in PSRAM
28// before initializing the HAL.
29#[cfg(feature = "psram")]
30static mut MAPPED_PSRAM: MappedPsram = MappedPsram { memory_range: 0..0 };
31
32pub(crate) fn psram_range() -> Range<usize> {
33    cfg_if::cfg_if! {
34        if #[cfg(feature = "psram")] {
35            #[allow(static_mut_refs)]
36            unsafe { MAPPED_PSRAM.memory_range.clone() }
37        } else {
38            0..0
39        }
40    }
41}
42
43#[cfg(feature = "psram")]
44pub struct MappedPsram {
45    memory_range: Range<usize>,
46}
47
48// Indicates the state of setting the mac address
49// 0 -- unset
50// 1 -- in the process of being set
51// 2 -- set
52//
53// Values other than 0 indicate that we cannot attempt setting the mac address
54// again, and values other than 2 indicate that we should read the mac address
55// from eFuse.
56#[cfg_attr(not(feature = "unstable"), allow(unused))]
57static MAC_OVERRIDE_STATE: AtomicU8 = AtomicU8::new(0);
58#[cfg_attr(not(feature = "unstable"), allow(unused))]
59static mut MAC_OVERRIDE: [u8; 6] = [0; 6];
60
61/// Error indicating issues with setting the MAC address.
62#[derive(PartialEq, Eq, Copy, Clone, Debug)]
63#[cfg_attr(not(feature = "unstable"), allow(unused))]
64pub enum SetMacError {
65    /// The MAC address has already been set and cannot be changed.
66    AlreadySet,
67}
68
69#[cfg_attr(not(feature = "unstable"), allow(unused))]
70impl self::efuse::Efuse {
71    /// Set the base mac address
72    ///
73    /// The new value will be returned by `read_mac_address` instead of the one
74    /// hard-coded in eFuse. This does not persist across device resets.
75    ///
76    /// Can only be called once. Returns `Err(SetMacError::AlreadySet)`
77    /// otherwise.
78    pub fn set_mac_address(mac: [u8; 6]) -> Result<(), SetMacError> {
79        if MAC_OVERRIDE_STATE
80            .compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed)
81            .is_err()
82        {
83            return Err(SetMacError::AlreadySet);
84        }
85
86        unsafe {
87            MAC_OVERRIDE = mac;
88        }
89
90        MAC_OVERRIDE_STATE.store(2, Ordering::Relaxed);
91
92        Ok(())
93    }
94
95    /// Get base mac address
96    ///
97    /// By default this reads the base mac address from eFuse, but it can be
98    /// overridden by `set_mac_address`.
99    pub fn mac_address() -> [u8; 6] {
100        if MAC_OVERRIDE_STATE.load(Ordering::Relaxed) == 2 {
101            unsafe { MAC_OVERRIDE }
102        } else {
103            Self::read_base_mac_address()
104        }
105    }
106}
107
108#[allow(unused)]
109pub(crate) fn is_valid_ram_address(address: usize) -> bool {
110    addr_in_range(address, memory_range!("DRAM"))
111}
112
113#[allow(unused)]
114pub(crate) fn is_slice_in_dram<T>(slice: &[T]) -> bool {
115    slice_in_range(slice, memory_range!("DRAM"))
116}
117
118#[allow(unused)]
119pub(crate) fn is_valid_psram_address(address: usize) -> bool {
120    addr_in_range(address, psram_range())
121}
122
123#[allow(unused)]
124pub(crate) fn is_slice_in_psram<T>(slice: &[T]) -> bool {
125    slice_in_range(slice, psram_range())
126}
127
128#[allow(unused)]
129pub(crate) fn is_valid_memory_address(address: usize) -> bool {
130    is_valid_ram_address(address) || is_valid_psram_address(address)
131}
132
133fn slice_in_range<T>(slice: &[T], range: Range<usize>) -> bool {
134    let slice = slice.as_ptr_range();
135    let start = slice.start as usize;
136    let end = slice.end as usize;
137    // `end` is >= `start`, so we don't need to check that `end > range.start`
138    // `end` is also one past the last element, so it can be equal to the range's
139    // end which is also one past the memory region's last valid address.
140    addr_in_range(start, range.clone()) && end <= range.end
141}
142
143pub(crate) fn addr_in_range(addr: usize, range: Range<usize>) -> bool {
144    range.contains(&addr)
145}
146
147#[cfg(feature = "rt")]
148#[cfg(riscv)]
149#[unsafe(export_name = "hal_main")]
150fn hal_main(a0: usize, a1: usize, a2: usize) -> ! {
151    unsafe extern "Rust" {
152        // This symbol will be provided by the user via `#[entry]`
153        fn main(a0: usize, a1: usize, a2: usize) -> !;
154    }
155
156    setup_stack_guard();
157
158    unsafe {
159        main(a0, a1, a2);
160    }
161}
162
163#[cfg(xtensa)]
164#[cfg(feature = "rt")]
165#[unsafe(no_mangle)]
166#[cfg_attr(esp32s3, unsafe(link_section = ".rwtext"))]
167unsafe extern "C" fn ESP32Reset() -> ! {
168    unsafe {
169        configure_cpu_caches();
170    }
171
172    /// The ESP32 has a first stage bootloader that handles loading program data
173    /// into the right place therefore we skip loading it again. This function
174    /// is called by xtensa-lx-rt in Reset.
175    #[doc(hidden)]
176    #[unsafe(no_mangle)]
177    pub extern "Rust" fn __init_data() -> bool {
178        false
179    }
180
181    // These symbols come from `memory.x`
182    unsafe extern "C" {
183        static mut _rtc_fast_bss_start: u32;
184        static mut _rtc_fast_bss_end: u32;
185        static mut _rtc_fast_persistent_start: u32;
186        static mut _rtc_fast_persistent_end: u32;
187
188        static mut _rtc_slow_bss_start: u32;
189        static mut _rtc_slow_bss_end: u32;
190        static mut _rtc_slow_persistent_start: u32;
191        static mut _rtc_slow_persistent_end: u32;
192
193        static mut _stack_start_cpu0: u32;
194
195        static mut __stack_chk_guard: u32;
196    }
197
198    // set stack pointer to end of memory: no need to retain stack up to this point
199    unsafe {
200        xtensa_lx::set_stack_pointer(core::ptr::addr_of_mut!(_stack_start_cpu0));
201    }
202
203    // copying data from flash to various data segments is done by the bootloader
204    // initialization to zero needs to be done by the application
205
206    // Initialize RTC RAM
207    unsafe {
208        xtensa_lx_rt::zero_bss(
209            core::ptr::addr_of_mut!(_rtc_fast_bss_start),
210            core::ptr::addr_of_mut!(_rtc_fast_bss_end),
211        );
212        xtensa_lx_rt::zero_bss(
213            core::ptr::addr_of_mut!(_rtc_slow_bss_start),
214            core::ptr::addr_of_mut!(_rtc_slow_bss_end),
215        );
216    }
217    if matches!(
218        crate::system::reset_reason(),
219        None | Some(crate::rtc_cntl::SocResetReason::ChipPowerOn)
220    ) {
221        unsafe {
222            xtensa_lx_rt::zero_bss(
223                core::ptr::addr_of_mut!(_rtc_fast_persistent_start),
224                core::ptr::addr_of_mut!(_rtc_fast_persistent_end),
225            );
226            xtensa_lx_rt::zero_bss(
227                core::ptr::addr_of_mut!(_rtc_slow_persistent_start),
228                core::ptr::addr_of_mut!(_rtc_slow_persistent_end),
229            );
230        }
231    }
232
233    setup_stack_guard();
234
235    crate::interrupt::setup_interrupts();
236
237    // continue with default reset handler
238    unsafe { xtensa_lx_rt::Reset() }
239}
240
241#[cfg(feature = "rt")]
242#[unsafe(export_name = "__stack_chk_fail")]
243unsafe extern "C" fn stack_chk_fail() {
244    panic!("Stack corruption detected");
245}
246
247#[cfg(feature = "rt")]
248fn setup_stack_guard() {
249    unsafe extern "C" {
250        static mut __stack_chk_guard: u32;
251    }
252
253    unsafe {
254        let stack_chk_guard = core::ptr::addr_of_mut!(__stack_chk_guard);
255        // we _should_ use a random value but we don't have a good source for random
256        // numbers here
257        stack_chk_guard.write_volatile(esp_config::esp_config_int!(
258            u32,
259            "ESP_HAL_CONFIG_STACK_GUARD_VALUE"
260        ));
261    }
262}