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
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 * ----------------------------------------------------------------------- */
32 * Shuffle and boot to protected mode code
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
};
47 uint8_t *_p = (void *)(P); \
53 uint16_t *_p = (void *)(P); \
59 uint32_t *_p = (void *)(P); \
64 #define MOV_TO_SEG(P,S,R) \
65 ST16(P, 0xc08e + ((R) << 8) + ((S) << 11))
66 #define MOV_TO_R16(P,R,V) \
71 #define MOV_TO_R32(P,R,V) \
73 ST16(P, 0xb866 + ((R) << 8)); \
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
{
88 uint8_t handoff_code
[8 + 5 * 5 + 8 * 6 + 1 + 5], *p
;
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
);
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
107 stublen
= sizeof handoff_code
;
108 rv
= syslinux_memmap_find_type(tmap
, SMT_FREE
, ®stub
, &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
, ®stub
, &stublen
, 16);
121 if (!rv
&& (regstub
> 0x100000 - sizeof handoff_code
))
122 rv
= -1; /* No acceptable memory found */
125 syslinux_free_memmap(tmap
);
129 /* Build register-setting stub */
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
++) {
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
++) {
157 MOV_TO_R32(p
, i
, rp
->gpr
[i
]);
160 ST8(p
, rp
->sti
? 0xfb : 0xfa); /* STI/CLI */
162 ST8(p
, 0xea); /* JMP FAR */
165 /* Add register-setting stub to shuffle list */
166 if (syslinux_add_movelist(&fraglist
, regstub
, (addr_t
) handoff_code
,
167 sizeof handoff_code
))
170 return syslinux_do_shuffle(fraglist
, memmap
, regstub
, 0, bootflags
);