1 /* 2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory 3 * PROJECT: ReactOS Virtual DOS Machine 4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c 5 * PURPOSE: DOS EMS Driver 6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> 7 * 8 * DOCUMENTATION: Official specification: 9 * LIM EMS v4.0: http://www.phatcode.net/res/218/files/limems40.txt 10 */ 11 12 /* INCLUDES *******************************************************************/ 13 14 #include "ntvdm.h" 15 16 #define NDEBUG 17 #include <debug.h> 18 19 #include "emulator.h" 20 #include "../../memory.h" 21 #include "bios/umamgr.h" 22 23 #include "dos.h" 24 #include "dos/dem.h" 25 #include "device.h" 26 27 #include "emsdrv.h" 28 29 #define EMS_DEVICE_NAME "EMMXXXX0" 30 31 #define EMS_SEGMENT_SIZE ((EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE) >> 4) 32 #define EMS_SYSTEM_HANDLE 0 33 34 /* PRIVATE VARIABLES **********************************************************/ 35 36 static PDOS_DEVICE_NODE Node; 37 static RTL_BITMAP AllocBitmap; 38 static PULONG EmsBitmapBuffer = NULL; 39 static PEMS_PAGE EmsPageTable = NULL; 40 static EMS_HANDLE EmsHandleTable[EMS_MAX_HANDLES]; 41 static PVOID Mapping[EMS_PHYSICAL_PAGES] = { NULL }; 42 static PVOID MappingBackup[EMS_PHYSICAL_PAGES] = { NULL }; 43 static ULONG EmsTotalPages = 0; 44 static PVOID EmsMemory = NULL; 45 static USHORT EmsSegment = EMS_SEGMENT; 46 47 /* PRIVATE FUNCTIONS **********************************************************/ 48 49 static VOID InitHandlesTable(VOID) 50 { 51 USHORT i; 52 53 for (i = 0; i < ARRAYSIZE(EmsHandleTable); i++) 54 { 55 EmsHandleTable[i].Allocated = FALSE; 56 EmsHandleTable[i].PageCount = 0; 57 RtlZeroMemory(EmsHandleTable[i].Name, sizeof(EmsHandleTable[i].Name)); 58 InitializeListHead(&EmsHandleTable[i].PageList); 59 } 60 } 61 62 static PEMS_HANDLE CreateHandle(PUSHORT Handle) 63 { 64 PEMS_HANDLE HandleEntry; 65 USHORT i; 66 67 /* Handle 0 is reserved (system handle) */ 68 for (i = 1; i < ARRAYSIZE(EmsHandleTable); i++) 69 { 70 HandleEntry = &EmsHandleTable[i]; 71 if (!HandleEntry->Allocated) 72 { 73 *Handle = i; 74 HandleEntry->Allocated = TRUE; 75 return HandleEntry; 76 } 77 } 78 79 return NULL; 80 } 81 82 static VOID FreeHandle(PEMS_HANDLE HandleEntry) 83 { 84 HandleEntry->Allocated = FALSE; 85 HandleEntry->PageCount = 0; 86 RtlZeroMemory(HandleEntry->Name, sizeof(HandleEntry->Name)); 87 // InitializeListHead(&HandleEntry->PageList); 88 } 89 90 static inline PEMS_HANDLE GetEmsHandleRecord(USHORT Handle) 91 { 92 if (Handle >= ARRAYSIZE(EmsHandleTable)) return NULL; 93 return &EmsHandleTable[Handle]; 94 } 95 96 static inline BOOLEAN ValidateHandle(PEMS_HANDLE HandleEntry) 97 { 98 return (HandleEntry != NULL && HandleEntry->Allocated); 99 } 100 101 static UCHAR EmsFree(USHORT Handle) 102 { 103 PLIST_ENTRY Entry; 104 PEMS_HANDLE HandleEntry = GetEmsHandleRecord(Handle); 105 106 if (!ValidateHandle(HandleEntry)) 107 return EMS_STATUS_INVALID_HANDLE; 108 109 for (Entry = HandleEntry->PageList.Flink; 110 Entry != &HandleEntry->PageList; 111 Entry = Entry->Flink) 112 { 113 PEMS_PAGE PageEntry = (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry); 114 ULONG PageNumber = ARRAY_INDEX(PageEntry, EmsPageTable); 115 116 /* Free the page */ 117 RtlClearBits(&AllocBitmap, PageNumber, 1); 118 } 119 120 InitializeListHead(&HandleEntry->PageList); 121 122 if (Handle != EMS_SYSTEM_HANDLE) 123 FreeHandle(HandleEntry); 124 125 return EMS_STATUS_SUCCESS; 126 } 127 128 static UCHAR EmsAlloc(USHORT NumPages, PUSHORT Handle) 129 { 130 ULONG i, CurrentIndex = 0; 131 PEMS_HANDLE HandleEntry; 132 133 if (NumPages == 0) return EMS_STATUS_ZERO_PAGES; 134 135 HandleEntry = CreateHandle(Handle); 136 if (!HandleEntry) return EMS_STATUS_NO_MORE_HANDLES; 137 138 while (HandleEntry->PageCount < NumPages) 139 { 140 ULONG RunStart; 141 ULONG RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart); 142 143 if (RunSize == 0) 144 { 145 /* Free what's been allocated already and report failure */ 146 EmsFree(*Handle); 147 return EMS_STATUS_INSUFFICIENT_PAGES; 148 } 149 else if ((HandleEntry->PageCount + RunSize) > NumPages) 150 { 151 /* We don't need the entire run */ 152 RunSize = NumPages - HandleEntry->PageCount; 153 } 154 155 CurrentIndex = RunStart + RunSize; 156 HandleEntry->PageCount += RunSize; 157 RtlSetBits(&AllocBitmap, RunStart, RunSize); 158 159 for (i = 0; i < RunSize; i++) 160 { 161 EmsPageTable[RunStart + i].Handle = *Handle; 162 InsertTailList(&HandleEntry->PageList, &EmsPageTable[RunStart + i].Entry); 163 } 164 } 165 166 return EMS_STATUS_SUCCESS; 167 } 168 169 static UCHAR InitSystemHandle(USHORT NumPages) 170 { 171 // 172 // FIXME: This is an adapted copy of EmsAlloc!! 173 // 174 175 ULONG i, CurrentIndex = 0; 176 PEMS_HANDLE HandleEntry = &EmsHandleTable[EMS_SYSTEM_HANDLE]; 177 178 /* The system handle must never have been initialized before */ 179 ASSERT(!HandleEntry->Allocated); 180 181 /* Now allocate it */ 182 HandleEntry->Allocated = TRUE; 183 184 while (HandleEntry->PageCount < NumPages) 185 { 186 ULONG RunStart; 187 ULONG RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart); 188 189 if (RunSize == 0) 190 { 191 /* Free what's been allocated already and report failure */ 192 EmsFree(EMS_SYSTEM_HANDLE); 193 // FIXME: For this function (and EmsAlloc as well), 194 // use instead an internal function that just uses 195 // PEMS_HANDLE pointers instead. It's only in the 196 // EMS interrupt handler that we should do the 197 // unfolding. 198 return EMS_STATUS_INSUFFICIENT_PAGES; 199 } 200 else if ((HandleEntry->PageCount + RunSize) > NumPages) 201 { 202 /* We don't need the entire run */ 203 RunSize = NumPages - HandleEntry->PageCount; 204 } 205 206 CurrentIndex = RunStart + RunSize; 207 HandleEntry->PageCount += RunSize; 208 RtlSetBits(&AllocBitmap, RunStart, RunSize); 209 210 for (i = 0; i < RunSize; i++) 211 { 212 EmsPageTable[RunStart + i].Handle = EMS_SYSTEM_HANDLE; 213 InsertTailList(&HandleEntry->PageList, &EmsPageTable[RunStart + i].Entry); 214 } 215 } 216 217 return EMS_STATUS_SUCCESS; 218 } 219 220 static PEMS_PAGE GetLogicalPage(PEMS_HANDLE HandleEntry, USHORT LogicalPage) 221 { 222 PLIST_ENTRY Entry = HandleEntry->PageList.Flink; 223 224 while (LogicalPage) 225 { 226 if (Entry == &HandleEntry->PageList) return NULL; 227 LogicalPage--; 228 Entry = Entry->Flink; 229 } 230 231 return (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry); 232 } 233 234 static UCHAR EmsMap(USHORT Handle, UCHAR PhysicalPage, USHORT LogicalPage) 235 { 236 PEMS_PAGE PageEntry; 237 PEMS_HANDLE HandleEntry = GetEmsHandleRecord(Handle); 238 239 if (!ValidateHandle(HandleEntry)) 240 return EMS_STATUS_INVALID_HANDLE; 241 242 if (PhysicalPage >= EMS_PHYSICAL_PAGES) 243 return EMS_STATUS_INV_PHYSICAL_PAGE; 244 245 if (LogicalPage == 0xFFFF) 246 { 247 /* Unmap */ 248 Mapping[PhysicalPage] = NULL; 249 return EMS_STATUS_SUCCESS; 250 } 251 252 PageEntry = GetLogicalPage(HandleEntry, LogicalPage); 253 if (!PageEntry) return EMS_STATUS_INV_LOGICAL_PAGE; 254 255 Mapping[PhysicalPage] = (PVOID)((ULONG_PTR)EmsMemory 256 + ARRAY_INDEX(PageEntry, EmsPageTable) * EMS_PAGE_SIZE); 257 return EMS_STATUS_SUCCESS; 258 } 259 260 static VOID WINAPI EmsIntHandler(LPWORD Stack) 261 { 262 switch (getAH()) 263 { 264 /* Get Manager Status */ 265 case 0x40: 266 { 267 setAH(EMS_STATUS_SUCCESS); 268 break; 269 } 270 271 /* Get Page Frame Segment */ 272 case 0x41: 273 { 274 setAH(EMS_STATUS_SUCCESS); 275 setBX(EmsSegment); 276 break; 277 } 278 279 /* Get Number of Unallocated Pages */ 280 case 0x42: 281 { 282 setAH(EMS_STATUS_SUCCESS); 283 setBX(RtlNumberOfClearBits(&AllocBitmap)); 284 setDX(EmsTotalPages); 285 break; 286 } 287 288 /* Get Handle and Allocate Memory */ 289 case 0x43: 290 { 291 USHORT Handle; 292 UCHAR Status = EmsAlloc(getBX(), &Handle); 293 294 if (Status == EMS_STATUS_SUCCESS) 295 setDX(Handle); 296 297 setAH(Status); 298 break; 299 } 300 301 /* Map Memory */ 302 case 0x44: 303 { 304 setAH(EmsMap(getDX(), getAL(), getBX())); 305 break; 306 } 307 308 /* Release Handle and Memory */ 309 case 0x45: 310 { 311 setAH(EmsFree(getDX())); 312 break; 313 } 314 315 /* Get EMM Version */ 316 case 0x46: 317 { 318 setAH(EMS_STATUS_SUCCESS); 319 setAL(EMS_VERSION_NUM); 320 break; 321 } 322 323 /* Save Page Map */ 324 case 0x47: 325 { 326 // FIXME: This depends on an EMS handle given in DX 327 RtlCopyMemory(MappingBackup, Mapping, sizeof(Mapping)); 328 setAH(EMS_STATUS_SUCCESS); 329 break; 330 } 331 332 /* Restore Page Map */ 333 case 0x48: 334 { 335 // FIXME: This depends on an EMS handle given in DX 336 RtlCopyMemory(Mapping, MappingBackup, sizeof(Mapping)); 337 setAH(EMS_STATUS_SUCCESS); 338 break; 339 } 340 341 /* Get Number of Opened Handles */ 342 case 0x4B: 343 { 344 USHORT NumOpenHandles = 0; 345 USHORT i; 346 347 for (i = 0; i < ARRAYSIZE(EmsHandleTable); i++) 348 { 349 if (EmsHandleTable[i].Allocated) 350 ++NumOpenHandles; 351 } 352 353 setAH(EMS_STATUS_SUCCESS); 354 setBX(NumOpenHandles); 355 break; 356 } 357 358 /* Get Handle Number of Pages */ 359 case 0x4C: 360 { 361 PEMS_HANDLE HandleEntry = GetEmsHandleRecord(getDX()); 362 363 if (!ValidateHandle(HandleEntry)) 364 { 365 setAH(EMS_STATUS_INVALID_HANDLE); 366 break; 367 } 368 369 setAH(EMS_STATUS_SUCCESS); 370 setBX(HandleEntry->PageCount); 371 break; 372 } 373 374 /* Get All Handles Number of Pages */ 375 case 0x4D: 376 { 377 PEMS_HANDLE_PAGE_INFO HandlePageInfo = (PEMS_HANDLE_PAGE_INFO)SEG_OFF_TO_PTR(getES(), getDI()); 378 USHORT NumOpenHandles = 0; 379 USHORT i; 380 381 for (i = 0; i < ARRAYSIZE(EmsHandleTable); i++) 382 { 383 if (EmsHandleTable[i].Allocated) 384 { 385 HandlePageInfo->Handle = i; 386 HandlePageInfo->PageCount = EmsHandleTable[i].PageCount; 387 ++HandlePageInfo; 388 ++NumOpenHandles; 389 } 390 } 391 392 setAH(EMS_STATUS_SUCCESS); 393 setBX(NumOpenHandles); 394 break; 395 } 396 397 /* Get or Set Page Map */ 398 case 0x4E: 399 { 400 switch (getAL()) 401 { 402 /* Get Mapping Registers */ 403 // case 0x00: // TODO: NOT IMPLEMENTED 404 405 /* Set Mapping Registers */ 406 // case 0x01: // TODO: NOT IMPLEMENTED 407 408 /* Get and Set Mapping Registers At Once */ 409 // case 0x02: // TODO: NOT IMPLEMENTED 410 411 /* Get Size of Page-Mapping Array */ 412 case 0x03: 413 { 414 setAH(EMS_STATUS_SUCCESS); 415 setAL(sizeof(Mapping)); 416 break; 417 } 418 419 default: 420 { 421 DPRINT1("EMS function AH = 0x4E, subfunction AL = %02X NOT IMPLEMENTED\n", getAL()); 422 setAH(EMS_STATUS_UNKNOWN_FUNCTION); 423 break; 424 } 425 } 426 427 break; 428 } 429 430 /* Get/Set Handle Name */ 431 case 0x53: 432 { 433 PEMS_HANDLE HandleEntry = GetEmsHandleRecord(getDX()); 434 435 if (!ValidateHandle(HandleEntry)) 436 { 437 setAH(EMS_STATUS_INVALID_HANDLE); 438 break; 439 } 440 441 if (getAL() == 0x00) 442 { 443 /* Retrieve the name */ 444 RtlCopyMemory(SEG_OFF_TO_PTR(getES(), getDI()), 445 HandleEntry->Name, 446 sizeof(HandleEntry->Name)); 447 setAH(EMS_STATUS_SUCCESS); 448 } 449 else if (getAL() == 0x01) 450 { 451 /* Store the name */ 452 RtlCopyMemory(HandleEntry->Name, 453 SEG_OFF_TO_PTR(getDS(), getSI()), 454 sizeof(HandleEntry->Name)); 455 setAH(EMS_STATUS_SUCCESS); 456 } 457 else 458 { 459 DPRINT1("Invalid subfunction %02X for EMS function AH = 53h\n", getAL()); 460 setAH(EMS_STATUS_INVALID_SUBFUNCTION); 461 } 462 463 break; 464 } 465 466 /* Handle Directory functions */ 467 case 0x54: 468 { 469 if (getAL() == 0x00) 470 { 471 /* Get Handle Directory */ 472 473 PEMS_HANDLE_DIR_ENTRY HandleDir = (PEMS_HANDLE_DIR_ENTRY)SEG_OFF_TO_PTR(getES(), getDI()); 474 USHORT NumOpenHandles = 0; 475 USHORT i; 476 477 for (i = 0; i < ARRAYSIZE(EmsHandleTable); i++) 478 { 479 if (EmsHandleTable[i].Allocated) 480 { 481 HandleDir->Handle = i; 482 RtlCopyMemory(HandleDir->Name, 483 EmsHandleTable[i].Name, 484 sizeof(HandleDir->Name)); 485 ++HandleDir; 486 ++NumOpenHandles; 487 } 488 } 489 490 setAH(EMS_STATUS_SUCCESS); 491 setAL((UCHAR)NumOpenHandles); 492 } 493 else if (getAL() == 0x01) 494 { 495 /* Search for Named Handle */ 496 497 PUCHAR HandleName = (PUCHAR)SEG_OFF_TO_PTR(getDS(), getSI()); 498 PEMS_HANDLE HandleFound = NULL; 499 USHORT i; 500 501 for (i = 0; i < ARRAYSIZE(EmsHandleTable); i++) 502 { 503 if (EmsHandleTable[i].Allocated && 504 RtlCompareMemory(HandleName, 505 EmsHandleTable[i].Name, 506 sizeof(EmsHandleTable[i].Name)) == sizeof(EmsHandleTable[i].Name)) 507 { 508 HandleFound = &EmsHandleTable[i]; 509 break; 510 } 511 } 512 513 /* Bail out if no handle was found */ 514 if (i >= ARRAYSIZE(EmsHandleTable)) // HandleFound == NULL 515 { 516 setAH(EMS_STATUS_HANDLE_NOT_FOUND); 517 break; 518 } 519 520 /* Return the handle number */ 521 setDX(i); 522 523 /* Sanity check: Check whether the handle was unnamed */ 524 i = 0; 525 while ((i < sizeof(HandleFound->Name)) && (HandleFound->Name[i] == '\0')) 526 ++i; 527 528 if (i >= sizeof(HandleFound->Name)) 529 { 530 setAH(EMS_STATUS_UNNAMED_HANDLE); 531 } 532 else 533 { 534 setAH(EMS_STATUS_SUCCESS); 535 } 536 } 537 else if (getAL() == 0x02) 538 { 539 /* 540 * Get Total Number of Handles 541 * 542 * This function retrieves the maximum number of handles 543 * (allocated or not) the memory manager supports, which 544 * a program may request. 545 */ 546 setAH(EMS_STATUS_SUCCESS); 547 setBX(ARRAYSIZE(EmsHandleTable)); 548 } 549 else 550 { 551 DPRINT1("Invalid subfunction %02X for EMS function AH = 54h\n", getAL()); 552 setAH(EMS_STATUS_INVALID_SUBFUNCTION); 553 } 554 555 break; 556 } 557 558 /* Move/Exchange Memory */ 559 case 0x57: 560 { 561 PUCHAR SourcePtr, DestPtr; 562 PEMS_HANDLE HandleEntry; 563 PEMS_PAGE PageEntry; 564 BOOLEAN Exchange = getAL(); 565 PEMS_COPY_DATA Data = (PEMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI()); 566 567 if (Data->SourceType) 568 { 569 /* Expanded memory */ 570 HandleEntry = GetEmsHandleRecord(Data->SourceHandle); 571 if (!ValidateHandle(HandleEntry)) 572 { 573 setAH(EMS_STATUS_INVALID_HANDLE); 574 break; 575 } 576 577 PageEntry = GetLogicalPage(HandleEntry, Data->SourceSegment); 578 if (!PageEntry) 579 { 580 setAH(EMS_STATUS_INV_LOGICAL_PAGE); 581 break; 582 } 583 584 SourcePtr = (PUCHAR)((ULONG_PTR)EmsMemory 585 + ARRAY_INDEX(PageEntry, EmsPageTable) * EMS_PAGE_SIZE 586 + Data->SourceOffset); 587 } 588 else 589 { 590 /* Conventional memory */ 591 SourcePtr = (PUCHAR)SEG_OFF_TO_PTR(Data->SourceSegment, Data->SourceOffset); 592 } 593 594 if (Data->DestType) 595 { 596 /* Expanded memory */ 597 HandleEntry = GetEmsHandleRecord(Data->DestHandle); 598 if (!ValidateHandle(HandleEntry)) 599 { 600 setAH(EMS_STATUS_INVALID_HANDLE); 601 break; 602 } 603 604 PageEntry = GetLogicalPage(HandleEntry, Data->DestSegment); 605 if (!PageEntry) 606 { 607 setAH(EMS_STATUS_INV_LOGICAL_PAGE); 608 break; 609 } 610 611 DestPtr = (PUCHAR)((ULONG_PTR)EmsMemory 612 + ARRAY_INDEX(PageEntry, EmsPageTable) * EMS_PAGE_SIZE 613 + Data->DestOffset); 614 } 615 else 616 { 617 /* Conventional memory */ 618 DestPtr = (PUCHAR)SEG_OFF_TO_PTR(Data->DestSegment, Data->DestOffset); 619 } 620 621 if (Exchange) 622 { 623 ULONG i; 624 625 /* Exchange */ 626 for (i = 0; i < Data->RegionLength; i++) 627 { 628 UCHAR Temp = DestPtr[i]; 629 DestPtr[i] = SourcePtr[i]; 630 SourcePtr[i] = Temp; 631 } 632 } 633 else 634 { 635 /* Move */ 636 RtlMoveMemory(DestPtr, SourcePtr, Data->RegionLength); 637 } 638 639 setAH(EMS_STATUS_SUCCESS); 640 break; 641 } 642 643 /* Get Mappable Physical Address Array */ 644 case 0x58: 645 { 646 if (getAL() == 0x00) 647 { 648 PEMS_MAPPABLE_PHYS_PAGE PageArray = (PEMS_MAPPABLE_PHYS_PAGE)SEG_OFF_TO_PTR(getES(), getDI()); 649 ULONG i; 650 651 for (i = 0; i < EMS_PHYSICAL_PAGES; i++) 652 { 653 PageArray->PageSegment = EMS_SEGMENT + i * (EMS_PAGE_SIZE >> 4); 654 PageArray->PageNumber = i; 655 ++PageArray; 656 } 657 658 setAH(EMS_STATUS_SUCCESS); 659 setCX(EMS_PHYSICAL_PAGES); 660 } 661 else if (getAL() == 0x01) 662 { 663 setAH(EMS_STATUS_SUCCESS); 664 setCX(EMS_PHYSICAL_PAGES); 665 } 666 else 667 { 668 DPRINT1("Invalid subfunction %02X for EMS function AH = 58h\n", getAL()); 669 setAH(EMS_STATUS_INVALID_SUBFUNCTION); 670 } 671 672 break; 673 } 674 675 /* Get Expanded Memory Hardware Information */ 676 case 0x59: 677 { 678 if (getAL() == 0x00) 679 { 680 PEMS_HARDWARE_INFO HardwareInfo = (PEMS_HARDWARE_INFO)SEG_OFF_TO_PTR(getES(), getDI()); 681 682 /* Return the hardware information */ 683 HardwareInfo->RawPageSize = EMS_PAGE_SIZE >> 4; 684 HardwareInfo->AlternateRegSets = 0; 685 HardwareInfo->ContextAreaSize = sizeof(Mapping); 686 HardwareInfo->DmaRegisterSets = 0; 687 HardwareInfo->DmaChannelOperation = 0; 688 689 setAH(EMS_STATUS_SUCCESS); 690 } 691 else if (getAL() == 0x01) 692 { 693 /* Same as function AH = 42h */ 694 setAH(EMS_STATUS_SUCCESS); 695 setBX(RtlNumberOfClearBits(&AllocBitmap)); 696 setDX(EmsTotalPages); 697 } 698 else 699 { 700 DPRINT1("Invalid subfunction %02X for EMS function AH = 59h\n", getAL()); 701 setAH(EMS_STATUS_INVALID_SUBFUNCTION); 702 } 703 704 break; 705 } 706 707 default: 708 { 709 DPRINT1("EMS function AH = %02X NOT IMPLEMENTED\n", getAH()); 710 setAH(EMS_STATUS_UNKNOWN_FUNCTION); 711 break; 712 } 713 } 714 } 715 716 static VOID FASTCALL EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size) 717 { 718 ULONG i; 719 ULONG RelativeAddress = Address - TO_LINEAR(EmsSegment, 0); 720 ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE; 721 ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE; 722 ULONG Offset, Length; 723 724 for (i = FirstPage; i <= LastPage; i++) 725 { 726 Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0; 727 Length = ((i == LastPage) 728 ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS)) 729 : EMS_PAGE_SIZE) - Offset; 730 731 if (Mapping[i]) RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Mapping[i] + Offset), Length); 732 Buffer = (PVOID)((ULONG_PTR)Buffer + Length); 733 } 734 } 735 736 static BOOLEAN FASTCALL EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size) 737 { 738 ULONG i; 739 ULONG RelativeAddress = Address - TO_LINEAR(EmsSegment, 0); 740 ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE; 741 ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE; 742 ULONG Offset, Length; 743 744 for (i = FirstPage; i <= LastPage; i++) 745 { 746 Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0; 747 Length = ((i == LastPage) 748 ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS)) 749 : EMS_PAGE_SIZE) - Offset; 750 751 if (Mapping[i]) RtlCopyMemory((PVOID)((ULONG_PTR)Mapping[i] + Offset), Buffer, Length); 752 Buffer = (PVOID)((ULONG_PTR)Buffer + Length); 753 } 754 755 return TRUE; 756 } 757 758 static WORD NTAPI EmsDrvDispatchIoctlRead(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length) 759 { 760 // TODO: NOT IMPLEMENTED 761 UNIMPLEMENTED; 762 return DOS_DEVSTAT_DONE; 763 } 764 765 /* PUBLIC FUNCTIONS ***********************************************************/ 766 767 BOOLEAN EmsDrvInitialize(USHORT Segment, ULONG TotalPages) 768 { 769 USHORT Size; 770 771 /* Try to allocate our page table in UMA at the given segment */ 772 EmsSegment = (Segment != 0 ? Segment : EMS_SEGMENT); 773 Size = EMS_SEGMENT_SIZE; // Size in paragraphs 774 if (!UmaDescReserve(&EmsSegment, &Size)) return FALSE; 775 776 EmsTotalPages = TotalPages; 777 EmsBitmapBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 778 HEAP_ZERO_MEMORY, 779 ((TotalPages + 31) / 32) * sizeof(ULONG)); 780 if (EmsBitmapBuffer == NULL) 781 { 782 UmaDescRelease(EmsSegment); 783 return FALSE; 784 } 785 786 RtlInitializeBitMap(&AllocBitmap, EmsBitmapBuffer, TotalPages); 787 788 EmsPageTable = (PEMS_PAGE)RtlAllocateHeap(RtlGetProcessHeap(), 789 HEAP_ZERO_MEMORY, 790 TotalPages * sizeof(EMS_PAGE)); 791 if (EmsPageTable == NULL) 792 { 793 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsBitmapBuffer); 794 EmsBitmapBuffer = NULL; 795 796 UmaDescRelease(EmsSegment); 797 return FALSE; 798 } 799 800 EmsMemory = (PVOID)RtlAllocateHeap(RtlGetProcessHeap(), 0, TotalPages * EMS_PAGE_SIZE); 801 if (EmsMemory == NULL) 802 { 803 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsPageTable); 804 EmsPageTable = NULL; 805 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsBitmapBuffer); 806 EmsBitmapBuffer = NULL; 807 808 UmaDescRelease(EmsSegment); 809 return FALSE; 810 } 811 812 InitHandlesTable(); 813 /* 814 * FIXME: We should ensure that the system handle is associated 815 * with mapped pages from conventional memory. DosEmu seems to do 816 * it correctly. 384kB of memory mapped. 817 */ 818 if (InitSystemHandle(384/16) != EMS_STATUS_SUCCESS) 819 { 820 DPRINT1("Impossible to allocate pages for the system handle!\n"); 821 822 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsMemory); 823 EmsMemory = NULL; 824 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsPageTable); 825 EmsPageTable = NULL; 826 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsBitmapBuffer); 827 EmsBitmapBuffer = NULL; 828 829 UmaDescRelease(EmsSegment); 830 return FALSE; 831 } 832 833 MemInstallFastMemoryHook(UlongToPtr(TO_LINEAR(EmsSegment, 0)), 834 EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE, 835 EmsReadMemory, 836 EmsWriteMemory); 837 838 /* Create the device */ 839 Node = DosCreateDeviceEx(DOS_DEVATTR_IOCTL | DOS_DEVATTR_CHARACTER, 840 EMS_DEVICE_NAME, 841 Int16To32StubSize); 842 Node->IoctlReadRoutine = EmsDrvDispatchIoctlRead; 843 844 RegisterInt32(DEVICE_PRIVATE_AREA(Node->Driver), 845 EMS_INTERRUPT_NUM, EmsIntHandler, NULL); 846 847 return TRUE; 848 } 849 850 VOID EmsDrvCleanup(VOID) 851 { 852 /* Delete the device */ 853 DosDeleteDevice(Node); 854 855 MemRemoveFastMemoryHook(UlongToPtr(TO_LINEAR(EmsSegment, 0)), 856 EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE); 857 858 if (EmsMemory) 859 { 860 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsMemory); 861 EmsMemory = NULL; 862 } 863 864 if (EmsPageTable) 865 { 866 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsPageTable); 867 EmsPageTable = NULL; 868 } 869 870 if (EmsBitmapBuffer) 871 { 872 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsBitmapBuffer); 873 EmsBitmapBuffer = NULL; 874 } 875 876 UmaDescRelease(EmsSegment); 877 } 878