shuffle_rm: set up SS:ESP and segs as quickly as possible
authorH. Peter Anvin <hpa@linux.intel.com>
Wed, 22 Apr 2009 22:21:39 +0000 (15:21 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Thu, 23 Apr 2009 21:18:33 +0000 (14:18 -0700)
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>
com32/lib/syslinux/shuffle_rm.c

index 7eb3585..2496b88 100644 (file)
 #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 */