summaryrefslogtreecommitdiff
path: root/util/blobtool/blobtool.y
diff options
context:
space:
mode:
Diffstat (limited to 'util/blobtool/blobtool.y')
-rw-r--r--util/blobtool/blobtool.y537
1 files changed, 537 insertions, 0 deletions
diff --git a/util/blobtool/blobtool.y b/util/blobtool/blobtool.y
new file mode 100644
index 0000000000..909102d239
--- /dev/null
+++ b/util/blobtool/blobtool.y
@@ -0,0 +1,537 @@
+/*
+ * blobtool - Compiler/Decompiler for data blobs with specs
+ * Copyright (C) 2017 Damien Zammit <damien@zamaudio.com>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+%{
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+//#define YYDEBUG 1
+int yylex (void);
+void yyerror (char const *);
+
+struct field {
+ char *name;
+ unsigned int width;
+ unsigned int value;
+ struct field *next;
+};
+
+extern struct field *sym_table;
+struct field *putsym (char const *, unsigned int);
+struct field *getsym (char const *);
+
+struct field *sym_table;
+struct field *sym_table_tail;
+
+FILE* fp;
+
+/* Bit array intermediary representation */
+struct blob {
+ unsigned int bloblen;
+ unsigned char *blb;
+ unsigned short checksum;
+ unsigned char *actualblob;
+ unsigned int lenactualblob;
+};
+
+#define VALID_BIT 0x80
+#define MAX_WIDTH 32
+#define CHECKSUM_SIZE 16
+
+struct blob *binary;
+
+unsigned char* value_to_bits (unsigned int v, unsigned int w)
+{
+ unsigned int i;
+ unsigned char* bitarr;
+
+ if (w > MAX_WIDTH) w = MAX_WIDTH;
+ bitarr = (unsigned char *) malloc (w * sizeof (unsigned char));
+ memset (bitarr, 0, w);
+
+ for (i = 0; i < w; i++) {
+ bitarr[i] = VALID_BIT | ((v & (1 << i)) >> i);
+ }
+ return bitarr;
+}
+
+/* Store each bit of a bitfield in a new byte sequentially 0x80 or 0x81 */
+void append_field_to_blob (unsigned char b[], unsigned int w)
+{
+ unsigned int i, j;
+ binary->blb = (unsigned char *) realloc (binary->blb, binary->bloblen + w);
+ for (j = 0, i = binary->bloblen; i < binary->bloblen + w; i++, j++) {
+ binary->blb[i] = VALID_BIT | (b[j] & 1);
+ //fprintf (stderr, "blob[%d] = %d\n", i, binary->blb[i] & 1);
+ }
+ binary->bloblen += w;
+}
+
+void set_bitfield(char *name, unsigned int value)
+{
+ unsigned long long i;
+ struct field *bf = getsym (name);
+ if (bf) {
+ bf->value = value & 0xffffffff;
+ i = (1 << bf->width) - 1;
+ if (bf->width > 8 * sizeof (unsigned int)) {
+ fprintf(stderr, "Overflow in bitfield, truncating bits to fit\n");
+ bf->value = value & i;
+ }
+ //fprintf(stderr, "Setting `%s` = %d\n", bf->name, bf->value);
+ } else {
+ fprintf(stderr, "Can't find bitfield `%s` in spec\n", name);
+ }
+}
+
+void set_bitfield_array(char *name, unsigned int n, unsigned int value)
+{
+ unsigned int i;
+ unsigned int len = strlen (name);
+ char *namen = (char *) malloc ((len + 9) * sizeof (char));
+ for (i = 0; i < n; i++) {
+ snprintf (namen, len + 8, "%s%x", name, i);
+ set_bitfield (namen, value);
+ }
+ free(namen);
+}
+
+void create_new_bitfield(char *name, unsigned int width)
+{
+ struct field *bf;
+
+ if (!(bf = putsym (name, width))) return;
+ //fprintf(stderr, "Added bitfield `%s` : %d\n", bf->name, width);
+}
+
+void create_new_bitfields(char *name, unsigned int n, unsigned int width)
+{
+ unsigned int i;
+ unsigned int len = strlen (name);
+ char *namen = (char *) malloc ((len + 9) * sizeof (char));
+ for (i = 0; i < n; i++) {
+ snprintf (namen, len + 8, "%s%x", name, i);
+ create_new_bitfield (namen, width);
+ }
+ free(namen);
+}
+
+struct field *putsym (char const *sym_name, unsigned int w)
+{
+ if (getsym(sym_name)) {
+ fprintf(stderr, "Cannot add duplicate named bitfield `%s`\n", sym_name);
+ return 0;
+ }
+ struct field *ptr = (struct field *) malloc (sizeof (struct field));
+ ptr->name = (char *) malloc (strlen (sym_name) + 1);
+ strcpy (ptr->name, sym_name);
+ ptr->width = w;
+ ptr->value = 0;
+ ptr->next = (struct field *)0;
+ if (sym_table_tail) {
+ sym_table_tail->next = ptr;
+ } else {
+ sym_table = ptr;
+ }
+ sym_table_tail = ptr;
+ return ptr;
+}
+
+struct field *getsym (char const *sym_name)
+{
+ struct field *ptr;
+ for (ptr = sym_table; ptr != (struct field *) 0;
+ ptr = (struct field *)ptr->next) {
+ if (strcmp (ptr->name, sym_name) == 0)
+ return ptr;
+ }
+ return 0;
+}
+
+void dump_all_values (void)
+{
+ struct field *ptr;
+ for (ptr = sym_table; ptr != (struct field *) 0;
+ ptr = (struct field *)ptr->next) {
+ fprintf(stderr, "%s = %d (%d bits)\n",
+ ptr->name,
+ ptr->value,
+ ptr->width);
+ }
+}
+
+void empty_field_table(void)
+{
+ struct field *ptr;
+ struct field *ptrnext;
+
+ for (ptr = sym_table; ptr != (struct field *) 0; ptr = ptrnext) {
+ if (ptr) {
+ ptrnext = ptr->next;
+ free(ptr);
+ } else {
+ ptrnext = (struct field *) 0;
+ }
+ }
+ sym_table = 0;
+ sym_table_tail = 0;
+}
+
+void create_binary_blob (void)
+{
+ if (binary && binary->blb) {
+ free(binary->blb);
+ free(binary);
+ }
+ binary = (struct blob *) malloc (sizeof (struct blob));
+ binary->blb = (unsigned char *) malloc (sizeof (unsigned char));
+ binary->bloblen = 0;
+ binary->blb[0] = VALID_BIT;
+}
+
+void interpret_next_blob_value (struct field *f)
+{
+ int i;
+ unsigned int v = 0;
+
+ if (binary->bloblen >= binary->lenactualblob * 8) {
+ f->value = 0;
+ return;
+ }
+
+ for (i = 0; i < f->width; i++) {
+ v |= (binary->blb[binary->bloblen++] & 1) << i;
+ }
+
+ f->value = v;
+}
+
+/* {}%BIN -> {} */
+void generate_setter_bitfields(unsigned char *bin)
+{
+ unsigned int i;
+ struct field *ptr;
+
+ /* Convert bytes to bit array */
+ for (i = 0; i < binary->lenactualblob; i++) {
+ append_field_to_blob (value_to_bits(bin[i], 8), 8);
+ }
+
+ /* Reset blob position to zero */
+ binary->bloblen = 0;
+
+ fprintf (fp, "# AUTOGENERATED SETTER BY BLOBTOOL\n{\n");
+
+ /* Traverse spec and output bitfield setters based on blob values */
+ for (ptr = sym_table; ptr != (struct field *) 0; ptr = ptr->next) {
+
+ interpret_next_blob_value(ptr);
+ fprintf (fp, "\t\"%s\" = 0x%x,\n", ptr->name, ptr->value);
+ }
+ fseek(fp, -2, SEEK_CUR);
+ fprintf (fp, "\n}\n");
+}
+
+void generate_binary_with_gbe_checksum(void)
+{
+ int i;
+ unsigned short checksum;
+
+ /* traverse spec, push to blob and add up for checksum */
+ struct field *ptr;
+ unsigned int uptochksum = 0;
+ for (ptr = sym_table; ptr != (struct field *) 0; ptr = ptr->next) {
+ if (strcmp (ptr->name, "checksum_gbe") == 0) {
+ /* Stop traversing because we hit checksum */
+ ptr = ptr->next;
+ break;
+ }
+ append_field_to_blob (
+ value_to_bits(ptr->value, ptr->width),
+ ptr->width);
+ uptochksum += ptr->width;
+ }
+
+ /* deserialize bits of blob up to checksum */
+ for (i = 0; i < uptochksum; i += 8) {
+ unsigned char byte = (((binary->blb[i+0] & 1) << 0)
+ | ((binary->blb[i+1] & 1) << 1)
+ | ((binary->blb[i+2] & 1) << 2)
+ | ((binary->blb[i+3] & 1) << 3)
+ | ((binary->blb[i+4] & 1) << 4)
+ | ((binary->blb[i+5] & 1) << 5)
+ | ((binary->blb[i+6] & 1) << 6)
+ | ((binary->blb[i+7] & 1) << 7)
+ );
+ fprintf(fp, "%c", byte);
+
+ /* incremental 16 bit checksum */
+ if ((i % 16) < 8) {
+ binary->checksum += byte;
+ } else {
+ binary->checksum += byte << 8;
+ }
+ }
+
+ checksum = (0xbaba - binary->checksum) & 0xffff;
+
+ /* Now write checksum */
+ set_bitfield ("checksum_gbe", checksum);
+
+ fprintf(fp, "%c", checksum & 0xff);
+ fprintf(fp, "%c", (checksum & 0xff00) >> 8);
+
+ append_field_to_blob (value_to_bits(checksum, 16), 16);
+
+ for (; ptr != (struct field *) 0; ptr = ptr->next) {
+ append_field_to_blob (
+ value_to_bits(ptr->value, ptr->width), ptr->width);
+ }
+
+ /* deserialize rest of blob past checksum */
+ for (i = uptochksum + CHECKSUM_SIZE; i < binary->bloblen; i += 8) {
+ unsigned char byte = (((binary->blb[i+0] & 1) << 0)
+ | ((binary->blb[i+1] & 1) << 1)
+ | ((binary->blb[i+2] & 1) << 2)
+ | ((binary->blb[i+3] & 1) << 3)
+ | ((binary->blb[i+4] & 1) << 4)
+ | ((binary->blb[i+5] & 1) << 5)
+ | ((binary->blb[i+6] & 1) << 6)
+ | ((binary->blb[i+7] & 1) << 7)
+ );
+ fprintf(fp, "%c", byte);
+ }
+}
+
+/* {}{} -> BIN */
+void generate_binary(void)
+{
+ unsigned int i;
+ struct field *ptr;
+
+ if (binary->bloblen % 8) {
+ fprintf (stderr, "ERROR: Spec must be multiple of 8 bits wide\n");
+ exit (1);
+ }
+
+ if (getsym ("checksum_gbe")) {
+ generate_binary_with_gbe_checksum();
+ return;
+ }
+
+ /* traverse spec, push to blob */
+ for (ptr = sym_table; ptr != (struct field *) 0; ptr = ptr->next) {
+ append_field_to_blob (
+ value_to_bits(ptr->value, ptr->width),
+ ptr->width);
+ }
+
+ /* deserialize bits of blob */
+ for (i = 0; i < binary->bloblen; i += 8) {
+ unsigned char byte = (((binary->blb[i+0] & 1) << 0)
+ | ((binary->blb[i+1] & 1) << 1)
+ | ((binary->blb[i+2] & 1) << 2)
+ | ((binary->blb[i+3] & 1) << 3)
+ | ((binary->blb[i+4] & 1) << 4)
+ | ((binary->blb[i+5] & 1) << 5)
+ | ((binary->blb[i+6] & 1) << 6)
+ | ((binary->blb[i+7] & 1) << 7)
+ );
+ fprintf(fp, "%c", byte);
+ }
+}
+
+%}
+
+%union
+{
+ char *str;
+ unsigned int u32;
+ unsigned int *u32array;
+ unsigned char u8;
+ unsigned char *u8array;
+}
+
+%token <str> name
+%token <u32> val
+%token <u32array> vals
+%token <u8> hexbyte
+%token <u8array> binblob
+%token <u8> eof
+
+%left '%'
+%left '{' '}'
+%left ','
+%left ':'
+%left '='
+
+%%
+
+input:
+ /* empty */
+| input spec setter eof { empty_field_table(); YYACCEPT;}
+| input spec blob { fprintf (stderr, "Parsed all bytes\n");
+ empty_field_table(); YYACCEPT;}
+;
+
+blob:
+ '%' eof { generate_setter_bitfields(binary->actualblob); }
+;
+
+spec:
+ '{' '}' { fprintf (stderr, "No spec\n"); }
+| '{' specmembers '}' { fprintf (stderr, "Parsed all spec\n");
+ create_binary_blob(); }
+;
+
+specmembers:
+ specpair
+| specpair ',' specmembers
+;
+
+specpair:
+ name ':' val { create_new_bitfield($1, $3); }
+| name '[' val ']' ':' val { create_new_bitfields($1, $3, $6); }
+;
+
+setter:
+ '{' '}' { fprintf (stderr, "No values\n"); }
+| '{' valuemembers '}' { fprintf (stderr, "Parsed all values\n");
+ generate_binary(); }
+;
+
+valuemembers:
+ setpair
+| setpair ',' valuemembers
+;
+
+setpair:
+ name '=' val { set_bitfield($1, $3); }
+| name '[' val ']' '=' val { set_bitfield_array($1, $3, $6); }
+;
+
+%%
+
+/* Called by yyparse on error. */
+void yyerror (char const *s)
+{
+ fprintf (stderr, "yyerror: %s\n", s);
+}
+
+/* Declarations */
+void set_input_string(char* in);
+
+/* This function parses a string */
+int parse_string(unsigned char* in) {
+ set_input_string ((char *)in);
+ return yyparse ();
+}
+
+int main (int argc, char *argv[])
+{
+ unsigned int lenspec, lensetter;
+ unsigned char *parsestring;
+ unsigned char c;
+ unsigned int pos = 0;
+ int ret = 0;
+
+#if YYDEBUG == 1
+ yydebug = 1;
+#endif
+ create_binary_blob();
+ binary->lenactualblob = 0;
+
+ if (argc == 4 && strcmp(argv[1], "-d") != 0) {
+ /* Compile mode */
+
+ /* Load Spec */
+ fp = fopen(argv[1], "r");
+ fseek(fp, 0, SEEK_END);
+ lenspec = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ parsestring = (unsigned char *) malloc (lenspec);
+ if (!parsestring) {
+ printf("Out of memory\n");
+ exit(1);
+ }
+ fread(parsestring, 1, lenspec, fp);
+ fclose(fp);
+
+ /* Load Setter */
+ fp = fopen(argv[2], "r");
+ fseek(fp, 0, SEEK_END);
+ lensetter = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ parsestring = (unsigned char *) realloc (parsestring,
+ lenspec + lensetter);
+ if (!parsestring) {
+ printf("Out of memory\n");
+ exit(1);
+ }
+ fread(parsestring + lenspec, 1, lensetter, fp);
+ fclose(fp);
+
+ /* Open output and parse string - output to fp */
+ fp = fopen(argv[3], "wb");
+ ret = parse_string(parsestring);
+ free(parsestring);
+ } else if (argc == 5 && strcmp (argv[1], "-d") == 0) {
+ /* Decompile mode */
+
+ /* Load Spec */
+ fp = fopen(argv[2], "r");
+ fseek(fp, 0, SEEK_END);
+ lenspec = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ parsestring = (unsigned char *) malloc (lenspec + 1);
+ fread(parsestring, 1, lenspec, fp);
+ if (!parsestring) {
+ printf("Out of memory\n");
+ exit(1);
+ }
+ fclose(fp);
+
+ /* Add binary read trigger token */
+ parsestring[lenspec] = '%';
+
+ /* Load Actual Binary */
+ fp = fopen(argv[3], "rb");
+ fseek(fp, 0, SEEK_END);
+ binary->lenactualblob = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ binary->actualblob = (unsigned char *) malloc (binary->lenactualblob);
+ if (!binary->actualblob) {
+ printf("Out of memory\n");
+ exit(1);
+ }
+ fread(binary->actualblob, 1, binary->lenactualblob, fp);
+ fclose(fp);
+
+ /* Open output and parse - output to fp */
+ fp = fopen(argv[4], "w");
+ ret = parse_string(parsestring);
+ free(parsestring);
+ free(binary->actualblob);
+ fclose(fp);
+ } else {
+ printf("Usage: Compile mode\n\n");
+ printf(" blobtool spec setter binaryoutput\n");
+ printf(" (file) (file) (file)\n");
+ printf(" OR : Decompile mode\n\n");
+ printf(" blobtool -d spec binary setteroutput\n");
+ }
+ return ret;
+}