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