source: branches/gcc-kmk/src/kernel32/oslibmem.cpp@ 21720

Last change on this file since 21720 was 21720, checked in by dmik, 13 years ago

Replace "\" with "/" in include statements.

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 int i;
466 for(i=0;i<16;i++) {
467 rc = DosQueryMem((PVOID)ulAddr, &ulSize, &ulAttr);
468 if(rc != NO_ERROR) {
469 dprintf(("!ERROR!: OSLibDosFreeMem: DosQueryMem %x failed with rc %d", lpMemAddr, rc));
470 i = 16; //fail
471 break;
472 }
473 if(ulAttr & PAG_BASE) {
474 break;
475 }
476 ulAddr -= PAGE_SIZE;
477 }
478 if(i == 16) {
479 //oh, oh. didn't find base; shouldn't happen!!
480 dprintf(("!ERROR!: OSLibDosFreeMem: Unable to find base of %x", lpMemAddr));
481 DebugInt3();
482 return ERROR_INVALID_PARAMETER;
483 }
484 FreeAllocRec((ULONG)lpMemAddr);
485
486 RasRemoveObject (rthVirtual, (ULONG)lpMemAddr);
487
488 return DosFreeMem((PVOID)ulAddr);
489}
490//******************************************************************************
491//NOTE: If name == NULL, allocated gettable unnamed shared memory
492//OS/2 returns error 123 (invalid name) if the shared memory name includes
493//colons. We need to replace them with underscores.
494//******************************************************************************
495DWORD OSLibDosAllocSharedMem(LPVOID *lplpMemAddr, DWORD size, DWORD flags, LPSTR name)
496{
497 APIRET rc;
498 char *sharedmemname = NULL, *tmp;
499
500 if(name) {
501 sharedmemname = (char *)malloc(strlen(name) + 16);
502 strcpy(sharedmemname, "\\SHAREMEM\\");
503 strcat(sharedmemname, name);
504 }
505 else flags |= OBJ_GETTABLE;
506
507 //SvL: Colons are unacceptable in shared memory names (for whatever reason)
508 tmp = sharedmemname;
509 while(*tmp != 0) {
510 if(*tmp == ':') {
511 *tmp = '_';
512 }
513 tmp++;
514 }
515
516 rc = DosAllocSharedMem(lplpMemAddr, sharedmemname, size, flags);
517 if(name) {
518 free(sharedmemname);
519 }
520 return rc;
521}
522//******************************************************************************
523//NOTE: If name == NULL, assume gettable unnamed shared memory
524//******************************************************************************
525DWORD OSLibDosGetNamedSharedMem(LPVOID *lplpMemAddr, LPSTR name)
526{
527 APIRET rc;
528 char *sharedmemname = NULL, *tmp;
529
530 if(name) {
531 sharedmemname = (char *)malloc(strlen(name) + 16);
532 strcpy(sharedmemname, "\\SHAREMEM\\");
533 strcat(sharedmemname, name);
534
535 //SvL: Colons are unacceptable in shared memory names (for whatever reason)
536 tmp = sharedmemname;
537 while(*tmp != 0) {
538 if(*tmp == ':') {
539 *tmp = '_';
540 }
541 tmp++;
542 }
543 rc = DosGetNamedSharedMem(lplpMemAddr, sharedmemname, PAG_READ|PAG_WRITE);
544 if(name) {
545 free(sharedmemname);
546 }
547 }
548 else rc = DosGetSharedMem((LPVOID)*(DWORD *)lplpMemAddr, PAG_READ|PAG_WRITE);
549
550 return rc;
551}
552//******************************************************************************
553//******************************************************************************
554DWORD OSLibDosQueryMem(LPVOID lpMemAddr, DWORD *lpRangeSize, DWORD *lpAttr)
555{
556 return DosQueryMem(lpMemAddr, lpRangeSize, lpAttr);
557}
558//******************************************************************************
559//******************************************************************************
560DWORD OSLibDosSetMem(LPVOID lpMemAddr, DWORD size, DWORD flags)
561{
562 APIRET rc;
563
564 rc = DosSetMem(lpMemAddr, size, flags);
565 switch(rc) {
566 case ERROR_INVALID_ADDRESS:
567 return OSLIB_ERROR_INVALID_ADDRESS;
568 case ERROR_ACCESS_DENIED:
569 return OSLIB_ERROR_ACCESS_DENIED;
570 default:
571 return rc;
572 }
573}
574//******************************************************************************
575//******************************************************************************
Note: See TracBrowser for help on using the repository browser.