diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2013-09-05 23:14:57 +0200 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2013-09-05 23:16:06 +0200 |
commit | 3713bc4b7ce68ebedc9d938dbcf38c20c39e049a (patch) | |
tree | 1179a2c30435c813fd9b67f96b058e2d56410fd2 /utils/imxtools/scsitools/scsitool.c | |
parent | 1ac762aebb142fe96a8f204614d1a2d66a05258d (diff) | |
download | rockbox-3713bc4b7ce68ebedc9d938dbcf38c20c39e049a.tar.gz rockbox-3713bc4b7ce68ebedc9d938dbcf38c20c39e049a.zip |
imxtools/scsitools: add option to extract firmware from the device
Change-Id: Ib8cbf2f864a9375575cea0edbba48f740bf8f12e
Diffstat (limited to 'utils/imxtools/scsitools/scsitool.c')
-rw-r--r-- | utils/imxtools/scsitools/scsitool.c | 150 |
1 files changed, 141 insertions, 9 deletions
diff --git a/utils/imxtools/scsitools/scsitool.c b/utils/imxtools/scsitools/scsitool.c index bb68fd882c..af06b7338c 100644 --- a/utils/imxtools/scsitools/scsitool.c +++ b/utils/imxtools/scsitools/scsitool.c | |||
@@ -378,6 +378,28 @@ static int stmp_get_serial_number(uint8_t info, void *data, int *len) | |||
378 | return do_sense_analysis(ret, sense, sense_size); | 378 | return do_sense_analysis(ret, sense, sense_size); |
379 | } | 379 | } |
380 | 380 | ||
381 | static int stmp_read_logical_drive_sectors(uint8_t drive, uint64_t address, | ||
382 | uint32_t count, void *buffer, int *buffer_size) | ||
383 | { | ||
384 | uint8_t cdb[16]; | ||
385 | memset(cdb, 0, sizeof(cdb)); | ||
386 | cdb[0] = SCSI_STMP_READ; | ||
387 | cdb[1] = SCSI_STMP_CMD_READ_LOGICAL_DRIVE_SECTOR; | ||
388 | cdb[2] = drive; | ||
389 | address = fix_endian64be(address); | ||
390 | memcpy(&cdb[3], &address, sizeof(address)); | ||
391 | count = fix_endian32be(count); | ||
392 | memcpy(&cdb[11], &count, sizeof(count)); | ||
393 | |||
394 | uint8_t sense[32]; | ||
395 | int sense_size = sizeof(sense); | ||
396 | |||
397 | int ret = do_scsi(cdb, sizeof(cdb), DO_READ, sense, &sense_size, buffer, buffer_size); | ||
398 | if(ret < 0) | ||
399 | return ret; | ||
400 | return do_sense_analysis(ret, sense, sense_size); | ||
401 | } | ||
402 | |||
381 | static const char *stmp_get_logical_media_type_string(uint32_t type) | 403 | static const char *stmp_get_logical_media_type_string(uint32_t type) |
382 | { | 404 | { |
383 | switch(type) | 405 | switch(type) |
@@ -418,10 +440,10 @@ static const char *stmp_get_logical_drive_type_string(uint32_t type) | |||
418 | } | 440 | } |
419 | } | 441 | } |
420 | 442 | ||
421 | static int do_work(void) | 443 | static int do_info(void) |
422 | { | 444 | { |
423 | cprintf(BLUE, "Information\n"); | 445 | cprintf(BLUE, "Information\n"); |
424 | 446 | ||
425 | uint8_t dev_type; | 447 | uint8_t dev_type; |
426 | char vendor[9]; | 448 | char vendor[9]; |
427 | char product[17]; | 449 | char product[17]; |
@@ -590,7 +612,7 @@ static int do_work(void) | |||
590 | { | 612 | { |
591 | struct scsi_stmp_logical_table_t header; | 613 | struct scsi_stmp_logical_table_t header; |
592 | struct scsi_stmp_logical_table_entry_t entry[20]; | 614 | struct scsi_stmp_logical_table_entry_t entry[20]; |
593 | }table; | 615 | }__attribute__((packed)) table; |
594 | 616 | ||
595 | ret = stmp_get_logical_table(&table.header, sizeof(table.entry) / sizeof(table.entry[0])); | 617 | ret = stmp_get_logical_table(&table.header, sizeof(table.entry) / sizeof(table.entry[0])); |
596 | if(ret) | 618 | if(ret) |
@@ -734,19 +756,118 @@ static int do_work(void) | |||
734 | return 0; | 756 | return 0; |
735 | } | 757 | } |
736 | 758 | ||
759 | void do_extract(const char *file) | ||
760 | { | ||
761 | FILE *f = NULL; | ||
762 | cprintf(BLUE, "Extracting firmware...\n"); | ||
763 | |||
764 | struct | ||
765 | { | ||
766 | struct scsi_stmp_logical_table_t header; | ||
767 | struct scsi_stmp_logical_table_entry_t entry[20]; | ||
768 | }__attribute__((packed)) table; | ||
769 | |||
770 | int ret = stmp_get_logical_table(&table.header, sizeof(table.entry) / sizeof(table.entry[0])); | ||
771 | if(ret) | ||
772 | { | ||
773 | cprintf(GREY, "Cannot get logical table: %d\n", ret); | ||
774 | goto Lend; | ||
775 | } | ||
776 | int entry = 0; | ||
777 | while(entry < table.header.count) | ||
778 | if(table.entry[entry].type == SCSI_STMP_DRIVE_TYPE_SYSTEM && | ||
779 | table.entry[entry].tag == SCSI_STMP_DRIVE_TAG_SYSTEM_BOOT) | ||
780 | break; | ||
781 | else | ||
782 | entry++; | ||
783 | if(entry == table.header.count) | ||
784 | { | ||
785 | cprintf(GREY, "Cannot find firmware partition\n"); | ||
786 | goto Lend; | ||
787 | } | ||
788 | uint8_t drive_no = table.entry[entry].drive_no; | ||
789 | uint64_t drive_sz = table.entry[entry].size; | ||
790 | if(g_debug) | ||
791 | { | ||
792 | cprintf(RED, "* "); | ||
793 | cprintf_field("Drive: ", "%#x\n", drive_no); | ||
794 | cprintf(RED, "* "); | ||
795 | cprintf_field("Size: ", "%#llx\n", (unsigned long long)drive_sz); | ||
796 | } | ||
797 | int len = 4; | ||
798 | uint32_t sector_size; | ||
799 | ret = stmp_get_logical_drive_info(drive_no, SCSI_STMP_DRIVE_INFO_SECTOR, §or_size, &len); | ||
800 | if(ret || len != 4) | ||
801 | { | ||
802 | cprintf(GREY, "Cannot get sector size\n"); | ||
803 | goto Lend; | ||
804 | } | ||
805 | sector_size = fix_endian32be(sector_size); | ||
806 | if(g_debug) | ||
807 | { | ||
808 | cprintf(RED, "* "); | ||
809 | cprintf_field("Sector size: ", "%lu\n", (unsigned long)sector_size); | ||
810 | } | ||
811 | uint8_t *sector = malloc(sector_size); | ||
812 | len = sector_size; | ||
813 | ret = stmp_read_logical_drive_sectors(drive_no, 0, 1, sector, &len); | ||
814 | if(ret || len != (int)sector_size) | ||
815 | { | ||
816 | cprintf(GREY, "Cannot read first sector\n"); | ||
817 | } | ||
818 | uint32_t fw_size = *(uint32_t *)(sector + 0x1c) * 16; | ||
819 | if(g_debug) | ||
820 | { | ||
821 | cprintf(RED, "* "); | ||
822 | cprintf_field("Firmware size: ", "%#x\n", fw_size); | ||
823 | } | ||
824 | |||
825 | f = fopen(file, "wb"); | ||
826 | if(f == NULL) | ||
827 | { | ||
828 | cprintf(GREY, "Cannot open '%s' for writing: %m\n", file); | ||
829 | goto Lend; | ||
830 | } | ||
831 | |||
832 | for(int sec = 0; sec * sector_size < fw_size; sec++) | ||
833 | { | ||
834 | ret = stmp_read_logical_drive_sectors(drive_no, sec, 1, sector, &len); | ||
835 | if(ret || len != (int)sector_size) | ||
836 | { | ||
837 | cprintf(GREY, "Cannot read sector %d\n", sec); | ||
838 | goto Lend; | ||
839 | } | ||
840 | if(fwrite(sector, sector_size, 1, f) != 1) | ||
841 | { | ||
842 | cprintf(GREY, "Write failed: %m\n"); | ||
843 | goto Lend; | ||
844 | } | ||
845 | } | ||
846 | cprintf(BLUE, "Done\n"); | ||
847 | Lend: | ||
848 | if(f) | ||
849 | fclose(f); | ||
850 | } | ||
851 | |||
737 | static void usage(void) | 852 | static void usage(void) |
738 | { | 853 | { |
739 | printf("Usage: scsitool [options] <dev>\n"); | 854 | printf("Usage: scsitool [options] <dev>\n"); |
740 | printf("Options:\n"); | 855 | printf("Options:\n"); |
741 | printf(" -f/--force\tForce to continue on errors\n"); | 856 | printf(" -f/--force Force to continue on errors\n"); |
742 | printf(" -?/--help\tDisplay this message\n"); | 857 | printf(" -?/--help Display this message\n"); |
743 | printf(" -d/--debug\tDisplay debug messages\n"); | 858 | printf(" -d/--debug Display debug messages\n"); |
744 | printf(" -c/--no-color\tDisable color output\n"); | 859 | printf(" -c/--no-color Disable color output\n"); |
860 | printf(" -x/--extract-fw <file> Extract firmware to file\n"); | ||
861 | printf(" -i/--info Display device information\n"); | ||
745 | exit(1); | 862 | exit(1); |
746 | } | 863 | } |
747 | 864 | ||
748 | int main(int argc, char **argv) | 865 | int main(int argc, char **argv) |
749 | { | 866 | { |
867 | if(argc == 1) | ||
868 | usage(); | ||
869 | const char *extract_fw = NULL; | ||
870 | bool info = false; | ||
750 | while(1) | 871 | while(1) |
751 | { | 872 | { |
752 | static struct option long_options[] = | 873 | static struct option long_options[] = |
@@ -755,10 +876,12 @@ int main(int argc, char **argv) | |||
755 | {"debug", no_argument, 0, 'd'}, | 876 | {"debug", no_argument, 0, 'd'}, |
756 | {"no-color", no_argument, 0, 'c'}, | 877 | {"no-color", no_argument, 0, 'c'}, |
757 | {"force", no_argument, 0, 'f'}, | 878 | {"force", no_argument, 0, 'f'}, |
879 | {"extract-fw", required_argument, 0, 'x'}, | ||
880 | {"info", no_argument, 0, 'i'}, | ||
758 | {0, 0, 0, 0} | 881 | {0, 0, 0, 0} |
759 | }; | 882 | }; |
760 | 883 | ||
761 | int c = getopt_long(argc, argv, "?dcf", long_options, NULL); | 884 | int c = getopt_long(argc, argv, "?dcfx:i", long_options, NULL); |
762 | if(c == -1) | 885 | if(c == -1) |
763 | break; | 886 | break; |
764 | switch(c) | 887 | switch(c) |
@@ -777,6 +900,12 @@ int main(int argc, char **argv) | |||
777 | case '?': | 900 | case '?': |
778 | usage(); | 901 | usage(); |
779 | break; | 902 | break; |
903 | case 'x': | ||
904 | extract_fw = optarg; | ||
905 | break; | ||
906 | case 'i': | ||
907 | info = true; | ||
908 | break; | ||
780 | default: | 909 | default: |
781 | abort(); | 910 | abort(); |
782 | } | 911 | } |
@@ -797,7 +926,10 @@ int main(int argc, char **argv) | |||
797 | goto Lend; | 926 | goto Lend; |
798 | } | 927 | } |
799 | 928 | ||
800 | do_work(); | 929 | if(extract_fw) |
930 | do_extract(extract_fw); | ||
931 | if(info) | ||
932 | do_info(); | ||
801 | 933 | ||
802 | scsi_pt_close_device(g_dev_fd); | 934 | scsi_pt_close_device(g_dev_fd); |
803 | Lend: | 935 | Lend: |