bootloader: Refuse to erase the second flash sector.
[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     PermissionDenied,
50 }
51
52 extern {
53     fn usb_serial_read(c: *mut u8) -> bool;
54
55     static mut cdc_tx_buf: Buffer;
56 }
57
58 fn try_read_u8() -> Option<u8> {
59     unsafe {
60         let mut c = b'\0';
61
62         if usb_serial_read(&mut c) {
63             Some(c)
64         } else {
65             None
66         }
67     }
68 }
69
70 fn read_u8() -> u8 {
71     let mut b = b'\0';
72
73     unsafe {
74         while !usb_serial_read(&mut b) {
75         }
76     }
77
78     b
79 }
80
81 fn read_u32_le() -> u32 {
82     let mut b = [0u8; 4];
83
84     for i in 0..4 {
85         b[i] = read_u8();
86     }
87
88     u32::from_le_bytes(b)
89 }
90
91 fn write_u8(b: u8) {
92     unsafe {
93         cdc_tx_buf.write(&[b]);
94         cdc_tx_buf.flush();
95     }
96 }
97
98 fn write_u32_le(u: u32) {
99     let b = u.to_le_bytes();
100
101     unsafe {
102         cdc_tx_buf.write(&b);
103         cdc_tx_buf.flush();
104     }
105 }
106
107 fn write_ack() {
108     write_u8(0u8);
109 }
110
111 fn write_nak(e: Error) {
112     write_u8(e as u8);
113 }
114
115 impl Bootloader {
116     pub fn new() -> Bootloader {
117         Bootloader {
118             command: 0,
119             command_bytes: 0,
120             sector_data: [0u8; flash::SECTOR_SIZE],
121             sector_data_length: 0,
122         }
123     }
124
125     pub fn run(&mut self) -> bool {
126         if self.command_bytes < 4 {
127             if let Some(b) = try_read_u8() {
128                 self.command >>= 8;
129                 self.command |= (b as u32) << 24;
130
131                 self.command_bytes += 1;
132             }
133         }
134
135         if self.command_bytes != 4 {
136             true
137         } else {
138             self.command_bytes = 0;
139
140             self.process_command();
141
142             self.command != COMMAND_START_APP
143         }
144     }
145
146     fn process_command(&mut self) {
147         match self.command {
148             COMMAND_ERASE => {
149                 if let Err(e) = self.exec_erase() {
150                     write_nak(e);
151                 } else {
152                     write_ack();
153                 }
154             },
155             COMMAND_LOADCHUNK => {
156                 if let Err(e) = self.exec_load_sector_data() {
157                     write_nak(e);
158                 } else {
159                     write_ack();
160                 }
161             },
162             COMMAND_VERIFY => {
163                 let result = self.exec_verify();
164
165                 if let Err(e) = result {
166                     write_nak(e);
167                 } else if let Ok((a, b)) = result {
168                     write_ack();
169
170                     write_u32_le(a);
171                     write_u32_le(b);
172                 }
173             },
174             COMMAND_PROGRAM => {
175                 if let Err(e) = self.exec_program() {
176                     write_nak(e);
177                 } else {
178                     write_ack();
179                 }
180             },
181             COMMAND_START_APP => {
182                 write_ack();
183             },
184             _ => {
185                 write_nak(Error::UnknownCommand);
186             }
187         };
188     }
189
190     fn exec_erase(&mut self) -> Result<(), Error> {
191         let sector = read_u32_le();
192
193         if sector > 0xff {
194             Err(Error::InvalidArgument)
195         } else if sector == 0x01 {
196             // The second sector contains the flash configuration field,
197             // and keeping it in the erased state has the potential
198             // to brick the device.
199             Err(Error::PermissionDenied)
200         } else {
201             flash::erase(sector);
202
203             Ok(())
204         }
205     }
206
207     fn exec_load_sector_data(&mut self) -> Result<(), Error> {
208         let num_bytes = read_u32_le() as usize;
209
210         if num_bytes < 1 {
211             Err(Error::InvalidArgument)
212         } else if num_bytes > flash::SECTOR_SIZE {
213             Err(Error::InvalidArgument)
214         } else if (num_bytes & 3) != 0 {
215             Err(Error::InvalidArgument)
216         } else {
217             write_ack();
218
219             self.sector_data_length = num_bytes;
220
221             for i in 0..num_bytes {
222                 self.sector_data[i] = read_u8();
223             }
224
225             let expected_crc32 = read_u32_le();
226
227             let chunk = &self.sector_data[0..self.sector_data_length];
228
229             let mut actual_crc32 = Crc32::new();
230             actual_crc32.update(chunk);
231
232             if actual_crc32.finish() != expected_crc32 {
233                 Err(Error::ChecksumMismatch)
234             } else {
235                 Ok(())
236             }
237         }
238     }
239
240     fn exec_verify(&mut self) -> Result<(u32, u32), Error> {
241         let sector = read_u32_le();
242
243         if sector > 0xff {
244             Err(Error::InvalidArgument)
245         } else if self.sector_data_length == 0 {
246             Err(Error::InvalidArgument)
247         } else {
248             let chunk = &self.sector_data[0..self.sector_data_length];
249
250             Ok(flash::verify(sector, chunk))
251         }
252     }
253
254     fn exec_program(&mut self) -> Result<(), Error> {
255         let sector = read_u32_le();
256
257         if sector > 0xff {
258             Err(Error::InvalidArgument)
259         } else if self.sector_data_length == 0 {
260             Err(Error::InvalidArgument)
261         } else {
262             let chunk = &self.sector_data[0..self.sector_data_length];
263
264             Ok(flash::program(sector, chunk))
265         }
266     }
267 }