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