[BASESRV][NTVDM][TESTVDD] Improve the FILE header section. Brought to you by Adam...
[reactos.git] / reactos / subsystems / mvdm / ntvdm / bios / umamgr.c
1 /*
2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/bios/umamgr.c
5 * PURPOSE: Upper Memory Area Manager
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7 *
8 * NOTE: The UMA Manager is used by the DOS XMS Driver (UMB Provider part),
9 * indirectly by the DOS EMS Driver, and by VDD memory management functions.
10 */
11
12 /* INCLUDES *******************************************************************/
13
14 #define NDEBUG
15
16 #include "ntvdm.h"
17 #include "emulator.h"
18 #include "memory.h"
19
20 #include "umamgr.h"
21
22 /* PRIVATE VARIABLES **********************************************************/
23
24 typedef struct _UMA_DESCRIPTOR
25 {
26 LIST_ENTRY Entry;
27 ULONG Start;
28 ULONG Size;
29 UMA_DESC_TYPE Type;
30 } UMA_DESCRIPTOR, *PUMA_DESCRIPTOR;
31
32 /*
33 * Sorted list of UMA descriptors.
34 *
35 * The descriptor list is (and must always be) sorted by memory addresses,
36 * and all consecutive free descriptors are always merged together, so that
37 * free ones are always separated from each other by descriptors of other types.
38 *
39 * TODO: Add the fact that no blocks of size == 0 are allowed.
40 */
41 static LIST_ENTRY UmaDescriptorList = { &UmaDescriptorList, &UmaDescriptorList };
42
43 /* PRIVATE FUNCTIONS **********************************************************/
44
45 static PUMA_DESCRIPTOR
46 CreateUmaDescriptor(IN OUT PLIST_ENTRY ListHead,
47 IN ULONG Address,
48 IN ULONG Size,
49 IN UMA_DESC_TYPE Type)
50 {
51 PUMA_DESCRIPTOR UmaDesc;
52
53 ASSERT(Size > 0);
54
55 UmaDesc = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*UmaDesc));
56 if (!UmaDesc) return NULL;
57
58 UmaDesc->Start = Address;
59 UmaDesc->Size = Size;
60 UmaDesc->Type = Type;
61
62 /*
63 * We use the trick of http://www.osronline.com/article.cfm?article=499 to insert
64 * the new descriptor just after the current entry that we specify via 'ListHead'.
65 * If 'ListHead' is NULL then we insert the descriptor at the tail of 'UmaDescriptorList'
66 * (which is equivalent to inserting it at the head of 'UmaDescriptorList.Blink').
67 */
68 if (ListHead == NULL) ListHead = UmaDescriptorList.Blink;
69 InsertHeadList(ListHead, &UmaDesc->Entry);
70
71 return UmaDesc;
72 }
73
74 static VOID FreeUmaDescriptor(PUMA_DESCRIPTOR UmaDesc)
75 {
76 RemoveEntryList(&UmaDesc->Entry);
77 RtlFreeHeap(RtlGetProcessHeap(), 0, UmaDesc);
78 }
79
80 /* PUBLIC FUNCTIONS ***********************************************************/
81
82 BOOLEAN UmaDescReserve(IN OUT PUSHORT Segment, IN OUT PUSHORT Size)
83 {
84 ULONG Address = (*Segment << 4); // Convert segment number into address.
85 ULONG RequestSize = (*Size << 4); // Convert size in paragraphs into size in bytes.
86 ULONG MaxSize = 0;
87 PLIST_ENTRY Entry;
88 PUMA_DESCRIPTOR UmaDesc, NewUmaDesc;
89 PUMA_DESCRIPTOR FoundUmaDesc = NULL;
90
91 // FIXME: Check! What to do?
92 if (RequestSize == 0) DPRINT1("Requesting UMA descriptor with null size?!\n");
93
94 Entry = UmaDescriptorList.Flink;
95 while (Entry != &UmaDescriptorList)
96 {
97 UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(Entry, UMA_DESCRIPTOR, Entry);
98 Entry = Entry->Flink;
99
100 /* Only check free descriptors */
101 if (UmaDesc->Type != UMA_FREE) continue;
102
103 /* Update the maximum descriptor size */
104 if (UmaDesc->Size > MaxSize) MaxSize = UmaDesc->Size;
105
106 /* Descriptor too small, continue... */
107 if (UmaDesc->Size < RequestSize) continue;
108
109 /* Do we want to reserve the descriptor at a precise address? */
110 if (Address)
111 {
112 /* If the descriptor ends before the desired region, try again */
113 if (UmaDesc->Start + UmaDesc->Size <= Address) continue;
114
115 /*
116 * If we have a descriptor, but one of its boundaries crosses the
117 * desired region (it starts after the desired region, or ends
118 * before the end of the desired region), this means that there
119 * is already something inside the region, so that we cannot
120 * allocate the region here. Bail out.
121 */
122 if (UmaDesc->Start > Address ||
123 UmaDesc->Start + UmaDesc->Size < Address + RequestSize)
124 {
125 goto Fail;
126 }
127
128 /* We now have a free descriptor that overlaps our desired region: split it */
129
130 /*
131 * Here, UmaDesc->Start == Address or UmaDesc->Start < Address,
132 * in which case we need to split the descriptor to the left.
133 */
134 if (UmaDesc->Start < Address)
135 {
136 /* Create a new free descriptor and insert it after the current one */
137 NewUmaDesc = CreateUmaDescriptor(&UmaDesc->Entry,
138 Address,
139 UmaDesc->Size - (Address - UmaDesc->Start),
140 UmaDesc->Type); // UMA_FREE
141 if (!NewUmaDesc)
142 {
143 DPRINT1("CreateUmaDescriptor failed, UMA descriptor list possibly corrupted!\n");
144 goto Fail;
145 }
146
147 /* Reduce the size of the splitted left descriptor */
148 UmaDesc->Size = (Address - UmaDesc->Start);
149
150 /* Current descriptor is now the new created one */
151 UmaDesc = NewUmaDesc;
152 }
153
154 /* Here, UmaDesc->Start == Address */
155 }
156
157 /* Descriptor of large enough size: split it to the right if needed */
158 // FIXME: It might be needed to consider a minimum size starting which we need to split.
159 // if (UmaDesc->Size - RequestSize > (3 << 4))
160 if (UmaDesc->Size > RequestSize)
161 {
162 /*
163 * Create a new free descriptor and insert it after the current one.
164 * Because consecutive free descriptors are always merged together,
165 * the descriptor following 'UmaDesc' cannot be free, so that this
166 * new free descriptor does not need to be merged with some others.
167 */
168 NewUmaDesc = CreateUmaDescriptor(&UmaDesc->Entry,
169 UmaDesc->Start + RequestSize,
170 UmaDesc->Size - RequestSize,
171 UmaDesc->Type); // UMA_FREE
172 if (!NewUmaDesc)
173 {
174 DPRINT1("CreateUmaDescriptor failed, UMA descriptor list possibly corrupted!\n");
175 goto Fail;
176 }
177
178 /* Reduce the size of the splitted left descriptor */
179 UmaDesc->Size = RequestSize;
180 }
181
182 /* We have a descriptor of correct size, initialize it */
183 UmaDesc->Type = UMA_UMB;
184 FoundUmaDesc = UmaDesc;
185 break;
186 }
187
188 if (FoundUmaDesc)
189 {
190 /* Returned address is a segment and size is in paragraphs */
191 *Segment = (FoundUmaDesc->Start >> 4);
192 *Size = (FoundUmaDesc->Size >> 4);
193 return TRUE;
194 }
195 else
196 {
197 Fail:
198 /* Returned address is a segment and size is in paragraphs */
199 *Segment = 0x0000;
200 *Size = (MaxSize >> 4);
201 return FALSE;
202 }
203 }
204
205 BOOLEAN UmaDescRelease(IN USHORT Segment)
206 {
207 ULONG Address = (Segment << 4); // Convert segment number into address.
208 PLIST_ENTRY Entry, PrevEntry, NextEntry;
209 PUMA_DESCRIPTOR UmaDesc, PrevDesc = NULL, NextDesc = NULL;
210 PUMA_DESCRIPTOR FoundUmaDesc = NULL;
211
212 Entry = UmaDescriptorList.Flink;
213 while (Entry != &UmaDescriptorList)
214 {
215 UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(Entry, UMA_DESCRIPTOR, Entry);
216 Entry = Entry->Flink;
217
218 /* Search for the descriptor in the list */
219 if (UmaDesc->Start == Address && UmaDesc->Type == UMA_UMB)
220 {
221 /* We found it */
222 FoundUmaDesc = UmaDesc;
223 break;
224 }
225 }
226
227 if (FoundUmaDesc)
228 {
229 FoundUmaDesc->Type = UMA_FREE;
230
231 /* Combine free descriptors adjacent to this one */
232 PrevEntry = FoundUmaDesc->Entry.Blink;
233 NextEntry = FoundUmaDesc->Entry.Flink;
234
235 if (PrevEntry != &UmaDescriptorList)
236 PrevDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(PrevEntry, UMA_DESCRIPTOR, Entry);
237 if (NextEntry != &UmaDescriptorList)
238 NextDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(NextEntry, UMA_DESCRIPTOR, Entry);
239
240 if (NextDesc && NextDesc->Type == FoundUmaDesc->Type) // UMA_FREE
241 {
242 FoundUmaDesc->Size += NextDesc->Size;
243 FreeUmaDescriptor(NextDesc);
244 }
245
246 if (PrevDesc && PrevDesc->Type == FoundUmaDesc->Type) // UMA_FREE
247 {
248 PrevDesc->Size += FoundUmaDesc->Size;
249 FreeUmaDescriptor(FoundUmaDesc);
250 }
251
252 return TRUE;
253 }
254
255 return FALSE;
256 }
257
258 BOOLEAN UmaDescReallocate(IN USHORT Segment, IN OUT PUSHORT Size)
259 {
260 ULONG Address = (Segment << 4); // Convert segment number into address.
261 ULONG RequestSize = (*Size << 4); // Convert size in paragraphs into size in bytes.
262 ULONG MaxSize = 0;
263 PLIST_ENTRY Entry, NextEntry;
264 PUMA_DESCRIPTOR UmaDesc, NextDesc = NULL, NewUmaDesc;
265 PUMA_DESCRIPTOR FoundUmaDesc = NULL;
266
267 // FIXME: Check! What to do?
268 if (RequestSize == 0) DPRINT1("Resizing UMA descriptor %04X to null size?!\n", Segment);
269
270 Entry = UmaDescriptorList.Flink;
271 while (Entry != &UmaDescriptorList)
272 {
273 UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(Entry, UMA_DESCRIPTOR, Entry);
274 Entry = Entry->Flink;
275
276 /* Only get the maximum size of free descriptors */
277 if (UmaDesc->Type == UMA_FREE)
278 {
279 /* Update the maximum descriptor size */
280 if (UmaDesc->Size > MaxSize) MaxSize = UmaDesc->Size;
281 }
282
283 /* Search for the descriptor in the list */
284 if (UmaDesc->Start == Address && UmaDesc->Type == UMA_UMB)
285 {
286 /* We found it */
287 FoundUmaDesc = UmaDesc;
288 break;
289 }
290 }
291
292 if (FoundUmaDesc)
293 {
294 /* If we do not resize anything, just quit with success */
295 if (FoundUmaDesc->Size == RequestSize) goto Success;
296
297 NextEntry = FoundUmaDesc->Entry.Flink;
298
299 if (NextEntry != &UmaDescriptorList)
300 NextDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(NextEntry, UMA_DESCRIPTOR, Entry);
301
302 /* Check for reduction or enlargement */
303 if (FoundUmaDesc->Size > RequestSize)
304 {
305 /* Reduction */
306
307 /*
308 * Check if the next descriptor is free, in which case we
309 * extend it, otherwise we create a new free descriptor.
310 */
311 if (NextDesc && NextDesc->Type == UMA_FREE)
312 {
313 /* Yes it is, expand its size and move it down */
314 NextDesc->Size += (FoundUmaDesc->Size - RequestSize);
315 NextDesc->Start -= (FoundUmaDesc->Size - RequestSize);
316 }
317 else
318 {
319 // FIXME: It might be needed to consider a minimum size starting which we need to split.
320 // if (FoundUmaDesc->Size - RequestSize > (3 << 4))
321
322 /* Create a new free descriptor and insert it after the current one */
323 NewUmaDesc = CreateUmaDescriptor(&FoundUmaDesc->Entry,
324 FoundUmaDesc->Start + RequestSize,
325 FoundUmaDesc->Size - RequestSize,
326 FoundUmaDesc->Type);
327 if (!NewUmaDesc)
328 {
329 DPRINT1("CreateUmaDescriptor failed, UMA descriptor list possibly corrupted!\n");
330 MaxSize = 0;
331 goto Fail;
332 }
333 }
334 }
335 else // if (FoundUmaDesc->Size <= RequestSize)
336 {
337 /* Enlargement */
338
339 /* Check whether the next descriptor is free and large enough for merging */
340 if (NextDesc && NextDesc->Type == UMA_FREE &&
341 FoundUmaDesc->Size + NextDesc->Size >= RequestSize)
342 {
343 /* Yes it is, reduce its size and move it up, and enlarge the reallocated descriptor */
344 NextDesc->Size -= (RequestSize - FoundUmaDesc->Size);
345 NextDesc->Start += (RequestSize - FoundUmaDesc->Size);
346
347 if (NextDesc->Size == 0) FreeUmaDescriptor(NextDesc);
348 }
349 else
350 {
351 MaxSize = 0;
352 goto Fail;
353 }
354 }
355
356 /* Finally, resize the descriptor */
357 FoundUmaDesc->Size = RequestSize;
358
359 Success:
360 /* Returned size is in paragraphs */
361 *Size = (FoundUmaDesc->Size >> 4);
362 return TRUE;
363 }
364 else
365 {
366 Fail:
367 /* Returned size is in paragraphs */
368 *Size = (MaxSize >> 4);
369 return FALSE;
370 }
371 }
372
373 BOOLEAN UmaMgrInitialize(VOID)
374 {
375 // See bios/rom.h
376 #define ROM_AREA_START 0xE0000
377 #define ROM_AREA_END 0xFFFFF
378
379 #define OPTION_ROM_SIGNATURE 0xAA55
380
381 PUMA_DESCRIPTOR UmaDesc = NULL;
382 ULONG StartAddress = 0;
383 ULONG Size = 0;
384 UMA_DESC_TYPE Type = UMA_FREE;
385
386 UINT i;
387
388 ULONG Start, End;
389 ULONG Increment;
390
391 ULONG Address;
392
393 // ULONG RomStart[] = {};
394 // ULONG RomEnd[] = {};
395 ULONG RomBoundaries[] = {0xA0000, 0xC0000, /*0xC8000, 0xE0000,*/ 0xF0000, 0x100000};
396 ULONG SizeIncrement[] = {0x20000, 0x00800, /*0x00800, 0x10000,*/ 0x10000, 0x0000 };
397
398 // InitializeListHead(&UmaDescriptorList);
399
400 /* NOTE: There must be always one UMA descriptor at least */
401 // FIXME: Maybe it should be a static object?
402
403 for (i = 0; i < ARRAYSIZE(RomBoundaries) - 1; i++)
404 {
405 Start = RomBoundaries[i]; // RomStart[i]
406 End = RomBoundaries[i+1]; // RomEnd[i]
407 Increment = SizeIncrement[i];
408
409 for (Address = Start; Address < End; Address += Increment)
410 {
411 if (StartAddress == 0)
412 {
413 /* Initialize data for a new descriptor */
414 StartAddress = Address;
415 Size = 0;
416 Type = UMA_FREE;
417 }
418
419 /* Is it a normal system zone/user-excluded zone? */
420 if (Address >= 0xA0000 && Address < 0xC0000)
421 {
422 // StartAddress = Address;
423 Size = Increment;
424 Type = UMA_SYSTEM;
425
426 /* Create descriptor */
427 UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type);
428 if (!UmaDesc) return FALSE;
429
430 StartAddress = 0;
431 continue;
432 }
433 /* Is it the PC ROM BIOS? */
434 else if (Address >= 0xF0000)
435 {
436 // StartAddress = Address;
437 Size = 0x10000; // Increment;
438 Type = UMA_ROM;
439
440 /* Create descriptor */
441 UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type);
442 if (!UmaDesc) return FALSE;
443
444 StartAddress = 0;
445 continue;
446 }
447 /* Is it an option ROM? */
448 else if (Address >= 0xC0000 && Address < 0xF0000)
449 {
450 ULONG RomSize;
451 ULONG PrevRomAddress = 0;
452 ULONG PrevRomSize = 0;
453
454 // while (Address < 0xF0000)
455 for (; Address < 0xF0000; Address += Increment)
456 {
457
458 #if 0 // FIXME: Is this block, better here...
459 {
460 // We may be looping inside a ROM block, if: Type == 2 and:
461 // (StartAddress <= Address &&) StartAddress + Size > Address.
462 // In this case, do nothing (do not increase size either)
463 // But if we are now outside of a ROM block, then we need
464 // to create the previous block, and initialize a new empty one!
465 // (and following the next passages, increase its size).
466
467 // if (StartAddress < Address && Type != 2)
468 if (Type == UMA_ROM && StartAddress + Size > Address)
469 {
470 /* We are inside ROM, do nothing */
471 }
472 else if (Type == UMA_ROM && StartAddress + Size <= Address)
473 {
474 /* We finished a ROM descriptor */
475
476 /* Create descriptor */
477 UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type);
478 if (!UmaDesc) return FALSE;
479
480 StartAddress = 0;
481 // goto Restart;
482 }
483 else if (Type != UMA_ROM)
484 {
485 Size += Increment;
486 }
487 }
488 #endif
489
490 Restart:
491 /// if (Address >= 0xE0000) { Increment = 0x10000; }
492
493 if (StartAddress == 0)
494 {
495 /* Initialize data for a new descriptor */
496 StartAddress = Address;
497 Size = 0;
498 Type = UMA_FREE;
499
500 PrevRomAddress = 0;
501 PrevRomSize = 0;
502 }
503
504 if (*(PUSHORT)REAL_TO_PHYS(Address) == OPTION_ROM_SIGNATURE)
505 {
506 /*
507 * If this is an adapter ROM (Start: C8000, End: E0000),
508 * its reported size is stored in byte 2 of the ROM.
509 *
510 * If this is an expansion ROM (Start: E0000, End: F0000),
511 * its real length is 64kB.
512 */
513 RomSize = *(PUCHAR)REAL_TO_PHYS(Address + 2) * 512;
514 // if (Address >= 0xE0000) RomSize = 0x10000;
515 if (Address >= 0xE0000) { RomSize = 0x10000; Increment = RomSize; }
516
517 DPRINT1("ROM present @ address 0x%p\n", Address);
518
519 if (StartAddress != 0 && Size != 0 &&
520 StartAddress + Size <= Address)
521 {
522 /* Finish old descriptor */
523 UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type);
524 if (!UmaDesc) return FALSE;
525 }
526
527 /*
528 * We may have overlapping ROMs, when PrevRomAddress + PrevRomSize > RomAddress.
529 * They must be put inside the same UMA descriptor.
530 */
531 if (PrevRomAddress + PrevRomSize > /*Rom*/Address)
532 {
533 // Overlapping ROM
534
535 /*
536 * PrevRomAddress remains the same, but adjust
537 * PrevRomSize (ROM descriptors merging).
538 */
539 PrevRomSize = max(PrevRomSize, RomSize + Address - PrevRomAddress);
540
541 // FIX: Confirm that the start address is really
542 // the one of the previous descriptor.
543 StartAddress = PrevRomAddress;
544 Size = PrevRomSize;
545 Type = UMA_ROM;
546 }
547 else
548 {
549 // Non-overlapping ROM
550
551 PrevRomAddress = Address;
552 PrevRomSize = RomSize;
553
554 /* Initialize a new descriptor. We will create it when it's OK */
555 StartAddress = Address;
556 Size = RomSize;
557 Type = UMA_ROM;
558 // continue;
559 }
560 }
561 #if 1 // FIXME: ...or there??
562 else
563 {
564 // We may be looping inside a ROM block, if: Type == 2 and:
565 // (StartAddress <= Address &&) StartAddress + Size > Address.
566 // In this case, do nothing (do not increase size either)
567 // But if we are now outside of a ROM block, then we need
568 // to create the previous block, and initialize a new empty one!
569 // (and following the next passages, increase its size).
570
571 // if (StartAddress < Address && Type != UMA_ROM)
572 if (Type == UMA_ROM && StartAddress + Size > Address)
573 {
574 // We are inside ROM, do nothing
575 }
576 else if (Type == UMA_ROM && StartAddress + Size <= Address)
577 {
578 // We finished a ROM descriptor.
579
580 /* Create descriptor */
581 UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type);
582 if (!UmaDesc) return FALSE;
583
584 StartAddress = 0;
585 goto Restart;
586 }
587 else if (Type != UMA_ROM)
588 {
589 Size += Increment;
590 }
591 }
592 #endif
593
594 // Fixed incroment; we may encounter again overlapping ROMs, etc.
595 // Address += Increment;
596 }
597
598 if (StartAddress != 0 && Size != 0)
599 {
600 /* Create descriptor */
601 UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type);
602 if (!UmaDesc) return FALSE;
603
604 StartAddress = 0;
605 }
606
607 }
608 }
609 }
610
611 return TRUE;
612 }
613
614 VOID UmaMgrCleanup(VOID)
615 {
616 PUMA_DESCRIPTOR UmaDesc;
617
618 while (!IsListEmpty(&UmaDescriptorList))
619 {
620 UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(UmaDescriptorList.Flink, UMA_DESCRIPTOR, Entry);
621 FreeUmaDescriptor(UmaDesc);
622 }
623 }
624
625 /* EOF */