2012-12-11 19:15:51 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Amaury Pouly
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2017-01-03 22:06:53 +00:00
|
|
|
#include "rbscsi.h"
|
2012-12-11 19:15:51 +00:00
|
|
|
#include "misc.h"
|
|
|
|
#include "stmp_scsi.h"
|
|
|
|
|
|
|
|
bool g_debug = false;
|
|
|
|
bool g_force = false;
|
2017-01-03 22:06:53 +00:00
|
|
|
stmp_device_t g_dev_fd;
|
2012-12-11 19:15:51 +00:00
|
|
|
|
|
|
|
static void print_hex(void *_buffer, int buffer_size)
|
|
|
|
{
|
|
|
|
uint8_t *buffer = _buffer;
|
|
|
|
for(int i = 0; i < buffer_size; i += 16)
|
|
|
|
{
|
|
|
|
for(int j = 0; j < 16; j++)
|
|
|
|
{
|
|
|
|
if(i + j < buffer_size)
|
|
|
|
cprintf(YELLOW, " %02x", buffer[i + j]);
|
|
|
|
else
|
|
|
|
cprintf(YELLOW, " ");
|
|
|
|
}
|
|
|
|
printf(" ");
|
|
|
|
for(int j = 0; j < 16; j++)
|
|
|
|
{
|
|
|
|
if(i + j < buffer_size)
|
|
|
|
cprintf(RED, "%c", isprint(buffer[i + j]) ? buffer[i + j] : '.');
|
|
|
|
else
|
|
|
|
cprintf(RED, " ");
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(buffer_size == 0)
|
|
|
|
printf("\n");
|
2012-12-11 19:15:51 +00:00
|
|
|
}
|
|
|
|
|
2013-10-09 22:11:47 +00:00
|
|
|
static const char *get_size_suffix(unsigned long long size)
|
|
|
|
{
|
|
|
|
int order = 0;
|
|
|
|
while(size >= 1024)
|
|
|
|
{
|
|
|
|
size /= 1024;
|
|
|
|
order++;
|
|
|
|
}
|
|
|
|
static const char *suffix[] = {"B", "KiB", "MiB", "GiB", "TiB"};
|
|
|
|
return suffix[order];
|
|
|
|
}
|
|
|
|
|
|
|
|
static float get_size_natural(unsigned long long size)
|
|
|
|
{
|
|
|
|
float res = size;
|
|
|
|
while(res >= 1024)
|
|
|
|
res /= 1024;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-01-03 23:26:52 +00:00
|
|
|
static void print_ver(struct scsi_stmp_logical_drive_info_version_t *ver)
|
|
|
|
{
|
|
|
|
cprintf(YELLOW, "%x.%x.%x\n", ver->major, ver->minor, ver->revision);
|
|
|
|
}
|
|
|
|
|
2013-09-05 21:14:57 +00:00
|
|
|
static int do_info(void)
|
2012-12-11 19:15:51 +00:00
|
|
|
{
|
|
|
|
cprintf(BLUE, "Information\n");
|
2013-09-05 21:14:57 +00:00
|
|
|
|
2012-12-11 19:15:51 +00:00
|
|
|
uint8_t dev_type;
|
|
|
|
char vendor[9];
|
|
|
|
char product[17];
|
2017-01-03 22:06:53 +00:00
|
|
|
int ret = stmp_scsi_inquiry(g_dev_fd, &dev_type, vendor, product);
|
|
|
|
if(ret == 0)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
|
|
|
cprintf_field(" Vendor: ", "%s\n", vendor);
|
|
|
|
cprintf_field(" Product: ", "%s\n", product);
|
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
else if(g_debug)
|
|
|
|
cprintf(GREY, "Cannot get inquiry data: %d\n", ret);
|
2013-10-09 22:11:47 +00:00
|
|
|
|
2012-12-11 19:15:51 +00:00
|
|
|
struct scsi_stmp_protocol_version_t ver;
|
2017-01-03 22:06:53 +00:00
|
|
|
ret = stmp_get_protocol_version(g_dev_fd, &ver);
|
|
|
|
if(ret == 0)
|
2013-01-15 19:42:20 +00:00
|
|
|
cprintf_field(" Protocol: ", "%x.%x\n", ver.major, ver.minor);
|
2017-01-03 23:26:52 +00:00
|
|
|
else if(g_debug)
|
|
|
|
cprintf(GREY, "Cannot get protocol version: %d\n", ret);
|
2013-01-15 19:42:20 +00:00
|
|
|
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf(BLUE, "Device\n");
|
2013-01-15 19:42:20 +00:00
|
|
|
|
2017-01-03 23:26:52 +00:00
|
|
|
uint8_t *serial;
|
|
|
|
int serial_len;
|
|
|
|
if(!stmp_get_device_serial(g_dev_fd, &serial, &serial_len))
|
|
|
|
{
|
|
|
|
cprintf_field(" Serial Number:", " ");
|
|
|
|
print_hex(serial, serial_len);
|
|
|
|
free(serial);
|
|
|
|
}
|
2013-10-09 22:11:47 +00:00
|
|
|
|
2017-01-03 23:26:52 +00:00
|
|
|
uint16_t chip_rev;
|
|
|
|
ret = stmp_get_chip_major_rev_id(g_dev_fd, &chip_rev);
|
|
|
|
if(ret)
|
|
|
|
cprintf(GREY, "Cannot get chip major revision id: %d\n", ret);
|
|
|
|
else
|
|
|
|
cprintf_field(" Chip Major Rev ID: ", "%x\n", chip_rev);
|
2013-01-15 19:42:20 +00:00
|
|
|
|
2017-01-03 23:26:52 +00:00
|
|
|
uint16_t rom_rev;
|
|
|
|
ret = stmp_get_rom_rev_id(g_dev_fd, &rom_rev);
|
|
|
|
if(ret)
|
|
|
|
cprintf(GREY, "Cannot get rom revision id: %d\n", ret);
|
|
|
|
else
|
|
|
|
cprintf_field(" ROM Rev ID: ", "%x\n", rom_rev);
|
2013-01-15 19:42:20 +00:00
|
|
|
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf(BLUE, "Logical Media\n");
|
|
|
|
struct stmp_logical_media_info_t info;
|
|
|
|
ret = stmp_get_logical_media_info(g_dev_fd, &info);
|
|
|
|
if(!ret)
|
|
|
|
{
|
|
|
|
if(info.has.nr_drives)
|
|
|
|
cprintf_field(" Number of drives:", " %u\n", info.nr_drives);
|
|
|
|
if(info.has.size)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Media size:", " %llu ", (unsigned long long)info.size);
|
|
|
|
cprintf(RED, "(%.3f %s)\n", get_size_natural(info.size), get_size_suffix(info.size));
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.alloc_size)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Allocation unit size:", " %lu ", (unsigned long)info.alloc_size);
|
|
|
|
cprintf(RED, "(%.3f %s)\n", get_size_natural(info.alloc_size), get_size_suffix(info.alloc_size));
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.initialised)
|
|
|
|
cprintf_field(" Initialised:", " %u\n", info.initialised);
|
|
|
|
if(info.has.state)
|
|
|
|
cprintf_field(" State:", " %u\n", info.state);
|
|
|
|
if(info.has.write_protected)
|
|
|
|
cprintf_field(" Write protected:", " %u\n", info.write_protected);
|
|
|
|
if(info.has.type)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Type:", " %u ", info.type);
|
|
|
|
cprintf(RED, "(%s)\n", stmp_get_logical_media_type_string(info.type));
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.serial)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Serial:", " ");
|
|
|
|
print_hex(info.serial, info.serial_len);
|
|
|
|
free(info.serial);
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.system)
|
|
|
|
cprintf_field(" System:", " %u\n", info.system);
|
|
|
|
if(info.has.present)
|
|
|
|
cprintf_field(" Present:", " %u\n", info.present);
|
|
|
|
if(info.has.page_size)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Page size:", " %lu ", (unsigned long)info.page_size);
|
|
|
|
cprintf(RED, "(%.3f %s)\n", get_size_natural(info.page_size), get_size_suffix(info.page_size));
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.vendor)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Vendor:", " %u ", info.vendor);
|
|
|
|
cprintf(RED, "(%s)\n", stmp_get_logical_media_vendor_string(info.vendor));
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.nand_id)
|
2013-10-09 22:11:47 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Nand ID:", " ");
|
|
|
|
print_hex(info.nand_id, sizeof(info.nand_id));
|
2013-10-09 22:11:47 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.nr_devices)
|
|
|
|
cprintf_field(" Number of devices:", " %lu\n", (unsigned long)info.nr_devices);
|
|
|
|
}
|
2013-01-15 19:42:20 +00:00
|
|
|
else
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf(GREY, "Cannot get media info: %d\n", ret);
|
2017-01-03 22:06:53 +00:00
|
|
|
|
2017-01-03 23:26:52 +00:00
|
|
|
struct stmp_logical_media_table_t *table;
|
|
|
|
ret = stmp_get_logical_media_table(g_dev_fd, &table);
|
|
|
|
if(!ret)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf(BLUE, "Logical Media Table\n");
|
|
|
|
for(int i = 0; i < table->header.count; i++)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf(RED, " Drive ");
|
|
|
|
cprintf_field("No: ", "%2x", table->entry[i].drive_no);
|
|
|
|
cprintf_field(" Type: ", "%#x ", table->entry[i].type);
|
|
|
|
cprintf(RED, "(%s)", stmp_get_logical_drive_type_string(table->entry[i].type));
|
|
|
|
cprintf_field(" Tag: ", "%#x ", table->entry[i].tag);
|
|
|
|
cprintf(RED, "(%s)", stmp_get_logical_drive_tag_string(table->entry[i].tag));
|
|
|
|
unsigned long long size = table->entry[i].size;
|
|
|
|
cprintf_field(" Size: ", "%.3f %s", get_size_natural(size), get_size_suffix(size));
|
2013-01-15 19:42:20 +00:00
|
|
|
cprintf(OFF, "\n");
|
|
|
|
}
|
|
|
|
|
2017-01-03 23:26:52 +00:00
|
|
|
for(int i = 0; i < table->header.count; i++)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
uint8_t drive = table->entry[i].drive_no;
|
|
|
|
cprintf(BLUE, "Drive ");
|
|
|
|
cprintf(YELLOW, "%02x\n", drive);
|
|
|
|
struct stmp_logical_drive_info_t info;
|
|
|
|
ret = stmp_get_logical_drive_info(g_dev_fd, drive, &info);
|
|
|
|
if(ret)
|
|
|
|
continue;
|
|
|
|
if(info.has.sector_size)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Sector size:", " %llu ", (unsigned long long)info.sector_size);
|
|
|
|
cprintf(RED, "(%.3f %s)\n", get_size_natural(info.sector_size), get_size_suffix(info.sector_size));
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.erase_size)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Erase size:", " %llu ", (unsigned long long)info.erase_size);
|
|
|
|
cprintf(RED, "(%.3f %s)\n", get_size_natural(info.erase_size), get_size_suffix(info.erase_size));
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.size)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Drive size:", " %llu ", (unsigned long long)info.size);
|
|
|
|
cprintf(RED, "(%.3f %s)\n", get_size_natural(info.size), get_size_suffix(info.size));
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.sector_count)
|
|
|
|
cprintf_field(" Sector count:", " %lu\n", (unsigned long)info.sector_count);
|
|
|
|
if(info.has.type)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Type:", " %u ", info.type);
|
|
|
|
cprintf(RED, "(%s)\n", stmp_get_logical_drive_type_string(info.type));
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.tag)
|
2013-01-15 19:42:20 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Tag:", " %u ", info.tag);
|
|
|
|
cprintf(RED, "(%s)\n", stmp_get_logical_drive_tag_string(info.tag));
|
2013-10-09 22:11:47 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.component_version)
|
2013-10-09 22:11:47 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Component version:", " ");
|
|
|
|
print_ver(&info.component_version);
|
2013-10-09 22:11:47 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.project_version)
|
2013-10-09 22:11:47 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Project version:", " ");
|
|
|
|
print_ver(&info.project_version);
|
2013-10-09 22:11:47 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.write_protected)
|
|
|
|
cprintf_field(" Write protected:", " %u\n", info.write_protected);
|
|
|
|
if(info.has.serial)
|
2013-10-09 22:11:47 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf_field(" Serial:", " ");
|
|
|
|
print_hex(info.serial, info.serial_len);
|
|
|
|
free(info.serial);
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
if(info.has.change)
|
|
|
|
cprintf_field(" Change:", " %u\n", info.change);
|
|
|
|
if(info.has.present)
|
|
|
|
cprintf_field(" Present:", " %u\n", info.present);
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
free(table);
|
2013-01-15 19:42:20 +00:00
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
else
|
|
|
|
cprintf(GREY, "Cannot get logical table: %d\n", ret);
|
2012-12-11 19:15:51 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-09-05 21:14:57 +00:00
|
|
|
void do_extract(const char *file)
|
|
|
|
{
|
|
|
|
FILE *f = NULL;
|
|
|
|
cprintf(BLUE, "Extracting firmware...\n");
|
|
|
|
|
2017-01-03 23:26:52 +00:00
|
|
|
struct stmp_logical_media_table_t *table = NULL;
|
|
|
|
int ret = stmp_get_logical_media_table(g_dev_fd, &table);
|
2013-09-05 21:14:57 +00:00
|
|
|
if(ret)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot get logical table: %d\n", ret);
|
|
|
|
goto Lend;
|
|
|
|
}
|
|
|
|
int entry = 0;
|
2017-01-03 23:26:52 +00:00
|
|
|
while(entry < table->header.count)
|
|
|
|
if(table->entry[entry].type == SCSI_STMP_DRIVE_TYPE_SYSTEM &&
|
|
|
|
table->entry[entry].tag == SCSI_STMP_DRIVE_TAG_SYSTEM_BOOT)
|
2013-09-05 21:14:57 +00:00
|
|
|
break;
|
|
|
|
else
|
|
|
|
entry++;
|
2017-01-03 23:26:52 +00:00
|
|
|
if(entry == table->header.count)
|
2013-09-05 21:14:57 +00:00
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot find firmware partition\n");
|
|
|
|
goto Lend;
|
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
uint8_t drive_no = table->entry[entry].drive_no;
|
|
|
|
uint64_t drive_sz = table->entry[entry].size;
|
2013-09-05 21:14:57 +00:00
|
|
|
if(g_debug)
|
|
|
|
{
|
|
|
|
cprintf(RED, "* ");
|
|
|
|
cprintf_field("Drive: ", "%#x\n", drive_no);
|
|
|
|
cprintf(RED, "* ");
|
|
|
|
cprintf_field("Size: ", "%#llx\n", (unsigned long long)drive_sz);
|
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
struct stmp_logical_drive_info_t info;
|
|
|
|
ret = stmp_get_logical_drive_info(g_dev_fd, drive_no, &info);
|
|
|
|
if(ret || !info.has.sector_size)
|
2013-09-05 21:14:57 +00:00
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot get sector size\n");
|
|
|
|
goto Lend;
|
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
unsigned sector_size = info.sector_size;
|
2013-09-05 21:14:57 +00:00
|
|
|
if(g_debug)
|
|
|
|
{
|
|
|
|
cprintf(RED, "* ");
|
|
|
|
cprintf_field("Sector size: ", "%lu\n", (unsigned long)sector_size);
|
|
|
|
}
|
|
|
|
uint8_t *sector = malloc(sector_size);
|
2017-01-03 23:26:52 +00:00
|
|
|
ret = stmp_read_logical_drive_sectors(g_dev_fd, drive_no, 0, 1, sector, sector_size);
|
|
|
|
if(ret)
|
2013-09-05 21:14:57 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf(GREY, "Cannot read first sector: %d\n", ret);
|
|
|
|
goto Lend;
|
2013-09-05 21:14:57 +00:00
|
|
|
}
|
|
|
|
uint32_t fw_size = *(uint32_t *)(sector + 0x1c) * 16;
|
|
|
|
if(g_debug)
|
|
|
|
{
|
|
|
|
cprintf(RED, "* ");
|
|
|
|
cprintf_field("Firmware size: ", "%#x\n", fw_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
f = fopen(file, "wb");
|
|
|
|
if(f == NULL)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot open '%s' for writing: %m\n", file);
|
|
|
|
goto Lend;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int sec = 0; sec * sector_size < fw_size; sec++)
|
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
ret = stmp_read_logical_drive_sectors(g_dev_fd, drive_no, sec, 1, sector, sector_size);
|
|
|
|
if(ret)
|
2013-09-05 21:14:57 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf(GREY, "Cannot read sector %d: %d\n", sec, ret);
|
2013-09-05 21:14:57 +00:00
|
|
|
goto Lend;
|
|
|
|
}
|
|
|
|
if(fwrite(sector, sector_size, 1, f) != 1)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "Write failed: %m\n");
|
|
|
|
goto Lend;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cprintf(BLUE, "Done\n");
|
|
|
|
Lend:
|
2017-01-03 23:26:52 +00:00
|
|
|
free(table);
|
2013-09-05 21:14:57 +00:00
|
|
|
if(f)
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2013-12-24 00:09:33 +00:00
|
|
|
void do_write(const char *file, int want_a_brick)
|
|
|
|
{
|
|
|
|
if(!want_a_brick)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "Writing a new firmware is a dangerous operation that should be attempted\n");
|
|
|
|
cprintf(GREY, "if you know what you are doing. If you do, please add the --yes-i-want-a-brick\n");
|
|
|
|
cprintf(GREY, "option on the command line and do not complain if you end up with a brick ;)\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
FILE *f = NULL;
|
|
|
|
cprintf(BLUE, "Writing firmware...\n");
|
|
|
|
|
2017-01-03 23:26:52 +00:00
|
|
|
struct stmp_logical_media_table_t *table = NULL;
|
|
|
|
int ret = stmp_get_logical_media_table(g_dev_fd, &table);
|
2013-12-24 00:09:33 +00:00
|
|
|
if(ret)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot get logical table: %d\n", ret);
|
|
|
|
goto Lend;
|
|
|
|
}
|
|
|
|
int entry = 0;
|
2017-01-03 23:26:52 +00:00
|
|
|
while(entry < table->header.count)
|
|
|
|
if(table->entry[entry].type == SCSI_STMP_DRIVE_TYPE_SYSTEM &&
|
|
|
|
table->entry[entry].tag == SCSI_STMP_DRIVE_TAG_SYSTEM_BOOT)
|
2013-12-24 00:09:33 +00:00
|
|
|
break;
|
|
|
|
else
|
|
|
|
entry++;
|
2017-01-03 23:26:52 +00:00
|
|
|
if(entry == table->header.count)
|
2013-12-24 00:09:33 +00:00
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot find firmware partition\n");
|
|
|
|
goto Lend;
|
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
uint8_t drive_no = table->entry[entry].drive_no;
|
|
|
|
uint64_t drive_sz = table->entry[entry].size;
|
2013-12-24 00:09:33 +00:00
|
|
|
if(g_debug)
|
|
|
|
{
|
|
|
|
cprintf(RED, "* ");
|
|
|
|
cprintf_field("Drive: ", "%#x\n", drive_no);
|
|
|
|
cprintf(RED, "* ");
|
|
|
|
cprintf_field("Size: ", "%#llx\n", (unsigned long long)drive_sz);
|
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
struct stmp_logical_drive_info_t info;
|
|
|
|
ret = stmp_get_logical_drive_info(g_dev_fd, drive_no, &info);
|
|
|
|
if(ret || !info.has.sector_size)
|
2013-12-24 00:09:33 +00:00
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot get sector size\n");
|
|
|
|
goto Lend;
|
|
|
|
}
|
2017-01-03 23:26:52 +00:00
|
|
|
unsigned sector_size = info.sector_size;
|
2013-12-24 00:09:33 +00:00
|
|
|
uint8_t *sector = malloc(sector_size);
|
|
|
|
|
|
|
|
/* sanity check by reading first sector */
|
2017-01-03 23:26:52 +00:00
|
|
|
ret = stmp_read_logical_drive_sectors(g_dev_fd, drive_no, 0, 1, sector, sector_size);
|
|
|
|
if(ret)
|
2013-12-24 00:09:33 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf(GREY, "Cannot read first sector: %d\n", ret);
|
2013-12-24 00:09:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
uint32_t sig = *(uint32_t *)(sector + 0x14);
|
|
|
|
if(sig != 0x504d5453)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "There is something wrong: the first sector doesn't have the STMP signature. Bailing out...\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
f = fopen(file, "rb");
|
|
|
|
if(f == NULL)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot open '%s' for writing: %m\n", file);
|
|
|
|
goto Lend;
|
|
|
|
}
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
|
|
int fw_size = ftell(f);
|
|
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
if(g_debug)
|
|
|
|
{
|
|
|
|
cprintf(RED, "* ");
|
|
|
|
cprintf_field("Firmware size: ", "%#x\n", fw_size);
|
|
|
|
}
|
|
|
|
/* sanity check size */
|
|
|
|
if((uint64_t)fw_size > drive_sz)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "You cannot write a firmware greater than the partition size.\n");
|
|
|
|
goto Lend;
|
|
|
|
}
|
|
|
|
|
2014-02-22 17:12:00 +00:00
|
|
|
int percent = -1;
|
2013-12-24 00:09:33 +00:00
|
|
|
for(int off = 0; off < fw_size; off += sector_size)
|
|
|
|
{
|
|
|
|
int sec = off / sector_size;
|
2014-02-22 17:12:00 +00:00
|
|
|
int this_percent = (sec * 100) / (fw_size / sector_size);
|
|
|
|
if(this_percent != percent && (this_percent % 5) == 0)
|
|
|
|
{
|
|
|
|
cprintf(RED, "%d%%", this_percent);
|
|
|
|
cprintf(YELLOW, "...");
|
|
|
|
fflush(stdout);
|
|
|
|
}
|
|
|
|
percent = this_percent;
|
2013-12-24 00:09:33 +00:00
|
|
|
int xfer_len = MIN(fw_size - off, (int)sector_size);
|
|
|
|
if(fread(sector, xfer_len, 1, f) != 1)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "Read failed: %m\n");
|
|
|
|
goto Lend;
|
|
|
|
}
|
|
|
|
/* NOTE transfer a whole sector even if incomplete, the device won't access
|
|
|
|
* partial sectors */
|
|
|
|
if(xfer_len < (int)sector_size)
|
|
|
|
memset(sector + xfer_len, 0, sector_size - xfer_len);
|
2017-01-03 23:26:52 +00:00
|
|
|
ret = stmp_write_logical_drive_sectors(g_dev_fd, drive_no, sec, 1, sector, sector_size);
|
|
|
|
if(ret)
|
2013-12-24 00:09:33 +00:00
|
|
|
{
|
2017-01-03 23:26:52 +00:00
|
|
|
cprintf(GREY, "Cannot write sector %d: %d\n", sec, ret);
|
2013-12-24 00:09:33 +00:00
|
|
|
goto Lend;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cprintf(BLUE, "Done\n");
|
|
|
|
Lend:
|
|
|
|
if(f)
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2012-12-11 19:15:51 +00:00
|
|
|
static void usage(void)
|
|
|
|
{
|
|
|
|
printf("Usage: scsitool [options] <dev>\n");
|
|
|
|
printf("Options:\n");
|
2013-09-05 21:14:57 +00:00
|
|
|
printf(" -f/--force Force to continue on errors\n");
|
|
|
|
printf(" -?/--help Display this message\n");
|
|
|
|
printf(" -d/--debug Display debug messages\n");
|
|
|
|
printf(" -c/--no-color Disable color output\n");
|
|
|
|
printf(" -x/--extract-fw <file> Extract firmware to file\n");
|
2013-12-24 00:09:33 +00:00
|
|
|
printf(" -w/--write-fw <file> Write firmware to device\n");
|
2013-09-05 21:14:57 +00:00
|
|
|
printf(" -i/--info Display device information\n");
|
2013-12-24 00:09:33 +00:00
|
|
|
printf(" --yes-i-want-a-brick Allow the tool to turn your device into a brick\n");
|
2012-12-11 19:15:51 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2013-12-24 00:09:33 +00:00
|
|
|
static int g_yes_i_want_a_brick = 0;
|
|
|
|
|
2017-01-03 22:06:53 +00:00
|
|
|
static void scsi_printf(void *user, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
(void)user;
|
|
|
|
if(!g_debug)
|
|
|
|
return;
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
color(GREY);
|
|
|
|
vprintf(fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
2012-12-11 19:15:51 +00:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2013-09-05 21:14:57 +00:00
|
|
|
if(argc == 1)
|
|
|
|
usage();
|
|
|
|
const char *extract_fw = NULL;
|
2013-12-24 00:09:33 +00:00
|
|
|
const char *write_fw = NULL;
|
2013-09-05 21:14:57 +00:00
|
|
|
bool info = false;
|
2012-12-11 19:15:51 +00:00
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
static struct option long_options[] =
|
|
|
|
{
|
|
|
|
{"help", no_argument, 0, '?'},
|
|
|
|
{"debug", no_argument, 0, 'd'},
|
|
|
|
{"no-color", no_argument, 0, 'c'},
|
|
|
|
{"force", no_argument, 0, 'f'},
|
2013-09-05 21:14:57 +00:00
|
|
|
{"extract-fw", required_argument, 0, 'x'},
|
2013-12-24 00:09:33 +00:00
|
|
|
{"write-fw", required_argument, 0, 'w'},
|
2013-09-05 21:14:57 +00:00
|
|
|
{"info", no_argument, 0, 'i'},
|
2013-12-24 00:09:33 +00:00
|
|
|
{"yes-i-want-a-brick", no_argument, &g_yes_i_want_a_brick, 1},
|
2012-12-11 19:15:51 +00:00
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
2013-12-24 00:09:33 +00:00
|
|
|
int c = getopt_long(argc, argv, "?dcfx:iw:", long_options, NULL);
|
2012-12-11 19:15:51 +00:00
|
|
|
if(c == -1)
|
|
|
|
break;
|
|
|
|
switch(c)
|
|
|
|
{
|
2013-12-24 00:09:33 +00:00
|
|
|
case 0:
|
|
|
|
continue;
|
2012-12-11 19:15:51 +00:00
|
|
|
case -1:
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
enable_color(false);
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
g_debug = true;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
g_force = true;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
usage();
|
|
|
|
break;
|
2013-09-05 21:14:57 +00:00
|
|
|
case 'x':
|
|
|
|
extract_fw = optarg;
|
|
|
|
break;
|
2013-12-24 00:09:33 +00:00
|
|
|
case 'w':
|
|
|
|
write_fw = optarg;
|
|
|
|
break;
|
2013-09-05 21:14:57 +00:00
|
|
|
case 'i':
|
|
|
|
info = true;
|
|
|
|
break;
|
2012-12-11 19:15:51 +00:00
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(argc - optind != 1)
|
|
|
|
{
|
|
|
|
usage();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = 0;
|
2017-01-03 22:06:53 +00:00
|
|
|
rb_scsi_device_t scsi_dev = rb_scsi_open(argv[optind], g_debug ? RB_SCSI_DEBUG : 0, NULL, scsi_printf);
|
|
|
|
if(scsi_dev == 0)
|
2012-12-11 19:15:51 +00:00
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot open device: %m\n");
|
|
|
|
ret = 1;
|
|
|
|
goto Lend;
|
|
|
|
}
|
2017-01-03 22:06:53 +00:00
|
|
|
g_dev_fd = stmp_open(scsi_dev, g_debug ? STMP_DEBUG : 0, NULL, scsi_printf);
|
|
|
|
if(g_dev_fd == 0)
|
|
|
|
{
|
|
|
|
cprintf(GREY, "Cannot open stmp device: %m\n");
|
|
|
|
ret = 2;
|
|
|
|
goto Lend;
|
|
|
|
}
|
2012-12-11 19:15:51 +00:00
|
|
|
|
2013-09-05 21:14:57 +00:00
|
|
|
if(extract_fw)
|
|
|
|
do_extract(extract_fw);
|
|
|
|
if(info)
|
|
|
|
do_info();
|
2013-12-24 00:09:33 +00:00
|
|
|
if(write_fw)
|
|
|
|
do_write(write_fw, g_yes_i_want_a_brick);
|
2012-12-11 19:15:51 +00:00
|
|
|
|
2017-01-03 22:06:53 +00:00
|
|
|
stmp_close(g_dev_fd);
|
|
|
|
rb_scsi_close(scsi_dev);
|
2012-12-11 19:15:51 +00:00
|
|
|
Lend:
|
|
|
|
color(OFF);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|