1 /*
2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c
5 * PURPOSE: DOS XMS Driver and UMB Provider
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 *
9 * DOCUMENTATION: Official specifications:
10 * XMS v2.0: http://www.phatcode.net/res/219/files/xms20.txt
11 * XMS v3.0: http://www.phatcode.net/res/219/files/xms30.txt
12 *
13 * About the implementation of UMBs in DOS:
14 * ----------------------------------------
15 * DOS needs a UMB provider to be able to use chunks of RAM in the C000-EFF0
16 * memory region. A UMB provider detects regions of memory that do not contain
17 * any ROMs or other system mapped area such as video RAM.
18 *
19 * Where can UMB providers be found?
20 *
21 * The XMS specification (see himem.c) provides three APIs to create, free, and
22 * resize UMB blocks. As such, DOS performs calls into the XMS driver chain to
23 * request UMB blocks and include them into the DOS memory arena.
24 * However, is it only the HIMEM driver (implementing the XMS specification)
25 * which can provide UMBs? It appears that this is not necessarily the case:
26 * for example the MS HIMEM versions do not implement the UMB APIs; instead
27 * it is the EMS driver (EMM386) which provides them, by hooking into the XMS
28 * driver chain (see https://support.microsoft.com/en-us/kb/95555 : "MS-DOS 5.0
29 * and later EMM386.EXE can also be configured to provide UMBs according to the
30 * XMS. This causes EMM386.EXE to be a provider of the UMB portion of the XMS.").
31 *
32 * Some alternative replacements of HIMEM/EMM386 (for example in FreeDOS)
33 * implement everything inside only one driver (XMS+EMS+UMB provider).
34 * Finally there are some UMB providers that exist separately of XMS and EMS
35 * drivers (for example, UMBPCI): they use hardware-specific tricks to discover
36 * and provide UMBs.
37 *
38 * For more details, see:
39 * http://www.freedos.org/technotes/technote/txt/169.txt
40 * http://www.freedos.org/technotes/technote/txt/202.txt
41 * http://www.uncreativelabs.net/textfiles/system/UMB.TXT
42 *
43 * This DOS XMS Driver provides the UMB APIs that are implemented on top
44 * of the internal Upper Memory Area Manager, in umamgr.c
45 */
46
47 /* INCLUDES *******************************************************************/
48
49 #include "ntvdm.h"
50
51 #define NDEBUG
52 #include <debug.h>
53
54 #include "emulator.h"
55 #include "cpu/bop.h"
56 #include "../../memory.h"
57 #include "bios/umamgr.h"
58
59 #include "device.h"
60 #include "himem.h"
61
62 #define XMS_DEVICE_NAME "XMSXXXX0"
63
64 /* BOP Identifiers */
65 #define BOP_XMS 0x52
66
67 /* PRIVATE VARIABLES **********************************************************/
68
69 static const BYTE EntryProcedure[] =
70 {
71 0xEB, // jmp short +0x03
72 0x03,
73 0x90, // nop
74 0x90, // nop
75 0x90, // nop
76 LOBYTE(EMULATOR_BOP),
77 HIBYTE(EMULATOR_BOP),
78 BOP_XMS,
79 0xCB // retf
80 };
81
82 static PDOS_DEVICE_NODE Node = NULL;
83 static XMS_HANDLE HandleTable[XMS_MAX_HANDLES];
84 static WORD FreeBlocks = XMS_BLOCKS;
85 static RTL_BITMAP AllocBitmap;
86 static ULONG BitmapBuffer[(XMS_BLOCKS + 31) / 32];
87
88 /*
89 * This value is associated to HIMEM's "/HMAMIN=" switch. It indicates the
90 * minimum account of space in the HMA a program can use, and is used in
91 * conjunction with the "Request HMA" function.
92 *
93 * NOTE: The "/HMAMIN=" value is in kilo-bytes, whereas HmaMinSize is in bytes.
94 *
95 * Default value: 0. This causes the HMA to be allocated on a first come,
96 * first served basis.
97 */
98 static WORD HmaMinSize = 0;
99 /*
100 * Flag used by "Request/Release HMA" functions, which indicates
101 * whether the HMA was reserved or not.
102 */
103 static BOOLEAN IsHmaReserved = FALSE;
104
105 /*
106 * Flag used by "Global Enable/Disable A20" functions, so that they don't
107 * need to re-change the state of A20 if it was already enabled/disabled.
108 */
109 static BOOLEAN IsA20Enabled = FALSE;
110 /*
111 * This flag is set to TRUE or FALSE when A20 line was already disabled or
112 * enabled when XMS driver was loaded.
113 * In case A20 was disabled, we are allowed to modify it. In case A20 was
114 * already enabled, we are not allowed to touch it.
115 */
116 static BOOLEAN CanChangeA20 = TRUE;
117 /*
118 * Count for enabling or disabling the A20 line. The A20 line is enabled
119 * only if the enabling count is greater than or equal to 0.
120 */
121 static LONG A20EnableCount = 0;
122
123 /* A20 LINE HELPERS ***********************************************************/
124
XmsLocalEnableA20(VOID)125 static VOID XmsLocalEnableA20(VOID)
126 {
127 /* Enable A20 only if we can do so, otherwise make the caller believe we enabled it */
128 if (!CanChangeA20) goto Quit;
129
130 /* The count is zero so enable A20 */
131 if (A20EnableCount == 0) EmulatorSetA20(TRUE);
132
133 ++A20EnableCount;
134
135 Quit:
136 setAX(0x0001); /* Line successfully enabled */
137 setBL(XMS_STATUS_SUCCESS);
138 return;
139 }
140
XmsLocalDisableA20(VOID)141 static VOID XmsLocalDisableA20(VOID)
142 {
143 UCHAR Result = XMS_STATUS_SUCCESS;
144
145 /* Disable A20 only if we can do so, otherwise make the caller believe we disabled it */
146 if (!CanChangeA20) goto Quit;
147
148 /* If the count is already zero, fail */
149 if (A20EnableCount == 0) goto Fail;
150
151 --A20EnableCount;
152
153 /* The count is zero so disable A20 */
154 if (A20EnableCount == 0)
155 EmulatorSetA20(FALSE); // Result = XMS_STATUS_SUCCESS;
156 else
157 Result = XMS_STATUS_A20_STILL_ENABLED;
158
159 Quit:
160 setAX(0x0001); /* Line successfully disabled */
161 setBL(Result);
162 return;
163
164 Fail:
165 setAX(0x0000); /* Line failed to be disabled */
166 setBL(XMS_STATUS_A20_ERROR);
167 return;
168 }
169
170 /* PRIVATE FUNCTIONS **********************************************************/
171
GetXmsHandleRecord(WORD Handle)172 static inline PXMS_HANDLE GetXmsHandleRecord(WORD Handle)
173 {
174 PXMS_HANDLE Entry;
175 if (Handle == 0 || Handle >= XMS_MAX_HANDLES) return NULL;
176
177 Entry = &HandleTable[Handle - 1];
178 return Entry->Size ? Entry : NULL;
179 }
180
ValidateXmsHandle(PXMS_HANDLE HandleEntry)181 static inline BOOLEAN ValidateXmsHandle(PXMS_HANDLE HandleEntry)
182 {
183 return (HandleEntry != NULL && HandleEntry->Handle != 0);
184 }
185
XmsGetLargestFreeBlock(VOID)186 static WORD XmsGetLargestFreeBlock(VOID)
187 {
188 WORD Result = 0;
189 DWORD CurrentIndex = 0;
190 ULONG RunStart;
191 ULONG RunSize;
192
193 while (CurrentIndex < XMS_BLOCKS)
194 {
195 RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
196 if (RunSize == 0) break;
197
198 /* Update the maximum */
199 if (RunSize > Result) Result = RunSize;
200
201 /* Go to the next run */
202 CurrentIndex = RunStart + RunSize;
203 }
204
205 return Result;
206 }
207
XmsAlloc(WORD Size,PWORD Handle)208 static UCHAR XmsAlloc(WORD Size, PWORD Handle)
209 {
210 BYTE i;
211 PXMS_HANDLE HandleEntry;
212 DWORD CurrentIndex = 0;
213 ULONG RunStart;
214 ULONG RunSize;
215
216 if (Size > FreeBlocks) return XMS_STATUS_OUT_OF_MEMORY;
217
218 for (i = 0; i < XMS_MAX_HANDLES; i++)
219 {
220 HandleEntry = &HandleTable[i];
221 if (HandleEntry->Handle == 0)
222 {
223 *Handle = i + 1;
224 break;
225 }
226 }
227
228 if (i == XMS_MAX_HANDLES) return XMS_STATUS_OUT_OF_HANDLES;
229
230 /* Optimize blocks */
231 for (i = 0; i < XMS_MAX_HANDLES; i++)
232 {
233 /* Skip free and locked blocks */
234 if (HandleEntry->Handle == 0 || HandleEntry->LockCount > 0) continue;
235
236 CurrentIndex = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE;
237
238 /* Check if there is any free space before this block */
239 RunSize = RtlFindLastBackwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
240 if (RunSize == 0) break;
241
242 /* Move this block back */
243 RtlMoveMemory((PVOID)REAL_TO_PHYS(HandleEntry->Address - RunSize * XMS_BLOCK_SIZE),
244 (PVOID)REAL_TO_PHYS(HandleEntry->Address),
245 RunSize * XMS_BLOCK_SIZE);
246
247 /* Update the address */
248 HandleEntry->Address -= RunSize * XMS_BLOCK_SIZE;
249 }
250
251 while (CurrentIndex < XMS_BLOCKS)
252 {
253 RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
254 if (RunSize == 0) break;
255
256 if (RunSize >= HandleEntry->Size)
257 {
258 /* Allocate it here */
259 HandleEntry->Handle = i + 1;
260 HandleEntry->LockCount = 0;
261 HandleEntry->Size = Size;
262 HandleEntry->Address = XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE;
263
264 FreeBlocks -= Size;
265 RtlSetBits(&AllocBitmap, RunStart, HandleEntry->Size);
266
267 return XMS_STATUS_SUCCESS;
268 }
269
270 /* Keep searching */
271 CurrentIndex = RunStart + RunSize;
272 }
273
274 return XMS_STATUS_OUT_OF_MEMORY;
275 }
276
XmsRealloc(WORD Handle,WORD NewSize)277 static UCHAR XmsRealloc(WORD Handle, WORD NewSize)
278 {
279 DWORD BlockNumber;
280 PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
281 DWORD CurrentIndex = 0;
282 ULONG RunStart;
283 ULONG RunSize;
284
285 if (!ValidateXmsHandle(HandleEntry))
286 return XMS_STATUS_INVALID_HANDLE;
287
288 if (HandleEntry->LockCount)
289 return XMS_STATUS_LOCKED;
290
291 /* Get the block number */
292 BlockNumber = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE;
293
294 if (NewSize < HandleEntry->Size)
295 {
296 /* Just reduce the size of this block */
297 RtlClearBits(&AllocBitmap, BlockNumber + NewSize, HandleEntry->Size - NewSize);
298 FreeBlocks += HandleEntry->Size - NewSize;
299 HandleEntry->Size = NewSize;
300 }
301 else if (NewSize > HandleEntry->Size)
302 {
303 /* Check if we can expand in-place */
304 if (RtlAreBitsClear(&AllocBitmap,
305 BlockNumber + HandleEntry->Size,
306 NewSize - HandleEntry->Size))
307 {
308 /* Just increase the size of this block */
309 RtlSetBits(&AllocBitmap,
310 BlockNumber + HandleEntry->Size,
311 NewSize - HandleEntry->Size);
312 FreeBlocks -= NewSize - HandleEntry->Size;
313 HandleEntry->Size = NewSize;
314
315 /* We're done */
316 return XMS_STATUS_SUCCESS;
317 }
318
319 /* Deallocate the current block range */
320 RtlClearBits(&AllocBitmap, BlockNumber, HandleEntry->Size);
321
322 /* Find a new place for this block */
323 while (CurrentIndex < XMS_BLOCKS)
324 {
325 RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
326 if (RunSize == 0) break;
327
328 if (RunSize >= NewSize)
329 {
330 /* Allocate the new range */
331 RtlSetBits(&AllocBitmap, RunStart, NewSize);
332
333 /* Move the data to the new location */
334 RtlMoveMemory((PVOID)REAL_TO_PHYS(XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE),
335 (PVOID)REAL_TO_PHYS(HandleEntry->Address),
336 HandleEntry->Size * XMS_BLOCK_SIZE);
337
338 /* Update the handle entry */
339 HandleEntry->Address = XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE;
340 HandleEntry->Size = NewSize;
341
342 /* Update the free block counter */
343 FreeBlocks -= NewSize - HandleEntry->Size;
344
345 return XMS_STATUS_SUCCESS;
346 }
347
348 /* Keep searching */
349 CurrentIndex = RunStart + RunSize;
350 }
351
352 /* Restore the old block range */
353 RtlSetBits(&AllocBitmap, BlockNumber, HandleEntry->Size);
354 return XMS_STATUS_OUT_OF_MEMORY;
355 }
356
357 return XMS_STATUS_SUCCESS;
358 }
359
XmsFree(WORD Handle)360 static UCHAR XmsFree(WORD Handle)
361 {
362 DWORD BlockNumber;
363 PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
364
365 if (!ValidateXmsHandle(HandleEntry))
366 return XMS_STATUS_INVALID_HANDLE;
367
368 if (HandleEntry->LockCount)
369 return XMS_STATUS_LOCKED;
370
371 BlockNumber = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE;
372 RtlClearBits(&AllocBitmap, BlockNumber, HandleEntry->Size);
373
374 HandleEntry->Handle = 0;
375 FreeBlocks += HandleEntry->Size;
376
377 return XMS_STATUS_SUCCESS;
378 }
379
XmsLock(WORD Handle,PDWORD Address)380 static UCHAR XmsLock(WORD Handle, PDWORD Address)
381 {
382 PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
383
384 if (!ValidateXmsHandle(HandleEntry))
385 return XMS_STATUS_INVALID_HANDLE;
386
387 if (HandleEntry->LockCount == 0xFF)
388 return XMS_STATUS_LOCK_OVERFLOW;
389
390 /* Increment the lock count */
391 HandleEntry->LockCount++;
392 *Address = HandleEntry->Address;
393
394 return XMS_STATUS_SUCCESS;
395 }
396
XmsUnlock(WORD Handle)397 static UCHAR XmsUnlock(WORD Handle)
398 {
399 PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
400
401 if (!ValidateXmsHandle(HandleEntry))
402 return XMS_STATUS_INVALID_HANDLE;
403
404 if (!HandleEntry->LockCount)
405 return XMS_STATUS_NOT_LOCKED;
406
407 /* Decrement the lock count */
408 HandleEntry->LockCount--;
409
410 return XMS_STATUS_SUCCESS;
411 }
412
XmsBopProcedure(LPWORD Stack)413 static VOID WINAPI XmsBopProcedure(LPWORD Stack)
414 {
415 switch (getAH())
416 {
417 /* Get XMS Version */
418 case 0x00:
419 {
420 setAX(0x0300); /* XMS version 3.00 */
421 setBX(0x0301); /* Driver version 3.01 */
422 setDX(0x0001); /* HMA present */
423 break;
424 }
425
426 /* Request HMA */
427 case 0x01:
428 {
429 /* Check whether HMA is already reserved */
430 if (IsHmaReserved)
431 {
432 /* It is, bail out */
433 setAX(0x0000);
434 setBL(XMS_STATUS_HMA_IN_USE);
435 break;
436 }
437
438 // NOTE: We implicitly suppose that we always have HMA.
439 // If not, we should fail there with the XMS_STATUS_HMA_DOES_NOT_EXIST
440 // error code.
441
442 /* Check whether the requested size is above the minimal allowed one */
443 if (getDX() < HmaMinSize)
444 {
445 /* It is not, bail out */
446 setAX(0x0000);
447 setBL(XMS_STATUS_HMA_MIN_SIZE);
448 break;
449 }
450
451 /* Reserve it */
452 IsHmaReserved = TRUE;
453 setAX(0x0001);
454 setBL(XMS_STATUS_SUCCESS);
455 break;
456 }
457
458 /* Release HMA */
459 case 0x02:
460 {
461 /* Check whether HMA was reserved */
462 if (!IsHmaReserved)
463 {
464 /* It was not, bail out */
465 setAX(0x0000);
466 setBL(XMS_STATUS_HMA_NOT_ALLOCATED);
467 break;
468 }
469
470 /* Release it */
471 IsHmaReserved = FALSE;
472 setAX(0x0001);
473 setBL(XMS_STATUS_SUCCESS);
474 break;
475 }
476
477 /* Global Enable A20 */
478 case 0x03:
479 {
480 /* Enable A20 if needed */
481 if (!IsA20Enabled)
482 {
483 XmsLocalEnableA20();
484 if (getAX() != 0x0001)
485 {
486 /* XmsLocalEnableA20 failed and already set AX and BL to their correct values */
487 break;
488 }
489
490 IsA20Enabled = TRUE;
491 }
492
493 setAX(0x0001); /* Line successfully enabled */
494 setBL(XMS_STATUS_SUCCESS);
495 break;
496 }
497
498 /* Global Disable A20 */
499 case 0x04:
500 {
501 UCHAR Result = XMS_STATUS_SUCCESS;
502
503 /* Disable A20 if needed */
504 if (IsA20Enabled)
505 {
506 XmsLocalDisableA20();
507 if (getAX() != 0x0001)
508 {
509 /* XmsLocalDisableA20 failed and already set AX and BL to their correct values */
510 break;
511 }
512
513 IsA20Enabled = FALSE;
514 Result = getBL();
515 }
516
517 setAX(0x0001); /* Line successfully disabled */
518 setBL(Result);
519 break;
520 }
521
522 /* Local Enable A20 */
523 case 0x05:
524 {
525 /* This call sets AX and BL to their correct values */
526 XmsLocalEnableA20();
527 break;
528 }
529
530 /* Local Disable A20 */
531 case 0x06:
532 {
533 /* This call sets AX and BL to their correct values */
534 XmsLocalDisableA20();
535 break;
536 }
537
538 /* Query A20 State */
539 case 0x07:
540 {
541 setAX(EmulatorGetA20());
542 setBL(XMS_STATUS_SUCCESS);
543 break;
544 }
545
546 /* Query Free Extended Memory */
547 case 0x08:
548 {
549 setAX(XmsGetLargestFreeBlock());
550 setDX(FreeBlocks);
551
552 if (FreeBlocks > 0)
553 setBL(XMS_STATUS_SUCCESS);
554 else
555 setBL(XMS_STATUS_OUT_OF_MEMORY);
556
557 break;
558 }
559
560 /* Allocate Extended Memory Block */
561 case 0x09:
562 {
563 WORD Handle;
564 UCHAR Result = XmsAlloc(getDX(), &Handle);
565
566 if (Result == XMS_STATUS_SUCCESS)
567 {
568 setAX(1);
569 setDX(Handle);
570 }
571 else
572 {
573 setAX(0);
574 setBL(Result);
575 }
576
577 break;
578 }
579
580 /* Free Extended Memory Block */
581 case 0x0A:
582 {
583 UCHAR Result = XmsFree(getDX());
584
585 setAX(Result == XMS_STATUS_SUCCESS);
586 setBL(Result);
587 break;
588 }
589
590 /* Move Extended Memory Block */
591 case 0x0B:
592 {
593 PVOID SourceAddress, DestAddress;
594 PXMS_COPY_DATA CopyData = (PXMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI());
595 PXMS_HANDLE HandleEntry;
596
597 if (CopyData->SourceHandle)
598 {
599 HandleEntry = GetXmsHandleRecord(CopyData->SourceHandle);
600 if (!ValidateXmsHandle(HandleEntry))
601 {
602 setAX(0);
603 setBL(XMS_STATUS_BAD_SRC_HANDLE);
604 break;
605 }
606
607 if (CopyData->SourceOffset >= HandleEntry->Size * XMS_BLOCK_SIZE)
608 {
609 setAX(0);
610 setBL(XMS_STATUS_BAD_SRC_OFFSET);
611 }
612
613 SourceAddress = (PVOID)REAL_TO_PHYS(HandleEntry->Address + CopyData->SourceOffset);
614 }
615 else
616 {
617 /* The offset is actually a 16-bit segment:offset pointer */
618 SourceAddress = FAR_POINTER(CopyData->SourceOffset);
619 }
620
621 if (CopyData->DestHandle)
622 {
623 HandleEntry = GetXmsHandleRecord(CopyData->DestHandle);
624 if (!ValidateXmsHandle(HandleEntry))
625 {
626 setAX(0);
627 setBL(XMS_STATUS_BAD_DEST_HANDLE);
628 break;
629 }
630
631 if (CopyData->DestOffset >= HandleEntry->Size * XMS_BLOCK_SIZE)
632 {
633 setAX(0);
634 setBL(XMS_STATUS_BAD_DEST_OFFSET);
635 }
636
637 DestAddress = (PVOID)REAL_TO_PHYS(HandleEntry->Address + CopyData->DestOffset);
638 }
639 else
640 {
641 /* The offset is actually a 16-bit segment:offset pointer */
642 DestAddress = FAR_POINTER(CopyData->DestOffset);
643 }
644
645 /* Perform the move */
646 RtlMoveMemory(DestAddress, SourceAddress, CopyData->Count);
647
648 setAX(1);
649 setBL(XMS_STATUS_SUCCESS);
650 break;
651 }
652
653 /* Lock Extended Memory Block */
654 case 0x0C:
655 {
656 DWORD Address;
657 UCHAR Result = XmsLock(getDX(), &Address);
658
659 if (Result == XMS_STATUS_SUCCESS)
660 {
661 setAX(1);
662
663 /* Store the LINEAR address in DX:BX */
664 setDX(HIWORD(Address));
665 setBX(LOWORD(Address));
666 }
667 else
668 {
669 setAX(0);
670 setBL(Result);
671 }
672
673 break;
674 }
675
676 /* Unlock Extended Memory Block */
677 case 0x0D:
678 {
679 UCHAR Result = XmsUnlock(getDX());
680
681 setAX(Result == XMS_STATUS_SUCCESS);
682 setBL(Result);
683 break;
684 }
685
686 /* Get Handle Information */
687 case 0x0E:
688 {
689 PXMS_HANDLE HandleEntry = GetXmsHandleRecord(getDX());
690 UINT i;
691 UCHAR Handles = 0;
692
693 if (!ValidateXmsHandle(HandleEntry))
694 {
695 setAX(0);
696 setBL(XMS_STATUS_INVALID_HANDLE);
697 break;
698 }
699
700 for (i = 0; i < XMS_MAX_HANDLES; i++)
701 {
702 if (HandleTable[i].Handle == 0) Handles++;
703 }
704
705 setAX(1);
706 setBH(HandleEntry->LockCount);
707 setBL(Handles);
708 setDX(HandleEntry->Size);
709 break;
710 }
711
712 /* Reallocate Extended Memory Block */
713 case 0x0F:
714 {
715 UCHAR Result = XmsRealloc(getDX(), getBX());
716
717 setAX(Result == XMS_STATUS_SUCCESS);
718 setBL(Result);
719 break;
720 }
721
722 /* Request UMB */
723 case 0x10:
724 {
725 BOOLEAN Result;
726 USHORT Segment = 0x0000; /* No preferred segment */
727 USHORT Size = getDX(); /* Size is in paragraphs */
728
729 Result = UmaDescReserve(&Segment, &Size);
730 if (Result)
731 setBX(Segment);
732 else
733 setBL(Size > 0 ? XMS_STATUS_SMALLER_UMB : XMS_STATUS_OUT_OF_UMBS);
734
735 setDX(Size);
736 setAX(Result);
737 break;
738 }
739
740 /* Release UMB */
741 case 0x11:
742 {
743 BOOLEAN Result;
744 USHORT Segment = getDX();
745
746 Result = UmaDescRelease(Segment);
747 if (!Result)
748 setBL(XMS_STATUS_INVALID_UMB);
749
750 setAX(Result);
751 break;
752 }
753
754 /* Reallocate UMB */
755 case 0x12:
756 {
757 BOOLEAN Result;
758 USHORT Segment = getDX();
759 USHORT Size = getBX(); /* Size is in paragraphs */
760
761 Result = UmaDescReallocate(Segment, &Size);
762 if (!Result)
763 {
764 if (Size > 0)
765 {
766 setBL(XMS_STATUS_SMALLER_UMB);
767 setDX(Size);
768 }
769 else
770 {
771 setBL(XMS_STATUS_INVALID_UMB);
772 }
773 }
774
775 setAX(Result);
776 break;
777 }
778
779 default:
780 {
781 DPRINT1("XMS command AH = 0x%02X NOT IMPLEMENTED\n", getAH());
782 setBL(XMS_STATUS_NOT_IMPLEMENTED);
783 }
784 }
785 }
786
787 /* PUBLIC FUNCTIONS ***********************************************************/
788
XmsGetDriverEntry(PDWORD Pointer)789 BOOLEAN XmsGetDriverEntry(PDWORD Pointer)
790 {
791 if (Node == NULL) return FALSE;
792 *Pointer = DEVICE_PRIVATE_AREA(Node->Driver);
793 return TRUE;
794 }
795
XmsInitialize(VOID)796 VOID XmsInitialize(VOID)
797 {
798 RtlZeroMemory(HandleTable, sizeof(HandleTable));
799 RtlZeroMemory(BitmapBuffer, sizeof(BitmapBuffer));
800 RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, XMS_BLOCKS);
801
802 Node = DosCreateDeviceEx(DOS_DEVATTR_IOCTL | DOS_DEVATTR_CHARACTER,
803 XMS_DEVICE_NAME,
804 sizeof(EntryProcedure));
805
806 RegisterBop(BOP_XMS, XmsBopProcedure);
807
808 /* Copy the entry routine to the device private area */
809 RtlMoveMemory(FAR_POINTER(DEVICE_PRIVATE_AREA(Node->Driver)),
810 EntryProcedure,
811 sizeof(EntryProcedure));
812 }
813
XmsCleanup(VOID)814 VOID XmsCleanup(VOID)
815 {
816 RegisterBop(BOP_XMS, NULL);
817 DosDeleteDevice(Node);
818 }
819