bootloader: Make run() function return a bool.
[gps-watch.git] / src / bootloader / bootloader.rs
1 /*
2  * Copyright (c) 2019 Tilman Sauerbeck (tilman at code-monkey de)
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 use common::buffer::Buffer;
25 use common::crc32::Crc32;
26 use flash;
27
28 const COMMAND_ERASE     : u32 = 0x49e89a20;
29 const COMMAND_PROGRAM   : u32 = 0x37f7dc8d;
30 const COMMAND_VERIFY    : u32 = 0x4a213efb;
31 const COMMAND_LOADCHUNK : u32 = 0x1b329768;
32 const COMMAND_START_APP : u32 = 0xd27df1bf;
33
34 pub struct Bootloader {
35     command: u32,
36     command_bytes: u32,
37     sector_data: [u8; flash::SECTOR_SIZE],
38
39     // The number of bytes in sector_data.
40     // This is always 32 bit aligned and never greater than flash::SECTOR_SIZE.
41     sector_data_length: usize,
42 }
43
44 #[derive(Copy,Clone)]
45 enum Error {
46     UnknownCommand = 1,
47     InvalidArgument,
48     ChecksumMismatch,
49 }
50
51 extern {
52     fn usb_serial_read(c: *mut u8) -> bool;
53
54     static mut cdc_tx_buf: Buffer;
55 }
56
57 fn try_read_u8() -> Option<u8> {
58     unsafe {
59         let mut c = b'\0';
60
61         if usb_serial_read(&mut c) {
62             Some(c)
63         } else {
64             None
65         }
66     }
67 }
68
69 fn read_u8() -> u8 {
70     let mut b = b'\0';
71
72     unsafe {
73         while !usb_serial_read(&mut b) {
74         }
75     }
76
77     b
78 }
79
80 fn read_u32_le() -> u32 {
81     let mut b = [0u8; 4];
82
83     for i in 0..4 {
84         b[i] = read_u8();
85     }
86
87     u32::from_le_bytes(b)
88 }
89
90 fn write_u8(b: u8) {
91     unsafe {
92         cdc_tx_buf.write(&[b]);
93         cdc_tx_buf.flush();
94     }
95 }
96
97 fn write_u32_le(u: u32) {
98     let b = u.to_le_bytes();
99
100     unsafe {
101         cdc_tx_buf.write(&b);
102         cdc_tx_buf.flush();
103     }
104 }
105
106 fn write_ack() {
107     write_u8(0u8);
108 }
109
110 fn write_nak(e: Error) {
111     write_u8(e as u8);
112 }
113
114 impl Bootloader {
115     pub fn new() -> Bootloader {
116         Bootloader {
117             command: 0,
118             command_bytes: 0,
119             sector_data: [0u8; flash::SECTOR_SIZE],
120             sector_data_length: 0,
121         }
122     }
123
124     pub fn run(&mut self) -> bool {
125         if self.command_bytes < 4 {
126             if let Some(b) = try_read_u8() {
127                 self.command >>= 8;
128                 self.command |= (b as u32) << 24;
129
130                 self.command_bytes += 1;
131             }
132         }
133
134         if self.command_bytes != 4 {
135             true
136         } else {
137             self.command_bytes = 0;
138
139             self.process_command();
140
141             self.command != COMMAND_START_APP
142         }
143     }
144
145     fn process_command(&mut self) {
146         match self.command {
147             COMMAND_ERASE => {
148                 if let Err(e) = self.exec_erase() {
149                     write_nak(e);
150                 } else {
151                     write_ack();
152                 }
153             },
154             COMMAND_LOADCHUNK => {
155                 if let Err(e) = self.exec_load_sector_data() {
156                     write_nak(e);
157                 } else {
158                     write_ack();
159                 }
160             },
161             COMMAND_VERIFY => {
162                 let result = self.exec_verify();
163
164                 if let Err(e) = result {
165                     write_nak(e);
166                 } else if let Ok((a, b)) = result {
167                     write_ack();
168
169                     write_u32_le(a);
170                     write_u32_le(b);
171                 }
172             },
173             COMMAND_PROGRAM => {
174                 if let Err(e) = self.exec_program() {
175                     write_nak(e);
176                 } else {
177                     write_ack();
178                 }
179             },
180             COMMAND_START_APP => {
181                 write_ack();
182             },
183             _ => {
184                 write_nak(Error::UnknownCommand);
185             }
186         };
187     }
188
189     fn exec_erase(&mut self) -> Result<(), Error> {
190         let sector = read_u32_le();
191
192         if sector > 0xff {
193             Err(Error::InvalidArgument)
194         } else {
195             flash::erase(sector);
196
197             Ok(())
198         }
199     }
200
201     fn exec_load_sector_data(&mut self) -> Result<(), Error> {
202         let num_bytes = read_u32_le() as usize;
203
204         if num_bytes < 1 {
205             Err(Error::InvalidArgument)
206         } else if num_bytes > flash::SECTOR_SIZE {
207             Err(Error::InvalidArgument)
208         } else if (num_bytes & 3) != 0 {
209             Err(Error::InvalidArgument)
210         } else {
211             write_ack();
212
213             self.sector_data_length = num_bytes;
214
215             for i in 0..num_bytes {
216                 self.sector_data[i] = read_u8();
217             }
218
219             let expected_crc32 = read_u32_le();
220
221             let chunk = &self.sector_data[0..self.sector_data_length];
222
223             let mut actual_crc32 = Crc32::new();
224             actual_crc32.update(chunk);
225
226             if actual_crc32.finish() != expected_crc32 {
227                 Err(Error::ChecksumMismatch)
228             } else {
229                 Ok(())
230             }
231         }
232     }
233
234     fn exec_verify(&mut self) -> Result<(u32, u32), Error> {
235         let sector = read_u32_le();
236
237         if sector > 0xff {
238             Err(Error::InvalidArgument)
239         } else if self.sector_data_length == 0 {
240             Err(Error::InvalidArgument)
241         } else {
242             let chunk = &self.sector_data[0..self.sector_data_length];
243
244             Ok(flash::verify(sector, chunk))
245         }
246     }
247
248     fn exec_program(&mut self) -> Result<(), Error> {
249         let sector = read_u32_le();
250
251         if sector > 0xff {
252             Err(Error::InvalidArgument)
253         } else if self.sector_data_length == 0 {
254             Err(Error::InvalidArgument)
255         } else {
256             let chunk = &self.sector_data[0..self.sector_data_length];
257
258             Ok(flash::program(sector, chunk))
259         }
260     }
261 }