summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-04-13 19:11:30 -0700
committerH. Peter Anvin <hpa@zytor.com>2009-04-13 19:11:30 -0700
commitf2ebe30cefa59def4bc590f3ced08465b839430e (patch)
tree383fb040cd3624aa2d531c8dcd7dfd51579fe16d
parent3a1bc74b6c26b55a52459d9748375bf3aa15db37 (diff)
downloadsyslinux-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.h3
-rw-r--r--com32/lib/syslinux/shuffle_pm.c2
-rw-r--r--com32/lib/syslinux/shuffle_rm.c23
-rw-r--r--com32/lib/syslinux/zonelist.c15
-rw-r--r--core/bcopyxx.inc15
-rw-r--r--core/bootsect.inc43
-rw-r--r--core/layout.inc3
-rw-r--r--core/syslinux.ld15
-rw-r--r--doc/comboot.txt19
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, &regstub, &stublen);
+ rv = syslinux_memmap_find(tmap, SMT_FREE, &regstub, &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, &regstub, &stublen);
+ rv = syslinux_memmap_find(tmap, SMT_FREE, &regstub, &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, &regstub, &stublen);
+ rv = syslinux_memmap_find(tmap, SMT_FREE, &regstub, &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.