Commit | Line | Data |
---|---|---|
7b7f1987 JH |
1 | /* |
2 | * DOS upper memory management. | |
3 | * | |
4 | * Copyright 2002 Jukka Heinonen | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
360a3f91 | 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
7b7f1987 JH |
19 | */ |
20 | ||
21 | #include "dosexe.h" | |
22 | #include "wine/debug.h" | |
23 | ||
24 | WINE_DEFAULT_DEBUG_CHANNEL(dosmem); | |
25 | ||
26 | /* | |
27 | * Wine DOS memory layout above 640k: | |
28 | * | |
29 | * a0000 - affff : VGA graphics (vga.c) | |
30 | * b0000 - bffff : Monochrome text (unused) | |
31 | * b8000 - bffff : VGA text (vga.c) | |
32 | * c0000 - cffff : EMS frame (int67.c) | |
33 | * d0000 - effff : Free memory for UMBs (himem.c) | |
34 | * f0000 - fffff : BIOS stuff (msdos/dosmem.c) | |
35 | * 100000 -10ffff : High memory area (unused) | |
36 | */ | |
37 | ||
38 | /* | |
39 | * Table of real mode segments and protected mode selectors | |
40 | * for code stubs and other miscellaneous storage. | |
41 | */ | |
42 | struct DPMI_segments *DOSVM_dpmi_segments = NULL; | |
43 | ||
44 | /* | |
45 | * First and last address available for upper memory blocks. | |
46 | */ | |
47 | #define DOSVM_UMB_BOTTOM 0xd0000 | |
48 | #define DOSVM_UMB_TOP 0xeffff | |
49 | ||
50 | /* | |
51 | * First free address for upper memory blocks. | |
52 | */ | |
53 | static DWORD DOSVM_umb_free = DOSVM_UMB_BOTTOM; | |
54 | ||
55 | ||
56 | /*********************************************************************** | |
57 | * DOSVM_AllocUMB | |
58 | * | |
59 | * Allocate upper memory block (UMB) from upper memory. | |
60 | * Returned pointer is aligned to 16-byte (paragraph) boundary. | |
61 | * | |
62 | * This routine is only for allocating static storage for | |
63 | * Wine internal uses. Allocated memory can be accessed from | |
64 | * real mode, memory is taken from area already mapped and reserved | |
65 | * by Wine and the allocation has very little memory and speed | |
66 | * overhead. Use of this routine also preserves precious DOS | |
67 | * conventional memory. | |
68 | */ | |
1cfc850b | 69 | LPVOID DOSVM_AllocUMB( DWORD size ) |
7b7f1987 JH |
70 | { |
71 | LPVOID ptr = (LPVOID)DOSVM_umb_free; | |
72 | ||
73 | size = ((size + 15) >> 4) << 4; | |
74 | ||
75 | if(DOSVM_umb_free + size - 1 > DOSVM_UMB_TOP) { | |
76 | ERR("Out of upper memory area.\n"); | |
77 | return 0; | |
78 | } | |
79 | ||
80 | DOSVM_umb_free += size; | |
81 | return ptr; | |
82 | } | |
83 | ||
84 | ||
204847e8 AJ |
85 | /********************************************************************** |
86 | * alloc_selector | |
87 | * | |
88 | * Allocate a selector corresponding to a real mode address. | |
89 | * size must be < 64k. | |
90 | */ | |
91 | static WORD alloc_selector( void *base, DWORD size, unsigned char flags ) | |
92 | { | |
93 | WORD sel = wine_ldt_alloc_entries( 1 ); | |
94 | ||
95 | if (sel) | |
96 | { | |
97 | LDT_ENTRY entry; | |
98 | wine_ldt_set_base( &entry, base ); | |
99 | wine_ldt_set_limit( &entry, size - 1 ); | |
100 | wine_ldt_set_flags( &entry, flags ); | |
101 | wine_ldt_set_entry( sel, &entry ); | |
102 | } | |
103 | return sel; | |
104 | } | |
105 | ||
106 | ||
7b7f1987 JH |
107 | /*********************************************************************** |
108 | * DOSVM_AllocCodeUMB | |
109 | * | |
110 | * Allocate upper memory block for storing code stubs. | |
111 | * Initializes real mode segment and 16-bit protected mode selector | |
112 | * for the allocated code block. | |
204847e8 AJ |
113 | * |
114 | * FIXME: should allocate a single PM selector for the whole UMB range. | |
7b7f1987 | 115 | */ |
1cfc850b | 116 | LPVOID DOSVM_AllocCodeUMB( DWORD size, WORD *segment, WORD *selector ) |
7b7f1987 JH |
117 | { |
118 | LPVOID ptr = DOSVM_AllocUMB( size ); | |
119 | ||
120 | if (segment) | |
121 | *segment = (DWORD)ptr >> 4; | |
122 | ||
123 | if (selector) | |
204847e8 | 124 | *selector = alloc_selector( ptr, size, WINE_LDT_FLAGS_CODE ); |
7b7f1987 JH |
125 | |
126 | return ptr; | |
127 | } | |
128 | ||
129 | ||
1cfc850b JH |
130 | /*********************************************************************** |
131 | * DOSVM_AllocDataUMB | |
132 | * | |
133 | * Allocate upper memory block for storing data. | |
134 | * Initializes real mode segment and 16-bit protected mode selector | |
135 | * for the allocated data block. | |
136 | */ | |
137 | LPVOID DOSVM_AllocDataUMB( DWORD size, WORD *segment, WORD *selector ) | |
138 | { | |
139 | LPVOID ptr = DOSVM_AllocUMB( size ); | |
140 | ||
141 | if (segment) | |
142 | *segment = (DWORD)ptr >> 4; | |
143 | ||
144 | if (selector) | |
204847e8 | 145 | *selector = alloc_selector( ptr, size, WINE_LDT_FLAGS_DATA ); |
1cfc850b JH |
146 | |
147 | return ptr; | |
148 | } | |
149 | ||
150 | ||
7b7f1987 JH |
151 | /*********************************************************************** |
152 | * DOSVM_InitSegments | |
153 | * | |
154 | * Initializes DOSVM_dpmi_segments. Allocates required memory and | |
155 | * sets up segments and selectors for accessing the memory. | |
156 | */ | |
157 | void DOSVM_InitSegments( void ) | |
158 | { | |
159 | LPSTR ptr; | |
160 | int i; | |
161 | ||
162 | static const char wrap_code[]={ | |
163 | 0xCD,0x31, /* int $0x31 */ | |
164 | 0xCB /* lret */ | |
165 | }; | |
166 | ||
167 | static const char enter_xms[]= | |
168 | { | |
169 | /* XMS hookable entry point */ | |
170 | 0xEB,0x03, /* jmp entry */ | |
171 | 0x90,0x90,0x90, /* nop;nop;nop */ | |
172 | /* entry: */ | |
173 | /* real entry point */ | |
174 | /* for simplicity, we'll just use the same hook as DPMI below */ | |
175 | 0xCD,0x31, /* int $0x31 */ | |
176 | 0xCB /* lret */ | |
177 | }; | |
178 | ||
179 | static const char enter_pm[]= | |
180 | { | |
181 | 0x50, /* pushw %ax */ | |
182 | 0x52, /* pushw %dx */ | |
183 | 0x55, /* pushw %bp */ | |
184 | 0x89,0xE5, /* movw %sp,%bp */ | |
185 | /* get return CS */ | |
186 | 0x8B,0x56,0x08, /* movw 8(%bp),%dx */ | |
187 | /* just call int 31 here to get into protected mode... */ | |
188 | /* it'll check whether it was called from dpmi_seg... */ | |
189 | 0xCD,0x31, /* int $0x31 */ | |
190 | /* we are now in the context of a 16-bit relay call */ | |
191 | /* need to fixup our stack; | |
192 | * 16-bit relay return address will be lost, | |
193 | * but we won't worry quite yet | |
194 | */ | |
195 | 0x8E,0xD0, /* movw %ax,%ss */ | |
196 | 0x66,0x0F,0xB7,0xE5, /* movzwl %bp,%esp */ | |
197 | /* set return CS */ | |
198 | 0x89,0x56,0x08, /* movw %dx,8(%bp) */ | |
199 | 0x5D, /* popw %bp */ | |
200 | 0x5A, /* popw %dx */ | |
201 | 0x58, /* popw %ax */ | |
4ef7ba72 | 202 | 0xfb, /* sti, enable and check virtual interrupts */ |
7b7f1987 JH |
203 | 0xCB /* lret */ |
204 | }; | |
205 | ||
9d7ff6c8 JH |
206 | static const char relay[]= |
207 | { | |
208 | 0xca, 0x04, 0x00, /* 16-bit far return and pop 4 bytes (relay void* arg) */ | |
2a3ce4c4 JH |
209 | 0xcd, 0x31, /* int 31 */ |
210 | 0xfb, 0x66, 0xcb /* sti and 32-bit far return */ | |
9d7ff6c8 JH |
211 | }; |
212 | ||
7b7f1987 JH |
213 | /* |
214 | * Allocate pointer array. | |
215 | */ | |
216 | DOSVM_dpmi_segments = DOSVM_AllocUMB( sizeof(struct DPMI_segments) ); | |
217 | ||
218 | /* | |
219 | * RM / offset 0: Exit from real mode. | |
220 | * RM / offset 2: Points to lret opcode. | |
221 | */ | |
222 | ptr = DOSVM_AllocCodeUMB( sizeof(wrap_code), | |
223 | &DOSVM_dpmi_segments->wrap_seg, 0 ); | |
224 | memcpy( ptr, wrap_code, sizeof(wrap_code) ); | |
225 | ||
226 | /* | |
227 | * RM / offset 0: XMS driver entry. | |
228 | */ | |
229 | ptr = DOSVM_AllocCodeUMB( sizeof(enter_xms), | |
230 | &DOSVM_dpmi_segments->xms_seg, 0 ); | |
231 | memcpy( ptr, enter_xms, sizeof(enter_xms) ); | |
232 | ||
233 | /* | |
234 | * RM / offset 0: Switch to DPMI. | |
235 | * PM / offset 8: DPMI raw mode switch. | |
236 | */ | |
237 | ptr = DOSVM_AllocCodeUMB( sizeof(enter_pm), | |
238 | &DOSVM_dpmi_segments->dpmi_seg, | |
239 | &DOSVM_dpmi_segments->dpmi_sel ); | |
240 | memcpy( ptr, enter_pm, sizeof(enter_pm) ); | |
241 | ||
242 | /* | |
243 | * PM / offset N*6: Interrupt N in DPMI32. | |
244 | */ | |
245 | ptr = DOSVM_AllocCodeUMB( 6 * 256, | |
246 | 0, &DOSVM_dpmi_segments->int48_sel ); | |
247 | for(i=0; i<256; i++) { | |
248 | /* | |
249 | * Each 32-bit interrupt handler is 6 bytes: | |
250 | * 0xCD,<i> = int <i> (nested 16-bit interrupt) | |
9d7ff6c8 | 251 | * 0x66,0xCA,0x04,0x00 = ret 4 (32-bit far return and pop 4 bytes / eflags) |
7b7f1987 JH |
252 | */ |
253 | ptr[i * 6 + 0] = 0xCD; | |
254 | ptr[i * 6 + 1] = i; | |
255 | ptr[i * 6 + 2] = 0x66; | |
256 | ptr[i * 6 + 3] = 0xCA; | |
257 | ptr[i * 6 + 4] = 0x04; | |
258 | ptr[i * 6 + 5] = 0x00; | |
259 | } | |
9d7ff6c8 JH |
260 | |
261 | /* | |
262 | * PM / offset N*5: Interrupt N in 16-bit protected mode. | |
263 | */ | |
264 | ptr = DOSVM_AllocCodeUMB( 5 * 256, | |
265 | 0, &DOSVM_dpmi_segments->int16_sel ); | |
266 | for(i=0; i<256; i++) { | |
267 | /* | |
268 | * Each 16-bit interrupt handler is 5 bytes: | |
269 | * 0xCD,<i> = int <i> (interrupt) | |
270 | * 0xCA,0x02,0x00 = ret 2 (16-bit far return and pop 2 bytes / eflags) | |
271 | */ | |
272 | ptr[i * 5 + 0] = 0xCD; | |
273 | ptr[i * 5 + 1] = i; | |
274 | ptr[i * 5 + 2] = 0xCA; | |
275 | ptr[i * 5 + 3] = 0x02; | |
276 | ptr[i * 5 + 4] = 0x00; | |
277 | } | |
278 | ||
279 | /* | |
280 | * PM / offset 0: Stub where __wine_call_from_16_regs returns. | |
281 | * PM / offset 3: Stub which swaps back to 32-bit application code/stack. | |
2a3ce4c4 | 282 | * PM / offset 5: Stub which enables interrupts |
9d7ff6c8 JH |
283 | */ |
284 | ptr = DOSVM_AllocCodeUMB( sizeof(relay), | |
285 | 0, &DOSVM_dpmi_segments->relay_code_sel); | |
286 | memcpy( ptr, relay, sizeof(relay) ); | |
287 | ||
288 | /* | |
289 | * Space for 16-bit stack used by relay code. | |
290 | */ | |
291 | ptr = DOSVM_AllocDataUMB( DOSVM_RELAY_DATA_SIZE, | |
292 | 0, &DOSVM_dpmi_segments->relay_data_sel); | |
293 | memset( ptr, 0, DOSVM_RELAY_DATA_SIZE ); | |
570eb609 MZ |
294 | |
295 | /* | |
296 | * As we store code in UMB we should make sure it is executable | |
297 | */ | |
298 | VirtualProtect((void *)DOSVM_UMB_BOTTOM, DOSVM_UMB_TOP - DOSVM_UMB_BOTTOM, PAGE_EXECUTE_READWRITE, NULL); | |
7b7f1987 | 299 | } |