x86: get rid of BOOT_TRAMPOLINE We recently saw a machine that has the EBDA extending as low as 0x7c000, so that Xen fails to boot after relocating the trampoline. To fix this, I removed BOOT_TRAMPOLINE and bootsym_phys completely. Here are the parts: 1) the trampoline segment is set to 64k below the EBDA. head.S grows the ability to relocate the trampoline segment 2) reloc.c is made position-independent. It allocates data below the trampoline, whose address is passed in _eax. 3) cmdline.S is called before relocating, so all bootsym_phys there become sym_phys. Signed-off-by: Paolo Bonzini jb: - fall back to low memory size (instead of segment 0x7c00) if EBDA value is out of range - also add upper limit check on EBDA value - fix and simplify inline assembly operands in reloc_mbi_struct() - use lret instead of retf - renamed early_stack to wakeup_stack, defined and used now only in wakeup.S - aligned reloc.bin's end of .text to 16 bytes, so that checking __bss_start == end works reliably Signed-off-by: Jan Beulich --- a/xen/arch/x86/boot/Makefile +++ b/xen/arch/x86/boot/Makefile @@ -2,8 +2,7 @@ obj-bin-y += head.o head.o: reloc.S -BOOT_TRAMPOLINE := $(shell sed -n 's,^\#define[[:space:]]\{1\,\}BOOT_TRAMPOLINE[[:space:]]\{1\,\},,p' head.S) %.S: %.c - RELOC=$(BOOT_TRAMPOLINE) $(MAKE) -f build32.mk $@ + $(MAKE) -f build32.mk $@ reloc.S: head.S --- a/xen/arch/x86/boot/build32.mk +++ b/xen/arch/x86/boot/build32.mk @@ -16,9 +16,10 @@ CFLAGS := $(filter-out -flto,$(CFLAGS)) $(OBJCOPY) -O binary $< $@ %.lnk: %.o - $(LD) $(LDFLAGS_DIRECT) -N -Ttext $(RELOC) -o $@ $< + $(LD) $(LDFLAGS_DIRECT) -N -Ttext 0 -o $@ $< %.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ + $(CC) $(CFLAGS) -c -fpic $< -o $@ reloc.o: $(BASEDIR)/include/asm-x86/config.h +.PRECIOUS: %.bin %.lnk --- a/xen/arch/x86/boot/cmdline.S +++ b/xen/arch/x86/boot/cmdline.S @@ -164,13 +164,13 @@ cmdline_parse_early: pushl MB_cmdline(%ebx) call .Lfind_option test %eax,%eax - setnz bootsym_phys(skip_realmode) + setnz sym_phys(skip_realmode) /* Check for 'tboot=' command-line option. */ movl $sym_phys(.Ltboot_opt),4(%esp) call .Lfind_option test %eax,%eax - setnz bootsym_phys(skip_realmode) /* tboot= implies no-real-mode */ + setnz sym_phys(skip_realmode) /* tboot= implies no-real-mode */ .Lparse_edd: /* Check for 'edd=' command-line option. */ @@ -181,13 +181,13 @@ cmdline_parse_early: cmpb $'=',3(%eax) jne .Lparse_edid add $4,%eax - movb $2,bootsym_phys(opt_edd) /* opt_edd=2: edd=off */ + movb $2,sym_phys(opt_edd) /* opt_edd=2: edd=off */ cmpw $0x666f,(%eax) /* 0x666f == "of" */ je .Lparse_edid - decb bootsym_phys(opt_edd) /* opt_edd=1: edd=skipmbr */ + decb sym_phys(opt_edd) /* opt_edd=1: edd=skipmbr */ cmpw $0x6b73,(%eax) /* 0x6b73 == "sk" */ je .Lparse_edid - decb bootsym_phys(opt_edd) /* opt_edd=0: edd=on (default) */ + decb sym_phys(opt_edd) /* opt_edd=0: edd=on (default) */ .Lparse_edid: /* Check for 'edid=' command-line option. */ @@ -203,17 +203,17 @@ cmdline_parse_early: pushl $sym_phys(.Ledid_force) call .Lstr_prefix add $8,%esp - movb $2,bootsym_phys(opt_edid) /* opt_edid=2: edid=force */ + movb $2,sym_phys(opt_edid) /* opt_edid=2: edid=force */ test %eax,%eax jz .Lparse_vga push %ebx pushl $sym_phys(.Ledid_no) call .Lstr_prefix add $8,%esp - decb bootsym_phys(opt_edid) /* opt_edid=1: edid=no */ + decb sym_phys(opt_edid) /* opt_edid=1: edid=no */ test %eax,%eax jz .Lparse_vga - decb bootsym_phys(opt_edid) /* opt_edid=0: default */ + decb sym_phys(opt_edid) /* opt_edid=0: default */ .Lparse_vga: /* Check for 'vga=' command-line option. */ @@ -227,7 +227,7 @@ cmdline_parse_early: add $4,%eax /* Found the 'vga=' option. Default option is to display vga menu. */ - movw $ASK_VGA,bootsym_phys(boot_vid_mode) + movw $ASK_VGA,sym_phys(boot_vid_mode) /* Check for 'vga=text-80x. */ mov %eax,%ebx @@ -251,7 +251,7 @@ cmdline_parse_early: cmp %ax,%bx lodsw jne 1b - mov %ax,bootsym_phys(boot_vid_mode) + mov %ax,sym_phys(boot_vid_mode) jmp .Lcmdline_exit .Lparse_vga_gfx: @@ -270,7 +270,7 @@ cmdline_parse_early: push %ebx call .Latoi pop %esi - mov %ax,bootsym_phys(vesa_size)+0 + mov %ax,sym_phys(vesa_size)+0 /* skip 'x' */ lodsb cmpb $'x',%al @@ -279,7 +279,7 @@ cmdline_parse_early: push %esi call .Latoi pop %esi - mov %ax,bootsym_phys(vesa_size)+2 + mov %ax,sym_phys(vesa_size)+2 /* skip 'x' */ lodsb cmpb $'x',%al @@ -288,9 +288,9 @@ cmdline_parse_early: push %esi call .Latoi pop %esi - mov %ax,bootsym_phys(vesa_size)+4 + mov %ax,sym_phys(vesa_size)+4 /* commit to vesa mode */ - movw $VIDEO_VESA_BY_SIZE,bootsym_phys(boot_vid_mode) + movw $VIDEO_VESA_BY_SIZE,sym_phys(boot_vid_mode) jmp .Lcmdline_exit .Lparse_vga_mode: @@ -307,7 +307,7 @@ cmdline_parse_early: push %ebx call .Latoi add $4,%esp - mov %ax,bootsym_phys(boot_vid_mode) + mov %ax,sym_phys(boot_vid_mode) jmp .Lcmdline_exit .Lparse_vga_current: @@ -320,7 +320,7 @@ cmdline_parse_early: jnz .Lcmdline_exit /* We have 'vga=current'. */ - movw $VIDEO_CURRENT_MODE,bootsym_phys(boot_vid_mode) + movw $VIDEO_CURRENT_MODE,sym_phys(boot_vid_mode) .Lcmdline_exit: popa --- a/xen/arch/x86/boot/head.S +++ b/xen/arch/x86/boot/head.S @@ -9,9 +9,7 @@ .text .code32 -#define BOOT_TRAMPOLINE 0x7c000 #define sym_phys(sym) ((sym) - __XEN_VIRT_START) -#define bootsym_phys(sym) ((sym) - trampoline_start + BOOT_TRAMPOLINE) #define BOOT_CS32 0x0008 #define BOOT_CS64 0x0010 @@ -79,6 +77,23 @@ __start: cmp $0x2BADB002,%eax jne not_multiboot + /* Set up trampoline segment 64k below EBDA */ + movzwl 0x40e,%eax /* EBDA segment */ + cmp $0xa000,%eax /* sanity check (high) */ + jae 0f + cmp $0x4000,%eax /* sanity check (low) */ + jae 1f +0: + movzwl 0x413,%eax /* use base memory size on failure */ + shl $10-4,%eax +1: + sub $0x1000,%eax + + /* From arch/x86/smpboot.c: start_eip had better be page-aligned! */ + xor %al, %al + shl $4, %eax + mov %eax,sym_phys(trampoline_phys) + /* Save the Multiboot info struct (after relocation) for later use. */ mov $sym_phys(cpu0_stack)+1024,%esp push %ebx @@ -190,7 +205,7 @@ __start: #endif /* Apply relocations to bootstrap trampoline. */ - mov $BOOT_TRAMPOLINE,%edx + mov sym_phys(trampoline_phys),%edx mov $sym_phys(__trampoline_rel_start),%edi mov %edx,sym_phys(trampoline_phys) 1: @@ -200,22 +215,35 @@ __start: cmp $sym_phys(__trampoline_rel_stop),%edi jb 1b + /* Patch in the trampoline segment. */ + shr $4,%edx + mov $sym_phys(__trampoline_seg_start),%edi +1: + mov (%edi),%eax + mov %dx,(%edi,%eax) + add $4,%edi + cmp $sym_phys(__trampoline_seg_stop),%edi + jb 1b + + call cmdline_parse_early + + /* Switch to low-memory stack. */ + mov sym_phys(trampoline_phys),%edi + lea 0x10000(%edi),%esp + lea trampoline_boot_cpu_entry-trampoline_start(%edi),%eax + pushl $BOOT_CS32 + push %eax + /* Copy bootstrap trampoline to low memory, below 1MB. */ mov $sym_phys(trampoline_start),%esi - mov %edx,%edi mov $trampoline_end - trampoline_start,%ecx rep movsb - lea early_stack-trampoline_start(%edx),%esp - call cmdline_parse_early - /* Jump into the relocated trampoline. */ - jmp $BOOT_CS32,$bootsym_phys(trampoline_boot_cpu_entry) + lret #include "cmdline.S" -#undef bootsym_phys - reloc: #include "reloc.S" --- a/xen/arch/x86/boot/reloc.c +++ b/xen/arch/x86/boot/reloc.c @@ -10,42 +10,49 @@ * Keir Fraser */ +/* entered with %eax = BOOT_TRAMPOLINE */ asm ( " .text \n" " .globl _start \n" "_start: \n" - " mov $_start,%edi \n" " call 1f \n" - "1: pop %esi \n" - " sub $1b-_start,%esi \n" - " mov $__bss_start-_start,%ecx \n" - " rep movsb \n" - " xor %eax,%eax \n" - " mov $_end,%ecx \n" - " sub %edi,%ecx \n" - " rep stosb \n" - " mov $reloc,%eax \n" - " jmp *%eax \n" + "1: pop %ebx \n" + " mov %eax,alloc-1b(%ebx) \n" + " mov $_end,%ecx \n" /* check that BSS is empty! */ + " sub $__bss_start,%ecx \n" + " jz reloc \n" + "1: jmp 1b \n" + ); + +/* This is our data. Because the code must be relocatable, no BSS is + * allowed. All data is accessed PC-relative with inline assembly. + */ +asm ( + "alloc: \n" + " .long 0 \n" + " .subsection 1 \n" + " .p2align 4, 0xcc \n" + " .subsection 0 \n" ); typedef unsigned int u32; #include "../../../include/xen/multiboot.h" -extern char _start[]; - -static void *memcpy(void *dest, const void *src, unsigned int n) -{ - char *s = (char *)src, *d = dest; - while ( n-- ) - *d++ = *s++; - return dest; -} - static void *reloc_mbi_struct(void *old, unsigned int bytes) { - static void *alloc = &_start; - alloc = (void *)(((unsigned long)alloc - bytes) & ~15ul); - return memcpy(alloc, old, bytes); + void *new; + asm( + " call 1f \n" + "1: pop %%edx \n" + " mov alloc-1b(%%edx),%0 \n" + " sub %1,%0 \n" + " and $~15,%0 \n" + " mov %0,alloc-1b(%%edx) \n" + " mov %0,%%edi \n" + " rep movsb \n" + : "=&r" (new), "+c" (bytes), "+S" (old) + : : "edx", "edi"); + return new; } static char *reloc_mbi_string(char *old) --- a/xen/arch/x86/boot/trampoline.S +++ b/xen/arch/x86/boot/trampoline.S @@ -11,6 +11,13 @@ .long 111b - (off) - .; \ .popsection +#define bootsym_segrel(sym, off) \ + $0,$bootsym(sym); \ +111:; \ + .pushsection .trampoline_seg, "a"; \ + .long 111b - (off) - .; \ + .popsection + .globl trampoline_realmode_entry trampoline_realmode_entry: mov %cs,%ax @@ -151,14 +158,14 @@ trampoline_boot_cpu_entry: 1: mov %eax,%cr0 # CR0.PE = 0 (leave protected mode) /* Load proper real-mode values into %cs, %ds, %es and %ss. */ - ljmp $(BOOT_TRAMPOLINE>>4),$bootsym(1f) + ljmp bootsym_segrel(1f,2) 1: mov %cs,%ax mov %ax,%ds mov %ax,%es mov %ax,%ss /* Initialise stack pointer and IDT, and enable irqs. */ - mov $bootsym(early_stack),%sp + xor %sp,%sp lidt bootsym(rm_idt) sti @@ -220,7 +227,3 @@ rm_idt: .word 256*4-1, 0, 0 #include "edd.S" #include "video.S" #include "wakeup.S" - - .align 16 - .fill PAGE_SIZE,1,0 -early_stack: --- a/xen/arch/x86/boot/wakeup.S +++ b/xen/arch/x86/boot/wakeup.S @@ -11,7 +11,7 @@ ENTRY(wakeup_start) movw %cs, %ax movw %ax, %ds movw %ax, %ss # A stack required for BIOS call - movw $wakesym(early_stack), %sp + movw $wakesym(wakeup_stack), %sp pushl $0 # Kill dangerous flag early popfl @@ -101,7 +101,7 @@ real_magic: .long 0x12345678 .globl video_mode, video_flags video_mode: .long 0 video_flags: .long 0 -trampoline_seg: .word BOOT_TRAMPOLINE >> 4 +trampoline_seg: .word 0 .pushsection .trampoline_seg, "a" .long trampoline_seg - . .popsection @@ -116,7 +116,7 @@ wakeup_32: mov $BOOT_DS, %eax mov %eax, %ds mov %eax, %ss - mov $bootsym_rel(early_stack, 4, %esp) + mov $bootsym_rel(wakeup_stack, 4, %esp) # check saved magic again mov $sym_phys(saved_magic), %eax @@ -188,3 +188,7 @@ ret_point: bogus_saved_magic: movw $0x0e00 + 'S', 0xb8014 jmp bogus_saved_magic + + .align 16 + .fill PAGE_SIZE,1,0 +wakeup_stack: