* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/hw.h"
#include "disas/disas.h"
#include "monitor/monitor.h"
#include "hw/nvram/fw_cfg.h"
#include "exec/memory.h"
#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "qemu/cutils.h"
#include <zlib.h>
-bool option_rom_has_mr = false;
-bool rom_file_has_mr = true;
-
static int roms_loaded;
/* return the size or -1 if error */
return did;
}
-/* return the size or -1 if error */
int load_image_targphys(const char *filename,
hwaddr addr, uint64_t max_sz)
+{
+ return load_image_targphys_as(filename, addr, max_sz, NULL);
+}
+
+/* return the size or -1 if error */
+int load_image_targphys_as(const char *filename,
+ hwaddr addr, uint64_t max_sz, AddressSpace *as)
{
int size;
return -1;
}
if (size > 0) {
- rom_add_file_fixed(filename, addr, -1);
+ rom_add_file_fixed_as(filename, addr, -1, as);
+ }
+ return size;
+}
+
+int load_image_mr(const char *filename, MemoryRegion *mr)
+{
+ int size;
+
+ if (!memory_access_is_direct(mr, false)) {
+ /* Can only load an image into RAM or ROM */
+ return -1;
+ }
+
+ size = get_image_size(filename);
+
+ if (size > memory_region_size(mr)) {
+ return -1;
+ }
+ if (size > 0) {
+ if (rom_add_file_mr(filename, mr, -1) < 0) {
+ return -1;
+ }
}
return size;
}
}
}
+void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp)
+{
+ int fd;
+ uint8_t e_ident_local[EI_NIDENT];
+ uint8_t *e_ident;
+ size_t hdr_size, off;
+ bool is64l;
+
+ if (!hdr) {
+ hdr = e_ident_local;
+ }
+ e_ident = hdr;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ error_setg_errno(errp, errno, "Failed to open file: %s", filename);
+ return;
+ }
+ if (read(fd, hdr, EI_NIDENT) != EI_NIDENT) {
+ error_setg_errno(errp, errno, "Failed to read file: %s", filename);
+ goto fail;
+ }
+ if (e_ident[0] != ELFMAG0 ||
+ e_ident[1] != ELFMAG1 ||
+ e_ident[2] != ELFMAG2 ||
+ e_ident[3] != ELFMAG3) {
+ error_setg(errp, "Bad ELF magic");
+ goto fail;
+ }
+
+ is64l = e_ident[EI_CLASS] == ELFCLASS64;
+ hdr_size = is64l ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr);
+ if (is64) {
+ *is64 = is64l;
+ }
+
+ off = EI_NIDENT;
+ while (hdr != e_ident_local && off < hdr_size) {
+ size_t br = read(fd, hdr + off, hdr_size - off);
+ switch (br) {
+ case 0:
+ error_setg(errp, "File too short: %s", filename);
+ goto fail;
+ case -1:
+ error_setg_errno(errp, errno, "Failed to read file: %s",
+ filename);
+ goto fail;
+ }
+ off += br;
+ }
+
+fail:
+ close(fd);
+}
+
/* return < 0 if error, otherwise the number of bytes loaded in memory */
int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
- uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb)
+ uint64_t *highaddr, int big_endian, int elf_machine,
+ int clear_lsb, int data_swab)
+{
+ return load_elf_as(filename, translate_fn, translate_opaque, pentry,
+ lowaddr, highaddr, big_endian, elf_machine, clear_lsb,
+ data_swab, NULL);
+}
+
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf_as(const char *filename,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
+ uint64_t *highaddr, int big_endian, int elf_machine,
+ int clear_lsb, int data_swab, AddressSpace *as)
+{
+ return load_elf_ram(filename, translate_fn, translate_opaque,
+ pentry, lowaddr, highaddr, big_endian, elf_machine,
+ clear_lsb, data_swab, as, true);
+}
+
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf_ram(const char *filename,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
+ uint64_t *highaddr, int big_endian, int elf_machine,
+ int clear_lsb, int data_swab, AddressSpace *as,
+ bool load_rom)
{
int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
uint8_t e_ident[EI_NIDENT];
lseek(fd, 0, SEEK_SET);
if (e_ident[EI_CLASS] == ELFCLASS64) {
ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
- pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+ pentry, lowaddr, highaddr, elf_machine, clear_lsb,
+ data_swab, as, load_rom);
} else {
ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
- pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+ pentry, lowaddr, highaddr, elf_machine, clear_lsb,
+ data_swab, as, load_rom);
}
fail:
#define DEFLATED 8
-/* This is the usual maximum in uboot, so if a uImage overflows this, it would
- * overflow on real hardware too. */
-#define UBOOT_MAX_GUNZIP_BYTES (64 << 20)
-
-static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
- size_t srclen)
+ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen)
{
z_stream s;
ssize_t dstbytes;
static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
int *is_linux, uint8_t image_type,
uint64_t (*translate_fn)(void *, uint64_t),
- void *translate_opaque)
+ void *translate_opaque, AddressSpace *as)
{
int fd;
int size;
hdr->ih_size = bytes;
}
- rom_add_blob_fixed(filename, data, hdr->ih_size, address);
+ rom_add_blob_fixed_as(filename, data, hdr->ih_size, address, as);
ret = hdr->ih_size;
out:
- if (data)
- g_free(data);
+ g_free(data);
close(fd);
return ret;
}
void *translate_opaque)
{
return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL,
- translate_fn, translate_opaque);
+ translate_fn, translate_opaque, NULL);
+}
+
+int load_uimage_as(const char *filename, hwaddr *ep, hwaddr *loadaddr,
+ int *is_linux,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque, AddressSpace *as)
+{
+ return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL,
+ translate_fn, translate_opaque, as);
}
/* Load a ramdisk. */
int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz)
{
return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK,
- NULL, NULL);
+ NULL, NULL, NULL);
}
/* Load a gzip-compressed kernel to a dynamically allocated buffer. */
uint8_t *data;
MemoryRegion *mr;
+ AddressSpace *as;
int isrom;
char *fw_dir;
char *fw_file;
static FWCfgState *fw_cfg;
static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms);
+static inline bool rom_order_compare(Rom *rom, Rom *item)
+{
+ return ((uintptr_t)(void *)rom->as > (uintptr_t)(void *)item->as) ||
+ (rom->as == item->as && rom->addr >= item->addr);
+}
+
static void rom_insert(Rom *rom)
{
Rom *item;
hw_error ("ROM images must be loaded at startup\n");
}
- /* list is ordered by load address */
+ /* The user didn't specify an address space, this is the default */
+ if (!rom->as) {
+ rom->as = &address_space_memory;
+ }
+
+ /* List is ordered by load address in the same address space */
QTAILQ_FOREACH(item, &roms, next) {
- if (rom->addr >= item->addr)
+ if (rom_order_compare(rom, item)) {
continue;
+ }
QTAILQ_INSERT_BEFORE(item, rom, next);
return;
}
}
}
-static void *rom_set_mr(Rom *rom, Object *owner, const char *name)
+static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro)
{
void *data;
memory_region_init_resizeable_ram(rom->mr, owner, name,
rom->datasize, rom->romsize,
fw_cfg_resized,
- &error_abort);
- memory_region_set_readonly(rom->mr, true);
+ &error_fatal);
+ memory_region_set_readonly(rom->mr, ro);
vmstate_register_ram_global(rom->mr);
data = memory_region_get_ram_ptr(rom->mr);
int rom_add_file(const char *file, const char *fw_dir,
hwaddr addr, int32_t bootindex,
- bool option_rom)
+ bool option_rom, MemoryRegion *mr,
+ AddressSpace *as)
{
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
Rom *rom;
int rc, fd = -1;
char devpath[100];
+ if (as && mr) {
+ fprintf(stderr, "Specifying an Address Space and Memory Region is " \
+ "not valid when loading a rom\n");
+ /* We haven't allocated anything so we don't need any cleanup */
+ return -1;
+ }
+
rom = g_malloc0(sizeof(*rom));
rom->name = g_strdup(file);
rom->path = qemu_find_file(QEMU_FILE_TYPE_BIOS, rom->name);
+ rom->as = as;
if (rom->path == NULL) {
rom->path = g_strdup(file);
}
basename);
snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
- if ((!option_rom || option_rom_has_mr) && rom_file_has_mr) {
- data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
+ if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) {
+ data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, true);
} else {
data = rom->data;
}
fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize);
} else {
- snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
+ if (mr) {
+ rom->mr = mr;
+ snprintf(devpath, sizeof(devpath), "/rom@%s", file);
+ } else {
+ snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
+ }
}
add_boot_device_path(bootindex, NULL, devpath);
err:
if (fd != -1)
close(fd);
+
g_free(rom->data);
g_free(rom->path);
g_free(rom->name);
+ if (fw_dir) {
+ g_free(rom->fw_dir);
+ g_free(rom->fw_file);
+ }
g_free(rom);
+
return -1;
}
MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
size_t max_len, hwaddr addr, const char *fw_file_name,
- FWCfgReadCallback fw_callback, void *callback_opaque)
+ FWCfgReadCallback fw_callback, void *callback_opaque,
+ AddressSpace *as, bool read_only)
{
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
Rom *rom;
MemoryRegion *mr = NULL;
rom = g_malloc0(sizeof(*rom));
rom->name = g_strdup(name);
+ rom->as = as;
rom->addr = addr;
rom->romsize = max_len ? max_len : len;
rom->datasize = len;
char devpath[100];
void *data;
- snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
+ if (read_only) {
+ snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
+ } else {
+ snprintf(devpath, sizeof(devpath), "/ram@%s", fw_file_name);
+ }
- if (rom_file_has_mr) {
- data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
+ if (mc->rom_file_has_mr) {
+ data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, read_only);
mr = rom->mr;
} else {
data = rom->data;
fw_cfg_add_file_callback(fw_cfg, fw_file_name,
fw_callback, callback_opaque,
- data, rom->datasize);
+ data, rom->datasize, read_only);
}
return mr;
}
* memory ownership of "data", so we don't have to allocate and copy the buffer.
*/
int rom_add_elf_program(const char *name, void *data, size_t datasize,
- size_t romsize, hwaddr addr)
+ size_t romsize, hwaddr addr, AddressSpace *as)
{
Rom *rom;
rom->datasize = datasize;
rom->romsize = romsize;
rom->data = data;
+ rom->as = as;
rom_insert(rom);
return 0;
}
int rom_add_vga(const char *file)
{
- return rom_add_file(file, "vgaroms", 0, -1, true);
+ return rom_add_file(file, "vgaroms", 0, -1, true, NULL, NULL);
}
int rom_add_option(const char *file, int32_t bootindex)
{
- return rom_add_file(file, "genroms", 0, bootindex, true);
+ return rom_add_file(file, "genroms", 0, bootindex, true, NULL, NULL);
}
static void rom_reset(void *unused)
void *host = memory_region_get_ram_ptr(rom->mr);
memcpy(host, rom->data, rom->datasize);
} else {
- cpu_physical_memory_write_rom(&address_space_memory,
- rom->addr, rom->data, rom->datasize);
+ cpu_physical_memory_write_rom(rom->as, rom->addr, rom->data,
+ rom->datasize);
}
if (rom->isrom) {
/* rom needs to be written only once */
hwaddr addr = 0;
MemoryRegionSection section;
Rom *rom;
+ AddressSpace *as = NULL;
QTAILQ_FOREACH(rom, &roms, next) {
if (rom->fw_file) {
continue;
}
- if (addr > rom->addr) {
+ if ((addr > rom->addr) && (as == rom->as)) {
fprintf(stderr, "rom: requested regions overlap "
"(rom %s. free=0x" TARGET_FMT_plx
", addr=0x" TARGET_FMT_plx ")\n",
}
addr = rom->addr;
addr += rom->romsize;
- section = memory_region_find(get_system_memory(), rom->addr, 1);
+ section = memory_region_find(rom->mr ? rom->mr : get_system_memory(),
+ rom->addr, 1);
rom->isrom = int128_nz(section.size) && memory_region_is_rom(section.mr);
memory_region_unref(section.mr);
+ as = rom->as;
}
qemu_register_reset(rom_reset, NULL);
roms_loaded = 1;
fw_cfg = f;
}
+void rom_set_order_override(int order)
+{
+ if (!fw_cfg)
+ return;
+ fw_cfg_set_order_override(fw_cfg, order);
+}
+
+void rom_reset_order_override(void)
+{
+ if (!fw_cfg)
+ return;
+ fw_cfg_reset_order_override(fw_cfg);
+}
+
static Rom *find_rom(hwaddr addr)
{
Rom *rom;