diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2020-05-27 23:38:41 +0200 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2020-05-27 23:39:06 +0200 |
commit | fba4df5d6d4317e02f1baf25b54c2c051eec5d1d (patch) | |
tree | 867329c156fa265bf0a5d1ad30ce56a51b0c5cff /utils | |
parent | 3bdb96aa02bee737278a2bd6f302cc10c8c1af30 (diff) | |
download | rockbox-fba4df5d6d4317e02f1baf25b54c2c051eec5d1d.tar.gz rockbox-fba4df5d6d4317e02f1baf25b54c2c051eec5d1d.zip |
nwztools/scsitool: add command to write NVP
Change-Id: I4a7a9688af1abb14de82013703f3a02b4644d5ed
Diffstat (limited to 'utils')
-rw-r--r-- | utils/nwztools/scsitools/scsitool.c | 202 |
1 files changed, 190 insertions, 12 deletions
diff --git a/utils/nwztools/scsitools/scsitool.c b/utils/nwztools/scsitools/scsitool.c index 7c18a46daa..c4834b709a 100644 --- a/utils/nwztools/scsitools/scsitool.c +++ b/utils/nwztools/scsitools/scsitool.c | |||
@@ -40,6 +40,7 @@ bool g_debug = false; | |||
40 | const char *g_force_series = NULL; | 40 | const char *g_force_series = NULL; |
41 | char *g_out_prefix = NULL; | 41 | char *g_out_prefix = NULL; |
42 | rb_scsi_device_t g_dev; | 42 | rb_scsi_device_t g_dev; |
43 | bool g_yes_i_want_a_brick = false; | ||
43 | 44 | ||
44 | void help_us(bool unsupported, unsigned long model_id); | 45 | void help_us(bool unsupported, unsigned long model_id); |
45 | 46 | ||
@@ -109,7 +110,7 @@ int do_sense_analysis(int status, uint8_t *sense, int sense_size) | |||
109 | } | 110 | } |
110 | 111 | ||
111 | /* | 112 | /* |
112 | * SCSI commands | 113 | * SCSI commands (see decode_scsi for more details) |
113 | */ | 114 | */ |
114 | #define CMD_A3 0xa3 /* start a complicated, authenticated, session to do things */ | 115 | #define CMD_A3 0xa3 /* start a complicated, authenticated, session to do things */ |
115 | #define CMD_A4 0xa4 /* start a complicated, authenticated, session to do things */ | 116 | #define CMD_A4 0xa4 /* start a complicated, authenticated, session to do things */ |
@@ -167,7 +168,7 @@ struct dnk_prop_t dnk_prop_list[] = | |||
167 | { "model_id", "Model ID", 0x23, 9, 4, DNK_EXACT_LENGTH | DNK_UINT32 | DNK_HEX}, | 168 | { "model_id", "Model ID", 0x23, 9, 4, DNK_EXACT_LENGTH | DNK_UINT32 | DNK_HEX}, |
168 | { "ufn", "Update filename", 0x23, 21, 8, DNK_STRING}, | 169 | { "ufn", "Update filename", 0x23, 21, 8, DNK_STRING}, |
169 | { "kas", "Key and Signature", 0x23, 22, 60, DNK_STRING}, | 170 | { "kas", "Key and Signature", 0x23, 22, 60, DNK_STRING}, |
170 | { "model_name", "Model Name", 0x12, 0, 64, DNK_STRING}, | 171 | { "devinfo", "Devide Info", 0x12, 0, 64, DNK_HEX}, |
171 | /* there are more obscure commands: | 172 | /* there are more obscure commands: |
172 | * - 0x11 returns a 10-byte packet containing a 8-byte "LeftIdl8", scrambled | 173 | * - 0x11 returns a 10-byte packet containing a 8-byte "LeftIdl8", scrambled |
173 | * with para_noise (the 2-byte padding is random so that output is random | 174 | * with para_noise (the 2-byte padding is random so that output is random |
@@ -180,7 +181,11 @@ struct dnk_prop_t dnk_prop_list[] = | |||
180 | * - 10 is nvp properties (see get_dnk_nvp) (NOTE: nvp number vary by model here) | 181 | * - 10 is nvp properties (see get_dnk_nvp) (NOTE: nvp number vary by model here) |
181 | * - 11 seems to read something from nvp and encrypt it with AES, not sure what | 182 | * - 11 seems to read something from nvp and encrypt it with AES, not sure what |
182 | * - many other read important/canonical entries of NVP (number does NOT vary model) | 183 | * - many other read important/canonical entries of NVP (number does NOT vary model) |
183 | * - 0x24 can write the same properties read by 0x23 */ | 184 | * - 0x24 can write the same properties read by 0x23 |
185 | * | ||
186 | * This website has some background on Sony's acronym (DNK, EKB, ...): | ||
187 | * https://wiki.physik.fu-berlin.de/linux-minidisc/doku.php?id=atracdownload-wiki | ||
188 | */ | ||
184 | }; | 189 | }; |
185 | 190 | ||
186 | #define NR_DNK_PROPS (sizeof(dnk_prop_list) / sizeof(dnk_prop_list[0])) | 191 | #define NR_DNK_PROPS (sizeof(dnk_prop_list) / sizeof(dnk_prop_list[0])) |
@@ -554,6 +559,91 @@ int get_dnk_nvp_multi(int argc, char **argv) | |||
554 | return 0; | 559 | return 0; |
555 | } | 560 | } |
556 | 561 | ||
562 | int set_dnk_nvp(int argc, char **argv) | ||
563 | { | ||
564 | if(argc <= 1) | ||
565 | { | ||
566 | printf("NOTE: this command is potentially very dangerous!\n"); | ||
567 | printf("\n"); | ||
568 | printf("You must specify a known nvp node or a full node specification:\n"); | ||
569 | printf("Usage: --yes-I-want-a-brick <node> <content>\n"); | ||
570 | printf("Content must be a list of byte, in decimal or hexadecimal format, e.g. 10 0x30\n"); | ||
571 | printf("Nodes:\n"); | ||
572 | for(unsigned i = 0; i < NWZ_NVP_COUNT; i++) | ||
573 | printf(" %-6s%s\n", nwz_nvp[i].name, nwz_nvp[i].desc); | ||
574 | printf("You can also specify a decimal or hexadecimal value directly\n"); | ||
575 | return 1; | ||
576 | } | ||
577 | int series_index, model_index; | ||
578 | int ret = get_model_and_series(&model_index, &series_index, NULL); | ||
579 | if(ret) | ||
580 | return ret; | ||
581 | if(!g_yes_i_want_a_brick) | ||
582 | { | ||
583 | cprintf(RED, "You must pass the option --yes-I-want-a-brick to show that you understand the risk\n"); | ||
584 | return 1; | ||
585 | } | ||
586 | /* find entry in NVP */ | ||
587 | const char *node_name = argv[0]; | ||
588 | const char *node_desc = NULL; | ||
589 | int node_index = NWZ_NVP_INVALID; | ||
590 | for(int i = 0; i < NWZ_NVP_COUNT; i++) | ||
591 | if(strcmp(nwz_nvp[i].name, node_name) == 0) | ||
592 | { | ||
593 | if(nwz_series[series_index].nvp_index) | ||
594 | node_index = (*nwz_series[series_index].nvp_index)[i]; | ||
595 | if(node_index == NWZ_NVP_INVALID) | ||
596 | { | ||
597 | cprintf(RED, "This device doesn't have node '%s'\n", node_name); | ||
598 | return 5; | ||
599 | } | ||
600 | node_desc = nwz_nvp[i].desc; | ||
601 | } | ||
602 | /* if we can't find it, maybe check if it's a number */ | ||
603 | if(node_index == NWZ_NVP_INVALID) | ||
604 | { | ||
605 | char *end; | ||
606 | node_index = strtol(node_name, &end, 0); | ||
607 | if(*end) | ||
608 | node_index = NWZ_NVP_INVALID; /* string is not a number */ | ||
609 | } | ||
610 | if(node_index == NWZ_NVP_INVALID) | ||
611 | { | ||
612 | cprintf(RED, "I don't know about node '%s'\n", node_name); | ||
613 | return 4; | ||
614 | } | ||
615 | /* build buffer */ | ||
616 | int size = argc - 1; | ||
617 | uint8_t *buffer = malloc(size); | ||
618 | for(int i = 0; i < size; i++) | ||
619 | { | ||
620 | char *end; | ||
621 | long val = strtol(argv[1 + i], &end, 0); | ||
622 | if(val < 0 || val >= 256) | ||
623 | { | ||
624 | cprintf(RED, "value '%s' does not fit into a byte\n", argv[i + 1]); | ||
625 | return 1; | ||
626 | } | ||
627 | buffer[i] = val; | ||
628 | } | ||
629 | if(g_debug) | ||
630 | { | ||
631 | cprintf(GREY, "Sending device the following buffer:\n"); | ||
632 | print_hex(buffer, size); | ||
633 | } | ||
634 | ret = write_nvp_node(node_index, buffer, size); | ||
635 | if(ret != 0) | ||
636 | { | ||
637 | cprintf(GREY, "An error occured during request\n"); | ||
638 | free(buffer); | ||
639 | return ret; | ||
640 | } | ||
641 | cprintf(GREEN, "Wrote %d bytes to %s (node %d%s%s):\n", size, node_name, node_index, | ||
642 | node_desc ? "," : "", node_desc ? node_desc : ""); | ||
643 | free(buffer); | ||
644 | return 0; | ||
645 | } | ||
646 | |||
557 | struct dpcc_devinfo_t | 647 | struct dpcc_devinfo_t |
558 | { | 648 | { |
559 | uint8_t vendor_identification[8]; | 649 | uint8_t vendor_identification[8]; |
@@ -964,6 +1054,7 @@ struct cmd_t cmd_list[] = | |||
964 | { | 1054 | { |
965 | { "get_dnk_prop", "Get DNK property", get_dnk_prop }, | 1055 | { "get_dnk_prop", "Get DNK property", get_dnk_prop }, |
966 | { "get_dnk_nvp", "Get DNK NVP content", get_dnk_nvp }, | 1056 | { "get_dnk_nvp", "Get DNK NVP content", get_dnk_nvp }, |
1057 | { "set_dnk_nvp", "Set DNK NVP content", set_dnk_nvp}, | ||
967 | { "get_dnk_nvp_multi", "Get several DNK NVP content", get_dnk_nvp_multi }, | 1058 | { "get_dnk_nvp_multi", "Get several DNK NVP content", get_dnk_nvp_multi }, |
968 | { "get_dpcc_prop", "Get DPCC property", get_dpcc_prop }, | 1059 | { "get_dpcc_prop", "Get DPCC property", get_dpcc_prop }, |
969 | { "get_user_time", "Get user time", get_user_time }, | 1060 | { "get_user_time", "Get user time", get_user_time }, |
@@ -1082,42 +1173,125 @@ inline uint8_t xdigit2val(char c) | |||
1082 | 1173 | ||
1083 | static int decode_scsi_a3(uint8_t *cdb, int cdb_len) | 1174 | static int decode_scsi_a3(uint8_t *cdb, int cdb_len) |
1084 | { | 1175 | { |
1085 | cprintf_field("Opcode: ", "A3\n"); | 1176 | cprintf_field("Opcode: ", "0xa3\n"); |
1086 | cprintf(RED, "Unimplemented\n"); | 1177 | cprintf(RED, "Unimplemented\n"); |
1087 | return 0; | 1178 | return 0; |
1088 | } | 1179 | } |
1089 | 1180 | ||
1090 | static int decode_scsi_a4(uint8_t *cdb, int cdb_len) | 1181 | static int decode_scsi_a4(uint8_t *cdb, int cdb_len) |
1091 | { | 1182 | { |
1092 | cprintf_field("Opcode: ", "A3\n"); | 1183 | cprintf_field("Opcode: ", "0xa4\n"); |
1093 | cprintf(RED, "Unimplemented\n"); | 1184 | cprintf(RED, "Unimplemented\n"); |
1094 | return 0; | 1185 | return 0; |
1095 | } | 1186 | } |
1096 | 1187 | ||
1097 | static int decode_scsi_empr_dpcc(uint8_t *cdb, int cdb_len) | 1188 | static int decode_scsi_empr_dpcc(uint8_t *cdb, int cdb_len) |
1098 | { | 1189 | { |
1099 | cprintf_field("Opcode: ", "%X (EMPR DPCC)\n", cdb[0]); | 1190 | cprintf_field("Opcode: ", "%#x (EMPR DPCC)\n", cdb[0]); |
1100 | cprintf(RED, "Unimplemented\n"); | 1191 | cprintf(RED, "Unimplemented\n"); |
1101 | return 0; | 1192 | return 0; |
1102 | } | 1193 | } |
1103 | 1194 | ||
1104 | static int decode_scsi_dnk(uint8_t *cdb, int cdb_len) | 1195 | static int decode_scsi_dnk(uint8_t *cdb, int cdb_len) |
1105 | { | 1196 | { |
1106 | cprintf_field("Opcode: ", "%X (DNK)\n", cdb[0]); | 1197 | /* |
1107 | cprintf(RED, "Unimplemented\n"); | 1198 | * DNK (Device Node Key) commands are 12 bytes long and the CDB is of the form |
1199 | * DD 00 00 00 xx xx 00 BC ll ll xx yy | ||
1200 | * where | ||
1201 | * - xxxx is the argument (16 bits) | ||
1202 | * - llll is the length (16 bits) | ||
1203 | * - xx is the command | ||
1204 | * - yy is the subcommand | ||
1205 | */ | ||
1206 | cprintf_field("Opcode: ", "%#x (DNK)\n", cdb[0]); | ||
1207 | if(cdb_len != 12 || cdb[7] != 0xbc) | ||
1208 | { | ||
1209 | cprintf(GREY, "Invalid length for a DNK command\n"); | ||
1210 | return 1; | ||
1211 | } | ||
1212 | uint8_t cmd = cdb[10]; | ||
1213 | uint8_t subcmd = cdb[11]; | ||
1214 | uint16_t length = cdb[8] << 8 | cdb[9]; | ||
1215 | uint16_t arg = cdb[4] << 8 | cdb[5]; | ||
1216 | |||
1217 | const char *cmd_str = "Unknown"; | ||
1218 | const char *subcmd_str = "Unknown"; | ||
1219 | switch(cmd) | ||
1220 | { | ||
1221 | case 0x11: | ||
1222 | cmd_str = "Read Leaf ID"; | ||
1223 | subcmd_str = "Unused"; | ||
1224 | break; | ||
1225 | case 0x12: | ||
1226 | cmd_str = "Get DEVINFO"; | ||
1227 | subcmd_str = "Unused"; | ||
1228 | break; | ||
1229 | case 0x21: | ||
1230 | cmd_str = "Report DNK"; | ||
1231 | subcmd_str = "Unused"; | ||
1232 | break; | ||
1233 | case 0x23: | ||
1234 | cmd_str = "Read Regs"; | ||
1235 | switch(subcmd) | ||
1236 | { | ||
1237 | case 1: subcmd_str = "Serial Num"; break; | ||
1238 | case 4: subcmd_str = "Storage Size"; break; | ||
1239 | case 5: subcmd_str = "DKS"; break; | ||
1240 | case 6: subcmd_str = "Product ID"; break; | ||
1241 | case 7: subcmd_str = "Product Group Scramble"; break; | ||
1242 | case 8: subcmd_str = "Destination"; break; | ||
1243 | case 9: subcmd_str = "Model ID"; break; | ||
1244 | case 0xa: subcmd_str = "NVP"; break; | ||
1245 | case 0xb: subcmd_str = "Marlin/Starfish stuff"; break; | ||
1246 | case 0xc: subcmd_str = "Unclear/Constant"; break; | ||
1247 | case 0xd: subcmd_str = "Secure Clock"; break; | ||
1248 | case 0xe: subcmd_str = "AAD ICV"; break; | ||
1249 | case 0xf: subcmd_str = "EMPR ICV"; break; | ||
1250 | case 0x10: subcmd_str = "Test Mode Flags"; break; | ||
1251 | case 0x11: subcmd_str = "Getty Mode Flags"; break; | ||
1252 | case 0x12: subcmd_str = "Key Mode Debug/Release"; break; | ||
1253 | case 0x13: subcmd_str = "System Information"; break; | ||
1254 | case 0x14: subcmd_str = "Random Data?"; break; | ||
1255 | case 0x15: subcmd_str = "Update Filename"; break; | ||
1256 | case 0x16: subcmd_str = "Key and Signature"; break; | ||
1257 | case 0x17: subcmd_str = "Bluetooth Parameters"; break; | ||
1258 | case 0x18: subcmd_str = "EMPR 0"; break; | ||
1259 | case 0x19: subcmd_str = "EMPR 1"; break; | ||
1260 | case 0x1a: subcmd_str = "EMPR 2"; break; | ||
1261 | case 0x1b: subcmd_str = "Color Variation"; break; | ||
1262 | case 0x1c: subcmd_str = "MTM Sec?"; break; | ||
1263 | case 0x1d: subcmd_str = "Slacker Time"; break; | ||
1264 | case 0x1f: subcmd_str = "Slacker ID File"; break; | ||
1265 | case 0x20: subcmd_str = "FM Parameters"; break; | ||
1266 | case 0x21: subcmd_str = "Speaker Ship Info"; break; | ||
1267 | case 0x22: subcmd_str = "BTMW Factory Pair Info"; break; | ||
1268 | case 0x23: subcmd_str = "U-boot Password"; break; | ||
1269 | case 0x24: subcmd_str = "Noise Cancel Driver Parameter"; break; | ||
1270 | case 0x25: subcmd_str = "Bluetooth PSKey"; break; | ||
1271 | case 0x27: subcmd_str = "VID/PID"; break; | ||
1272 | } | ||
1273 | default: | ||
1274 | break; | ||
1275 | } | ||
1276 | |||
1277 | cprintf_field("Command: ", "%#x (%s)\n", cmd, cmd_str); | ||
1278 | cprintf_field("Sub-cmd: ", "%#x (%s)\n", subcmd, subcmd_str); | ||
1279 | cprintf_field("Argument: ", "%#x\n", arg); | ||
1280 | cprintf_field("Length: ", "%#x\n", length); | ||
1281 | |||
1108 | return 0; | 1282 | return 0; |
1109 | } | 1283 | } |
1110 | 1284 | ||
1111 | static int decode_scsi_dpcc(uint8_t *cdb, int cdb_len) | 1285 | static int decode_scsi_dpcc(uint8_t *cdb, int cdb_len) |
1112 | { | 1286 | { |
1113 | cprintf_field("Opcode: ", "%X (DPCC)\n", cdb[0]); | 1287 | cprintf_field("Opcode: ", "%#x (DPCC)\n", cdb[0]); |
1114 | cprintf(RED, "Unimplemented\n"); | 1288 | cprintf(RED, "Unimplemented\n"); |
1115 | return 0; | 1289 | return 0; |
1116 | } | 1290 | } |
1117 | 1291 | ||
1118 | static int decode_scsi_fc(uint8_t *cdb, int cdb_len) | 1292 | static int decode_scsi_fc(uint8_t *cdb, int cdb_len) |
1119 | { | 1293 | { |
1120 | cprintf_field("Opcode: ", "FC\n"); | 1294 | cprintf_field("Opcode: ", "0xfc\n"); |
1121 | if(cdb[3] == 'd' && cdb[4] == 'b' && cdb[5] == 'm' && cdb[6] == 'n') | 1295 | if(cdb[3] == 'd' && cdb[4] == 'b' && cdb[5] == 'm' && cdb[6] == 'n') |
1122 | { | 1296 | { |
1123 | uint8_t cmd = cdb[2]; | 1297 | uint8_t cmd = cdb[2]; |
@@ -1129,8 +1303,8 @@ static int decode_scsi_fc(uint8_t *cdb, int cdb_len) | |||
1129 | cmd_name = "Get Device Info"; | 1303 | cmd_name = "Get Device Info"; |
1130 | 1304 | ||
1131 | cprintf(BLUE, "Device request:\n"); | 1305 | cprintf(BLUE, "Device request:\n"); |
1132 | cprintf_field(" Command: ", "%x (%s)\n", cmd, cmd_name); | 1306 | cprintf_field(" Command: ", "%#x (%s)\n", cmd, cmd_name); |
1133 | cprintf_field(" Flags(?): ", "%x (Unknown)\n", flags); | 1307 | cprintf_field(" Flags(?): ", "%#x (Unknown)\n", flags); |
1134 | 1308 | ||
1135 | } | 1309 | } |
1136 | return 0; | 1310 | return 0; |
@@ -1213,6 +1387,7 @@ int main(int argc, char **argv) | |||
1213 | {"no-color", no_argument, 0, 'c'}, | 1387 | {"no-color", no_argument, 0, 'c'}, |
1214 | {"series", required_argument, 0, 's'}, | 1388 | {"series", required_argument, 0, 's'}, |
1215 | {"all", no_argument, 0, 'a'}, | 1389 | {"all", no_argument, 0, 'a'}, |
1390 | {"yes-I-want-a-brick", no_argument, 0, -2}, | ||
1216 | {0, 0, 0, 0} | 1391 | {0, 0, 0, 0} |
1217 | }; | 1392 | }; |
1218 | 1393 | ||
@@ -1221,6 +1396,9 @@ int main(int argc, char **argv) | |||
1221 | break; | 1396 | break; |
1222 | switch(c) | 1397 | switch(c) |
1223 | { | 1398 | { |
1399 | case -2: | ||
1400 | g_yes_i_want_a_brick = true; | ||
1401 | break; | ||
1224 | case -1: | 1402 | case -1: |
1225 | break; | 1403 | break; |
1226 | case 'c': | 1404 | case 'c': |