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.

843 lines
21 KiB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <sys/socket.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>
  9. #include <termios.h>
  10. #include <sys/time.h>
  11. #include "chipinfo_avr.h"
  12. #include "multiboot.h"
  13. #include "optarg.h"
  14. #define READ_BLOCK_SIZE 256 /* bytes in one flash/eeprom read request */
  15. #define WRITE_BLOCK_SIZE 16 /* bytes in one eeprom write request */
  16. #define CMD_SWITCH_APPLICATION 0x01
  17. #define CMD_GET_BOOTLOADER_VERSION 0x02
  18. #define CMD_GET_CHIP_INFO 0x03
  19. #define CMD_READ_MEMORY 0x11
  20. #define CMD_WRITE_MEMORY 0x12
  21. #define CAUSE_SUCCESS 0x00
  22. #define CAUSE_COMMAND_NOT_SUPPORTED 0xF0
  23. #define CAUSE_INVALID_PARAMETER 0xF1
  24. #define CAUSE_UNSPECIFIED_ERROR 0xFF
  25. /* CMD_SWITCH_APPLICATION parameter */
  26. #define BOOTTYPE_BOOTLOADER 0x00
  27. #define BOOTTYPE_APPLICATION 0x80
  28. #define MEMTYPE_FLASH 0x01
  29. #define MEMTYPE_EEPROM 0x02
  30. #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
  31. #define MIN(a, b) ((a) < (b) ? (a) : (b))
  32. struct multiboot_ops mpm_ops;
  33. struct mpm_privdata
  34. {
  35. char *device;
  36. int fd;
  37. int connected;
  38. uint8_t address;
  39. uint16_t flashsize;
  40. uint8_t flashpage;
  41. uint16_t eepromsize;
  42. struct termios oldtio;
  43. };
  44. static struct option mpm_optargs[] =
  45. {
  46. { "address", 1, 0, 'a'}, /* -a <addr> */
  47. { "device", 1, 0, 'd'}, /* [ -d <device> ] */
  48. };
  49. /* *************************************************************************
  50. * mpm_optarg_cb
  51. * ************************************************************************* */
  52. static int mpm_optarg_cb(int val, const char *arg, void *privdata)
  53. {
  54. struct mpm_privdata *mpm = (struct mpm_privdata *)privdata;
  55. switch (val)
  56. {
  57. case 'a': /* address */
  58. {
  59. char *endptr;
  60. mpm->address = strtol(arg, &endptr, 16);
  61. if (*endptr != '\0' || mpm->address < 0x01 || mpm->address > 0x7F)
  62. {
  63. fprintf(stderr, "invalid address: '%s'\n", arg);
  64. return -1;
  65. }
  66. }
  67. break;
  68. case 'd': /* device */
  69. if (mpm->device != NULL)
  70. {
  71. fprintf(stderr, "invalid device: '%s'\n", optarg);
  72. return -1;
  73. }
  74. mpm->device = strdup(optarg);
  75. if (mpm->device == NULL)
  76. {
  77. perror("strdup()");
  78. return -1;
  79. }
  80. break;
  81. case 'h':
  82. case '?': /* error */
  83. fprintf(stderr, "Usage: mpmboot [options]\n"
  84. " -a <address> - selects mpm address (0x01 - 0xFF)\n"
  85. " -d <device> - selects mpm device\n"
  86. " -r <flash|eeprom>:<file> - reads flash/eeprom to file (.bin | .hex | -)\n"
  87. " -w <flash|eeprom>:<file> - write flash/eeprom from file (.bin | .hex)\n"
  88. " -n - disable verify after write\n"
  89. " -p <0|1|2> - progress bar mode\n"
  90. "\n"
  91. "Example: mpmboot -d /dev/ttyUSB0 -a 0x22 -w flash:blmc.hex -w flash:blmc_eeprom.hex\n"
  92. "\n");
  93. return -1;
  94. default:
  95. return 1;
  96. }
  97. return 0;
  98. } /* mpm_optarg_cb */
  99. /* *************************************************************************
  100. * mpm_alloc
  101. * ************************************************************************* */
  102. static struct multiboot * mpm_alloc(void)
  103. {
  104. struct multiboot * mboot = malloc(sizeof(struct multiboot));
  105. if (mboot == NULL)
  106. {
  107. return NULL;
  108. }
  109. memset(mboot, 0x00, sizeof(struct multiboot));
  110. mboot->ops = &mpm_ops;
  111. struct mpm_privdata *mpm = malloc(sizeof(struct mpm_privdata));
  112. if (mpm == NULL)
  113. {
  114. free(mboot);
  115. return NULL;
  116. }
  117. memset(mpm, 0x00, sizeof(struct mpm_privdata));
  118. mpm->device = NULL;
  119. mpm->address = 0;
  120. optarg_register(mpm_optargs, ARRAY_SIZE(mpm_optargs),
  121. mpm_optarg_cb, (void *)mpm);
  122. mboot->privdata = mpm;
  123. return mboot;
  124. } /* mpm_alloc */
  125. /* *************************************************************************
  126. * mpm_free
  127. * ************************************************************************* */
  128. static void mpm_free(struct multiboot *mboot)
  129. {
  130. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  131. if (mpm->device != NULL)
  132. {
  133. free(mpm->device);
  134. }
  135. free(mpm);
  136. free(mboot);
  137. } /* mpm_free */
  138. /* *************************************************************************
  139. * mpm_get_memtype
  140. * ************************************************************************* */
  141. static int mpm_get_memtype(struct multiboot *mboot,
  142. const char *memname)
  143. {
  144. /* unused parameter */
  145. (void)mboot;
  146. if (strcmp(memname, "flash") == 0)
  147. {
  148. return MEMTYPE_FLASH;
  149. }
  150. else if (strcmp(memname, "eeprom") == 0)
  151. {
  152. return MEMTYPE_EEPROM;
  153. }
  154. return -1;
  155. } /* mpm_get_memtype */
  156. /* *************************************************************************
  157. * mpm_get_memsize
  158. * ************************************************************************* */
  159. static int mpm_get_memsize(struct multiboot *mboot, int memtype)
  160. {
  161. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  162. if (!mpm->connected)
  163. {
  164. return 0;
  165. }
  166. switch (memtype)
  167. {
  168. case MEMTYPE_FLASH:
  169. return mpm->flashsize;
  170. case MEMTYPE_EEPROM:
  171. return mpm->eepromsize;
  172. default:
  173. return 0;
  174. }
  175. } /* mpm_get_memsize */
  176. /* *************************************************************************
  177. * mpm_send
  178. * ************************************************************************* */
  179. static int mpm_send(struct mpm_privdata *mpm, uint8_t command,
  180. uint8_t *data, uint16_t length)
  181. {
  182. struct termios tio;
  183. if (tcgetattr(mpm->fd, &tio) < 0)
  184. {
  185. perror("tcgetattr(tio)");
  186. return -1;
  187. }
  188. tio.c_cflag |= PARODD;
  189. if (tcsetattr(mpm->fd, TCSAFLUSH, &tio) < 0)
  190. {
  191. perror("tcsetattr(tio)");
  192. return -1;
  193. }
  194. // usleep(5000);
  195. uint8_t address = mpm->address;
  196. if (write(mpm->fd, &address, sizeof(address)) != sizeof(address))
  197. {
  198. perror("write(address)");
  199. return -1;
  200. }
  201. usleep(500);
  202. tio.c_cflag &= ~(PARODD);
  203. if (tcsetattr(mpm->fd, TCSAFLUSH, &tio) < 0)
  204. {
  205. perror("tcsetattr(tio)");
  206. return -1;
  207. }
  208. uint8_t header[3];
  209. header[0] = command;
  210. header[1] = (length >> 8) & 0xFF;
  211. header[2] = length & 0xFF;
  212. if (write(mpm->fd, header, sizeof(header)) != sizeof(header))
  213. {
  214. perror("write(header)");
  215. return -1;
  216. }
  217. if (data != NULL && length != 0)
  218. {
  219. if (write(mpm->fd, data, length) != length)
  220. {
  221. perror("write(data)");
  222. return -1;
  223. }
  224. }
  225. return 0;
  226. } /* mpm_send */
  227. /* *************************************************************************
  228. * mpm_serial_read
  229. * ************************************************************************* */
  230. static int mpm_serial_read(int fd, void *data, int size)
  231. {
  232. int pos = 0;
  233. while (1)
  234. {
  235. fd_set fdset;
  236. struct timeval timeout = { .tv_sec = 1, .tv_usec = 0 };
  237. FD_ZERO(&fdset);
  238. FD_SET(fd, &fdset);
  239. int ret = select(fd +1, &fdset, NULL, NULL, &timeout);
  240. if (ret == -1)
  241. {
  242. perror("select");
  243. return -1;
  244. }
  245. else if (ret == 0)
  246. {
  247. break;
  248. }
  249. else if (FD_ISSET(fd, &fdset))
  250. {
  251. int len = read(fd, data + pos, size - pos);
  252. if (len < 0)
  253. {
  254. return -1;
  255. }
  256. else
  257. {
  258. pos += len;
  259. if (pos == size)
  260. {
  261. break;
  262. }
  263. }
  264. }
  265. }
  266. return pos;
  267. } /* mpm_serial_read */
  268. /* *************************************************************************
  269. * mpm_recv
  270. * ************************************************************************* */
  271. static int mpm_recv(struct mpm_privdata *mpm,
  272. uint8_t command,
  273. uint8_t *cause,
  274. uint8_t *buffer,
  275. uint16_t buffersize)
  276. {
  277. int len;
  278. uint8_t header[4];
  279. len = mpm_serial_read(mpm->fd, header, sizeof(header));
  280. if (len != sizeof(header))
  281. {
  282. fprintf(stderr, "short read() from device (not addressed?)\n");
  283. return -1;
  284. }
  285. if (header[0] != command)
  286. {
  287. fprintf(stderr, "invalid command response (0x%02x != 0x%02x)\n", header[0], command);
  288. return -1;
  289. }
  290. *cause = header[1];
  291. uint16_t length = (header[2] << 8) | header[3];
  292. // printf("mpm_recv() cmd=0x%02x cause=0x%02x length=0x%04x\n", command, *cause, length);
  293. uint16_t bufferpos = 0;
  294. while (length > 0)
  295. {
  296. /* free space in output buffer? */
  297. if ((bufferpos < buffersize) && (buffer != NULL))
  298. {
  299. uint16_t size = MIN(buffersize - bufferpos, length);
  300. len = mpm_serial_read(mpm->fd, buffer + bufferpos, size);
  301. if (len <= 0)
  302. {
  303. fprintf(stderr, "short read() from device (%d != %d)\n", len, size);
  304. return -1;
  305. }
  306. bufferpos += len;
  307. length -= len;
  308. }
  309. else
  310. {
  311. uint8_t dummy[256];
  312. /* no space in output buffer, but device still sends data -> do dummy read */
  313. uint16_t size = MIN(sizeof(dummy), length);
  314. len = mpm_serial_read(mpm->fd, dummy, size);
  315. if (len <= 0)
  316. {
  317. fprintf(stderr, "short read() from device (%d != %d)\n", len, size);
  318. return -1;
  319. }
  320. length -= len;
  321. }
  322. }
  323. return bufferpos;
  324. } /* mpm_recv */
  325. /* *************************************************************************
  326. * mpm_close_device
  327. * ************************************************************************* */
  328. static void mpm_close_device(struct mpm_privdata *mpm)
  329. {
  330. /* delay close() / tcsetattr() */
  331. usleep(100000);
  332. tcsetattr(mpm->fd, TCSANOW, &mpm->oldtio);
  333. close(mpm->fd);
  334. } /* mpm_close_device */
  335. /* *************************************************************************
  336. * mpm_open_device
  337. * ************************************************************************* */
  338. static int mpm_open_device(struct mpm_privdata *mpm)
  339. {
  340. mpm->fd = open(mpm->device, O_RDWR | O_NOCTTY | O_CLOEXEC);
  341. if (mpm->fd < 0)
  342. {
  343. perror("open()");
  344. return -1;
  345. }
  346. if (tcgetattr(mpm->fd, &mpm->oldtio) < 0)
  347. {
  348. perror("tcgetattr(oldtio)");
  349. close(mpm->fd);
  350. return -1;
  351. }
  352. struct termios newtio;
  353. memset(&newtio, 0, sizeof(newtio));
  354. newtio.c_iflag |= IGNBRK;
  355. newtio.c_cflag |= B115200 | CS8 | CLOCAL | CREAD | PARENB | CMSPAR;
  356. newtio.c_cc[VMIN] = 1;
  357. newtio.c_cc[VTIME] = 0;
  358. int err = tcsetattr(mpm->fd, TCSANOW, &newtio);
  359. if (err < 0)
  360. {
  361. perror("tcsetattr(newtio)");
  362. close(mpm->fd);
  363. return -1;
  364. }
  365. mpm->connected = 1;
  366. return 0;
  367. } /* mpm_open_device */
  368. /* *************************************************************************
  369. * mpm_switch_application
  370. * ************************************************************************* */
  371. static int mpm_switch_application(struct mpm_privdata *mpm,
  372. uint8_t application)
  373. {
  374. uint8_t data[] = { application };
  375. int ret = mpm_send(mpm, CMD_SWITCH_APPLICATION, data, sizeof(data));
  376. if (ret < 0)
  377. {
  378. return ret;
  379. }
  380. uint8_t cause = CAUSE_SUCCESS;
  381. ret = mpm_recv(mpm, CMD_SWITCH_APPLICATION, &cause, NULL, 0);
  382. if (ret < 0)
  383. {
  384. return ret;
  385. }
  386. return (cause != CAUSE_SUCCESS);
  387. } /* mpm_switch_application */
  388. /* *************************************************************************
  389. * mpm_read_version
  390. * ************************************************************************* */
  391. static int mpm_read_version(struct mpm_privdata *mpm,
  392. uint8_t *version, uint16_t length)
  393. {
  394. memset(version, 0, length);
  395. int ret = mpm_send(mpm, CMD_GET_BOOTLOADER_VERSION, NULL, 0);
  396. if (ret < 0)
  397. {
  398. return ret;
  399. }
  400. uint8_t cause = CAUSE_SUCCESS;
  401. ret = mpm_recv(mpm, CMD_GET_BOOTLOADER_VERSION, &cause, version, length);
  402. if (ret < 0)
  403. {
  404. return ret;
  405. }
  406. int i;
  407. for (i = 0; i < length; i++)
  408. {
  409. version[i] &= ~0x80;
  410. }
  411. return (cause != CAUSE_SUCCESS);
  412. }
  413. /* *************************************************************************
  414. * mpm_read_chipinfo
  415. * ************************************************************************* */
  416. static int mpm_read_chipinfo(struct mpm_privdata *mpm,
  417. uint8_t *chipinfo, uint16_t length)
  418. {
  419. int ret = mpm_send(mpm, CMD_GET_CHIP_INFO, NULL, 0);
  420. if (ret < 0)
  421. {
  422. return ret;
  423. }
  424. uint8_t cause = CAUSE_SUCCESS;
  425. ret = mpm_recv(mpm, CMD_GET_CHIP_INFO, &cause, chipinfo, length);
  426. if (ret < 0)
  427. {
  428. return ret;
  429. }
  430. return (cause != CAUSE_SUCCESS);
  431. } /* mpm_read_chipinfo */
  432. /* *************************************************************************
  433. * mpm_read_memory
  434. * ************************************************************************* */
  435. static int mpm_read_memory(struct mpm_privdata *mpm,
  436. uint8_t *buffer, uint16_t size,
  437. uint8_t memtype, uint16_t address)
  438. {
  439. uint8_t param[5] = {
  440. memtype,
  441. (address >> 8) & 0xFF,
  442. (address & 0xFF),
  443. (size >> 8) & 0xFF,
  444. (size & 0xFF)
  445. };
  446. int ret = mpm_send(mpm, CMD_READ_MEMORY, param, sizeof(param));
  447. if (ret < 0)
  448. {
  449. return ret;
  450. }
  451. uint8_t cause = CAUSE_SUCCESS;
  452. ret = mpm_recv(mpm, CMD_READ_MEMORY, &cause, buffer, size);
  453. if (ret < 0)
  454. {
  455. return ret;
  456. }
  457. return (cause != CAUSE_SUCCESS);
  458. } /* mpm_read_memory */
  459. /* *************************************************************************
  460. * mpm_write_memory
  461. * ************************************************************************* */
  462. static int mpm_write_memory(struct mpm_privdata *mpm,
  463. uint8_t *buffer, uint16_t size,
  464. uint8_t memtype, uint16_t address)
  465. {
  466. int bufsize;
  467. if (memtype == MEMTYPE_FLASH)
  468. {
  469. if ((address & (mpm->flashpage -1)) != 0x00)
  470. {
  471. fprintf(stderr, "mpm_write_memory(): address 0x%04x not aligned to pagesize 0x%02x\n", address, mpm->flashpage);
  472. return -1;
  473. }
  474. bufsize = 5 + mpm->flashpage;
  475. }
  476. else
  477. {
  478. bufsize = 5 + size;
  479. }
  480. uint8_t *cmd = malloc(bufsize);
  481. if (cmd == NULL)
  482. {
  483. return -1;
  484. }
  485. cmd[0] = memtype;
  486. cmd[1] = (address >> 8) & 0xFF;
  487. cmd[2] = (address & 0xFF);
  488. cmd[3] = ((bufsize -5) >> 8) & 0xFF;
  489. cmd[4] = ((bufsize -5) & 0xFF);
  490. memcpy(cmd +5, buffer, size);
  491. if (memtype == MEMTYPE_FLASH)
  492. {
  493. memset(cmd +5 +size, 0xFF, mpm->flashpage - size);
  494. }
  495. int ret = mpm_send(mpm, CMD_WRITE_MEMORY, cmd, bufsize);
  496. if (ret < 0)
  497. {
  498. return ret;
  499. }
  500. free(cmd);
  501. uint8_t cause = CAUSE_SUCCESS;
  502. ret = mpm_recv(mpm, CMD_WRITE_MEMORY, &cause, NULL, 0);
  503. if (ret < 0)
  504. {
  505. return ret;
  506. }
  507. return (cause != CAUSE_SUCCESS);
  508. } /* mpm_write_memory */
  509. /* *************************************************************************
  510. * mpm_close
  511. * ************************************************************************* */
  512. static int mpm_close(struct multiboot *mboot)
  513. {
  514. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  515. if (mpm->connected)
  516. {
  517. mpm_switch_application(mpm, BOOTTYPE_APPLICATION);
  518. }
  519. mpm_close_device(mpm);
  520. return 0;
  521. } /* mpm_close */
  522. /* *************************************************************************
  523. * mpm_open
  524. * ************************************************************************* */
  525. static int mpm_open(struct multiboot *mboot)
  526. {
  527. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  528. if (mpm->address == 0)
  529. {
  530. fprintf(stderr, "abort: no address given\n");
  531. return -1;
  532. }
  533. if (mpm->device == NULL)
  534. {
  535. fprintf(stderr, "abort: no device given\n");
  536. return -1;
  537. }
  538. if (mpm_open_device(mpm) < 0)
  539. {
  540. return -1;
  541. }
  542. if (mpm_switch_application(mpm, BOOTTYPE_BOOTLOADER))
  543. {
  544. fprintf(stderr, "failed to switch to bootloader (invalid address?)\n");
  545. mpm_close(mboot);
  546. return -1;
  547. }
  548. /* wait for watchdog and startup time */
  549. usleep(100000);
  550. char version[16 +1];
  551. if (mpm_read_version(mpm, (uint8_t *)version, sizeof(version) -1))
  552. {
  553. fprintf(stderr, "failed to get bootloader version\n");
  554. mpm_close(mboot);
  555. return -1;
  556. }
  557. version[16] = '\0';
  558. uint8_t chipinfo[8];
  559. if (mpm_read_chipinfo(mpm, chipinfo, sizeof(chipinfo)))
  560. {
  561. fprintf(stderr, "failed to get bootloader version\n");
  562. mpm_close(mboot);
  563. return -1;
  564. }
  565. const char *chipname = chipinfo_get_avr_name(chipinfo);
  566. mpm->flashpage = chipinfo[3];
  567. mpm->flashsize = (chipinfo[4] << 8) + chipinfo[5];
  568. mpm->eepromsize = (chipinfo[6] << 8) + chipinfo[7];
  569. printf("device : %-16s (address: 0x%02X)\n",
  570. mpm->device, mpm->address);
  571. printf("version : %-16s (sig: 0x%02x 0x%02x 0x%02x => %s)\n",
  572. version, chipinfo[0], chipinfo[1], chipinfo[2], chipname);
  573. printf("flash size : 0x%04x / %5d (0x%02x bytes/page)\n",
  574. mpm->flashsize, mpm->flashsize, mpm->flashpage);
  575. printf("eeprom size : 0x%04x / %5d\n",
  576. mpm->eepromsize, mpm->eepromsize);
  577. return 0;
  578. } /* mpm_open */
  579. /* *************************************************************************
  580. * mpm_read
  581. * ************************************************************************* */
  582. static int mpm_read(struct multiboot *mboot,
  583. struct databuf *dbuf, int memtype)
  584. {
  585. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  586. char *progress_msg = (memtype == MEMTYPE_FLASH) ? "reading flash" : "reading eeprom";
  587. uint16_t pos = 0;
  588. uint16_t size = (memtype == MEMTYPE_FLASH) ? mpm->flashsize : mpm->eepromsize;
  589. while (pos < size)
  590. {
  591. mboot->progress_cb(progress_msg, pos, size);
  592. uint16_t len = MIN(READ_BLOCK_SIZE, size - pos);
  593. if (mpm_read_memory(mpm, dbuf->data + pos, len, memtype, pos))
  594. {
  595. mboot->progress_cb(progress_msg, -1, -1);
  596. return -1;
  597. }
  598. pos += len;
  599. }
  600. dbuf->length = pos;
  601. mboot->progress_cb(progress_msg, pos, size);
  602. return 0;
  603. } /* mpm_read */
  604. /* *************************************************************************
  605. * mpm_write
  606. * ************************************************************************* */
  607. static int mpm_write(struct multiboot *mboot,
  608. struct databuf *dbuf, int memtype)
  609. {
  610. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  611. char *progress_msg = (memtype == MEMTYPE_FLASH) ? "writing flash" : "writing eeprom";
  612. uint16_t pos = 0;
  613. while (pos < dbuf->length)
  614. {
  615. mboot->progress_cb(progress_msg, pos, dbuf->length);
  616. uint16_t len = (memtype == MEMTYPE_FLASH) ? mpm->flashpage : WRITE_BLOCK_SIZE;
  617. len = MIN(len, dbuf->length - pos);
  618. if (mpm_write_memory(mpm, dbuf->data + pos, len, memtype, pos))
  619. {
  620. mboot->progress_cb(progress_msg, -1, -1);
  621. return -1;
  622. }
  623. pos += len;
  624. }
  625. mboot->progress_cb(progress_msg, pos, dbuf->length);
  626. return 0;
  627. } /* mpm_write */
  628. /* *************************************************************************
  629. * mpm_verify
  630. * ************************************************************************* */
  631. static int mpm_verify(struct multiboot *mboot,
  632. struct databuf *dbuf, int memtype)
  633. {
  634. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  635. char *progress_msg = (memtype == MEMTYPE_FLASH) ? "verifing flash" : "verifing eeprom";
  636. uint16_t pos = 0;
  637. uint8_t comp[READ_BLOCK_SIZE];
  638. while (pos < dbuf->length)
  639. {
  640. mboot->progress_cb(progress_msg, pos, dbuf->length);
  641. uint16_t len = MIN(READ_BLOCK_SIZE, dbuf->length - pos);
  642. if (mpm_read_memory(mpm, comp, len, memtype, pos))
  643. {
  644. mboot->progress_cb(progress_msg, -1, -1);
  645. return -1;
  646. }
  647. if (memcmp(comp, dbuf->data + pos, len) != 0x00)
  648. {
  649. mboot->progress_cb(progress_msg, -1, -1);
  650. fprintf(stderr, "verify failed at page 0x%04x!!\n", pos);
  651. return -1;
  652. }
  653. pos += len;
  654. }
  655. dbuf->length = pos;
  656. mboot->progress_cb(progress_msg, pos, dbuf->length);
  657. return 0;
  658. } /* mpm_verify */
  659. struct multiboot_ops mpm_ops =
  660. {
  661. .exec_name = "mpmboot",
  662. .alloc = mpm_alloc,
  663. .free = mpm_free,
  664. .get_memtype = mpm_get_memtype,
  665. .get_memsize = mpm_get_memsize,
  666. .open = mpm_open,
  667. .close = mpm_close,
  668. .read = mpm_read,
  669. .write = mpm_write,
  670. .verify = mpm_verify,
  671. };