[e16e8f2] | 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 | |
---|
| 42 | enum gpr_index { R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI }; |
---|
| 43 | enum 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 | |
---|
| 77 | int 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_type(tmap, SMT_FREE, ®stub, &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_type(tmap, SMT_FREE, ®stub, &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 | } |
---|