reactos/subsystems/mvdm/ntvdm/bios/umamgr.c

628 lines
22 KiB
C
Raw Normal View History

[NTVDM] 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
2015-08-01 17:07:07 +00:00
/*
* COPYRIGHT: GPLv2+ - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: subsystems/mvdm/ntvdm/bios/umamgr.c
[NTVDM] 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
2015-08-01 17:07:07 +00:00
* 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 *******************************************************************/
#include "ntvdm.h"
[NTVDM] 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
2015-08-01 17:07:07 +00:00
#define NDEBUG
#include <debug.h>
[NTVDM] 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
2015-08-01 17:07:07 +00:00
#include "emulator.h"
#include "memory.h"
#include "umamgr.h"
[NTVDM] 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
2015-08-01 17:07:07 +00:00
/* 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");
Entry = UmaDescriptorList.Flink;
while (Entry != &UmaDescriptorList)
[NTVDM] 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
2015-08-01 17:07:07 +00:00
{
UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(Entry, UMA_DESCRIPTOR, Entry);
Entry = Entry->Flink;
[NTVDM] 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
2015-08-01 17:07:07 +00:00
/* 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;
Entry = UmaDescriptorList.Flink;
while (Entry != &UmaDescriptorList)
[NTVDM] 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
2015-08-01 17:07:07 +00:00
{
UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(Entry, UMA_DESCRIPTOR, Entry);
Entry = Entry->Flink;
[NTVDM] 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
2015-08-01 17:07:07 +00:00
/* 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);
Entry = UmaDescriptorList.Flink;
while (Entry != &UmaDescriptorList)
[NTVDM] 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
2015-08-01 17:07:07 +00:00
{
UmaDesc = (PUMA_DESCRIPTOR)CONTAINING_RECORD(Entry, UMA_DESCRIPTOR, Entry);
Entry = Entry->Flink;
[NTVDM] 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
2015-08-01 17:07:07 +00:00
/* 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 */