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 
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 
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 
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 
181 static inline BOOLEAN ValidateXmsHandle(PXMS_HANDLE HandleEntry)
182 {
183     return (HandleEntry != NULL && HandleEntry->Handle != 0);
184 }
185 
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 
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 
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 
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 
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 
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 
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 implicitely 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 
789 BOOLEAN XmsGetDriverEntry(PDWORD Pointer)
790 {
791     if (Node == NULL) return FALSE;
792     *Pointer = DEVICE_PRIVATE_AREA(Node->Driver);
793     return TRUE;
794 }
795 
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 
814 VOID XmsCleanup(VOID)
815 {
816     RegisterBop(BOP_XMS, NULL);
817     DosDeleteDevice(Node);
818 }
819