diff mbox series

[01/20] loader.c: Add support Motrola S-record format.

Message ID 20200827123859.81793-2-ysato@users.sourceforge.jp
State New
Headers show
Series [01/20] loader.c: Add support Motrola S-record format. | expand

Commit Message

Yoshinori Sato Aug. 27, 2020, 12:38 p.m. UTC
Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 include/hw/loader.h |  14 +++
 hw/core/loader.c    | 208 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 222 insertions(+)

Comments

Philippe Mathieu-Daudé Oct. 25, 2020, 12:43 a.m. UTC | #1
Cc'ing Alistair and Su Hang.

On 8/27/20 2:38 PM, Yoshinori Sato wrote:
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>   include/hw/loader.h |  14 +++
>   hw/core/loader.c    | 208 ++++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 222 insertions(+)
> 
> diff --git a/include/hw/loader.h b/include/hw/loader.h
> index a9eeea3952..6f1fb62ded 100644
> --- a/include/hw/loader.h
> +++ b/include/hw/loader.h
> @@ -55,6 +55,20 @@ int load_image_targphys_as(const char *filename,
>    */
>   int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as);
>   
> +/*
> + * load_targphys_srec_as:
> + * @filename: Path to the .hex file
> + * @entry: Store the entry point given by the .hex file
> + * @as: The AddressSpace to load the .hex file to. The value of
> + *      address_space_memory is used if nothing is supplied here.
> + *
> + * Load a fixed .srec file into memory.
> + *
> + * Returns the size of the loaded .hex file on success, -1 otherwise.
> + */
> +int load_targphys_srec_as(const char *filename,
> +                          hwaddr *entry, AddressSpace *as);
> +
>   /** load_image_targphys:
>    * Same as load_image_targphys_as(), but doesn't allow the caller to specify
>    * an AddressSpace.
> diff --git a/hw/core/loader.c b/hw/core/loader.c
> index 8bbb1797a4..6964b04ec7 100644
> --- a/hw/core/loader.c
> +++ b/hw/core/loader.c
> @@ -1618,3 +1618,211 @@ int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as)
>       g_free(hex_blob);
>       return total_size;
>   }
> +
> +typedef enum {
> +    SREC_SOH,
> +    SREC_TYPE,
> +    SREC_LEN,
> +    SREC_ADDR,
> +    SREC_DATA,
> +    SREC_SKIP,
> +    SREC_SUM,
> +} srec_state;
> +
> +typedef struct {
> +    srec_state state;
> +    int nibble;
> +    int total_size;
> +    uint32_t address;
> +    uint32_t topaddr;
> +    uint32_t bufremain;
> +    int length;
> +    int addr_len;
> +    int record_type;
> +    uint8_t byte;
> +    uint8_t data[DATA_FIELD_MAX_LEN];
> +    uint8_t *datap;
> +    uint8_t *bufptr;
> +    uint8_t sum;
> +} SrecLine;
> +
> +static bool parse_srec_line(SrecLine *line, char c)
> +{
> +    if (!g_ascii_isxdigit(c)) {
> +        return false;
> +    }
> +    line->byte <<= 4;
> +    line->byte |= g_ascii_xdigit_value(c);
> +    line->nibble++;
> +    if (line->nibble == 2) {
> +        line->nibble = 0;
> +        line->length--;
> +        line->sum += line->byte;
> +        switch (line->state) {
> +        case SREC_SOH:
> +        case SREC_TYPE:
> +            /* first 2chars ignore parse */
> +            break;
> +        case SREC_LEN:
> +            line->sum = line->length = line->byte;
> +            if (line->addr_len > 0) {
> +                line->state = SREC_ADDR;
> +                line->address = 0;
> +            } else {
> +                line->state = SREC_SKIP;
> +            }
> +            break;
> +        case SREC_ADDR:
> +            line->address <<= 8;
> +            line->address |= line->byte;
> +            if (--line->addr_len == 0) {
> +                if (line->length > 1) {
> +                    if (line->record_type != 0) {
> +                        line->state = SREC_DATA;
> +                    } else {
> +                        line->state = SREC_SKIP;
> +                    }
> +                    line->datap = line->data;
> +                } else {
> +                    line->state = SREC_SUM;
> +                }
> +            }
> +            break;
> +        case SREC_DATA:
> +            *line->datap++ = line->byte;
> +            /* fail through */
> +        case SREC_SKIP:
> +            if (line->length == 1) {
> +                line->state = SREC_SUM;
> +            }
> +            break;
> +        case SREC_SUM:
> +            if ((line->sum & 0xff) != 0xff) {
> +                return false;
> +            }
> +        }
> +    }
> +    return true;
> +}
> +
> +#define SRECBUFSIZE 0x40000
> +
> +/* return size or -1 if error */
> +static int parse_srec_blob(const char *filename, hwaddr *addr,
> +                           uint8_t *hex_blob, size_t hex_blob_size,
> +                           AddressSpace *as)
> +{
> +    SrecLine line;
> +    size_t len;
> +    int total_len = 0;
> +    uint8_t *end = hex_blob + hex_blob_size;
> +    rom_transaction_begin();
> +    line.state = SREC_SOH;
> +    line.bufptr = g_malloc(SRECBUFSIZE);
> +    line.bufremain = SRECBUFSIZE;
> +    line.topaddr = UINT32_MAX;
> +    for (; hex_blob < end; ++hex_blob) {
> +        switch (*hex_blob) {
> +        case '\r':
> +        case '\n':
> +            if (line.state == SREC_SUM) {
> +                switch (line.record_type) {
> +                case 1:
> +                case 2:
> +                case 3:
> +                    len = line.datap - line.data;
> +                    if (line.topaddr == UINT32_MAX) {
> +                        line.topaddr = line.address;
> +                    }
> +                    if (line.bufremain < len || line.address < line.topaddr) {
> +                        rom_add_blob_fixed_as(filename, line.bufptr,
> +                                              SRECBUFSIZE - line.bufremain,
> +                                              line.topaddr, as);
> +                        line.topaddr = line.address;
> +                        line.bufremain = SRECBUFSIZE;
> +                    }
> +                    memcpy(line.bufptr + (line.address  - line.topaddr),
> +                           line.data, len);
> +                    line.bufremain -= len;
> +                    total_len += len;
> +                    break;
> +                case 7:
> +                case 8:
> +                case 9:
> +                    *addr = line.address;
> +                    break;
> +                }
> +                line.state = SREC_SOH;
> +            }
> +            break;
> +        /* start of a new record. */
> +        case 'S':
> +            if (line.state != SREC_SOH) {
> +                total_len = -1;
> +                goto out;
> +            }
> +            line.state = SREC_TYPE;
> +            break;
> +        /* decoding lines */
> +        default:
> +            if (line.state == SREC_TYPE) {
> +                if (g_ascii_isdigit(*hex_blob)) {
> +                    line.record_type = g_ascii_digit_value(*hex_blob);
> +                    switch (line.record_type) {
> +                    case 1:
> +                    case 2:
> +                    case 3:
> +                        line.addr_len = 1 + line.record_type;
> +                        break;
> +                    case 0:
> +                    case 5:
> +                        line.addr_len = 2;
> +                        break;
> +                    case 7:
> +                    case 8:
> +                    case 9:
> +                        line.addr_len = 11 - line.record_type;
> +                        break;
> +                    default:
> +                        line.addr_len = 0;
> +                    }
> +                }
> +                line.state = SREC_LEN;
> +                line.nibble = 0;
> +            } else {
> +                if (!parse_srec_line(&line, *hex_blob)) {
> +                    total_len = -1;
> +                    goto out;
> +                }
> +            }
> +            break;
> +        }
> +    }
> +    if (line.bufremain < SRECBUFSIZE) {
> +        rom_add_blob_fixed_as(filename, line.bufptr,
> +                              SRECBUFSIZE - line.bufremain,
> +                              line.topaddr, as);
> +    }
> +out:
> +    rom_transaction_end(total_len != -1);
> +    g_free(line.bufptr);
> +    return total_len;
> +}
> +
> +/* return size or -1 if error */
> +int load_targphys_srec_as(const char *filename, hwaddr *entry, AddressSpace *as)
> +{
> +    gsize hex_blob_size;
> +    gchar *hex_blob;
> +    int total_size = 0;
> +
> +    if (!g_file_get_contents(filename, &hex_blob, &hex_blob_size, NULL)) {
> +        return -1;
> +    }
> +
> +    total_size = parse_srec_blob(filename, entry, (uint8_t *)hex_blob,
> +                                 hex_blob_size, as);
> +
> +    g_free(hex_blob);
> +    return total_size;
> +}
>
Alistair Francis Oct. 27, 2020, 9:05 p.m. UTC | #2
On Thu, Aug 27, 2020 at 5:39 AM Yoshinori Sato
<ysato@users.sourceforge.jp> wrote:
>
> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
> ---
>  include/hw/loader.h |  14 +++
>  hw/core/loader.c    | 208 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 222 insertions(+)
>
> diff --git a/include/hw/loader.h b/include/hw/loader.h
> index a9eeea3952..6f1fb62ded 100644
> --- a/include/hw/loader.h
> +++ b/include/hw/loader.h
> @@ -55,6 +55,20 @@ int load_image_targphys_as(const char *filename,
>   */
>  int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as);
>
> +/*
> + * load_targphys_srec_as:
> + * @filename: Path to the .hex file
> + * @entry: Store the entry point given by the .hex file
> + * @as: The AddressSpace to load the .hex file to. The value of
> + *      address_space_memory is used if nothing is supplied here.
> + *
> + * Load a fixed .srec file into memory.
> + *
> + * Returns the size of the loaded .hex file on success, -1 otherwise.
> + */
> +int load_targphys_srec_as(const char *filename,
> +                          hwaddr *entry, AddressSpace *as);
> +
>  /** load_image_targphys:
>   * Same as load_image_targphys_as(), but doesn't allow the caller to specify
>   * an AddressSpace.
> diff --git a/hw/core/loader.c b/hw/core/loader.c
> index 8bbb1797a4..6964b04ec7 100644
> --- a/hw/core/loader.c
> +++ b/hw/core/loader.c
> @@ -1618,3 +1618,211 @@ int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as)
>      g_free(hex_blob);
>      return total_size;
>  }
> +
> +typedef enum {
> +    SREC_SOH,
> +    SREC_TYPE,
> +    SREC_LEN,
> +    SREC_ADDR,
> +    SREC_DATA,
> +    SREC_SKIP,
> +    SREC_SUM,
> +} srec_state;
> +
> +typedef struct {
> +    srec_state state;
> +    int nibble;
> +    int total_size;
> +    uint32_t address;
> +    uint32_t topaddr;
> +    uint32_t bufremain;
> +    int length;
> +    int addr_len;
> +    int record_type;
> +    uint8_t byte;
> +    uint8_t data[DATA_FIELD_MAX_LEN];
> +    uint8_t *datap;
> +    uint8_t *bufptr;
> +    uint8_t sum;
> +} SrecLine;
> +
> +static bool parse_srec_line(SrecLine *line, char c)
> +{
> +    if (!g_ascii_isxdigit(c)) {
> +        return false;
> +    }
> +    line->byte <<= 4;
> +    line->byte |= g_ascii_xdigit_value(c);
> +    line->nibble++;
> +    if (line->nibble == 2) {
> +        line->nibble = 0;
> +        line->length--;
> +        line->sum += line->byte;
> +        switch (line->state) {
> +        case SREC_SOH:
> +        case SREC_TYPE:
> +            /* first 2chars ignore parse */
> +            break;
> +        case SREC_LEN:
> +            line->sum = line->length = line->byte;
> +            if (line->addr_len > 0) {
> +                line->state = SREC_ADDR;
> +                line->address = 0;
> +            } else {
> +                line->state = SREC_SKIP;
> +            }
> +            break;
> +        case SREC_ADDR:
> +            line->address <<= 8;
> +            line->address |= line->byte;
> +            if (--line->addr_len == 0) {
> +                if (line->length > 1) {
> +                    if (line->record_type != 0) {
> +                        line->state = SREC_DATA;
> +                    } else {
> +                        line->state = SREC_SKIP;
> +                    }
> +                    line->datap = line->data;
> +                } else {
> +                    line->state = SREC_SUM;
> +                }
> +            }
> +            break;
> +        case SREC_DATA:
> +            *line->datap++ = line->byte;
> +            /* fail through */

s/fail/fall/g

> +        case SREC_SKIP:
> +            if (line->length == 1) {
> +                line->state = SREC_SUM;
> +            }
> +            break;
> +        case SREC_SUM:
> +            if ((line->sum & 0xff) != 0xff) {
> +                return false;
> +            }
> +        }
> +    }
> +    return true;
> +}
> +
> +#define SRECBUFSIZE 0x40000
> +
> +/* return size or -1 if error */
> +static int parse_srec_blob(const char *filename, hwaddr *addr,
> +                           uint8_t *hex_blob, size_t hex_blob_size,
> +                           AddressSpace *as)
> +{
> +    SrecLine line;
> +    size_t len;
> +    int total_len = 0;
> +    uint8_t *end = hex_blob + hex_blob_size;
> +    rom_transaction_begin();
> +    line.state = SREC_SOH;
> +    line.bufptr = g_malloc(SRECBUFSIZE);
> +    line.bufremain = SRECBUFSIZE;
> +    line.topaddr = UINT32_MAX;
> +    for (; hex_blob < end; ++hex_blob) {
> +        switch (*hex_blob) {
> +        case '\r':
> +        case '\n':
> +            if (line.state == SREC_SUM) {
> +                switch (line.record_type) {

What if a file starts with a new line, won't line.record_type be invalid?

Alistair

> +                case 1:
> +                case 2:
> +                case 3:
> +                    len = line.datap - line.data;
> +                    if (line.topaddr == UINT32_MAX) {
> +                        line.topaddr = line.address;
> +                    }
> +                    if (line.bufremain < len || line.address < line.topaddr) {
> +                        rom_add_blob_fixed_as(filename, line.bufptr,
> +                                              SRECBUFSIZE - line.bufremain,
> +                                              line.topaddr, as);
> +                        line.topaddr = line.address;
> +                        line.bufremain = SRECBUFSIZE;
> +                    }
> +                    memcpy(line.bufptr + (line.address  - line.topaddr),
> +                           line.data, len);
> +                    line.bufremain -= len;
> +                    total_len += len;
> +                    break;
> +                case 7:
> +                case 8:
> +                case 9:
> +                    *addr = line.address;
> +                    break;
> +                }
> +                line.state = SREC_SOH;
> +            }
> +            break;
> +        /* start of a new record. */
> +        case 'S':
> +            if (line.state != SREC_SOH) {
> +                total_len = -1;
> +                goto out;
> +            }
> +            line.state = SREC_TYPE;
> +            break;
> +        /* decoding lines */
> +        default:
> +            if (line.state == SREC_TYPE) {
> +                if (g_ascii_isdigit(*hex_blob)) {
> +                    line.record_type = g_ascii_digit_value(*hex_blob);
> +                    switch (line.record_type) {
> +                    case 1:
> +                    case 2:
> +                    case 3:
> +                        line.addr_len = 1 + line.record_type;
> +                        break;
> +                    case 0:
> +                    case 5:
> +                        line.addr_len = 2;
> +                        break;
> +                    case 7:
> +                    case 8:
> +                    case 9:
> +                        line.addr_len = 11 - line.record_type;
> +                        break;
> +                    default:
> +                        line.addr_len = 0;
> +                    }
> +                }
> +                line.state = SREC_LEN;
> +                line.nibble = 0;
> +            } else {
> +                if (!parse_srec_line(&line, *hex_blob)) {
> +                    total_len = -1;
> +                    goto out;
> +                }
> +            }
> +            break;
> +        }
> +    }
> +    if (line.bufremain < SRECBUFSIZE) {
> +        rom_add_blob_fixed_as(filename, line.bufptr,
> +                              SRECBUFSIZE - line.bufremain,
> +                              line.topaddr, as);
> +    }
> +out:
> +    rom_transaction_end(total_len != -1);
> +    g_free(line.bufptr);
> +    return total_len;
> +}
> +
> +/* return size or -1 if error */
> +int load_targphys_srec_as(const char *filename, hwaddr *entry, AddressSpace *as)
> +{
> +    gsize hex_blob_size;
> +    gchar *hex_blob;
> +    int total_size = 0;
> +
> +    if (!g_file_get_contents(filename, &hex_blob, &hex_blob_size, NULL)) {
> +        return -1;
> +    }
> +
> +    total_size = parse_srec_blob(filename, entry, (uint8_t *)hex_blob,
> +                                 hex_blob_size, as);
> +
> +    g_free(hex_blob);
> +    return total_size;
> +}
> --
> 2.20.1
>
>
diff mbox series

Patch

diff --git a/include/hw/loader.h b/include/hw/loader.h
index a9eeea3952..6f1fb62ded 100644
--- a/include/hw/loader.h
+++ b/include/hw/loader.h
@@ -55,6 +55,20 @@  int load_image_targphys_as(const char *filename,
  */
 int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as);
 
+/*
+ * load_targphys_srec_as:
+ * @filename: Path to the .hex file
+ * @entry: Store the entry point given by the .hex file
+ * @as: The AddressSpace to load the .hex file to. The value of
+ *      address_space_memory is used if nothing is supplied here.
+ *
+ * Load a fixed .srec file into memory.
+ *
+ * Returns the size of the loaded .hex file on success, -1 otherwise.
+ */
+int load_targphys_srec_as(const char *filename,
+                          hwaddr *entry, AddressSpace *as);
+
 /** load_image_targphys:
  * Same as load_image_targphys_as(), but doesn't allow the caller to specify
  * an AddressSpace.
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 8bbb1797a4..6964b04ec7 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -1618,3 +1618,211 @@  int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as)
     g_free(hex_blob);
     return total_size;
 }
+
+typedef enum {
+    SREC_SOH,
+    SREC_TYPE,
+    SREC_LEN,
+    SREC_ADDR,
+    SREC_DATA,
+    SREC_SKIP,
+    SREC_SUM,
+} srec_state;
+
+typedef struct {
+    srec_state state;
+    int nibble;
+    int total_size;
+    uint32_t address;
+    uint32_t topaddr;
+    uint32_t bufremain;
+    int length;
+    int addr_len;
+    int record_type;
+    uint8_t byte;
+    uint8_t data[DATA_FIELD_MAX_LEN];
+    uint8_t *datap;
+    uint8_t *bufptr;
+    uint8_t sum;
+} SrecLine;
+
+static bool parse_srec_line(SrecLine *line, char c)
+{
+    if (!g_ascii_isxdigit(c)) {
+        return false;
+    }
+    line->byte <<= 4;
+    line->byte |= g_ascii_xdigit_value(c);
+    line->nibble++;
+    if (line->nibble == 2) {
+        line->nibble = 0;
+        line->length--;
+        line->sum += line->byte;
+        switch (line->state) {
+        case SREC_SOH:
+        case SREC_TYPE:
+            /* first 2chars ignore parse */
+            break;
+        case SREC_LEN:
+            line->sum = line->length = line->byte;
+            if (line->addr_len > 0) {
+                line->state = SREC_ADDR;
+                line->address = 0;
+            } else {
+                line->state = SREC_SKIP;
+            }
+            break;
+        case SREC_ADDR:
+            line->address <<= 8;
+            line->address |= line->byte;
+            if (--line->addr_len == 0) {
+                if (line->length > 1) {
+                    if (line->record_type != 0) {
+                        line->state = SREC_DATA;
+                    } else {
+                        line->state = SREC_SKIP;
+                    }
+                    line->datap = line->data;
+                } else {
+                    line->state = SREC_SUM;
+                }
+            }
+            break;
+        case SREC_DATA:
+            *line->datap++ = line->byte;
+            /* fail through */
+        case SREC_SKIP:
+            if (line->length == 1) {
+                line->state = SREC_SUM;
+            }
+            break;
+        case SREC_SUM:
+            if ((line->sum & 0xff) != 0xff) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+#define SRECBUFSIZE 0x40000
+
+/* return size or -1 if error */
+static int parse_srec_blob(const char *filename, hwaddr *addr,
+                           uint8_t *hex_blob, size_t hex_blob_size,
+                           AddressSpace *as)
+{
+    SrecLine line;
+    size_t len;
+    int total_len = 0;
+    uint8_t *end = hex_blob + hex_blob_size;
+    rom_transaction_begin();
+    line.state = SREC_SOH;
+    line.bufptr = g_malloc(SRECBUFSIZE);
+    line.bufremain = SRECBUFSIZE;
+    line.topaddr = UINT32_MAX;
+    for (; hex_blob < end; ++hex_blob) {
+        switch (*hex_blob) {
+        case '\r':
+        case '\n':
+            if (line.state == SREC_SUM) {
+                switch (line.record_type) {
+                case 1:
+                case 2:
+                case 3:
+                    len = line.datap - line.data;
+                    if (line.topaddr == UINT32_MAX) {
+                        line.topaddr = line.address;
+                    }
+                    if (line.bufremain < len || line.address < line.topaddr) {
+                        rom_add_blob_fixed_as(filename, line.bufptr,
+                                              SRECBUFSIZE - line.bufremain,
+                                              line.topaddr, as);
+                        line.topaddr = line.address;
+                        line.bufremain = SRECBUFSIZE;
+                    }
+                    memcpy(line.bufptr + (line.address  - line.topaddr),
+                           line.data, len);
+                    line.bufremain -= len;
+                    total_len += len;
+                    break;
+                case 7:
+                case 8:
+                case 9:
+                    *addr = line.address;
+                    break;
+                }
+                line.state = SREC_SOH;
+            }
+            break;
+        /* start of a new record. */
+        case 'S':
+            if (line.state != SREC_SOH) {
+                total_len = -1;
+                goto out;
+            }
+            line.state = SREC_TYPE;
+            break;
+        /* decoding lines */
+        default:
+            if (line.state == SREC_TYPE) {
+                if (g_ascii_isdigit(*hex_blob)) {
+                    line.record_type = g_ascii_digit_value(*hex_blob);
+                    switch (line.record_type) {
+                    case 1:
+                    case 2:
+                    case 3:
+                        line.addr_len = 1 + line.record_type;
+                        break;
+                    case 0:
+                    case 5:
+                        line.addr_len = 2;
+                        break;
+                    case 7:
+                    case 8:
+                    case 9:
+                        line.addr_len = 11 - line.record_type;
+                        break;
+                    default:
+                        line.addr_len = 0;
+                    }
+                }
+                line.state = SREC_LEN;
+                line.nibble = 0;
+            } else {
+                if (!parse_srec_line(&line, *hex_blob)) {
+                    total_len = -1;
+                    goto out;
+                }
+            }
+            break;
+        }
+    }
+    if (line.bufremain < SRECBUFSIZE) {
+        rom_add_blob_fixed_as(filename, line.bufptr,
+                              SRECBUFSIZE - line.bufremain,
+                              line.topaddr, as);
+    }
+out:
+    rom_transaction_end(total_len != -1);
+    g_free(line.bufptr);
+    return total_len;
+}
+
+/* return size or -1 if error */
+int load_targphys_srec_as(const char *filename, hwaddr *entry, AddressSpace *as)
+{
+    gsize hex_blob_size;
+    gchar *hex_blob;
+    int total_size = 0;
+
+    if (!g_file_get_contents(filename, &hex_blob, &hex_blob_size, NULL)) {
+        return -1;
+    }
+
+    total_size = parse_srec_blob(filename, entry, (uint8_t *)hex_blob,
+                                 hex_blob_size, as);
+
+    g_free(hex_blob);
+    return total_size;
+}