source: trunk/src/kernel32/oslibmem.cpp@ 21339

Last change on this file since 21339 was 21339, checked in by vladest, 16 years ago
  1. Attempt to add support for DosAllocMem at specific address
  2. Fixed crashes in Handle manager when its tries to access non initialized pointer
File size: 17.5 KB
Line 
1/* $Id: oslibmem.cpp,v 1.7 2003-03-27 14:00:53 sandervl Exp $ */
2/*
3 * Wrappers for OS/2 Dos* API
4 *
5 * Copyright 1998-2002 Sander van Leeuwen (sandervl@xs4all.nl)
6 * Copyright 2000 knut st. osmundsen (knut.stange.osmundsen@mynd.no)
7 *
8 * Project Odin Software License can be found in LICENSE.TXT
9 *
10 */
11
12
13
14/*******************************************************************************
15* Header Files *
16*******************************************************************************/
17#define INCL_BASE
18#define INCL_DOSEXCEPTIONS
19#define INCL_DOSMEMMGR
20#define INCL_DOSPROCESS
21#define INCL_DOSFILEMGR
22#define INCL_DOSERRORS
23#define INCL_DOSDEVIOCTL
24#define INCL_DOSDEVICES
25#define INCL_NPIPES
26#include <os2wrap.h> //Odin32 OS/2 api wrappers
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30#include <ctype.h>
31#include <win32api.h>
32#include <winconst.h>
33#include <win\winioctl.h>
34#include <dbglog.h>
35#include <vmutex.h>
36#include "initterm.h"
37#include "oslibdos.h"
38#include "oslibmem.h"
39#include "dosqss.h"
40#include "win32k.h"
41#include "exceptstackdump.h"
42
43#define DBG_LOCALLOG DBG_oslibmem
44#include "dbglocal.h"
45
46#include <_ras.h>
47
48#ifdef RAS
49RAS_TRACK_HANDLE rthVirtual = 0;
50
51void rasInitVirtual (void)
52{
53 RasRegisterObjectTracking (&rthVirtual, "Virtual* memory allocation",
54 0, RAS_TRACK_FLAG_MEMORY | RAS_TRACK_FLAG_LOG_OBJECTS_AT_EXIT,
55 NULL, NULL);
56}
57#endif
58
59typedef struct _VirtAllocRec {
60 ULONG baseaddr;
61 ULONG size;
62 ULONG attr;
63
64 struct _VirtAllocRec *next;
65} VirtAllocRec;
66
67static VirtAllocRec *allocrecords = NULL;
68static CRITICAL_SECTION_OS2 alloccritsect = {0};
69
70//******************************************************************************
71//******************************************************************************
72void AddAllocRec(ULONG baseaddr, ULONG size, ULONG attr)
73{
74 VirtAllocRec *rec, *tmp;
75
76 rec = (VirtAllocRec *)malloc(sizeof(VirtAllocRec));
77 if(!rec) {
78 DebugInt3();
79 return;
80 }
81 rec->baseaddr = baseaddr;
82 rec->size = size;
83 rec->attr = attr;
84
85 DosEnterCriticalSection(&alloccritsect);
86 if(!allocrecords || allocrecords->baseaddr > baseaddr) {
87 rec->next = allocrecords;
88 allocrecords = rec;
89 }
90 else {
91 tmp = allocrecords;
92 while(tmp->next) {
93 if(tmp->next->baseaddr > baseaddr) {
94 break;
95 }
96 tmp = tmp->next;
97 }
98
99 rec->next = tmp->next;
100 tmp->next = rec;
101 }
102 DosLeaveCriticalSection(&alloccritsect);
103}
104//******************************************************************************
105//******************************************************************************
106void FreeAllocRec(ULONG baseaddr)
107{
108 VirtAllocRec *rec = NULL, *tmp;
109
110 if(!allocrecords) {
111 DebugInt3();
112 return;
113 }
114
115 DosEnterCriticalSection(&alloccritsect);
116 if(allocrecords->baseaddr == baseaddr) {
117 rec = allocrecords;
118 allocrecords = allocrecords->next;
119 }
120 else {
121 tmp = allocrecords;
122 while(tmp->next) {
123 if(tmp->next->baseaddr == baseaddr) {
124 break;
125 }
126 tmp = tmp->next;
127 }
128 if(tmp->next) {
129 rec = tmp->next;
130 tmp->next = tmp->next->next;
131 }
132 else dprintf(("ERROR: FreeAllocRec: allocation not found!! (%x)", baseaddr));
133 }
134 DosLeaveCriticalSection(&alloccritsect);
135 if(rec) free(rec);
136}
137//******************************************************************************
138//******************************************************************************
139BOOL FindAllocRec(ULONG addr, ULONG *lpBase, ULONG *lpSize, ULONG *lpAttr)
140{
141 VirtAllocRec *rec = NULL;
142
143 DosEnterCriticalSection(&alloccritsect);
144 rec = allocrecords;
145 while(rec) {
146 if(rec->baseaddr <= addr && rec->baseaddr + rec->size > addr) {
147 *lpBase = rec->baseaddr;
148 *lpSize = rec->size;
149 *lpAttr = rec->attr;
150 break; //found it
151 }
152 if(rec->baseaddr > addr) {
153 //sorted list, so no need to search any further
154 rec = NULL;
155 break;
156 }
157 rec = rec->next;
158 }
159 DosLeaveCriticalSection(&alloccritsect);
160 return (rec != NULL);
161}
162//******************************************************************************
163//TODO: Check if this works for code aliases...
164//NOTE: DosAliasMem fails if the address isn't page aligned
165//******************************************************************************
166DWORD OSLibDosAliasMem(LPVOID pb, ULONG cb, LPVOID *ppbAlias, ULONG fl)
167{
168 DWORD rc;
169 DWORD attr;
170 DWORD size = cb;
171
172 cb = (cb-1) & ~0xfff;
173 cb+= PAGE_SIZE;
174
175 rc = DosAliasMem(pb, cb, ppbAlias, 2);
176 if(rc) {
177 dprintf(("!ERROR!: OSLibDosAliasMem: DosAliasMem %x %x returned %d", pb, cb, rc));
178 return rc;
179 }
180 //Now try to change the protection flags of all pages in the aliased range
181 DWORD pAlias = (DWORD)*ppbAlias;
182
183 while(pAlias < (DWORD)*ppbAlias + cb)
184 {
185 rc = DosQueryMem((PVOID)pAlias, &size, &attr);
186 if(rc != NO_ERROR) {
187 dprintf(("!ERROR!: OSLibDosAliasMem: DosQueryMem %x returned %d", pAlias, rc));
188 DebugInt3();
189 return rc;
190 }
191 //Don't bother if the pages are not committed. DosSetMem will return
192 //ERROR_ACCESS_DENIED.
193 if(attr & PAG_COMMIT) {
194 rc = DosSetMem((PVOID)pAlias, size, fl);
195 if(rc) {
196 dprintf(("!ERROR!: OSLibDosAliasMem: DosSetMem %x %x return %d", *ppbAlias, size, rc));
197 DebugInt3();
198 return rc;
199 }
200 }
201 pAlias += size;
202 }
203 AddAllocRec((ULONG)*ppbAlias, cb, fl);
204 return 0;
205}
206
207//***************************************************************************
208//Allocation memory at address helper
209//***************************************************************************
210#define OBJ_LOCSPECIFIC 0x00001000UL
211
212int allocAtAddress(void *pvReq, ULONG cbReq, ULONG fReq)
213{
214 dprintf(("DosAllocMemEx pvReq=%p cbReq=%lu fReq=%#lx\n", pvReq, cbReq, fReq));
215 PVOID apvTmps[3000];
216 ULONG cbTmp;
217 ULONG fTmp;
218 int iTmp;
219 int rcRet = ERROR_NOT_ENOUGH_MEMORY;
220
221 /*
222 * Adjust flags and size.
223 */
224 if ((ULONG)pvReq < 0x20000000 /*512MB*/)
225 fReq &= ~OBJ_ANY;
226 else
227 fReq |= OBJ_ANY;
228 cbReq = (cbReq + 0xfff) & ~0xfff;
229
230 /*
231 * Allocation loop.
232 * This algorithm is not optimal!
233 */
234 fTmp = fReq & ~(PAG_COMMIT);
235 cbTmp = 1*1024*1024; /* 1MB*/
236 for (iTmp = 0; iTmp < sizeof(apvTmps) / sizeof(apvTmps[0]); iTmp++)
237 {
238 PVOID pvNew = NULL;
239 int rc;
240
241 /* Allocate chunk. */
242 rc = DosAllocMem(&pvNew, cbReq, fReq);
243 apvTmps[iTmp] = pvNew;
244 if (rc)
245 break;
246
247 /*
248 * Passed it?
249 * Then retry with the requested size.
250 */
251 if (pvNew > pvReq)
252 {
253 if (cbTmp <= cbReq)
254 break;
255 DosFreeMem(pvNew);
256 cbTmp = cbReq;
257 iTmp--;
258 continue;
259 }
260
261 /*
262 * Does the allocated object touch into the requested one?
263 */
264 if ((char *)pvNew + cbTmp > (char *)pvReq)
265 {
266 /*
267 * Yes, we've found the requested address!
268 */
269 apvTmps[iTmp] = NULL;
270 DosFreeMem(pvNew);
271
272 /*
273 * Adjust the allocation size to fill the gap between the
274 * one we just got and the requested one.
275 * If no gap we'll attempt the real allocation.
276 */
277 cbTmp = (ULONG)pvReq - (ULONG)pvNew;
278 if (cbTmp)
279 {
280 iTmp--;
281 continue;
282 }
283
284 rc = DosAllocMem(&pvNew, cbReq, fReq);
285 if (rc || (char *)pvNew > (char *)pvReq)
286 break; /* we failed! */
287 if (pvNew == pvReq)
288 {
289 rcRet = 0;
290 break;
291 }
292
293 /*
294 * We've got an object which start is below the one we
295 * requested. This is probably caused by the requested object
296 * fitting in somewhere our tmp objects didn't.
297 * So, we'll have loop and retry till all such holes are filled.
298 */
299 apvTmps[iTmp] = pvNew;
300 }
301 }
302
303 /*
304 * Cleanup reserved memory and return.
305 */
306 while (iTmp-- > 0)
307 if (apvTmps[iTmp])
308 DosFreeMem(apvTmps[iTmp]);
309
310 return rcRet;
311}
312
313//******************************************************************************
314//Allocate memory aligned at 64kb boundary
315//******************************************************************************
316DWORD OSLibDosAllocMem(LPVOID *lplpMemAddr, DWORD cbSize, DWORD flFlags, BOOL fLowMemory)
317{
318 PVOID pvMemAddr;
319 DWORD offset;
320 APIRET rc;
321 BOOL fMemFlags = flAllocMem;
322
323 //Override low/high memory flag if necessary
324 if(fLowMemory) {
325 fMemFlags = 0;
326 }
327
328 /*
329 * Let's try use the extended DosAllocMem API of Win32k.sys.
330 */
331 if (libWin32kInstalled())
332 {
333 rc = DosAllocMemEx(lplpMemAddr, cbSize, flFlags | fMemFlags | OBJ_ALIGN64K);
334#ifdef RAS
335 if (rc == NO_ERROR)
336 {
337 RasAddObject (rthVirtual, (ULONG)*lplpMemAddr, NULL, cbSize);
338 }
339#endif
340 if (rc != ERROR_NOT_SUPPORTED) /* This call was stubbed until recently. */
341 return rc;
342 }
343
344 /*
345 * If no or old Win32k fall back to old method.
346 */
347
348 if (flFlags & OBJ_LOCSPECIFIC)
349 {
350 rc = allocAtAddress(&pvMemAddr, cbSize, (flFlags & ~OBJ_LOCSPECIFIC) | fMemFlags);
351 } else
352 {
353 rc = DosAllocMem(&pvMemAddr, cbSize, flFlags | fMemFlags);
354 }
355 if(rc) {
356 dprintf(("!ERROR!: DosAllocMem failed with rc %d", rc));
357 return rc;
358 }
359 // already 64k aligned ?
360 if((ULONG) pvMemAddr & 0xFFFF)
361 {
362 ULONG addr64kb;
363
364 //free misaligned allocated memory
365 DosFreeMem(pvMemAddr);
366
367 //Allocate 64kb more so we can round the address to a 64kb aligned value
368 rc = DosAllocMem((PPVOID)&addr64kb, cbSize + 64*1024, (flFlags & ~PAG_COMMIT) | fMemFlags);
369 if(rc) {
370 dprintf(("!ERROR!: DosAllocMem failed with rc %d", rc));
371 return rc;
372 }
373
374 PVOID baseAddr = (PVOID)addr64kb; // sunlover20040613: save returned address for a possible Free on failure
375
376 dprintf(("Allocate aligned memory %x -> %x", addr64kb, (addr64kb + 0xFFFF) & ~0xFFFF));
377
378 if(addr64kb & 0xFFFF) {
379 addr64kb = (addr64kb + 0xFFFF) & ~0xFFFF;
380 }
381 pvMemAddr = (PVOID)addr64kb;
382
383 //and set the correct page flags for the request range
384 if((flFlags & ~PAG_COMMIT) != flFlags) {
385 rc = DosSetMem(pvMemAddr, cbSize, flFlags);
386 if(rc) {
387 dprintf(("!ERROR!: DosSetMem failed with rc %d", rc));
388 DosFreeMem (baseAddr); // sunlover20040613: Free allocated memory
389 return rc;
390 }
391 }
392 }
393
394 if(!rc) {
395 *lplpMemAddr = pvMemAddr;
396 AddAllocRec((ULONG)pvMemAddr, cbSize, flFlags);
397 RasAddObject (rthVirtual, (ULONG)*lplpMemAddr, NULL, cbSize);
398 }
399 return rc;
400}
401//******************************************************************************
402//Locate the base page of a memory allocation (the page with the PAG_BASE attribute)
403//******************************************************************************
404PVOID OSLibDosFindMemBase(LPVOID lpMemAddr, DWORD *lpAttr)
405{
406 ULONG ulAttr, ulSize, ulAddr, ulBase;
407 APIRET rc;
408 VirtAllocRec *allocrec;
409
410 *lpAttr = 0;
411
412 if(FindAllocRec((ULONG)lpMemAddr, &ulBase, &ulSize, lpAttr) == TRUE) {
413 return (PVOID)ulBase;
414 }
415
416 ulSize = PAGE_SIZE;
417 rc = DosQueryMem(lpMemAddr, &ulSize, &ulAttr);
418 if(rc != NO_ERROR) {
419 dprintf(("!ERROR!: OSLibDosFindMemBase: DosQueryMem %x failed with rc %d", lpMemAddr, rc));
420 return lpMemAddr;
421 }
422 if(!(ulAttr & PAG_BASE)) {
423 //Not the base of the initial allocation (can happen due to alignment) or app
424 //passing address inside memory allocation range
425 ulAddr = (DWORD)lpMemAddr & ~0xFFF;
426 ulAddr -= PAGE_SIZE;
427
428 while(ulAddr > 0)
429 {
430 rc = DosQueryMem((PVOID)ulAddr, &ulSize, &ulAttr);
431 if(rc) {
432 dprintf(("!ERROR!: OSLibDosFindMemBase: DosQueryMem %x failed with rc %d", lpMemAddr, rc));
433 DebugInt3();
434 return NULL;
435 }
436 if(ulAttr & PAG_BASE) {
437 //Memory below the 512 MB boundary is always aligned at 64kb and VirtualAlloc only
438 //returns high memory (if OS/2 version supports it)
439 //If it is above the 512 MB boundary, then we must make sure the right base address
440 //is returned. VirtualAlloc allocates extra memory to make sure it can return addresses
441 //aligned at 64kb. If extra pages are needed, then the allocation base is inside
442 //the filler region. In that case we must return the next 64kb address as base.
443 if(ulAddr > MEM_TILED_CEILING) {
444 ulAddr = (ulAddr + 0xFFFF) & ~0xFFFF;
445 }
446 lpMemAddr = (PVOID)ulAddr;
447 break;
448 }
449 ulAddr -= PAGE_SIZE;
450 }
451 }
452 return lpMemAddr;
453}
454//******************************************************************************
455//******************************************************************************
456DWORD OSLibDosFreeMem(LPVOID lpMemAddr)
457{
458 ULONG ulAttr, ulSize, ulAddr;
459 APIRET rc;
460
461 ulAddr = (DWORD)lpMemAddr & ~0xFFF;
462 ulSize = 0x1000;
463
464 //Find base within previous 64kb (alignment can add filler pages)
465 for(int i=0;i<16;i++) {
466 rc = DosQueryMem((PVOID)ulAddr, &ulSize, &ulAttr);
467 if(rc != NO_ERROR) {
468 dprintf(("!ERROR!: OSLibDosFreeMem: DosQueryMem %x failed with rc %d", lpMemAddr, rc));
469 i = 16; //fail
470 break;
471 }
472 if(ulAttr & PAG_BASE) {
473 break;
474 }
475 ulAddr -= PAGE_SIZE;
476 }
477 if(i == 16) {
478 //oh, oh. didn't find base; shouldn't happen!!
479 dprintf(("!ERROR!: OSLibDosFreeMem: Unable to find base of %x", lpMemAddr));
480 DebugInt3();
481 return ERROR_INVALID_PARAMETER;
482 }
483 FreeAllocRec((ULONG)lpMemAddr);
484
485 RasRemoveObject (rthVirtual, (ULONG)lpMemAddr);
486
487 return DosFreeMem((PVOID)ulAddr);
488}
489//******************************************************************************
490//NOTE: If name == NULL, allocated gettable unnamed shared memory
491//OS/2 returns error 123 (invalid name) if the shared memory name includes
492//colons. We need to replace them with underscores.
493//******************************************************************************
494DWORD OSLibDosAllocSharedMem(LPVOID *lplpMemAddr, DWORD size, DWORD flags, LPSTR name)
495{
496 APIRET rc;
497 char *sharedmemname = NULL, *tmp;
498
499 if(name) {
500 sharedmemname = (char *)malloc(strlen(name) + 16);
501 strcpy(sharedmemname, "\\SHAREMEM\\");
502 strcat(sharedmemname, name);
503 }
504 else flags |= OBJ_GETTABLE;
505
506 //SvL: Colons are unacceptable in shared memory names (for whatever reason)
507 tmp = sharedmemname;
508 while(*tmp != 0) {
509 if(*tmp == ':') {
510 *tmp = '_';
511 }
512 tmp++;
513 }
514
515 rc = DosAllocSharedMem(lplpMemAddr, sharedmemname, size, flags);
516 if(name) {
517 free(sharedmemname);
518 }
519 return rc;
520}
521//******************************************************************************
522//NOTE: If name == NULL, assume gettable unnamed shared memory
523//******************************************************************************
524DWORD OSLibDosGetNamedSharedMem(LPVOID *lplpMemAddr, LPSTR name)
525{
526 APIRET rc;
527 char *sharedmemname = NULL, *tmp;
528
529 if(name) {
530 sharedmemname = (char *)malloc(strlen(name) + 16);
531 strcpy(sharedmemname, "\\SHAREMEM\\");
532 strcat(sharedmemname, name);
533
534 //SvL: Colons are unacceptable in shared memory names (for whatever reason)
535 tmp = sharedmemname;
536 while(*tmp != 0) {
537 if(*tmp == ':') {
538 *tmp = '_';
539 }
540 tmp++;
541 }
542 rc = DosGetNamedSharedMem(lplpMemAddr, sharedmemname, PAG_READ|PAG_WRITE);
543 if(name) {
544 free(sharedmemname);
545 }
546 }
547 else rc = DosGetSharedMem((LPVOID)*(DWORD *)lplpMemAddr, PAG_READ|PAG_WRITE);
548
549 return rc;
550}
551//******************************************************************************
552//******************************************************************************
553DWORD OSLibDosQueryMem(LPVOID lpMemAddr, DWORD *lpRangeSize, DWORD *lpAttr)
554{
555 return DosQueryMem(lpMemAddr, lpRangeSize, lpAttr);
556}
557//******************************************************************************
558//******************************************************************************
559DWORD OSLibDosSetMem(LPVOID lpMemAddr, DWORD size, DWORD flags)
560{
561 APIRET rc;
562
563 rc = DosSetMem(lpMemAddr, size, flags);
564 switch(rc) {
565 case ERROR_INVALID_ADDRESS:
566 return OSLIB_ERROR_INVALID_ADDRESS;
567 case ERROR_ACCESS_DENIED:
568 return OSLIB_ERROR_ACCESS_DENIED;
569 default:
570 return rc;
571 }
572}
573//******************************************************************************
574//******************************************************************************
Note: See TracBrowser for help on using the repository browser.