diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2013-12-24 01:09:33 +0100 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2013-12-24 12:39:58 +0100 |
commit | 84f1a0af3b62c3c10d838fe387035a8145c35ca3 (patch) | |
tree | 82a6db61bfab6ff25f26666f1956ab9dcd584884 /utils/imxtools | |
parent | e0636afed2cfc64df77225607b21a0e6be12c48e (diff) | |
download | rockbox-84f1a0af3b62c3c10d838fe387035a8145c35ca3.tar.gz rockbox-84f1a0af3b62c3c10d838fe387035a8145c35ca3.zip |
imxtools/scsitools: tool can now write a firmware using SCSI
Change-Id: Iac803d4b2d48319a1a32d1101f5041d20174c7a1
Diffstat (limited to 'utils/imxtools')
-rw-r--r-- | utils/imxtools/scsitools/scsitool.c | 163 |
1 files changed, 162 insertions, 1 deletions
diff --git a/utils/imxtools/scsitools/scsitool.c b/utils/imxtools/scsitools/scsitool.c index d8d47f8b48..2c82d5b841 100644 --- a/utils/imxtools/scsitools/scsitool.c +++ b/utils/imxtools/scsitools/scsitool.c | |||
@@ -400,6 +400,28 @@ static int stmp_read_logical_drive_sectors(uint8_t drive, uint64_t address, | |||
400 | return do_sense_analysis(ret, sense, sense_size); | 400 | return do_sense_analysis(ret, sense, sense_size); |
401 | } | 401 | } |
402 | 402 | ||
403 | static int stmp_write_logical_drive_sectors(uint8_t drive, uint64_t address, | ||
404 | uint32_t count, void *buffer, int *buffer_size) | ||
405 | { | ||
406 | uint8_t cdb[16]; | ||
407 | memset(cdb, 0, sizeof(cdb)); | ||
408 | cdb[0] = SCSI_STMP_WRITE; | ||
409 | cdb[1] = SCSI_STMP_CMD_WRITE_LOGICAL_DRIVE_SECTOR; | ||
410 | cdb[2] = drive; | ||
411 | address = fix_endian64be(address); | ||
412 | memcpy(&cdb[3], &address, sizeof(address)); | ||
413 | count = fix_endian32be(count); | ||
414 | memcpy(&cdb[11], &count, sizeof(count)); | ||
415 | |||
416 | uint8_t sense[32]; | ||
417 | int sense_size = sizeof(sense); | ||
418 | |||
419 | int ret = do_scsi(cdb, sizeof(cdb), DO_WRITE, sense, &sense_size, buffer, buffer_size); | ||
420 | if(ret < 0) | ||
421 | return ret; | ||
422 | return do_sense_analysis(ret, sense, sense_size); | ||
423 | } | ||
424 | |||
403 | static const char *stmp_get_logical_media_type_string(uint32_t type) | 425 | static const char *stmp_get_logical_media_type_string(uint32_t type) |
404 | { | 426 | { |
405 | switch(type) | 427 | switch(type) |
@@ -912,6 +934,7 @@ void do_extract(const char *file) | |||
912 | if(ret || len != (int)sector_size) | 934 | if(ret || len != (int)sector_size) |
913 | { | 935 | { |
914 | cprintf(GREY, "Cannot read first sector\n"); | 936 | cprintf(GREY, "Cannot read first sector\n"); |
937 | return; | ||
915 | } | 938 | } |
916 | uint32_t fw_size = *(uint32_t *)(sector + 0x1c) * 16; | 939 | uint32_t fw_size = *(uint32_t *)(sector + 0x1c) * 16; |
917 | if(g_debug) | 940 | if(g_debug) |
@@ -947,6 +970,130 @@ Lend: | |||
947 | fclose(f); | 970 | fclose(f); |
948 | } | 971 | } |
949 | 972 | ||
973 | void do_write(const char *file, int want_a_brick) | ||
974 | { | ||
975 | if(!want_a_brick) | ||
976 | { | ||
977 | cprintf(GREY, "Writing a new firmware is a dangerous operation that should be attempted\n"); | ||
978 | cprintf(GREY, "if you know what you are doing. If you do, please add the --yes-i-want-a-brick\n"); | ||
979 | cprintf(GREY, "option on the command line and do not complain if you end up with a brick ;)\n"); | ||
980 | return; | ||
981 | } | ||
982 | FILE *f = NULL; | ||
983 | cprintf(BLUE, "Writing firmware...\n"); | ||
984 | |||
985 | struct | ||
986 | { | ||
987 | struct scsi_stmp_logical_table_t header; | ||
988 | struct scsi_stmp_logical_table_entry_t entry[20]; | ||
989 | }__attribute__((packed)) table; | ||
990 | |||
991 | int ret = stmp_get_logical_table(&table.header, sizeof(table.entry) / sizeof(table.entry[0])); | ||
992 | if(ret) | ||
993 | { | ||
994 | cprintf(GREY, "Cannot get logical table: %d\n", ret); | ||
995 | goto Lend; | ||
996 | } | ||
997 | int entry = 0; | ||
998 | while(entry < table.header.count) | ||
999 | if(table.entry[entry].type == SCSI_STMP_DRIVE_TYPE_SYSTEM && | ||
1000 | table.entry[entry].tag == SCSI_STMP_DRIVE_TAG_SYSTEM_BOOT) | ||
1001 | break; | ||
1002 | else | ||
1003 | entry++; | ||
1004 | if(entry == table.header.count) | ||
1005 | { | ||
1006 | cprintf(GREY, "Cannot find firmware partition\n"); | ||
1007 | goto Lend; | ||
1008 | } | ||
1009 | uint8_t drive_no = table.entry[entry].drive_no; | ||
1010 | uint64_t drive_sz = table.entry[entry].size; | ||
1011 | if(g_debug) | ||
1012 | { | ||
1013 | cprintf(RED, "* "); | ||
1014 | cprintf_field("Drive: ", "%#x\n", drive_no); | ||
1015 | cprintf(RED, "* "); | ||
1016 | cprintf_field("Size: ", "%#llx\n", (unsigned long long)drive_sz); | ||
1017 | } | ||
1018 | int len = 4; | ||
1019 | uint32_t sector_size; | ||
1020 | ret = stmp_get_logical_drive_info(drive_no, SCSI_STMP_DRIVE_INFO_SECTOR_SIZE, §or_size, &len); | ||
1021 | if(ret || len != 4) | ||
1022 | { | ||
1023 | cprintf(GREY, "Cannot get sector size\n"); | ||
1024 | goto Lend; | ||
1025 | } | ||
1026 | sector_size = fix_endian32be(sector_size); | ||
1027 | if(g_debug) | ||
1028 | { | ||
1029 | cprintf(RED, "* "); | ||
1030 | cprintf_field("Sector size: ", "%lu\n", (unsigned long)sector_size); | ||
1031 | } | ||
1032 | uint8_t *sector = malloc(sector_size); | ||
1033 | |||
1034 | /* sanity check by reading first sector */ | ||
1035 | len = sector_size; | ||
1036 | ret = stmp_read_logical_drive_sectors(drive_no, 0, 1, sector, &len); | ||
1037 | if(ret || len != (int)sector_size) | ||
1038 | { | ||
1039 | cprintf(GREY, "Cannot read first sector\n"); | ||
1040 | return; | ||
1041 | } | ||
1042 | uint32_t sig = *(uint32_t *)(sector + 0x14); | ||
1043 | if(sig != 0x504d5453) | ||
1044 | { | ||
1045 | cprintf(GREY, "There is something wrong: the first sector doesn't have the STMP signature. Bailing out...\n"); | ||
1046 | return; | ||
1047 | } | ||
1048 | |||
1049 | f = fopen(file, "rb"); | ||
1050 | if(f == NULL) | ||
1051 | { | ||
1052 | cprintf(GREY, "Cannot open '%s' for writing: %m\n", file); | ||
1053 | goto Lend; | ||
1054 | } | ||
1055 | fseek(f, 0, SEEK_END); | ||
1056 | int fw_size = ftell(f); | ||
1057 | fseek(f, 0, SEEK_SET); | ||
1058 | if(g_debug) | ||
1059 | { | ||
1060 | cprintf(RED, "* "); | ||
1061 | cprintf_field("Firmware size: ", "%#x\n", fw_size); | ||
1062 | } | ||
1063 | /* sanity check size */ | ||
1064 | if((uint64_t)fw_size > drive_sz) | ||
1065 | { | ||
1066 | cprintf(GREY, "You cannot write a firmware greater than the partition size.\n"); | ||
1067 | goto Lend; | ||
1068 | } | ||
1069 | |||
1070 | for(int off = 0; off < fw_size; off += sector_size) | ||
1071 | { | ||
1072 | int sec = off / sector_size; | ||
1073 | int xfer_len = MIN(fw_size - off, (int)sector_size); | ||
1074 | if(fread(sector, xfer_len, 1, f) != 1) | ||
1075 | { | ||
1076 | cprintf(GREY, "Read failed: %m\n"); | ||
1077 | goto Lend; | ||
1078 | } | ||
1079 | /* NOTE transfer a whole sector even if incomplete, the device won't access | ||
1080 | * partial sectors */ | ||
1081 | if(xfer_len < (int)sector_size) | ||
1082 | memset(sector + xfer_len, 0, sector_size - xfer_len); | ||
1083 | len = sector_size; | ||
1084 | ret = stmp_write_logical_drive_sectors(drive_no, sec, 1, sector, &len); | ||
1085 | if(ret || len != (int)sector_size) | ||
1086 | { | ||
1087 | cprintf(GREY, "Cannot write sector %d\n", sec); | ||
1088 | goto Lend; | ||
1089 | } | ||
1090 | } | ||
1091 | cprintf(BLUE, "Done\n"); | ||
1092 | Lend: | ||
1093 | if(f) | ||
1094 | fclose(f); | ||
1095 | } | ||
1096 | |||
950 | static void usage(void) | 1097 | static void usage(void) |
951 | { | 1098 | { |
952 | printf("Usage: scsitool [options] <dev>\n"); | 1099 | printf("Usage: scsitool [options] <dev>\n"); |
@@ -956,15 +1103,20 @@ static void usage(void) | |||
956 | printf(" -d/--debug Display debug messages\n"); | 1103 | printf(" -d/--debug Display debug messages\n"); |
957 | printf(" -c/--no-color Disable color output\n"); | 1104 | printf(" -c/--no-color Disable color output\n"); |
958 | printf(" -x/--extract-fw <file> Extract firmware to file\n"); | 1105 | printf(" -x/--extract-fw <file> Extract firmware to file\n"); |
1106 | printf(" -w/--write-fw <file> Write firmware to device\n"); | ||
959 | printf(" -i/--info Display device information\n"); | 1107 | printf(" -i/--info Display device information\n"); |
1108 | printf(" --yes-i-want-a-brick Allow the tool to turn your device into a brick\n"); | ||
960 | exit(1); | 1109 | exit(1); |
961 | } | 1110 | } |
962 | 1111 | ||
1112 | static int g_yes_i_want_a_brick = 0; | ||
1113 | |||
963 | int main(int argc, char **argv) | 1114 | int main(int argc, char **argv) |
964 | { | 1115 | { |
965 | if(argc == 1) | 1116 | if(argc == 1) |
966 | usage(); | 1117 | usage(); |
967 | const char *extract_fw = NULL; | 1118 | const char *extract_fw = NULL; |
1119 | const char *write_fw = NULL; | ||
968 | bool info = false; | 1120 | bool info = false; |
969 | while(1) | 1121 | while(1) |
970 | { | 1122 | { |
@@ -975,15 +1127,19 @@ int main(int argc, char **argv) | |||
975 | {"no-color", no_argument, 0, 'c'}, | 1127 | {"no-color", no_argument, 0, 'c'}, |
976 | {"force", no_argument, 0, 'f'}, | 1128 | {"force", no_argument, 0, 'f'}, |
977 | {"extract-fw", required_argument, 0, 'x'}, | 1129 | {"extract-fw", required_argument, 0, 'x'}, |
1130 | {"write-fw", required_argument, 0, 'w'}, | ||
978 | {"info", no_argument, 0, 'i'}, | 1131 | {"info", no_argument, 0, 'i'}, |
1132 | {"yes-i-want-a-brick", no_argument, &g_yes_i_want_a_brick, 1}, | ||
979 | {0, 0, 0, 0} | 1133 | {0, 0, 0, 0} |
980 | }; | 1134 | }; |
981 | 1135 | ||
982 | int c = getopt_long(argc, argv, "?dcfx:i", long_options, NULL); | 1136 | int c = getopt_long(argc, argv, "?dcfx:iw:", long_options, NULL); |
983 | if(c == -1) | 1137 | if(c == -1) |
984 | break; | 1138 | break; |
985 | switch(c) | 1139 | switch(c) |
986 | { | 1140 | { |
1141 | case 0: | ||
1142 | continue; | ||
987 | case -1: | 1143 | case -1: |
988 | break; | 1144 | break; |
989 | case 'c': | 1145 | case 'c': |
@@ -1001,6 +1157,9 @@ int main(int argc, char **argv) | |||
1001 | case 'x': | 1157 | case 'x': |
1002 | extract_fw = optarg; | 1158 | extract_fw = optarg; |
1003 | break; | 1159 | break; |
1160 | case 'w': | ||
1161 | write_fw = optarg; | ||
1162 | break; | ||
1004 | case 'i': | 1163 | case 'i': |
1005 | info = true; | 1164 | info = true; |
1006 | break; | 1165 | break; |
@@ -1028,6 +1187,8 @@ int main(int argc, char **argv) | |||
1028 | do_extract(extract_fw); | 1187 | do_extract(extract_fw); |
1029 | if(info) | 1188 | if(info) |
1030 | do_info(); | 1189 | do_info(); |
1190 | if(write_fw) | ||
1191 | do_write(write_fw, g_yes_i_want_a_brick); | ||
1031 | 1192 | ||
1032 | scsi_pt_close_device(g_dev_fd); | 1193 | scsi_pt_close_device(g_dev_fd); |
1033 | Lend: | 1194 | Lend: |