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)
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.
12 /* INCLUDES *******************************************************************/
24 /* PRIVATE VARIABLES **********************************************************/
26 typedef struct _UMA_DESCRIPTOR
32 } UMA_DESCRIPTOR
, *PUMA_DESCRIPTOR
;
35 * Sorted list of UMA descriptors.
37 * The descriptor list is (and must always be) sorted by memory addresses,
38 * and all consecutive free descriptors are always merged together, so that
39 * free ones are always separated from each other by descriptors of other types.
41 * TODO: Add the fact that no blocks of size == 0 are allowed.
43 static LIST_ENTRY UmaDescriptorList
= { &UmaDescriptorList
, &UmaDescriptorList
};
45 /* PRIVATE FUNCTIONS **********************************************************/
47 static PUMA_DESCRIPTOR
48 CreateUmaDescriptor(IN OUT PLIST_ENTRY ListHead
,
51 IN UMA_DESC_TYPE Type
)
53 PUMA_DESCRIPTOR UmaDesc
;
57 UmaDesc
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*UmaDesc
));
58 if (!UmaDesc
) return NULL
;
60 UmaDesc
->Start
= Address
;
65 * We use the trick of http://www.osronline.com/article.cfm?article=499 to insert
66 * the new descriptor just after the current entry that we specify via 'ListHead'.
67 * If 'ListHead' is NULL then we insert the descriptor at the tail of 'UmaDescriptorList'
68 * (which is equivalent to inserting it at the head of 'UmaDescriptorList.Blink').
70 if (ListHead
== NULL
) ListHead
= UmaDescriptorList
.Blink
;
71 InsertHeadList(ListHead
, &UmaDesc
->Entry
);
76 static VOID
FreeUmaDescriptor(PUMA_DESCRIPTOR UmaDesc
)
78 RemoveEntryList(&UmaDesc
->Entry
);
79 RtlFreeHeap(RtlGetProcessHeap(), 0, UmaDesc
);
82 /* PUBLIC FUNCTIONS ***********************************************************/
84 BOOLEAN
UmaDescReserve(IN OUT PUSHORT Segment
, IN OUT PUSHORT Size
)
86 ULONG Address
= (*Segment
<< 4); // Convert segment number into address.
87 ULONG RequestSize
= (*Size
<< 4); // Convert size in paragraphs into size in bytes.
90 PUMA_DESCRIPTOR UmaDesc
, NewUmaDesc
;
91 PUMA_DESCRIPTOR FoundUmaDesc
= NULL
;
93 // FIXME: Check! What to do?
94 if (RequestSize
== 0) DPRINT1("Requesting UMA descriptor with null size?!\n");
96 Entry
= UmaDescriptorList
.Flink
;
97 while (Entry
!= &UmaDescriptorList
)
99 UmaDesc
= (PUMA_DESCRIPTOR
)CONTAINING_RECORD(Entry
, UMA_DESCRIPTOR
, Entry
);
100 Entry
= Entry
->Flink
;
102 /* Only check free descriptors */
103 if (UmaDesc
->Type
!= UMA_FREE
) continue;
105 /* Update the maximum descriptor size */
106 if (UmaDesc
->Size
> MaxSize
) MaxSize
= UmaDesc
->Size
;
108 /* Descriptor too small, continue... */
109 if (UmaDesc
->Size
< RequestSize
) continue;
111 /* Do we want to reserve the descriptor at a precise address? */
114 /* If the descriptor ends before the desired region, try again */
115 if (UmaDesc
->Start
+ UmaDesc
->Size
<= Address
) continue;
118 * If we have a descriptor, but one of its boundaries crosses the
119 * desired region (it starts after the desired region, or ends
120 * before the end of the desired region), this means that there
121 * is already something inside the region, so that we cannot
122 * allocate the region here. Bail out.
124 if (UmaDesc
->Start
> Address
||
125 UmaDesc
->Start
+ UmaDesc
->Size
< Address
+ RequestSize
)
130 /* We now have a free descriptor that overlaps our desired region: split it */
133 * Here, UmaDesc->Start == Address or UmaDesc->Start < Address,
134 * in which case we need to split the descriptor to the left.
136 if (UmaDesc
->Start
< Address
)
138 /* Create a new free descriptor and insert it after the current one */
139 NewUmaDesc
= CreateUmaDescriptor(&UmaDesc
->Entry
,
141 UmaDesc
->Size
- (Address
- UmaDesc
->Start
),
142 UmaDesc
->Type
); // UMA_FREE
145 DPRINT1("CreateUmaDescriptor failed, UMA descriptor list possibly corrupted!\n");
149 /* Reduce the size of the splitted left descriptor */
150 UmaDesc
->Size
= (Address
- UmaDesc
->Start
);
152 /* Current descriptor is now the new created one */
153 UmaDesc
= NewUmaDesc
;
156 /* Here, UmaDesc->Start == Address */
159 /* Descriptor of large enough size: split it to the right if needed */
160 // FIXME: It might be needed to consider a minimum size starting which we need to split.
161 // if (UmaDesc->Size - RequestSize > (3 << 4))
162 if (UmaDesc
->Size
> RequestSize
)
165 * Create a new free descriptor and insert it after the current one.
166 * Because consecutive free descriptors are always merged together,
167 * the descriptor following 'UmaDesc' cannot be free, so that this
168 * new free descriptor does not need to be merged with some others.
170 NewUmaDesc
= CreateUmaDescriptor(&UmaDesc
->Entry
,
171 UmaDesc
->Start
+ RequestSize
,
172 UmaDesc
->Size
- RequestSize
,
173 UmaDesc
->Type
); // UMA_FREE
176 DPRINT1("CreateUmaDescriptor failed, UMA descriptor list possibly corrupted!\n");
180 /* Reduce the size of the splitted left descriptor */
181 UmaDesc
->Size
= RequestSize
;
184 /* We have a descriptor of correct size, initialize it */
185 UmaDesc
->Type
= UMA_UMB
;
186 FoundUmaDesc
= UmaDesc
;
192 /* Returned address is a segment and size is in paragraphs */
193 *Segment
= (FoundUmaDesc
->Start
>> 4);
194 *Size
= (FoundUmaDesc
->Size
>> 4);
200 /* Returned address is a segment and size is in paragraphs */
202 *Size
= (MaxSize
>> 4);
207 BOOLEAN
UmaDescRelease(IN USHORT Segment
)
209 ULONG Address
= (Segment
<< 4); // Convert segment number into address.
210 PLIST_ENTRY Entry
, PrevEntry
, NextEntry
;
211 PUMA_DESCRIPTOR UmaDesc
, PrevDesc
= NULL
, NextDesc
= NULL
;
212 PUMA_DESCRIPTOR FoundUmaDesc
= NULL
;
214 Entry
= UmaDescriptorList
.Flink
;
215 while (Entry
!= &UmaDescriptorList
)
217 UmaDesc
= (PUMA_DESCRIPTOR
)CONTAINING_RECORD(Entry
, UMA_DESCRIPTOR
, Entry
);
218 Entry
= Entry
->Flink
;
220 /* Search for the descriptor in the list */
221 if (UmaDesc
->Start
== Address
&& UmaDesc
->Type
== UMA_UMB
)
224 FoundUmaDesc
= UmaDesc
;
231 FoundUmaDesc
->Type
= UMA_FREE
;
233 /* Combine free descriptors adjacent to this one */
234 PrevEntry
= FoundUmaDesc
->Entry
.Blink
;
235 NextEntry
= FoundUmaDesc
->Entry
.Flink
;
237 if (PrevEntry
!= &UmaDescriptorList
)
238 PrevDesc
= (PUMA_DESCRIPTOR
)CONTAINING_RECORD(PrevEntry
, UMA_DESCRIPTOR
, Entry
);
239 if (NextEntry
!= &UmaDescriptorList
)
240 NextDesc
= (PUMA_DESCRIPTOR
)CONTAINING_RECORD(NextEntry
, UMA_DESCRIPTOR
, Entry
);
242 if (NextDesc
&& NextDesc
->Type
== FoundUmaDesc
->Type
) // UMA_FREE
244 FoundUmaDesc
->Size
+= NextDesc
->Size
;
245 FreeUmaDescriptor(NextDesc
);
248 if (PrevDesc
&& PrevDesc
->Type
== FoundUmaDesc
->Type
) // UMA_FREE
250 PrevDesc
->Size
+= FoundUmaDesc
->Size
;
251 FreeUmaDescriptor(FoundUmaDesc
);
260 BOOLEAN
UmaDescReallocate(IN USHORT Segment
, IN OUT PUSHORT Size
)
262 ULONG Address
= (Segment
<< 4); // Convert segment number into address.
263 ULONG RequestSize
= (*Size
<< 4); // Convert size in paragraphs into size in bytes.
265 PLIST_ENTRY Entry
, NextEntry
;
266 PUMA_DESCRIPTOR UmaDesc
, NextDesc
= NULL
, NewUmaDesc
;
267 PUMA_DESCRIPTOR FoundUmaDesc
= NULL
;
269 // FIXME: Check! What to do?
270 if (RequestSize
== 0) DPRINT1("Resizing UMA descriptor %04X to null size?!\n", Segment
);
272 Entry
= UmaDescriptorList
.Flink
;
273 while (Entry
!= &UmaDescriptorList
)
275 UmaDesc
= (PUMA_DESCRIPTOR
)CONTAINING_RECORD(Entry
, UMA_DESCRIPTOR
, Entry
);
276 Entry
= Entry
->Flink
;
278 /* Only get the maximum size of free descriptors */
279 if (UmaDesc
->Type
== UMA_FREE
)
281 /* Update the maximum descriptor size */
282 if (UmaDesc
->Size
> MaxSize
) MaxSize
= UmaDesc
->Size
;
285 /* Search for the descriptor in the list */
286 if (UmaDesc
->Start
== Address
&& UmaDesc
->Type
== UMA_UMB
)
289 FoundUmaDesc
= UmaDesc
;
296 /* If we do not resize anything, just quit with success */
297 if (FoundUmaDesc
->Size
== RequestSize
) goto Success
;
299 NextEntry
= FoundUmaDesc
->Entry
.Flink
;
301 if (NextEntry
!= &UmaDescriptorList
)
302 NextDesc
= (PUMA_DESCRIPTOR
)CONTAINING_RECORD(NextEntry
, UMA_DESCRIPTOR
, Entry
);
304 /* Check for reduction or enlargement */
305 if (FoundUmaDesc
->Size
> RequestSize
)
310 * Check if the next descriptor is free, in which case we
311 * extend it, otherwise we create a new free descriptor.
313 if (NextDesc
&& NextDesc
->Type
== UMA_FREE
)
315 /* Yes it is, expand its size and move it down */
316 NextDesc
->Size
+= (FoundUmaDesc
->Size
- RequestSize
);
317 NextDesc
->Start
-= (FoundUmaDesc
->Size
- RequestSize
);
321 // FIXME: It might be needed to consider a minimum size starting which we need to split.
322 // if (FoundUmaDesc->Size - RequestSize > (3 << 4))
324 /* Create a new free descriptor and insert it after the current one */
325 NewUmaDesc
= CreateUmaDescriptor(&FoundUmaDesc
->Entry
,
326 FoundUmaDesc
->Start
+ RequestSize
,
327 FoundUmaDesc
->Size
- RequestSize
,
331 DPRINT1("CreateUmaDescriptor failed, UMA descriptor list possibly corrupted!\n");
337 else // if (FoundUmaDesc->Size <= RequestSize)
341 /* Check whether the next descriptor is free and large enough for merging */
342 if (NextDesc
&& NextDesc
->Type
== UMA_FREE
&&
343 FoundUmaDesc
->Size
+ NextDesc
->Size
>= RequestSize
)
345 /* Yes it is, reduce its size and move it up, and enlarge the reallocated descriptor */
346 NextDesc
->Size
-= (RequestSize
- FoundUmaDesc
->Size
);
347 NextDesc
->Start
+= (RequestSize
- FoundUmaDesc
->Size
);
349 if (NextDesc
->Size
== 0) FreeUmaDescriptor(NextDesc
);
358 /* Finally, resize the descriptor */
359 FoundUmaDesc
->Size
= RequestSize
;
362 /* Returned size is in paragraphs */
363 *Size
= (FoundUmaDesc
->Size
>> 4);
369 /* Returned size is in paragraphs */
370 *Size
= (MaxSize
>> 4);
375 BOOLEAN
UmaMgrInitialize(VOID
)
378 #define ROM_AREA_START 0xE0000
379 #define ROM_AREA_END 0xFFFFF
381 #define OPTION_ROM_SIGNATURE 0xAA55
383 PUMA_DESCRIPTOR UmaDesc
= NULL
;
384 ULONG StartAddress
= 0;
386 UMA_DESC_TYPE Type
= UMA_FREE
;
395 // ULONG RomStart[] = {};
396 // ULONG RomEnd[] = {};
397 ULONG RomBoundaries
[] = {0xA0000, 0xC0000, /*0xC8000, 0xE0000,*/ 0xF0000, 0x100000};
398 ULONG SizeIncrement
[] = {0x20000, 0x00800, /*0x00800, 0x10000,*/ 0x10000, 0x0000 };
400 // InitializeListHead(&UmaDescriptorList);
402 /* NOTE: There must be always one UMA descriptor at least */
403 // FIXME: Maybe it should be a static object?
405 for (i
= 0; i
< ARRAYSIZE(RomBoundaries
) - 1; i
++)
407 Start
= RomBoundaries
[i
]; // RomStart[i]
408 End
= RomBoundaries
[i
+1]; // RomEnd[i]
409 Increment
= SizeIncrement
[i
];
411 for (Address
= Start
; Address
< End
; Address
+= Increment
)
413 if (StartAddress
== 0)
415 /* Initialize data for a new descriptor */
416 StartAddress
= Address
;
421 /* Is it a normal system zone/user-excluded zone? */
422 if (Address
>= 0xA0000 && Address
< 0xC0000)
424 // StartAddress = Address;
428 /* Create descriptor */
429 UmaDesc
= CreateUmaDescriptor(NULL
, StartAddress
, Size
, Type
);
430 if (!UmaDesc
) return FALSE
;
435 /* Is it the PC ROM BIOS? */
436 else if (Address
>= 0xF0000)
438 // StartAddress = Address;
439 Size
= 0x10000; // Increment;
442 /* Create descriptor */
443 UmaDesc
= CreateUmaDescriptor(NULL
, StartAddress
, Size
, Type
);
444 if (!UmaDesc
) return FALSE
;
449 /* Is it an option ROM? */
450 else if (Address
>= 0xC0000 && Address
< 0xF0000)
453 ULONG PrevRomAddress
= 0;
454 ULONG PrevRomSize
= 0;
456 // while (Address < 0xF0000)
457 for (; Address
< 0xF0000; Address
+= Increment
)
460 #if 0 // FIXME: Is this block, better here...
462 // We may be looping inside a ROM block, if: Type == 2 and:
463 // (StartAddress <= Address &&) StartAddress + Size > Address.
464 // In this case, do nothing (do not increase size either)
465 // But if we are now outside of a ROM block, then we need
466 // to create the previous block, and initialize a new empty one!
467 // (and following the next passages, increase its size).
469 // if (StartAddress < Address && Type != 2)
470 if (Type
== UMA_ROM
&& StartAddress
+ Size
> Address
)
472 /* We are inside ROM, do nothing */
474 else if (Type
== UMA_ROM
&& StartAddress
+ Size
<= Address
)
476 /* We finished a ROM descriptor */
478 /* Create descriptor */
479 UmaDesc
= CreateUmaDescriptor(NULL
, StartAddress
, Size
, Type
);
480 if (!UmaDesc
) return FALSE
;
485 else if (Type
!= UMA_ROM
)
493 /// if (Address >= 0xE0000) { Increment = 0x10000; }
495 if (StartAddress
== 0)
497 /* Initialize data for a new descriptor */
498 StartAddress
= Address
;
506 if (*(PUSHORT
)REAL_TO_PHYS(Address
) == OPTION_ROM_SIGNATURE
)
509 * If this is an adapter ROM (Start: C8000, End: E0000),
510 * its reported size is stored in byte 2 of the ROM.
512 * If this is an expansion ROM (Start: E0000, End: F0000),
513 * its real length is 64kB.
515 RomSize
= *(PUCHAR
)REAL_TO_PHYS(Address
+ 2) * 512;
516 // if (Address >= 0xE0000) RomSize = 0x10000;
517 if (Address
>= 0xE0000) { RomSize
= 0x10000; Increment
= RomSize
; }
519 DPRINT1("ROM present @ address 0x%p\n", Address
);
521 if (StartAddress
!= 0 && Size
!= 0 &&
522 StartAddress
+ Size
<= Address
)
524 /* Finish old descriptor */
525 UmaDesc
= CreateUmaDescriptor(NULL
, StartAddress
, Size
, Type
);
526 if (!UmaDesc
) return FALSE
;
530 * We may have overlapping ROMs, when PrevRomAddress + PrevRomSize > RomAddress.
531 * They must be put inside the same UMA descriptor.
533 if (PrevRomAddress
+ PrevRomSize
> /*Rom*/Address
)
538 * PrevRomAddress remains the same, but adjust
539 * PrevRomSize (ROM descriptors merging).
541 PrevRomSize
= max(PrevRomSize
, RomSize
+ Address
- PrevRomAddress
);
543 // FIX: Confirm that the start address is really
544 // the one of the previous descriptor.
545 StartAddress
= PrevRomAddress
;
551 // Non-overlapping ROM
553 PrevRomAddress
= Address
;
554 PrevRomSize
= RomSize
;
556 /* Initialize a new descriptor. We will create it when it's OK */
557 StartAddress
= Address
;
563 #if 1 // FIXME: ...or there??
566 // We may be looping inside a ROM block, if: Type == 2 and:
567 // (StartAddress <= Address &&) StartAddress + Size > Address.
568 // In this case, do nothing (do not increase size either)
569 // But if we are now outside of a ROM block, then we need
570 // to create the previous block, and initialize a new empty one!
571 // (and following the next passages, increase its size).
573 // if (StartAddress < Address && Type != UMA_ROM)
574 if (Type
== UMA_ROM
&& StartAddress
+ Size
> Address
)
576 // We are inside ROM, do nothing
578 else if (Type
== UMA_ROM
&& StartAddress
+ Size
<= Address
)
580 // We finished a ROM descriptor.
582 /* Create descriptor */
583 UmaDesc
= CreateUmaDescriptor(NULL
, StartAddress
, Size
, Type
);
584 if (!UmaDesc
) return FALSE
;
589 else if (Type
!= UMA_ROM
)
596 // Fixed incroment; we may encounter again overlapping ROMs, etc.
597 // Address += Increment;
600 if (StartAddress
!= 0 && Size
!= 0)
602 /* Create descriptor */
603 UmaDesc
= CreateUmaDescriptor(NULL
, StartAddress
, Size
, Type
);
604 if (!UmaDesc
) return FALSE
;
616 VOID
UmaMgrCleanup(VOID
)
618 PUMA_DESCRIPTOR UmaDesc
;
620 while (!IsListEmpty(&UmaDescriptorList
))
622 UmaDesc
= (PUMA_DESCRIPTOR
)CONTAINING_RECORD(UmaDescriptorList
.Flink
, UMA_DESCRIPTOR
, Entry
);
623 FreeUmaDescriptor(UmaDesc
);