cf4bca2926638a34be6be5572d9a3a241970f32c
[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) {
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             self.command_bytes = 0;
136
137             self.process_command();
138         }
139     }
140
141     fn process_command(&mut self) {
142         match self.command {
143             COMMAND_ERASE => {
144                 if let Err(e) = self.exec_erase() {
145                     write_nak(e);
146                 } else {
147                     write_ack();
148                 }
149             },
150             COMMAND_LOADCHUNK => {
151                 if let Err(e) = self.exec_load_sector_data() {
152                     write_nak(e);
153                 } else {
154                     write_ack();
155                 }
156             },
157             COMMAND_VERIFY => {
158                 let result = self.exec_verify();
159
160                 if let Err(e) = result {
161                     write_nak(e);
162                 } else if let Ok((a, b)) = result {
163                     write_ack();
164
165                     write_u32_le(a);
166                     write_u32_le(b);
167                 }
168             },
169             COMMAND_PROGRAM => {
170                 if let Err(e) = self.exec_program() {
171                     write_nak(e);
172                 } else {
173                     write_ack();
174                 }
175             },
176             COMMAND_START_APP => {
177                 write_ack();
178             },
179             _ => {
180                 write_nak(Error::UnknownCommand);
181             }
182         };
183     }
184
185     fn exec_erase(&mut self) -> Result<(), Error> {
186         let sector = read_u32_le();
187
188         if sector > 0xff {
189             Err(Error::InvalidArgument)
190         } else {
191             flash::erase(sector);
192
193             Ok(())
194         }
195     }
196
197     fn exec_load_sector_data(&mut self) -> Result<(), Error> {
198         let num_bytes = read_u32_le() as usize;
199
200         if num_bytes < 1 {
201             Err(Error::InvalidArgument)
202         } else if num_bytes > flash::SECTOR_SIZE {
203             Err(Error::InvalidArgument)
204         } else if (num_bytes & 3) != 0 {
205             Err(Error::InvalidArgument)
206         } else {
207             write_ack();
208
209             self.sector_data_length = num_bytes;
210
211             for i in 0..num_bytes {
212                 self.sector_data[i] = read_u8();
213             }
214
215             let expected_crc32 = read_u32_le();
216
217             let chunk = &self.sector_data[0..self.sector_data_length];
218
219             let mut actual_crc32 = Crc32::new();
220             actual_crc32.update(chunk);
221
222             if actual_crc32.finish() != expected_crc32 {
223                 Err(Error::ChecksumMismatch)
224             } else {
225                 Ok(())
226             }
227         }
228     }
229
230     fn exec_verify(&mut self) -> Result<(u32, u32), Error> {
231         let sector = read_u32_le();
232
233         if sector > 0xff {
234             Err(Error::InvalidArgument)
235         } else if self.sector_data_length == 0 {
236             Err(Error::InvalidArgument)
237         } else {
238             let chunk = &self.sector_data[0..self.sector_data_length];
239
240             Ok(flash::verify(sector, chunk))
241         }
242     }
243
244     fn exec_program(&mut self) -> Result<(), Error> {
245         let sector = read_u32_le();
246
247         if sector > 0xff {
248             Err(Error::InvalidArgument)
249         } else if self.sector_data_length == 0 {
250             Err(Error::InvalidArgument)
251         } else {
252             let chunk = &self.sector_data[0..self.sector_data_length];
253
254             Ok(flash::program(sector, chunk))
255         }
256     }
257 }