Adding upstream version 6.03~pre1+dfsg.
[syslinux-debian/hramrach.git] / com32 / lib / syslinux / shuffle_rm.c
blob9935f4cd09c279b698026c3bcec59e91f3fd45f5
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009 Intel Corporation; author: H. Peter Anvin
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:
15 * The above copyright notice and this permission notice shall
16 * be included in all copies or substantial portions of the Software.
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.
27 * ----------------------------------------------------------------------- */
30 * shuffle_rm.c
32 * Shuffle and boot to protected mode code
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>
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 };
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)
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)
77 int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist,
78 struct syslinux_memmap *memmap,
79 uint16_t bootflags, struct syslinux_rm_regs *regs)
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 };
96 tmap = syslinux_target_memmap(fraglist, memmap);
97 if (!tmap)
98 return -1;
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.
106 regstub = 0x800;
107 stublen = sizeof handoff_code;
108 rv = syslinux_memmap_find_type(tmap, SMT_FREE, &regstub, &stublen, 16);
110 if (rv || (regstub > 0x100000 - sizeof handoff_code)) {
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.
117 regstub = 0x510; /* Try the 0x5xx segment... */
118 stublen = sizeof handoff_code;
119 rv = syslinux_memmap_find_type(tmap, SMT_FREE, &regstub, &stublen, 16);
121 if (!rv && (regstub > 0x100000 - sizeof handoff_code))
122 rv = -1; /* No acceptable memory found */
125 syslinux_free_memmap(tmap);
126 if (rv)
127 return -1;
129 /* Build register-setting stub */
130 p = handoff_code;
131 rp = (const struct syslinux_rm_regs_alt *)regs;
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]);
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 */
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]);
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);
155 for (i = 0; i < 8; i++) {
156 if (i != R_SP)
157 MOV_TO_R32(p, i, rp->gpr[i]);
160 ST8(p, rp->sti ? 0xfb : 0xfa); /* STI/CLI */
162 ST8(p, 0xea); /* JMP FAR */
163 ST32(p, rp->csip);
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;
170 return syslinux_do_shuffle(fraglist, memmap, regstub, 0, bootflags);