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
|