Create the AHCI branch for Aman's work
[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/Set Handle Name */
398 case 0x53:
399 {
400 PEMS_HANDLE HandleEntry = GetHandleRecord(getDX());
401
402 if (!ValidateHandle(HandleEntry))
403 {
404 setAH(EMS_STATUS_INVALID_HANDLE);
405 break;
406 }
407
408 if (getAL() == 0x00)
409 {
410 /* Retrieve the name */
411 RtlCopyMemory(SEG_OFF_TO_PTR(getES(), getDI()),
412 HandleEntry->Name,
413 sizeof(HandleEntry->Name));
414 setAH(EMS_STATUS_SUCCESS);
415 }
416 else if (getAL() == 0x01)
417 {
418 /* Store the name */
419 RtlCopyMemory(HandleEntry->Name,
420 SEG_OFF_TO_PTR(getDS(), getSI()),
421 sizeof(HandleEntry->Name));
422 setAH(EMS_STATUS_SUCCESS);
423 }
424 else
425 {
426 DPRINT1("Invalid subfunction %02X for EMS function AH = 53h\n", getAL());
427 setAH(EMS_STATUS_INVALID_SUBFUNCTION);
428 }
429
430 break;
431 }
432
433 /* Handle Directory functions */
434 case 0x54:
435 {
436 if (getAL() == 0x00)
437 {
438 /* Get Handle Directory */
439
440 PEMS_HANDLE_DIR_ENTRY HandleDir = (PEMS_HANDLE_DIR_ENTRY)SEG_OFF_TO_PTR(getES(), getDI());
441 USHORT NumOpenHandles = 0;
442 USHORT i;
443
444 for (i = 0; i < ARRAYSIZE(HandleTable); i++)
445 {
446 if (HandleTable[i].Allocated)
447 {
448 HandleDir->Handle = i;
449 RtlCopyMemory(HandleDir->Name,
450 HandleTable[i].Name,
451 sizeof(HandleDir->Name));
452 ++HandleDir;
453 ++NumOpenHandles;
454 }
455 }
456
457 setAH(EMS_STATUS_SUCCESS);
458 setAL((UCHAR)NumOpenHandles);
459 }
460 else if (getAL() == 0x01)
461 {
462 /* Search for Named Handle */
463
464 PUCHAR HandleName = (PUCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
465 PEMS_HANDLE HandleFound = NULL;
466 USHORT i;
467
468 for (i = 0; i < ARRAYSIZE(HandleTable); i++)
469 {
470 if (HandleTable[i].Allocated &&
471 RtlCompareMemory(HandleName,
472 HandleTable[i].Name,
473 sizeof(HandleTable[i].Name)) == sizeof(HandleTable[i].Name))
474 {
475 HandleFound = &HandleTable[i];
476 break;
477 }
478 }
479
480 /* Bail out if no handle was found */
481 if (i >= ARRAYSIZE(HandleTable)) // HandleFound == NULL
482 {
483 setAH(EMS_STATUS_HANDLE_NOT_FOUND);
484 break;
485 }
486
487 /* Return the handle number */
488 setDX(i);
489
490 /* Sanity check: Check whether the handle was unnamed */
491 i = 0;
492 while ((i < sizeof(HandleFound->Name)) && (HandleFound->Name[i] == '\0'))
493 ++i;
494
495 if (i >= sizeof(HandleFound->Name))
496 {
497 setAH(EMS_STATUS_UNNAMED_HANDLE);
498 }
499 else
500 {
501 setAH(EMS_STATUS_SUCCESS);
502 }
503 }
504 else if (getAL() == 0x02)
505 {
506 /*
507 * Get Total Number of Handles
508 *
509 * This function retrieves the maximum number of handles
510 * (allocated or not) the memory manager supports, which
511 * a program may request.
512 */
513 setAH(EMS_STATUS_SUCCESS);
514 setBX(ARRAYSIZE(HandleTable));
515 }
516 else
517 {
518 DPRINT1("Invalid subfunction %02X for EMS function AH = 54h\n", getAL());
519 setAH(EMS_STATUS_INVALID_SUBFUNCTION);
520 }
521
522 break;
523 }
524
525 /* Move/Exchange Memory */
526 case 0x57:
527 {
528 PUCHAR SourcePtr, DestPtr;
529 PEMS_HANDLE HandleEntry;
530 PEMS_PAGE PageEntry;
531 BOOLEAN Exchange = getAL();
532 PEMS_COPY_DATA Data = (PEMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI());
533
534 if (Data->SourceType)
535 {
536 /* Expanded memory */
537 HandleEntry = GetHandleRecord(Data->SourceHandle);
538 if (!ValidateHandle(HandleEntry))
539 {
540 setAH(EMS_STATUS_INVALID_HANDLE);
541 break;
542 }
543
544 PageEntry = GetLogicalPage(HandleEntry, Data->SourceSegment);
545 if (!PageEntry)
546 {
547 setAH(EMS_STATUS_INV_LOGICAL_PAGE);
548 break;
549 }
550
551 SourcePtr = (PUCHAR)((ULONG_PTR)EmsMemory
552 + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE
553 + Data->SourceOffset);
554 }
555 else
556 {
557 /* Conventional memory */
558 SourcePtr = (PUCHAR)SEG_OFF_TO_PTR(Data->SourceSegment, Data->SourceOffset);
559 }
560
561 if (Data->DestType)
562 {
563 /* Expanded memory */
564 HandleEntry = GetHandleRecord(Data->DestHandle);
565 if (!ValidateHandle(HandleEntry))
566 {
567 setAH(EMS_STATUS_INVALID_HANDLE);
568 break;
569 }
570
571 PageEntry = GetLogicalPage(HandleEntry, Data->DestSegment);
572 if (!PageEntry)
573 {
574 setAH(EMS_STATUS_INV_LOGICAL_PAGE);
575 break;
576 }
577
578 DestPtr = (PUCHAR)((ULONG_PTR)EmsMemory
579 + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE
580 + Data->DestOffset);
581 }
582 else
583 {
584 /* Conventional memory */
585 DestPtr = (PUCHAR)SEG_OFF_TO_PTR(Data->DestSegment, Data->DestOffset);
586 }
587
588 if (Exchange)
589 {
590 ULONG i;
591
592 /* Exchange */
593 for (i = 0; i < Data->RegionLength; i++)
594 {
595 UCHAR Temp = DestPtr[i];
596 DestPtr[i] = SourcePtr[i];
597 SourcePtr[i] = Temp;
598 }
599 }
600 else
601 {
602 /* Move */
603 RtlMoveMemory(DestPtr, SourcePtr, Data->RegionLength);
604 }
605
606 setAH(EMS_STATUS_SUCCESS);
607 break;
608 }
609
610 /* Get Mappable Physical Address Array */
611 case 0x58:
612 {
613 if (getAL() == 0x00)
614 {
615 PEMS_MAPPABLE_PHYS_PAGE PageArray = (PEMS_MAPPABLE_PHYS_PAGE)SEG_OFF_TO_PTR(getES(), getDI());
616 ULONG i;
617
618 for (i = 0; i < EMS_PHYSICAL_PAGES; i++)
619 {
620 PageArray->PageSegment = EMS_SEGMENT + i * (EMS_PAGE_SIZE >> 4);
621 PageArray->PageNumber = i;
622 ++PageArray;
623 }
624
625 setAH(EMS_STATUS_SUCCESS);
626 setCX(EMS_PHYSICAL_PAGES);
627 }
628 else if (getAL() == 0x01)
629 {
630 setAH(EMS_STATUS_SUCCESS);
631 setCX(EMS_PHYSICAL_PAGES);
632 }
633 else
634 {
635 DPRINT1("Invalid subfunction %02X for EMS function AH = 58h\n", getAL());
636 setAH(EMS_STATUS_INVALID_SUBFUNCTION);
637 }
638
639 break;
640 }
641
642 /* Get Expanded Memory Hardware Information */
643 case 0x59:
644 {
645 if (getAL() == 0x00)
646 {
647 PEMS_HARDWARE_INFO HardwareInfo = (PEMS_HARDWARE_INFO)SEG_OFF_TO_PTR(getES(), getDI());
648
649 /* Return the hardware information */
650 HardwareInfo->RawPageSize = EMS_PAGE_SIZE >> 4;
651 HardwareInfo->AlternateRegSets = 0;
652 HardwareInfo->ContextAreaSize = sizeof(Mapping);
653 HardwareInfo->DmaRegisterSets = 0;
654 HardwareInfo->DmaChannelOperation = 0;
655
656 setAH(EMS_STATUS_SUCCESS);
657 }
658 else if (getAL() == 0x01)
659 {
660 /* Same as function AH = 42h */
661 setAH(EMS_STATUS_SUCCESS);
662 setBX(RtlNumberOfClearBits(&AllocBitmap));
663 setDX(EmsTotalPages);
664 }
665 else
666 {
667 DPRINT1("Invalid subfunction %02X for EMS function AH = 59h\n", getAL());
668 setAH(EMS_STATUS_INVALID_SUBFUNCTION);
669 }
670
671 break;
672 }
673
674 default:
675 {
676 DPRINT1("EMS function AH = %02X NOT IMPLEMENTED\n", getAH());
677 setAH(EMS_STATUS_UNKNOWN_FUNCTION);
678 break;
679 }
680 }
681 }
682
683 static VOID FASTCALL EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
684 {
685 ULONG i;
686 ULONG RelativeAddress = Address - TO_LINEAR(EmsSegment, 0);
687 ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
688 ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
689 ULONG Offset, Length;
690
691 for (i = FirstPage; i <= LastPage; i++)
692 {
693 Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0;
694 Length = ((i == LastPage)
695 ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS))
696 : EMS_PAGE_SIZE) - Offset;
697
698 if (Mapping[i]) RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Mapping[i] + Offset), Length);
699 Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
700 }
701 }
702
703 static BOOLEAN FASTCALL EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
704 {
705 ULONG i;
706 ULONG RelativeAddress = Address - TO_LINEAR(EmsSegment, 0);
707 ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
708 ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
709 ULONG Offset, Length;
710
711 for (i = FirstPage; i <= LastPage; i++)
712 {
713 Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0;
714 Length = ((i == LastPage)
715 ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS))
716 : EMS_PAGE_SIZE) - Offset;
717
718 if (Mapping[i]) RtlCopyMemory((PVOID)((ULONG_PTR)Mapping[i] + Offset), Buffer, Length);
719 Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
720 }
721
722 return TRUE;
723 }
724
725 static WORD NTAPI EmsDrvDispatchIoctlRead(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length)
726 {
727 // TODO: NOT IMPLEMENTED
728 UNIMPLEMENTED;
729 return DOS_DEVSTAT_DONE;
730 }
731
732 /* PUBLIC FUNCTIONS ***********************************************************/
733
734 BOOLEAN EmsDrvInitialize(USHORT Segment, ULONG TotalPages)
735 {
736 USHORT Size;
737
738 /* Try to allocate our page table in UMA at the given segment */
739 EmsSegment = (Segment != 0 ? Segment : EMS_SEGMENT);
740 Size = EMS_SEGMENT_SIZE; // Size in paragraphs
741 if (!UmaDescReserve(&EmsSegment, &Size)) return FALSE;
742
743 EmsTotalPages = TotalPages;
744 BitmapBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
745 HEAP_ZERO_MEMORY,
746 ((TotalPages + 31) / 32) * sizeof(ULONG));
747 if (BitmapBuffer == NULL)
748 {
749 UmaDescRelease(EmsSegment);
750 return FALSE;
751 }
752
753 RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, TotalPages);
754
755 PageTable = (PEMS_PAGE)RtlAllocateHeap(RtlGetProcessHeap(),
756 HEAP_ZERO_MEMORY,
757 TotalPages * sizeof(EMS_PAGE));
758 if (PageTable == NULL)
759 {
760 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
761 BitmapBuffer = NULL;
762
763 UmaDescRelease(EmsSegment);
764 return FALSE;
765 }
766
767 EmsMemory = (PVOID)RtlAllocateHeap(RtlGetProcessHeap(), 0, TotalPages * EMS_PAGE_SIZE);
768 if (EmsMemory == NULL)
769 {
770 RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
771 PageTable = NULL;
772 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
773 BitmapBuffer = NULL;
774
775 UmaDescRelease(EmsSegment);
776 return FALSE;
777 }
778
779 InitHandlesTable();
780 /*
781 * FIXME: We should ensure that the system handle is associated
782 * with mapped pages from conventional memory. DosEmu seems to do
783 * it correctly. 384kB of memory mapped.
784 */
785 if (InitSystemHandle(384/16) != EMS_STATUS_SUCCESS)
786 {
787 DPRINT1("Impossible to allocate pages for the system handle!\n");
788
789 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsMemory);
790 EmsMemory = NULL;
791 RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
792 PageTable = NULL;
793 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
794 BitmapBuffer = NULL;
795
796 UmaDescRelease(EmsSegment);
797 return FALSE;
798 }
799
800 MemInstallFastMemoryHook((PVOID)TO_LINEAR(EmsSegment, 0),
801 EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE,
802 EmsReadMemory,
803 EmsWriteMemory);
804
805 /* Create the device */
806 Node = DosCreateDeviceEx(DOS_DEVATTR_IOCTL | DOS_DEVATTR_CHARACTER,
807 EMS_DEVICE_NAME,
808 Int16To32StubSize);
809 Node->IoctlReadRoutine = EmsDrvDispatchIoctlRead;
810
811 RegisterInt32(DEVICE_PRIVATE_AREA(Node->Driver),
812 EMS_INTERRUPT_NUM, EmsIntHandler, NULL);
813
814 return TRUE;
815 }
816
817 VOID EmsDrvCleanup(VOID)
818 {
819 /* Delete the device */
820 DosDeleteDevice(Node);
821
822 MemRemoveFastMemoryHook((PVOID)TO_LINEAR(EmsSegment, 0),
823 EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE);
824
825 if (EmsMemory)
826 {
827 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsMemory);
828 EmsMemory = NULL;
829 }
830
831 if (PageTable)
832 {
833 RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
834 PageTable = NULL;
835 }
836
837 if (BitmapBuffer)
838 {
839 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
840 BitmapBuffer = NULL;
841 }
842
843 UmaDescRelease(EmsSegment);
844 }