forked from len0rd/rockbox
sbtools: more work on sbtoelf and elftosb, support more attributes
Now handle timestamp, sb minor version, component/product versions, section flags. Change-Id: I35313efe60c28f04ea3732b36e5e01be3213cf9e
This commit is contained in:
parent
a122b52d66
commit
260399ee8c
6 changed files with 106 additions and 85 deletions
|
|
@ -419,44 +419,29 @@ struct cmd_option_t *db_find_option_by_id(struct cmd_option_t *opt, const char *
|
|||
|
||||
#define INVALID_SB_SUBVERSION 0xffff
|
||||
|
||||
static uint16_t parse_sb_subversion(char *str)
|
||||
static const char *parse_sb_subversion(const char *str, uint16_t *v)
|
||||
{
|
||||
int len = strlen(str);
|
||||
uint16_t n = 0;
|
||||
if(len == 0 || len > 4)
|
||||
return INVALID_SB_SUBVERSION;
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
if(!isdigit(str[i]))
|
||||
return INVALID_SB_SUBVERSION;
|
||||
n = n << 4 | (str[i] - '0');
|
||||
}
|
||||
return n;
|
||||
int len = 0;
|
||||
*v = 0;
|
||||
while(isdigit(str[len]) && len < 3)
|
||||
*v = (*v) << 4 | (str[len++] - '0');
|
||||
if(len == 0)
|
||||
*v = INVALID_SB_SUBVERSION;
|
||||
return str + len;
|
||||
}
|
||||
|
||||
bool db_parse_sb_version(struct sb_version_t *ver, char *str)
|
||||
bool db_parse_sb_version(struct sb_version_t *ver, const char *str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
int cnt = 0;
|
||||
int pos[2];
|
||||
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
if(str[i] != '.')
|
||||
continue;
|
||||
if(cnt == 2)
|
||||
return false;
|
||||
pos[cnt++] = i + 1;
|
||||
str[i] = 0;
|
||||
}
|
||||
if(cnt != 2)
|
||||
str = parse_sb_subversion(str, &ver->major);
|
||||
if(ver->major == INVALID_SB_SUBVERSION || *str != '.')
|
||||
return false;
|
||||
ver->major = parse_sb_subversion(str);
|
||||
ver->minor = parse_sb_subversion(str + pos[0]);
|
||||
ver->revision = parse_sb_subversion(str + pos[1]);
|
||||
return ver->major != INVALID_SB_SUBVERSION &&
|
||||
ver->minor != INVALID_SB_SUBVERSION &&
|
||||
ver->revision != INVALID_SB_SUBVERSION;
|
||||
str = parse_sb_subversion(str + 1, &ver->minor);
|
||||
if(ver->minor == INVALID_SB_SUBVERSION || *str != '.')
|
||||
return false;
|
||||
str = parse_sb_subversion(str + 1, &ver->revision);
|
||||
if(ver->revision == INVALID_SB_SUBVERSION || *str != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool db_generate_sb_subversion(uint16_t subver, char *str)
|
||||
|
|
@ -831,11 +816,6 @@ struct cmd_file_t *db_parse_file(const char *file)
|
|||
return cmd_file;
|
||||
}
|
||||
|
||||
void db_generate_default_sb_version(struct sb_version_t *ver)
|
||||
{
|
||||
ver->major = ver->minor = ver->revision = 0x999;
|
||||
}
|
||||
|
||||
void db_free_option_list(struct cmd_option_t *opt_list)
|
||||
{
|
||||
while(opt_list)
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ typedef void (*db_color_printf)(void *u, bool err, color_t c, const char *f, ...
|
|||
|
||||
struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const char *id);
|
||||
struct cmd_option_t *db_find_option_by_id(struct cmd_option_t *opt, const char *name);
|
||||
bool db_parse_sb_version(struct sb_version_t *ver, char *str);
|
||||
bool db_parse_sb_version(struct sb_version_t *ver, const char *str);
|
||||
bool db_generate_sb_version(struct sb_version_t *ver, char *str, int size);
|
||||
void db_generate_default_sb_version(struct sb_version_t *ver);
|
||||
struct cmd_file_t *db_parse_file(const char *file);
|
||||
|
|
|
|||
|
|
@ -118,13 +118,51 @@ static void load_bin_by_id(struct cmd_file_t *cmd_file, const char *id)
|
|||
src->loaded = true;
|
||||
}
|
||||
|
||||
static const char *get_str_opt(struct cmd_option_t *opt_list, const char *id, const char *dflt)
|
||||
{
|
||||
struct cmd_option_t *opt = db_find_option_by_id(opt_list, id);
|
||||
if(!opt)
|
||||
return dflt;
|
||||
if(!opt->is_string)
|
||||
bug("'%s' option must be a string\n", id);
|
||||
return opt->str;
|
||||
}
|
||||
|
||||
static uint32_t get_int_opt(struct cmd_option_t *opt_list, const char *id, uint32_t dflt)
|
||||
{
|
||||
struct cmd_option_t *opt = db_find_option_by_id(opt_list, id);
|
||||
if(!opt)
|
||||
return dflt;
|
||||
if(opt->is_string)
|
||||
bug("'%s' option must be an integer\n", id);
|
||||
return opt->val;
|
||||
}
|
||||
|
||||
static struct sb_file_t *apply_cmd_file(struct cmd_file_t *cmd_file)
|
||||
{
|
||||
struct sb_file_t *sb = xmalloc(sizeof(struct sb_file_t));
|
||||
memset(sb, 0, sizeof(struct sb_file_t));
|
||||
sb_build_default_image(sb);
|
||||
|
||||
db_generate_default_sb_version(&sb->product_ver);
|
||||
db_generate_default_sb_version(&sb->component_ver);
|
||||
if(db_find_option_by_id(cmd_file->opt_list, "componentVersion") &&
|
||||
!db_parse_sb_version(&sb->component_ver, get_str_opt(cmd_file->opt_list, "componentVersion", "")))
|
||||
bug("Invalid 'componentVersion' format\n");
|
||||
if(db_find_option_by_id(cmd_file->opt_list, "productVersion") &&
|
||||
!db_parse_sb_version(&sb->product_ver, get_str_opt(cmd_file->opt_list, "productVersion", "")))
|
||||
bug("Invalid 'productVersion' format\n");
|
||||
if(db_find_option_by_id(cmd_file->opt_list, "sbMinorVersion"))
|
||||
sb->minor_version = get_int_opt(cmd_file->opt_list, "sbMinorVersion", 0);
|
||||
if(db_find_option_by_id(cmd_file->opt_list, "flags"))
|
||||
sb->flags = get_int_opt(cmd_file->opt_list, "flags", 0);
|
||||
if(db_find_option_by_id(cmd_file->opt_list, "driveTag"))
|
||||
sb->drive_tag = get_int_opt(cmd_file->opt_list, "driveTag", 0);
|
||||
if(db_find_option_by_id(cmd_file->opt_list, "timestampLow"))
|
||||
{
|
||||
if(!db_find_option_by_id(cmd_file->opt_list, "timestampHigh"))
|
||||
bug("Option 'timestampLow' and 'timestampHigh' must both specified\n");
|
||||
sb->timestamp = (uint64_t)get_int_opt(cmd_file->opt_list, "timestampHigh", 0) << 32 |
|
||||
get_int_opt(cmd_file->opt_list, "timestampLow", 0);
|
||||
}
|
||||
|
||||
if(g_debug)
|
||||
printf("Applying command file...\n");
|
||||
|
|
@ -149,32 +187,16 @@ static struct sb_file_t *apply_cmd_file(struct cmd_file_t *cmd_file)
|
|||
do
|
||||
{
|
||||
/* cleartext */
|
||||
struct cmd_option_t *opt = db_find_option_by_id(csec->opt_list, "cleartext");
|
||||
if(opt != NULL)
|
||||
{
|
||||
if(opt->is_string)
|
||||
bug("Cleartext section attribute must be an integer\n");
|
||||
if(opt->val != 0 && opt->val != 1)
|
||||
bug("Cleartext section attribute must be 0 or 1\n");
|
||||
sec->is_cleartext = opt->val;
|
||||
}
|
||||
sec->is_cleartext = get_int_opt(csec->opt_list, "cleartext", false);
|
||||
/* alignment */
|
||||
opt = db_find_option_by_id(csec->opt_list, "alignment");
|
||||
if(opt != NULL)
|
||||
{
|
||||
if(opt->is_string)
|
||||
bug("Cleartext section attribute must be an integer\n");
|
||||
// n is a power of 2 iff n & (n - 1) = 0
|
||||
// alignement cannot be lower than block size
|
||||
if((opt->val & (opt->val - 1)) != 0)
|
||||
bug("Cleartext section attribute must be a power of two\n");
|
||||
if(opt->val < BLOCK_SIZE)
|
||||
sec->alignment = BLOCK_SIZE;
|
||||
else
|
||||
sec->alignment = opt->val;
|
||||
}
|
||||
else
|
||||
sec->alignment = get_int_opt(csec->opt_list, "alignment", BLOCK_SIZE);
|
||||
// alignement cannot be lower than block size
|
||||
if((sec->alignment & (sec->alignment - 1)) != 0)
|
||||
bug("Alignment section attribute must be a power of two\n");
|
||||
if(sec->alignment < BLOCK_SIZE)
|
||||
sec->alignment = BLOCK_SIZE;
|
||||
/* other flags */
|
||||
sec->other_flags = get_int_opt(csec->opt_list, "sectionFlags", 0) & ~SECTION_STD_MASK;
|
||||
}while(0);
|
||||
|
||||
if(csec->is_data)
|
||||
|
|
@ -424,12 +446,6 @@ int main(int argc, char **argv)
|
|||
memcpy(sb_file->crypto_iv, crypto_iv.u.key, 16);
|
||||
}
|
||||
|
||||
/* fill with default parameters since there is no command file support for them */
|
||||
sb_file->drive_tag = 0;
|
||||
sb_file->first_boot_sec_id = sb_file->sections[0].identifier;
|
||||
sb_file->flags = 0;
|
||||
sb_file->minor_version = 1;
|
||||
|
||||
sb_write_file(sb_file, output_filename, 0, generic_std_printf);
|
||||
sb_free(sb_file);
|
||||
clear_keys();
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ static void compute_sb_offsets(struct sb_file_t *sb, void *u, generic_printf_t c
|
|||
#undef printf
|
||||
}
|
||||
|
||||
static uint64_t generate_timestamp()
|
||||
uint64_t sb_generate_timestamp(void)
|
||||
{
|
||||
struct tm tm_base;
|
||||
memset(&tm_base, 0, sizeof(tm_base));
|
||||
|
|
@ -218,11 +218,11 @@ static void produce_sb_header(struct sb_file_t *sb, struct sb_header_t *sb_hdr)
|
|||
sb_hdr->signature[2] = 'M';
|
||||
sb_hdr->signature[3] = 'P';
|
||||
sb_hdr->major_ver = IMAGE_MAJOR_VERSION;
|
||||
sb_hdr->minor_ver = IMAGE_MINOR_VERSION;
|
||||
sb_hdr->flags = 0;
|
||||
sb_hdr->minor_ver = sb->minor_version;
|
||||
sb_hdr->flags = sb->flags;
|
||||
sb_hdr->image_size = sb->image_size;
|
||||
sb_hdr->header_size = sizeof(struct sb_header_t) / BLOCK_SIZE;
|
||||
sb_hdr->first_boot_sec_id = sb->first_boot_sec_id;
|
||||
sb_hdr->first_boot_sec_id = sb->sections[0].identifier;
|
||||
sb_hdr->nr_keys = g_nr_keys;
|
||||
sb_hdr->nr_sections = sb->nr_sections;
|
||||
sb_hdr->sec_hdr_size = sizeof(struct sb_section_header_t) / BLOCK_SIZE;
|
||||
|
|
@ -237,10 +237,7 @@ static void produce_sb_header(struct sb_file_t *sb, struct sb_header_t *sb_hdr)
|
|||
if(sb->minor_version >= 1)
|
||||
memcpy(&sb_hdr->rand_pad0[2], "sgtl", 4);
|
||||
|
||||
if(sb->override_timestamp)
|
||||
sb_hdr->timestamp = sb->timestamp;
|
||||
else
|
||||
sb_hdr->timestamp = generate_timestamp();
|
||||
sb_hdr->timestamp = sb->timestamp;
|
||||
sb_hdr->product_ver = sb->product_ver;
|
||||
fix_version(&sb_hdr->product_ver);
|
||||
sb_hdr->component_ver = sb->component_ver;
|
||||
|
|
@ -261,7 +258,8 @@ static void produce_sb_section_header(struct sb_section_t *sec,
|
|||
sec_hdr->offset = sec->file_offset;
|
||||
sec_hdr->size = sec->sec_size;
|
||||
sec_hdr->flags = (sec->is_data ? 0 : SECTION_BOOTABLE)
|
||||
| (sec->is_cleartext ? SECTION_CLEARTEXT : 0);
|
||||
| (sec->is_cleartext ? SECTION_CLEARTEXT : 0)
|
||||
| sec->other_flags;
|
||||
}
|
||||
|
||||
static uint8_t instruction_checksum(struct sb_instruction_header_t *hdr)
|
||||
|
|
@ -281,7 +279,8 @@ static void produce_section_tag_cmd(struct sb_section_t *sec,
|
|||
tag->identifier = sec->identifier;
|
||||
tag->len = sec->sec_size;
|
||||
tag->flags = (sec->is_data ? 0 : SECTION_BOOTABLE)
|
||||
| (sec->is_cleartext ? SECTION_CLEARTEXT : 0);
|
||||
| (sec->is_cleartext ? SECTION_CLEARTEXT : 0)
|
||||
| sec->other_flags;
|
||||
tag->hdr.checksum = instruction_checksum(&tag->hdr);
|
||||
}
|
||||
|
||||
|
|
@ -333,6 +332,11 @@ enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *
|
|||
memset(cbc_macs[i], 0, 16);
|
||||
|
||||
fill_gaps(sb);
|
||||
if(sb->nr_sections == 0 || sb->sections[0].is_data)
|
||||
{
|
||||
cprintf(u, true, GREY, "First section of the image is not bootable, I cannot handle that.\n");
|
||||
return SB_ERROR;
|
||||
}
|
||||
compute_sb_offsets(sb, u, cprintf);
|
||||
|
||||
generate_random_data(real_key.u.key, 16);
|
||||
|
|
@ -983,6 +987,7 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, bool raw_mode, voi
|
|||
sec, size, " ", u, cprintf, err);
|
||||
if(s)
|
||||
{
|
||||
s->other_flags = sec_hdr->flags & ~SECTION_STD_MASK;
|
||||
s->is_cleartext = !encrypted;
|
||||
s->alignment = guess_alignment(pos);
|
||||
memcpy(&sb_file->sections[i], s, sizeof(struct sb_section_t));
|
||||
|
|
@ -1073,6 +1078,7 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, bool raw_mode, voi
|
|||
sec, size, " ", u, cprintf, err);
|
||||
if(s)
|
||||
{
|
||||
s->other_flags = tag->flags & ~SECTION_STD_MASK;
|
||||
s->is_cleartext = !encrypted;
|
||||
s->alignment = guess_alignment(pos);
|
||||
sb_file->sections = augment_array(sb_file->sections,
|
||||
|
|
@ -1138,6 +1144,19 @@ struct sb_file_t *sb_read_memory(void *_buf, size_t filesize, bool raw_mode, voi
|
|||
#undef print_hex
|
||||
}
|
||||
|
||||
void sb_generate_default_version(struct sb_version_t *ver)
|
||||
{
|
||||
ver->major = ver->minor = ver->revision = 0x999;
|
||||
}
|
||||
|
||||
void sb_build_default_image(struct sb_file_t *sb)
|
||||
{
|
||||
sb->minor_version = IMAGE_MINOR_VERSION;
|
||||
sb->timestamp = sb_generate_timestamp();
|
||||
sb_generate_default_version(&sb->product_ver);
|
||||
sb_generate_default_version(&sb->component_ver);
|
||||
}
|
||||
|
||||
void sb_free_instruction(struct sb_inst_t inst)
|
||||
{
|
||||
free(inst.padding);
|
||||
|
|
@ -1246,6 +1265,9 @@ void sb_dump(struct sb_file_t *file, void *u, generic_printf_t cprintf)
|
|||
printf(HEADER, "Alignment: ");
|
||||
printf(TEXT, "%d (bytes)\n", sec->alignment);
|
||||
printf(TREE, "| +-");
|
||||
printf(HEADER, "Other Flags: ");
|
||||
printf(TEXT, "%#x\n", sec->other_flags);
|
||||
printf(TREE, "| +-");
|
||||
printf(HEADER, "Instructions\n");
|
||||
for(int j = 0; j < sec->nr_insts; j++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ struct sb_key_dictionary_entry_t
|
|||
|
||||
#define SECTION_BOOTABLE (1 << 0)
|
||||
#define SECTION_CLEARTEXT (1 << 1)
|
||||
#define SECTION_STD_MASK (3 << 0)
|
||||
|
||||
#define SB_INST_NOP 0x0
|
||||
#define SB_INST_TAG 0x1
|
||||
|
|
@ -187,6 +188,7 @@ struct sb_section_t
|
|||
uint32_t identifier;
|
||||
bool is_data;
|
||||
bool is_cleartext;
|
||||
uint32_t other_flags;
|
||||
uint32_t alignment;
|
||||
// data sections are handled as one or more SB_INST_DATA virtual instruction
|
||||
int nr_insts;
|
||||
|
|
@ -204,15 +206,13 @@ struct sb_file_t
|
|||
/* override crypto IV, use with caution ! Use NULL to generate it */
|
||||
bool override_crypto_iv;
|
||||
uint8_t crypto_iv[16];
|
||||
/* override timestamp */
|
||||
bool override_timestamp;
|
||||
uint64_t timestamp; /* In microseconds since 2000/1/1 00:00:00 */
|
||||
uint8_t minor_version;
|
||||
|
||||
int nr_sections;
|
||||
uint16_t drive_tag;
|
||||
uint32_t first_boot_sec_id;
|
||||
uint16_t flags;
|
||||
uint8_t minor_version;
|
||||
struct sb_section_t *sections;
|
||||
struct sb_version_t product_ver;
|
||||
struct sb_version_t component_ver;
|
||||
|
|
@ -244,6 +244,9 @@ struct sb_file_t *sb_read_file_ex(const char *filename, size_t offset, size_t si
|
|||
struct sb_file_t *sb_read_memory(void *buffer, size_t size, bool raw_mode, void *u,
|
||||
generic_printf_t printf, enum sb_error_t *err);
|
||||
|
||||
uint64_t sb_generate_timestamp(void);
|
||||
void sb_generate_default_version(struct sb_version_t *ver);
|
||||
void sb_build_default_image(struct sb_file_t *file);
|
||||
void sb_fill_section_name(char name[5], uint32_t identifier);
|
||||
void sb_dump(struct sb_file_t *file, void *u, generic_printf_t printf);
|
||||
void sb_free_instruction(struct sb_inst_t inst);
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ static void extract_sb_section(struct sb_section_t *sec, struct cmd_file_t *cmd_
|
|||
struct cmd_section_t *db_sec = db_add_section(cmd_file, sec->identifier, sec->is_data);
|
||||
db_add_int_opt(&db_sec->opt_list, "alignment", sec->alignment);
|
||||
db_add_int_opt(&db_sec->opt_list, "cleartext", sec->is_cleartext);
|
||||
db_add_int_opt(&db_sec->opt_list, "sectionFlags", sec->other_flags);
|
||||
|
||||
if(sec->is_data)
|
||||
{
|
||||
|
|
@ -413,7 +414,6 @@ int main(int argc, char **argv)
|
|||
* garbage */
|
||||
file->override_real_key = false;
|
||||
file->override_crypto_iv = false;
|
||||
file->override_timestamp = true;
|
||||
sb_write_file(file, loopback, 0, generic_std_printf);
|
||||
}
|
||||
sb_free(file);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue