├── .cargo └── config ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── blink.bin ├── blinkpattern.bin ├── flash.py ├── memory.x ├── openocd.gdb └── src └── main.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabihf] 2 | runner = "arm-none-eabi-gdb.exe -q -x openocd.gdb" 3 | rustflags = ["-C", "link-arg=-Tlink.x"] 4 | 5 | [build] 6 | target = "thumbv7em-none-eabihf" 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aligned" 5 | version = "0.3.2" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" 8 | dependencies = [ 9 | "as-slice", 10 | ] 11 | 12 | [[package]] 13 | name = "as-slice" 14 | version = "0.1.3" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70" 17 | dependencies = [ 18 | "generic-array 0.12.3", 19 | "generic-array 0.13.2", 20 | "stable_deref_trait", 21 | ] 22 | 23 | [[package]] 24 | name = "bare-metal" 25 | version = "0.2.5" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 28 | dependencies = [ 29 | "rustc_version", 30 | ] 31 | 32 | [[package]] 33 | name = "bitflags" 34 | version = "1.2.1" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 37 | 38 | [[package]] 39 | name = "byteorder" 40 | version = "1.3.4" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 43 | 44 | [[package]] 45 | name = "cast" 46 | version = "0.2.3" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" 49 | dependencies = [ 50 | "rustc_version", 51 | ] 52 | 53 | [[package]] 54 | name = "cfg-if" 55 | version = "0.1.10" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 58 | 59 | [[package]] 60 | name = "cortex-m" 61 | version = "0.6.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" 64 | dependencies = [ 65 | "aligned", 66 | "bare-metal", 67 | "volatile-register", 68 | ] 69 | 70 | [[package]] 71 | name = "cortex-m-rt" 72 | version = "0.6.12" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28" 75 | dependencies = [ 76 | "cortex-m-rt-macros", 77 | "r0", 78 | ] 79 | 80 | [[package]] 81 | name = "cortex-m-rt-macros" 82 | version = "0.1.8" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "4717562afbba06e760d34451919f5c3bf3ac15c7bb897e8b04862a7428378647" 85 | dependencies = [ 86 | "proc-macro2", 87 | "quote", 88 | "syn", 89 | ] 90 | 91 | [[package]] 92 | name = "embedded-hal" 93 | version = "0.2.3" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" 96 | dependencies = [ 97 | "nb", 98 | "void", 99 | ] 100 | 101 | [[package]] 102 | name = "generic-array" 103 | version = "0.12.3" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" 106 | dependencies = [ 107 | "typenum", 108 | ] 109 | 110 | [[package]] 111 | name = "generic-array" 112 | version = "0.13.2" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" 115 | dependencies = [ 116 | "typenum", 117 | ] 118 | 119 | [[package]] 120 | name = "log" 121 | version = "0.4.8" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 124 | dependencies = [ 125 | "cfg-if", 126 | ] 127 | 128 | [[package]] 129 | name = "managed" 130 | version = "0.7.1" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" 133 | 134 | [[package]] 135 | name = "micromath" 136 | version = "1.0.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "c61d2b2dd22f6e15164f8ae1b6cec2028662c57e210c5809328bd065f92eba13" 139 | 140 | [[package]] 141 | name = "nb" 142 | version = "0.1.2" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" 145 | 146 | [[package]] 147 | name = "panic-halt" 148 | version = "0.2.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" 151 | 152 | [[package]] 153 | name = "panic-itm" 154 | version = "0.4.1" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "98830d17a95587207e41edaa3009b143d326ce134b0e3538ac98246a67d66cc3" 157 | dependencies = [ 158 | "cortex-m", 159 | ] 160 | 161 | [[package]] 162 | name = "proc-macro2" 163 | version = "1.0.18" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" 166 | dependencies = [ 167 | "unicode-xid", 168 | ] 169 | 170 | [[package]] 171 | name = "quote" 172 | version = "1.0.6" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" 175 | dependencies = [ 176 | "proc-macro2", 177 | ] 178 | 179 | [[package]] 180 | name = "r0" 181 | version = "0.2.2" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" 184 | 185 | [[package]] 186 | name = "rustboot" 187 | version = "0.1.0" 188 | dependencies = [ 189 | "cortex-m", 190 | "cortex-m-rt", 191 | "panic-halt", 192 | "panic-itm", 193 | "stm32-eth", 194 | "stm32f7xx-hal", 195 | ] 196 | 197 | [[package]] 198 | name = "rustc_version" 199 | version = "0.2.3" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 202 | dependencies = [ 203 | "semver", 204 | ] 205 | 206 | [[package]] 207 | name = "semver" 208 | version = "0.9.0" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 211 | dependencies = [ 212 | "semver-parser", 213 | ] 214 | 215 | [[package]] 216 | name = "semver-parser" 217 | version = "0.7.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 220 | 221 | [[package]] 222 | name = "smoltcp" 223 | version = "0.6.0" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" 226 | dependencies = [ 227 | "bitflags", 228 | "byteorder", 229 | "log", 230 | "managed", 231 | ] 232 | 233 | [[package]] 234 | name = "stable_deref_trait" 235 | version = "1.1.1" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" 238 | 239 | [[package]] 240 | name = "stm32-eth" 241 | version = "0.1.2" 242 | source = "git+https://github.com/dtjones190/stm32-eth.git#96c14a66384c98292acfc0eab931da77a32b3e4c" 243 | dependencies = [ 244 | "aligned", 245 | "cortex-m", 246 | "log", 247 | "smoltcp", 248 | "stm32f7xx-hal", 249 | "volatile-register", 250 | ] 251 | 252 | [[package]] 253 | name = "stm32f7" 254 | version = "0.9.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "3bc0268b8d03ae113a370f755ae4c52ac73b7f2a2123e9f8f2ead6ee3a1d15f2" 257 | dependencies = [ 258 | "bare-metal", 259 | "cortex-m", 260 | "cortex-m-rt", 261 | "vcell", 262 | ] 263 | 264 | [[package]] 265 | name = "stm32f7xx-hal" 266 | version = "0.1.0" 267 | source = "git+https://github.com/stm32-rs/stm32f7xx-hal.git#a66f6d2b6f1864df0379524c96ba98d631f57f1a" 268 | dependencies = [ 269 | "as-slice", 270 | "bare-metal", 271 | "cast", 272 | "cortex-m", 273 | "cortex-m-rt", 274 | "embedded-hal", 275 | "micromath", 276 | "nb", 277 | "stm32f7", 278 | "void", 279 | ] 280 | 281 | [[package]] 282 | name = "syn" 283 | version = "1.0.30" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" 286 | dependencies = [ 287 | "proc-macro2", 288 | "quote", 289 | "unicode-xid", 290 | ] 291 | 292 | [[package]] 293 | name = "typenum" 294 | version = "1.12.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 297 | 298 | [[package]] 299 | name = "unicode-xid" 300 | version = "0.2.0" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 303 | 304 | [[package]] 305 | name = "vcell" 306 | version = "0.1.2" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" 309 | 310 | [[package]] 311 | name = "void" 312 | version = "1.0.2" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 315 | 316 | [[package]] 317 | name = "volatile-register" 318 | version = "0.2.0" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" 321 | dependencies = [ 322 | "vcell", 323 | ] 324 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustboot" 3 | version = "0.1.0" 4 | authors = ["karthick "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | cortex-m = "^0.6.0" 11 | cortex-m-rt = "^0.6.0" 12 | panic-halt = "0.2.0" 13 | panic-itm = "0.4" 14 | 15 | [dependencies.stm32f7xx-hal] 16 | git = "https://github.com/stm32-rs/stm32f7xx-hal.git" 17 | branch = "master" 18 | version = "0.1.0" 19 | features = ["rt", "stm32f767"] # replace the model of your microcontroller here 20 | 21 | [dependencies.stm32-eth] 22 | version = "0.1.2" 23 | git = "https://github.com/dtjones190/stm32-eth.git" 24 | branch = "master" 25 | features = ["smoltcp-phy", "nucleo-f767zi"] 26 | 27 | 28 | [profile.release] 29 | debug = true 30 | lto = true 31 | codegen-units = 1 32 | incremental = false 33 | opt-level = "s" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rustboot 2 | Embedded Rust Bootloader for STM32F7 Microcontroller 3 | -------------------------------------------------------------------------------- /blink.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthickai/rustboot/248559af81ee23201fc31a902450acf2a844d3ba/blink.bin -------------------------------------------------------------------------------- /blinkpattern.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthickai/rustboot/248559af81ee23201fc31a902450acf2a844d3ba/blinkpattern.bin -------------------------------------------------------------------------------- /flash.py: -------------------------------------------------------------------------------- 1 | from cfonts import render, say 2 | import time 3 | import struct 4 | import socket 5 | import argparse 6 | import crcmod 7 | 8 | try: 9 | from tqdm import tqdm 10 | except ImportError: 11 | print("Notice: tqdm not installed, install for progress bars.") 12 | 13 | def tqdm(x, *args, **kwargs): 14 | return x 15 | 16 | 17 | commands = { 18 | "erase": 2, 19 | "write": 3, 20 | "boot": 4, 21 | } 22 | 23 | 24 | errors = { 25 | 0: "Success", 26 | 1: "Invalid Address", 27 | 2: "Length Not Multiple of 4", 28 | 3: "Length Too Long", 29 | 4: "Data Length Incorrect", 30 | 5: "Erase Error", 31 | 6: "Write Error", 32 | 7: "Flash Error", 33 | 8: "Network Error", 34 | 9: "Internal Error", 35 | } 36 | 37 | 38 | class BootloaderError(Exception): 39 | def __init__(self, errno): 40 | self.errno = errno 41 | 42 | def __str__(self): 43 | if self.errno in errors: 44 | return "{}".format(errors[self.errno]) 45 | else: 46 | return "Unknown error {}".format(self.errno) 47 | 48 | 49 | class MismatchError(Exception): 50 | def __init__(self, addr, tx, rx): 51 | self.addr = addr 52 | self.tx = tx 53 | self.rx = rx 54 | 55 | def __str__(self): 56 | return "Mismatch at address {:08X}: {:02X}!={:02X}".format( 57 | self.addr, self.tx, self.rx) 58 | 59 | 60 | def interact(hostname, port, command, timeout=2): 61 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 62 | s.settimeout(timeout) 63 | s.connect((hostname, port)) 64 | s.sendall(command) 65 | data = check_response(s.recv(2048)) 66 | s.close() 67 | time.sleep(0.01) 68 | return data 69 | 70 | 71 | def check_response(data): 72 | errno = struct.unpack(" = core::result::Result; 48 | 49 | // Flash memory sector array 50 | pub const FLASH_SECTOR_ADDRESSES: [u32; 12] = [ 51 | 0x0800_0000, 52 | 0x0800_8000, 53 | 0x0801_0000, 54 | 0x0801_8000, 55 | 0x0802_0000, 56 | 0x0804_0000, 57 | 0x0808_0000, 58 | 0x080C_0000, 59 | 0x0810_0000, 60 | 0x0814_0000, 61 | 0x0818_0000, 62 | 0x081C_0000, 63 | ]; 64 | /// Final valid address in flash 65 | pub const FLASH_END: u32 = 0x081F_FFFF; 66 | pub const FLASH_USER: u32 = 0x0808_0000; 67 | static mut FLASH: Option = None; 68 | 69 | pub fn init(flash: device::FLASH) { 70 | unsafe { FLASH = Some(flash) }; 71 | } 72 | 73 | const SRC_MAC: [u8; 6] = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; 74 | static TIME: Mutex> = Mutex::new(RefCell::new(0)); 75 | static ETH_PENDING: Mutex> = Mutex::new(RefCell::new(false)); 76 | 77 | #[entry] 78 | fn main() -> ! { 79 | let p = device::Peripherals::take().unwrap(); 80 | let mut cp = device::CorePeripherals::take().unwrap(); 81 | 82 | setup_systick(&mut cp.SYST); // enable systic peripherals 83 | 84 | let gpioa = p.GPIOA.split(); 85 | let gpiob = p.GPIOB.split(); 86 | let gpioc = p.GPIOC.split(); 87 | let gpiog = p.GPIOG.split(); 88 | 89 | let mut led = gpiob.pb7.into_push_pull_output(); 90 | 91 | // check the user button to jump user application 92 | let pushbtn = gpioc.pc13.into_floating_input(); 93 | match pushbtn.is_high() { 94 | Ok(true) => {} 95 | Ok(false) => match valid_user_code() { 96 | Some(_) => boot(&mut cp.SCB), 97 | None => (), 98 | }, 99 | _ => unreachable!(), 100 | }; 101 | 102 | // stm32-eth initialisation 103 | stm32_eth::setup(&p.RCC, &p.SYSCFG); 104 | stm32_eth::setup_pins( 105 | gpioa.pa1, gpioa.pa2, gpioa.pa7, gpiob.pb13, gpioc.pc1, gpioc.pc4, gpioc.pc5, gpiog.pg11, 106 | gpiog.pg13, 107 | ); 108 | 109 | let mut rx_ring: [RingEntry<_>; 8] = Default::default(); 110 | let mut tx_ring: [RingEntry<_>; 2] = Default::default(); 111 | let clocks = p.RCC.constrain().cfgr.freeze(); 112 | let mut eth = Eth::new( 113 | p.ETHERNET_MAC, 114 | p.ETHERNET_DMA, 115 | &mut rx_ring[..], 116 | &mut tx_ring[..], 117 | &clocks, 118 | ); 119 | eth.enable_interrupt(); 120 | 121 | let local_addr = Ipv4Address::new(192, 168, 0, 167); 122 | let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); 123 | let mut ip_addrs = [ip_addr]; 124 | let mut neighbor_storage = [None; 16]; 125 | let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]); 126 | let ethernet_addr = EthernetAddress(SRC_MAC); 127 | let mut iface = EthernetInterfaceBuilder::new(&mut eth) 128 | .ethernet_addr(ethernet_addr) 129 | .ip_addrs(&mut ip_addrs[..]) 130 | .neighbor_cache(neighbor_cache) 131 | .finalize(); 132 | 133 | let mut server_rx_buffer = [0; 2048]; 134 | let mut server_tx_buffer = [0; 2048]; 135 | let server_socket = TcpSocket::new( 136 | TcpSocketBuffer::new(&mut server_rx_buffer[..]), 137 | TcpSocketBuffer::new(&mut server_tx_buffer[..]), 138 | ); 139 | let mut sockets_storage = [None, None]; 140 | let mut sockets = SocketSet::new(&mut sockets_storage[..]); 141 | let server_handle = sockets.add(server_socket); 142 | // turn blue led on 143 | led.set_high().expect("GPIO can never fail"); 144 | //init the flash peripheral 145 | init(p.FLASH); 146 | 147 | loop { 148 | let time: u64 = cortex_m::interrupt::free(|cs| *TIME.borrow(cs).borrow()); 149 | cortex_m::interrupt::free(|cs| { 150 | let mut eth_pending = ETH_PENDING.borrow(cs).borrow_mut(); 151 | *eth_pending = false; 152 | }); 153 | match iface.poll(&mut sockets, Instant::from_millis(time as i64)) { 154 | Ok(true) => { 155 | let mut socket = sockets.get::(server_handle); 156 | if !socket.is_open() { 157 | socket.listen(8080).unwrap(); 158 | } 159 | if !socket.may_recv() && socket.may_send() { 160 | socket.close(); 161 | } 162 | if socket.can_recv() { 163 | let mut cmd = [0u8; 4]; 164 | 165 | socket.recv_slice(&mut cmd[..]).ok(); 166 | let cmd = u32::from_le_bytes(cmd); 167 | 168 | match cmd { 169 | CMD_ERASE => cmd_erase(&mut socket), 170 | CMD_WRITE => { 171 | cmd_write(&mut socket); 172 | } 173 | CMD_BOOT => { 174 | let aircr = 0xE000ED0C as *mut u32; 175 | unsafe { *aircr = (0x5FA << 16) | (1 << 2) }; 176 | } 177 | _ => (), 178 | }; 179 | socket.close(); 180 | } 181 | } 182 | Ok(false) => { 183 | // Sleep if no ethernet work is pending 184 | cortex_m::interrupt::free(|cs| { 185 | let eth_pending = ETH_PENDING.borrow(cs).borrow_mut(); 186 | if !*eth_pending { 187 | asm::wfi(); 188 | // Awaken by interrupt 189 | } 190 | }); 191 | } 192 | Err(_e) => 193 | // Ignore malformed packets 194 | {} 195 | } 196 | } 197 | } 198 | 199 | fn setup_systick(syst: &mut SYST) { 200 | syst.set_reload(SYST::get_ticks_per_10ms() / 10); 201 | syst.enable_counter(); 202 | syst.enable_interrupt(); 203 | } 204 | 205 | #[exception] 206 | fn SysTick() { 207 | cortex_m::interrupt::free(|cs| { 208 | let mut time = TIME.borrow(cs).borrow_mut(); 209 | *time += 1; 210 | }) 211 | } 212 | 213 | #[interrupt] 214 | fn ETH() { 215 | cortex_m::interrupt::free(|cs| { 216 | let mut eth_pending = ETH_PENDING.borrow(cs).borrow_mut(); 217 | *eth_pending = true; 218 | }); 219 | 220 | // Clear interrupt flags 221 | let p = unsafe { device::Peripherals::steal() }; 222 | stm32_eth::eth_interrupt_handler(&p.ETHERNET_DMA); 223 | } 224 | 225 | /// Jump to the user application code 226 | fn boot(scb: &mut cortex_m::peripheral::SCB) { 227 | unsafe { 228 | // let sp: u32 = *(FLASH_USER as *const u32); 229 | let rv: usize = *((FLASH_USER + 4) as *const usize); 230 | scb.vtor.write(FLASH_USER); 231 | // cortex_m::register::msp::write(sp); 232 | let function = core::mem::transmute:: !>(rv); 233 | function(); 234 | } 235 | } 236 | 237 | /// check the valid user code in flash user memory address 238 | pub fn valid_user_code() -> Option { 239 | let reset_vector: u32 = unsafe { *((FLASH_USER + 4) as *const u32) }; 240 | if reset_vector >= FLASH_USER && reset_vector <= FLASH_END { 241 | Some(FLASH_USER) 242 | } else { 243 | None 244 | } 245 | } 246 | 247 | /// Read an address and length from the socket 248 | fn read_adr_len(socket: &mut TcpSocket) -> (u32, usize) { 249 | let mut adr = [0u8; 4]; 250 | let mut len = [0u8; 4]; 251 | socket.recv_slice(&mut adr[..]).ok(); 252 | socket.recv_slice(&mut len[..]).ok(); 253 | let adr = u32::from_le_bytes(adr); 254 | let len = u32::from_le_bytes(len); 255 | (adr, len as usize) 256 | } 257 | 258 | /// Check if address+length is valid for read/write flash. 259 | fn check_address_valid(address: u32, length: usize) -> Result<()> { 260 | if address > (FLASH_END - length as u32 + 1) { 261 | Err(Error::InvalidAddress) 262 | } else { 263 | Ok(()) 264 | } 265 | } 266 | 267 | /// Check length is a multiple of 4 and no greater than 1024 268 | fn check_length_valid(length: usize) -> Result<()> { 269 | if length % 4 != 0 { 270 | Err(Error::LengthNotMultiple4) 271 | } else if length > 2048 { 272 | Err(Error::LengthTooLong) 273 | } else { 274 | Ok(()) 275 | } 276 | } 277 | 278 | /// Check the specified length matches the amount of data available 279 | fn check_length_correct(length: usize, data: &[u8]) -> Result<()> { 280 | if length != data.len() { 281 | Err(Error::DataLengthIncorrect) 282 | } else { 283 | Ok(()) 284 | } 285 | } 286 | 287 | /// Send a status word back at the start of a response 288 | fn send_status(socket: &mut TcpSocket, status: Error) { 289 | let resp = (status as u32).to_le_bytes(); 290 | socket.send_slice(&resp).unwrap(); 291 | } 292 | 293 | /// Try to get the FLASH peripheral 294 | fn get_flash_peripheral() -> Result<&'static mut device::FLASH> { 295 | match unsafe { FLASH.as_mut() } { 296 | Some(flash) => Ok(flash), 297 | None => Err(Error::InternalError), 298 | } 299 | } 300 | 301 | /// Try to unlock flash 302 | fn unlock(flash: &mut device::FLASH) -> Result<()> { 303 | // Wait for any ongoing operations 304 | while flash.sr.read().bsy().bit_is_set() {} 305 | 306 | // Attempt unlock 307 | flash.keyr.write(|w| w.key().bits(0x45670123)); 308 | flash.keyr.write(|w| w.key().bits(0xCDEF89AB)); 309 | 310 | // Verify success 311 | match flash.cr.read().lock().is_unlocked() { 312 | true => Ok(()), 313 | false => Err(Error::FlashError), 314 | } 315 | } 316 | 317 | /// Lock flash 318 | fn lock(flash: &mut device::FLASH) { 319 | flash.cr.write(|w| w.lock().locked()); 320 | } 321 | 322 | /// Write to flash. 323 | /// Returns () on success, None on failure. 324 | /// length must be a multiple of 4. 325 | pub fn write(address: u32, length: usize, data: &[u8]) -> Result<()> { 326 | check_address_valid(address, length)?; 327 | check_length_valid(length)?; 328 | check_length_correct(length, data)?; 329 | 330 | let flash = get_flash_peripheral()?; 331 | unlock(flash)?; 332 | 333 | // Set parallelism to write in 32 bit chunks, and enable programming. 334 | // Note reset value has 1 for lock so we need to explicitly clear it. 335 | flash 336 | .cr 337 | .write(|w| w.lock().unlocked().psize().psize32().pg().program()); 338 | 339 | for idx in 0..(length / 4) { 340 | let offset = idx * 4; 341 | let word: u32 = (data[offset] as u32) 342 | | (data[offset + 1] as u32) << 8 343 | | (data[offset + 2] as u32) << 16 344 | | (data[offset + 3] as u32) << 24; 345 | let write_address = (address + offset as u32) as *mut u32; 346 | unsafe { core::ptr::write_volatile(write_address, word) }; 347 | 348 | // Wait for write 349 | while flash.sr.read().bsy().bit_is_set() {} 350 | 351 | // Check for errors 352 | let sr = flash.sr.read(); 353 | if sr.pgperr().bit_is_set() || sr.pgaerr().bit_is_set() || sr.wrperr().bit_is_set() { 354 | lock(flash); 355 | return Err(Error::WriteError); 356 | } 357 | } 358 | 359 | lock(flash); 360 | Ok(()) 361 | } 362 | 363 | fn cmd_write(socket: &mut TcpSocket) { 364 | let (adr, len) = read_adr_len(socket); 365 | match socket.recv(|buf| (buf.len(), write(adr, len, buf))) { 366 | Ok(Ok(())) => send_status(socket, Error::Success), 367 | Ok(Err(err)) => send_status(socket, err), 368 | Err(_) => send_status(socket, Error::NetworkError), 369 | } 370 | } 371 | 372 | pub fn cmd_erase(socket: &mut TcpSocket) { 373 | let (adr, len) = read_adr_len(socket); 374 | match erase(adr, len) { 375 | Ok(()) => send_status(socket, Error::Success), 376 | Err(err) => send_status(socket, err), 377 | } 378 | } 379 | 380 | /// Erase flash sectors that cover the given address and length. 381 | pub fn erase(address: u32, length: usize) -> Result<()> { 382 | check_address_valid(address, length)?; 383 | let address_start = address; 384 | let address_end = address + length as u32; 385 | // writeln!(stdout, "addr {} , end {}", address_start, address_end).unwrap(); 386 | for (idx, sector_start) in FLASH_SECTOR_ADDRESSES.iter().enumerate() { 387 | let sector_start = *sector_start; 388 | let sector_end = match FLASH_SECTOR_ADDRESSES.get(idx + 1) { 389 | Some(adr) => *adr - 1, 390 | None => FLASH_END, 391 | }; 392 | if (address_start >= sector_start && address_start <= sector_end) 393 | || (address_end >= sector_start && address_end <= sector_end) 394 | || (address_start <= sector_start && address_end >= sector_end) 395 | { 396 | erase_sector(idx as u8)?; 397 | } 398 | } 399 | Ok(()) 400 | } 401 | 402 | /// Erase specified sector 403 | fn erase_sector(sector: u8) -> Result<()> { 404 | if (sector as usize) >= FLASH_SECTOR_ADDRESSES.len() { 405 | return Err(Error::InternalError); 406 | } 407 | let flash = get_flash_peripheral()?; 408 | unlock(flash)?; 409 | 410 | // Erase. 411 | // UNSAFE: We've verified that `sector`