diff options
author | H. Peter Anvin <hpa@zytor.com> | 2009-04-13 19:11:30 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2009-04-13 19:11:30 -0700 |
commit | f2ebe30cefa59def4bc590f3ced08465b839430e (patch) | |
tree | 383fb040cd3624aa2d531c8dcd7dfd51579fe16d | |
parent | 3a1bc74b6c26b55a52459d9748375bf3aa15db37 (diff) | |
download | syslinux-f2ebe30cefa59def4bc590f3ced08465b839430e.tar.gz |
shuffler: work around KVM problem with the new shuffler
KVM uses V86 mode to simulate real mode. This causes problems with
the new shuffler. This changes the shuffler handover to be in
16-bit protected mode instead, and requires the stub to do the actual
entry to real mode. For the KVM hack to work, all segments must have:
(seg.base & 0xfff0000f) == 0 && seg.limit == 0xffff
As a result, we have to make sure the real-mode entry stub is
paragraph-aligned, lest we violate the first criterion.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r-- | com32/include/syslinux/movebits.h | 3 | ||||
-rw-r--r-- | com32/lib/syslinux/shuffle_pm.c | 2 | ||||
-rw-r--r-- | com32/lib/syslinux/shuffle_rm.c | 23 | ||||
-rw-r--r-- | com32/lib/syslinux/zonelist.c | 15 | ||||
-rw-r--r-- | core/bcopyxx.inc | 15 | ||||
-rw-r--r-- | core/bootsect.inc | 43 | ||||
-rw-r--r-- | core/layout.inc | 3 | ||||
-rw-r--r-- | core/syslinux.ld | 15 | ||||
-rw-r--r-- | doc/comboot.txt | 19 |
9 files changed, 85 insertions, 53 deletions
diff --git a/com32/include/syslinux/movebits.h b/com32/include/syslinux/movebits.h index ff3711ee..f0cb2774 100644 --- a/com32/include/syslinux/movebits.h +++ b/com32/include/syslinux/movebits.h @@ -79,6 +79,9 @@ int syslinux_memmap_largest(struct syslinux_memmap *list, addr_t *start, addr_t *len); void syslinux_free_memmap(struct syslinux_memmap *list); struct syslinux_memmap *syslinux_dup_memmap(struct syslinux_memmap *list); +int syslinux_memmap_find(struct syslinux_memmap *list, + enum syslinux_memmap_types type, + addr_t *start, addr_t *len, addr_t align); /* Debugging functions */ void syslinux_dump_movelist(FILE *file, struct syslinux_movelist *ml); diff --git a/com32/lib/syslinux/shuffle_pm.c b/com32/lib/syslinux/shuffle_pm.c index c9bc0407..69847639 100644 --- a/com32/lib/syslinux/shuffle_pm.c +++ b/com32/lib/syslinux/shuffle_pm.c @@ -59,7 +59,7 @@ int syslinux_shuffle_boot_pm(struct syslinux_movelist *fraglist, regstub = 0x800; /* Locate anywhere above this point */ stublen = sizeof handoff_code; - rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen); + rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen, 1); syslinux_free_memmap(tmap); if (rv) return -1; diff --git a/com32/lib/syslinux/shuffle_rm.c b/com32/lib/syslinux/shuffle_rm.c index 3ef8de7a..fd103d0f 100644 --- a/com32/lib/syslinux/shuffle_rm.c +++ b/com32/lib/syslinux/shuffle_rm.c @@ -51,7 +51,7 @@ int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist, bool sti; } *rp; int i, rv; - uint8_t handoff_code[5*5+8*6+1+5], *p; + uint8_t handoff_code[8+5*5+8*6+1+5], *p; struct syslinux_memmap *tmap; addr_t regstub, stublen; @@ -59,11 +59,15 @@ int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist, if (!tmap) return -1; - /* Search for a good place to put the real-mode register stub. - We prefer it as low as possible above 0x800. */ + /* + * Search for a good place to put the real-mode register stub. + * We prefer it as low as possible above 0x800. KVM barfs horribly + * if we're not aligned to a paragraph boundary, so set the alignment + * appropriately. + */ regstub = 0x800; stublen = sizeof handoff_code; - rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen); + rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen, 16); if (rv || (regstub > 0x100000 - sizeof handoff_code)) { /* @@ -74,7 +78,7 @@ int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist, */ regstub = 0x510; /* Try the 0x5xx segment... */ stublen = sizeof handoff_code; - rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen); + rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen, 16); if (!rv && (regstub > 0x100000 - sizeof handoff_code)) rv = -1; /* No acceptable memory found */ @@ -87,6 +91,12 @@ int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist, /* Build register-setting stub */ p = handoff_code; rp = (const struct syslinux_rm_regs_alt *)regs; + + *((uint32_t *)p) = 0xeac0220f; /* MOV CR0,EAX; JMP FAR */ + *((uint16_t *)(p+4)) = 8; /* Offset */ + *((uint16_t *)(p+6)) = regstub >> 4; /* Segment */ + p += 8; + for (i = 0; i < 6; i++) { if (i != 1) { /* Skip CS */ p[0] = 0xb8; /* MOV AX,imm16 */ @@ -111,8 +121,5 @@ int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist, sizeof handoff_code)) return -1; - /* Convert regstub to a CS:IP entrypoint pair */ - regstub = (SEG((void *)regstub) << 16) + OFFS((void *)regstub); - return syslinux_do_shuffle(fraglist, memmap, regstub, 0, bootflags); } diff --git a/com32/lib/syslinux/zonelist.c b/com32/lib/syslinux/zonelist.c index e3036b1a..77c1571a 100644 --- a/com32/lib/syslinux/zonelist.c +++ b/com32/lib/syslinux/zonelist.c @@ -37,6 +37,7 @@ */ #include <stdlib.h> +#include <syslinux/align.h> #include <syslinux/movebits.h> /* @@ -208,15 +209,21 @@ int syslinux_memmap_largest(struct syslinux_memmap *list, */ int syslinux_memmap_find(struct syslinux_memmap *list, enum syslinux_memmap_types type, - addr_t *start, addr_t *len) + addr_t *start, addr_t *len, addr_t align) { addr_t min_start = *start; addr_t min_len = *len; while (list->type != SMT_END) { - if (list->type == type && list->next->start > min_start) { - addr_t xstart = min_start > list->start ? min_start : list->start; - addr_t xlen = list->next->start - xstart; + if (list->type == type) { + addr_t xstart, xlen; + xstart = min_start > list->start ? min_start : list->start; + xstart = ALIGN_UP(xstart, align); + + if (xstart >= list->next->start) + continue; + + xlen = list->next->start - xstart; if (xlen >= min_len) { *start = xstart; *len = xlen; diff --git a/core/bcopyxx.inc b/core/bcopyxx.inc index 066499cb..7dc7c157 100644 --- a/core/bcopyxx.inc +++ b/core/bcopyxx.inc @@ -239,10 +239,8 @@ pm_shuffle: ; to real mode... pm_shuffle_real_mode: call .here -.here: pop eax - mov ebx,eax - add eax,.next-.here - mov [ebx-.here+.rm_entry],edi +.here: pop ebx + mov eax,edi mov [ebx-.here+bcopy_gdt.CS16+2],ax shr eax,16 mov [ebx-.here+bcopy_gdt.CS16+4],al @@ -253,16 +251,9 @@ pm_shuffle_real_mode: mov fs,eax mov gs,eax mov ss,eax - jmp PM_CS16:0 - bits 16 -.next: mov eax,cr0 and al,~1 - mov cr0,eax - jmp 0:0 -.rm_entry equ $-4 - - bits 32 + jmp PM_CS16:0 align 16 ; GDT descriptor entry diff --git a/core/bootsect.inc b/core/bootsect.inc index c7d82a49..7da4a25a 100644 --- a/core/bootsect.inc +++ b/core/bootsect.inc @@ -160,42 +160,48 @@ replace_bootstrap: push di ; Terminating entry... - lea eax,[di+12] + lea eax,[replace_stub] ; Entrypoint + push ax stosd xor ax,ax ; EAX[31:16] == 0 already - stosd ; Real mode + stosd ; 16-bit mode stosd ; End of list - ; Copy the stub - mov si,replace_stub - mov cx,replace_stub.len >> 2 + ; Copy the stub + pop di + mov si,__replacestub_lma + mov cx,__replacestub_dwords rep movsd xor ecx,ecx pop cx ; ECX <- length of list - pop word [di+replace_stub.ss] - pop word [di+replace_stub.esp] - pop dword [di+replace_stub.csip] + pop word [replace_stub.ss] + pop word [replace_stub.esp] + pop dword [replace_stub.csip] cli - mov ss,[di+replace_stub.ss] - mov esp,[di+replace_stub.esp] + mov ss,[replace_stub.ss] + mov esp,[replace_stub.esp] mov edi,trackbuf mov esi,edi jmp shuffle_and_boot_raw - ; This stub gets run after the shuffle, but not in-place. - ; THE ALIGNS ARE CRITICAL. - align 4 + ; This stub gets run after the shuffle. It is copied + ; below 0x7c00 in order to properly handle the case + ; of bootstrap replacement. + section .replacestub replace_stub: + mov cr0,eax + jmp 0:.next +.next: mov ax,strict word 0 -.ss equ $-2-.end +.ss equ $-2 mov ss,ax mov esp,strict dword 0 -.esp: equ $-4-.end +.esp equ $-4 pop gs pop fs pop es @@ -203,7 +209,6 @@ replace_stub: popad popfd jmp 0:0 -.csip equ $-4-.end - align 4 -.end: -.len equ $-replace_stub +.csip equ $-4 + + section .text diff --git a/core/layout.inc b/core/layout.inc index 2f6e8317..f75b0857 100644 --- a/core/layout.inc +++ b/core/layout.inc @@ -41,7 +41,7 @@ LATEBSS_START equ 0B800h ; Use .earlybss for things that MUST be in low memory. section .earlybss nobits start=BSS_START section .config write progbits align=4 - section .config.end write nobits align=4 + section .replacestub exec write progbits align=16 ; Use .bss for things that doesn't have to be in low memory; ; with .bss1 and .bss2 to offload. .earlybss should be used @@ -81,6 +81,7 @@ RBFG_brainfuck: resb 2048 ; Bigger than an Ethernet packet... extern __%1_len, __%1_dwords %endmacro SECINFO config + SECINFO replacestub global _start diff --git a/core/syslinux.ld b/core/syslinux.ld index 6f7f5a78..c1da230e 100644 --- a/core/syslinux.ld +++ b/core/syslinux.ld @@ -44,7 +44,7 @@ SECTIONS __bss_len = __bss_end - __bss_start; __bss_dwords = (__bss_len + 3) >> 2; - .config : AT (__config_lma) { + .config : AT (__config_lma) { __config_start = .; *(.config) __config_end = .; @@ -52,6 +52,15 @@ SECTIONS __config_len = __config_end - __config_start; __config_dwords = (__config_len + 3) >> 2; + . = ALIGN(16); + .replacestub : AT (__replacestub_lma) { + __replacestub_start = .; + *(.replacestub) + __replacestub_end = .; + } + __replacestub_len = __replacestub_end - __replacestub_start; + __replacestub_dwords = (__replacestub_len + 3) >> 2; + /* Stack */ STACK_BASE = 0x7c00 - STACK_LEN; @@ -99,6 +108,10 @@ SECTIONS __config_lma = .; . += SIZEOF(.config); + . = ALIGN(4); + __replacestub_lma = .; + . += SIZEOF(.replacestub); + /* ADV, must be the last intialized section */ . = ALIGN(512); diff --git a/doc/comboot.txt b/doc/comboot.txt index b1334511..9c81c489 100644 --- a/doc/comboot.txt +++ b/doc/comboot.txt @@ -937,13 +937,18 @@ AX=0024h [3.80] Cleanup, shuffle and boot, raw version entry, the destination is used as an entry point, and the source represents the type of entry point: - 0 Real mode (dst is CS:IP) + 0 16-bit protected mode (dst is CS.base) 1 Flat 32-bit protected mode (dst is EIP) - This routine does not set up any register state whatsoever, - including stack. It is the responsibility of the caller to - make sure the entry point provided sets up any registers - needed. This is particularly important that a real mode entry - point reloads all data segment registers at the earliest - possible point. + This routine does not set up any GPR register state + whatsoever, including stack. It is the responsibility of the + caller to make sure the entry point provided sets up any + registers needed. + For mode 0 (16-bit real mode), EAX will contain CR0 with bit 0 + masked out, suitable for loading into CR0 to immediately enter + real mode. + + In both mode 0 and mode 1, the data segments will be loaded + with base-zero read/write segments. For mode 0, B=0 and the + limits will be 64K, for mode 1, B=1 and the limits will be 4 GB. |