[NTVDM]: Move EMS support as a BIOS module (and initialize it after the BIOS has...
[reactos.git] / reactos / subsystems / mvdm / ntvdm / bios / bios32 / ems.c
1 /*
2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: ems.c
5 * PURPOSE: Expanded Memory Support
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "emulator.h"
14 #include "bios/bios32/bios32p.h"
15 #include <ndk/rtltypes.h>
16 #include <ndk/rtlfuncs.h>
17 #include "ems.h"
18 #include "memory.h"
19
20 /* PRIVATE VARIABLES **********************************************************/
21
22 static RTL_BITMAP AllocBitmap;
23 static ULONG BitmapBuffer[(EMS_TOTAL_PAGES + sizeof(ULONG) - 1) / sizeof(ULONG)];
24 static EMS_PAGE PageTable[EMS_TOTAL_PAGES];
25 static EMS_HANDLE HandleTable[EMS_MAX_HANDLES];
26 static PVOID Mapping[EMS_PHYSICAL_PAGES] = { NULL };
27
28 /* PRIVATE FUNCTIONS **********************************************************/
29
30 static USHORT EmsFree(USHORT Handle)
31 {
32 PLIST_ENTRY Entry;
33 PEMS_HANDLE HandleEntry = &HandleTable[Handle];
34
35 if (Handle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
36 {
37 return EMS_STATUS_INVALID_HANDLE;
38 }
39
40 for (Entry = HandleEntry->PageList.Flink;
41 Entry != &HandleEntry->PageList;
42 Entry = Entry->Flink)
43 {
44 PEMS_PAGE PageEntry = (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
45 ULONG PageNumber = ARRAY_INDEX(PageEntry, PageTable);
46
47 /* Free the page */
48 RtlClearBits(&AllocBitmap, PageNumber, 1);
49 }
50
51 HandleEntry->Allocated = FALSE;
52 HandleEntry->PageCount = 0;
53 InitializeListHead(&HandleEntry->PageList);
54
55 return EMS_STATUS_OK;
56 }
57
58 static UCHAR EmsAlloc(USHORT NumPages, PUSHORT Handle)
59 {
60 ULONG i, CurrentIndex = 0;
61 PEMS_HANDLE HandleEntry;
62
63 if (NumPages == 0) return EMS_STATUS_ZERO_PAGES;
64
65 for (i = 0; i < EMS_MAX_HANDLES; i++)
66 {
67 HandleEntry = &HandleTable[i];
68 if (!HandleEntry->Allocated)
69 {
70 *Handle = i;
71 break;
72 }
73 }
74
75 if (i == EMS_MAX_HANDLES) return EMS_STATUS_NO_MORE_HANDLES;
76 HandleEntry->Allocated = TRUE;
77
78 while (HandleEntry->PageCount < NumPages)
79 {
80 ULONG RunStart;
81 ULONG RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
82
83 if (RunSize == 0)
84 {
85 /* Free what's been allocated already and report failure */
86 EmsFree(*Handle);
87 return EMS_STATUS_INSUFFICIENT_PAGES;
88 }
89 else if ((HandleEntry->PageCount + RunSize) > NumPages)
90 {
91 /* We don't need the entire run */
92 RunSize = NumPages - HandleEntry->PageCount;
93 }
94
95 CurrentIndex = RunStart + RunSize;
96 HandleEntry->PageCount += RunSize;
97 RtlSetBits(&AllocBitmap, RunStart, RunSize);
98
99 for (i = 0; i < RunSize; i++)
100 {
101 PageTable[RunStart + i].Handle = *Handle;
102 InsertTailList(&HandleEntry->PageList, &PageTable[RunStart + i].Entry);
103 }
104 }
105
106 return EMS_STATUS_OK;
107 }
108
109 static PEMS_PAGE GetLogicalPage(PEMS_HANDLE Handle, USHORT LogicalPage)
110 {
111 PLIST_ENTRY Entry = Handle->PageList.Flink;
112
113 while (LogicalPage)
114 {
115 if (Entry == &Handle->PageList) return NULL;
116 LogicalPage--;
117 Entry = Entry->Flink;
118 }
119
120 return (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
121 }
122
123 static USHORT EmsMap(USHORT Handle, UCHAR PhysicalPage, USHORT LogicalPage)
124 {
125 PEMS_PAGE PageEntry;
126 PEMS_HANDLE HandleEntry = &HandleTable[Handle];
127
128 if (PhysicalPage >= EMS_PHYSICAL_PAGES) return EMS_STATUS_INV_PHYSICAL_PAGE;
129 if (LogicalPage == 0xFFFF)
130 {
131 /* Unmap */
132 Mapping[PhysicalPage] = NULL;
133 return EMS_STATUS_OK;
134 }
135
136 if (Handle >= EMS_MAX_HANDLES || !HandleEntry->Allocated) return EMS_STATUS_INVALID_HANDLE;
137
138 PageEntry = GetLogicalPage(HandleEntry, LogicalPage);
139 if (!PageEntry) return EMS_STATUS_INV_LOGICAL_PAGE;
140
141 Mapping[PhysicalPage] = (PVOID)(EMS_ADDRESS + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE);
142 return EMS_STATUS_OK;
143 }
144
145 static VOID WINAPI EmsIntHandler(LPWORD Stack)
146 {
147 switch (getAH())
148 {
149 /* Get Manager Status */
150 case 0x40:
151 {
152 setAH(EMS_STATUS_OK);
153 break;
154 }
155
156 /* Get Page Frame Segment */
157 case 0x41:
158 {
159 setAH(EMS_STATUS_OK);
160 setBX(EMS_SEGMENT);
161 break;
162 }
163
164 /* Get Number Of Pages */
165 case 0x42:
166 {
167 setAH(EMS_STATUS_OK);
168 setBX(RtlNumberOfClearBits(&AllocBitmap));
169 setDX(EMS_TOTAL_PAGES);
170 break;
171 }
172
173 /* Get Handle And Allocate Memory */
174 case 0x43:
175 {
176 USHORT Handle;
177 UCHAR Status = EmsAlloc(getBX(), &Handle);
178
179 setAH(Status);
180 if (Status == EMS_STATUS_OK) setDX(Handle);
181 break;
182 }
183
184 /* Map Memory */
185 case 0x44:
186 {
187 setAH(EmsMap(getDX(), getAL(), getBX()));
188 break;
189 }
190
191 /* Release Handle And Memory */
192 case 0x45:
193 {
194 setAH(EmsFree(getDX()));
195 break;
196 }
197
198 /* Get EMM Version */
199 case 0x46:
200 {
201 setAH(EMS_STATUS_OK);
202 setAL(EMS_VERSION_NUM);
203 break;
204 }
205
206 /* Move/Exchange Memory */
207 case 0x57:
208 {
209 PUCHAR SourcePtr, DestPtr;
210 PEMS_HANDLE HandleEntry;
211 PEMS_PAGE PageEntry;
212 BOOLEAN Exchange = getAL();
213 PEMS_COPY_DATA Data = (PEMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI());
214
215 if (Data->SourceType)
216 {
217 /* Expanded memory */
218 HandleEntry = &HandleTable[Data->SourceHandle];
219
220 if (Data->SourceHandle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
221 {
222 setAL(EMS_STATUS_INVALID_HANDLE);
223 break;
224 }
225
226 PageEntry = GetLogicalPage(HandleEntry, Data->SourceSegment);
227
228 if (!PageEntry)
229 {
230 setAL(EMS_STATUS_INV_LOGICAL_PAGE);
231 break;
232 }
233
234 SourcePtr = (PUCHAR)REAL_TO_PHYS(EMS_ADDRESS
235 + ARRAY_INDEX(PageEntry, PageTable)
236 * EMS_PAGE_SIZE
237 + Data->SourceOffset);
238 }
239 else
240 {
241 /* Conventional memory */
242 SourcePtr = (PUCHAR)SEG_OFF_TO_PTR(Data->SourceSegment, Data->SourceOffset);
243 }
244
245 if (Data->DestType)
246 {
247 /* Expanded memory */
248 HandleEntry = &HandleTable[Data->DestHandle];
249
250 if (Data->SourceHandle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
251 {
252 setAL(EMS_STATUS_INVALID_HANDLE);
253 break;
254 }
255
256 PageEntry = GetLogicalPage(HandleEntry, Data->DestSegment);
257
258 if (!PageEntry)
259 {
260 setAL(EMS_STATUS_INV_LOGICAL_PAGE);
261 break;
262 }
263
264 DestPtr = (PUCHAR)REAL_TO_PHYS(EMS_ADDRESS
265 + ARRAY_INDEX(PageEntry, PageTable)
266 * EMS_PAGE_SIZE
267 + Data->DestOffset);
268 }
269 else
270 {
271 /* Conventional memory */
272 DestPtr = (PUCHAR)SEG_OFF_TO_PTR(Data->DestSegment, Data->DestOffset);
273 }
274
275 if (Exchange)
276 {
277 ULONG i;
278
279 /* Exchange */
280 for (i = 0; i < Data->RegionLength; i++)
281 {
282 UCHAR Temp = DestPtr[i];
283 DestPtr[i] = SourcePtr[i];
284 SourcePtr[i] = Temp;
285 }
286 }
287 else
288 {
289 /* Move */
290 RtlMoveMemory(DestPtr, SourcePtr, Data->RegionLength);
291 }
292
293 setAL(EMS_STATUS_OK);
294 break;
295 }
296
297 default:
298 {
299 DPRINT1("EMS function AH = %02X NOT IMPLEMENTED\n", getAH());
300 setAH(EMS_STATUS_UNKNOWN_FUNCTION);
301 break;
302 }
303 }
304 }
305
306 static VOID NTAPI EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
307 {
308 ULONG i;
309 ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0);
310 ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
311 ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
312 ULONG Offset, Length;
313
314 for (i = FirstPage; i <= LastPage; i++)
315 {
316 Offset = (i == FirstPage) ? Address & (EMS_PAGE_SIZE - 1) : 0;
317 Length = ((i == LastPage)
318 ? (Address + Size - (LastPage << EMS_PAGE_BITS))
319 : EMS_PAGE_SIZE) - Offset;
320
321 if (Mapping[i]) RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Mapping[i] + Offset), Length);
322 Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
323 }
324 }
325
326 static BOOLEAN NTAPI EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
327 {
328 ULONG i;
329 ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0);
330 ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
331 ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
332 ULONG Offset, Length;
333
334 for (i = FirstPage; i <= LastPage; i++)
335 {
336 Offset = (i == FirstPage) ? Address & (EMS_PAGE_SIZE - 1) : 0;
337 Length = ((i == LastPage)
338 ? (Address + Size - (LastPage << EMS_PAGE_BITS))
339 : EMS_PAGE_SIZE) - Offset;
340
341 if (Mapping[i]) RtlCopyMemory((PVOID)((ULONG_PTR)Mapping[i] + Offset), Buffer, Length);
342 Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
343 }
344
345 return TRUE;
346 }
347
348 /* PUBLIC FUNCTIONS ***********************************************************/
349
350 VOID EmsInitialize(VOID)
351 {
352 ULONG i;
353
354 RtlZeroMemory(BitmapBuffer, sizeof(BitmapBuffer));
355 RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, EMS_TOTAL_PAGES);
356
357 for (i = 0; i < EMS_MAX_HANDLES; i++)
358 {
359 HandleTable[i].Allocated = FALSE;
360 HandleTable[i].PageCount = 0;
361 InitializeListHead(&HandleTable[i].PageList);
362 }
363
364 MemInstallFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0),
365 EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE,
366 EmsReadMemory,
367 EmsWriteMemory);
368
369 RegisterBiosInt32(EMS_INTERRUPT_NUM, EmsIntHandler);
370 }
371
372 VOID EmsCleanup(VOID)
373 {
374 MemRemoveFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0),
375 EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE);
376 }