diff --git a/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt b/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt index 33252b27310..5765e76a125 100644 --- a/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt +++ b/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt @@ -14,6 +14,7 @@ list(APPEND SOURCE bios/bios.c bios/kbdbios.c bios/rom.c + bios/umamgr.c bios/vidbios.c cpu/bop.c cpu/callback.c @@ -40,8 +41,8 @@ list(APPEND SOURCE dos/dos32krnl/himem.c dos/dos32krnl/memory.c dos/dos32krnl/process.c - dos/mouse32.c dos/dem.c + dos/mouse32.c clock.c emulator.c int32.c diff --git a/reactos/subsystems/mvdm/ntvdm/bios/bios.c b/reactos/subsystems/mvdm/ntvdm/bios/bios.c index ccf4aa206aa..3043e092c78 100644 --- a/reactos/subsystems/mvdm/ntvdm/bios/bios.c +++ b/reactos/subsystems/mvdm/ntvdm/bios/bios.c @@ -17,8 +17,8 @@ #include "bios.h" #include "bios32/bios32.h" - #include "rom.h" +#include "umamgr.h" #include "io.h" #include "hardware/cmos.h" @@ -87,22 +87,22 @@ BiosInitialize(IN LPCSTR BiosFileName) DisplayMessage(L"First bytes at 0x%p: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n" L"3 last bytes at 0x%p: 0x%02x 0x%02x 0x%02x", - BiosLocation, - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 0), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 1), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 2), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 3), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 4), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 5), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 6), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 7), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 8), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 9), + BiosLocation, + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 0), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 1), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 2), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 3), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 4), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 5), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 6), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 7), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 8), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + 9), - (PVOID)((ULONG_PTR)BiosLocation + BiosSize - 2), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 2), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 1), - *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 0)); + (PVOID)((ULONG_PTR)BiosLocation + BiosSize - 2), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 2), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 1), + *(PCHAR)((ULONG_PTR)REAL_TO_PHYS(BiosLocation) + BiosSize - 0)); DisplayMessage(L"POST at 0x%p: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", TO_LINEAR(getCS(), getIP()), @@ -135,12 +135,21 @@ BiosInitialize(IN LPCSTR BiosFileName) // /* Enable interrupts */ // setIF(1); + /* Initialize the Upper Memory Area Manager */ + if (!UmaMgrInitialize()) + { + wprintf(L"FATAL: Failed to initialize the UMA manager.\n"); + return FALSE; + } + return Success; } VOID BiosCleanup(VOID) { + UmaMgrCleanup(); + if (Bios32Loaded) Bios32Cleanup(); } diff --git a/reactos/subsystems/mvdm/ntvdm/bios/umamgr.c b/reactos/subsystems/mvdm/ntvdm/bios/umamgr.c new file mode 100644 index 00000000000..258b9eafa38 --- /dev/null +++ b/reactos/subsystems/mvdm/ntvdm/bios/umamgr.c @@ -0,0 +1,625 @@ +/* + * COPYRIGHT: GPLv2+ - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: umamgr.c + * PURPOSE: Upper Memory Area Manager + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * + * NOTE: The UMA Manager is used by the DOS XMS Driver (UMB Provider part), + * indirectly by the DOS EMS Driver, and by VDD memory management functions. + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "ntvdm.h" +#include "emulator.h" +#include "memory.h" + +#include "umamgr.h" + +/* PRIVATE VARIABLES **********************************************************/ + +typedef struct _UMA_DESCRIPTOR +{ + LIST_ENTRY Entry; + ULONG Start; + ULONG Size; + UMA_DESC_TYPE Type; +} UMA_DESCRIPTOR, *PUMA_DESCRIPTOR; + +/* + * Sorted list of UMA descriptors. + * + * The descriptor list is (and must always be) sorted by memory addresses, + * and all consecutive free descriptors are always merged together, so that + * free ones are always separated from each other by descriptors of other types. + * + * TODO: Add the fact that no blocks of size == 0 are allowed. + */ +static LIST_ENTRY UmaDescriptorList = { &UmaDescriptorList, &UmaDescriptorList }; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static PUMA_DESCRIPTOR +CreateUmaDescriptor(IN OUT PLIST_ENTRY ListHead, + IN ULONG Address, + IN ULONG Size, + IN UMA_DESC_TYPE Type) +{ + PUMA_DESCRIPTOR UmaDesc; + + ASSERT(Size > 0); + + UmaDesc = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*UmaDesc)); + if (!UmaDesc) return NULL; + + UmaDesc->Start = Address; + UmaDesc->Size = Size; + UmaDesc->Type = Type; + + /* + * We use the trick of http://www.osronline.com/article.cfm?article=499 to insert + * the new descriptor just after the current entry that we specify via 'ListHead'. + * If 'ListHead' is NULL then we insert the descriptor at the tail of 'UmaDescriptorList' + * (which is equivalent to inserting it at the head of 'UmaDescriptorList.Blink'). + */ + if (ListHead == NULL) ListHead = UmaDescriptorList.Blink; + InsertHeadList(ListHead, &UmaDesc->Entry); + + return UmaDesc; +} + +static VOID FreeUmaDescriptor(PUMA_DESCRIPTOR UmaDesc) +{ + RemoveEntryList(&UmaDesc->Entry); + RtlFreeHeap(RtlGetProcessHeap(), 0, UmaDesc); +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +BOOLEAN UmaDescReserve(IN OUT PUSHORT Segment, IN OUT PUSHORT Size) +{ + ULONG Address = (*Segment << 4); // Convert segment number into address. + ULONG RequestSize = (*Size << 4); // Convert size in paragraphs into size in bytes. + ULONG MaxSize = 0; + PLIST_ENTRY Entry; + PUMA_DESCRIPTOR UmaDesc, NewUmaDesc; + PUMA_DESCRIPTOR FoundUmaDesc = NULL; + + // FIXME: Check! What to do? + if (RequestSize == 0) DPRINT1("Requesting UMA descriptor with null size?!\n"); + + for (Entry = UmaDescriptorList.Flink; + Entry != &UmaDescriptorList; + Entry = Entry->Flink) + { + UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(Entry, UMA_DESCRIPTOR, Entry); + + /* Only check free descriptors */ + if (UmaDesc->Type != UMA_FREE) continue; + + /* Update the maximum descriptor size */ + if (UmaDesc->Size > MaxSize) MaxSize = UmaDesc->Size; + + /* Descriptor too small, continue... */ + if (UmaDesc->Size < RequestSize) continue; + + /* Do we want to reserve the descriptor at a precise address? */ + if (Address) + { + /* If the descriptor ends before the desired region, try again */ + if (UmaDesc->Start + UmaDesc->Size <= Address) continue; + + /* + * If we have a descriptor, but one of its boundaries crosses the + * desired region (it starts after the desired region, or ends + * before the end of the desired region), this means that there + * is already something inside the region, so that we cannot + * allocate the region here. Bail out. + */ + if (UmaDesc->Start > Address || + UmaDesc->Start + UmaDesc->Size < Address + RequestSize) + { + goto Fail; + } + + /* We now have a free descriptor that overlaps our desired region: split it */ + + /* + * Here, UmaDesc->Start == Address or UmaDesc->Start < Address, + * in which case we need to split the descriptor to the left. + */ + if (UmaDesc->Start < Address) + { + /* Create a new free descriptor and insert it after the current one */ + NewUmaDesc = CreateUmaDescriptor(&UmaDesc->Entry, + Address, + UmaDesc->Size - (Address - UmaDesc->Start), + UmaDesc->Type); // UMA_FREE + if (!NewUmaDesc) + { + DPRINT1("CreateUmaDescriptor failed, UMA descriptor list possibly corrupted!\n"); + goto Fail; + } + + /* Reduce the size of the splitted left descriptor */ + UmaDesc->Size = (Address - UmaDesc->Start); + + /* Current descriptor is now the new created one */ + UmaDesc = NewUmaDesc; + } + + /* Here, UmaDesc->Start == Address */ + } + + /* Descriptor of large enough size: split it to the right if needed */ + // FIXME: It might be needed to consider a minimum size starting which we need to split. + // if (UmaDesc->Size - RequestSize > (3 << 4)) + if (UmaDesc->Size > RequestSize) + { + /* + * Create a new free descriptor and insert it after the current one. + * Because consecutive free descriptors are always merged together, + * the descriptor following 'UmaDesc' cannot be free, so that this + * new free descriptor does not need to be merged with some others. + */ + NewUmaDesc = CreateUmaDescriptor(&UmaDesc->Entry, + UmaDesc->Start + RequestSize, + UmaDesc->Size - RequestSize, + UmaDesc->Type); // UMA_FREE + if (!NewUmaDesc) + { + DPRINT1("CreateUmaDescriptor failed, UMA descriptor list possibly corrupted!\n"); + goto Fail; + } + + /* Reduce the size of the splitted left descriptor */ + UmaDesc->Size = RequestSize; + } + + /* We have a descriptor of correct size, initialize it */ + UmaDesc->Type = UMA_UMB; + FoundUmaDesc = UmaDesc; + break; + } + + if (FoundUmaDesc) + { + /* Returned address is a segment and size is in paragraphs */ + *Segment = (FoundUmaDesc->Start >> 4); + *Size = (FoundUmaDesc->Size >> 4); + return TRUE; + } + else + { +Fail: + /* Returned address is a segment and size is in paragraphs */ + *Segment = 0x0000; + *Size = (MaxSize >> 4); + return FALSE; + } +} + +BOOLEAN UmaDescRelease(IN USHORT Segment) +{ + ULONG Address = (Segment << 4); // Convert segment number into address. + PLIST_ENTRY Entry, PrevEntry, NextEntry; + PUMA_DESCRIPTOR UmaDesc, PrevDesc = NULL, NextDesc = NULL; + PUMA_DESCRIPTOR FoundUmaDesc = NULL; + + for (Entry = UmaDescriptorList.Flink; + Entry != &UmaDescriptorList; + Entry = Entry->Flink) + { + UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(Entry, UMA_DESCRIPTOR, Entry); + + /* Search for the descriptor in the list */ + if (UmaDesc->Start == Address && UmaDesc->Type == UMA_UMB) + { + /* We found it */ + FoundUmaDesc = UmaDesc; + break; + } + } + + if (FoundUmaDesc) + { + FoundUmaDesc->Type = UMA_FREE; + + /* Combine free descriptors adjacent to this one */ + PrevEntry = FoundUmaDesc->Entry.Blink; + NextEntry = FoundUmaDesc->Entry.Flink; + + if (PrevEntry != &UmaDescriptorList) + PrevDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(PrevEntry, UMA_DESCRIPTOR, Entry); + if (NextEntry != &UmaDescriptorList) + NextDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(NextEntry, UMA_DESCRIPTOR, Entry); + + if (NextDesc && NextDesc->Type == FoundUmaDesc->Type) // UMA_FREE + { + FoundUmaDesc->Size += NextDesc->Size; + FreeUmaDescriptor(NextDesc); + } + + if (PrevDesc && PrevDesc->Type == FoundUmaDesc->Type) // UMA_FREE + { + PrevDesc->Size += FoundUmaDesc->Size; + FreeUmaDescriptor(FoundUmaDesc); + } + + return TRUE; + } + + return FALSE; +} + +BOOLEAN UmaDescReallocate(IN USHORT Segment, IN OUT PUSHORT Size) +{ + ULONG Address = (Segment << 4); // Convert segment number into address. + ULONG RequestSize = (*Size << 4); // Convert size in paragraphs into size in bytes. + ULONG MaxSize = 0; + PLIST_ENTRY Entry, NextEntry; + PUMA_DESCRIPTOR UmaDesc, NextDesc = NULL, NewUmaDesc; + PUMA_DESCRIPTOR FoundUmaDesc = NULL; + + // FIXME: Check! What to do? + if (RequestSize == 0) DPRINT1("Resizing UMA descriptor %04X to null size?!\n", Segment); + + for (Entry = UmaDescriptorList.Flink; + Entry != &UmaDescriptorList; + Entry = Entry->Flink) + { + UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(Entry, UMA_DESCRIPTOR, Entry); + + /* Only get the maximum size of free descriptors */ + if (UmaDesc->Type == UMA_FREE) + { + /* Update the maximum descriptor size */ + if (UmaDesc->Size > MaxSize) MaxSize = UmaDesc->Size; + } + + /* Search for the descriptor in the list */ + if (UmaDesc->Start == Address && UmaDesc->Type == UMA_UMB) + { + /* We found it */ + FoundUmaDesc = UmaDesc; + break; + } + } + + if (FoundUmaDesc) + { + /* If we do not resize anything, just quit with success */ + if (FoundUmaDesc->Size == RequestSize) goto Success; + + NextEntry = FoundUmaDesc->Entry.Flink; + + if (NextEntry != &UmaDescriptorList) + NextDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(NextEntry, UMA_DESCRIPTOR, Entry); + + /* Check for reduction or enlargement */ + if (FoundUmaDesc->Size > RequestSize) + { + /* Reduction */ + + /* + * Check if the next descriptor is free, in which case we + * extend it, otherwise we create a new free descriptor. + */ + if (NextDesc && NextDesc->Type == UMA_FREE) + { + /* Yes it is, expand its size and move it down */ + NextDesc->Size += (FoundUmaDesc->Size - RequestSize); + NextDesc->Start -= (FoundUmaDesc->Size - RequestSize); + } + else + { + // FIXME: It might be needed to consider a minimum size starting which we need to split. + // if (FoundUmaDesc->Size - RequestSize > (3 << 4)) + + /* Create a new free descriptor and insert it after the current one */ + NewUmaDesc = CreateUmaDescriptor(&FoundUmaDesc->Entry, + FoundUmaDesc->Start + RequestSize, + FoundUmaDesc->Size - RequestSize, + FoundUmaDesc->Type); + if (!NewUmaDesc) + { + DPRINT1("CreateUmaDescriptor failed, UMA descriptor list possibly corrupted!\n"); + MaxSize = 0; + goto Fail; + } + } + } + else // if (FoundUmaDesc->Size <= RequestSize) + { + /* Enlargement */ + + /* Check whether the next descriptor is free and large enough for merging */ + if (NextDesc && NextDesc->Type == UMA_FREE && + FoundUmaDesc->Size + NextDesc->Size >= RequestSize) + { + /* Yes it is, reduce its size and move it up, and enlarge the reallocated descriptor */ + NextDesc->Size -= (RequestSize - FoundUmaDesc->Size); + NextDesc->Start += (RequestSize - FoundUmaDesc->Size); + + if (NextDesc->Size == 0) FreeUmaDescriptor(NextDesc); + } + else + { + MaxSize = 0; + goto Fail; + } + } + + /* Finally, resize the descriptor */ + FoundUmaDesc->Size = RequestSize; + +Success: + /* Returned size is in paragraphs */ + *Size = (FoundUmaDesc->Size >> 4); + return TRUE; + } + else + { +Fail: + /* Returned size is in paragraphs */ + *Size = (MaxSize >> 4); + return FALSE; + } +} + +BOOLEAN UmaMgrInitialize(VOID) +{ +// See bios/rom.h +#define ROM_AREA_START 0xE0000 +#define ROM_AREA_END 0xFFFFF + +#define OPTION_ROM_SIGNATURE 0xAA55 + + PUMA_DESCRIPTOR UmaDesc = NULL; + ULONG StartAddress = 0; + ULONG Size = 0; + UMA_DESC_TYPE Type = UMA_FREE; + + UINT i; + + ULONG Start, End; + ULONG Increment; + + ULONG Address; + + // ULONG RomStart[] = {}; + // ULONG RomEnd[] = {}; + ULONG RomBoundaries[] = {0xA0000, 0xC0000, /*0xC8000, 0xE0000,*/ 0xF0000, 0x100000}; + ULONG SizeIncrement[] = {0x20000, 0x00800, /*0x00800, 0x10000,*/ 0x10000, 0x0000 }; + + // InitializeListHead(&UmaDescriptorList); + + /* NOTE: There must be always one UMA descriptor at least */ + // FIXME: Maybe it should be a static object? + + for (i = 0; i < ARRAYSIZE(RomBoundaries) - 1; i++) + { + Start = RomBoundaries[i]; // RomStart[i] + End = RomBoundaries[i+1]; // RomEnd[i] + Increment = SizeIncrement[i]; + + for (Address = Start; Address < End; Address += Increment) + { + if (StartAddress == 0) + { + /* Initialize data for a new descriptor */ + StartAddress = Address; + Size = 0; + Type = UMA_FREE; + } + + /* Is it a normal system zone/user-excluded zone? */ + if (Address >= 0xA0000 && Address < 0xC0000) + { + // StartAddress = Address; + Size = Increment; + Type = UMA_SYSTEM; + + /* Create descriptor */ + UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type); + if (!UmaDesc) return FALSE; + + StartAddress = 0; + continue; + } + /* Is it the PC ROM BIOS? */ + else if (Address >= 0xF0000) + { + // StartAddress = Address; + Size = 0x10000; // Increment; + Type = UMA_ROM; + + /* Create descriptor */ + UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type); + if (!UmaDesc) return FALSE; + + StartAddress = 0; + continue; + } + /* Is it an option ROM? */ + else if (Address >= 0xC0000 && Address < 0xF0000) + { + ULONG RomSize; + ULONG PrevRomAddress = 0; + ULONG PrevRomSize = 0; + + // while (Address < 0xF0000) + for (; Address < 0xF0000; Address += Increment) + { + +#if 0 // FIXME: Is this block, better here... + { + // We may be looping inside a ROM block, if: Type == 2 and: + // (StartAddress <= Address &&) StartAddress + Size > Address. + // In this case, do nothing (do not increase size either) + // But if we are now outside of a ROM block, then we need + // to create the previous block, and initialize a new empty one! + // (and following the next passages, increase its size). + + // if (StartAddress < Address && Type != 2) + if (Type == UMA_ROM && StartAddress + Size > Address) + { + /* We are inside ROM, do nothing */ + } + else if (Type == UMA_ROM && StartAddress + Size <= Address) + { + /* We finished a ROM descriptor */ + + /* Create descriptor */ + UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type); + if (!UmaDesc) return FALSE; + + StartAddress = 0; + // goto Restart; + } + else if (Type != UMA_ROM) + { + Size += Increment; + } + } +#endif + +Restart: + /// if (Address >= 0xE0000) { Increment = 0x10000; } + + if (StartAddress == 0) + { + /* Initialize data for a new descriptor */ + StartAddress = Address; + Size = 0; + Type = UMA_FREE; + + PrevRomAddress = 0; + PrevRomSize = 0; + } + + if (*(PUSHORT)REAL_TO_PHYS(Address) == OPTION_ROM_SIGNATURE) + { + /* + * If this is an adapter ROM (Start: C8000, End: E0000), + * its reported size is stored in byte 2 of the ROM. + * + * If this is an expansion ROM (Start: E0000, End: F0000), + * its real length is 64kB. + */ + RomSize = *(PUCHAR)REAL_TO_PHYS(Address + 2) * 512; + // if (Address >= 0xE0000) RomSize = 0x10000; + if (Address >= 0xE0000) { RomSize = 0x10000; Increment = RomSize; } + + DPRINT1("ROM present @ address 0x%p\n", Address); + + if (StartAddress != 0 && Size != 0 && + StartAddress + Size <= Address) + { + /* Finish old descriptor */ + UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type); + if (!UmaDesc) return FALSE; + } + + /* + * We may have overlapping ROMs, when PrevRomAddress + PrevRomSize > RomAddress. + * They must be put inside the same UMA descriptor. + */ + if (PrevRomAddress + PrevRomSize > /*Rom*/Address) + { + // Overlapping ROM + + /* + * PrevRomAddress remains the same, but adjust + * PrevRomSize (ROM descriptors merging). + */ + PrevRomSize = max(PrevRomSize, RomSize + Address - PrevRomAddress); + + // FIX: Confirm that the start address is really + // the one of the previous descriptor. + StartAddress = PrevRomAddress; + Size = PrevRomSize; + Type = UMA_ROM; + } + else + { + // Non-overlapping ROM + + PrevRomAddress = Address; + PrevRomSize = RomSize; + + /* Initialize a new descriptor. We will create it when it's OK */ + StartAddress = Address; + Size = RomSize; + Type = UMA_ROM; + // continue; + } + } +#if 1 // FIXME: ...or there?? + else + { + // We may be looping inside a ROM block, if: Type == 2 and: + // (StartAddress <= Address &&) StartAddress + Size > Address. + // In this case, do nothing (do not increase size either) + // But if we are now outside of a ROM block, then we need + // to create the previous block, and initialize a new empty one! + // (and following the next passages, increase its size). + + // if (StartAddress < Address && Type != UMA_ROM) + if (Type == UMA_ROM && StartAddress + Size > Address) + { + // We are inside ROM, do nothing + } + else if (Type == UMA_ROM && StartAddress + Size <= Address) + { + // We finished a ROM descriptor. + + /* Create descriptor */ + UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type); + if (!UmaDesc) return FALSE; + + StartAddress = 0; + goto Restart; + } + else if (Type != UMA_ROM) + { + Size += Increment; + } + } +#endif + + // Fixed incroment; we may encounter again overlapping ROMs, etc. + // Address += Increment; + } + + if (StartAddress != 0 && Size != 0) + { + /* Create descriptor */ + UmaDesc = CreateUmaDescriptor(NULL, StartAddress, Size, Type); + if (!UmaDesc) return FALSE; + + StartAddress = 0; + } + + } + } + } + + return TRUE; +} + +VOID UmaMgrCleanup(VOID) +{ + PUMA_DESCRIPTOR UmaDesc; + + while (!IsListEmpty(&UmaDescriptorList)) + { + UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(UmaDescriptorList.Flink, UMA_DESCRIPTOR, Entry); + FreeUmaDescriptor(UmaDesc); + } +} + +/* EOF */ diff --git a/reactos/subsystems/mvdm/ntvdm/bios/umamgr.h b/reactos/subsystems/mvdm/ntvdm/bios/umamgr.h new file mode 100644 index 00000000000..c0b91fd59bd --- /dev/null +++ b/reactos/subsystems/mvdm/ntvdm/bios/umamgr.h @@ -0,0 +1,34 @@ +/* + * COPYRIGHT: GPLv2+ - See COPYING in the top level directory + * PROJECT: ReactOS Virtual DOS Machine + * FILE: umamgr.h + * PURPOSE: Upper Memory Area Manager + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _UMAMGR_H_ +#define _UMAMGR_H_ + +/* DEFINITIONS ****************************************************************/ + +typedef enum +{ + UMA_FREE = 0, // Free RAM block + UMA_SYSTEM, // System memory (eg. VGA memory, etc...) + UMA_ROM, // ROM block + UMA_UMB, // Upper memory block + UMA_VDD // VDD-reserved block +} UMA_DESC_TYPE; + +/* FUNCTIONS ******************************************************************/ + +BOOLEAN UmaDescReserve(IN OUT PUSHORT UmbSegment, IN OUT PUSHORT Size); +BOOLEAN UmaDescRelease(IN USHORT UmbSegment); +BOOLEAN UmaDescReallocate(IN USHORT UmbSegment, IN OUT PUSHORT Size); + +BOOLEAN UmaMgrInitialize(VOID); +VOID UmaMgrCleanup(VOID); + +#endif // _UMAMGR_H_ + +/* EOF */ diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c index 55d7b40f85c..ee5fdab09eb 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c @@ -1658,19 +1658,25 @@ VOID WINAPI DosInt21h(LPWORD Stack) case 0x02: { Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; - setAL(DosUmbLinked ? 0x01 : 0x00); + setAL(SysVars->UmbLinked ? 0x01 : 0x00); break; } /* Set UMB link state */ case 0x03: { - if (getBX()) - DosLinkUmb(); - else - DosUnlinkUmb(); + BOOLEAN Success; + + if (getBX()) + Success = DosLinkUmb(); + else + Success = DosUnlinkUmb(); + + if (Success) + Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; + else + Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; - Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } @@ -2306,7 +2312,6 @@ BOOLEAN DosKRNLInitialize(VOID) /* Initialize the list of lists */ SysVars = &DosData->SysVars; RtlZeroMemory(SysVars, sizeof(*SysVars)); - SysVars->FirstMcb = FIRST_MCB_SEGMENT; SysVars->FirstSft = MAKELONG(DOS_DATA_OFFSET(Sft), DOS_DATA_SEGMENT); SysVars->CurrentDirs = MAKELONG(DOS_DATA_OFFSET(CurrentDirectories), DOS_DATA_SEGMENT); @@ -2397,9 +2402,6 @@ BOOLEAN DosKRNLInitialize(VOID) /* Set the current PSP to the system PSP */ Sda->CurrentPsp = SYSTEM_PSP; - /* Set the initial allocation strategy to "best fit" */ - Sda->AllocStrategy = DOS_ALLOC_BEST_FIT; - /* Initialize the SFT */ Sft = (PDOS_SFT)FAR_POINTER(SysVars->FirstSft); Sft->Link = 0xFFFFFFFF; @@ -2451,12 +2453,15 @@ BOOLEAN DosKRNLInitialize(VOID) XmsInitialize(); /* Load the EMS driver */ - if (!EmsDrvInitialize(EMS_TOTAL_PAGES)) + if (!EmsDrvInitialize(EMS_SEGMENT, EMS_TOTAL_PAGES)) { DPRINT1("Could not initialize EMS. EMS will not be available.\n" - "Try reducing the number of EMS pages.\n"); + "Page frame segment or number of EMS pages invalid.\n"); } + /* Finally initialize the UMBs */ + DosInitializeUmb(); + return TRUE; } diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h index 7d322b8bc60..f3208284806 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h @@ -34,7 +34,7 @@ #define DOS_DATA_OFFSET(x) FIELD_OFFSET(DOS_DATA, x) -#define SYSTEM_ENV_BLOCK 0x600 // FIXME: Should be dynamic +#define SYSTEM_ENV_BLOCK 0x600 // FIXME: Should be dynamically initialized! #define SYSTEM_PSP 0x0008 @@ -96,9 +96,9 @@ typedef struct _DOS_SYSVARS BYTE UseDwordMoves; // 0x44 WORD ExtMemSize; // 0x45 BYTE Reserved4[0x1C]; // 0x47 - BYTE ChainUMB; // 0x63 - 0/1: UMB chain (un)linked to MCB chain + BYTE UmbLinked; // 0x63 - 0/1: UMB chain (un)linked to MCB chain WORD Reserved5; // 0x64 - WORD UMBChainStart; // 0x66 - Segment of the first UMB MCB + WORD UmbChainStart; // 0x66 - Segment of the first UMB MCB WORD MemAllocScanStart; // 0x68 - Segment where allocation scan starts } DOS_SYSVARS, *PDOS_SYSVARS; diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c index b2e9cc1c2c8..1df7b5ce1cb 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.c @@ -12,15 +12,18 @@ #include "ntvdm.h" #include "emulator.h" +#include "../../memory.h" +#include "bios/umamgr.h" #include "dos.h" #include "dos/dem.h" #include "device.h" -#include "../../memory.h" #include "emsdrv.h" -#define EMS_DEVICE_NAME "EMMXXXX0" +#define EMS_DEVICE_NAME "EMMXXXX0" + +#define EMS_SEGMENT_SIZE ((EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE) >> 4) /* PRIVATE VARIABLES **********************************************************/ @@ -30,26 +33,70 @@ static PULONG BitmapBuffer = NULL; static PEMS_PAGE PageTable = NULL; static EMS_HANDLE HandleTable[EMS_MAX_HANDLES]; static PVOID Mapping[EMS_PHYSICAL_PAGES] = { NULL }; +static PVOID MappingBackup[EMS_PHYSICAL_PAGES] = { NULL }; static ULONG EmsTotalPages = 0; static PVOID EmsMemory = NULL; +static USHORT EmsSegment = EMS_SEGMENT; /* PRIVATE FUNCTIONS **********************************************************/ +static VOID InitHandlesTable(VOID) +{ + ULONG i; + + for (i = 0; i < EMS_MAX_HANDLES; i++) + { + HandleTable[i].Allocated = FALSE; + HandleTable[i].PageCount = 0; + InitializeListHead(&HandleTable[i].PageList); + } +} + +static PEMS_HANDLE CreateHandle(PUSHORT Handle) +{ + PEMS_HANDLE HandleEntry; + ULONG i; + + for (i = 0; i < EMS_MAX_HANDLES; i++) + { + HandleEntry = &HandleTable[i]; + if (!HandleEntry->Allocated) + { + *Handle = i; + break; + } + } + + if (i == EMS_MAX_HANDLES) return NULL; + HandleEntry->Allocated = TRUE; + + return HandleEntry; +} + +static VOID FreeHandle(PEMS_HANDLE HandleEntry) +{ + HandleEntry->Allocated = FALSE; + HandleEntry->PageCount = 0; +} + static inline PEMS_HANDLE GetHandleRecord(USHORT Handle) { if (Handle >= EMS_MAX_HANDLES) return NULL; return &HandleTable[Handle]; } +static inline BOOLEAN ValidateHandle(PEMS_HANDLE HandleEntry) +{ + return (HandleEntry != NULL && HandleEntry->Allocated); +} + static USHORT EmsFree(USHORT Handle) { PLIST_ENTRY Entry; PEMS_HANDLE HandleEntry = GetHandleRecord(Handle); - if (HandleEntry == NULL || !HandleEntry->Allocated) - { + if (!ValidateHandle(HandleEntry)) return EMS_STATUS_INVALID_HANDLE; - } for (Entry = HandleEntry->PageList.Flink; Entry != &HandleEntry->PageList; @@ -62,10 +109,10 @@ static USHORT EmsFree(USHORT Handle) RtlClearBits(&AllocBitmap, PageNumber, 1); } - HandleEntry->Allocated = FALSE; - HandleEntry->PageCount = 0; InitializeListHead(&HandleEntry->PageList); + FreeHandle(HandleEntry); + return EMS_STATUS_OK; } @@ -76,18 +123,8 @@ static UCHAR EmsAlloc(USHORT NumPages, PUSHORT Handle) if (NumPages == 0) return EMS_STATUS_ZERO_PAGES; - for (i = 0; i < EMS_MAX_HANDLES; i++) - { - HandleEntry = &HandleTable[i]; - if (!HandleEntry->Allocated) - { - *Handle = i; - break; - } - } - - if (i == EMS_MAX_HANDLES) return EMS_STATUS_NO_MORE_HANDLES; - HandleEntry->Allocated = TRUE; + HandleEntry = CreateHandle(Handle); + if (!HandleEntry) return EMS_STATUS_NO_MORE_HANDLES; while (HandleEntry->PageCount < NumPages) { @@ -120,13 +157,13 @@ static UCHAR EmsAlloc(USHORT NumPages, PUSHORT Handle) return EMS_STATUS_OK; } -static PEMS_PAGE GetLogicalPage(PEMS_HANDLE Handle, USHORT LogicalPage) +static PEMS_PAGE GetLogicalPage(PEMS_HANDLE HandleEntry, USHORT LogicalPage) { - PLIST_ENTRY Entry = Handle->PageList.Flink; + PLIST_ENTRY Entry = HandleEntry->PageList.Flink; while (LogicalPage) { - if (Entry == &Handle->PageList) return NULL; + if (Entry == &HandleEntry->PageList) return NULL; LogicalPage--; Entry = Entry->Flink; } @@ -139,7 +176,12 @@ static USHORT EmsMap(USHORT Handle, UCHAR PhysicalPage, USHORT LogicalPage) PEMS_PAGE PageEntry; PEMS_HANDLE HandleEntry = GetHandleRecord(Handle); - if (PhysicalPage >= EMS_PHYSICAL_PAGES) return EMS_STATUS_INV_PHYSICAL_PAGE; + if (!ValidateHandle(HandleEntry)) + return EMS_STATUS_INVALID_HANDLE; + + if (PhysicalPage >= EMS_PHYSICAL_PAGES) + return EMS_STATUS_INV_PHYSICAL_PAGE; + if (LogicalPage == 0xFFFF) { /* Unmap */ @@ -147,11 +189,6 @@ static USHORT EmsMap(USHORT Handle, UCHAR PhysicalPage, USHORT LogicalPage) return EMS_STATUS_OK; } - if (HandleEntry == NULL || !HandleEntry->Allocated) - { - return EMS_STATUS_INVALID_HANDLE; - } - PageEntry = GetLogicalPage(HandleEntry, LogicalPage); if (!PageEntry) return EMS_STATUS_INV_LOGICAL_PAGE; @@ -162,8 +199,6 @@ static USHORT EmsMap(USHORT Handle, UCHAR PhysicalPage, USHORT LogicalPage) static VOID WINAPI EmsIntHandler(LPWORD Stack) { - static PVOID MappingBackup[EMS_PHYSICAL_PAGES] = { NULL }; - switch (getAH()) { /* Get Manager Status */ @@ -177,11 +212,11 @@ static VOID WINAPI EmsIntHandler(LPWORD Stack) case 0x41: { setAH(EMS_STATUS_OK); - setBX(EMS_SEGMENT); + setBX(EmsSegment); break; } - /* Get Number Of Pages */ + /* Get Number of Pages */ case 0x42: { setAH(EMS_STATUS_OK); @@ -190,7 +225,7 @@ static VOID WINAPI EmsIntHandler(LPWORD Stack) break; } - /* Get Handle And Allocate Memory */ + /* Get Handle and Allocate Memory */ case 0x43: { USHORT Handle; @@ -208,7 +243,7 @@ static VOID WINAPI EmsIntHandler(LPWORD Stack) break; } - /* Release Handle And Memory */ + /* Release Handle and Memory */ case 0x45: { setAH(EmsFree(getDX())); @@ -226,14 +261,14 @@ static VOID WINAPI EmsIntHandler(LPWORD Stack) /* Save Page Map */ case 0x47: { - RtlCopyMemory(MappingBackup, Mapping, sizeof(PVOID) * EMS_PHYSICAL_PAGES); + RtlCopyMemory(MappingBackup, Mapping, sizeof(Mapping)); break; } /* Restore Page Map */ case 0x48: { - RtlCopyMemory(Mapping, MappingBackup, sizeof(PVOID) * EMS_PHYSICAL_PAGES); + RtlCopyMemory(Mapping, MappingBackup, sizeof(Mapping)); break; } @@ -241,7 +276,8 @@ static VOID WINAPI EmsIntHandler(LPWORD Stack) case 0x53: { PEMS_HANDLE HandleEntry = GetHandleRecord(getDX()); - if (HandleEntry == NULL || !HandleEntry->Allocated) + + if (!ValidateHandle(HandleEntry)) { setAL(EMS_STATUS_INVALID_HANDLE); break; @@ -286,7 +322,7 @@ static VOID WINAPI EmsIntHandler(LPWORD Stack) /* Expanded memory */ HandleEntry = GetHandleRecord(Data->SourceHandle); - if (HandleEntry == NULL || !HandleEntry->Allocated) + if (!ValidateHandle(HandleEntry)) { setAL(EMS_STATUS_INVALID_HANDLE); break; @@ -315,7 +351,7 @@ static VOID WINAPI EmsIntHandler(LPWORD Stack) /* Expanded memory */ HandleEntry = GetHandleRecord(Data->DestHandle); - if (HandleEntry == NULL || !HandleEntry->Allocated) + if (!ValidateHandle(HandleEntry)) { setAL(EMS_STATUS_INVALID_HANDLE); break; @@ -405,7 +441,7 @@ static VOID WINAPI EmsIntHandler(LPWORD Stack) static VOID FASTCALL EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size) { ULONG i; - ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0); + ULONG RelativeAddress = Address - TO_LINEAR(EmsSegment, 0); ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE; ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE; ULONG Offset, Length; @@ -425,7 +461,7 @@ static VOID FASTCALL EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size) static BOOLEAN FASTCALL EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size) { ULONG i; - ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0); + ULONG RelativeAddress = Address - TO_LINEAR(EmsSegment, 0); ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE; ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE; ULONG Offset, Length; @@ -444,8 +480,7 @@ static BOOLEAN FASTCALL EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size) return TRUE; } - -WORD NTAPI EmsDrvDispatchIoctlRead(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length) +static WORD NTAPI EmsDrvDispatchIoctlRead(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD Length) { // TODO: NOT IMPLEMENTED UNIMPLEMENTED; @@ -455,22 +490,26 @@ WORD NTAPI EmsDrvDispatchIoctlRead(PDOS_DEVICE_NODE Device, DWORD Buffer, PWORD /* PUBLIC FUNCTIONS ***********************************************************/ -BOOLEAN EmsDrvInitialize(ULONG TotalPages) +BOOLEAN EmsDrvInitialize(USHORT Segment, ULONG TotalPages) { - ULONG i; + USHORT Size; - for (i = 0; i < EMS_MAX_HANDLES; i++) - { - HandleTable[i].Allocated = FALSE; - HandleTable[i].PageCount = 0; - InitializeListHead(&HandleTable[i].PageList); - } + /* Try to allocate our page table in UMA at the given segment */ + EmsSegment = (Segment != 0 ? Segment : EMS_SEGMENT); + Size = EMS_SEGMENT_SIZE; // Size in paragraphs + if (!UmaDescReserve(&EmsSegment, &Size)) return FALSE; + + InitHandlesTable(); EmsTotalPages = TotalPages; BitmapBuffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, ((TotalPages + 31) / 32) * sizeof(ULONG)); - if (BitmapBuffer == NULL) return FALSE; + if (BitmapBuffer == NULL) + { + UmaDescRelease(EmsSegment); + return FALSE; + } RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, TotalPages); @@ -482,6 +521,7 @@ BOOLEAN EmsDrvInitialize(ULONG TotalPages) RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer); BitmapBuffer = NULL; + UmaDescRelease(EmsSegment); return FALSE; } @@ -493,10 +533,11 @@ BOOLEAN EmsDrvInitialize(ULONG TotalPages) RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer); BitmapBuffer = NULL; + UmaDescRelease(EmsSegment); return FALSE; } - MemInstallFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0), + MemInstallFastMemoryHook((PVOID)TO_LINEAR(EmsSegment, 0), EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE, EmsReadMemory, EmsWriteMemory); @@ -520,7 +561,7 @@ VOID EmsDrvCleanup(VOID) /* Delete the device */ DosDeleteDevice(Node); - MemRemoveFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0), + MemRemoveFastMemoryHook((PVOID)TO_LINEAR(EmsSegment, 0), EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE); if (EmsMemory) @@ -540,4 +581,6 @@ VOID EmsDrvCleanup(VOID) RtlFreeHeap(RtlGetProcessHeap(), 0, BitmapBuffer); BitmapBuffer = NULL; } + + UmaDescRelease(EmsSegment); } diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.h b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.h index b02b9947794..dfb37125ede 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.h +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/emsdrv.h @@ -13,7 +13,7 @@ #define EMS_VERSION_NUM 0x40 #define EMS_INTERRUPT_NUM 0x67 -#define EMS_SEGMENT 0xD000 +#define EMS_SEGMENT 0xD000 // Default segment #define EMS_MAX_HANDLES 16 #define EMS_PAGE_BITS 14 #define EMS_PAGE_SIZE (1 << EMS_PAGE_BITS) @@ -72,9 +72,11 @@ typedef struct _EMS_HARDWARE_INFO #pragma pack(pop) -#endif - /* FUNCTIONS ******************************************************************/ -BOOLEAN EmsDrvInitialize(ULONG TotalPages); +BOOLEAN EmsDrvInitialize(USHORT Segment, ULONG TotalPages); VOID EmsDrvCleanup(VOID); + +#endif // _EMSDRV_H_ + +/* EOF */ diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c index 4d774ec4bd6..dce3da29a5b 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c @@ -2,8 +2,46 @@ * COPYRIGHT: GPLv2+ - See COPYING in the top level directory * PROJECT: ReactOS Virtual DOS Machine * FILE: himem.c - * PURPOSE: DOS XMS Driver + * PURPOSE: DOS XMS Driver and UMB Provider * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) + * + * DOCUMENTATION: Official specifications: + * XMS v2.0: http://www.phatcode.net/res/219/files/xms20.txt + * XMS v3.0: http://www.phatcode.net/res/219/files/xms30.txt + * + * About the implementation of UMBs in DOS: + * ---------------------------------------- + * DOS needs a UMB provider to be able to use chunks of RAM in the C000-EFF0 + * memory region. A UMB provider detects regions of memory that do not contain + * any ROMs or other system mapped area such as video RAM. + * + * Where can UMB providers be found? + * + * The XMS specification (see himem.c) provides three APIs to create, free, and + * resize UMB blocks. As such, DOS performs calls into the XMS driver chain to + * request UMB blocks and include them into the DOS memory arena. + * However, is it only the HIMEM driver (implementing the XMS specification) + * which can provide UMBs? It appears that this is not necessarily the case: + * for example the MS HIMEM versions do not implement the UMB APIs; instead + * it is the EMS driver (EMM386) which provides them, by hooking into the XMS + * driver chain (see https://support.microsoft.com/en-us/kb/95555 : "MS-DOS 5.0 + * and later EMM386.EXE can also be configured to provide UMBs according to the + * XMS. This causes EMM386.EXE to be a provider of the UMB portion of the XMS."). + * + * Some alternative replacements of HIMEM/EMM386 (for example in FreeDOS) + * implement everything inside only one driver (XMS+EMS+UMB provider). + * Finally there are some UMB providers that exist separately of XMS and EMS + * drivers (for example, UMBPCI): they use hardware-specific tricks to discover + * and provide UMBs. + * + * For more details, see: + * http://www.freedos.org/technotes/technote/txt/169.txt + * http://www.freedos.org/technotes/technote/txt/202.txt + * http://www.uncreativelabs.net/textfiles/system/UMB.TXT + * + * This DOS XMS Driver provides the UMB APIs that are implemented on top + * of the internal Upper Memory Area Manager, in umamgr.c */ /* INCLUDES *******************************************************************/ @@ -14,14 +52,10 @@ #include "emulator.h" #include "cpu/bop.h" #include "../../memory.h" -#include "io.h" -#include "hardware/ps2.h" +#include "bios/umamgr.h" -#include "dos.h" -#include "dos/dem.h" #include "device.h" #include "himem.h" -#include "memory.h" #define XMS_DEVICE_NAME "XMSXXXX0" @@ -30,7 +64,8 @@ /* PRIVATE VARIABLES **********************************************************/ -static const BYTE EntryProcedure[] = { +static const BYTE EntryProcedure[] = +{ 0xEB, // jmp short +0x03 0x03, 0x90, // nop @@ -49,7 +84,24 @@ static RTL_BITMAP AllocBitmap; static ULONG BitmapBuffer[(XMS_BLOCKS + 31) / 32]; /* - * Flag used by Global Enable/Disable A20 functions, so that they don't + * This value is associated to HIMEM's "/HMAMIN=" switch. It indicates the + * minimum account of space in the HMA a program can use, and is used in + * conjunction with the "Request HMA" function. + * + * NOTE: The "/HMAMIN=" value is in kilo-bytes, whereas HmaMinSize is in bytes. + * + * Default value: 0. This causes the HMA to be allocated on a first come, + * first served basis. + */ +static WORD HmaMinSize = 0; +/* + * Flag used by "Request/Release HMA" functions, which indicates + * whether the HMA was reserved or not. + */ +static BOOLEAN IsHmaReserved = FALSE; + +/* + * Flag used by "Global Enable/Disable A20" functions, so that they don't * need to re-change the state of A20 if it was already enabled/disabled. */ static BOOLEAN IsA20Enabled = FALSE; @@ -66,7 +118,7 @@ static BOOLEAN CanChangeA20 = TRUE; */ static LONG A20EnableCount = 0; -/* HELPERS FOR A20 LINE *******************************************************/ +/* A20 LINE HELPERS ***********************************************************/ static VOID XmsLocalEnableA20(VOID) { @@ -86,6 +138,8 @@ Quit: static VOID XmsLocalDisableA20(VOID) { + UCHAR Result = XMS_STATUS_SUCCESS; + /* Disable A20 only if we can do so, otherwise make the caller believe we disabled it */ if (!CanChangeA20) goto Quit; @@ -95,15 +149,18 @@ static VOID XmsLocalDisableA20(VOID) --A20EnableCount; /* The count is zero so disable A20 */ - if (A20EnableCount == 0) EmulatorSetA20(FALSE); + if (A20EnableCount == 0) + EmulatorSetA20(FALSE); // Result = XMS_STATUS_SUCCESS; + else + Result = XMS_STATUS_A20_STILL_ENABLED; Quit: setAX(0x0001); /* Line successfully disabled */ - setBL(XMS_STATUS_SUCCESS); + setBL(Result); return; Fail: - setAX(0x0000); /* Line failed to be enabled */ + setAX(0x0000); /* Line failed to be disabled */ setBL(XMS_STATUS_A20_ERROR); return; } @@ -119,6 +176,11 @@ static inline PXMS_HANDLE GetHandleRecord(WORD Handle) return Entry->Size ? Entry : NULL; } +static inline BOOLEAN ValidateHandle(PXMS_HANDLE HandleEntry) +{ + return (HandleEntry != NULL && HandleEntry->Handle != 0); +} + static WORD XmsGetLargestFreeBlock(VOID) { WORD Result = 0; @@ -141,7 +203,7 @@ static WORD XmsGetLargestFreeBlock(VOID) return Result; } -static CHAR XmsAlloc(WORD Size, PWORD Handle) +static UCHAR XmsAlloc(WORD Size, PWORD Handle) { BYTE i; PXMS_HANDLE HandleEntry; @@ -210,13 +272,16 @@ static CHAR XmsAlloc(WORD Size, PWORD Handle) return XMS_STATUS_OUT_OF_MEMORY; } -static CHAR XmsFree(WORD Handle) +static UCHAR XmsFree(WORD Handle) { DWORD BlockNumber; PXMS_HANDLE HandleEntry = GetHandleRecord(Handle); - if (HandleEntry == NULL || HandleEntry->Handle == 0) return XMS_STATUS_INVALID_HANDLE; - if (HandleEntry->LockCount) return XMS_STATUS_LOCKED; + if (!ValidateHandle(HandleEntry)) + return XMS_STATUS_INVALID_HANDLE; + + if (HandleEntry->LockCount) + return XMS_STATUS_LOCKED; BlockNumber = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE; RtlClearBits(&AllocBitmap, BlockNumber, HandleEntry->Size); @@ -227,12 +292,15 @@ static CHAR XmsFree(WORD Handle) return XMS_STATUS_SUCCESS; } -static CHAR XmsLock(WORD Handle, PDWORD Address) +static UCHAR XmsLock(WORD Handle, PDWORD Address) { PXMS_HANDLE HandleEntry = GetHandleRecord(Handle); - if (HandleEntry == NULL) return XMS_STATUS_INVALID_HANDLE; - if (HandleEntry->LockCount == 0xFF) return XMS_STATUS_LOCK_OVERFLOW; + if (!ValidateHandle(HandleEntry)) + return XMS_STATUS_INVALID_HANDLE; + + if (HandleEntry->LockCount == 0xFF) + return XMS_STATUS_LOCK_OVERFLOW; /* Increment the lock count */ HandleEntry->LockCount++; @@ -241,12 +309,15 @@ static CHAR XmsLock(WORD Handle, PDWORD Address) return XMS_STATUS_SUCCESS; } -static CHAR XmsUnlock(WORD Handle) +static UCHAR XmsUnlock(WORD Handle) { PXMS_HANDLE HandleEntry = GetHandleRecord(Handle); - if (HandleEntry == NULL) return XMS_STATUS_INVALID_HANDLE; - if (!HandleEntry->LockCount) return XMS_STATUS_NOT_LOCKED; + if (!ValidateHandle(HandleEntry)) + return XMS_STATUS_INVALID_HANDLE; + + if (!HandleEntry->LockCount) + return XMS_STATUS_NOT_LOCKED; /* Decrement the lock count */ HandleEntry->LockCount--; @@ -267,6 +338,57 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack) break; } + /* Request HMA */ + case 0x01: + { + /* Check whether HMA is already reserved */ + if (IsHmaReserved) + { + /* It is, bail out */ + setAX(0x0000); + setBL(XMS_STATUS_HMA_IN_USE); + break; + } + + // NOTE: We implicitely suppose that we always have HMA. + // If not, we should fail there with the XMS_STATUS_HMA_DOES_NOT_EXIST + // error code. + + /* Check whether the requested size is above the minimal allowed one */ + if (getDX() < HmaMinSize) + { + /* It is not, bail out */ + setAX(0x0000); + setBL(XMS_STATUS_HMA_MIN_SIZE); + break; + } + + /* Reserve it */ + IsHmaReserved = TRUE; + setAX(0x0001); + setBL(XMS_STATUS_SUCCESS); + break; + } + + /* Release HMA */ + case 0x02: + { + /* Check whether HMA was reserved */ + if (!IsHmaReserved) + { + /* It was not, bail out */ + setAX(0x0000); + setBL(XMS_STATUS_HMA_NOT_ALLOCATED); + break; + } + + /* Release it */ + IsHmaReserved = FALSE; + setAX(0x0001); + setBL(XMS_STATUS_SUCCESS); + break; + } + /* Global Enable A20 */ case 0x03: { @@ -274,7 +396,7 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack) if (!IsA20Enabled) { XmsLocalEnableA20(); - if (getAX() != 1) + if (getAX() != 0x0001) { /* XmsLocalEnableA20 failed and already set AX and BL to their correct values */ break; @@ -291,21 +413,24 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack) /* Global Disable A20 */ case 0x04: { + UCHAR Result = XMS_STATUS_SUCCESS; + /* Disable A20 if needed */ if (IsA20Enabled) { XmsLocalDisableA20(); - if (getAX() != 1) + if (getAX() != 0x0001) { /* XmsLocalDisableA20 failed and already set AX and BL to their correct values */ break; } IsA20Enabled = FALSE; + Result = getBL(); } setAX(0x0001); /* Line successfully disabled */ - setBL(XMS_STATUS_SUCCESS); + setBL(Result); break; } @@ -338,7 +463,12 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack) { setAX(XmsGetLargestFreeBlock()); setDX(FreeBlocks); - setBL(XMS_STATUS_SUCCESS); + + if (FreeBlocks > 0) + setBL(XMS_STATUS_SUCCESS); + else + setBL(XMS_STATUS_OUT_OF_MEMORY); + break; } @@ -346,7 +476,7 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack) case 0x09: { WORD Handle; - CHAR Result = XmsAlloc(getDX(), &Handle); + UCHAR Result = XmsAlloc(getDX(), &Handle); if (Result >= 0) { @@ -365,11 +495,10 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack) /* Free Extended Memory Block */ case 0x0A: { - CHAR Result = XmsFree(getDX()); + UCHAR Result = XmsFree(getDX()); setAX(Result >= 0); setBL(Result); - break; } @@ -378,60 +507,61 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack) { PVOID SourceAddress, DestAddress; PXMS_COPY_DATA CopyData = (PXMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI()); + PXMS_HANDLE HandleEntry; if (CopyData->SourceHandle) { - PXMS_HANDLE Entry = GetHandleRecord(CopyData->SourceHandle); - if (!Entry) + HandleEntry = GetHandleRecord(CopyData->SourceHandle); + if (!ValidateHandle(HandleEntry)) { setAX(0); setBL(XMS_STATUS_BAD_SRC_HANDLE); break; } - if (CopyData->SourceOffset >= Entry->Size * XMS_BLOCK_SIZE) + if (CopyData->SourceOffset >= HandleEntry->Size * XMS_BLOCK_SIZE) { setAX(0); setBL(XMS_STATUS_BAD_SRC_OFFSET); } - SourceAddress = (PVOID)REAL_TO_PHYS(Entry->Address + CopyData->SourceOffset); + SourceAddress = (PVOID)REAL_TO_PHYS(HandleEntry->Address + CopyData->SourceOffset); } else { /* The offset is actually a 16-bit segment:offset pointer */ - SourceAddress = SEG_OFF_TO_PTR(HIWORD(CopyData->SourceOffset), - LOWORD(CopyData->SourceOffset)); + SourceAddress = FAR_POINTER(CopyData->SourceOffset); } if (CopyData->DestHandle) { - PXMS_HANDLE Entry = GetHandleRecord(CopyData->DestHandle); - if (!Entry) + HandleEntry = GetHandleRecord(CopyData->DestHandle); + if (!ValidateHandle(HandleEntry)) { setAX(0); setBL(XMS_STATUS_BAD_DEST_HANDLE); break; } - if (CopyData->DestOffset >= Entry->Size * XMS_BLOCK_SIZE) + if (CopyData->DestOffset >= HandleEntry->Size * XMS_BLOCK_SIZE) { setAX(0); setBL(XMS_STATUS_BAD_DEST_OFFSET); } - DestAddress = (PVOID)REAL_TO_PHYS(Entry->Address + CopyData->DestOffset); + DestAddress = (PVOID)REAL_TO_PHYS(HandleEntry->Address + CopyData->DestOffset); } else { /* The offset is actually a 16-bit segment:offset pointer */ - DestAddress = SEG_OFF_TO_PTR(HIWORD(CopyData->DestOffset), - LOWORD(CopyData->DestOffset)); + DestAddress = FAR_POINTER(CopyData->DestOffset); } + /* Perform the move */ + RtlMoveMemory(DestAddress, SourceAddress, CopyData->Count); + setAX(1); setBL(XMS_STATUS_SUCCESS); - RtlMoveMemory(DestAddress, SourceAddress, CopyData->Count); break; } @@ -439,7 +569,7 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack) case 0x0C: { DWORD Address; - CHAR Result = XmsLock(getDX(), &Address); + UCHAR Result = XmsLock(getDX(), &Address); if (Result >= 0) { @@ -461,108 +591,133 @@ static VOID WINAPI XmsBopProcedure(LPWORD Stack) /* Unlock Extended Memory Block */ case 0x0D: { - CHAR Result = XmsUnlock(getDX()); + UCHAR Result = XmsUnlock(getDX()); setAX(Result >= 0); setBL(Result); - break; } /* Get Handle Information */ case 0x0E: { - PXMS_HANDLE Entry = GetHandleRecord(getDX()); + PXMS_HANDLE HandleEntry = GetHandleRecord(getDX()); + UINT i; + UCHAR Handles = 0; - if (Entry && Entry->Handle != 0) - { - UINT i; - UCHAR Handles = 0; - - for (i = 0; i < XMS_MAX_HANDLES; i++) - { - if (HandleTable[i].Handle == 0) Handles++; - } - - setAX(1); - setBH(Entry->LockCount); - setBL(Handles); - setDX(Entry->Size); - } - else + if (!ValidateHandle(HandleEntry)) { setAX(0); setBL(XMS_STATUS_INVALID_HANDLE); + break; } + for (i = 0; i < XMS_MAX_HANDLES; i++) + { + if (HandleTable[i].Handle == 0) Handles++; + } + + setAX(1); + setBH(HandleEntry->LockCount); + setBL(Handles); + setDX(HandleEntry->Size); break; } - /* Allocate UMB */ + /* Reallocate Extended Memory Block */ + case 0x0F: + { + PXMS_HANDLE HandleEntry = GetHandleRecord(getDX()); + WORD NewSize = getBX(); + + if (!ValidateHandle(HandleEntry)) + { + setAX(0); + setBL(XMS_STATUS_INVALID_HANDLE); + break; + } + + if (HandleEntry->LockCount) + { + setAX(0); + setBL(XMS_STATUS_LOCKED); + break; + } + + /* Check for reduction or enlargement */ + if (NewSize < HandleEntry->Size) + { + /* Reduction, the easy case: just update the size */ + HandleEntry->Size = NewSize; + } + else + { + /* Enlargement: we need to reallocate elsewhere */ + UNIMPLEMENTED; + setAX(0); + setBL(XMS_STATUS_NOT_IMPLEMENTED); + break; + } + + setAX(1); + setBL(XMS_STATUS_SUCCESS); + break; + } + + /* Request UMB */ case 0x10: { - WORD Segment; - WORD MaxAvailable; - BYTE OldAllocStrategy = Sda->AllocStrategy; - BOOLEAN OldLinkState = DosUmbLinked; + BOOLEAN Result; + USHORT Segment = 0x0000; /* No preferred segment */ + USHORT Size = getDX(); /* Size is in paragraphs */ - DosLinkUmb(); - Sda->AllocStrategy = DOS_ALLOC_HIGH | DOS_ALLOC_BEST_FIT; - Segment = DosAllocateMemory(getDX(), &MaxAvailable); - - if (Segment) - { - setAX(1); + Result = UmaDescReserve(&Segment, &Size); + if (Result) setBX(Segment); - } else - { - setAX(0); - setBL(MaxAvailable ? XMS_STATUS_SMALLER_UMB : XMS_STATUS_OUT_OF_UMBS); - setDX(MaxAvailable); - } + setBL(Size > 0 ? XMS_STATUS_SMALLER_UMB : XMS_STATUS_OUT_OF_UMBS); - Sda->AllocStrategy = OldAllocStrategy; - if (!OldLinkState) DosUnlinkUmb(); + setDX(Size); + setAX(Result); break; } - /* Free UMB */ + /* Release UMB */ case 0x11: { - if (DosFreeMemory(getDX())) - { - setAX(1); - } - else - { - setAX(0); - setBL(XMS_STATUS_INVALID_UMB); - } + BOOLEAN Result; + USHORT Segment = getDX(); + Result = UmaDescRelease(Segment); + if (!Result) + setBL(XMS_STATUS_INVALID_UMB); + + setAX(Result); break; } /* Reallocate UMB */ case 0x12: { - WORD Segment; - WORD MaxAvailable; + BOOLEAN Result; + USHORT Segment = getDX(); + USHORT Size = getBX(); /* Size is in paragraphs */ - Segment = DosResizeMemory(getDX(), getBX(), &MaxAvailable); - - if (Segment) + Result = UmaDescReallocate(Segment, &Size); + if (!Result) { - setAX(1); - } - else - { - setAX(0); - setBL(Sda->LastErrorCode == ERROR_INVALID_HANDLE - ? XMS_STATUS_INVALID_UMB : XMS_STATUS_SMALLER_UMB); - setDX(MaxAvailable); + if (Size > 0) + { + setBL(XMS_STATUS_SMALLER_UMB); + setDX(Size); + } + else + { + setBL(XMS_STATUS_INVALID_UMB); + } } + setAX(Result); break; } diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.h b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.h index c4d28748092..02a799446ae 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.h +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/himem.h @@ -2,35 +2,43 @@ * COPYRIGHT: GPLv2+ - See COPYING in the top level directory * PROJECT: ReactOS Virtual DOS Machine * FILE: himem.h - * PURPOSE: DOS XMS Driver + * PURPOSE: DOS XMS Driver and UMB Provider * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) */ +#ifndef _HIMEM_H_ +#define _HIMEM_H_ + /* DEFINITIONS ****************************************************************/ -#define XMS_ADDRESS 0x110000 -#define XMS_BLOCKS 0x3BC0 +#define XMS_ADDRESS 0x110000 // Just above HMA +#define XMS_BLOCKS 0x3BC0 // XMS_ADDRESS + (XMS_BLOCKS * XMS_BLOCK_SIZE) == 16 MB #define XMS_BLOCK_SIZE 1024 -#define XMS_MAX_HANDLES 16 +#define XMS_MAX_HANDLES 16 // Specification: min 1, max 128, default 32 -#define XMS_STATUS_SUCCESS 0x00 -#define XMS_STATUS_NOT_IMPLEMENTED 0x80 -#define XMS_STATUS_A20_ERROR 0x82 -#define XMS_STATUS_HMA_IN_USE 0x91 -#define XMS_STATUS_OUT_OF_MEMORY 0xA0 -#define XMS_STATUS_OUT_OF_HANDLES 0xA1 -#define XMS_STATUS_INVALID_HANDLE 0xA2 -#define XMS_STATUS_BAD_SRC_HANDLE 0xA3 -#define XMS_STATUS_BAD_DEST_HANDLE 0xA4 -#define XMS_STATUS_BAD_SRC_OFFSET 0xA5 -#define XMS_STATUS_BAD_DEST_OFFSET 0xA6 -#define XMS_STATUS_NOT_LOCKED 0xAA -#define XMS_STATUS_LOCKED 0xAB -#define XMS_STATUS_LOCK_OVERFLOW 0xAC -#define XMS_STATUS_CANNOT_LOCK 0xAD -#define XMS_STATUS_SMALLER_UMB 0xB0 -#define XMS_STATUS_OUT_OF_UMBS 0xB1 -#define XMS_STATUS_INVALID_UMB 0xB2 +#define XMS_STATUS_SUCCESS 0x00 +#define XMS_STATUS_NOT_IMPLEMENTED 0x80 +#define XMS_STATUS_A20_ERROR 0x82 +#define XMS_STATUS_HMA_DOES_NOT_EXIST 0x90 +#define XMS_STATUS_HMA_IN_USE 0x91 +#define XMS_STATUS_HMA_MIN_SIZE 0x92 +#define XMS_STATUS_HMA_NOT_ALLOCATED 0x93 +#define XMS_STATUS_A20_STILL_ENABLED 0x94 +#define XMS_STATUS_OUT_OF_MEMORY 0xA0 +#define XMS_STATUS_OUT_OF_HANDLES 0xA1 +#define XMS_STATUS_INVALID_HANDLE 0xA2 +#define XMS_STATUS_BAD_SRC_HANDLE 0xA3 +#define XMS_STATUS_BAD_DEST_HANDLE 0xA4 +#define XMS_STATUS_BAD_SRC_OFFSET 0xA5 +#define XMS_STATUS_BAD_DEST_OFFSET 0xA6 +#define XMS_STATUS_NOT_LOCKED 0xAA +#define XMS_STATUS_LOCKED 0xAB +#define XMS_STATUS_LOCK_OVERFLOW 0xAC +#define XMS_STATUS_CANNOT_LOCK 0xAD +#define XMS_STATUS_SMALLER_UMB 0xB0 +#define XMS_STATUS_OUT_OF_UMBS 0xB1 +#define XMS_STATUS_INVALID_UMB 0xB2 typedef struct _XMS_HANDLE { @@ -56,3 +64,7 @@ typedef struct _XMS_COPY_DATA BOOLEAN XmsGetDriverEntry(PDWORD Pointer); VOID XmsInitialize(VOID); VOID XmsCleanup(VOID); + +#endif // _HIMEM_H_ + +/* EOF */ diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.c index 18ff0628fa7..e17a36d9684 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.c +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.c @@ -4,6 +4,7 @@ * FILE: dos/dos32krnl/memory.c * PURPOSE: DOS32 Memory Manager * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) */ /* INCLUDES *******************************************************************/ @@ -13,17 +14,61 @@ #include "ntvdm.h" #include "emulator.h" +#include "bios/umamgr.h" // HACK until we correctly call XMS services for UMBs. + #include "dos.h" #include "dos/dem.h" #include "memory.h" #include "process.h" +#include "himem.h" + +// FIXME: Should be dynamically initialized! +#define FIRST_MCB_SEGMENT (SYSTEM_ENV_BLOCK + 0x200) // old value: 0x1000 +#define USER_MEMORY_SIZE (0x9FFE - FIRST_MCB_SEGMENT) + +/* PRIVATE VARIABLES **********************************************************/ /* PUBLIC VARIABLES ***********************************************************/ -BOOLEAN DosUmbLinked = FALSE; - /* PRIVATE FUNCTIONS **********************************************************/ +static inline BOOLEAN ValidateMcb(PDOS_MCB Mcb) +{ + return (Mcb->BlockType == 'M' || Mcb->BlockType == 'Z'); +} + +/* + * This is a helper function to help us detecting + * when the DOS arena starts to become corrupted. + */ +static VOID DosMemValidate(VOID) +{ + WORD PrevSegment, Segment = SysVars->FirstMcb; + PDOS_MCB CurrentMcb; + + PrevSegment = Segment; + while (TRUE) + { + /* Get a pointer to the MCB */ + CurrentMcb = SEGMENT_TO_MCB(Segment); + + /* Make sure it's valid */ + if (!ValidateMcb(CurrentMcb)) + { + DPRINT1("The DOS memory arena is corrupted! (CurrentMcb = %04x; PreviousMcb = %04X)\n", Segment, PrevSegment); + return; + } + + PrevSegment = Segment; + + /* If this was the last MCB in the chain, quit */ + if (CurrentMcb->BlockType == 'Z') return; + + /* Otherwise, update the segment and continue */ + Segment += CurrentMcb->Size + 1; + } +} + static VOID DosCombineFreeBlocks(WORD StartBlock) { PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb; @@ -36,6 +81,14 @@ static VOID DosCombineFreeBlocks(WORD StartBlock) /* Get a pointer to the next MCB */ NextMcb = SEGMENT_TO_MCB(StartBlock + CurrentMcb->Size + 1); + /* Make sure it's valid */ + if (!ValidateMcb(NextMcb)) + { + DPRINT1("The DOS memory arena is corrupted!\n"); + // Sda->LastErrorCode = ERROR_ARENA_TRASHED; + return; + } + /* Check if the next MCB is free */ if (NextMcb->OwnerPsp == 0) { @@ -56,16 +109,19 @@ static VOID DosCombineFreeBlocks(WORD StartBlock) WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable) { - WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0; + WORD Result = 0, Segment = SysVars->FirstMcb, MaxSize = 0; PDOS_MCB CurrentMcb; BOOLEAN SearchUmb = FALSE; DPRINT("DosAllocateMemory: Size 0x%04X\n", Size); - if (DosUmbLinked && (Sda->AllocStrategy & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))) + DosMemValidate(); + + if (SysVars->UmbLinked && SysVars->UmbChainStart != 0xFFFF && + (Sda->AllocStrategy & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))) { /* Search UMB first */ - Segment = UMB_START_SEGMENT; + Segment = SysVars->UmbChainStart; SearchUmb = TRUE; } @@ -75,7 +131,7 @@ WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable) CurrentMcb = SEGMENT_TO_MCB(Segment); /* Make sure it's valid */ - if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') + if (!ValidateMcb(CurrentMcb)) { DPRINT1("The DOS memory arena is corrupted!\n"); Sda->LastErrorCode = ERROR_ARENA_TRASHED; @@ -130,7 +186,7 @@ Next: if ((Result == 0) && SearchUmb && (Sda->AllocStrategy & DOS_ALLOC_HIGH_LOW)) { /* Search low memory */ - Segment = FIRST_MCB_SEGMENT; + Segment = SysVars->FirstMcb; SearchUmb = FALSE; continue; } @@ -144,7 +200,9 @@ Next: Done: - /* If we didn't find a free block, return 0 */ + DosMemValidate(); + + /* If we didn't find a free block, bail out */ if (Result == 0) { Sda->LastErrorCode = ERROR_NOT_ENOUGH_MEMORY; @@ -194,6 +252,9 @@ Done: /* Take ownership of the block */ CurrentMcb->OwnerPsp = Sda->CurrentPsp; + RtlCopyMemory(CurrentMcb->Name, SEGMENT_TO_MCB(Sda->CurrentPsp - 1)->Name, sizeof(CurrentMcb->Name)); + + DosMemValidate(); /* Return the segment of the data portion of the block */ return Result + 1; @@ -209,16 +270,18 @@ BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable) BlockData, NewSize); + DosMemValidate(); + /* Make sure this is a valid, allocated block */ - if (BlockData == 0 - || (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') - || Mcb->OwnerPsp == 0) + if (BlockData == 0 || !ValidateMcb(Mcb) || Mcb->OwnerPsp == 0) { Success = FALSE; Sda->LastErrorCode = ERROR_INVALID_HANDLE; goto Done; } + DosCombineFreeBlocks(SysVars->FirstMcb); + ReturnSize = Mcb->Size; /* Check if we need to expand or contract the block */ @@ -235,10 +298,18 @@ BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable) NextSegment = Segment + Mcb->Size + 1; NextMcb = SEGMENT_TO_MCB(NextSegment); + /* Make sure it's valid */ + if (!ValidateMcb(NextMcb)) + { + DPRINT1("The DOS memory arena is corrupted!\n"); + Sda->LastErrorCode = ERROR_ARENA_TRASHED; + return FALSE; + } + /* Make sure the next segment is free */ if (NextMcb->OwnerPsp != 0) { - DPRINT("Cannot expand memory block: next segment is not free!\n"); + DPRINT("Cannot expand memory block 0x%04X: next segment is not free!\n", Segment); Sda->LastErrorCode = ERROR_NOT_ENOUGH_MEMORY; Success = FALSE; goto Done; @@ -252,7 +323,7 @@ BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable) if (ReturnSize < NewSize) { - DPRINT("Cannot expand memory block: insufficient free segments available!\n"); + DPRINT("Cannot expand memory block 0x%04X: insufficient free segments available!\n", Segment); Sda->LastErrorCode = ERROR_NOT_ENOUGH_MEMORY; Success = FALSE; goto Done; @@ -269,8 +340,7 @@ BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable) if (Mcb->Size > NewSize) { DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n", - Mcb->Size, - NewSize); + Mcb->Size, NewSize); /* It is, split it into two blocks */ NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1); @@ -288,8 +358,7 @@ BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable) else if (NewSize < Mcb->Size) { DPRINT("Shrinking block from 0x%04X to 0x%04X\n", - Mcb->Size, - NewSize); + Mcb->Size, NewSize); /* Just split the block */ NextSegment = Segment + NewSize + 1; @@ -317,6 +386,8 @@ Done: if (MaxAvailable) *MaxAvailable = ReturnSize; } + DosMemValidate(); + return Success; } @@ -327,8 +398,10 @@ BOOLEAN DosFreeMemory(WORD BlockData) DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData); if (BlockData == 0) return FALSE; + DosMemValidate(); + /* Make sure the MCB is valid */ - if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') + if (!ValidateMcb(Mcb)) { DPRINT("MCB block type '%c' not valid!\n", Mcb->BlockType); return FALSE; @@ -342,45 +415,55 @@ BOOLEAN DosFreeMemory(WORD BlockData) BOOLEAN DosLinkUmb(VOID) { - DWORD Segment = FIRST_MCB_SEGMENT; + DWORD Segment = SysVars->FirstMcb; PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment); DPRINT("Linking UMB\n"); /* Check if UMBs are already linked */ - if (DosUmbLinked) return FALSE; + if (SysVars->UmbChainStart == 0xFFFF) return FALSE; + if (SysVars->UmbLinked) return TRUE; + + DosMemValidate(); /* Find the last block */ + // FIXME: Use SysVars->UmbChainStart while ((Mcb->BlockType == 'M') && (Segment <= 0xFFFF)) { Segment += Mcb->Size + 1; Mcb = SEGMENT_TO_MCB(Segment); } + DosMemValidate(); + /* Make sure it's valid */ if (Mcb->BlockType != 'Z') return FALSE; /* Connect the MCB with the UMB chain */ Mcb->BlockType = 'M'; - DosUmbLinked = TRUE; + SysVars->UmbLinked = TRUE; return TRUE; } BOOLEAN DosUnlinkUmb(VOID) { - DWORD Segment = FIRST_MCB_SEGMENT; + DWORD Segment = SysVars->FirstMcb; PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment); DPRINT("Unlinking UMB\n"); /* Check if UMBs are already unlinked */ - if (!DosUmbLinked) return FALSE; + if (SysVars->UmbChainStart == 0xFFFF) return FALSE; + if (!SysVars->UmbLinked) return TRUE; + + DosMemValidate(); /* Find the block preceding the MCB that links it with the UMB chain */ while (Segment <= 0xFFFF) { - if ((Segment + Mcb->Size) == (FIRST_MCB_SEGMENT + USER_MEMORY_SIZE)) + // FIXME: Use SysVars->UmbChainStart + if ((Segment + Mcb->Size) == (SysVars->FirstMcb + USER_MEMORY_SIZE)) { /* This is the last non-UMB segment */ break; @@ -394,38 +477,164 @@ BOOLEAN DosUnlinkUmb(VOID) /* Mark the MCB as the last MCB */ Mcb->BlockType = 'Z'; - DosUmbLinked = FALSE; + DosMemValidate(); + + SysVars->UmbLinked = FALSE; return TRUE; } VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner) { PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1); - - /* Just set the owner */ Mcb->OwnerPsp = NewOwner; } +/* + * Some information about DOS UMBs: + * http://textfiles.com/virus/datut010.txt + * http://www.asmcommunity.net/forums/topic/?id=30884 + */ + +WORD DosGetPreviousUmb(WORD UmbSegment) +{ + PDOS_MCB CurrentMcb; + WORD Segment, PrevSegment = 0; + + if (SysVars->UmbChainStart == 0xFFFF) + return 0; + + /* Start scanning the UMB chain */ + Segment = SysVars->UmbChainStart; + while (TRUE) + { + /* Get a pointer to the MCB */ + CurrentMcb = SEGMENT_TO_MCB(Segment); + + /* Make sure it's valid */ + if (!ValidateMcb(CurrentMcb)) + { + DPRINT1("The UMB DOS memory arena is corrupted!\n"); + Sda->LastErrorCode = ERROR_ARENA_TRASHED; + return 0; + } + + if (Segment >= UmbSegment) + break; + + PrevSegment = Segment; + + /* If this was the last MCB in the chain, quit */ + if (CurrentMcb->BlockType == 'Z') + break; + + /* Otherwise, update the segment and continue */ + Segment += CurrentMcb->Size + 1; + } + + return PrevSegment; +} + +VOID DosInitializeUmb(VOID) +{ + BOOLEAN Result; + USHORT UmbSegment = 0x0000, PrevSegment; + USHORT Size; + PDOS_MCB Mcb, PrevMcb; + + ASSERT(SysVars->UmbChainStart == 0xFFFF); + + // SysVars->UmbLinked = FALSE; + + /* Try to allocate all the UMBs */ + while (TRUE) + { + /* Find the maximum amount of memory that can be allocated */ + Size = 0xFFFF; + Result = UmaDescReserve(&UmbSegment, &Size); + + /* If we are out of UMBs, bail out */ + if (!Result && Size == 0) // XMS_STATUS_OUT_OF_UMBS + break; + + /* We should not have succeeded! */ + ASSERT(!Result); + + /* 'Size' now contains the size of the biggest UMB block. Request it. */ + Result = UmaDescReserve(&UmbSegment, &Size); + ASSERT(Result); // XMS_STATUS_SUCCESS + + /* If this is our first UMB block, initialize the UMB chain */ + if (SysVars->UmbChainStart == 0xFFFF) + { + /* Initialize the link MCB to the UMB area */ + // NOTE: We use the fact that UmbChainStart is still == 0xFFFF + // so that we initialize this block from 9FFF:0000 up to FFFF:000F. + // It will be splitted as needed just below. + Mcb = SEGMENT_TO_MCB(SysVars->FirstMcb + USER_MEMORY_SIZE + 1); // '+1': Readjust the fact that USER_MEMORY_SIZE is based using 0x9FFE instead of 0x9FFF + Mcb->BlockType = 'Z'; // At the moment it is really the last block + Mcb->Size = SysVars->UmbChainStart /* UmbSegment */ - SysVars->FirstMcb - USER_MEMORY_SIZE - 2; + Mcb->OwnerPsp = SYSTEM_PSP; + RtlCopyMemory(Mcb->Name, "SC ", sizeof("SC ")-1); + +#if 0 // Keep here for reference; this will be deleted as soon as it becomes unneeded. + /* Initialize the UMB area */ + Mcb = SEGMENT_TO_MCB(SysVars->UmbChainStart); + Mcb->Size = UMB_END_SEGMENT - SysVars->UmbChainStart; +#endif + + // FIXME: We should adjust the size of the previous block!! + + /* Initialize the start of the UMB chain */ + SysVars->UmbChainStart = SysVars->FirstMcb + USER_MEMORY_SIZE + 1; + } + + /* Split the block */ + + /* Get the previous block */ + PrevSegment = DosGetPreviousUmb(UmbSegment); + PrevMcb = SEGMENT_TO_MCB(PrevSegment); + + /* Initialize the next block */ + Mcb = SEGMENT_TO_MCB(UmbSegment + /*Mcb->Size*/(Size - 1) + 0); + // Mcb->BlockType = 'Z'; // FIXME: What if this block happens to be the last one?? + Mcb->BlockType = PrevMcb->BlockType; + Mcb->Size = PrevMcb->Size - (UmbSegment + Size - PrevSegment); + Mcb->OwnerPsp = PrevMcb->OwnerPsp; + RtlCopyMemory(Mcb->Name, PrevMcb->Name, sizeof(PrevMcb->Name)); + + /* The previous block is not the latest one anymore */ + PrevMcb->BlockType = 'M'; + PrevMcb->Size = UmbSegment - PrevSegment - 1; + + /* Initialize the new UMB block */ + Mcb = SEGMENT_TO_MCB(UmbSegment); + Mcb->BlockType = 'M'; // 'Z' // FIXME: What if this block happens to be the last one?? + Mcb->Size = Size - 1 - 1; // minus 2 because we need to have one arena at the beginning and one at the end. + Mcb->OwnerPsp = 0; + // FIXME: Which MCB name should we use? I need to explore more the docs! + RtlCopyMemory(Mcb->Name, "UMB ", sizeof("UMB ")-1); + // RtlCopyMemory(Mcb->Name, "SM ", sizeof("SM ")-1); + } +} + VOID DosInitializeMemory(VOID) { - PDOS_MCB Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT); + PDOS_MCB Mcb; + + /* Set the initial allocation strategy to "best fit" */ + Sda->AllocStrategy = DOS_ALLOC_BEST_FIT; + + /* Initialize conventional memory; we don't have UMBs yet */ + SysVars->FirstMcb = FIRST_MCB_SEGMENT; // The Arena Head + SysVars->UmbLinked = FALSE; + SysVars->UmbChainStart = 0xFFFF; + + Mcb = SEGMENT_TO_MCB(SysVars->FirstMcb); /* Initialize the MCB */ Mcb->BlockType = 'Z'; Mcb->Size = USER_MEMORY_SIZE; Mcb->OwnerPsp = 0; - - /* Initialize the link MCB to the UMB area */ - Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT + USER_MEMORY_SIZE + 1); - Mcb->BlockType = 'M'; - Mcb->Size = UMB_START_SEGMENT - FIRST_MCB_SEGMENT - USER_MEMORY_SIZE - 2; - Mcb->OwnerPsp = SYSTEM_PSP; - - /* Initialize the UMB area */ - Mcb = SEGMENT_TO_MCB(UMB_START_SEGMENT); - Mcb->BlockType = 'Z'; - Mcb->Size = UMB_END_SEGMENT - UMB_START_SEGMENT; - Mcb->OwnerPsp = 0; } /* EOF */ diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.h b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.h index 401cafed7df..543eb771e04 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.h +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.h @@ -4,6 +4,7 @@ * FILE: dos/dos32krnl/memory.h * PURPOSE: DOS32 Memory Manager * PROGRAMMERS: Aleksandar Andrejevic + * Hermes Belusca-Maito (hermes.belusca@sfr.fr) */ #ifndef _DOS_MEMORY_H_ @@ -13,12 +14,6 @@ #define SEGMENT_TO_MCB(seg) ((PDOS_MCB)SEG_OFF_TO_PTR((seg), 0)) -#define FIRST_MCB_SEGMENT 0x1000 -#define USER_MEMORY_SIZE (0x9FFE - FIRST_MCB_SEGMENT) - -#define UMB_START_SEGMENT 0xC000 -#define UMB_END_SEGMENT 0xDFFF - #define DOS_ALLOC_HIGH 0x40 #define DOS_ALLOC_HIGH_LOW 0x80 @@ -43,9 +38,6 @@ C_ASSERT(sizeof(DOS_MCB) == 0x10); /* VARIABLES ******************************************************************/ -extern BYTE DosAllocStrategy; -extern BOOLEAN DosUmbLinked; - /* FUNCTIONS ******************************************************************/ WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable); @@ -55,6 +47,7 @@ BOOLEAN DosLinkUmb(VOID); BOOLEAN DosUnlinkUmb(VOID); VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner); +VOID DosInitializeUmb(VOID); VOID DosInitializeMemory(VOID); #endif // _DOS_MEMORY_H_ diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c index ba91b9168fc..14eb3ccc713 100644 --- a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c +++ b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c @@ -852,7 +852,7 @@ Command: VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident) { WORD i; - WORD McbSegment = FIRST_MCB_SEGMENT; + WORD McbSegment = SysVars->FirstMcb; PDOS_MCB CurrentMcb; LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress); PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);