aboutsummaryrefslogtreecommitdiffstats
path: root/com32/lib/syslinux/shuffle_rm.c
diff options
context:
space:
mode:
Diffstat (limited to 'com32/lib/syslinux/shuffle_rm.c')
-rw-r--r--com32/lib/syslinux/shuffle_rm.c76
1 files changed, 62 insertions, 14 deletions
diff --git a/com32/lib/syslinux/shuffle_rm.c b/com32/lib/syslinux/shuffle_rm.c
index e9226a82..4852d3c5 100644
--- a/com32/lib/syslinux/shuffle_rm.c
+++ b/com32/lib/syslinux/shuffle_rm.c
@@ -46,24 +46,72 @@ int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist,
int nd;
com32sys_t ireg;
char *regbuf;
+ const struct syslinux_rm_regs_alt {
+ uint16_t seg[6];
+ uint32_t gpr[8];
+ uint32_t csip;
+ } *rp;
+ int i, rv;
+ uint8_t handoff_code[5*5+8*6+5], *p;
+ struct syslinux_memmap *tmap, *tp;
+ addr_t regstub;
- nd = syslinux_prepare_shuffle(fraglist, memmap);
- if (nd < 0)
+ tmap = syslinux_target_memmap(fraglist, memmap);
+ if (!tmap)
return -1;
- regbuf = (char *)__com32.cs_bounce + (12*nd);
- memcpy(regbuf, regs, sizeof(*regs));
+ /* Search for a good place to put the real-mode register stub.
+ We prefer to put it as high as possible in the low 640K. */
+ regstub = 0;
+ for (tp = tmap; tp->type != SMT_END; tp = tp->next) {
+ addr_t xend, xlen;
+ if (tp->start >= 640*1024)
+ continue;
+ if (tp->type != SMT_FREE)
+ continue;
+ xend = tp->next->start;
+ if (xend > 640*1024)
+ xend = 640*1024;
+ xlen = xend - tp->start;
+ if (xlen < sizeof handoff_code)
+ continue;
+ regstub = xend - sizeof handoff_code; /* Best alternative so far */
+ }
- memset(&ireg, 0, sizeof ireg);
+ syslinux_free_memmap(tmap);
- ireg.eax.w[0] = 0x001b;
- ireg.edx.w[0] = bootflags;
- ireg.es = SEG(__com32.cs_bounce);
- ireg.edi.l = OFFS(__com32.cs_bounce);
- ireg.ecx.l = nd;
- ireg.ds = SEG(regbuf);
- ireg.esi.l = OFFS(regbuf);
- __intcall(0x22, &ireg, NULL);
+ /* XXX: it might be possible to do something insane here like
+ putting the stub in the IRQ vectors... */
+ if (!regstub)
+ return -1; /* No space at all */
- return -1; /* Too many descriptors? */
+ /* Build register-setting stub */
+ p = handoff_code;
+ rp = (const struct syslinux_rm_regs_alt *)regs;
+ for (i = 0; i < 6; i++) {
+ if (i != 1) { /* Skip CS */
+ p[0] = 0xb8; /* MOV AX,imm16 */
+ *(uint16_t *)(p+1) = rp->seg[i];
+ *(uint16_t *)(p+3) = 0xc08e + (i << 11); /* MOV seg,AX */
+ p += 5;
+ }
+ }
+ for (i = 0; i < 8; i++) {
+ p[0] = 0x66; /* MOV exx,imm32 */
+ p[1] = 0xb8 + i;
+ *(uint32_t *)(p+2) = rp->gpr[i];
+ p += 6;
+ }
+ *p++ = 0xea; /* JMP FAR */
+ *(uint32_t *)p = rp->csip;
+
+ /* Add register-setting stub to shuffle list */
+ if (syslinux_add_movelist(&fraglist, regstub, (addr_t)handoff_code,
+ 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);
}