summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-06-19 10:36:07 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-06-19 10:36:07 -0700
commita350eabac495ab9f8bb7da94e2d784ccba2b3bc5 (patch)
treedcf0f356682ba5cba0458604b0b1ee1242cb9f9d
parent0e5138b77e40bdc214c470d4b35245445f9002f2 (diff)
downloadsyslinux-a350eabac495ab9f8bb7da94e2d784ccba2b3bc5.tar.gz
chain.c: we can't rely on dosmem and int13 in here...
dosmem and int13 can change during boot loader shutdown. It is therefore unsafe to rely on them retaining their value from running in chain.c32 itself. Instead, we have to install a suitable stub which makes that particular determination at runtime. Fortunately that is not all that hard to do.
-rw-r--r--com32/modules/chain.c205
1 files changed, 119 insertions, 86 deletions
diff --git a/com32/modules/chain.c b/com32/modules/chain.c
index 8edf756e..f1c75c22 100644
--- a/com32/modules/chain.c
+++ b/com32/modules/chain.c
@@ -341,22 +341,11 @@ find_logical_partition(int whichpart, char *table, struct part_entry *self,
static void do_boot(void *boot_sector, size_t boot_size,
struct syslinux_rm_regs *regs)
{
- static const uint8_t swapstub[] = {
- 0x53, /* 00: push bx */
- 0x0f,0xb6,0xda, /* 01: movzx bx,dl */
- 0x2e,0x8a,0x57,0x10, /* 04: mov dl,[cs:bx+16] */
- 0x5b, /* 08: pop bx */
- 0xea,0,0,0,0, /* 09: jmp far 0:0 */
- 0x90,0x90, /* 0E: nop; nop */
- };
uint16_t * const bios_fbm = (uint16_t *)0x413;
- uint32_t * const int13_vec = (uint32_t *)(0x13*4);
- uint16_t old_bios_fbm = *bios_fbm;
- uint32_t old_int13_vec = *int13_vec;
+ addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */
struct syslinux_memmap *mmap;
struct syslinux_movelist *mlist = NULL;
- addr_t dosmem = old_bios_fbm << 10;
- addr_t protect;
+ addr_t endimage;
uint8_t driveno = regs->edx.b[0];
uint8_t swapdrive = driveno & 0x80;
int i;
@@ -369,29 +358,6 @@ static void do_boot(void *boot_sector, size_t boot_size,
return;
}
- if (opt.swap && driveno != swapdrive) {
- uint8_t *p;
-
- regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
-
- dosmem -= 1024;
- p = (uint8_t *)dosmem;
-
- /* Install swapper stub */
- memset(p, 0, 1024); /* For debugging... */
- memcpy(p, swapstub, sizeof swapstub);
- *(uint32_t *)&p[0x0a] = old_int13_vec;
- p += sizeof swapstub;
-
- /* Mapping table; start out with identity mapping everything */
- for (i = 0; i < 256; i++)
- p[i] = i;
-
- /* And the actual swap */
- p[driveno] = swapdrive;
- p[swapdrive] = driveno;
- }
-
if (loadbase < 0x7c00) {
/* Special hack: if we are to be loaded below 0x7c00, we need to handle
the part that goes below 0x7c00 specially, since that's where the
@@ -401,87 +367,154 @@ static void do_boot(void *boot_sector, size_t boot_size,
The only tricky bit is that we need to set up registers for our
move, and then restore them to what they should be at the end of
the code. */
- static const uint8_t copy_down_code[] = {
- 0xf3, 0x66, 0xa5, /* rep movsd */
- 0xbe, 0, 0, /* mov si,0 */
- 0xbf, 0, 0, /* mov di,0 */
- 0x8e, 0xde, /* mov ds,si */
- 0x8e, 0xc7, /* mov es,di */
- 0x66, 0xb9, 0, 0, 0, 0, /* mov ecx,0 */
- 0x66, 0xbe, 0, 0, 0, 0, /* mov esi,0 */
- 0x66, 0xbf, 0, 0, 0, 0, /* mov edi,0 */
- 0xea, 0, 0, 0, 0, /* jmp 0:0 */
+ static uint8_t copy_down_code[] = {
+ 0xf3, 0x66, 0xa5, /* 00: rep movsd */
+ 0xbe, 0, 0, /* 03: mov si,0 */
+ 0xbf, 0, 0, /* 06: mov di,0 */
+ 0x8e, 0xde, /* 09: mov ds,si */
+ 0x8e, 0xc7, /* 0b: mov es,di */
+ 0x66, 0xb9, 0, 0, 0, 0, /* 0d: mov ecx,0 */
+ 0x66, 0xbe, 0, 0, 0, 0, /* 13: mov esi,0 */
+ 0x66, 0xbf, 0, 0, 0, 0, /* 19: mov edi,0 */
+ 0xea, 0, 0, 0, 0, /* 1f: jmp 0:0 */
/* pad out to segment boundary */
- 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, /* 24: ... */
+ 0x90, 0x90, 0x90, 0x90, /* 28: ... */
+ 0x90, 0x90, 0x90, 0x90, /* 2c: ... */
};
- uint8_t *p;
size_t low_size = min(boot_size, 0x7c00-loadbase);
size_t high_size = boot_size - low_size;
+ size_t low_addr = (0x7c00 + high_size + 15) & ~15;
+ size_t move_addr = (low_addr + low_size + 15) & ~15;
const size_t move_size = sizeof copy_down_code;
- if (boot_size >= dosmem-move_size-0x7c00)
+ if (move_addr+move_size >= dosmem-0x7c00)
goto too_big;
- if (high_size)
- if (syslinux_add_movelist(&mlist, 0x7c00,
- (addr_t)boot_sector+low_size, high_size))
- goto enomem;
-
- if (syslinux_add_movelist(&mlist, (dosmem-low_size-move_size) & ~15,
- (addr_t)boot_sector, low_size))
- goto enomem;
-
- p = (uint8_t *)dosmem-move_size;
- protect = (addr_t)p;
- memcpy(p, copy_down_code, move_size);
- *(uint16_t *)(p+0x04) = regs->ds;
- *(uint16_t *)(p+0x07) = regs->es;
- *(uint32_t *)(p+0x0f) = regs->ecx.l;
- *(uint32_t *)(p+0x15) = regs->esi.l;
- *(uint32_t *)(p+0x1b) = regs->edi.l;
- *(uint16_t *)(p+0x20) = regs->ip;
- *(uint16_t *)(p+0x22) = regs->cs;
+ *(uint16_t *)&copy_down_code[0x04] = regs->ds;
+ *(uint16_t *)&copy_down_code[0x07] = regs->es;
+ *(uint32_t *)&copy_down_code[0x0f] = regs->ecx.l;
+ *(uint32_t *)&copy_down_code[0x15] = regs->esi.l;
+ *(uint32_t *)&copy_down_code[0x1b] = regs->edi.l;
+ *(uint16_t *)&copy_down_code[0x20] = regs->ip;
+ *(uint16_t *)&copy_down_code[0x22] = regs->cs;
regs->ecx.l = (low_size+3) >> 2;
regs->esi.l = 0;
regs->edi.l = loadbase & 15;
- regs->ds = (dosmem-low_size-move_size) >> 4;
+ regs->ds = low_addr >> 4;
regs->es = loadbase >> 4;
- regs->cs = (addr_t)p >> 4;
+ regs->cs = move_addr >> 4;
regs->ip = 0;
+
+ endimage = move_addr + move_size;
+
+ if (high_size)
+ if (syslinux_add_movelist(&mlist, 0x7c00,
+ (addr_t)boot_sector+low_size, high_size))
+ goto enomem;
+ if (syslinux_add_movelist(&mlist, low_addr,
+ (addr_t)boot_sector, low_size))
+ goto enomem;
+ if (syslinux_add_movelist(&mlist, move_addr,
+ (addr_t)copy_down_code, move_size))
+ goto enomem;
} else {
/* Nothing below 0x7c00, much simpler... */
if (boot_size >= dosmem-0x7c00)
goto too_big;
- protect = dosmem;
+ endimage = loadbase + boot_size;
if (syslinux_add_movelist(&mlist, loadbase, (addr_t)boot_sector,
boot_size))
goto enomem;
}
- /* Tell the shuffler not to muck with this area... */
- syslinux_add_memmap(&mmap, protect, 0xa0000-protect, SMT_RESERVED);
+ if (opt.swap && driveno != swapdrive) {
+ static const uint8_t swapstub_master[] = {
+ /* The actual swap code */
+ 0x53, /* 00: push bx */
+ 0x0f,0xb6,0xda, /* 01: movzx bx,dl */
+ 0x2e,0x8a,0x57,0x60, /* 04: mov dl,[cs:bx+0x60] */
+ 0x5b, /* 08: pop bx */
+ 0xea,0,0,0,0, /* 09: jmp far 0:0 */
+ 0x90,0x90, /* 0E: nop; nop */
+ /* Code to install this in the right location */
+ /* Entry with DS = CS; ES = SI = 0; CX = 256 */
+ 0x26,0x66,0x8b,0x7c,0x4c, /* 10: mov edi,[es:si+4*0x13] */
+ 0x66,0x89,0x3e,0x0a,0x00, /* 15: mov [0x0A],edi */
+ 0x26,0x8b,0x3e,0x13,0x04, /* 1A: mov di,[es:0x413] */
+ 0x4f, /* 1F: dec di */
+ 0x26,0x89,0x3e,0x13,0x04, /* 20: mov [es:0x413],di */
+ 0x66,0xc1,0xe7,0x16, /* 25: shl edi,16+6 */
+ 0x26,0x66,0x89,0x7c,0x4c, /* 29: mov [es:si+4*0x13],edi */
+ 0x66,0xc1,0xef,0x10, /* 2E: shr edi,16 */
+ 0x8e,0xc7, /* 32: mov es,di */
+ 0x31,0xff, /* 34: xor di,di */
+ 0xf3,0x66,0xa5, /* 36: rep movsd */
+ 0xbe,0,0, /* 39: mov si,0 */
+ 0xbf,0,0, /* 3C: mov di,0 */
+ 0x8e,0xde, /* 3F: mov ds,si */
+ 0x8e,0xc7, /* 41: mov es,di */
+ 0x66,0xb9,0,0,0,0, /* 43: mov ecx,0 */
+ 0x66,0xbe,0,0,0,0, /* 49: mov esi,0 */
+ 0x66,0xbf,0,0,0,0, /* 4F: mov edi,0 */
+ 0xea,0,0,0,0, /* 55: jmp 0:0 */
+ /* pad out to segment boundary */
+ 0x90, 0x90, /* 5A: ... */
+ 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
+ };
+ static uint8_t swapstub[1024];
+ uint8_t *p;
- fputs("Booting...\n", stdout);
+ regs->ebx.b[0] = regs->edx.b[0] = swapdrive;
- if (opt.swap) {
- /* Do this as late as possible */
- *bios_fbm = dosmem >> 10;
- *int13_vec = dosmem << 12;
- }
+ /* Note: we can't rely on either INT 13h nor the dosmem
+ vector to be correct at this stage, so we have to use an
+ installer stub to put things in the right place.
+ Round the installer location to a 1K boundary so the only
+ possible overlap is the identity mapping. */
+ endimage = (endimage + 1023) & ~1023;
+
+ /* Create swap stub */
+ memcpy(swapstub, swapstub_master, sizeof swapstub_master);
+ *(uint16_t *)&swapstub[0x3a] = regs->ds;
+ *(uint16_t *)&swapstub[0x3d] = regs->es;
+ *(uint32_t *)&swapstub[0x45] = regs->ecx.l;
+ *(uint32_t *)&swapstub[0x4b] = regs->esi.l;
+ *(uint32_t *)&swapstub[0x51] = regs->edi.l;
+ *(uint16_t *)&swapstub[0x56] = regs->ip;
+ *(uint16_t *)&swapstub[0x58] = regs->cs;
+ p = &swapstub[sizeof swapstub_master];
- syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
+ /* Mapping table; start out with identity mapping everything */
+ for (i = 0; i < 256; i++)
+ p[i] = i;
+
+ /* And the actual swap */
+ p[driveno] = swapdrive;
+ p[swapdrive] = driveno;
+
+ /* Adjust registers */
+ regs->ds = regs->cs = endimage >> 4;
+ regs->es = regs->esi.l = 0;
+ regs->ecx.l = sizeof swapstub >> 2;
+ regs->ip = 0x10; /* Installer offset */
- /* If we get here, badness happened */
- if (opt.swap) {
- *bios_fbm = old_bios_fbm;
- *int13_vec = old_int13_vec;
+ if (syslinux_add_movelist(&mlist, endimage, (addr_t)swapstub,
+ sizeof swapstub))
+ goto enomem;
+
+ endimage += sizeof swapstub;
}
+
+ /* Tell the shuffler not to muck with this area... */
+ syslinux_add_memmap(&mmap, endimage, 0xa0000-endimage, SMT_RESERVED);
+
+ fputs("Booting...\n", stdout);
+ syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs);
error("Chainboot failed!\n");
return;