From 74287114c98ecb969b7ce4b5c959da8a8a431d0f Mon Sep 17 00:00:00 2001 From: ths Date: Sun, 1 Apr 2007 17:56:37 +0000 Subject: [PATCH] Improved initrd support for mips. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2574 c046a42c-6fe2-441c-8c8c-71466251a162 --- elf_ops.h | 12 +++++++++++- hw/arm_boot.c | 2 +- hw/mips_malta.c | 47 +++++++++++++++++++++++++++++++---------------- hw/mips_r4k.c | 27 ++++++++++++++++++++------- hw/sun4m.c | 4 ++-- hw/sun4u.c | 4 ++-- loader.c | 8 +++++--- vl.h | 3 ++- 8 files changed, 74 insertions(+), 33 deletions(-) diff --git a/elf_ops.h b/elf_ops.h index abcce093e..3fdde8128 100644 --- a/elf_ops.h +++ b/elf_ops.h @@ -139,11 +139,13 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab) } int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend, - int must_swab, uint64_t *pentry) + int must_swab, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr) { struct elfhdr ehdr; struct elf_phdr *phdr = NULL, *ph; int size, i, total_size; + elf_word low = 0, high = 0; elf_word mem_size, addr; uint8_t *data = NULL; @@ -193,12 +195,20 @@ int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend, cpu_physical_memory_write_rom(addr, data, mem_size); total_size += mem_size; + if (!low || addr < low) + low = addr; + if (!high || (addr + mem_size) > high) + high = addr + mem_size; qemu_free(data); data = NULL; } } qemu_free(phdr); + if (lowaddr) + *lowaddr = (uint64_t)low; + if (highaddr) + *highaddr = (uint64_t)high; return total_size; fail: qemu_free(data); diff --git a/hw/arm_boot.c b/hw/arm_boot.c index dfc00db28..095b0bb79 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -101,7 +101,7 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename, qemu_register_reset(main_cpu_reset, env); } /* Assume that raw images are linux kernels, and ELF images are not. */ - kernel_size = load_elf(kernel_filename, 0, &elf_entry); + kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uboot(kernel_filename, &entry, &is_linux); diff --git a/hw/mips_malta.c b/hw/mips_malta.c index e4b43bea9..0ba3359ef 100644 --- a/hw/mips_malta.c +++ b/hw/mips_malta.c @@ -31,13 +31,13 @@ #endif #ifdef TARGET_MIPS64 -#define INITRD_LOAD_ADDR (int64_t)0x80800000 +#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffULL) #else -#define INITRD_LOAD_ADDR (int32_t)0x80800000 +#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffU) #endif -#define ENVP_ADDR (int32_t)0x80002000 -#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000)) +#define ENVP_ADDR (int32_t)0x80002000 +#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000)) #define ENVP_NB_ENTRIES 16 #define ENVP_ENTRY_SIZE 256 @@ -536,7 +536,7 @@ static void network_init (PCIBus *pci_bus) a3 - RAM size in bytes */ -static void write_bootloader (CPUState *env, unsigned long bios_offset, int64_t kernel_addr) +static void write_bootloader (CPUState *env, unsigned long bios_offset, int64_t kernel_entry) { uint32_t *p; @@ -555,8 +555,8 @@ static void write_bootloader (CPUState *env, unsigned long bios_offset, int64_t stl_raw(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */ stl_raw(p++, 0x3c070000 | (env->ram_size >> 16)); /* lui a3, high(env->ram_size) */ stl_raw(p++, 0x34e70000 | (env->ram_size & 0xffff)); /* ori a3, a3, low(env->ram_size) */ - stl_raw(p++, 0x3c1f0000 | ((kernel_addr >> 16) & 0xffff)); /* lui ra, high(kernel_addr) */; - stl_raw(p++, 0x37ff0000 | (kernel_addr & 0xffff)); /* ori ra, ra, low(kernel_addr) */ + stl_raw(p++, 0x3c1f0000 | ((kernel_entry >> 16) & 0xffff)); /* lui ra, high(kernel_entry) */ + stl_raw(p++, 0x37ff0000 | (kernel_entry & 0xffff)); /* ori ra, ra, low(kernel_entry) */ stl_raw(p++, 0x03e00008); /* jr ra */ stl_raw(p++, 0x00000000); /* nop */ } @@ -592,11 +592,13 @@ static void prom_set(int index, const char *string, ...) /* Kernel */ static int64_t load_kernel (CPUState *env) { - int64_t kernel_addr = 0; + int64_t kernel_entry, kernel_low, kernel_high; int index = 0; long initrd_size; + ram_addr_t initrd_offset; - if (load_elf(env->kernel_filename, VIRT_TO_PHYS_ADDEND, &kernel_addr) < 0) { + if (load_elf(env->kernel_filename, VIRT_TO_PHYS_ADDEND, + &kernel_entry, &kernel_low, &kernel_high) < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", env->kernel_filename); exit(1); @@ -604,9 +606,20 @@ static int64_t load_kernel (CPUState *env) /* load initrd */ initrd_size = 0; + initrd_offset = 0; if (env->initrd_filename) { - initrd_size = load_image(env->initrd_filename, - phys_ram_base + INITRD_LOAD_ADDR + VIRT_TO_PHYS_ADDEND); + initrd_size = get_image_size (env->initrd_filename); + if (initrd_size > 0) { + initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) & TARGET_PAGE_MASK; + if (initrd_offset + initrd_size > env->ram_size) { + fprintf(stderr, + "qemu: memory too small for initial ram disk '%s'\n", + env->initrd_filename); + exit(1); + } + initrd_size = load_image(env->initrd_filename, + phys_ram_base + initrd_offset); + } if (initrd_size == (target_ulong) -1) { fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", env->initrd_filename); @@ -617,7 +630,9 @@ static int64_t load_kernel (CPUState *env) /* Store command line. */ prom_set(index++, env->kernel_filename); if (initrd_size > 0) - prom_set(index++, "rd_start=0x" TARGET_FMT_lx " rd_size=%li %s", INITRD_LOAD_ADDR, initrd_size, env->kernel_cmdline); + prom_set(index++, "rd_start=0x" TARGET_FMT_lx " rd_size=%li %s", + PHYS_TO_VIRT(initrd_offset), initrd_size, + env->kernel_cmdline); else prom_set(index++, env->kernel_cmdline); @@ -628,7 +643,7 @@ static int64_t load_kernel (CPUState *env) prom_set(index++, "38400n8r"); prom_set(index++, NULL); - return kernel_addr; + return kernel_entry; } static void main_cpu_reset(void *opaque) @@ -651,7 +666,7 @@ void mips_malta_init (int ram_size, int vga_ram_size, int boot_device, { char buf[1024]; unsigned long bios_offset; - int64_t kernel_addr; + int64_t kernel_entry; PCIBus *pci_bus; CPUState *env; RTCState *rtc_state; @@ -693,8 +708,8 @@ void mips_malta_init (int ram_size, int vga_ram_size, int boot_device, env->kernel_filename = kernel_filename; env->kernel_cmdline = kernel_cmdline; env->initrd_filename = initrd_filename; - kernel_addr = load_kernel(env); - write_bootloader(env, bios_offset, kernel_addr); + kernel_entry = load_kernel(env); + write_bootloader(env, bios_offset, kernel_entry); } else { snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); ret = load_image(buf, phys_ram_base + bios_offset); diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c index 487983e46..8fbddf3c3 100644 --- a/hw/mips_r4k.c +++ b/hw/mips_r4k.c @@ -16,9 +16,9 @@ #endif #ifdef TARGET_MIPS64 -#define INITRD_LOAD_ADDR (int64_t)(int32_t)0x80800000 +#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffULL) #else -#define INITRD_LOAD_ADDR (int32_t)0x80800000 +#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffU) #endif #define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000)) @@ -73,10 +73,12 @@ void load_kernel (CPUState *env, int ram_size, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename) { - int64_t entry = 0; + int64_t entry, kernel_low, kernel_high; long kernel_size, initrd_size; + ram_addr_t initrd_offset; - kernel_size = load_elf(kernel_filename, VIRT_TO_PHYS_ADDEND, &entry); + kernel_size = load_elf(kernel_filename, VIRT_TO_PHYS_ADDEND, + &entry, &kernel_low, &kernel_high); if (kernel_size >= 0) { if ((entry & ~0x7fffffffULL) == 0x80000000) entry = (int32_t)entry; @@ -89,9 +91,20 @@ void load_kernel (CPUState *env, int ram_size, const char *kernel_filename, /* load initrd */ initrd_size = 0; + initrd_offset = 0; if (initrd_filename) { - initrd_size = load_image(initrd_filename, - phys_ram_base + INITRD_LOAD_ADDR + VIRT_TO_PHYS_ADDEND); + initrd_size = get_image_size (initrd_filename); + if (initrd_size > 0) { + initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) & TARGET_PAGE_MASK; + if (initrd_offset + initrd_size > ram_size) { + fprintf(stderr, + "qemu: memory too small for initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + initrd_size = load_image(initrd_filename, + phys_ram_base + initrd_offset); + } if (initrd_size == (target_ulong) -1) { fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", initrd_filename); @@ -104,7 +117,7 @@ void load_kernel (CPUState *env, int ram_size, const char *kernel_filename, int ret; ret = sprintf(phys_ram_base + (16 << 20) - 256, "rd_start=0x" TARGET_FMT_lx " rd_size=%li ", - INITRD_LOAD_ADDR, + PHYS_TO_VIRT((uint32_t)initrd_offset), initrd_size); strcpy (phys_ram_base + (16 << 20) - 256 + ret, kernel_cmdline); } diff --git a/hw/sun4m.c b/hw/sun4m.c index a23baee17..ee34c1b5b 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -295,7 +295,7 @@ static void sun4m_load_kernel(long vram_size, int ram_size, int boot_device, prom_offset | IO_MEM_ROM); snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAME); - ret = load_elf(buf, 0, NULL); + ret = load_elf(buf, 0, NULL, NULL, NULL); if (ret < 0) { fprintf(stderr, "qemu: could not load prom '%s'\n", buf); @@ -304,7 +304,7 @@ static void sun4m_load_kernel(long vram_size, int ram_size, int boot_device, kernel_size = 0; if (linux_boot) { - kernel_size = load_elf(kernel_filename, -0xf0000000, NULL); + kernel_size = load_elf(kernel_filename, -0xf0000000, NULL, NULL, NULL); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); if (kernel_size < 0) diff --git a/hw/sun4u.c b/hw/sun4u.c index 906695690..e536c48ea 100644 --- a/hw/sun4u.c +++ b/hw/sun4u.c @@ -292,7 +292,7 @@ static void sun4u_init(int ram_size, int vga_ram_size, int boot_device, prom_offset | IO_MEM_ROM); snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAME); - ret = load_elf(buf, 0, NULL); + ret = load_elf(buf, 0, NULL, NULL, NULL); if (ret < 0) { fprintf(stderr, "qemu: could not load prom '%s'\n", buf); @@ -303,7 +303,7 @@ static void sun4u_init(int ram_size, int vga_ram_size, int boot_device, initrd_size = 0; if (linux_boot) { /* XXX: put correct offset */ - kernel_size = load_elf(kernel_filename, 0, NULL); + kernel_size = load_elf(kernel_filename, 0, NULL, NULL, NULL); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); if (kernel_size < 0) diff --git a/loader.c b/loader.c index 827d456d2..2e4fecc34 100644 --- a/loader.c +++ b/loader.c @@ -196,7 +196,7 @@ static void *load_at(int fd, int offset, int size) /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf(const char *filename, int64_t virt_to_phys_addend, - uint64_t *pentry) + uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr) { int fd, data_order, host_data_order, must_swab, ret; uint8_t e_ident[EI_NIDENT]; @@ -230,9 +230,11 @@ int load_elf(const char *filename, int64_t virt_to_phys_addend, lseek(fd, 0, SEEK_SET); if (e_ident[EI_CLASS] == ELFCLASS64) { - ret = load_elf64(fd, virt_to_phys_addend, must_swab, pentry); + ret = load_elf64(fd, virt_to_phys_addend, must_swab, pentry, + lowaddr, highaddr); } else { - ret = load_elf32(fd, virt_to_phys_addend, must_swab, pentry); + ret = load_elf32(fd, virt_to_phys_addend, must_swab, pentry, + lowaddr, highaddr); } close(fd); diff --git a/vl.h b/vl.h index be24d72a4..2fbfa7fc1 100644 --- a/vl.h +++ b/vl.h @@ -1180,7 +1180,8 @@ void slavio_pic_set_irq_cpu(void *opaque, int irq, int level, unsigned int cpu); /* loader.c */ int get_image_size(const char *filename); int load_image(const char *filename, uint8_t *addr); -int load_elf(const char *filename, int64_t virt_to_phys_addend, uint64_t *pentry); +int load_elf(const char *filename, int64_t virt_to_phys_addend, + uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr); int load_aout(const char *filename, uint8_t *addr); int load_uboot(const char *filename, target_ulong *ep, int *is_linux);