Release 970329
[wine] / memory / selector.c
1 /*
2  * Selector manipulation functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6
7 #include <string.h>
8 #include "windows.h"
9 #include "ldt.h"
10 #include "miscemu.h"
11 #include "selectors.h"
12 #include "stackframe.h"
13 #include "stddebug.h"
14 #include "debug.h"
15
16
17 #define FIRST_LDT_ENTRY_TO_ALLOC  17
18
19
20 /***********************************************************************
21  *           AllocSelectorArray   (KERNEL.206)
22  */
23 WORD AllocSelectorArray( WORD count )
24 {
25     WORD i, size = 0;
26
27     if (!count) return 0;
28     for (i = FIRST_LDT_ENTRY_TO_ALLOC; i < LDT_SIZE; i++)
29     {
30         if (!IS_LDT_ENTRY_FREE(i)) size = 0;
31         else if (++size >= count) break;
32     }
33     if (i == LDT_SIZE) return 0;
34     /* Mark selector as allocated */
35     while (size--) ldt_flags_copy[i--] |= LDT_FLAGS_ALLOCATED;
36     return ENTRY_TO_SELECTOR( i + 1 );
37 }
38
39
40 /***********************************************************************
41  *           AllocSelector   (KERNEL.175)
42  */
43 WORD AllocSelector( WORD sel )
44 {
45     WORD newsel, count, i;
46
47     count = sel ? ((GET_SEL_LIMIT(sel) >> 16) + 1) : 1;
48     newsel = AllocSelectorArray( count );
49     dprintf_selector( stddeb, "AllocSelector(%04x): returning %04x\n",
50                       sel, newsel );
51     if (!newsel) return 0;
52     if (!sel) return newsel;  /* nothing to copy */
53     for (i = 0; i < count; i++)
54     {
55         ldt_entry entry;
56         LDT_GetEntry( SELECTOR_TO_ENTRY(sel) + i, &entry );
57         LDT_SetEntry( SELECTOR_TO_ENTRY(newsel) + i, &entry );
58     }
59     return newsel;
60 }
61
62
63 /***********************************************************************
64  *           FreeSelector   (KERNEL.176)
65  */
66 WORD FreeSelector( WORD sel )
67 {
68     if (IS_SELECTOR_FREE(sel)) return sel;  /* error */
69     SELECTOR_FreeBlock( sel, 1 );
70     return 0;
71 }
72
73
74 /***********************************************************************
75  *           SELECTOR_SetEntries
76  *
77  * Set the LDT entries for an array of selectors.
78  */
79 static void SELECTOR_SetEntries( WORD sel, const void *base, DWORD size,
80                                  enum seg_type type, BOOL32 is32bit,
81                                  BOOL32 readonly )
82 {
83     ldt_entry entry;
84     WORD i, count;
85
86     /* The limit for the first selector is the whole */
87     /* block. The next selectors get a 64k limit.    */
88     entry.base           = (unsigned long)base;
89     entry.type           = type;
90     entry.seg_32bit      = is32bit;
91     entry.read_only      = readonly;
92     entry.limit_in_pages = (size > 0x100000);
93     if (entry.limit_in_pages) entry.limit = ((size + 0xfff) >> 12) - 1;
94     else entry.limit = size - 1;
95     /* Make sure base and limit are not 0 together if the size is not 0 */
96     if (!base && !entry.limit && size) entry.limit = 1;
97     count = (size + 0xffff) / 0x10000;
98     for (i = 0; i < count; i++)
99     {
100         LDT_SetEntry( SELECTOR_TO_ENTRY(sel) + i, &entry );
101         entry.base += 0x10000;
102         /* Apparently the next selectors should *not* get a 64k limit. */
103         /* Can't remember where I read they should... --AJ */
104         entry.limit -= entry.limit_in_pages ? 0x10 : 0x10000;
105     }
106 }
107
108
109 /***********************************************************************
110  *           SELECTOR_AllocBlock
111  *
112  * Allocate selectors for a block of linear memory.
113  */
114 WORD SELECTOR_AllocBlock( const void *base, DWORD size, enum seg_type type,
115                           BOOL32 is32bit, BOOL32 readonly )
116 {
117     WORD sel, count;
118
119     if (!size) return 0;
120     count = (size + 0xffff) / 0x10000;
121     sel = AllocSelectorArray( count );
122     if (sel) SELECTOR_SetEntries( sel, base, size, type, is32bit, readonly );
123     return sel;
124 }
125
126
127 /***********************************************************************
128  *           SELECTOR_FreeBlock
129  *
130  * Free a block of selectors.
131  */
132 void SELECTOR_FreeBlock( WORD sel, WORD count )
133 {
134     WORD i, nextsel;
135     ldt_entry entry;
136     STACK16FRAME *frame;
137
138     dprintf_selector( stddeb, "SELECTOR_FreeBlock(%04x,%d)\n", sel, count );
139     sel &= ~(__AHINCR - 1);  /* clear bottom bits of selector */
140     nextsel = sel + (count << __AHSHIFT);
141     memset( &entry, 0, sizeof(entry) );  /* clear the LDT entries */
142     for (i = SELECTOR_TO_ENTRY(sel); count; i++, count--)
143     {
144         LDT_SetEntry( i, &entry );
145         ldt_flags_copy[i] &= ~LDT_FLAGS_ALLOCATED;
146     }
147
148     /* Clear the saved 16-bit selector */
149     frame = CURRENT_STACK16;
150     while (frame)
151     {
152         if ((frame->ds >= sel) && (frame->ds < nextsel)) frame->ds = 0;
153         if ((frame->es >= sel) && (frame->es < nextsel)) frame->es = 0;
154         frame = PTR_SEG_OFF_TO_LIN(frame->saved_ss, frame->saved_sp);
155     }
156 }
157
158
159 /***********************************************************************
160  *           SELECTOR_ReallocBlock
161  *
162  * Change the size of a block of selectors.
163  */
164 WORD SELECTOR_ReallocBlock( WORD sel, const void *base, DWORD size,
165                            enum seg_type type, BOOL32 is32bit, BOOL32 readonly)
166 {
167     WORD i, oldcount, newcount;
168
169     if (!size) size = 1;
170     oldcount = (GET_SEL_LIMIT(sel) >> 16) + 1;
171     newcount = (size + 0xffff) >> 16;
172
173     if (oldcount < newcount)  /* We need to add selectors */
174     {
175           /* Check if the next selectors are free */
176         if (SELECTOR_TO_ENTRY(sel) + newcount > LDT_SIZE) i = oldcount;
177         else
178             for (i = oldcount; i < newcount; i++)
179                 if (!IS_LDT_ENTRY_FREE(SELECTOR_TO_ENTRY(sel)+i)) break;
180
181         if (i < newcount)  /* they are not free */
182         {
183             SELECTOR_FreeBlock( sel, oldcount );
184             sel = AllocSelectorArray( newcount );
185         }
186         else  /* mark the selectors as allocated */
187         {
188             for (i = oldcount; i < newcount; i++)
189                 ldt_flags_copy[SELECTOR_TO_ENTRY(sel)+i] |=LDT_FLAGS_ALLOCATED;
190         }
191     }
192     else if (oldcount > newcount) /* We need to remove selectors */
193     {
194         SELECTOR_FreeBlock( ENTRY_TO_SELECTOR(SELECTOR_TO_ENTRY(sel)+newcount),
195                             oldcount - newcount );
196     }
197     if (sel) SELECTOR_SetEntries( sel, base, size, type, is32bit, readonly );
198     return sel;
199 }
200
201
202 /***********************************************************************
203  *           PrestoChangoSelector   (KERNEL.177)
204  */
205 WORD PrestoChangoSelector( WORD selSrc, WORD selDst )
206 {
207     ldt_entry entry;
208     LDT_GetEntry( SELECTOR_TO_ENTRY( selSrc ), &entry );
209     entry.type ^= SEGMENT_CODE;  /* toggle the executable bit */
210     LDT_SetEntry( SELECTOR_TO_ENTRY( selDst ), &entry );
211     return selDst;
212 }
213
214
215 /***********************************************************************
216  *           AllocCStoDSAlias   (KERNEL.170)
217  */
218 WORD AllocCStoDSAlias( WORD sel )
219 {
220     WORD newsel;
221     ldt_entry entry;
222
223     newsel = AllocSelectorArray( 1 );
224     dprintf_selector( stddeb, "AllocCStoDSAlias(%04x): returning %04x\n",
225                       sel, newsel );
226     if (!newsel) return 0;
227     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
228     entry.type = SEGMENT_DATA;
229     LDT_SetEntry( SELECTOR_TO_ENTRY(newsel), &entry );
230     return newsel;
231 }
232
233
234 /***********************************************************************
235  *           AllocDStoCSAlias   (KERNEL.171)
236  */
237 WORD AllocDStoCSAlias( WORD sel )
238 {
239     WORD newsel;
240     ldt_entry entry;
241
242     newsel = AllocSelectorArray( 1 );
243     dprintf_selector( stddeb, "AllocDStoCSAlias(%04x): returning %04x\n",
244                       sel, newsel );
245     if (!newsel) return 0;
246     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
247     entry.type = SEGMENT_CODE;
248     LDT_SetEntry( SELECTOR_TO_ENTRY(newsel), &entry );
249     return newsel;
250 }
251
252
253 /***********************************************************************
254  *           LongPtrAdd   (KERNEL.180)
255  */
256 void LongPtrAdd( DWORD ptr, DWORD add )
257 {
258     ldt_entry entry;
259     LDT_GetEntry( SELECTOR_TO_ENTRY(SELECTOROF(ptr)), &entry );
260     entry.base += add;
261     LDT_SetEntry( SELECTOR_TO_ENTRY(SELECTOROF(ptr)), &entry );
262 }
263
264
265 /***********************************************************************
266  *           GetSelectorBase   (KERNEL.186)
267  */
268 DWORD GetSelectorBase( WORD sel )
269 {
270     DWORD base = GET_SEL_BASE(sel);
271
272     /* if base points into DOSMEM, assume we have to
273      * return pointer into physical lower 1MB */
274
275     return DOSMEM_MapLinearToDos( (LPVOID)base );
276 }
277
278
279 /***********************************************************************
280  *           SetSelectorBase   (KERNEL.187)
281  */
282 WORD SetSelectorBase( WORD sel, DWORD base )
283 {
284     ldt_entry entry;
285
286     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
287
288     entry.base = (DWORD)DOSMEM_MapDosToLinear(base);
289
290     LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
291     return sel;
292 }
293
294
295 /***********************************************************************
296  *           GetSelectorLimit   (KERNEL.188)
297  */
298 DWORD GetSelectorLimit( WORD sel )
299 {
300     return GET_SEL_LIMIT(sel);
301 }
302
303
304 /***********************************************************************
305  *           SetSelectorLimit   (KERNEL.189)
306  */
307 WORD SetSelectorLimit( WORD sel, DWORD limit )
308 {
309     ldt_entry entry;
310     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
311     entry.limit_in_pages = (limit >= 0x100000);
312     if (entry.limit_in_pages) entry.limit = limit >> 12;
313     else entry.limit = limit;
314     LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
315     return sel;
316 }
317
318
319 /***********************************************************************
320  *           SelectorAccessRights   (KERNEL.196)
321  */
322 WORD SelectorAccessRights( WORD sel, WORD op, WORD val )
323 {
324     ldt_entry entry;
325     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
326     if (op == 0)  /* get */
327     {
328         return 0x01 | /* accessed */
329                0x10 | /* not system */
330                0x60 | /* DPL 3 */
331                0x80 | /* present */
332                ((entry.read_only == 0) << 1) |
333                (entry.type << 2) |
334                (entry.seg_32bit << 14) |
335                (entry.limit_in_pages << 15);
336     }
337     else  /* set */
338     {
339         entry.read_only = ((val & 2) == 0);
340         entry.type = (val >> 2) & 3;
341         entry.seg_32bit = val & 0x4000;
342         entry.limit_in_pages = val & 0x8000;
343         LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
344         return 0;
345     }
346 }
347
348
349 /***********************************************************************
350  *           IsBadCodePtr16   (KERNEL.336)
351  */
352 BOOL16 IsBadCodePtr16( SEGPTR lpfn )
353 {
354     WORD sel;
355     ldt_entry entry;
356
357     sel = SELECTOROF(lpfn);
358     if (!sel) return TRUE;
359     if (IS_SELECTOR_FREE(sel)) return TRUE;
360     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
361     if (entry.type != SEGMENT_CODE) return TRUE;
362     if (OFFSETOF(lpfn) > entry.limit) return TRUE;
363     return FALSE;
364 }
365
366
367 /***********************************************************************
368  *           IsBadStringPtr16   (KERNEL.337)
369  */
370 BOOL16 IsBadStringPtr16( SEGPTR ptr, UINT16 size )
371 {
372     WORD sel;
373     ldt_entry entry;
374
375     sel = SELECTOROF(ptr);
376     if (!sel) return TRUE;
377     if (IS_SELECTOR_FREE(sel)) return TRUE;
378     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
379     if ((entry.type == SEGMENT_CODE) && entry.read_only) return TRUE;
380     if (strlen(PTR_SEG_TO_LIN(ptr)) < size) size = strlen(PTR_SEG_TO_LIN(ptr));
381     if (OFFSETOF(ptr) + size - 1 > entry.limit) return TRUE;
382     return FALSE;
383 }
384
385
386 /***********************************************************************
387  *           IsBadHugeReadPtr16   (KERNEL.346)
388  */
389 BOOL16 IsBadHugeReadPtr16( SEGPTR ptr, DWORD size )
390 {
391     WORD sel;
392     ldt_entry entry;
393
394     sel = SELECTOROF(ptr);
395     if (!sel) return TRUE;
396     if (IS_SELECTOR_FREE(sel)) return TRUE;
397     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
398     if ((entry.type == SEGMENT_CODE) && entry.read_only) return TRUE;
399     if (OFFSETOF(ptr) + size - 1 > entry.limit) return TRUE;
400     return FALSE;
401 }
402
403
404 /***********************************************************************
405  *           IsBadHugeWritePtr16   (KERNEL.347)
406  */
407 BOOL16 IsBadHugeWritePtr16( SEGPTR ptr, DWORD size )
408 {
409     WORD sel;
410     ldt_entry entry;
411
412     sel = SELECTOROF(ptr);
413     if (!sel) return TRUE;
414     if (IS_SELECTOR_FREE(sel)) return TRUE;
415     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
416     if ((entry.type == SEGMENT_CODE) || entry.read_only) return TRUE;
417     if (OFFSETOF(ptr) + size - 1 > entry.limit) return TRUE;
418     return FALSE;
419 }
420
421 /***********************************************************************
422  *           IsBadReadPtr16   (KERNEL.334)
423  */
424 BOOL16 IsBadReadPtr16( SEGPTR ptr, UINT16 size )
425 {
426     return IsBadHugeReadPtr16( ptr, size );
427 }
428
429
430 /***********************************************************************
431  *           IsBadWritePtr16   (KERNEL.335)
432  */
433 BOOL16 IsBadWritePtr16( SEGPTR ptr, UINT16 size )
434 {
435     return IsBadHugeWritePtr16( ptr, size );
436 }
437
438
439 /***********************************************************************
440  *           MemoryRead   (TOOLHELP.78)
441  */
442 DWORD MemoryRead( WORD sel, DWORD offset, void *buffer, DWORD count )
443 {
444     if (IS_SELECTOR_FREE(sel)) return 0;
445     if (offset > GET_SEL_LIMIT(sel)) return 0;
446     if (offset + count > GET_SEL_LIMIT(sel) + 1)
447         count = GET_SEL_LIMIT(sel) + 1 - offset;
448     memcpy( buffer, ((char *)GET_SEL_BASE(sel)) + offset, count );
449     return count;
450 }
451
452
453 /***********************************************************************
454  *           MemoryWrite   (TOOLHELP.79)
455  */
456 DWORD MemoryWrite( WORD sel, DWORD offset, void *buffer, DWORD count )
457 {
458     if (IS_SELECTOR_FREE(sel)) return 0;
459     if (offset > GET_SEL_LIMIT(sel)) return 0;
460     if (offset + count > GET_SEL_LIMIT(sel) + 1)
461         count = GET_SEL_LIMIT(sel) + 1 - offset;
462     memcpy( ((char *)GET_SEL_BASE(sel)) + offset, buffer, count );
463     return count;
464 }
465
466 /************************************* Win95 pointer mapping functions *
467  *
468  * NOTE: MapSLFix and UnMapSLFixArray are probably needed to prevent
469  * unexpected linear address change when GlobalCompact() shuffles
470  * moveable blocks.
471  */
472
473 /***********************************************************************
474  *           MapSL   (KERNEL32.662)
475  *
476  * Maps fixed segmented pointer to linear.
477  */
478 LPVOID MapSL( SEGPTR sptr )
479 {
480     return (LPVOID)PTR_SEG_TO_LIN(sptr);
481 }
482
483
484 /***********************************************************************
485  *           MapLS   (KERNEL32.679)
486  *
487  * Maps linear pointer to segmented.
488  */
489 SEGPTR MapLS( LPVOID ptr )
490 {
491     WORD sel = SELECTOR_AllocBlock( ptr, 0x10000, SEGMENT_DATA, FALSE, FALSE );
492     return PTR_SEG_OFF_TO_SEGPTR( sel, 0 );
493 }
494
495
496 /***********************************************************************
497  *           UnMapLS   (KERNEL32.680)
498  *
499  * Free mapped selector.
500  */
501 void UnMapLS( SEGPTR sptr )
502 {
503     if (!__winelib) SELECTOR_FreeBlock( SELECTOROF(sptr), 1 );
504 }