summaryrefslogtreecommitdiff
path: root/com32/lib/syslinux/shuffle_rm.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2009-04-22 15:21:39 -0700
committerH. Peter Anvin <hpa@zytor.com>2009-04-23 14:18:33 -0700
commitafa6e3dec454424863733317ea29ff20a718e277 (patch)
tree7fef117f450121d5754137df6df37be896e55952 /com32/lib/syslinux/shuffle_rm.c
parent645398175a19c9d0bade8dc48d20cc9d0d17b4d0 (diff)
downloadsyslinux-afa6e3dec454424863733317ea29ff20a718e277.tar.gz
shuffle_rm: set up SS:ESP and segs as quickly as possible
For extra paranoia's sake, set up SS:ESP immediately after the real-mode switch, and then set all the segment registers. Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'com32/lib/syslinux/shuffle_rm.c')
-rw-r--r--com32/lib/syslinux/shuffle_rm.c53
1 files changed, 40 insertions, 13 deletions
diff --git a/com32/lib/syslinux/shuffle_rm.c b/com32/lib/syslinux/shuffle_rm.c
index 7eb3585d..2496b888 100644
--- a/com32/lib/syslinux/shuffle_rm.c
+++ b/com32/lib/syslinux/shuffle_rm.c
@@ -39,6 +39,12 @@
#include <syslinux/movebits.h>
#include <syslinux/bootrm.h>
+enum gpr_index { R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI };
+enum seg_index { R_ES, R_CS, R_SS, R_DS, R_FS, R_GS };
+
+#define MOV_TO_SEG(S,R) (0xc08e + ((R) << 8) + ((S) << 11))
+#define MOV_TO_R32(R) (0xb866 + ((R) << 8))
+
int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist,
struct syslinux_memmap *memmap,
uint16_t bootflags,
@@ -54,6 +60,8 @@ int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist,
uint8_t handoff_code[8+5*5+8*6+1+5], *p;
struct syslinux_memmap *tmap;
addr_t regstub, stublen;
+ /* Assign GPRs for each sreg, don't use AX and SP */
+ static const uint8_t gpr_for_seg[6] = { R_CX, R_DX, R_BX, R_BP, R_SI, R_DI };
tmap = syslinux_target_memmap(fraglist, memmap);
if (!tmap)
@@ -92,24 +100,43 @@ int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist,
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;
-
+ /* Set up GPRs with segment registers - don't use AX */
for (i = 0; i < 6; i++) {
- if (i != 1) { /* Skip CS */
- p[0] = 0xb8; /* MOV AX,imm16 */
+ if (i != R_CS) {
+ p[0] = 0xb8 + gpr_for_seg[i]; /* MOV reg,imm16 */
*(uint16_t *)(p+1) = rp->seg[i];
- *(uint16_t *)(p+3) = 0xc08e + (i << 11); /* MOV seg,AX */
- p += 5;
+ p += 3;
}
}
+
+ /* Actual transition to real mode */
+ *(uint32_t *)p = 0xeac0220f; /* MOV CR0,EAX; JMP FAR */
+ *(uint16_t *)(p+4) = (p-handoff_code)+8; /* Offset */
+ *(uint16_t *)(p+6) = regstub >> 4; /* Segment */
+ p += 8;
+
+ /* Load SS and ESP immediately */
+ *(uint16_t *)p = MOV_TO_SEG(R_SS, R_BX);
+ *(uint16_t *)(p+2) = MOV_TO_R32(R_SP);
+ *(uint32_t *)(p+4) = rp->seg[R_SP];
+ p += 8;
+
+ /* Load the other segments */
+ *(uint16_t *)p = MOV_TO_SEG(R_ES, R_CX);
+ p += 2;
+ *(uint16_t *)p = MOV_TO_SEG(R_DS, R_BP);
+ p += 2;
+ *(uint16_t *)p = MOV_TO_SEG(R_FS, R_SI);
+ p += 2;
+ *(uint16_t *)p = MOV_TO_SEG(R_GS, R_DI);
+ p += 2;
+
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;
+ if (i != R_SP) {
+ *(uint16_t *)p = MOV_TO_R32(i); /* MOV r32,imm32 */
+ *(uint32_t *)(p+2) = rp->gpr[i];
+ p += 6;
+ }
}
*p++ = rp->sti ? 0xfb : 0xfa; /* STI/CLI */