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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
//! Memory initialization code ("[crt0]") written in Rust. //! //! This crate is meant for bare metal systems where there is no ELF loader or //! OS to take care of initializing RAM for the program. It provides functions //! for initializing the `.data` and `.bss` sections. //! //! [crt0]: https://en.wikipedia.org/wiki/Crt0 //! //! # Initializing RAM //! //! On the linker script side, we must assign names (symbols) to the boundaries //! of the `.bss` and `.data` sections. For example: //! //! ```text //! .bss : ALIGN(4) //! { //! _sbss = .; //! *(.bss.*); //! _ebss = ALIGN(4); //! } > RAM //! //! .data : ALIGN(4) //! { //! _sdata = .; //! *(.data.*); //! _edata = ALIGN(4); //! } > RAM AT > FLASH //! //! _sidata = LOADADDR(.data); //! ``` //! //! This script defines symbols `_sbss`/`_ebss`, and `_sdata`/`_edata` to point //! at the boundaries of the `.bss` and `.data` sections in RAM, respectively. //! The `AT > FLASH` directive places the actual contents of the `.data` section //! in the `FLASH` memory region (which needs to be defined separately from this //! linker script snippet). Then `_sidata` is set to the address of that data in //! flash. //! //! Note that while `_sbss`, `_ebss`, `_sdata` and `_edata` are Virtual Memory //! Addresses (VMAs), `_sidata` is the Load Memory Address (LMA) of the `.data` //! section. //! //! On the Rust side, we must bind to those symbols using an `extern` block, //! and can then call into this crate to perform RAM initialization: //! //! ```no_run //! # use r0::{zero_bss, init_data}; //! unsafe fn before_main() { //! // The type, `u32`, indicates that the memory is 4-byte aligned //! extern "C" { //! static mut _sbss: u32; //! static mut _ebss: u32; //! //! static mut _sdata: u32; //! static mut _edata: u32; //! //! static _sidata: u32; //! } //! //! zero_bss(&mut _sbss, &mut _ebss); //! init_data(&mut _sdata, &mut _edata, &_sidata); //! } //! ``` //! //! # Minimum Supported Rust Version (MSRV) //! //! The MSRV of this release is Rust 1.31.0 #![deny(warnings)] #![no_std] #![doc(html_root_url = "https://docs.rs/r0/1.0.0")] #[cfg(test)] mod test; use core::{mem, ptr}; mod sealed { pub trait Sealed {} } /// Trait for machine word types. /// /// This trait is implemented by unsigned integers representing common machine /// word sizes. It can not be implemented by the user. /// /// Types implementing this trait can be used by the [`init_data`] and /// [`zero_bss`] functions. For that to be sound, all bit patterns need to be /// valid for the type, the type must implement `Copy`, and the type must not /// be zero-sized. /// /// [`init_data`]: fn.init_data.html /// [`zero_bss`]: fn.zero_bss.html pub unsafe trait Word: sealed::Sealed + Copy {} impl sealed::Sealed for u8 {} impl sealed::Sealed for u16 {} impl sealed::Sealed for u32 {} impl sealed::Sealed for u64 {} impl sealed::Sealed for u128 {} unsafe impl Word for u8 {} unsafe impl Word for u16 {} unsafe impl Word for u32 {} unsafe impl Word for u64 {} unsafe impl Word for u128 {} /// Initializes the `.data` section by copying it from the location indicated /// by `sidata`. /// /// # Arguments /// /// - `sdata`: Pointer to the start of the `.data` section in RAM. /// - `edata`: Pointer to the open/non-inclusive end of the `.data` section in /// RAM (the value behind this pointer will not be modified). /// - `sidata`: `.data` section Load Memory Address (LMA). Data will be copied /// from here. /// - Use `T` to indicate the alignment of the `.data` section and its LMA. /// /// # Safety /// /// - Must be called exactly once, before the application has started. /// - `edata >= sdata`. /// - The `sdata -> edata` region must not overlap with the `sidata -> ...` /// region. /// - `sdata`, `edata` and `sidata` must be `T` aligned. pub unsafe fn init_data<T>(mut sdata: *mut T, edata: *mut T, mut sidata: *const T) where T: Word, { while sdata < edata { ptr::write(sdata, ptr::read(sidata)); sdata = sdata.offset(1); sidata = sidata.offset(1); } } /// Zeroes the `.bss` section. /// /// # Arguments /// /// - `sbss`: Pointer to the start of the `.bss` section in RAM. /// - `ebss`: Pointer to the open/non-inclusive end of the `.bss` section in /// RAM (the value behind this pointer will not be modified). /// - Use `T` to indicate the alignment of the `.bss` section. /// /// # Safety /// /// - Must be called exactly once, before the application has started. /// - `ebss >= sbss`. /// - `sbss` and `ebss` must be `T` aligned. pub unsafe fn zero_bss<T>(mut sbss: *mut T, ebss: *mut T) where T: Word, { while sbss < ebss { // NOTE(volatile) to prevent this from being transformed into `memclr` ptr::write_volatile(sbss, mem::zeroed()); sbss = sbss.offset(1); } }