You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

butterfly.c 22 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. /***************************************************************************
  2. * Copyright (C) 01/2020 by Olaf Rempel *
  3. * razzor@kopf-tisch.de *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; version 2 of the License, *
  8. * *
  9. * This program is distributed in the hope that it will be useful, *
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  12. * GNU General Public License for more details. *
  13. * *
  14. * You should have received a copy of the GNU General Public License *
  15. * along with this program; if not, write to the *
  16. * Free Software Foundation, Inc., *
  17. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  18. ***************************************************************************/
  19. #include <stdint.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <unistd.h>
  23. #include <string.h>
  24. #include <sys/socket.h>
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #include <fcntl.h>
  28. #include <termios.h>
  29. #include <sys/time.h>
  30. #include "chipinfo_avr.h"
  31. #include "multiboot.h"
  32. #include "optarg.h"
  33. #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
  34. #define MIN(a, b) ((a) < (b) ? (a) : (b))
  35. #define SERIAL_BAUDRATE B115200
  36. #define SERIAL_TIMEOUT 1000
  37. #define WRITE_SIZE_EEPROM 16
  38. struct multiboot_ops butterfly_ops;
  39. typedef struct bfly_privdata_s
  40. {
  41. char * p_device;
  42. struct termios oldtio;
  43. int fd;
  44. uint8_t twi_address;
  45. uint8_t chip_erase;
  46. uint16_t buffersize;
  47. uint16_t flashsize;
  48. uint16_t eepromsize;
  49. uint8_t progmode_active;
  50. } bfly_privdata_t;
  51. static struct option bfly_optargs[] =
  52. {
  53. { "address", 1, 0, 'a' }, /* [ -a <address ] */
  54. { "device", 1, 0, 'd' }, /* -d <device> */
  55. { "erase", 0, 0, 'e' }, /* [ -e ] */
  56. };
  57. /* *************************************************************************
  58. * bfly_optarg_cb
  59. * ************************************************************************* */
  60. static int bfly_optarg_cb(int val, const char *arg, void *privdata)
  61. {
  62. bfly_privdata_t * p_priv;
  63. p_priv = (bfly_privdata_t *)privdata;
  64. switch (val)
  65. {
  66. case 'a': /* address */
  67. {
  68. char *endptr;
  69. p_priv->twi_address = strtol(arg, &endptr, 16);
  70. if ((*endptr != '\0') ||
  71. (p_priv->twi_address < 0x01) ||
  72. (p_priv->twi_address > 0x7F)
  73. )
  74. {
  75. fprintf(stderr, "invalid address: '%s'\n", arg);
  76. return -1;
  77. }
  78. }
  79. break;
  80. case 'd': /* device */
  81. if (p_priv->p_device != NULL)
  82. {
  83. fprintf(stderr, "invalid device: '%s'\n", arg);
  84. return -1;
  85. }
  86. p_priv->p_device = strdup(optarg);
  87. if (p_priv->p_device == NULL)
  88. {
  89. perror("strdup()");
  90. return -1;
  91. }
  92. break;
  93. case 'e': /* chip erase */
  94. p_priv->chip_erase = 1;
  95. break;
  96. case 'h':
  97. case '?': /* error */
  98. fprintf(stderr, "Usage: butterfly_prog [options]\n"
  99. " -a <address> - optional: twi address for twiboot bridge mode\n"
  100. " -d <device> - selects butterfly serial device\n"
  101. " -e - executes a chip erase\n"
  102. " -r <flash|eeprom>:<file> - reads flash/eeprom to file (.bin | .hex | -)\n"
  103. " -w <flash|eeprom>:<file> - write flash/eeprom from file (.bin | .hex)\n"
  104. " -n - disable verify after write\n"
  105. " -p <0|1|2> - progress bar mode\n"
  106. "\n"
  107. "Example: butterfly_prog -d /dev/ttyUSB0 -w flash:code.hex\n"
  108. "\n");
  109. return -1;
  110. default:
  111. return 1;
  112. }
  113. return 0;
  114. } /* bfly_optarg_cb */
  115. /* *************************************************************************
  116. * butterfly_alloc
  117. * ************************************************************************* */
  118. static struct multiboot * butterfly_alloc(void)
  119. {
  120. struct multiboot * mboot = malloc(sizeof(struct multiboot));
  121. if (mboot == NULL)
  122. {
  123. return NULL;
  124. }
  125. memset(mboot, 0x00, sizeof(struct multiboot));
  126. mboot->ops= &butterfly_ops;
  127. bfly_privdata_t * p_priv = malloc(sizeof(bfly_privdata_t));
  128. if (p_priv == NULL)
  129. {
  130. free(mboot);
  131. return NULL;
  132. }
  133. memset(p_priv, 0x00, sizeof(bfly_privdata_t));
  134. optarg_register(bfly_optargs, ARRAY_SIZE(bfly_optargs),
  135. bfly_optarg_cb, (void *)p_priv);
  136. mboot->privdata = p_priv;
  137. return mboot;
  138. } /* butterfly_alloc */
  139. /* *************************************************************************
  140. * butterfly_free
  141. * ************************************************************************* */
  142. static void butterfly_free(struct multiboot * p_mboot)
  143. {
  144. bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata;
  145. if (p_priv->p_device != NULL)
  146. {
  147. free(p_priv->p_device);
  148. }
  149. free(p_priv);
  150. free(p_mboot);
  151. } /* butterfly_free */
  152. /* *************************************************************************
  153. * butterfly_get_memtype
  154. * ************************************************************************* */
  155. static int butterfly_get_memtype(struct multiboot * p_mboot,
  156. const char * p_memname)
  157. {
  158. /* unused parameter */
  159. (void)p_mboot;
  160. if (strcmp(p_memname, "flash") == 0)
  161. {
  162. return 'F';
  163. }
  164. else if (strcmp(p_memname, "eeprom") == 0)
  165. {
  166. return 'E';
  167. }
  168. return -1;
  169. } /* butterfly_get_memtype */
  170. /* *************************************************************************
  171. * butterfly_get_memsize
  172. * ************************************************************************* */
  173. static int butterfly_get_memsize(struct multiboot * p_mboot,
  174. int memtype)
  175. {
  176. bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata;
  177. if (!p_priv->progmode_active)
  178. {
  179. return 0;
  180. }
  181. switch (memtype)
  182. {
  183. case 'F':
  184. return p_priv->flashsize;
  185. case 'E':
  186. return p_priv->eepromsize;
  187. default:
  188. return 0;
  189. }
  190. } /* butterfly_get_memsize */
  191. /* *************************************************************************
  192. * butterfly_close_device
  193. * ************************************************************************* */
  194. static void butterfly_close_device(bfly_privdata_t * p_priv)
  195. {
  196. /* delay close() / tcsetattr() */
  197. usleep(100000);
  198. tcsetattr(p_priv->fd, TCSANOW, &p_priv->oldtio);
  199. close(p_priv->fd);
  200. } /* butterfly_close_device */
  201. /* *************************************************************************
  202. * butterlfy_open_device
  203. * ************************************************************************* */
  204. static int butterfly_open_device(bfly_privdata_t * p_priv)
  205. {
  206. p_priv->fd = open(p_priv->p_device, O_RDWR | O_NOCTTY | O_CLOEXEC);
  207. if (p_priv->fd < 0)
  208. {
  209. perror("open()");
  210. return -1;
  211. }
  212. if (tcgetattr(p_priv->fd, &p_priv->oldtio) < 0)
  213. {
  214. perror("tcgetattr(oldtio)");
  215. close(p_priv->fd);
  216. return -1;
  217. }
  218. struct termios newtio;
  219. memset(&newtio, 0, sizeof(newtio));
  220. newtio.c_iflag |= IGNBRK;
  221. newtio.c_cflag |= SERIAL_BAUDRATE | CS8 | CLOCAL | CREAD;
  222. newtio.c_cc[VMIN] = 1;
  223. newtio.c_cc[VTIME] = 0;
  224. int err = tcsetattr(p_priv->fd, TCSANOW, &newtio);
  225. if (err < 0)
  226. {
  227. perror("tcsetattr(newtio)");
  228. close(p_priv->fd);
  229. return -1;
  230. }
  231. return 0;
  232. } /* butterlfy_open_device */
  233. /* *************************************************************************
  234. * butterfly_serial_read
  235. * ************************************************************************* */
  236. static int butterfly_serial_read(int fd, void * data, int size,
  237. unsigned int timeout_ms)
  238. {
  239. int pos = 0;
  240. while (1)
  241. {
  242. fd_set fdset;
  243. struct timeval timeout;
  244. struct timeval * p_timeout = NULL;
  245. FD_ZERO(&fdset);
  246. FD_SET(fd, &fdset);
  247. if (timeout_ms != 0)
  248. {
  249. p_timeout = &timeout;
  250. timeout.tv_sec = timeout_ms / 1000;
  251. timeout.tv_usec = (timeout_ms % 1000) * 1000;
  252. }
  253. int ret = select(fd +1, &fdset, NULL, NULL, p_timeout);
  254. if (ret == -1)
  255. {
  256. perror("select");
  257. return -1;
  258. }
  259. else if (ret == 0)
  260. {
  261. break;
  262. }
  263. else if (FD_ISSET(fd, &fdset))
  264. {
  265. int len = read(fd, data + pos, size - pos);
  266. if (len < 0)
  267. {
  268. return -1;
  269. }
  270. else
  271. {
  272. pos += len;
  273. if (pos == size)
  274. {
  275. break;
  276. }
  277. }
  278. }
  279. }
  280. return pos;
  281. } /* butterfly_serial_read */
  282. /* *************************************************************************
  283. * butterfly_expect_cr
  284. * ************************************************************************* */
  285. static int butterfly_expect_cr(bfly_privdata_t * p_priv)
  286. {
  287. uint8_t buffer[1];
  288. int result;
  289. result = butterfly_serial_read(p_priv->fd, buffer, sizeof(buffer),
  290. SERIAL_TIMEOUT);
  291. if ((result == sizeof(buffer)) &&
  292. (buffer[0] == '\r')
  293. )
  294. {
  295. return 0;
  296. }
  297. return -1;
  298. } /* butterfly_expect_cr */
  299. /* *************************************************************************
  300. * butterfly_enter_progmode
  301. * ************************************************************************* */
  302. static int butterfly_enter_progmode(bfly_privdata_t * p_priv)
  303. {
  304. if (p_priv->twi_address == 0x00)
  305. {
  306. (void)write(p_priv->fd, "P", 1);
  307. }
  308. else
  309. {
  310. uint8_t cmd[2] = { 'I' , p_priv->twi_address };
  311. (void)write(p_priv->fd, cmd, 2);
  312. }
  313. return butterfly_expect_cr(p_priv);
  314. } /* butterfly_enter_progmode */
  315. /* *************************************************************************
  316. * butterfly_leave_progmode
  317. * ************************************************************************* */
  318. static int butterfly_leave_progmode(bfly_privdata_t * p_priv)
  319. {
  320. (void)write(p_priv->fd, "L", 1);
  321. return butterfly_expect_cr(p_priv);
  322. } /* butterfly_leave_progmode */
  323. /* *************************************************************************
  324. * butterfly_get_signature
  325. * ************************************************************************* */
  326. static int butterfly_get_signature(bfly_privdata_t * p_priv,
  327. uint8_t * p_signature)
  328. {
  329. int result;
  330. uint8_t buffer[3];
  331. (void)write(p_priv->fd, "s", 1);
  332. result = butterfly_serial_read(p_priv->fd, buffer, sizeof(buffer),
  333. SERIAL_TIMEOUT);
  334. if (result == 3)
  335. {
  336. p_signature[0] = buffer[2];
  337. p_signature[1] = buffer[1];
  338. p_signature[2] = buffer[0];
  339. return 0;
  340. }
  341. return -1;
  342. } /* butterfly_get_signature */
  343. /* *************************************************************************
  344. * butterfly_get_buffersize
  345. * ************************************************************************* */
  346. static int butterfly_get_buffersize(bfly_privdata_t * p_priv,
  347. uint16_t * p_buffersize)
  348. {
  349. int result;
  350. uint8_t buffer[3];
  351. (void)write(p_priv->fd, "b", 1);
  352. result = butterfly_serial_read(p_priv->fd, buffer, sizeof(buffer),
  353. SERIAL_TIMEOUT);
  354. if (result == sizeof(buffer))
  355. {
  356. if (buffer[0] == 'Y')
  357. {
  358. *p_buffersize = (buffer[1] << 8) | buffer[2];
  359. }
  360. else
  361. {
  362. *p_buffersize = 0;
  363. }
  364. }
  365. return (result != sizeof(buffer));
  366. } /* butterfly_get_buffersize */
  367. /* *************************************************************************
  368. * butterfly_chiperase
  369. * ************************************************************************* */
  370. static int butterfly_chiperase(bfly_privdata_t * p_priv)
  371. {
  372. (void)write(p_priv->fd, "e", 1);
  373. return butterfly_expect_cr(p_priv);
  374. } /* butterfly_chiperase */
  375. /* *************************************************************************
  376. * butterfly_set_address
  377. * ************************************************************************* */
  378. static int butterfly_set_address(bfly_privdata_t * p_priv, uint16_t pos)
  379. {
  380. uint8_t buffer[1];
  381. int result;
  382. (void)write(p_priv->fd, "a", 1);
  383. result = butterfly_serial_read(p_priv->fd, buffer, sizeof(buffer),
  384. SERIAL_TIMEOUT);
  385. if ((result == 1) &&
  386. (buffer[0] == 'Y')
  387. )
  388. {
  389. /* convert to word address */
  390. pos >>= 1;
  391. uint8_t cmd[3] = { 'A', pos >> 8, pos & 0xFF };
  392. (void)write(p_priv->fd, cmd, sizeof(cmd));
  393. result = butterfly_expect_cr(p_priv);
  394. }
  395. return result;
  396. } /* butterfly_set_address */
  397. /* *************************************************************************
  398. * butterfly_read_data
  399. * ************************************************************************* */
  400. static int butterfly_read_data(bfly_privdata_t * p_priv,
  401. uint8_t * p_data, uint16_t size,
  402. uint8_t memtype)
  403. {
  404. int result;
  405. uint8_t cmd[4] = { 'g', size >> 8, size & 0xFF, memtype };
  406. (void)write(p_priv->fd, cmd, 4);
  407. result = butterfly_serial_read(p_priv->fd, p_data, size,
  408. SERIAL_TIMEOUT);
  409. return (result != size);
  410. } /* butterfly_read_data */
  411. /* *************************************************************************
  412. * butterfly_write_data
  413. * ************************************************************************* */
  414. static int butterfly_write_data(bfly_privdata_t * p_priv,
  415. const uint8_t * p_data, uint16_t size,
  416. uint8_t memtype)
  417. {
  418. uint8_t cmd[4] = { 'B', size >> 8, size & 0xFF, memtype };
  419. (void)write(p_priv->fd, cmd, 4);
  420. (void)write(p_priv->fd, p_data, size);
  421. return butterfly_expect_cr(p_priv);
  422. } /* butterfly_write_data */
  423. /* *************************************************************************
  424. * butterfly_close
  425. * ************************************************************************* */
  426. static int butterfly_close(struct multiboot * p_mboot)
  427. {
  428. bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata;
  429. if (p_priv->progmode_active)
  430. {
  431. butterfly_leave_progmode(p_priv);
  432. }
  433. butterfly_close_device(p_priv);
  434. return 0;
  435. } /* butterfly_close */
  436. /* *************************************************************************
  437. * butterfly_open
  438. * ************************************************************************* */
  439. static int butterfly_open(struct multiboot * p_mboot)
  440. {
  441. bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata;
  442. if (p_priv->p_device == NULL)
  443. {
  444. fprintf(stderr, "abort: no device given\n");
  445. return -1;
  446. }
  447. if (butterfly_open_device(p_priv) < 0)
  448. {
  449. return -1;
  450. }
  451. if (butterfly_enter_progmode(p_priv) != 0)
  452. {
  453. fprintf(stderr, "failed to enter progmode\n");
  454. butterfly_close_device(p_priv);
  455. return -1;
  456. }
  457. p_priv->progmode_active = 1;
  458. uint8_t signature[3];
  459. if (butterfly_get_signature(p_priv, signature) != 0)
  460. {
  461. fprintf(stderr, "failed to get signature\n");
  462. butterfly_close_device(p_priv);
  463. return -1;
  464. }
  465. const avr_chipinfo_t * p_chipinfo;
  466. p_chipinfo = chipinfo_get_by_signature(signature);
  467. if (p_chipinfo == NULL)
  468. {
  469. fprintf(stderr, "failed to identify chip signature [0x%02x 0x%02x 0x%02x]\n",
  470. signature[0], signature[1], signature[2]);
  471. butterfly_close_device(p_priv);
  472. return -1;
  473. }
  474. p_priv->flashsize = p_chipinfo->flashsize;
  475. p_priv->eepromsize = p_chipinfo->eepromsize;
  476. if (butterfly_get_buffersize(p_priv, &p_priv->buffersize) != 0)
  477. {
  478. fprintf(stderr, "failed to get buffersize\n");
  479. butterfly_close_device(p_priv);
  480. return -1;
  481. }
  482. if (p_priv->twi_address != 0x00)
  483. {
  484. printf("twi address : 0x%02x\n",
  485. p_priv->twi_address);
  486. }
  487. printf("device : %-16s (sig: 0x%02x 0x%02x 0x%02x)\n",
  488. p_chipinfo->name, p_chipinfo->sig[0],
  489. p_chipinfo->sig[1], p_chipinfo->sig[2]);
  490. printf("flash size : 0x%04x / %5d\n",
  491. p_chipinfo->flashsize, p_chipinfo->flashsize);
  492. printf("eeprom size : 0x%04x / %5d\n",
  493. p_chipinfo->eepromsize, p_chipinfo->eepromsize);
  494. if (p_priv->chip_erase)
  495. {
  496. if (butterfly_chiperase(p_priv) != 0)
  497. {
  498. fprintf(stderr, "failed to chip erase\n");
  499. butterfly_close_device(p_priv);
  500. return -1;
  501. }
  502. printf("chip erased : OK\n");
  503. }
  504. return 0;
  505. } /* butterfly_open */
  506. /* *************************************************************************
  507. * butterfly_read
  508. * ************************************************************************* */
  509. static int butterfly_read(struct multiboot * p_mboot,
  510. struct databuf * p_dbuf,
  511. int memtype)
  512. {
  513. bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata;
  514. char * p_progress_msg = (memtype == 'F') ? "reading flash" : "reading eeprom";
  515. uint16_t pos = 0;
  516. uint16_t size = (memtype == 'F') ? p_priv->flashsize :
  517. p_priv->eepromsize;
  518. if (butterfly_set_address(p_priv, pos) < 0)
  519. {
  520. fprintf(stderr, "failed to set address\n");
  521. return -1;
  522. }
  523. while (pos < size)
  524. {
  525. p_mboot->progress_cb(p_progress_msg, pos, size);
  526. uint16_t len = MIN(p_priv->buffersize, size - pos);
  527. if (butterfly_read_data(p_priv, p_dbuf->data + pos, len, memtype))
  528. {
  529. p_mboot->progress_cb(p_progress_msg, -1, -1);
  530. return -1;
  531. }
  532. pos += len;
  533. }
  534. p_dbuf->length = pos;
  535. p_mboot->progress_cb(p_progress_msg, pos, size);
  536. return 0;
  537. } /* butterfly_read */
  538. /* *************************************************************************
  539. * butterfly_write
  540. * ************************************************************************* */
  541. static int butterfly_write(struct multiboot * p_mboot,
  542. struct databuf * p_dbuf,
  543. int memtype)
  544. {
  545. bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata;
  546. char * p_progress_msg = (memtype == 'F') ? "writing flash" : "writing eeprom";
  547. uint16_t pos = 0;
  548. if (butterfly_set_address(p_priv, pos) < 0)
  549. {
  550. fprintf(stderr, "failed to set address\n");
  551. return -1;
  552. }
  553. while (pos < p_dbuf->length)
  554. {
  555. p_mboot->progress_cb(p_progress_msg, pos, p_dbuf->length);
  556. uint16_t len = (memtype == 'F') ? p_priv->buffersize : WRITE_SIZE_EEPROM;
  557. len = MIN(len, p_dbuf->length - pos);
  558. if (butterfly_write_data(p_priv, p_dbuf->data + pos, len, memtype))
  559. {
  560. p_mboot->progress_cb(p_progress_msg, -1, -1);
  561. return -1;
  562. }
  563. pos += len;
  564. }
  565. p_mboot->progress_cb(p_progress_msg, pos, p_dbuf->length);
  566. return 0;
  567. } /* butterfly_write */
  568. /* *************************************************************************
  569. * butterfly_verify
  570. * ************************************************************************* */
  571. static int butterfly_verify(struct multiboot * p_mboot,
  572. struct databuf * p_dbuf,
  573. int memtype)
  574. {
  575. bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata;
  576. char * p_progress_msg = (memtype == 'F') ? "verifing flash" : "verifing eeprom";
  577. uint16_t pos = 0;
  578. uint8_t comp[256];
  579. if (butterfly_set_address(p_priv, pos) < 0)
  580. {
  581. fprintf(stderr, "failed to set address\n");
  582. return -1;
  583. }
  584. while (pos < p_dbuf->length)
  585. {
  586. p_mboot->progress_cb(p_progress_msg, pos, p_dbuf->length);
  587. uint16_t len = MIN(p_priv->buffersize, p_dbuf->length - pos);
  588. if (butterfly_read_data(p_priv, comp, len, memtype))
  589. {
  590. p_mboot->progress_cb(p_progress_msg, -1, -1);
  591. return -1;
  592. }
  593. if (memcmp(comp, p_dbuf->data + pos, len) != 0x00)
  594. {
  595. p_mboot->progress_cb(p_progress_msg, -1, -1);
  596. fprintf(stderr, "verify failed at pos 0x%04x!!\n", pos);
  597. return -1;
  598. }
  599. pos += len;
  600. }
  601. p_dbuf->length = pos;
  602. p_mboot->progress_cb(p_progress_msg, pos, p_dbuf->length);
  603. return 0;
  604. } /* butterfly_verify */
  605. struct multiboot_ops butterfly_ops =
  606. {
  607. .exec_name = "butterfly_prog",
  608. .alloc = butterfly_alloc,
  609. .free = butterfly_free,
  610. .get_memtype = butterfly_get_memtype,
  611. .get_memsize = butterfly_get_memsize,
  612. .open = butterfly_open,
  613. .close = butterfly_close,
  614. .read = butterfly_read,
  615. .write = butterfly_write,
  616. .verify = butterfly_verify,
  617. };