bootloader.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. /*
  2. * CNC-remote-control
  3. * CPU - Bootloader
  4. *
  5. * Copyright (C) 2011-2016 Michael Buesch <m@bues.ch>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * version 2 as published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. */
  16. #include "util.h"
  17. #include "uart.h"
  18. #include "pdiusb.h"
  19. #include "usb.h"
  20. #include "usb_application.h"
  21. #include "spi.h"
  22. #include "machine_interface.h"
  23. #include "4094.h"
  24. #include <avr/io.h>
  25. #include <avr/interrupt.h>
  26. #include <avr/wdt.h>
  27. #include <avr/pgmspace.h>
  28. #include <avr/eeprom.h>
  29. #include <avr/boot.h>
  30. #include <util/crc16.h>
  31. #include <string.h>
  32. #include <stdint.h>
  33. /* AtMega32 */
  34. #define CPU_SPM_PAGESIZE SPM_PAGESIZE
  35. #define CPU_E2SIZE (E2END + 1)
  36. /* AtMega8 */
  37. #define COPROC_SPM_PAGESIZE 64
  38. #define COPROC_E2SIZE (0x1FF + 1)
  39. #define PGBUF_SIZE (max(CPU_SPM_PAGESIZE, \
  40. max(CPU_E2SIZE, \
  41. max(COPROC_SPM_PAGESIZE, \
  42. COPROC_E2SIZE))))
  43. static uint8_t page_buffer[PGBUF_SIZE];
  44. static void disable_all_irq_sources(void)
  45. {
  46. GICR = 0;
  47. TIMSK = 0;
  48. SPCR = 0;
  49. UCSRB = (uint8_t)(UCSRB & ~(1u << RXCIE));
  50. UCSRB = (uint8_t)(UCSRB & ~(1u << TXCIE));
  51. UCSRB = (uint8_t)(UCSRB & ~(1u << UDRIE));
  52. ADCSRA = 0;
  53. EECR = 0;
  54. ACSR = 0;
  55. TWCR = 0;
  56. SPMCR = 0;
  57. }
  58. static void route_irqs_to_bootloader(void)
  59. {
  60. uint8_t tmp;
  61. __asm__ __volatile__(
  62. " ldi %[_tmp], %[_IVCE] \n"
  63. " out %[_GICR], %[_tmp] \n"
  64. " ldi %[_tmp], %[_IVSEL] \n"
  65. " out %[_GICR], %[_tmp] \n"
  66. : [_tmp] "=a" (tmp)
  67. : [_GICR] "I" (_SFR_IO_ADDR(GICR))
  68. , [_IVSEL] "M" (1 << IVSEL)
  69. , [_IVCE] "M" (1 << IVCE)
  70. );
  71. }
  72. static void route_irqs_to_application(void)
  73. {
  74. uint8_t tmp;
  75. __asm__ __volatile__(
  76. " ldi %[_tmp], %[_IVCE] \n"
  77. " out %[_GICR], %[_tmp] \n"
  78. " clr %[_tmp] \n"
  79. " out %[_GICR], %[_tmp] \n"
  80. : [_tmp] "=a" (tmp)
  81. : [_GICR] "I" (_SFR_IO_ADDR(GICR))
  82. , [_IVCE] "M" (1 << IVCE)
  83. );
  84. }
  85. static void coprocessor_spi_busywait(void)
  86. {
  87. while (SPI_MASTER_TRANSIRQ_PIN & (1 << SPI_MASTER_TRANSIRQ_BIT));
  88. }
  89. static uint8_t coprocessor_spi_transfer(uint8_t data)
  90. {
  91. _delay_ms(1);
  92. coprocessor_spi_busywait();
  93. return spi_transfer_sync(data);
  94. }
  95. static uint8_t coprocessor_spi_transfer_nobusy(uint8_t data)
  96. {
  97. return spi_transfer_slowsync(data);
  98. }
  99. static bool coprocessor_is_in_application(void)
  100. {
  101. uint8_t result;
  102. spi_slave_select(1);
  103. coprocessor_spi_transfer_nobusy(SPI_CONTROL_TESTAPP);
  104. result = coprocessor_spi_transfer_nobusy(SPI_CONTROL_NOP);
  105. spi_slave_select(0);
  106. if (result == SPI_RESULT_OK)
  107. return 1;
  108. return 0;
  109. }
  110. static bool coprocessor_enter_bootloader(void)
  111. {
  112. spi_slave_select(1);
  113. coprocessor_spi_transfer_nobusy(SPI_CONTROL_ENTERBOOT);
  114. coprocessor_spi_transfer_nobusy(SPI_CONTROL_ENTERBOOT2);
  115. spi_slave_select(0);
  116. _delay_ms(150);
  117. if (!coprocessor_is_in_application())
  118. return 1;
  119. return 0;
  120. }
  121. static bool coprocessor_exit_bootloader(void)
  122. {
  123. spi_slave_select(1);
  124. coprocessor_spi_transfer_nobusy(SPI_CONTROL_ENTERAPP);
  125. spi_slave_select(0);
  126. _delay_ms(150);
  127. if (coprocessor_is_in_application())
  128. return 1;
  129. return 0;
  130. }
  131. static void boot_coprocessor_init(void)
  132. {
  133. spi_lowlevel_init();
  134. }
  135. static noreturn noinline void exit_bootloader(void)
  136. {
  137. uart_putstr("EXIT BOOT\n");
  138. irq_disable();
  139. /* Shutdown hardware */
  140. spi_lowlevel_exit();
  141. pdiusb_exit();
  142. uart_exit();
  143. disable_all_irq_sources();
  144. wdt_enable(WDTO_2S);
  145. route_irqs_to_application();
  146. /* Jump to application code */
  147. __asm__ __volatile__(
  148. "ijmp\n"
  149. : /* None */
  150. : [_Z] "z" (0x0000)
  151. );
  152. unreachable();
  153. }
  154. static bool verify_page(uint16_t page_address)
  155. {
  156. uint8_t i, data0, data1;
  157. for (i = 0; i < CPU_SPM_PAGESIZE; i++) {
  158. wdt_reset();
  159. data0 = page_buffer[i];
  160. data1 = pgm_read_byte((void PROGPTR *)(void *)(page_address + i));
  161. if (data0 != data1)
  162. return 0;
  163. }
  164. return 1;
  165. }
  166. static void write_page(uint16_t page_address)
  167. {
  168. uint8_t i, sreg;
  169. uint16_t data;
  170. eeprom_busy_wait();
  171. boot_spm_busy_wait();
  172. sreg = irq_disable_save();
  173. boot_page_erase(page_address);
  174. boot_spm_busy_wait();
  175. for (i = 0; i < CPU_SPM_PAGESIZE; i = (uint8_t)(i + 2u)) {
  176. wdt_reset();
  177. data = (uint16_t)(page_buffer[i]);
  178. data |= ((uint16_t)(page_buffer[i + 1]) << 8);
  179. boot_page_fill(page_address + i, data);
  180. }
  181. boot_page_write(page_address);
  182. boot_spm_busy_wait();
  183. boot_rww_enable();
  184. irq_restore(sreg);
  185. }
  186. static noinline uint8_t calc_crc8(uint8_t crc, uint8_t data)
  187. {
  188. return spi_crc8(crc, data);
  189. }
  190. uint8_t usb_app_ep2_rx(uint8_t *data, uint8_t ctl_size,
  191. uint8_t *reply_buf)
  192. {
  193. struct control_message *ctl = (struct control_message *)data;
  194. struct control_reply *reply = (struct control_reply *)reply_buf;
  195. BUILD_BUG_ON(USBCFG_EP2_MAXSIZE < CONTROL_REPLY_MAX_SIZE);
  196. if (ctl_size < CONTROL_MSG_HDR_SIZE)
  197. goto err_size;
  198. if (!(ctl->flags & CONTROL_FLG_BOOTLOADER))
  199. goto err_context;
  200. switch (ctl->id) {
  201. case CONTROL_PING: {
  202. break;
  203. }
  204. case CONTROL_ENTERBOOT: {
  205. if (ctl_size < CONTROL_MSG_SIZE(enterboot))
  206. goto err_size;
  207. if (!control_enterboot_magic_ok(ctl))
  208. goto err_inval;
  209. switch (ctl->enterboot.target) {
  210. case TARGET_CPU:
  211. /* We're already here. */
  212. break;
  213. case TARGET_COPROC:
  214. if (!coprocessor_enter_bootloader())
  215. goto err_cmdfail;
  216. break;
  217. default:
  218. goto err_context;
  219. }
  220. break;
  221. }
  222. case CONTROL_EXITBOOT: {
  223. if (ctl_size < CONTROL_MSG_SIZE(exitboot))
  224. goto err_size;
  225. switch (ctl->exitboot.target) {
  226. case TARGET_CPU:
  227. exit_bootloader();
  228. goto err_cmdfail;
  229. case TARGET_COPROC:
  230. if (!coprocessor_exit_bootloader())
  231. goto err_cmdfail;
  232. break;
  233. default:
  234. goto err_context;
  235. }
  236. break;
  237. }
  238. case CONTROL_BOOT_WRITEBUF: {
  239. uint8_t i, size, crc = 0, d;
  240. uint16_t offset;
  241. if (ctl_size < CONTROL_MSG_SIZE(boot_writebuf))
  242. goto err_size;
  243. size = ctl->boot_writebuf.size;
  244. offset = ctl->boot_writebuf.offset;
  245. if (size > sizeof(ctl->boot_writebuf.data) ||
  246. offset >= sizeof(page_buffer) ||
  247. offset + size > sizeof(page_buffer))
  248. goto err_inval;
  249. for (i = 0; i < size; i++) {
  250. d = ctl->boot_writebuf.data[i];
  251. crc = calc_crc8(crc, d);
  252. page_buffer[offset + i] = d;
  253. }
  254. crc ^= 0xFF;
  255. if (crc != ctl->boot_writebuf.crc)
  256. goto err_checksum;
  257. break;
  258. }
  259. case CONTROL_BOOT_FLASHPG: {
  260. uint16_t i, address;
  261. uint8_t d, retval, crc = 0;
  262. if (ctl_size < CONTROL_MSG_SIZE(boot_flashpg))
  263. goto err_size;
  264. address = ctl->boot_flashpg.address;
  265. switch (ctl->boot_flashpg.target) {
  266. case TARGET_CPU:
  267. write_page(address);
  268. if (!verify_page(address))
  269. goto err_cmdfail;
  270. break;
  271. case TARGET_COPROC:
  272. spi_slave_select(1);
  273. coprocessor_spi_transfer(SPI_CONTROL_STARTFLASH);
  274. crc = calc_crc8(crc, lo8(address));
  275. coprocessor_spi_transfer(lo8(address));
  276. crc = calc_crc8(crc, hi8(address));
  277. coprocessor_spi_transfer(hi8(address));
  278. for (i = 0; i < COPROC_SPM_PAGESIZE; i++) {
  279. d = page_buffer[i];
  280. crc = calc_crc8(crc, d);
  281. coprocessor_spi_transfer(d);
  282. }
  283. crc ^= 0xFF;
  284. coprocessor_spi_transfer(crc);
  285. retval = coprocessor_spi_transfer(SPI_CONTROL_NOP);
  286. if (retval != SPI_RESULT_OK) {
  287. spi_slave_select(0);
  288. goto err_checksum; /* CRC error */
  289. }
  290. /* Flashing starts. Wait for result and read it. */
  291. retval = coprocessor_spi_transfer(SPI_CONTROL_NOP);
  292. if (retval != SPI_RESULT_OK) {
  293. spi_slave_select(0);
  294. goto err_cmdfail;
  295. }
  296. spi_slave_select(0);
  297. break;
  298. default:
  299. goto err_context;
  300. }
  301. break;
  302. }
  303. case CONTROL_BOOT_EEPWRITE: {
  304. uint16_t i, size, address;
  305. uint8_t d;
  306. if (ctl_size < CONTROL_MSG_SIZE(boot_eepwrite))
  307. goto err_size;
  308. address = ctl->boot_eepwrite.address;
  309. size = ctl->boot_eepwrite.size;
  310. switch (ctl->boot_eepwrite.target) {
  311. case TARGET_CPU: {
  312. if (size > CPU_E2SIZE ||
  313. address >= CPU_E2SIZE ||
  314. address + size > CPU_E2SIZE)
  315. goto err_inval;
  316. eeprom_busy_wait();
  317. eeprom_write_block(page_buffer, (void *)address, size);
  318. eeprom_busy_wait();
  319. for (i = 0; i < size; i++) {
  320. d = eeprom_read_byte((void *)(address + i));
  321. if (d != page_buffer[i])
  322. goto err_cmdfail;
  323. }
  324. break;
  325. }
  326. case TARGET_COPROC: {
  327. if (size > COPROC_E2SIZE ||
  328. address >= COPROC_E2SIZE ||
  329. address + size > COPROC_E2SIZE)
  330. goto err_inval;
  331. //TODO
  332. break;
  333. }
  334. default:
  335. goto err_context;
  336. }
  337. break;
  338. }
  339. default:
  340. goto err_command;
  341. }
  342. init_control_reply(reply, REPLY_OK, 0, ctl->seqno);
  343. return CONTROL_REPLY_SIZE(ok);
  344. err_context:
  345. reply->error.code = CTLERR_CONTEXT;
  346. goto error;
  347. err_size:
  348. reply->error.code = CTLERR_SIZE;
  349. goto error;
  350. err_command:
  351. reply->error.code = CTLERR_COMMAND;
  352. goto error;
  353. err_inval:
  354. reply->error.code = CTLERR_INVAL;
  355. goto error;
  356. err_cmdfail:
  357. reply->error.code = CTLERR_CMDFAIL;
  358. goto error;
  359. err_checksum:
  360. reply->error.code = CTLERR_CHECKSUM;
  361. goto error;
  362. error:
  363. init_control_reply(reply, REPLY_ERROR, 0, ctl->seqno);
  364. return CONTROL_REPLY_SIZE(error);
  365. }
  366. static bool should_enter_bootloader(void)
  367. {
  368. uint8_t value;
  369. uart_exit();
  370. /* If PD0 (which is UART RXD) is pulled low, stay in bootloader */
  371. PORTD = 0x01;
  372. DDRD = 0x00;
  373. _delay_ms(25);
  374. value = PIND;
  375. uart_init();
  376. if (value & 0x01)
  377. return 0;
  378. return 1;
  379. }
  380. static uint8_t saved_mcucsr __attribute__((section(".noinit")));
  381. void early_init(void) __attribute__((naked, section(".init3"), used));
  382. void early_init(void)
  383. {
  384. irq_disable();
  385. saved_mcucsr = MCUCSR;
  386. MCUCSR = 0;
  387. wdt_enable(WDTO_2S);
  388. }
  389. int main(void) _mainfunc;
  390. int main(void)
  391. {
  392. uint8_t mcucsr;
  393. wdt_enable(WDTO_2S);
  394. mcucsr = saved_mcucsr;
  395. saved_mcucsr = 0;
  396. uart_init();
  397. uart_putstr("BOOT\n");
  398. /* Only enter bootloader on special user request
  399. * or by jump from application code. */
  400. if (!should_enter_bootloader()) {
  401. if ((mcucsr & (1 << EXTRF)) ||
  402. (mcucsr & (1 << JTRF)) ||
  403. (mcucsr & (1 << PORF)) ||
  404. (mcucsr & (1 << WDRF)) ||
  405. (mcucsr & (1 << BORF)))
  406. exit_bootloader();
  407. }
  408. /* Disable shift registers OE */
  409. SR4094_OUTEN_PORT = (uint8_t)(SR4094_OUTEN_PORT & ~(1u << SR4094_OUTEN_BIT));
  410. SR4094_OUTEN_DDR = (uint8_t)(SR4094_OUTEN_DDR | (1u << SR4094_OUTEN_BIT));
  411. disable_all_irq_sources();
  412. route_irqs_to_bootloader();
  413. boot_coprocessor_init();
  414. GICR = 0;
  415. MCUCR = (0 << ISC11) | (0 << ISC10) |
  416. (1 << ISC01) | (0 << ISC00);
  417. pdiusb_init();
  418. irq_enable();
  419. while (1) {
  420. wdt_reset();
  421. }
  422. }