Release 950901
[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 "selectors.h"
11 #include "stackframe.h"
12 #include "stddebug.h"
13 #include "debug.h"
14
15
16 #define FIRST_LDT_ENTRY_TO_ALLOC  6
17
18
19 /***********************************************************************
20  *           AllocSelectorArray   (KERNEL.206)
21  */
22 WORD AllocSelectorArray( WORD count )
23 {
24     WORD i, size = 0;
25
26     if (!count) return 0;
27     for (i = FIRST_LDT_ENTRY_TO_ALLOC; i < LDT_SIZE; i++)
28     {
29         if (!IS_LDT_ENTRY_FREE(i)) size = 0;
30         else if (++size >= count) break;
31     }
32     if (i == LDT_SIZE) return 0;
33     /* Mark selector as allocated */
34     while (size--) ldt_flags_copy[i--] |= LDT_FLAGS_ALLOCATED;
35     return ENTRY_TO_SELECTOR( i + 1 );
36 }
37
38
39 /***********************************************************************
40  *           AllocSelector   (KERNEL.175)
41  */
42 WORD AllocSelector( WORD sel )
43 {
44     WORD newsel, count, i;
45
46     count = sel ? ((GET_SEL_LIMIT(sel) >> 16) + 1) : 1;
47     newsel = AllocSelectorArray( count );
48     dprintf_selector( stddeb, "AllocSelector(%04x): returning %04x\n",
49                       sel, newsel );
50     if (!newsel) return 0;
51     if (!sel) return newsel;  /* nothing to copy */
52     for (i = 0; i < count; i++)
53     {
54         ldt_entry entry;
55         LDT_GetEntry( SELECTOR_TO_ENTRY(sel) + i, &entry );
56         LDT_SetEntry( SELECTOR_TO_ENTRY(newsel) + i, &entry );
57     }
58     return newsel;
59 }
60
61
62 /***********************************************************************
63  *           FreeSelector   (KERNEL.176)
64  */
65 WORD FreeSelector( WORD sel )
66 {
67     WORD i, count;
68     ldt_entry entry;
69     STACK16FRAME *frame;
70
71     dprintf_selector( stddeb, "FreeSelector(%04x)\n", sel );
72     if (IS_SELECTOR_FREE(sel)) return sel;  /* error */
73     count = (GET_SEL_LIMIT(sel) >> 16) + 1;
74     memset( &entry, 0, sizeof(entry) );  /* clear the LDT entries */
75     /* FIXME: is it correct to free the whole array? */
76     for (i = SELECTOR_TO_ENTRY(sel); count; i++, count--)
77     {
78         LDT_SetEntry( i, &entry );
79         ldt_flags_copy[i] &= ~LDT_FLAGS_ALLOCATED;
80     }
81
82     /* Clear the saved 16-bit selector */
83 #ifndef WINELIB
84     frame = CURRENT_STACK16;
85     while (frame)
86     {
87         if (frame->ds == sel) frame->ds = 0;
88         if (frame->es == sel) frame->es = 0;
89         frame = PTR_SEG_OFF_TO_LIN(frame->saved_ss, frame->saved_sp);
90     }
91 #endif
92     return 0;
93 }
94
95
96 /***********************************************************************
97  *           SELECTOR_SetEntries
98  *
99  * Set the LDT entries for an array of selectors.
100  */
101 static void SELECTOR_SetEntries( WORD sel, void *base, DWORD size,
102                                  enum seg_type type, BOOL is32bit,
103                                  BOOL readonly )
104 {
105     ldt_entry entry;
106     WORD i, count;
107
108       /* The limit for the first selector is the whole */
109       /* block. The next selectors get a 64k limit.    */
110     entry.base           = (unsigned long)base;
111     entry.type           = type;
112     entry.seg_32bit      = is32bit;
113     entry.read_only      = readonly;
114     entry.limit_in_pages = (size > 0x100000);
115     if (entry.limit_in_pages) entry.limit = ((size + 0xfff) >> 12) - 1;
116     else entry.limit = size - 1;
117     count = (size + 0xffff) / 0x10000;
118     for (i = 0; i < count; i++)
119     {
120         LDT_SetEntry( SELECTOR_TO_ENTRY(sel) + i, &entry );
121         entry.base += 0x10000;
122         size -= 0x10000;
123         entry.limit = (size > 0x10000) ? 0xffff : size-1;
124         entry.limit_in_pages = 0;
125     }
126 }
127
128
129 /***********************************************************************
130  *           SELECTOR_AllocBlock
131  *
132  * Allocate selectors for a block of linear memory.
133  */
134 WORD SELECTOR_AllocBlock( void *base, DWORD size, enum seg_type type,
135                           BOOL is32bit, BOOL readonly )
136 {
137     WORD sel, count;
138
139     if (!size) return 0;
140     count = (size + 0xffff) / 0x10000;
141     sel = AllocSelectorArray( count );
142     if (sel) SELECTOR_SetEntries( sel, base, size, type, is32bit, readonly );
143     return sel;
144 }
145
146
147 /***********************************************************************
148  *           SELECTOR_ReallocBlock
149  *
150  * Change the size of a block of selectors.
151  */
152 WORD SELECTOR_ReallocBlock( WORD sel, void *base, DWORD size,
153                             enum seg_type type, BOOL is32bit, BOOL readonly )
154 {
155     WORD i, oldcount, newcount;
156     ldt_entry entry;
157
158     if (!size) size = 1;
159     oldcount = (GET_SEL_LIMIT(sel) >> 16) + 1;
160     newcount = (size + 0xffff) >> 16;
161
162     if (oldcount < newcount)  /* We need to add selectors */
163     {
164           /* Check if the next selectors are free */
165         if (SELECTOR_TO_ENTRY(sel) + newcount > LDT_SIZE) i = oldcount;
166         else
167             for (i = oldcount; i < newcount; i++)
168                 if (!IS_LDT_ENTRY_FREE(SELECTOR_TO_ENTRY(sel)+i)) break;
169
170         if (i < newcount)  /* they are not free */
171         {
172             FreeSelector( sel );
173             sel = AllocSelectorArray( newcount );
174         }
175         else  /* mark the selectors as allocated */
176         {
177             for (i = oldcount; i < newcount; i++)
178                 ldt_flags_copy[SELECTOR_TO_ENTRY(sel)+i] |=LDT_FLAGS_ALLOCATED;
179         }
180     }
181     else if (oldcount > newcount) /* We need to remove selectors */
182     {
183         memset( &entry, 0, sizeof(entry) );  /* clear the LDT entries */
184         for (i = oldcount; i < newcount; i++)
185         {
186             LDT_SetEntry( SELECTOR_TO_ENTRY(sel) + i, &entry );
187             ldt_flags_copy[SELECTOR_TO_ENTRY(sel) + i] &= ~LDT_FLAGS_ALLOCATED;
188         }
189     }
190     if (sel) SELECTOR_SetEntries( sel, base, size, type, is32bit, readonly );
191     return sel;
192 }
193
194
195 /***********************************************************************
196  *           PrestoChangoSelector   (KERNEL.177)
197  */
198 WORD PrestoChangoSelector( WORD selSrc, WORD selDst )
199 {
200     ldt_entry entry;
201     LDT_GetEntry( SELECTOR_TO_ENTRY( selSrc ), &entry );
202     entry.type ^= SEGMENT_CODE;  /* toggle the executable bit */
203     LDT_SetEntry( SELECTOR_TO_ENTRY( selDst ), &entry );
204     return selDst;
205 }
206
207
208 /***********************************************************************
209  *           AllocCStoDSAlias   (KERNEL.170)
210  */
211 WORD AllocCStoDSAlias( WORD sel )
212 {
213     WORD newsel;
214     ldt_entry entry;
215
216     newsel = AllocSelectorArray( 1 );
217     dprintf_selector( stddeb, "AllocCStoDSAlias(%04x): returning %04x\n",
218                       sel, newsel );
219     if (!newsel) return 0;
220     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
221     entry.type = SEGMENT_DATA;
222     LDT_SetEntry( SELECTOR_TO_ENTRY(newsel), &entry );
223     return newsel;
224 }
225
226
227 /***********************************************************************
228  *           AllocDStoCSAlias   (KERNEL.171)
229  */
230 WORD AllocDStoCSAlias( WORD sel )
231 {
232     WORD newsel;
233     ldt_entry entry;
234
235     newsel = AllocSelectorArray( 1 );
236     dprintf_selector( stddeb, "AllocDStoCSAlias(%04x): returning %04x\n",
237                       sel, newsel );
238     if (!newsel) return 0;
239     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
240     entry.type = SEGMENT_CODE;
241     LDT_SetEntry( SELECTOR_TO_ENTRY(newsel), &entry );
242     return newsel;
243 }
244
245
246 /***********************************************************************
247  *           LongPtrAdd   (KERNEL.180)
248  */
249 void LongPtrAdd( DWORD ptr, DWORD add )
250 {
251     ldt_entry entry;
252     LDT_GetEntry( SELECTOR_TO_ENTRY(SELECTOROF(ptr)), &entry );
253     entry.base += add;
254     LDT_SetEntry( SELECTOR_TO_ENTRY(SELECTOROF(ptr)), &entry );
255 }
256
257
258 /***********************************************************************
259  *           GetSelectorBase   (KERNEL.186)
260  */
261 DWORD GetSelectorBase( WORD sel )
262 {
263     return GET_SEL_BASE(sel);
264 }
265
266
267 /***********************************************************************
268  *           SetSelectorBase   (KERNEL.187)
269  */
270 WORD SetSelectorBase( WORD sel, DWORD base )
271 {
272     ldt_entry entry;
273     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
274     entry.base = base;
275     LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
276     return sel;
277 }
278
279
280 /***********************************************************************
281  *           GetSelectorLimit   (KERNEL.188)
282  */
283 DWORD GetSelectorLimit( WORD sel )
284 {
285     return GET_SEL_LIMIT(sel);
286 }
287
288
289 /***********************************************************************
290  *           SetSelectorLimit   (KERNEL.189)
291  */
292 WORD SetSelectorLimit( WORD sel, DWORD limit )
293 {
294     ldt_entry entry;
295     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
296     entry.limit = limit;
297     LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
298     return sel;
299 }
300
301
302 /***********************************************************************
303  *           SelectorAccessRights   (KERNEL.196)
304  */
305 WORD SelectorAccessRights( WORD sel, WORD op, WORD val )
306 {
307     ldt_entry entry;
308     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
309     if (op == 0)  /* get */
310     {
311         return 1 /* accessed */ |
312                (entry.read_only << 1) |
313                (entry.type << 2) |
314                (entry.seg_32bit << 14) |
315                (entry.limit_in_pages << 15);
316     }
317     else  /* set */
318     {
319         entry.read_only = val & 2;
320         entry.type = (val >> 2) & 3;
321         entry.seg_32bit = val & 0x4000;
322         entry.limit_in_pages = val & 0x8000;
323         LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
324         return 0;
325     }
326 }
327
328
329 /***********************************************************************
330  *           IsBadCodePtr   (KERNEL.336)
331  */
332 BOOL IsBadCodePtr( SEGPTR lpfn )
333 {
334     WORD sel;
335     ldt_entry entry;
336
337     sel = SELECTOROF(lpfn);
338     if (!sel) return TRUE;
339     if (IS_SELECTOR_FREE(sel)) return TRUE;
340     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
341     if (entry.type != SEGMENT_CODE) return TRUE;
342     if (OFFSETOF(lpfn) > entry.limit) return TRUE;
343     return FALSE;
344 }
345
346
347 /***********************************************************************
348  *           IsBadStringPtr   (KERNEL.337)
349  */
350 BOOL IsBadStringPtr( SEGPTR ptr, WORD size )
351 {
352     WORD sel;
353     ldt_entry entry;
354
355     sel = SELECTOROF(ptr);
356     if (!sel) return TRUE;
357     if (IS_SELECTOR_FREE(sel)) return TRUE;
358     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
359     if ((entry.type == SEGMENT_CODE) && entry.read_only) return TRUE;
360     if (strlen(PTR_SEG_TO_LIN(ptr)) < size) size = strlen(PTR_SEG_TO_LIN(ptr));
361     if (OFFSETOF(ptr) + size > entry.limit) return TRUE;
362     return FALSE;
363 }
364
365
366 /***********************************************************************
367  *           IsBadHugeReadPtr   (KERNEL.346)
368  */
369 BOOL IsBadHugeReadPtr( SEGPTR ptr, DWORD size )
370 {
371     WORD sel;
372     ldt_entry entry;
373
374     sel = SELECTOROF(ptr);
375     if (!sel) return TRUE;
376     if (IS_SELECTOR_FREE(sel)) return TRUE;
377     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
378     if ((entry.type == SEGMENT_CODE) && entry.read_only) return TRUE;
379     if (OFFSETOF(ptr) + size > entry.limit) return TRUE;
380     return FALSE;
381 }
382
383
384 /***********************************************************************
385  *           IsBadHugeWritePtr   (KERNEL.347)
386  */
387 BOOL IsBadHugeWritePtr( SEGPTR ptr, DWORD size )
388 {
389     WORD sel;
390     ldt_entry entry;
391
392     sel = SELECTOROF(ptr);
393     if (!sel) return TRUE;
394     if (IS_SELECTOR_FREE(sel)) return TRUE;
395     LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
396     if ((entry.type == SEGMENT_CODE) || entry.read_only) return TRUE;
397     if (OFFSETOF(ptr) + size > entry.limit) return TRUE;
398     return FALSE;
399 }
400
401 /***********************************************************************
402  *           IsBadReadPtr   (KERNEL.334)
403  */
404 BOOL IsBadReadPtr( SEGPTR ptr, WORD size )
405 {
406     return IsBadHugeReadPtr( ptr, size );
407 }
408
409
410 /***********************************************************************
411  *           IsBadWritePtr   (KERNEL.335)
412  */
413 BOOL IsBadWritePtr( SEGPTR ptr, WORD size )
414 {
415     return IsBadHugeWritePtr( ptr, size );
416 }
417
418
419 /***********************************************************************
420  *           MemoryRead   (TOOLHELP.78)
421  */
422 DWORD MemoryRead( WORD sel, DWORD offset, void *buffer, DWORD count )
423 {
424     if (IS_SELECTOR_FREE(sel)) return 0;
425     if (offset > GET_SEL_LIMIT(sel)) return 0;
426     if (offset + count > GET_SEL_LIMIT(sel) + 1)
427         count = GET_SEL_LIMIT(sel) + 1 - offset;
428     memcpy( buffer, ((char *)GET_SEL_BASE(sel)) + offset, count );
429     return count;
430 }
431
432
433 /***********************************************************************
434  *           MemoryWrite   (TOOLHELP.79)
435  */
436 DWORD MemoryWrite( WORD sel, DWORD offset, void *buffer, DWORD count )
437 {
438     if (IS_SELECTOR_FREE(sel)) return 0;
439     if (offset > GET_SEL_LIMIT(sel)) return 0;
440     if (offset + count > GET_SEL_LIMIT(sel) + 1)
441         count = GET_SEL_LIMIT(sel) + 1 - offset;
442     memcpy( ((char *)GET_SEL_BASE(sel)) + offset, buffer, count );
443     return count;
444 }