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

Re: [Xen-devel] [PATCH] Elf loader fixes



> some with p_vaddr == p_paddr on purpose and some which don't. We rely on a
> "boot loader" (ie grub or domain builder) that only cares about p_paddr.

Same goes for the x86_64 linux kernel with the vsyscall page ...

I've settled for a slightly different approach now.  To keep behaviour
as close as possible to classic i386 boot loaders I'll check paddr only
and use the virt_base value from the __xen_guest section to shift the
addresses.  For bug compatibility with old linux kernels I compare paddr
+ virt_base.  New patch below, this time tested both 32 and 64 bit
(linux only though), I think it should be ok for OpenSolaris too ;)

cheers,

  Gerd

-- 
Gerd 'just married' Hoffmann <kraxel@xxxxxxx>
I'm the hacker formerly known as Gerd Knorr.
http://www.suse.de/~kraxel/just-married.jpeg
diff -r 175ad739d8bc linux-2.6-xen-sparse/arch/i386/kernel/vmlinux.lds.S
--- a/linux-2.6-xen-sparse/arch/i386/kernel/vmlinux.lds.S       Wed Feb 22 
20:52:30 2006
+++ b/linux-2.6-xen-sparse/arch/i386/kernel/vmlinux.lds.S       Thu Feb 23 
10:31:01 2006
@@ -10,7 +10,7 @@
 
 OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
 OUTPUT_ARCH(i386)
-ENTRY(phys_startup_32)
+ENTRY(startup_32)
 jiffies = jiffies_64;
 SECTIONS
 {
diff -r 175ad739d8bc linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/page.h
--- a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/page.h Wed Feb 22 
20:52:30 2006
+++ b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/page.h Thu Feb 23 
10:31:01 2006
@@ -288,10 +288,6 @@
 #endif
 #define __KERNEL_START         (__PAGE_OFFSET + __PHYSICAL_START)
 
-#undef LOAD_OFFSET
-#define LOAD_OFFSET            0
-
-
 #define PAGE_OFFSET            ((unsigned long)__PAGE_OFFSET)
 #define VMALLOC_RESERVE                ((unsigned long)__VMALLOC_RESERVE)
 #define MAXMEM                 
(HYPERVISOR_VIRT_START-__PAGE_OFFSET-__VMALLOC_RESERVE)
diff -r 175ad739d8bc linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/page.h
--- a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/page.h       Wed Feb 
22 20:52:30 2006
+++ b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/page.h       Thu Feb 
23 10:31:01 2006
@@ -259,9 +259,6 @@
 #define __PAGE_OFFSET           0xffff880000000000
 #endif /* !__ASSEMBLY__ */
 
-#undef LOAD_OFFSET
-#define LOAD_OFFSET            0
-
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)       (((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
diff -r 175ad739d8bc tools/libxc/xc_load_elf.c
--- a/tools/libxc/xc_load_elf.c Wed Feb 22 20:52:30 2006
+++ b/tools/libxc/xc_load_elf.c Thu Feb 23 10:31:01 2006
@@ -60,6 +60,7 @@
     Elf_Phdr *phdr;
     Elf_Shdr *shdr;
     unsigned long kernstart = ~0UL, kernend=0UL;
+    unsigned long sstart, send;
     char *shstrtab, *guestinfo=NULL, *p;
     int h;
 
@@ -117,6 +118,8 @@
         }
         if ( (strstr(guestinfo, "PAE=yes") != NULL) )
             dsi->pae_kernel = 1;
+        if ( (p = strstr(guestinfo, "VIRT_BASE=")) != NULL )
+            dsi->virt_base = strtoul(p+10, &p, 0);
 
         break;
     }
@@ -138,10 +141,27 @@
         phdr = (Elf_Phdr *)(image + ehdr->e_phoff + (h*ehdr->e_phentsize));
         if ( !is_loadable_phdr(phdr) )
             continue;
-        if ( phdr->p_paddr < kernstart )
-            kernstart = phdr->p_paddr;
-        if ( (phdr->p_paddr + phdr->p_memsz) > kernend )
-            kernend = phdr->p_paddr + phdr->p_memsz;
+        sstart = phdr->p_paddr;
+        send   = phdr->p_paddr + phdr->p_memsz;
+        /*
+         * bug comparibility alert: old linux kernels used to have
+         * virtual addresses in the paddr headers, whereas newer ones
+         * (since kexec merge, around 2.6.14) correctly use physical
+         * addresses.
+         *
+         * As we want to be able to boot both kinds of kernels we'll
+         * do some guesswork here: If paddr is greater than virt_base
+         * we assume it is a old kernel and use it as-is.  Otherwise
+         * we'll add virt_base to get the correct address.
+         */
+        if (sstart < dsi->virt_base) {
+            sstart += dsi->virt_base;
+            send   += dsi->virt_base;
+        }
+        if ( sstart < kernstart )
+            kernstart = sstart;
+        if ( send > kernend )
+            kernend = send;
     }
 
     if ( (kernstart > kernend) || 
@@ -189,7 +209,11 @@
         
         for ( done = 0; done < phdr->p_filesz; done += chunksz )
         {
-            pa = (phdr->p_paddr + done) - dsi->v_start;
+            /* bug compatibility alert, see above */
+            pa = phdr->p_paddr + done;
+            if (pa > dsi->virt_base)
+                pa -= dsi->virt_base;
+
             va = xc_map_foreign_range(
                 xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
             chunksz = phdr->p_filesz - done;
@@ -202,7 +226,11 @@
 
         for ( ; done < phdr->p_memsz; done += chunksz )
         {
-            pa = (phdr->p_paddr + done) - dsi->v_start;
+            /* bug compatibility alert, see above */
+            pa = phdr->p_paddr + done;
+            if (pa > dsi->virt_base)
+                pa -= dsi->virt_base;
+
             va = xc_map_foreign_range(
                 xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
             chunksz = phdr->p_memsz - done;
diff -r 175ad739d8bc tools/libxc/xg_private.h
--- a/tools/libxc/xg_private.h  Wed Feb 22 20:52:30 2006
+++ b/tools/libxc/xg_private.h  Thu Feb 23 10:31:01 2006
@@ -131,6 +131,7 @@
     unsigned long v_kernstart;
     unsigned long v_kernend;
     unsigned long v_kernentry;
+    unsigned long virt_base;
 
     unsigned int  load_symtab;
     unsigned int  pae_kernel;
diff -r 175ad739d8bc xen/common/elf.c
--- a/xen/common/elf.c  Wed Feb 22 20:52:30 2006
+++ b/xen/common/elf.c  Thu Feb 23 10:31:01 2006
@@ -24,6 +24,7 @@
     Elf_Phdr *phdr;
     Elf_Shdr *shdr;
     unsigned long kernstart = ~0UL, kernend=0UL;
+    unsigned long sstart, send;
     char *shstrtab, *guestinfo=NULL, *p;
     char *elfbase = (char *)dsi->image_addr;
     int h;
@@ -77,6 +78,8 @@
             return -EINVAL;
         }
 
+        if ( (p = strstr(guestinfo, "VIRT_BASE=")) != NULL )
+            dsi->virt_base = simple_strtoul(p+10, &p, 0);
         break;
     }
 
@@ -87,11 +90,38 @@
         phdr = (Elf_Phdr *)(elfbase + ehdr->e_phoff + (h*ehdr->e_phentsize));
         if ( !is_loadable_phdr(phdr) )
             continue;
-        if ( phdr->p_paddr < kernstart )
-            kernstart = phdr->p_paddr;
-        if ( (phdr->p_paddr + phdr->p_memsz) > kernend )
-            kernend = phdr->p_paddr + phdr->p_memsz;
-    }
+        sstart = phdr->p_paddr;
+        send   = phdr->p_paddr + phdr->p_memsz;
+        /*
+         * bug comparibility alert: old linux kernels used to have
+         * virtual addresses in the paddr headers, whereas newer ones
+         * (since kexec merge, around 2.6.14) correctly use physical
+         * addresses.
+         *
+         * As we want to be able to boot both kinds of kernels we'll
+         * do some guesswork here: If paddr is greater than virt_base
+         * we assume it is a old kernel and use it as-is.  Otherwise
+         * we'll add virt_base to get the correct address.
+         */
+        if (sstart < dsi->virt_base) {
+            sstart += dsi->virt_base;
+            send   += dsi->virt_base;
+        }
+        printk("%s: program hdr: %08lx (=vaddr)  "
+               "paddr: %08lx  filesz: %08lx  memsz: %08lx  =>  %08lx-%08lx\n",
+               __FUNCTION__,
+               (unsigned long)phdr->p_vaddr,
+               (unsigned long)phdr->p_paddr,
+               (unsigned long)phdr->p_filesz,
+               (unsigned long)phdr->p_memsz,
+               sstart, send);
+        if ( sstart < kernstart )
+            kernstart = sstart;
+        if ( send > kernend )
+            kernend = send;
+    }
+    printk("%s: entry point: %08lx\n", __FUNCTION__,
+           (unsigned long)ehdr->e_entry);
 
     if ( (kernstart > kernend) || 
          (ehdr->e_entry < kernstart) || 
@@ -127,6 +157,7 @@
     char *elfbase = (char *)dsi->image_addr;
     Elf_Ehdr *ehdr = (Elf_Ehdr *)dsi->image_addr;
     Elf_Phdr *phdr;
+    unsigned long vaddr;
     int h;
   
     for ( h = 0; h < ehdr->e_phnum; h++ ) 
@@ -134,11 +165,15 @@
         phdr = (Elf_Phdr *)(elfbase + ehdr->e_phoff + (h*ehdr->e_phentsize));
         if ( !is_loadable_phdr(phdr) )
             continue;
+        vaddr = phdr->p_paddr;
+        if (vaddr < dsi->virt_base)
+            vaddr += dsi->virt_base;
         if ( phdr->p_filesz != 0 )
-            memcpy((char *)phdr->p_paddr, elfbase + phdr->p_offset, 
+            memcpy((char *)vaddr,
+                   elfbase + phdr->p_offset, 
                    phdr->p_filesz);
         if ( phdr->p_memsz > phdr->p_filesz )
-            memset((char *)phdr->p_paddr + phdr->p_filesz, 0, 
+            memset((char *)phdr->p_vaddr + phdr->p_filesz, 0, 
                    phdr->p_memsz - phdr->p_filesz);
     }
 
diff -r 175ad739d8bc xen/include/xen/sched.h
--- a/xen/include/xen/sched.h   Wed Feb 22 20:52:30 2006
+++ b/xen/include/xen/sched.h   Thu Feb 23 10:31:01 2006
@@ -166,6 +166,7 @@
     unsigned long v_kernstart;
     unsigned long v_kernend;
     unsigned long v_kernentry;
+    unsigned long virt_base;
     /* Initialised by loader: Private. */
     unsigned int  load_symtab;
     unsigned long symtab_addr;
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.