1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
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
102
103
104
105
106 regstub = 0x800;
107 stublen = sizeof handoff_code;
108 rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen, 16);
109
110 if (rv || (regstub > 0x100000 - sizeof handoff_code)) {
111
112
113
114
115
116
117 regstub = 0x510;
118 stublen = sizeof handoff_code;
119 rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen, 16);
120
121 if (!rv && (regstub > 0x100000 - sizeof handoff_code))
122 rv = -1;
123 }
124
125 syslinux_free_memmap(tmap);
126 if (rv)
127 return -1;
128
129
130 p = handoff_code;
131 rp = (const struct syslinux_rm_regs_alt *)regs;
132
133
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
140 ST32(p, 0xeac0220f);
141 off = (p - handoff_code) + 4;
142 ST16(p, off);
143 ST16(p, regstub >> 4);
144
145
146 MOV_TO_SEG(p, R_SS, R_BX);
147 MOV_TO_R32(p, R_SP, rp->gpr[R_SP]);
148
149
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);
161
162 ST8(p, 0xea);
163 ST32(p, rp->csip);
164
165
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