qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH] Add MIPS ELF loader


From: Thiemo Seufer
Subject: Re: [Qemu-devel] [PATCH] Add MIPS ELF loader
Date: Wed, 26 Apr 2006 00:57:13 +0100
User-agent: Mutt/1.5.11+cvs20060403

Fabrice Bellard wrote:
> I just added an ELF loader (inspirated from the SPARC one) which should 
> work for all architectures. It supports symbol loading and use the CPU 
> physical memory mappings to compute correct RAM addresses.

I extended it so that it provides the ELF entry point address, fixed
a memory leak for the failure case, put the large buffer read in a
read loop, and let the mips-4kc use it with fallback to raw binaries.

Also, the mips support handles a missing BIOS nor in a more graceful
way.

I was busier that I hoped the last days, collecting the MIPS specific
patches may take a few days longer.


Thiemo


Index: qemu-work/hw/mips_r4k.c
===================================================================
--- qemu-work.orig/hw/mips_r4k.c        2006-04-25 23:22:22.000000000 +0100
+++ qemu-work/hw/mips_r4k.c     2006-04-25 23:22:25.000000000 +0100
@@ -5,6 +5,8 @@
 #define KERNEL_LOAD_ADDR 0x80010000
 #define INITRD_LOAD_ADDR 0x80800000
 
+#define VIRT_TO_PHYS_ADDEND (-0x80000000LL)
+
 extern FILE *logfile;
 
 static PITState *pit;
@@ -101,6 +103,7 @@
     cpu_mips_update_count(env, 1, 0);
 }
 
+
 static void io_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
 {
 #if 0
@@ -189,72 +192,71 @@
                     const char *initrd_filename)
 {
     char buf[1024];
-    target_ulong kernel_base, kernel_size, initrd_base, initrd_size;
+    int64_t entry = 0;
     unsigned long bios_offset;
     int io_memory;
-    int linux_boot;
     int ret;
     CPUState *env;
-
-    printf("%s: start\n", __func__);
-    linux_boot = (kernel_filename != NULL);
+    long kernel_size;
 
     env = cpu_init();
     register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
 
     /* allocate RAM */
     cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+
+    /* Try to load a BIOS image. If this fails, we continue regardless,
+       but initialize the hardware ourselves. When a kernel gets
+       preloaded we also initialize the hardware, since the BIOS wasn't
+       run. */
     bios_offset = ram_size + vga_ram_size;
     snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
     printf("%s: load BIOS '%s' size %d\n", __func__, buf, BIOS_SIZE);
     ret = load_image(buf, phys_ram_base + bios_offset);
-    if (ret != BIOS_SIZE) {
-        fprintf(stderr, "qemu: could not load MIPS bios '%s'\n", buf);
-        exit(1);
+    if (ret == BIOS_SIZE) {
+       cpu_register_physical_memory((uint32_t)(0x1fc00000),
+                                    BIOS_SIZE, bios_offset | IO_MEM_ROM);
+       env->PC = 0xBFC00000;
+       if (!kernel_filename)
+           return;
+    } else {
+       /* not fatal */
+        fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n",
+               buf);
     }
-    cpu_register_physical_memory((uint32_t)(0x1fc00000),
-                                 BIOS_SIZE, bios_offset | IO_MEM_ROM);
-#if 0
-    memcpy(phys_ram_base + 0x10000, phys_ram_base + bios_offset, BIOS_SIZE);
-    env->PC = 0x80010004;
-#else
-    env->PC = 0xBFC00004;
-#endif
-    if (linux_boot) {
-        kernel_base = KERNEL_LOAD_ADDR;
-        /* now we can load the kernel */
-        kernel_size = load_image(kernel_filename,
-                                phys_ram_base + (kernel_base - 0x80000000));
-        if (kernel_size == (target_ulong) -1) {
-            fprintf(stderr, "qemu: could not load kernel '%s'\n", 
-                    kernel_filename);
-            exit(1);
-        }
+
+    kernel_size = 0;
+    if (kernel_filename) {
+       kernel_size = load_elf(kernel_filename, VIRT_TO_PHYS_ADDEND, &entry);
+       if (kernel_size >= 0)
+           env->PC = entry;
+       else {
+           kernel_size = load_image(kernel_filename,
+                                     phys_ram_base + KERNEL_LOAD_ADDR + 
VIRT_TO_PHYS_ADDEND);
+            if (kernel_size < 0) {
+                fprintf(stderr, "qemu: could not load kernel '%s'\n",
+                        kernel_filename);
+                exit(1);
+            }
+            env->PC = KERNEL_LOAD_ADDR;
+       }
+
         /* load initrd */
         if (initrd_filename) {
-            initrd_base = INITRD_LOAD_ADDR;
-            initrd_size = load_image(initrd_filename,
-                                     phys_ram_base + initrd_base);
-            if (initrd_size == (target_ulong) -1) {
+            if (load_image(initrd_filename,
+                          phys_ram_base + INITRD_LOAD_ADDR + 
VIRT_TO_PHYS_ADDEND)
+               == (target_ulong) -1) {
                 fprintf(stderr, "qemu: could not load initial ram disk 
'%s'\n", 
                         initrd_filename);
                 exit(1);
             }
-        } else {
-            initrd_base = 0;
-            initrd_size = 0;
         }
-        env->PC = KERNEL_LOAD_ADDR;
+
        /* Store command line.  */
         strcpy (phys_ram_base + (16 << 20) - 256, kernel_cmdline);
         /* FIXME: little endian support */
         *(int *)(phys_ram_base + (16 << 20) - 260) = tswap32 (0x12345678);
         *(int *)(phys_ram_base + (16 << 20) - 264) = tswap32 (ram_size);
-    } else {
-        kernel_base = 0;
-        kernel_size = 0;
-        initrd_base = 0;
-        initrd_size = 0;
     }
 
     /* Init internal devices */
Index: qemu-work/elf_ops.h
===================================================================
--- qemu-work.orig/elf_ops.h    2006-04-25 23:22:22.000000000 +0100
+++ qemu-work/elf_ops.h 2006-04-25 23:23:10.000000000 +0100
@@ -138,7 +138,8 @@
     return -1;
 }
 
-int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend, int must_swab)
+int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend,
+                       int must_swab, int64_t *entry)
 {
     struct elfhdr ehdr;
     struct elf_phdr *phdr = NULL, *ph;
@@ -152,6 +153,9 @@
         glue(bswap_ehdr, SZ)(&ehdr);
     }
 
+    if (entry)
+       *entry = (int64_t)ehdr.e_entry;
+
     glue(load_symbols, SZ)(&ehdr, fd, must_swab);
 
     size = ehdr.e_phnum * sizeof(phdr[0]);
@@ -176,9 +180,29 @@
             /* XXX: avoid allocating */
             data = qemu_mallocz(mem_size);
             if (ph->p_filesz > 0) {
-                lseek(fd, ph->p_offset, SEEK_SET);
-                if (read(fd, data, ph->p_filesz) != ph->p_filesz)
-                    goto fail;
+                size_t sz = ph->p_filesz;
+                int retval;
+
+                retval = lseek(fd, ph->p_offset, SEEK_SET);
+               if (retval < 0)
+                   goto fail2;
+                while (sz) {
+                   retval = read(fd, data, sz);
+                   switch (retval) {
+                   case -1:
+                        goto fail2;
+                   case 0: /* EOF */
+                       if (sz)
+                           goto fail;
+                       break;
+                   default:
+                       if (sz < retval)
+                           goto fail2;
+                       sz -= retval;
+                       retval = 0;
+                       break;
+                   }
+               }
             }
             addr = ph->p_vaddr + virt_to_phys_addend;
 
@@ -190,6 +214,8 @@
         }
     }
     return total_size;
+ fail2:
+    qemu_free(data);
  fail:
     qemu_free(phdr);
     return -1;
Index: qemu-work/hw/sun4m.c
===================================================================
--- qemu-work.orig/hw/sun4m.c   2006-04-25 23:22:22.000000000 +0100
+++ qemu-work/hw/sun4m.c        2006-04-25 23:22:25.000000000 +0100
@@ -269,7 +269,7 @@
                                  prom_offset | IO_MEM_ROM);
 
     snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEE);
-    ret = load_elf(buf, 0);
+    ret = load_elf(buf, 0, NULL);
     if (ret < 0) {
        snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEB);
        ret = load_image(buf, phys_ram_base + prom_offset);
@@ -282,7 +282,7 @@
 
     kernel_size = 0;
     if (linux_boot) {
-        kernel_size = load_elf(kernel_filename, -0xf0000000);
+        kernel_size = load_elf(kernel_filename, -0xf0000000, NULL);
         if (kernel_size < 0)
            kernel_size = load_aout(kernel_filename, phys_ram_base + 
KERNEL_LOAD_ADDR);
        if (kernel_size < 0)
Index: qemu-work/hw/sun4u.c
===================================================================
--- qemu-work.orig/hw/sun4u.c   2006-04-25 23:22:22.000000000 +0100
+++ qemu-work/hw/sun4u.c        2006-04-25 23:22:25.000000000 +0100
@@ -283,7 +283,7 @@
                                  prom_offset | IO_MEM_ROM);
 
     snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEE);
-    ret = load_elf(buf, 0);
+    ret = load_elf(buf, 0, NULL);
     if (ret < 0) {
        snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEB);
        ret = load_image(buf, phys_ram_base + prom_offset);
@@ -298,7 +298,7 @@
     initrd_size = 0;
     if (linux_boot) {
         /* XXX: put correct offset */
-        kernel_size = load_elf(kernel_filename, 0);
+        kernel_size = load_elf(kernel_filename, 0, NULL);
         if (kernel_size < 0)
            kernel_size = load_aout(kernel_filename, phys_ram_base + 
KERNEL_LOAD_ADDR);
        if (kernel_size < 0)
Index: qemu-work/loader.c
===================================================================
--- qemu-work.orig/loader.c     2006-04-25 23:22:22.000000000 +0100
+++ qemu-work/loader.c  2006-04-25 23:22:25.000000000 +0100
@@ -194,7 +194,8 @@
 #include "elf_ops.h"
 
 /* return < 0 if error, otherwise the number of bytes loaded in memory */
-int load_elf(const char *filename, int64_t virt_to_phys_addend)
+int load_elf(const char *filename, int64_t virt_to_phys_addend,
+             int64_t *entry)
 {
     int fd, data_order, must_swab, ret;
     uint8_t e_ident[EI_NIDENT];
@@ -220,9 +221,9 @@
     
     lseek(fd, 0, SEEK_SET);
     if (e_ident[EI_CLASS] == ELFCLASS64) {
-        ret = load_elf64(fd, virt_to_phys_addend, must_swab);
+        ret = load_elf64(fd, virt_to_phys_addend, must_swab, entry);
     } else {
-        ret = load_elf32(fd, virt_to_phys_addend, must_swab);
+        ret = load_elf32(fd, virt_to_phys_addend, must_swab, entry);
     }
 
     close(fd);
Index: qemu-work/vl.h
===================================================================
--- qemu-work.orig/vl.h 2006-04-25 23:22:22.000000000 +0100
+++ qemu-work/vl.h      2006-04-25 23:22:25.000000000 +0100
@@ -872,7 +872,7 @@
 /* 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);
+int load_elf(const char *filename, int64_t virt_to_phys_addend, int64_t 
*entry);
 int load_aout(const char *filename, uint8_t *addr);
 
 /* slavio_timer.c */




reply via email to

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