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