EMS:
- Introduce (and use) helpers for checking validity of EMS handles.
- Do not hardcode the EMS page frame segment, but instead allow it to be changed (for now, it is still set to a default value; in the future, via some configuration file or via the registry). This is done by acquiring a UMB block (see after).

XMS:
- Implement functions 0x01 "Request HMA" and 0x02 "Release HMA".
- Fix the return value of functions 0x04 "Global disable A20" and 0x08 "Query free Extended Memory"; simplify code of function 0x0B "Move EMB".
- Halfplement function 0x0F "Reallocate Extended Memory Block" in the simple case of size reduction (size expansion is left to the programmer as an exercise :PP )
- Rewrite the UMB provider support (functions 0x10, 0x11, 0x12) by calling the Upper Memory Area manager helpers (see after) (this is closer to reality: UMBs are either provided by XMS driver itself, or by an EMS driver which hooks into the XMS driver chain -- as it is done with MS' himem+EMM386; sometimes all that stuff is contained inside one driver only --) instead of calling back into DOS. This is the DOS which calls XMS for getting the UMB blocks and initializing them! (and not the other way around as it was done in r68001!).

NTVDM:
- Introduce an "Upper Memory Area manager" which maintains a list of areas of upper memory (>= A000:0000 and <= FFFF:000F) that can be used as RAM blocks. It is intended to work closely with the NTVDM memory manager and be used by XMS for getting possible free UMBs, and by VDDs for implementing the VDDInclude/ExcludeMem APIs (which adds/remove blocks in/from the UMB pool; those are unaccessible to DOS if those APIs are called after NTVDM have been started, but are accessible by XMS).

DOS:
- Add a helper function for detecting early DOS arena corruptions (for debugging purposes only).
- Make the DOS memory manager really UMB-compatible. This means:
  * not hardcoding the start of the UMB chain;
  * getting all the available UMB blocks from XMS and initializing them, marking the reserved blocks as read-only (with a correct header; reserved blocks are eg. VGA memory area, ROM blocks...).

There is room for improvements obviously (see the FIXMEs in the code). Used documentation is mentioned in comments in the code.

This commit should fix quite some apps, as well as it fixes corruptions of loaded ROMs in upper memory: that's how I came into working on fixing the UMB support. In other words, during those two last weeks, I was like in: http://i.imgur.com/zoWpqEB.gifv

CORE-9969 #resolve

svn path=/trunk/; revision=68586
This commit is contained in:
Hermès Bélusca-Maïto 2015-08-01 17:07:07 +00:00
parent 4d6da0ec7a
commit 2d44138c21
13 changed files with 1355 additions and 267 deletions

View file

@ -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

View file

@ -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();
}

View file

@ -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 */

View file

@ -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 */

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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 */

View file

@ -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 <theflash AT sdf DOT lonestar DOT org>
* 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;
}

View file

@ -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 <theflash AT sdf DOT lonestar DOT org>
* 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 */

View file

@ -4,6 +4,7 @@
* FILE: dos/dos32krnl/memory.c
* PURPOSE: DOS32 Memory Manager
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
* 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 */

View file

@ -4,6 +4,7 @@
* FILE: dos/dos32krnl/memory.h
* PURPOSE: DOS32 Memory Manager
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
* 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_

View file

@ -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);