bootloader.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. * Simple PWM controller
  3. * Bootloader
  4. *
  5. * Copyright (c) 2020 Michael Buesch <m@bues.ch>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. */
  21. #include "compat.h"
  22. #include "crc.h"
  23. #include "uart.h" /* for BAUDRATE */
  24. #include "util.h"
  25. #include "watchdog.h" /* for wdt_setup */
  26. #include <avr/boot.h>
  27. #define BOOT_IRQ_SUPPORT 0
  28. #define TIMEOUT_SMALL ((F_CPU / 1024u) / 11u) /* 1/11th sec. */
  29. #define TIMEOUT_BIG ((F_CPU / 1024u) * 1u) /* 1 sec. */
  30. #define TIMEOUT_SHIFT 8
  31. #define BOOTCMD_EXIT 0x00u
  32. #define BOOTCMD_GETID 0x01u
  33. #define BOOTCMD_WRITEPAGE 0x5Au
  34. #define BOOTCMD_NOP 0xFFu
  35. #define BOOTRESULT_OK 1u
  36. #define BOOTRESULT_NOTOK 2u
  37. #define BOOT_WRITEPAGE_MAGIC 0x97u
  38. #define BOOT_WRITEPAGE_FLG_ERASEONLY 0x01u
  39. #define BOOT_WRITEPAGE_FLG_EEPROM 0x02u
  40. #define BOOT_WRITEPAGE_FLG_UNKNOWN 0xFCu
  41. #define WRITEPAGE_SIZE SPM_PAGESIZE
  42. static uint8_t bootloader_timeout_thres section_noinit;
  43. static uint8_t saved_mcusr section_noinit;
  44. static uint8_t rx_errors section_noinit;
  45. static inline void bootloader_timeout_reset(void)
  46. {
  47. TCNT1 = 0;
  48. TIFR1 = 0xFFu;
  49. }
  50. static void bootloader_timeout_exit(void)
  51. {
  52. TCCR1B = 0;
  53. TCCR1A = 0;
  54. TCCR1C = 0;
  55. TIMSK1 = 0;
  56. bootloader_timeout_reset();
  57. }
  58. static void bootloader_timeout_init(bool small_timeout)
  59. {
  60. bootloader_timeout_exit();
  61. if (small_timeout)
  62. bootloader_timeout_thres = TIMEOUT_SMALL >> TIMEOUT_SHIFT;
  63. else
  64. bootloader_timeout_thres = TIMEOUT_BIG >> TIMEOUT_SHIFT;
  65. memory_barrier();
  66. TCCR1B = (1u << CS12) | (0u << CS11) | (1u << CS10); /* 1024 */
  67. }
  68. static bool bootloader_timeout(void)
  69. {
  70. uint8_t tcnt;
  71. if (TIFR1 & (1u << TOV1))
  72. return true;
  73. tcnt = (uint8_t)(TCNT1 >> TIMEOUT_SHIFT);
  74. return tcnt > bootloader_timeout_thres;
  75. }
  76. #define write_ivsel(ivsel_insn) \
  77. do { \
  78. uint8_t tmp0, tmp1; \
  79. __asm__ __volatile__( \
  80. " in %[_tmp0], %[_MCUCR] \n" \
  81. " mov %[_tmp1], %[_tmp0] \n" \
  82. " sbr %[_tmp0], %[_IVCE] \n" \
  83. " cbr %[_tmp1], %[_IVCE] \n" \
  84. ivsel_insn " %[_tmp1], %[_IVSEL] \n" \
  85. " out %[_MCUCR], %[_tmp0] \n" \
  86. " out %[_MCUCR], %[_tmp1] \n" \
  87. : [_tmp0] "=a" (tmp0) \
  88. , [_tmp1] "=a" (tmp1) \
  89. : [_MCUCR] "I" (_SFR_IO_ADDR(MCUCR)) \
  90. , [_IVCE] "M" (1 << IVCE) \
  91. , [_IVSEL] "M" (1 << IVSEL) \
  92. ); \
  93. } while (0)
  94. static void route_irqs_to_bootloader(void)
  95. {
  96. if (BOOT_IRQ_SUPPORT)
  97. write_ivsel("sbr");
  98. }
  99. static void route_irqs_to_application(void)
  100. {
  101. if (BOOT_IRQ_SUPPORT)
  102. write_ivsel("cbr");
  103. }
  104. static noreturn void exit_bootloader(void)
  105. {
  106. if (BOOT_IRQ_SUPPORT)
  107. irq_disable();
  108. route_irqs_to_application();
  109. bootloader_timeout_exit();
  110. wdt_reset();
  111. /* jump to application */
  112. {
  113. void (*application)(void) = (void (*)(void))(uintptr_t)0x0000;
  114. memory_barrier();
  115. application();
  116. }
  117. unreachable();
  118. }
  119. static bool write_page(const uint8_t *page_buffer,
  120. uint16_t page_address,
  121. bool erase_only)
  122. {
  123. uint8_t i;
  124. uint16_t data, dataRead;
  125. bool ok;
  126. /* write page */
  127. eeprom_busy_wait();
  128. boot_spm_busy_wait();
  129. boot_page_erase(page_address);
  130. boot_spm_busy_wait();
  131. if (!erase_only) {
  132. for (i = 0; i < WRITEPAGE_SIZE; i = (uint8_t)(i + 2u)) {
  133. data = page_buffer[i];
  134. data |= (uint16_t)page_buffer[i + 1u] << 8;
  135. boot_page_fill(page_address + i, data);
  136. }
  137. boot_page_write(page_address);
  138. boot_spm_busy_wait();
  139. }
  140. boot_rww_enable();
  141. /* verify page */
  142. ok = true;
  143. for (i = 0; i < WRITEPAGE_SIZE; i++) {
  144. data = erase_only ? 0xFF : page_buffer[i];
  145. dataRead = pgm_read_byte((void *)(page_address + i));
  146. if (data != dataRead)
  147. ok = false;
  148. }
  149. return ok;
  150. }
  151. static bool write_eeprom(const uint8_t *buffer,
  152. uint16_t address,
  153. bool erase_only)
  154. {
  155. uint8_t *eeptr;
  156. uint8_t i;
  157. uint8_t data, dataRead;
  158. bool ok;
  159. ok = true;
  160. eeprom_busy_wait();
  161. boot_spm_busy_wait();
  162. for (i = 0; i < WRITEPAGE_SIZE; i++) {
  163. data = erase_only ? 0xFF : buffer[i];
  164. eeptr = (uint8_t *)(void *)(address + i);
  165. /* write */
  166. eeprom_update_byte(eeptr, data);
  167. eeprom_busy_wait();
  168. /* verify */
  169. dataRead = eeprom_read_byte(eeptr);
  170. if (data != dataRead)
  171. ok = false;
  172. wdt_reset();
  173. }
  174. return ok;
  175. }
  176. static inline uint8_t get_rx_errors(void)
  177. {
  178. return (UCSR0A & ((1u << FE0) | (1u << DOR0) | (1u << UPE0)));
  179. }
  180. static inline bool have_rx_byte(void)
  181. {
  182. return ((UCSR0A & (1u << RXC0)) != 0u);
  183. }
  184. static inline uint8_t get_rx_byte(void)
  185. {
  186. return UDR0;
  187. }
  188. static inline bool tx_ready(void)
  189. {
  190. return ((UCSR0A & (1u << UDRE0)) != 0u);
  191. }
  192. static inline void set_tx_byte(uint8_t byte)
  193. {
  194. UDR0 = byte;
  195. }
  196. static noinline uint8_t receive_byte(void)
  197. {
  198. while (!have_rx_byte());
  199. rx_errors |= get_rx_errors();
  200. return get_rx_byte();
  201. }
  202. static noinline void send_byte(uint8_t byte)
  203. {
  204. while (!tx_ready());
  205. set_tx_byte(byte);
  206. }
  207. /* RX byte stream:
  208. * [magic] [flags] [addrlo] [addrhi] [pagedata...] [crc8]
  209. * pagedata is not present, if flag BOOT_WRITEPAGE_FLG_ERASEONLY is set.
  210. * TX byte stream:
  211. * [result]
  212. */
  213. static void handle_writepage(void)
  214. {
  215. uint8_t magic, flags, data, addr_lo, addr_hi;
  216. uint8_t crc, expected_crc;
  217. uint16_t page_address, i;
  218. bool ok;
  219. bool erase_only;
  220. static uint8_t page_buffer[WRITEPAGE_SIZE] section_noinit;
  221. ok = false;
  222. crc = 0u;
  223. magic = receive_byte();
  224. crc = crc8_update(crc, magic);
  225. flags = receive_byte();
  226. crc = crc8_update(crc, flags);
  227. addr_lo = receive_byte();
  228. crc = crc8_update(crc, addr_lo);
  229. addr_hi = receive_byte();
  230. crc = crc8_update(crc, addr_hi);
  231. erase_only = !!(flags & BOOT_WRITEPAGE_FLG_ERASEONLY);
  232. if (!erase_only) {
  233. for (i = 0; i < WRITEPAGE_SIZE; i++) {
  234. data = receive_byte();
  235. page_buffer[i] = data;
  236. crc = crc8_update(crc, data);
  237. }
  238. }
  239. crc ^= 0xFFu;
  240. expected_crc = receive_byte();
  241. if ((rx_errors == 0u) &&
  242. (magic == BOOT_WRITEPAGE_MAGIC) &&
  243. ((flags & BOOT_WRITEPAGE_FLG_UNKNOWN) == 0u) &&
  244. (expected_crc == crc)) {
  245. page_address = ((uint16_t)addr_hi << 8) | addr_lo;
  246. if (flags & BOOT_WRITEPAGE_FLG_EEPROM) {
  247. if (page_address < (E2END + 1u)) {
  248. ok = write_eeprom(page_buffer,
  249. page_address,
  250. erase_only);
  251. }
  252. } else {
  253. if (page_address < BOOT_OFFSET) {
  254. ok = write_page(page_buffer,
  255. page_address,
  256. erase_only);
  257. }
  258. }
  259. }
  260. send_byte(ok ? BOOTRESULT_OK : BOOTRESULT_NOTOK);
  261. }
  262. static void handle_getid(void)
  263. {
  264. send_byte(CHIP_ID);
  265. }
  266. static void receive_commands(void)
  267. {
  268. uint8_t command;
  269. if (have_rx_byte()) {
  270. rx_errors = get_rx_errors();
  271. command = get_rx_byte();
  272. if (rx_errors == 0u) {
  273. switch (command) {
  274. default:
  275. case BOOTCMD_EXIT:
  276. exit_bootloader();
  277. break;
  278. case BOOTCMD_GETID:
  279. handle_getid();
  280. break;
  281. case BOOTCMD_WRITEPAGE:
  282. handle_writepage();
  283. break;
  284. case BOOTCMD_NOP:
  285. break;
  286. }
  287. bootloader_timeout_reset();
  288. }
  289. }
  290. }
  291. static void init_commands(void)
  292. {
  293. UBRR0 = UBRRVAL;
  294. UCSR0A = (1 << TXC0) | (!!(USE_2X) << U2X0) | (0 << MPCM0);
  295. UCSR0C = (0 << UMSEL01) | (0 << UMSEL00) |
  296. (1 << UPM01) | (1 << UPM00) |
  297. (1 << USBS0) |
  298. (1 << UCSZ01) | (1 << UCSZ00);
  299. UCSR0B = (0 << RXCIE0) | (0 << TXCIE0) | (0 << UDRIE0) |
  300. (1 << RXEN0) | (1 << TXEN0) |
  301. (0 << UCSZ02);
  302. }
  303. static void section_init3 early_init(void)
  304. {
  305. irq_disable();
  306. saved_mcusr = MCUSR;
  307. MCUSR = 0;
  308. wdt_setup(WDTO_500MS, true, false);
  309. }
  310. int _mainfunc main(void)
  311. {
  312. bool small_timeout = false;
  313. if (saved_mcusr & ((1u << PORF) | (1u << EXTRF) | (1u << BORF)))
  314. small_timeout = true;
  315. bootloader_timeout_init(small_timeout);
  316. route_irqs_to_bootloader();
  317. init_commands();
  318. while (1) {
  319. wdt_reset();
  320. receive_commands();
  321. if (bootloader_timeout())
  322. exit_bootloader();
  323. }
  324. }