[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH] Elf loader fixes
Hi folks, The Xen ELF kernel loader is quite quirky wrt. physical and virtual addresses, probably for historical reasons, linux got that wrong too until very recently (kexec merge in 2.6.14 or so). The patch below fixes that. Changes: * Fix linux kernel ELF entry point (also submitted to lkml) * Drop LOAD_OFFSET re-#define hack in xen headers. * Fix both dom0 and libxc elf loaders. * add quick mode so loading old linux kernels doesn't break. Linux-wise everything should be OK with that, but it might break other OS'es which also use the ELF loader (in case they create bug-compatible ELF headers with broken paddr entries ...). please apply, Gerd diff -r 5abf652c4c52 linux-2.6-xen-sparse/arch/i386/kernel/vmlinux.lds.S --- a/linux-2.6-xen-sparse/arch/i386/kernel/vmlinux.lds.S Tue Feb 21 18:36:00 2006 +++ b/linux-2.6-xen-sparse/arch/i386/kernel/vmlinux.lds.S Wed Feb 22 12:20:06 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 5abf652c4c52 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 Tue Feb 21 18:36:00 2006 +++ b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/page.h Wed Feb 22 12:20:06 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 5abf652c4c52 tools/libxc/xc_load_elf.c --- a/tools/libxc/xc_load_elf.c Tue Feb 21 18:36:00 2006 +++ b/tools/libxc/xc_load_elf.c Wed Feb 22 12:20:06 2006 @@ -138,10 +138,10 @@ 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; + if ( phdr->p_vaddr < kernstart ) + kernstart = phdr->p_vaddr; + if ( (phdr->p_vaddr + phdr->p_memsz) > kernend ) + kernend = phdr->p_vaddr + phdr->p_memsz; } if ( (kernstart > kernend) || @@ -189,7 +189,18 @@ for ( done = 0; done < phdr->p_filesz; done += chunksz ) { - pa = (phdr->p_paddr + done) - dsi->v_start; + if (phdr->p_paddr == phdr->p_vaddr) { + /* + * Bug compatibility alert: In older linux kernels + * p_paddr is broken, it doesn't contain the physical + * address but instead is identical to p_vaddr. Thus + * we can't use it directly, instead we'll guess it + * using dsi->v_start. + */ + pa = (phdr->p_vaddr + done) - dsi->v_start; + } else { + pa = (phdr->p_paddr + done); + } va = xc_map_foreign_range( xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]); chunksz = phdr->p_filesz - done; @@ -202,7 +213,12 @@ for ( ; done < phdr->p_memsz; done += chunksz ) { - pa = (phdr->p_paddr + done) - dsi->v_start; + if (phdr->p_paddr == phdr->p_vaddr) { + /* bug compatibility alert, see above */ + pa = (phdr->p_vaddr + done) - dsi->v_start; + } else { + pa = (phdr->p_paddr + done); + } va = xc_map_foreign_range( xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]); chunksz = phdr->p_memsz - done; diff -r 5abf652c4c52 xen/arch/x86/domain.c --- a/xen/arch/x86/domain.c Tue Feb 21 18:36:00 2006 +++ b/xen/arch/x86/domain.c Wed Feb 22 12:20:06 2006 @@ -346,7 +346,7 @@ struct vcpu *v, struct vcpu_guest_context *c) { struct domain *d = v->domain; - unsigned long phys_basetab; + unsigned long phys_basetab = 0; int i, rc; /* diff -r 5abf652c4c52 xen/common/elf.c --- a/xen/common/elf.c Tue Feb 21 18:36:00 2006 +++ b/xen/common/elf.c Wed Feb 22 12:20:06 2006 @@ -23,7 +23,8 @@ Elf_Ehdr *ehdr = (Elf_Ehdr *)dsi->image_addr; Elf_Phdr *phdr; Elf_Shdr *shdr; - unsigned long kernstart = ~0UL, kernend=0UL; + unsigned long v_kernstart = ~0UL, v_kernend=0UL; + unsigned long p_kernstart = ~0UL, p_kernend=0UL; char *shstrtab, *guestinfo=NULL, *p; char *elfbase = (char *)dsi->image_addr; int h; @@ -87,21 +88,31 @@ 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; - } - - if ( (kernstart > kernend) || - (ehdr->e_entry < kernstart) || - (ehdr->e_entry > kernend) ) + printk("%s: phdr: vaddr %08lx paddr %08lx filesz %08lx\n", + __FUNCTION__, + (unsigned long)phdr->p_vaddr, + (unsigned long)phdr->p_paddr, + (unsigned long)phdr->p_filesz); + if ( phdr->p_vaddr < v_kernstart ) + v_kernstart = phdr->p_vaddr; + if ( (phdr->p_vaddr + phdr->p_memsz) > v_kernend ) + v_kernend = phdr->p_vaddr + phdr->p_memsz; + if ( phdr->p_paddr < p_kernstart ) + p_kernstart = phdr->p_paddr; + if ( (phdr->p_paddr + phdr->p_memsz) > p_kernend ) + p_kernend = phdr->p_paddr + phdr->p_memsz; + } + + if ( (v_kernstart > v_kernend) || + (p_kernstart > p_kernend) || + (ehdr->e_entry < v_kernstart) || + (ehdr->e_entry > v_kernend) ) { printk("Malformed ELF image.\n"); return -EINVAL; } - dsi->v_start = kernstart; + dsi->v_start = v_kernstart; if ( guestinfo != NULL ) { @@ -112,10 +123,26 @@ dsi->load_symtab = 1; } - dsi->v_kernstart = kernstart; - dsi->v_kernend = kernend; + dsi->v_kernstart = v_kernstart; + dsi->v_kernend = v_kernend; dsi->v_kernentry = ehdr->e_entry; dsi->v_end = dsi->v_kernend; + + if (p_kernstart == v_kernstart) { + /* + * Bug compatibility alert: In older linux kernels + * p_paddr is broken, it doesn't contain the physical + * address but instead is identical to p_vaddr. Thus + * we can't use it directly, instead we'll guess it + * using dsi->v_start. + */ + printk("%s: linux kernel paddr quirk\n", __FUNCTION__); + dsi->p_kernstart = v_kernstart - dsi->v_start; + dsi->p_kernend = v_kernend - dsi->v_start; + } else { + dsi->p_kernstart = p_kernstart; + dsi->p_kernend = p_kernend; + } loadelfsymtab(dsi, 0); @@ -135,10 +162,10 @@ if ( !is_loadable_phdr(phdr) ) continue; if ( phdr->p_filesz != 0 ) - memcpy((char *)phdr->p_paddr, elfbase + phdr->p_offset, + memcpy((char *)phdr->p_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 5abf652c4c52 xen/include/xen/sched.h --- a/xen/include/xen/sched.h Tue Feb 21 18:36:00 2006 +++ b/xen/include/xen/sched.h Wed Feb 22 12:20:06 2006 @@ -165,6 +165,8 @@ unsigned long v_end; unsigned long v_kernstart; unsigned long v_kernend; + unsigned long p_kernstart; + unsigned long p_kernend; unsigned long v_kernentry; /* Initialised by loader: Private. */ unsigned int load_symtab; _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |