syslinux/com32/lib/syslinux/shuffle_rm.c
<<
>>
Prefs
   1/* ----------------------------------------------------------------------- *
   2 *
   3 *   Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
   4 *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
   5 *
   6 *   Permission is hereby granted, free of charge, to any person
   7 *   obtaining a copy of this software and associated documentation
   8 *   files (the "Software"), to deal in the Software without
   9 *   restriction, including without limitation the rights to use,
  10 *   copy, modify, merge, publish, distribute, sublicense, and/or
  11 *   sell copies of the Software, and to permit persons to whom
  12 *   the Software is furnished to do so, subject to the following
  13 *   conditions:
  14 *
  15 *   The above copyright notice and this permission notice shall
  16 *   be included in all copies or substantial portions of the Software.
  17 *
  18 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  19 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  20 *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  21 *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  22 *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  23 *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  24 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  25 *   OTHER DEALINGS IN THE SOFTWARE.
  26 *
  27 * ----------------------------------------------------------------------- */
  28
  29/*
  30 * shuffle_rm.c
  31 *
  32 * Shuffle and boot to protected mode code
  33 */
  34
  35#include <stdlib.h>
  36#include <inttypes.h>
  37#include <com32.h>
  38#include <string.h>
  39#include <syslinux/movebits.h>
  40#include <syslinux/bootrm.h>
  41
  42enum gpr_index { R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI };
  43enum seg_index { R_ES, R_CS, R_SS, R_DS, R_FS, R_GS };
  44
  45#define ST8(P,V)                                                \
  46  do {                                                          \
  47    uint8_t *_p = (void *)(P);                                  \
  48    *_p++ = (V);                                                \
  49    (P) = (void *)_p;                                           \
  50  } while (0);
  51#define ST16(P,V)                                               \
  52  do {                                                          \
  53    uint16_t *_p = (void *)(P);                                 \
  54    *_p++ = (V);                                                \
  55    (P) = (void *)_p;                                           \
  56  } while (0)
  57#define ST32(P,V)                                               \
  58  do {                                                          \
  59    uint32_t *_p = (void *)(P);                                 \
  60    *_p++ = (V);                                                \
  61    (P) = (void *)_p;                                           \
  62  } while (0)
  63
  64#define MOV_TO_SEG(P,S,R)                                       \
  65    ST16(P, 0xc08e + ((R) << 8) + ((S) << 11))
  66#define MOV_TO_R16(P,R,V)                                       \
  67  do {                                                          \
  68    ST8(P, 0xb8 + (R));                                         \
  69    ST16(P, V);                                                 \
  70  }  while (0)
  71#define MOV_TO_R32(P,R,V)                                       \
  72  do {                                                          \
  73    ST16(P, 0xb866 + ((R) << 8));                               \
  74    ST32(P, V);                                                 \
  75  } while (0)
  76
  77int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist,
  78                             struct syslinux_memmap *memmap,
  79                             uint16_t bootflags, struct syslinux_rm_regs *regs)
  80{
  81    const struct syslinux_rm_regs_alt {
  82        uint16_t seg[6];
  83        uint32_t gpr[8];
  84        uint32_t csip;
  85        bool sti;
  86    } *rp;
  87    int i, rv;
  88    uint8_t handoff_code[8 + 5 * 5 + 8 * 6 + 1 + 5], *p;
  89    uint16_t off;
  90    struct syslinux_memmap *tmap;
  91    addr_t regstub, stublen;
  92    /* Assign GPRs for each sreg, don't use AX and SP */
  93    static const uint8_t gpr_for_seg[6] =
  94        { R_CX, R_DX, R_BX, R_BP, R_SI, R_DI };
  95
  96    tmap = syslinux_target_memmap(fraglist, memmap);
  97    if (!tmap)
  98        return -1;
  99
 100    /*
 101     * Search for a good place to put the real-mode register stub.
 102     * We prefer it as low as possible above 0x800.  KVM barfs horribly
 103     * if we're not aligned to a paragraph boundary, so set the alignment
 104     * appropriately.
 105     */
 106    regstub = 0x800;
 107    stublen = sizeof handoff_code;
 108    rv = syslinux_memmap_find(tmap, SMT_FREE, &regstub, &stublen, 16);
 109
 110    if (rv || (regstub > 0x100000 - sizeof handoff_code)) {
 111        /*
 112         * Uh-oh.  This isn't real-mode accessible memory.
 113         * It might be possible to do something insane here like
 114         * putting the stub in the IRQ vectors, or in the 0x5xx segment.
 115         * This code tries the 0x510-0x7ff range and hopes for the best.
 116         */
 117        regstub = 0x510;        /* Try the 0x5xx segment... */
 118        stublen = sizeof handoff_code;
 119        rv = syslinux_memmap_find(tmap, SMT_FREE, &regstub, &stublen, 16);
 120
 121        if (!rv && (regstub > 0x100000 - sizeof handoff_code))
 122            rv = -1;            /* No acceptable memory found */
 123    }
 124
 125    syslinux_free_memmap(tmap);
 126    if (rv)
 127        return -1;
 128
 129    /* Build register-setting stub */
 130    p = handoff_code;
 131    rp = (const struct syslinux_rm_regs_alt *)regs;
 132
 133    /* Set up GPRs with segment registers - don't use AX */
 134    for (i = 0; i < 6; i++) {
 135        if (i != R_CS)
 136            MOV_TO_R16(p, gpr_for_seg[i], rp->seg[i]);
 137    }
 138
 139    /* Actual transition to real mode */
 140    ST32(p, 0xeac0220f);        /* MOV CR0,EAX; JMP FAR */
 141    off = (p - handoff_code) + 4;
 142    ST16(p, off);               /* Offset */
 143    ST16(p, regstub >> 4);      /* Segment */
 144
 145    /* Load SS and ESP immediately */
 146    MOV_TO_SEG(p, R_SS, R_BX);
 147    MOV_TO_R32(p, R_SP, rp->gpr[R_SP]);
 148
 149    /* Load the other segments */
 150    MOV_TO_SEG(p, R_ES, R_CX);
 151    MOV_TO_SEG(p, R_DS, R_BP);
 152    MOV_TO_SEG(p, R_FS, R_SI);
 153    MOV_TO_SEG(p, R_GS, R_DI);
 154
 155    for (i = 0; i < 8; i++) {
 156        if (i != R_SP)
 157            MOV_TO_R32(p, i, rp->gpr[i]);
 158    }
 159
 160    ST8(p, rp->sti ? 0xfb : 0xfa);      /* STI/CLI */
 161
 162    ST8(p, 0xea);               /* JMP FAR */
 163    ST32(p, rp->csip);
 164
 165    /* Add register-setting stub to shuffle list */
 166    if (syslinux_add_movelist(&fraglist, regstub, (addr_t) handoff_code,
 167                              sizeof handoff_code))
 168        return -1;
 169
 170    return syslinux_do_shuffle(fraglist, memmap, regstub, 0, bootflags);
 171}
 172
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.