[NTVDM]
[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: emsdrv.c
5 * PURPOSE: DOS EMS Driver
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "ntvdm.h"
14 #include "emulator.h"
15
16 #include "dos.h"
17 #include "dos/dem.h"
18 #include "device.h"
19
20 #include "../../memory.h"
21 #include "emsdrv.h"
22
23 #define EMS_DEVICE_NAME "EMMXXXX0"
24
25 /* PRIVATE VARIABLES **********************************************************/
26
27 static PDOS_DEVICE_NODE Node;
28 static RTL_BITMAP AllocBitmap;
29 static PULONG BitmapBuffer = NULL;
30 static PEMS_PAGE PageTable = NULL;
31 static EMS_HANDLE HandleTable[EMS_MAX_HANDLES];
32 static PVOID Mapping[EMS_PHYSICAL_PAGES] = { NULL };
33 static ULONG EmsTotalPages = 0;
34 static PVOID EmsMemory = NULL;
35
36 /* PRIVATE FUNCTIONS **********************************************************/
37
38 static inline PEMS_HANDLE GetHandleRecord(USHORT Handle)
39 {
40 if (Handle >= EMS_MAX_HANDLES) return NULL;
41 return &HandleTable[Handle];
42 }
43
44 static USHORT EmsFree(USHORT Handle)
45 {
46 PLIST_ENTRY Entry;
47 PEMS_HANDLE HandleEntry = GetHandleRecord(Handle);
48
49 if (HandleEntry == NULL || !HandleEntry->Allocated)
50 {
51 return EMS_STATUS_INVALID_HANDLE;
52 }
53
54 for (Entry = HandleEntry->PageList.Flink;
55 Entry != &HandleEntry->PageList;
56 Entry = Entry->Flink)
57 {
58 PEMS_PAGE PageEntry = (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
59 ULONG PageNumber = ARRAY_INDEX(PageEntry, PageTable);
60
61 /* Free the page */
62 RtlClearBits(&AllocBitmap, PageNumber, 1);
63 }
64
65 HandleEntry->Allocated = FALSE;
66 HandleEntry->PageCount = 0;
67 InitializeListHead(&HandleEntry->PageList);
68
69 return EMS_STATUS_OK;
70 }
71
72 static UCHAR EmsAlloc(USHORT NumPages, PUSHORT Handle)
73 {
74 ULONG i, CurrentIndex = 0;
75 PEMS_HANDLE HandleEntry;
76
77 if (NumPages == 0) return EMS_STATUS_ZERO_PAGES;
78
79 for (i = 0; i < EMS_MAX_HANDLES; i++)
80 {
81 HandleEntry = &HandleTable[i];
82 if (!HandleEntry->Allocated)
83 {
84 *Handle = i;
85 break;
86 }
87 }
88
89 if (i == EMS_MAX_HANDLES) return EMS_STATUS_NO_MORE_HANDLES;
90 HandleEntry->Allocated = TRUE;
91
92 while (HandleEntry->PageCount < NumPages)
93 {
94 ULONG RunStart;
95 ULONG RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
96
97 if (RunSize == 0)
98 {
99 /* Free what's been allocated already and report failure */
100 EmsFree(*Handle);
101 return EMS_STATUS_INSUFFICIENT_PAGES;
102 }
103 else if ((HandleEntry->PageCount + RunSize) > NumPages)
104 {
105 /* We don't need the entire run */
106 RunSize = NumPages - HandleEntry->PageCount;
107 }
108
109 CurrentIndex = RunStart + RunSize;
110 HandleEntry->PageCount += RunSize;
111 RtlSetBits(&AllocBitmap, RunStart, RunSize);
112
113 for (i = 0; i < RunSize; i++)
114 {
115 PageTable[RunStart + i].Handle = *Handle;
116 InsertTailList(&HandleEntry->PageList, &PageTable[RunStart + i].Entry);
117 }
118 }
119
120 return EMS_STATUS_OK;
121 }
122
123 static PEMS_PAGE GetLogicalPage(PEMS_HANDLE Handle, USHORT LogicalPage)
124 {
125 PLIST_ENTRY Entry = Handle->PageList.Flink;
126
127 while (LogicalPage)
128 {
129 if (Entry == &Handle->PageList) return NULL;
130 LogicalPage--;
131 Entry = Entry->Flink;
132 }
133
134 return (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
135 }
136
137 static USHORT EmsMap(USHORT Handle, UCHAR PhysicalPage, USHORT LogicalPage)
138 {
139 PEMS_PAGE PageEntry;
140 PEMS_HANDLE HandleEntry = GetHandleRecord(Handle);
141
142 if (PhysicalPage >= EMS_PHYSICAL_PAGES) return EMS_STATUS_INV_PHYSICAL_PAGE;
143 if (LogicalPage == 0xFFFF)
144 {
145 /* Unmap */
146 Mapping[PhysicalPage] = NULL;
147 return EMS_STATUS_OK;
148 }
149
150 if (HandleEntry == NULL || !HandleEntry->Allocated)
151 {
152 return EMS_STATUS_INVALID_HANDLE;
153 }
154
155 PageEntry = GetLogicalPage(HandleEntry, LogicalPage);
156 if (!PageEntry) return EMS_STATUS_INV_LOGICAL_PAGE;
157
158 Mapping[PhysicalPage] = (PVOID)((ULONG_PTR)EmsMemory
159 + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE);
160 return EMS_STATUS_OK;
161 }
162
163 static VOID WINAPI EmsIntHandler(LPWORD Stack)
164 {
165 static PVOID MappingBackup[EMS_PHYSICAL_PAGES] = { NULL };
166
167 switch (getAH())
168 {
169 /* Get Manager Status */
170 case 0x40:
171 {
172 setAH(EMS_STATUS_OK);
173 break;
174 }
175
176 /* Get Page Frame Segment */
177 case 0x41:
178 {
179 setAH(EMS_STATUS_OK);
180 setBX(EMS_SEGMENT);
181 break;
182 }
183
184 /* Get Number Of Pages */
185 case 0x42:
186 {
187 setAH(EMS_STATUS_OK);
188 setBX(RtlNumberOfClearBits(&AllocBitmap));
189 setDX(EmsTotalPages);
190 break;
191 }
192
193 /* Get Handle And Allocate Memory */
194 case 0x43:
195 {
196 USHORT Handle;
197 UCHAR Status = EmsAlloc(getBX(), &Handle);
198
199 setAH(Status);
200 if (Status == EMS_STATUS_OK) setDX(Handle);
201 break;
202 }
203
204 /* Map Memory */
205 case 0x44:
206 {
207 setAH(EmsMap(getDX(), getAL(), getBX()));
208 break;
209 }
210
211 /* Release Handle And Memory */
212 case 0x45:
213 {
214 setAH(EmsFree(getDX()));
215 break;
216 }
217
218 /* Get EMM Version */
219 case 0x46:
220 {
221 setAH(EMS_STATUS_OK);
222 setAL(EMS_VERSION_NUM);
223 break;
224 }
225
226 /* Save Page Map */
227 case 0x47:
228 {
229 RtlCopyMemory(MappingBackup, Mapping, sizeof(PVOID) * EMS_PHYSICAL_PAGES);
230 break;
231 }
232
233 /* Restore Page Map */
234 case 0x48:
235 {
236 RtlCopyMemory(Mapping, MappingBackup, sizeof(PVOID) * EMS_PHYSICAL_PAGES);
237 break;
238 }
239
240 /* Get/Set Handle Name */
241 case 0x53:
242 {
243 PEMS_HANDLE HandleEntry = GetHandleRecord(getDX());
244 if (HandleEntry == NULL || !HandleEntry->Allocated)
245 {
246 setAL(EMS_STATUS_INVALID_HANDLE);
247 break;
248 }
249
250 if (getAL() == 0x00)
251 {
252 /* Retrieve the name */
253 RtlCopyMemory(SEG_OFF_TO_PTR(getES(), getDI()),
254 HandleEntry->Name,
255 sizeof(HandleEntry->Name));
256 setAH(EMS_STATUS_OK);
257 }
258 else if (getAL() == 0x01)
259 {
260 /* Store the name */
261 RtlCopyMemory(HandleEntry->Name,
262 SEG_OFF_TO_PTR(getDS(), getSI()),
263 sizeof(HandleEntry->Name));
264 setAH(EMS_STATUS_OK);
265 }
266 else
267 {
268 DPRINT1("Invalid subfunction %02X for EMS function AH = 53h\n", getAL());
269 setAH(EMS_STATUS_UNKNOWN_FUNCTION);
270 }
271
272 break;
273 }
274
275 /* Move/Exchange Memory */
276 case 0x57:
277 {
278 PUCHAR SourcePtr, DestPtr;
279 PEMS_HANDLE HandleEntry;
280 PEMS_PAGE PageEntry;
281 BOOLEAN Exchange = getAL();
282 PEMS_COPY_DATA Data = (PEMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI());
283
284 if (Data->SourceType)
285 {
286 /* Expanded memory */
287 HandleEntry = GetHandleRecord(Data->SourceHandle);
288
289 if (HandleEntry == NULL || !HandleEntry->Allocated)
290 {
291 setAL(EMS_STATUS_INVALID_HANDLE);
292 break;
293 }
294
295 PageEntry = GetLogicalPage(HandleEntry, Data->SourceSegment);
296
297 if (!PageEntry)
298 {
299 setAL(EMS_STATUS_INV_LOGICAL_PAGE);
300 break;
301 }
302
303 SourcePtr = (PUCHAR)((ULONG_PTR)EmsMemory
304 + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE
305 + Data->SourceOffset);
306 }
307 else
308 {
309 /* Conventional memory */
310 SourcePtr = (PUCHAR)SEG_OFF_TO_PTR(Data->SourceSegment, Data->SourceOffset);
311 }
312
313 if (Data->DestType)
314 {
315 /* Expanded memory */
316 HandleEntry = GetHandleRecord(Data->DestHandle);
317
318 if (HandleEntry == NULL || !HandleEntry->Allocated)
319 {
320 setAL(EMS_STATUS_INVALID_HANDLE);
321 break;
322 }
323
324 PageEntry = GetLogicalPage(HandleEntry, Data->DestSegment);
325
326 if (!PageEntry)
327 {
328 setAL(EMS_STATUS_INV_LOGICAL_PAGE);
329 break;
330 }
331
332 DestPtr = (PUCHAR)((ULONG_PTR)EmsMemory
333 + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE
334 + Data->DestOffset);
335 }
336 else
337 {
338 /* Conventional memory */
339 DestPtr = (PUCHAR)SEG_OFF_TO_PTR(Data->DestSegment, Data->DestOffset);
340 }
341
342 if (Exchange)
343 {
344 ULONG i;
345
346 /* Exchange */
347 for (i = 0; i < Data->RegionLength; i++)
348 {
349 UCHAR Temp = DestPtr[i];
350 DestPtr[i] = SourcePtr[i];
351 SourcePtr[i] = Temp;
352 }
353 }
354 else
355 {
356 /* Move */
357 RtlMoveMemory(DestPtr, SourcePtr, Data->RegionLength);
358 }
359
360 setAL(EMS_STATUS_OK);
361 break;
362 }
363
364 /* Get Expanded Memory Hardware Information */
365 case 0x59:
366 {
367 if (getAL() == 0x00)
368 {
369 PEMS_HARDWARE_INFO HardwareInfo = (PEMS_HARDWARE_INFO)SEG_OFF_TO_PTR(getES(), getDI());
370
371 /* Return the hardware information */
372 HardwareInfo->RawPageSize = EMS_PAGE_SIZE >> 4;
373 HardwareInfo->AlternateRegSets = 0;
374 HardwareInfo->ContextAreaSize = sizeof(Mapping);
375 HardwareInfo->DmaRegisterSets = 0;
376 HardwareInfo->DmaChannelOperation = 0;
377
378 setAH(EMS_STATUS_OK);
379 }
380 else if (getAL() == 0x01)
381 {
382 /* Same as function AH = 42h */
383 setAH(EMS_STATUS_OK);
384 setBX(RtlNumberOfClearBits(&AllocBitmap));
385 setDX(EmsTotalPages);
386 }
387 else
388 {
389 DPRINT1("Invalid subfunction %02X for EMS function AH = 59h\n", getAL());
390 setAH(EMS_STATUS_UNKNOWN_FUNCTION);
391 }
392
393 break;
394 }
395
396 default:
397 {
398 DPRINT1("EMS function AH = %02X NOT IMPLEMENTED\n", getAH());
399 setAH(EMS_STATUS_UNKNOWN_FUNCTION);
400 break;
401 }
402 }
403 }
404
405 static VOID FASTCALL EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
406 {
407 ULONG i;
408 ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0);
409 ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
410 ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
411 ULONG Offset, Length;
412
413 for (i = FirstPage; i <= LastPage; i++)
414 {
415 Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0;
416 Length = ((i == LastPage)
417 ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS))
418 : EMS_PAGE_SIZE) - Offset;
419
420 if (Mapping[i]) RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Mapping[i] + Offset), Length);
421 Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
422 }
423 }
424
425 static BOOLEAN FASTCALL EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
426 {
427 ULONG i;
428 ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0);
429 ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
430 ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
431 ULONG Offset, Length;
432
433 for (i = FirstPage; i <= LastPage; i++)
434 {
435 Offset = (i == FirstPage) ? RelativeAddress & (EMS_PAGE_SIZE - 1) : 0;
436 Length = ((i == LastPage)
437 ? (RelativeAddress + Size - (LastPage << EMS_PAGE_BITS))
438 : EMS_PAGE_SIZE) - Offset;
439
440 if (Mapping[i]) RtlCopyMemory((PVOID)((ULONG_PTR)Mapping[i] + Offset), Buffer, Length);
441 Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
442 }
443
444 return TRUE;
445 }
446
447
448 WORD NTAPI EmsDrvDispatchIoctlRead(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length)
449 {
450 // TODO: NOT IMPLEMENTED
451 UNIMPLEMENTED;
452
453 return DOS_DEVSTAT_DONE;
454 }
455
456 /* PUBLIC FUNCTIONS ***********************************************************/
457
458 BOOLEAN EmsDrvInitialize(ULONG TotalPages)
459 {
460 ULONG i;
461
462 for (i = 0; i < EMS_MAX_HANDLES; i++)
463 {
464 HandleTable[i].Allocated = FALSE;
465 HandleTable[i].PageCount = 0;
466 InitializeListHead(&HandleTable[i].PageList);
467 }
468
469 EmsTotalPages = TotalPages;
470 BitmapBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
471 HEAP_ZERO_MEMORY,
472 ((TotalPages + 31) / 32) * sizeof(ULONG));
473 if (BitmapBuffer == NULL) return FALSE;
474
475 RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, TotalPages);
476
477 PageTable = (PEMS_PAGE)RtlAllocateHeap(RtlGetProcessHeap(),
478 HEAP_ZERO_MEMORY,
479 TotalPages * sizeof(EMS_PAGE));
480 if (PageTable == NULL)
481 {
482 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
483 BitmapBuffer = NULL;
484
485 return FALSE;
486 }
487
488 EmsMemory = (PVOID)RtlAllocateHeap(RtlGetProcessHeap(), 0, TotalPages * EMS_PAGE_SIZE);
489 if (EmsMemory == NULL)
490 {
491 RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
492 PageTable = NULL;
493 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
494 BitmapBuffer = NULL;
495
496 return FALSE;
497 }
498
499 MemInstallFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0),
500 EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE,
501 EmsReadMemory,
502 EmsWriteMemory);
503
504
505 /* Create the device */
506 Node = DosCreateDeviceEx(DOS_DEVATTR_IOCTL | DOS_DEVATTR_CHARACTER,
507 EMS_DEVICE_NAME,
508 32);
509 Node->IoctlReadRoutine = EmsDrvDispatchIoctlRead;
510
511 RegisterInt32(MAKELONG(sizeof(DOS_DRIVER) + DEVICE_CODE_SIZE, HIWORD(Node->Driver)),
512 EMS_INTERRUPT_NUM,
513 EmsIntHandler,
514 NULL);
515
516 return TRUE;
517 }
518
519 VOID EmsDrvCleanup(VOID)
520 {
521 /* Delete the device */
522 DosDeleteDevice(Node);
523
524 MemRemoveFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0),
525 EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE);
526
527 if (EmsMemory)
528 {
529 RtlFreeHeap(RtlGetProcessHeap(), 0, EmsMemory);
530 EmsMemory = NULL;
531 }
532
533 if (PageTable)
534 {
535 RtlFreeHeap(RtlGetProcessHeap(), 0, PageTable);
536 PageTable = NULL;
537 }
538
539 if (BitmapBuffer)
540 {
541 RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer);
542 BitmapBuffer = NULL;
543 }
544 }