summaryrefslogtreecommitdiff
path: root/com32/lib/syslinux
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 /com32/lib/syslinux
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>
Diffstat (limited to 'com32/lib/syslinux')
-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
3 files changed, 27 insertions, 13 deletions
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;