qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v5 1/2] Implement .hex file loader


From: Stefan Hajnoczi
Subject: Re: [Qemu-devel] [PATCH v5 1/2] Implement .hex file loader
Date: Mon, 23 Apr 2018 14:02:41 +0100
User-agent: Mutt/1.9.2 (2017-12-15)

On Fri, Apr 20, 2018 at 02:45:31PM +0800, Su Hang wrote:
> diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> index 26184bcd7c26..898a7af114ea 100644
> --- a/hw/arm/boot.c
> +++ b/hw/arm/boot.c
> @@ -1070,12 +1070,20 @@ static void arm_load_kernel_notify(Notifier 
> *notifier, void *data)
>          kernel_size = load_aarch64_image(info->kernel_filename,
>                                           info->loader_start, &entry, as);
>          is_linux = 1;
> -    } else if (kernel_size < 0) {
> -        /* 32-bit ARM */
> +    }
> +    if (kernel_size < 0) {

Please try the Intel HEX loader right after load_uimage_as().  In theory
it should be usable on aarch64 too, so the Intel HEX loader needs to be
called earlier.

> +        /* 32-bit ARM .hex file */
> +        entry = info->loader_start + KERNEL_LOAD_ADDR;

KERNEL_LOAD_ADDR is for Linux, can you explain why you used it for this
non-Linux image format?

> +        kernel_size =
> +            load_targphys_hex_as(info->kernel_filename, entry,
> +                                 info->ram_size - KERNEL_LOAD_ADDR, as);

The Intel HEX file may provide the entrypoint address, so the argument
should be &entry.

> +/* return size or -1 if error */
> +static size_t handle_record_type(const char *filename, HexLine *line,
> +                                 uint8_t *bin_buf, const hwaddr addr,
> +                                 size_t *total_size, uint8_t 
> *ext_linear_record,
> +                                 uint32_t *next_address_to_write,
> +                                 uint32_t *current_address,
> +                                 uint32_t *current_rom_index,
> +                                 uint32_t *rom_start_address, AddressSpace 
> *as)

There are a lot of arguments.  A struct makes the code easier to read
and prevents bugs if arguments are accidentally swapped (e.g.
next_address_to_write and current_address have the same type so the
compiler cannot warn if they are swapped):

  typedef struct {
      HexLine line;
      uint8_t *bin_buf;
      ...
  } HexParser;

static size_t handle_record_type(HexParser *parser);

> +{
> +    switch (line->record_type) {
> +    case DATA_RECORD:
> +        *current_address =
> +            (*next_address_to_write & 0xffff0000) | line->address;
> +        /* verify this is a contiguous block of memory */
> +        if (*current_address != *next_address_to_write ||
> +            *ext_linear_record == 1) {
> +            if (*ext_linear_record != 1) {
> +                return -1;
> +            }
> +            *ext_linear_record = 0;
> +            *rom_start_address = *current_address;
> +        }

What is the purpose of ext_linear_record?

Given this input file:

:10000000C0070000D1060000D1000000B1060000CA
<--- address 0010 is skipped
:100020000000000000000000000000005107000078
<--- address 0030 is skipped
:10004000EF000000F9000000030100000D010000B6
:00000001FF

ext_linear_record is 0 when the final data record is handled, so
handle_record_type() returns -1.  I don't understand why the parser
should reject this input file.

I think a rom blob should be added if data records are discontiguous.

> +
> +        /* copy from line buffer to output bin_buf */
> +        memcpy(bin_buf + *current_rom_index, line->data, line->byte_count);
> +        *current_rom_index += line->byte_count;
> +        *total_size += line->byte_count;
> +        /* Save next address to write */
> +        *next_address_to_write = *current_address + line->byte_count;
> +        break;
> +
> +    case EOF_RECORD:
> +        if (*current_rom_index != 0) {
> +            rom_add_blob(filename, bin_buf, *current_rom_index,
> +                         *current_rom_index, addr + *rom_start_address, NULL,
> +                         NULL, NULL, as, true);
> +        }
> +        return *total_size;
> +    case EXT_SEG_ADDR_RECORD:
> +    case EXT_LINEAR_ADDR_RECORD:
> +        if (line->byte_count != 2) {
> +            return -1;
> +        }
> +
> +        if (*current_rom_index != 0) {
> +            rom_add_blob(filename, bin_buf, *current_rom_index,
> +                         *current_rom_index, addr + *rom_start_address, NULL,
> +                         NULL, NULL, as, true);
> +            memset(bin_buf, 0, *current_rom_index);

This is unnecessary since bin_buf is overwritten from index 0
(*current_rom_index = 0 below) before it is read again.

> +            memset(line, 0, sizeof(HexLine));

This is unnecessary since in_process = 0 and the next ':' input
character will clear line.

> +        }
> +
> +        *current_rom_index = 0;
> +        *ext_linear_record = 1;
> +
> +        /* save next address to write,
> +         * in case of non-contiguous block of memory */
> +        *next_address_to_write =
> +            line->record_type == EXT_SEG_ADDR_RECORD

This is broken because of the memset(line, 0, sizeof(HexLine)) above.

> +                ? ((line->data[0] << 12) | (line->data[1] << 4))
> +                : ((line->data[0] << 24) | (line->data[1] << 16));
> +        break;
> +
> +    case START_SEG_ADDR_RECORD:
> +        /* nothing to do */
> +        break;
> +
> +    case START_LINEAR_ADDR_RECORD:
> +        /* nothing to do */
> +        break;

These record types specify the entry point (the starting address).
Please store the entry point into arm_boot_info->entry so that
do_cpu_reset() starts the CPU at the right address.

> +/* return size or -1 if error */
> +static size_t __parse_hex_blob(const char *filename, const hwaddr addr,

QEMU does not use double underscore function names because they are
reserved by the C standard (see 2.4 in ./HACKING and 7.1.3 in the C
standard).  Please rename this function.

> +                               uint8_t *hex_blob, off_t hex_blob_size,
> +                               uint8_t *bin_buf, AddressSpace *as)
> +{
> +    uint8_t ext_linear_record = 1; /* record non-constitutes block */

s/constitutes/contiguous/ ?

> +    uint8_t in_process = 0;        /* avoid re-enter and
> +                                    * check whether record begin with ':' */

As mentioned in previous review comments, please use bool for boolean
flags and not integer types.

> +/* return size or -1 if error */
> +int load_targphys_hex_as(const char *filename, hwaddr addr, uint64_t max_sz,
> +                         AddressSpace *as)
> +{
> +    int size;
> +
> +    size = get_image_size(filename);
> +    if (size < 0 || size > max_sz) {

Why check the size of the file?  I'm not sure why the size of the file
matters here.

> +        return -1;
> +    }
> +
> +    return parse_hex_blob(filename, addr, as);
> +}
> diff --git a/include/hw/loader.h b/include/hw/loader.h
> index 5ed3fd8ae67a..2b0cdfe56e70 100644
> --- a/include/hw/loader.h
> +++ b/include/hw/loader.h
> @@ -28,6 +28,20 @@ ssize_t load_image_size(const char *filename, void *addr, 
> size_t size);
>  int load_image_targphys_as(const char *filename,
>                             hwaddr addr, uint64_t max_sz, AddressSpace *as);
>  
> +/**load_image_targphys_hex_as:
> + * @filename: Path to the .hex file
> + * @addr: Address to load the .hex file to

Do you have a use in mind for this argument?  It seems like most callers
would pass 0 and treat the Intel HEX file addresses as absolute
addresses.

If there is no real need for this then please drop it.

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]