@@ -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) {
+ /* 32-bit ARM .hex file */
+ entry = info->loader_start + KERNEL_LOAD_ADDR;
+ kernel_size =
+ load_targphys_hex_as(info->kernel_filename, entry,
+ info->ram_size - KERNEL_LOAD_ADDR, as);
+ }
+ if (kernel_size < 0) {
+ /* 32-bit ARM binary file */
entry = info->loader_start + KERNEL_LOAD_ADDR;
- kernel_size = load_image_targphys_as(info->kernel_filename, entry,
- info->ram_size - KERNEL_LOAD_ADDR,
- as);
+ kernel_size =
+ load_image_targphys_as(info->kernel_filename, entry,
+ info->ram_size - KERNEL_LOAD_ADDR, as);
is_linux = 1;
}
if (kernel_size < 0) {
@@ -1286,3 +1286,256 @@ void hmp_info_roms(Monitor *mon, const QDict *qdict)
}
}
}
+
+typedef enum HexRecord HexRecord;
+enum HexRecord {
+ DATA_RECORD = 0,
+ EOF_RECORD,
+ EXT_SEG_ADDR_RECORD,
+ START_SEG_ADDR_RECORD,
+ EXT_LINEAR_ADDR_RECORD,
+ START_LINEAR_ADDR_RECORD,
+};
+
+#define DATA_FIELD_MAX_LEN 0xff
+#define LEN_EXCEPT_DATA 0x5
+/* 0x5 = sizeof(byte_count) + sizeof(address) + sizeof(record_type) +
+ * sizeof(checksum) */
+typedef struct HexLine HexLine;
+struct HexLine {
+ uint8_t byte_count;
+ uint16_t address;
+ uint8_t record_type;
+ uint8_t data[DATA_FIELD_MAX_LEN];
+ uint8_t checksum;
+};
+
+/* 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)
+{
+ 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;
+ }
+
+ /* 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);
+ memset(line, 0, sizeof(HexLine));
+ }
+
+ *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
+ ? ((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;
+
+ default:
+ return -1;
+ }
+ return *total_size;
+}
+
+/* return 0 or -1 if error */
+static size_t parse_record(HexLine *line, uint8_t c, const uint32_t idx,
+ uint8_t *our_checksum, const uint8_t in_process)
+{
+ /*+-------+---------------+-------+---------------------+--------+
+ *| byte | |record | | |
+ *| count | address | type | data |checksum|
+ *+-------+---------------+-------+---------------------+--------+
+ *^ ^ ^ ^ ^ ^
+ *|1 byte | 2 bytes |1 byte | 0-255 bytes | 1 byte |
+ */
+ uint8_t value = 0;
+
+ /* ignore space */
+ if (g_ascii_isspace(c)) {
+ return 0;
+ }
+ if (!g_ascii_isxdigit(c) || in_process == 0) {
+ return -1;
+ }
+ value = g_ascii_xdigit_value(c);
+ value = idx & 0x1 ? value & 0xf : value << 4;
+ if (idx < 2) {
+ line->byte_count |= value;
+ } else if (2 <= idx && idx < 6) {
+ line->address <<= 4;
+ line->address += g_ascii_xdigit_value(c);
+ } else if (6 <= idx && idx < 8) {
+ line->record_type |= value;
+ } else if (8 <= idx && idx < 8 + 2 * line->byte_count) {
+ line->data[(idx - 8) >> 1] |= value;
+ } else if (8 + 2 * line->byte_count <= idx &&
+ idx < 10 + 2 * line->byte_count) {
+ line->checksum |= value;
+ } else {
+ return -1;
+ }
+ *our_checksum += value;
+ return 0;
+}
+
+/* return size or -1 if error */
+static size_t __parse_hex_blob(const char *filename, const hwaddr addr,
+ 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 */
+ uint8_t in_process = 0; /* avoid re-enter and
+ * check whether record begin with ':' */
+ uint8_t *end = (uint8_t *)hex_blob + hex_blob_size;
+ uint8_t our_checksum = 0;
+ uint32_t record_index = 0;
+ uint32_t next_address_to_write = 0;
+ uint32_t current_address = 0;
+ uint32_t current_rom_index = 0;
+ uint32_t rom_start_address = 0;
+ size_t total_size = 0;
+ HexLine line = {0};
+
+ for (; hex_blob < end; ++hex_blob) {
+ switch (*hex_blob) {
+ case '\r':
+ case '\n':
+ if (!in_process) {
+ break;
+ }
+
+ in_process = 0;
+ /* uint8_t line.byte_count <= DATA_FIELD_MAX_LEN */
+ if ((record_index >> 1) - LEN_EXCEPT_DATA != line.byte_count ||
+ our_checksum != 0) {
+ return -1;
+ }
+
+ if (handle_record_type(filename, &line, bin_buf, addr, &total_size,
+ &ext_linear_record, &next_address_to_write,
+ ¤t_address, ¤t_rom_index,
+ &rom_start_address, as) == -1) {
+ return -1;
+ }
+ break;
+
+ /* start of a new record. */
+ case ':':
+ memset(&line, 0, sizeof(HexLine));
+ in_process = 1;
+ record_index = 0;
+ break;
+
+ /* decoding lines */
+ default:
+ if (parse_record(&line, *hex_blob, record_index, &our_checksum,
+ in_process) != 0) {
+ return -1;
+ }
+ ++record_index;
+ break;
+ }
+ }
+ return total_size;
+}
+
+/* return size or -1 if error */
+static size_t parse_hex_blob(const char *filename, const hwaddr addr,
+ AddressSpace *as)
+{
+ int fd;
+ off_t hex_blob_size;
+ uint8_t *hex_blob;
+ uint8_t *bin_buf;
+ size_t total_size = 0;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ return -1;
+ }
+ hex_blob_size = lseek(fd, 0, SEEK_END);
+ if (hex_blob_size < 0) {
+ close(fd);
+ return -1;
+ }
+ hex_blob = g_malloc(hex_blob_size);
+ bin_buf = g_malloc(hex_blob_size);
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, hex_blob, hex_blob_size) != hex_blob_size) {
+ total_size = -1;
+ goto hex_parser_exit;
+ }
+
+ total_size =
+ __parse_hex_blob(filename, addr, hex_blob, hex_blob_size, bin_buf, as);
+
+hex_parser_exit:
+ close(fd);
+ g_free(hex_blob);
+ g_free(bin_buf);
+ return total_size;
+}
+
+/* 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) {
+ return -1;
+ }
+
+ return parse_hex_blob(filename, addr, as);
+}
@@ -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
+ * @max_sz: The maximum size of the .hex file to load
+ * @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 .hex file into memory.
+ *
+ * Returns the size of the loaded .hex file on success, -1 otherwise.
+ */
+int load_targphys_hex_as(const char *filename,
+ hwaddr addr, uint64_t max_sz, AddressSpace *as);
+
/** load_image_targphys:
* Same as load_image_targphys_as(), but doesn't allow the caller to specify
* an AddressSpace.
This patch adds Intel Hexadecimal Object File format support to the loader. The file format specification is available here: http://www.piclist.com/techref/fileext/hex/intel.htm The file format is mainly intended for embedded systems and microcontrollers, such as Arduino, ARM, STM32, etc. Suggested-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Su Hang <suhang16@mails.ucas.ac.cn> --- hw/arm/boot.c | 18 ++-- hw/core/loader.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/loader.h | 14 +++ 3 files changed, 280 insertions(+), 5 deletions(-)