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