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