38da36c23e9ae2656eb557fa3804e186f8fae04f
[reactos.git] / subsystems / mvdm / ntvdm / dos / dos32krnl / emsdrv.c
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 BitmapBuffer = NULL;
39 static PEMS_PAGE PageTable = NULL;
40 static EMS_HANDLE HandleTable[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(HandleTable); i++)
54 {
55 HandleTable[i].Allocated = FALSE;
56 HandleTable[i].PageCount = 0;
57 RtlZeroMemory(HandleTable[i].Name, sizeof(HandleTable[i].Name));
58 InitializeListHead(&HandleTable[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(HandleTable); i++)
69 {
70 HandleEntry = &HandleTable[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 GetHandleRecord(USHORT Handle)
91 {
92 if (Handle >= ARRAYSIZE(HandleTable)) return NULL;
93 return &HandleTable[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 = GetHandleRecord(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, PageTable);
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 PageTable[RunStart + i].Handle = *Handle;
162 InsertTailList(&HandleEntry->PageList, &PageTable[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 = &HandleTable[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 PageTable[RunStart + i].Handle = EMS_SYSTEM_HANDLE;
213 InsertTailList(&HandleEntry->PageList, &PageTable[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 = GetHandleRecord(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, PageTable) * 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(HandleTable); i++)
348 {
349 if (HandleTable[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 = GetHandleRecord(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(HandleTable); i++)
382 {
383 if (HandleTable[i].Allocated)
384 {
385 HandlePageInfo->Handle = i;
386 HandlePageInfo->PageCount = HandleTable[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 = GetHandleRecord(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(HandleTable); i++)
478 {
479 if (HandleTable[i].Allocated)
480 {
481 HandleDir->Handle = i;
482 RtlCopyMemory(HandleDir->Name,
483 HandleTable[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(HandleTable); i++)
502 {
503 if (HandleTable[i].Allocated &&
504 RtlCompareMemory(HandleName,
505 HandleTable[i].Name,
506 sizeof(HandleTable[i].Name)) == sizeof(HandleTable[i].Name))
507 {
508 HandleFound = &HandleTable[i];
509 break;
510 }
511 }
512
513 /* Bail out if no handle was found */
514 if (i >= ARRAYSIZE(HandleTable)) // 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(HandleTable));
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 = GetHandleRecord(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, PageTable) * 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 = GetHandleRecord(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, PageTable) * 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 BitmapBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
778 HEAP_ZERO_MEMORY,
779 ((TotalPages + 31) / 32) * sizeof(ULONG));
780 if (BitmapBuffer == NULL)
781 {
782 UmaDescRelease(EmsSegment);
783 return FALSE;
784 }
785
786 RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, TotalPages);
787
788 PageTable = (PEMS_PAGE)RtlAllocateHeap(RtlGetProcessHeap(),
789 HEAP_ZERO_MEMORY,
790 TotalPages * sizeof(EMS_PAGE));
791 if (PageTable == NULL)
792 {
793 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
794 BitmapBuffer = 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, PageTable);
804 PageTable = NULL;
805 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
806 BitmapBuffer = 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, PageTable);
825 PageTable = NULL;
826 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
827 BitmapBuffer = NULL;
828
829 UmaDescRelease(EmsSegment);
830 return FALSE;
831 }
832
833 MemInstallFastMemoryHook((PVOID)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((PVOID)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 (PageTable)
865 {
866 RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
867 PageTable = NULL;
868 }
869
870 if (BitmapBuffer)
871 {
872 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
873 BitmapBuffer = NULL;
874 }
875
876 UmaDescRelease(EmsSegment);
877 }