1693 lines
46 KiB
C
1693 lines
46 KiB
C
/*-
|
|
* Copyright (c) 1997 Helmut Wirth <hfwirth@ping.at>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice immediately at the beginning of the file, witout modification,
|
|
* this list of conditions, and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/*
|
|
* EMS memory emulation
|
|
*
|
|
* To emulate Expanded Memory we use a DOS driver (emsdriv.sys) which
|
|
* routes calls to int 0x67 to this emulator routine. The main entry point
|
|
* is ems_entry(..). The emulator needs to be initialized before the first
|
|
* call. The first step of the initialization is done during program startup
|
|
* the second part is done during DOS boot, from a call of the DOS driver.
|
|
* The DOS driver is neccessary because DOS programs look for it to
|
|
* determine if EMS is available.
|
|
*
|
|
* To emulate a configurable amount of EMS memory we use a file created
|
|
* at startup with the size of the configured EMS memory. This file is
|
|
* mapped into the EMS window like any DOS memory manager would do, using
|
|
* mmap calls.
|
|
*
|
|
* The emulation follows the LIM EMS 4.0 standard. Not all functions of it
|
|
* are implemented yet. The "alter page map and jump" and "alter page map
|
|
* and call" functions are not implemented, because they are rather hard to
|
|
* do. (It would mean a call to the emulator executes a routine in EMS
|
|
* memory and returns to the emulator, the emulator switches the page map
|
|
* and then returns to the DOS program.) LINUX does not emulate this
|
|
* functions and I think they were very rarely used by DOS applications.
|
|
*
|
|
* Credits: To the writers of LINUX dosemu, I looked at their code
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
|
|
#include "doscmd.h"
|
|
#include "ems.h"
|
|
|
|
/* Will be configurable */
|
|
u_long ems_max_size = EMS_MAXSIZE * 1024;
|
|
u_long ems_frame_addr = EMS_FRAME_ADDR;
|
|
|
|
/*
|
|
* Method for EMS: Allocate a mapfile with the size of EMS memory
|
|
* and map the needed part into the page frame
|
|
*/
|
|
|
|
#define EMS_MAP_PATH "/var/tmp/" /* Use a big file system */
|
|
#define EMS_MAP_FILE "doscmd.XXXXXX"
|
|
static int mapfile_fd = -1;
|
|
|
|
/* Pages are always 16 kB in size. The page frame is 64 kB, there are
|
|
* 4 positions (0..3) for a page to map in. The pages are numbered from 0 to
|
|
* the highest 16 kB page in the mapfile, depending on the EMS size
|
|
*/
|
|
|
|
EMS_mapping_context ems_mapping_context;
|
|
|
|
/* Handle and page management (see ems.h) */
|
|
|
|
/* The handle array. If the pointer is NULL, the handle is unallocated */
|
|
static EMS_handle *ems_handle[EMS_NUM_HANDLES];
|
|
static u_long ems_alloc_handles;
|
|
/* The active handle, if any */
|
|
static short active_handle;
|
|
|
|
/* The page array. It is malloced at runtime, depending on the total
|
|
* allocation size
|
|
*/
|
|
|
|
static EMS_page *ems_page = NULL;
|
|
static u_long ems_total_pages;
|
|
static u_long ems_alloc_pages;
|
|
static u_long ems_free_pages;
|
|
|
|
/* Local structure used for region copy and move operations */
|
|
|
|
struct copydesc {
|
|
#define SRC_EMS 1
|
|
#define DST_EMS 2
|
|
short copytype; /* Type of source and destination memory */
|
|
EMS_addr src_addr; /* Combined pointer for source */
|
|
EMS_addr dst_addr; /* Combined pointer for destination */
|
|
u_long rest_len; /* Lenght to copy */
|
|
};
|
|
|
|
|
|
/* Local prototypes */
|
|
static int init_mapfile(void);
|
|
static void map_page(u_long, u_char, short, int);
|
|
static EMS_handle *get_new_handle(long);
|
|
static void context_to_handle(short);
|
|
static long find_next_free_handle(void);
|
|
static short lookup_handle(Hname *hp);
|
|
static void allocate_pages_to_handle(u_short, long);
|
|
static void allocate_handle(short, long);
|
|
static void reallocate_pages_to_handle(u_short, long);
|
|
static void free_handle(short);
|
|
static void free_pages_of_handle(short);
|
|
static void restore_context(EMS_mapping_context *);
|
|
static void save_context_to_dos(EMScontext *);
|
|
static int check_saved_context(EMScontext *);
|
|
static void *get_valid_pointer(u_short, u_short, u_long);
|
|
static u_long move_ems_to_conv(short, u_short, u_short, u_long, u_long);
|
|
static u_long move_conv_to_ems(u_long, u_short, u_short, u_short, u_long);
|
|
static u_long move_ems_to_ems(u_short, u_short, u_short, u_short,
|
|
u_short, u_short, u_long);
|
|
|
|
/*
|
|
* EMS initialization routine: Return 1, if successful, return 0 if
|
|
* init problem or EMS disabled
|
|
*/
|
|
|
|
int
|
|
ems_init()
|
|
{
|
|
unsigned i;
|
|
|
|
if (ems_max_size == 0)
|
|
return 0;
|
|
if (init_mapfile() == 0)
|
|
return 0;
|
|
/* Sanity */
|
|
bzero((void *)(&ems_handle[0]), sizeof(ems_handle));
|
|
ems_total_pages = ems_max_size / EMS_PAGESIZE;
|
|
ems_alloc_pages = 0;
|
|
ems_free_pages = ems_total_pages;
|
|
ems_alloc_handles = 0;
|
|
active_handle = 0;
|
|
/* Malloc the page array */
|
|
ems_page = (EMS_page *)malloc(sizeof(EMS_page) * ems_total_pages);
|
|
if (ems_page == NULL) {
|
|
debug(D_ALWAYS, "Could not malloc page array, EMS disabled\n");
|
|
ems_frame_addr = 0;
|
|
ems_max_size = 0;
|
|
ems_total_pages = 0;
|
|
return 0;
|
|
}
|
|
for (i = 0; i < ems_total_pages; i++) {
|
|
ems_page[i].handle = 0;
|
|
ems_page[i].status = EMS_FREE;
|
|
}
|
|
debug(D_EMS, "EMS: Emulation init OK.\n");
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* Main entry point */
|
|
|
|
void
|
|
ems_entry(regcontext_t *REGS)
|
|
{
|
|
/*
|
|
* If EMS is not enabled, the DOS ems.exe module should not have
|
|
* been loaded. If it is loaded anyway, report software malfunction
|
|
*/
|
|
if (ems_max_size == 0) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
debug(D_EMS, "EMS emulation not enabled\n");
|
|
return;
|
|
}
|
|
|
|
switch (R_AH)
|
|
{
|
|
case GET_MANAGER_STATUS:
|
|
debug(D_EMS, "EMS: Get manager status\n");
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
|
|
case GET_PAGE_FRAME_SEGMENT:
|
|
debug(D_EMS, "EMS: Get page frame segment\n");
|
|
R_BX = ems_frame_addr >> 4;
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
|
|
case GET_PAGE_COUNTS:
|
|
R_BX = ems_total_pages - ems_alloc_pages;
|
|
R_DX = ems_total_pages;
|
|
debug(D_EMS, "EMS: Get page count: Returned total=%d, free=%d\n",
|
|
R_DX, R_BX);
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
|
|
case GET_HANDLE_AND_ALLOCATE:
|
|
{
|
|
u_short npages;
|
|
short handle;
|
|
|
|
npages = R_BX;
|
|
debug(D_EMS, "EMS: Get handle and allocate %d pages: ", npages);
|
|
|
|
/* Enough handles? */
|
|
if ((handle = find_next_free_handle()) < 0) {
|
|
debug(D_EMS,"Return error:No handles\n");
|
|
R_AH = EMS_OUT_OF_HANDLES;
|
|
break;
|
|
}
|
|
/* Enough memory for this request ? */
|
|
if (npages > ems_free_pages) {
|
|
debug(D_EMS,"Return error:Request too big\n");
|
|
R_AH = EMS_OUT_OF_LOG;
|
|
break;
|
|
}
|
|
if (npages > ems_total_pages) {
|
|
debug(D_EMS,"Return error:Request too big\n");
|
|
R_AH = EMS_OUT_OF_PHYS;
|
|
break;
|
|
}
|
|
/* Not allowed to allocate zero pages with this function */
|
|
if (npages == 0) {
|
|
debug(D_EMS,"Return error:Cannot allocate 0 pages\n");
|
|
R_AH = EMS_ZERO_PAGES;
|
|
break;
|
|
}
|
|
/* Allocate the handle */
|
|
allocate_handle(handle, npages);
|
|
|
|
/* Allocate the pages */
|
|
allocate_pages_to_handle(handle, npages);
|
|
R_DX = handle;
|
|
R_AH = EMS_SUCCESS;
|
|
debug(D_EMS,"Return success:Handle = %d\n", handle);
|
|
break;
|
|
}
|
|
|
|
case MAP_UNMAP:
|
|
{
|
|
u_char position;
|
|
u_short hpagenum, spagenum;
|
|
short handle;
|
|
|
|
debug(D_EMS, "EMS: Map/Unmap handle=%d, pos=%d, pagenum=%d ",
|
|
R_DX, R_AL, R_BX);
|
|
handle = R_DX;
|
|
position = R_AL;
|
|
if (position > 3) {
|
|
debug(D_EMS, "invalid position\n");
|
|
R_AH = EMS_ILL_PHYS;
|
|
break;
|
|
}
|
|
hpagenum = R_BX;
|
|
/* This succeeds without a valid handle ! */
|
|
if (hpagenum == 0xffff) {
|
|
/* Unmap only */
|
|
map_page(0, position, handle, 1);
|
|
debug(D_EMS, "(unmap only) success\n");
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
|
|
R_AH = EMS_INV_HANDLE;
|
|
debug(D_EMS, "invalid handle\n");
|
|
break;
|
|
}
|
|
if (hpagenum >= ems_handle[handle]->npages) {
|
|
R_AH = EMS_LOGPAGE_TOOBIG;
|
|
debug(D_EMS, "invalid pagenumber\n");
|
|
break;
|
|
}
|
|
spagenum = ems_handle[handle]->pagenum[hpagenum];
|
|
map_page(spagenum, position, handle, 0);
|
|
debug(D_EMS, "success\n");
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case DEALLOCATE_HANDLE:
|
|
{
|
|
short handle;
|
|
|
|
/* Handle valid ? */
|
|
handle = R_DX;
|
|
debug(D_EMS, "EMS: Deallocate handle %d\n", handle);
|
|
if (handle > 255 || ems_handle[handle] == NULL) {
|
|
R_AH = EMS_INV_HANDLE;
|
|
break;
|
|
}
|
|
/* Mapping context saved ? */
|
|
if (ems_handle[handle]->mcontext != NULL) {
|
|
R_AH = EMS_SAVED_MAP;
|
|
break;
|
|
}
|
|
|
|
free_pages_of_handle(handle);
|
|
free_handle(handle);
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case GET_EMM_VERSION:
|
|
debug(D_EMS, "EMS: Get version\n");
|
|
R_AL = EMS_VERSION;
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
|
|
case SAVE_PAGE_MAP:
|
|
{
|
|
short handle;
|
|
|
|
debug(D_EMS, "EMS: Save page map\n");
|
|
handle = R_DX;
|
|
if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
|
|
R_AH = EMS_INV_HANDLE;
|
|
break;
|
|
}
|
|
if (ems_handle[handle]->mcontext != NULL) {
|
|
/* There is already a context saved */
|
|
if (memcmp((void *)ems_handle[handle]->mcontext,
|
|
(void *)&ems_mapping_context,
|
|
sizeof(EMS_mapping_context)) == 0)
|
|
R_AH = EMS_ALREADY_SAVED;
|
|
else
|
|
R_AH = EMS_NO_ROOM_TO_SAVE;
|
|
break;
|
|
}
|
|
context_to_handle(handle);
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case RESTORE_PAGE_MAP:
|
|
{
|
|
short handle;
|
|
|
|
debug(D_EMS, "EMS: Restore page map\n");
|
|
handle = R_DX;
|
|
if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
|
|
R_AH = EMS_INV_HANDLE;
|
|
break;
|
|
}
|
|
if (ems_handle[handle]->mcontext == NULL) {
|
|
R_AH = EMS_NO_SAVED_CONTEXT;
|
|
break;
|
|
}
|
|
restore_context(ems_handle[handle]->mcontext);
|
|
free((void *)ems_handle[handle]->mcontext);
|
|
ems_handle[handle]->mcontext = NULL;
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case RESERVED_1:
|
|
case RESERVED_2:
|
|
debug(D_ALWAYS, "Reserved function called: %02x\n", R_AH);
|
|
R_AH = EMS_FUNC_NOSUP;
|
|
break;
|
|
|
|
case GET_HANDLE_COUNT:
|
|
debug(D_EMS, "EMS: Get handle count\n");
|
|
R_BX = ems_alloc_handles + 1;
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
|
|
case GET_PAGES_OWNED:
|
|
{
|
|
short handle;
|
|
|
|
debug(D_EMS, "EMS: Get pages owned\n");
|
|
/* Handle valid ? */
|
|
handle = R_DX;
|
|
if (handle > 255 || ems_handle[handle] == NULL) {
|
|
R_AH = EMS_INV_HANDLE;
|
|
break;
|
|
}
|
|
if (handle == 0)
|
|
R_BX = 0;
|
|
else
|
|
R_BX = ems_handle[handle]->npages;
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case GET_PAGES_FOR_ALL:
|
|
{
|
|
EMShandlepage *ehp;
|
|
unsigned safecount;
|
|
int i;
|
|
|
|
debug(D_EMS, "EMS: Get pages for all\n");
|
|
/* Get the address passed from DOS app */
|
|
ehp = (EMShandlepage *)get_valid_pointer(R_ES, R_DI,
|
|
sizeof(EMShandlepage) * ems_alloc_handles);
|
|
if (ehp == NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
|
|
R_BX = ems_alloc_handles;
|
|
safecount = 0;
|
|
for (i = 0; i < 255; i++) {
|
|
if (ems_handle[i] != NULL) {
|
|
if (safecount > (ems_alloc_handles+1))
|
|
fatal("EMS: ems_alloc_handles is wrong, "
|
|
"cannot continue\n");
|
|
ehp->handle = i;
|
|
ehp->npages = ems_handle[i]->npages;
|
|
ehp++;
|
|
safecount++;
|
|
}
|
|
}
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case PAGE_MAP:
|
|
/* This function is a nuisance. It was invented to save time and
|
|
* memory, but in our case it is useless. We have to support it
|
|
* but we use the same save memory as for the page map function.
|
|
* It uses only 20 bytes anyway. We store/restore the entire mapping
|
|
*/
|
|
case PAGE_MAP_PARTIAL:
|
|
{
|
|
int subfunction;
|
|
EMScontext *src, *dest;
|
|
|
|
debug(D_EMS, "EMS: Page map ");
|
|
subfunction = R_AL;
|
|
if (R_AH == PAGE_MAP_PARTIAL) {
|
|
debug(D_EMS, "partial ");
|
|
/* Page map partial has slightly different subfunctions
|
|
* GET_SET does not exist and is GET_SIZE in this case
|
|
*/
|
|
if (subfunction == GET_SET)
|
|
subfunction = GET_SIZE;
|
|
}
|
|
switch (subfunction)
|
|
{
|
|
case GET:
|
|
{
|
|
debug(D_EMS, "get\n");
|
|
/* Get the address passed from DOS app */
|
|
dest = (EMScontext *)get_valid_pointer(R_ES, R_DI,
|
|
sizeof(EMScontext));
|
|
if (dest == NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
save_context_to_dos(dest);
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
case SET:
|
|
{
|
|
debug(D_EMS, "set\n");
|
|
src = (EMScontext *)get_valid_pointer(R_DS, R_SI,
|
|
sizeof(EMScontext));
|
|
if (src == NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
if (check_saved_context(src) == 0) {
|
|
R_AH = EMS_SAVED_CONTEXT_BAD;
|
|
break;
|
|
}
|
|
restore_context(&src->ems_saved_context);
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
case GET_SET:
|
|
{
|
|
debug(D_EMS, "get/set\n");
|
|
dest = (EMScontext *)get_valid_pointer(R_ES, R_DI,
|
|
sizeof(EMScontext));
|
|
if (dest == NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
save_context_to_dos(dest);
|
|
src = (EMScontext *)get_valid_pointer(R_DS, R_SI,
|
|
sizeof(EMScontext));
|
|
if (src == NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
if (check_saved_context(src) == 0) {
|
|
R_AH = EMS_SAVED_CONTEXT_BAD;
|
|
break;
|
|
}
|
|
restore_context(&src->ems_saved_context);
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
case GET_SIZE:
|
|
debug(D_EMS, "get size\n");
|
|
R_AL = (sizeof(EMScontext) + 1) & 0xfe;
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
default:
|
|
debug(D_EMS, "invalid subfunction\n");
|
|
R_AH = EMS_INVALID_SUB;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MAP_UNMAP_MULTI_HANDLE:
|
|
{
|
|
u_char position;
|
|
u_short hpagenum, spagenum;
|
|
short handle;
|
|
EMSmapunmap *mp;
|
|
int n_entry, i;
|
|
|
|
|
|
debug(D_EMS, "EMS: Map/Unmap multiple ");
|
|
|
|
if ((n_entry = R_CX) > 3) {
|
|
R_AH = EMS_ILL_PHYS;
|
|
}
|
|
|
|
/* This is valid according to the LIM EMS 4.0 spec */
|
|
if (n_entry == 0) {
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
handle = R_DX;
|
|
if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
|
|
R_AH = EMS_INV_HANDLE;
|
|
break;
|
|
}
|
|
|
|
mp = (EMSmapunmap *)get_valid_pointer(R_DS, R_SI,
|
|
sizeof(EMSmapunmap) * n_entry);
|
|
if (mp == NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
|
|
R_AH = EMS_SUCCESS;
|
|
/* Walk through the table and map/unmap */
|
|
for (i = 0; i < n_entry; i++) {
|
|
hpagenum = mp->log;
|
|
/* Method is in R_AL */
|
|
if (R_AL == 0) {
|
|
debug(D_EMS, "phys page method\n");
|
|
if (mp->phys <= 3) {
|
|
position = mp->phys;
|
|
} else {
|
|
R_AH = EMS_ILL_PHYS;
|
|
break;
|
|
}
|
|
} else if (R_AL == 1) {
|
|
/* Compute position from segment address */
|
|
u_short p_seg;
|
|
|
|
debug(D_EMS, "segment method\n");
|
|
p_seg = mp->phys;
|
|
p_seg -= ems_frame_addr;
|
|
p_seg /= EMS_PAGESIZE;
|
|
if (p_seg <= 3) {
|
|
position = p_seg;
|
|
} else {
|
|
R_AH = EMS_ILL_PHYS;
|
|
break;
|
|
}
|
|
} else {
|
|
debug(D_EMS, "invalid subfunction\n");
|
|
R_AH = EMS_INVALID_SUB;
|
|
break;
|
|
}
|
|
|
|
mp++;
|
|
if (hpagenum == 0xffff) {
|
|
/* Unmap only */
|
|
map_page(0, position, handle, 1);
|
|
continue;
|
|
}
|
|
if (hpagenum >= ems_handle[handle]->npages) {
|
|
R_AH = EMS_LOGPAGE_TOOBIG;
|
|
break;
|
|
}
|
|
spagenum = ems_handle[handle]->pagenum[hpagenum];
|
|
map_page(spagenum, position, handle, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case REALLOC_PAGES:
|
|
{
|
|
short handle;
|
|
u_long newpages;
|
|
|
|
debug(D_EMS, "EMS: Realloc pages ");
|
|
|
|
handle = R_DX;
|
|
if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
|
|
R_AH = EMS_INV_HANDLE;
|
|
debug(D_EMS, "invalid handle\n");
|
|
break;
|
|
}
|
|
newpages = R_BX;
|
|
debug(D_EMS, "changed from %ld to %ld pages\n",
|
|
ems_handle[handle]->npages, newpages);
|
|
|
|
/* Case 1: Realloc to zero pages */
|
|
if (newpages == 0) {
|
|
free_pages_of_handle(handle);
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
/* Case 2: New allocation is equal to allocated number */
|
|
if (newpages == ems_handle[handle]->npages) {
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
/* Case 3: Reallocate to bigger and smaller sizes */
|
|
if (newpages > ems_handle[handle]->npages) {
|
|
if (newpages > ems_free_pages) {
|
|
R_AH = EMS_OUT_OF_LOG;
|
|
break;
|
|
}
|
|
if (newpages > ems_total_pages) {
|
|
R_AH = EMS_OUT_OF_PHYS;
|
|
break;
|
|
}
|
|
}
|
|
reallocate_pages_to_handle(handle, newpages);
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
/* We do not support nonvolatile pages */
|
|
case HANDLE_ATTRIBUTES:
|
|
debug(D_EMS, "Handle attributes called\n");
|
|
switch (R_AL) {
|
|
case GET:
|
|
case SET:
|
|
R_AH = EMS_FEAT_NOSUP;
|
|
break;
|
|
case HANDLE_CAPABILITY:
|
|
R_AL = 0; /* Volatile only */
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
default:
|
|
R_AH = EMS_FUNC_NOSUP;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case HANDLE_NAME:
|
|
{
|
|
short handle;
|
|
Hname *hp;
|
|
|
|
handle = R_DX;
|
|
if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
|
|
R_AH = EMS_INV_HANDLE;
|
|
debug(D_EMS, "invalid handle\n");
|
|
break;
|
|
}
|
|
switch (R_AL) {
|
|
case GET:
|
|
if ((hp = (Hname *)get_valid_pointer(R_ES, R_DI, 8))
|
|
== NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
*hp = ems_handle[handle]->hname;
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
|
|
case SET:
|
|
if ((hp = (Hname *)get_valid_pointer(R_DS, R_SI, 8))
|
|
== NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
/* If the handle name is not 0, it may not exist */
|
|
if ((hp->ul_hn[0] | hp->ul_hn[1]) != 0) {
|
|
if (lookup_handle(hp) == 0) {
|
|
ems_handle[handle]->hname = *hp;
|
|
R_AH = EMS_SUCCESS;
|
|
} else {
|
|
R_AH = EMS_NAME_EXISTS;
|
|
break;
|
|
}
|
|
} else {
|
|
/* Name is deleted (set to zeros) */
|
|
ems_handle[handle]->hname = *hp;
|
|
R_AH = EMS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
R_AH = EMS_FUNC_NOSUP;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HANDLE_DIRECTORY:
|
|
{
|
|
int i;
|
|
EMShandledir *hdp;
|
|
Hname *hp;
|
|
short handle;
|
|
|
|
switch(R_AL) {
|
|
case GET:
|
|
hdp = (EMShandledir *)get_valid_pointer(R_ES, R_DI,
|
|
sizeof(EMShandledir) * ems_alloc_handles);
|
|
if (hdp == NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
for (i = 0; i < EMS_NUM_HANDLES; i++) {
|
|
if (ems_handle[i] != NULL) {
|
|
hdp->log = i;
|
|
hdp->name = ems_handle[i]->hname;
|
|
}
|
|
}
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
|
|
case HANDLE_SEARCH:
|
|
hp = (Hname *)get_valid_pointer(R_DS, R_SI, 8);
|
|
if (hp == NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
/* Cannot search for NULL handle name */
|
|
if ((hp->ul_hn[0] | hp->ul_hn[1]) != 0) {
|
|
R_AH = EMS_NAME_EXISTS;
|
|
break;
|
|
}
|
|
if ((handle = lookup_handle(hp)) == 0) {
|
|
R_AH = EMS_HNAME_NOT_FOUND;
|
|
} else {
|
|
R_DX = handle;
|
|
R_AH = EMS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case GET_TOTAL_HANDLES:
|
|
R_AH = EMS_SUCCESS;
|
|
R_BX = EMS_NUM_HANDLES; /* Includes OS handle */
|
|
break;
|
|
|
|
default:
|
|
R_AH = EMS_FUNC_NOSUP;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
/* I do not know if we need this. LINUX emulation leaves it out
|
|
* so I leave it out too for now.
|
|
*/
|
|
case ALTER_PAGEMAP_JUMP:
|
|
debug(D_ALWAYS, "Alter pagemap and jump used!\n");
|
|
R_AH = EMS_FUNC_NOSUP;
|
|
break;
|
|
case ALTER_PAGEMAP_CALL:
|
|
debug(D_ALWAYS, "Alter pagemap and call used!\n");
|
|
R_AH = EMS_FUNC_NOSUP;
|
|
break;
|
|
|
|
|
|
case MOVE_MEMORY_REGION:
|
|
{
|
|
EMSmovemem *emvp;
|
|
u_long src_addr, dst_addr;
|
|
u_short src_handle, dst_handle;
|
|
|
|
if (R_AL == EXCHANGE)
|
|
debug(D_EMS, "EMS: Exchange memory region ");
|
|
else
|
|
debug(D_EMS, "EMS: Move memory region ");
|
|
|
|
emvp = (EMSmovemem *)get_valid_pointer(R_DS, R_SI,
|
|
sizeof(EMSmovemem));
|
|
if (emvp == NULL) {
|
|
debug(D_EMS, "Invalid structure pointer\n");
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
/* Zero length is not an error */
|
|
if (emvp->length == 0) {
|
|
debug(D_EMS, "Zero length\n");
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
/* Some checks */
|
|
if (emvp->src_type == EMS_MOVE_CONV) {
|
|
/* Conventional memory source */
|
|
src_addr = MAKEPTR(emvp->src_seg, emvp->src_offset);
|
|
/* May not exceed conventional memory */
|
|
if ((src_addr + emvp->length) > 640 * 1024) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
} else {
|
|
/* Check the handle */
|
|
src_handle = emvp->src_handle;
|
|
if (src_handle > 255 || src_handle == 0 ||
|
|
ems_handle[src_handle] == NULL) {
|
|
R_AH = EMS_INV_HANDLE;
|
|
debug(D_EMS, "invalid source handle\n");
|
|
break;
|
|
}
|
|
/* Offset may not exceed page size */
|
|
if (emvp->src_offset >= (16 * 1024)) {
|
|
R_AH = EMS_PAGEOFFSET;
|
|
debug(D_EMS, "source page offset too big\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (emvp->dst_type == EMS_MOVE_CONV) {
|
|
/* Conventional memory source */
|
|
dst_addr = MAKEPTR(emvp->dst_seg, emvp->dst_offset);
|
|
/* May not exceed conventional memory */
|
|
if ((dst_addr + emvp->length) > 640 * 1024) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
} else {
|
|
/* Check the handle */
|
|
dst_handle = emvp->dst_handle;
|
|
if (dst_handle > 255 || dst_handle == 0 ||
|
|
ems_handle[dst_handle] == NULL) {
|
|
R_AH = EMS_INV_HANDLE;
|
|
debug(D_EMS, "invalid destination handle\n");
|
|
break;
|
|
}
|
|
/* Offset may not exceed page size */
|
|
if (emvp->dst_offset >= (16 * 1024)) {
|
|
R_AH = EMS_PAGEOFFSET;
|
|
debug(D_EMS, "destination page offset too big\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (R_AL == MOVE) {
|
|
/* If it is conventional memory only, do it */
|
|
if (emvp->src_type == EMS_MOVE_CONV &&
|
|
emvp->dst_type == EMS_MOVE_CONV) {
|
|
memmove((void *)dst_addr, (void *)src_addr,
|
|
(size_t) emvp->length);
|
|
debug(D_EMS, "conventional to conventional memory done\n");
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
if (emvp->src_type == EMS_MOVE_EMS &&
|
|
emvp->dst_type == EMS_MOVE_CONV)
|
|
R_AH = move_ems_to_conv(src_handle, emvp->src_seg,
|
|
emvp->src_offset, dst_addr, emvp->length);
|
|
else if (emvp->src_type == EMS_MOVE_CONV &&
|
|
emvp->dst_type == EMS_MOVE_EMS)
|
|
R_AH = move_conv_to_ems(src_addr, dst_handle,
|
|
emvp->dst_seg, emvp->dst_offset, emvp->length);
|
|
else
|
|
R_AH = move_ems_to_ems(src_handle, emvp->src_seg,
|
|
emvp->src_offset, dst_handle, emvp->dst_seg,
|
|
emvp->dst_offset, emvp->length);
|
|
debug(D_EMS, " done\n");
|
|
break;
|
|
} else {
|
|
/* exchange memory region */
|
|
|
|
/* We need a scratch area for the exchange */
|
|
void *buffer;
|
|
if ((buffer = malloc(emvp->length)) == NULL)
|
|
fatal("EMS: Could not malloc scratch area for exchange");
|
|
|
|
/* If it is conventional memory only, do it */
|
|
if (emvp->src_type == EMS_MOVE_CONV &&
|
|
emvp->dst_type == EMS_MOVE_CONV) {
|
|
/* destination -> buffer */
|
|
memmove(buffer, (void *)dst_addr, (size_t) emvp->length);
|
|
/* Source -> destination */
|
|
memmove((void *)dst_addr, (void *)src_addr,
|
|
(size_t) emvp->length);
|
|
/* Buffer -> source */
|
|
memmove((void *)src_addr, buffer, (size_t) emvp->length);
|
|
free(buffer);
|
|
debug(D_EMS, "conventional to conventional memory done\n");
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
/* Exchange EMS with conventional */
|
|
if (emvp->src_type == EMS_MOVE_EMS &&
|
|
emvp->dst_type == EMS_MOVE_CONV) {
|
|
/* Destination -> buffer */
|
|
memmove(buffer, (void *)dst_addr, (size_t) emvp->length);
|
|
/* Source -> destination */
|
|
R_AH = move_ems_to_conv(src_handle, emvp->src_seg,
|
|
emvp->src_offset, dst_addr, emvp->length);
|
|
if (R_AH != EMS_SUCCESS) {
|
|
free(buffer);
|
|
break;
|
|
}
|
|
/* Buffer -> source */
|
|
R_AH = move_conv_to_ems((u_long)buffer, src_handle,
|
|
emvp->src_seg, emvp->src_offset, emvp->length);
|
|
|
|
/* Exchange conventional with EMS */
|
|
} else if (emvp->src_type == EMS_MOVE_CONV &&
|
|
emvp->dst_type == EMS_MOVE_EMS) {
|
|
/* Destination -> buffer */
|
|
R_AH = move_ems_to_conv(dst_handle, emvp->dst_seg,
|
|
emvp->dst_offset, (u_long)buffer, emvp->length);
|
|
if (R_AH != EMS_SUCCESS) {
|
|
free(buffer);
|
|
break;
|
|
}
|
|
/* Source -> destination */
|
|
R_AH = move_conv_to_ems((u_long)buffer, dst_handle,
|
|
emvp->dst_seg, emvp->dst_offset, emvp->length);
|
|
/* Buffer -> source */
|
|
memmove(buffer, (void *)src_addr, (size_t) emvp->length);
|
|
|
|
/* Exchange EMS with EMS */
|
|
} else {
|
|
/* Destination -> buffer */
|
|
R_AH = move_ems_to_conv(dst_handle, emvp->dst_seg,
|
|
emvp->dst_offset, (u_long)buffer, emvp->length);
|
|
if (R_AH != EMS_SUCCESS) {
|
|
free(buffer);
|
|
break;
|
|
}
|
|
/* Source -> destination */
|
|
R_AH = move_ems_to_ems(src_handle, emvp->src_seg,
|
|
emvp->src_offset, dst_handle, emvp->dst_seg,
|
|
emvp->dst_offset, emvp->length);
|
|
if (R_AH != EMS_SUCCESS) {
|
|
free(buffer);
|
|
break;
|
|
}
|
|
/* Buffer -> source */
|
|
R_AH = move_conv_to_ems((u_long)buffer, src_handle,
|
|
emvp->src_seg, emvp->src_offset, emvp->length);
|
|
}
|
|
free(buffer);
|
|
}
|
|
debug(D_EMS, " done\n");
|
|
break;
|
|
}
|
|
|
|
case GET_MAPPABLE_PHYS_ADDR:
|
|
{
|
|
switch (R_AL) {
|
|
case GET_ARRAY:
|
|
{
|
|
EMSaddrarray *eadp;
|
|
int i;
|
|
u_short seg;
|
|
|
|
eadp = (EMSaddrarray *)get_valid_pointer(R_ES, R_DI,
|
|
sizeof(EMSaddrarray) * 4);
|
|
if (eadp == NULL) {
|
|
R_AH = EMS_SW_MALFUNC;
|
|
break;
|
|
}
|
|
for (i = 0, seg = (ems_frame_addr >> 4); i < 4; i++) {
|
|
eadp->segm = seg;
|
|
eadp->phys = i;
|
|
eadp++;
|
|
seg += 1024;
|
|
}
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
}
|
|
case GET_ARRAY_ENTRIES:
|
|
/* There are always 4 positions, 4*16kB = 64kB */
|
|
R_CX = 4;
|
|
R_AH = EMS_SUCCESS;
|
|
break;
|
|
default:
|
|
R_AH = EMS_FUNC_NOSUP;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* This is an OS function in the LIM EMS 4.0 standard: It is
|
|
* usable only by an OS and its use can be disabled for all other
|
|
* programs. I think we do not need to support it. It is not
|
|
* implemented and it reports "disabled" to any caller.
|
|
*/
|
|
case GET_HW_CONFIGURATION:
|
|
R_AH = EMS_FUNCTION_DISABLED;
|
|
break;
|
|
|
|
/* This function is a little different, it was defined with
|
|
* LIM EMS 4.0: It is allowed to allocate zero pages and raw
|
|
* page size (i.e. page size != 16kB) is supported. We have
|
|
* only 16kB pages, so the second difference does not matter.
|
|
*/
|
|
case ALLOCATE_PAGES:
|
|
{
|
|
u_short npages;
|
|
short handle;
|
|
|
|
npages = R_BX;
|
|
debug(D_EMS, "EMS: Get handle and allocate %d pages: ", npages);
|
|
|
|
/* Enough handles? */
|
|
if ((handle = find_next_free_handle()) < 0) {
|
|
debug(D_EMS,"Return error:No handles\n");
|
|
R_AH = EMS_OUT_OF_HANDLES;
|
|
break;
|
|
}
|
|
/* Enough memory for this request ? */
|
|
if (npages > ems_free_pages) {
|
|
debug(D_EMS,"Return error:Request too big\n");
|
|
R_AH = EMS_OUT_OF_LOG;
|
|
break;
|
|
}
|
|
if (npages > ems_total_pages) {
|
|
debug(D_EMS,"Return error:Request too big\n");
|
|
R_AH = EMS_OUT_OF_PHYS;
|
|
break;
|
|
}
|
|
|
|
/* Allocate the handle */
|
|
allocate_handle(handle, npages);
|
|
|
|
/* Allocate the pages */
|
|
allocate_pages_to_handle(handle, npages);
|
|
R_DX = handle;
|
|
R_AH = EMS_SUCCESS;
|
|
debug(D_EMS,"Return success:Handle = %d\n", handle);
|
|
break;
|
|
}
|
|
|
|
/* This is an OS function in the LIM EMS 4.0 standard: It is
|
|
* usable only by an OS and its use can be disabled for all other
|
|
* programs. I think we do not need to support it. It is not
|
|
* implemented and it reports "disabled" to any caller.
|
|
*/
|
|
case ALTERNATE_MAP_REGISTER:
|
|
R_AH = EMS_FUNCTION_DISABLED;
|
|
break;
|
|
|
|
/* We cannot support that ! */
|
|
case PREPARE_WARMBOOT:
|
|
R_AH = EMS_FUNC_NOSUP;
|
|
break;
|
|
|
|
case OS_FUNCTION_SET:
|
|
R_AH = EMS_FUNCTION_DISABLED;
|
|
break;
|
|
|
|
default:
|
|
debug(D_ALWAYS, "EMS: Unknown function called: %02x\n", R_AH);
|
|
R_AH = EMS_FUNC_NOSUP;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Initialize the EMS memory: Return 1 on success, 0 on failure */
|
|
|
|
static int
|
|
init_mapfile()
|
|
{
|
|
char path[256];
|
|
int mfd;
|
|
|
|
/* Sanity */
|
|
if (ems_max_size == 0)
|
|
return 0;
|
|
strcpy(path, EMS_MAP_PATH);
|
|
strcat(path, EMS_MAP_FILE);
|
|
|
|
mfd = mkstemp(path);
|
|
|
|
if (mfd < 0) {
|
|
debug(D_ALWAYS, "Could not create EMS mapfile, ");
|
|
goto fail;
|
|
}
|
|
unlink(path);
|
|
mapfile_fd = squirrel_fd(mfd);
|
|
|
|
if (lseek(mapfile_fd, (off_t)(ems_max_size - 1), 0) < 0) {
|
|
debug(D_ALWAYS, "Could not seek into EMS mapfile, ");
|
|
goto fail;
|
|
}
|
|
if (write(mapfile_fd, "", 1) < 0) {
|
|
debug(D_ALWAYS, "Could not write to EMS mapfile, ");
|
|
goto fail;
|
|
}
|
|
/* Unmap the entire page frame */
|
|
if (munmap((caddr_t)ems_frame_addr, 64 * 1024) < 0) {
|
|
debug(D_ALWAYS, "Could not unmap EMS page frame, ");
|
|
goto fail;
|
|
}
|
|
/* DOS programs will access the page frame without allocating
|
|
* pages first. Microsoft diagnose MSD.EXE does this, for example
|
|
* We need to have memory here to avoid segmentation violation
|
|
*/
|
|
if (mmap((caddr_t)ems_frame_addr, 64 * 1024,
|
|
PROT_EXEC | PROT_READ | PROT_WRITE,
|
|
MAP_ANON | MAP_FIXED | MAP_SHARED,
|
|
-1, 0) == MAP_FAILED) {
|
|
debug(D_ALWAYS, "Could not map EMS page frame, ");
|
|
goto fail;
|
|
}
|
|
bzero((void *)&ems_mapping_context, sizeof(EMS_mapping_context));
|
|
return (1);
|
|
|
|
fail:
|
|
debug(D_ALWAYS, "EMS disabled\n");
|
|
ems_max_size = 0;
|
|
ems_frame_addr = 0;
|
|
return (0);
|
|
}
|
|
|
|
/* Map/Unmap pages into one of four positions in the frame segment */
|
|
|
|
static void
|
|
map_page(u_long pagenum, u_char position, short handle, int unmaponly)
|
|
{
|
|
caddr_t map_addr;
|
|
size_t len;
|
|
off_t file_offs;
|
|
|
|
if (position > 3)
|
|
fatal("EMS: Internal error: Mapping position\n");
|
|
|
|
map_addr = (caddr_t)(ems_frame_addr + (1024 * 16 * (u_long)position));
|
|
len = 1024 * 16;
|
|
file_offs = (off_t)(pagenum * 16 * 1024);
|
|
|
|
if (ems_mapping_context.pos_mapped[position]) {
|
|
if (munmap(map_addr, len) < 0) {
|
|
fatal("EMS unmapping error: %s\nCannot recover\n",
|
|
strerror(errno));
|
|
}
|
|
ems_page[ems_mapping_context.pos_pagenum[position]].status
|
|
&= ~EMS_MAPPED;
|
|
ems_mapping_context.pos_mapped[position] = 0;
|
|
ems_mapping_context.handle[position] = 0;
|
|
}
|
|
if (unmaponly) {
|
|
/* DOS programs will access the page frame without allocating
|
|
* pages first. Microsoft diagnose MSD.EXE does this, for example
|
|
* We need to have memory here to avoid segmentation violation
|
|
*/
|
|
if (mmap((caddr_t)ems_frame_addr, 64 * 1024,
|
|
PROT_EXEC | PROT_READ | PROT_WRITE,
|
|
MAP_ANON | MAP_FIXED | MAP_SHARED,
|
|
-1, 0) == MAP_FAILED)
|
|
fatal("Could not map EMS page frame during unmap only\n");
|
|
return;
|
|
}
|
|
if (mmap(map_addr, len,
|
|
PROT_EXEC | PROT_READ | PROT_WRITE,
|
|
MAP_FILE | MAP_FIXED | MAP_SHARED,
|
|
mapfile_fd, file_offs) == MAP_FAILED) {
|
|
fatal("EMS mapping error: %s\nCannot recover\n", strerror(errno));
|
|
}
|
|
ems_mapping_context.pos_mapped[position] = 1;
|
|
ems_mapping_context.pos_pagenum[position] = pagenum;
|
|
ems_mapping_context.handle[position] = handle;
|
|
ems_page[pagenum].status |= EMS_MAPPED;
|
|
}
|
|
|
|
/* Get a pointer from VM86 app, check it and return it. This returns NULL
|
|
* if the pointer is not valid. We can check only for very limited
|
|
* criteria: The pointer and the area defined by size may not point to
|
|
* memory over 1MB and it may not may to addresses under 1kB, because there
|
|
* is the VM86 interrupt table.
|
|
*/
|
|
static void
|
|
*get_valid_pointer(u_short seg, u_short offs, u_long size)
|
|
{
|
|
u_long addr;
|
|
addr = MAKEPTR(seg, offs);
|
|
/* Check bounds */
|
|
if ((addr + size) >= (1024 * 1024) || addr < 1024)
|
|
return NULL;
|
|
else
|
|
return (void *)addr;
|
|
}
|
|
|
|
/* Malloc a new handle */
|
|
static EMS_handle
|
|
*get_new_handle(long npages)
|
|
{
|
|
EMS_handle *ehp;
|
|
size_t dynsize = sizeof(EMS_handle) + sizeof(short) * npages;
|
|
|
|
if ((ehp = calloc(1, dynsize)) == NULL)
|
|
fatal("Cannot malloc EMS handle, cannot continue\n");
|
|
return ehp;
|
|
}
|
|
|
|
/* Allocate a mapping context to a handle */
|
|
static void
|
|
context_to_handle(short handle)
|
|
{
|
|
EMS_mapping_context *emc;
|
|
|
|
if (ems_handle[handle] == NULL)
|
|
fatal("EMS context_to_handle called with invalid handle\n");
|
|
if ((emc = calloc(1, sizeof(EMS_mapping_context))) == NULL)
|
|
fatal("EMS Cannot malloc mapping context, cannot continue\n");
|
|
ems_handle[handle]->mcontext = emc;
|
|
memmove((void *)emc, (void *)&ems_mapping_context,
|
|
sizeof(EMS_mapping_context));
|
|
}
|
|
|
|
/* Find the next free handle, returns -1 if there are no more handles */
|
|
static long
|
|
find_next_free_handle()
|
|
{
|
|
int i;
|
|
|
|
if (ems_alloc_handles >= 255)
|
|
return (-1);
|
|
/* handle 0 is OS handle */
|
|
for (i = 1; i < EMS_NUM_HANDLES; i++) {
|
|
if (ems_handle[i] == NULL)
|
|
return (i);
|
|
}
|
|
fatal("EMS handle count garbled, should not happen\n");
|
|
/* quiet 'gcc -Wall' */
|
|
return (-1);
|
|
}
|
|
|
|
/* Look for a named handle, returns 0 if not found, else handle */
|
|
static short
|
|
lookup_handle(Hname *hp)
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i < EMS_NUM_HANDLES; i++) {
|
|
if (ems_handle[i] != NULL) {
|
|
if (hp->ul_hn[0] == ems_handle[i]->hname.ul_hn[0] &&
|
|
hp->ul_hn[1] == ems_handle[i]->hname.ul_hn[1])
|
|
return (i);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* Malloc a new handle struct and put into array at index handle */
|
|
static void
|
|
allocate_handle(short handle, long npages)
|
|
{
|
|
if (ems_handle[handle] != NULL)
|
|
fatal("EMS allocate_handle, handle was not free\n");
|
|
ems_handle[handle] = get_new_handle(npages);
|
|
ems_alloc_handles++;
|
|
}
|
|
|
|
/* Free a handle, return its memory. Call this *after* freeing the
|
|
* allocated pages !
|
|
*/
|
|
static void
|
|
free_handle(short handle)
|
|
{
|
|
if (ems_handle[handle] == NULL)
|
|
fatal("EMS free_handle, handle was free\n");
|
|
if (ems_handle[handle]->mcontext != NULL)
|
|
free((void *)ems_handle[handle]->mcontext);
|
|
free((void *)ems_handle[handle]);
|
|
ems_handle[handle] = NULL;
|
|
ems_alloc_handles--;
|
|
}
|
|
|
|
|
|
/* Allocates npages to handle. Call this routine only after you have
|
|
* ensured there are enough free pages *and* the new handle is in place
|
|
* in the handle array !
|
|
*/
|
|
static void
|
|
allocate_pages_to_handle(u_short handle, long npages)
|
|
{
|
|
unsigned syspagenum;
|
|
int pages_to_alloc = npages;
|
|
int allocpagenum = 0;
|
|
|
|
/* sanity */
|
|
if (handle > 255 || ems_handle[handle] == NULL)
|
|
fatal("EMS allocate_pages_to_handle called with invalid handle\n");
|
|
|
|
ems_handle[handle]->npages = npages;
|
|
for (syspagenum = 0; syspagenum < ems_total_pages; syspagenum++) {
|
|
if (ems_page[syspagenum].status == EMS_FREE) {
|
|
ems_page[syspagenum].handle = handle;
|
|
ems_page[syspagenum].status = EMS_ALLOCED;
|
|
ems_handle[handle]->pagenum[allocpagenum] = syspagenum;
|
|
allocpagenum++;
|
|
pages_to_alloc--;
|
|
if (pages_to_alloc == 0)
|
|
break;
|
|
}
|
|
}
|
|
if (pages_to_alloc > 0)
|
|
fatal("EMS allocate_pages_to_handle found not enough free pages\n");
|
|
ems_alloc_pages += npages;
|
|
ems_free_pages -= npages;
|
|
}
|
|
|
|
/* Reallocates npages to handle. Call this routine only after you have
|
|
* ensured there are enough free pages *and* the new handle is in place
|
|
* in the handle array !
|
|
*/
|
|
static void
|
|
reallocate_pages_to_handle(u_short handle, long npages)
|
|
{
|
|
unsigned allocpagenum;
|
|
unsigned syspagenum;
|
|
int pages_to_alloc;
|
|
long delta;
|
|
size_t dynsize;
|
|
EMS_handle *emp;
|
|
|
|
/* sanity */
|
|
if (handle > 255 || ems_handle[handle] == NULL)
|
|
fatal("EMS allocate_pages_to_handle called with invalid handle\n");
|
|
|
|
delta = npages - ems_handle[handle]->npages;
|
|
if (delta > 0) {
|
|
/* Grow array size and allocation */
|
|
|
|
emp = ems_handle[handle];
|
|
dynsize = sizeof(EMS_handle) + sizeof(short) * npages;
|
|
|
|
/* First step: Make room in the handle pagenum array */
|
|
if ((emp = (EMS_handle *)realloc((void *)emp, dynsize)) == NULL)
|
|
fatal("Cannot malloc EMS handle, cannot continue\n");
|
|
ems_handle[handle] = emp;
|
|
|
|
/* Second step: Add pages to the handle */
|
|
pages_to_alloc = delta;
|
|
allocpagenum = ems_handle[handle]->npages;
|
|
ems_handle[handle]->npages = npages;
|
|
for (syspagenum = 0; syspagenum < ems_total_pages; syspagenum++) {
|
|
if (ems_page[syspagenum].status == EMS_FREE) {
|
|
ems_page[syspagenum].handle = handle;
|
|
ems_page[syspagenum].status = EMS_ALLOCED;
|
|
ems_handle[handle]->pagenum[allocpagenum] = syspagenum;
|
|
allocpagenum++;
|
|
pages_to_alloc--;
|
|
if (pages_to_alloc == 0)
|
|
break;
|
|
}
|
|
}
|
|
if (pages_to_alloc > 0)
|
|
fatal("EMS allocate_pages_to_handle found not enough free pages\n");
|
|
|
|
} else {
|
|
/* Shrink array size and allocation */
|
|
|
|
/* First step: Deallocate all pages from new size to old size */
|
|
for (allocpagenum = npages;
|
|
allocpagenum < ems_handle[handle]->npages;
|
|
allocpagenum++) {
|
|
syspagenum = ems_handle[handle]->pagenum[allocpagenum];
|
|
|
|
/* sanity */
|
|
if (syspagenum > ems_total_pages)
|
|
fatal("EMS free_pages_of_handle found invalid page number\n");
|
|
if (!(ems_page[syspagenum].status & EMS_ALLOCED))
|
|
fatal("EMS free_pages_of_handle tried to free page already free\n");
|
|
ems_page[syspagenum].handle = 0;
|
|
ems_page[syspagenum].status = EMS_FREE;
|
|
}
|
|
|
|
/* Second step: Shrink the dynamic array of the handle */
|
|
dynsize = sizeof(EMS_handle) + sizeof(short) * npages;
|
|
emp = ems_handle[handle];
|
|
if ((emp = (EMS_handle *)realloc((void *)emp, dynsize)) == NULL)
|
|
fatal("Cannot realloc EMS handle, cannot continue\n");
|
|
ems_handle[handle] = emp;
|
|
ems_handle[handle]->npages = npages;
|
|
}
|
|
ems_alloc_pages += delta;
|
|
ems_free_pages -= delta;
|
|
}
|
|
|
|
/* Free all pages belonging to a handle, handle must be valid */
|
|
static void
|
|
free_pages_of_handle(short handle)
|
|
{
|
|
int allocpagenum;
|
|
unsigned syspagenum;
|
|
int npages;
|
|
|
|
/* sanity */
|
|
|
|
if (handle > 255 || ems_handle[handle] == NULL)
|
|
fatal("EMS free_pages_of_handle called with invalid handle\n");
|
|
|
|
if ((npages = ems_handle[handle]->npages) == 0)
|
|
return;
|
|
|
|
for (allocpagenum = 0; allocpagenum < npages; allocpagenum++) {
|
|
syspagenum = ems_handle[handle]->pagenum[allocpagenum];
|
|
/* sanity */
|
|
if (syspagenum > ems_total_pages)
|
|
fatal("EMS free_pages_of_handle found invalid page number\n");
|
|
if (!(ems_page[syspagenum].status & EMS_ALLOCED))
|
|
fatal("EMS free_pages_of_handle tried to free page already free\n");
|
|
ems_page[syspagenum].handle = 0;
|
|
ems_page[syspagenum].status = EMS_FREE;
|
|
}
|
|
ems_alloc_pages -= npages;
|
|
ems_free_pages += npages;
|
|
}
|
|
|
|
/* Restore a saved mapping context, overwrites current mapping context */
|
|
static void
|
|
restore_context(EMS_mapping_context *emc)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
ems_mapping_context.handle[i] = emc->handle[i];
|
|
if (emc->pos_mapped[i] != 0 &&
|
|
ems_mapping_context.pos_pagenum[i] != emc->pos_pagenum[i]) {
|
|
map_page(emc->pos_pagenum[i], (u_char) i, emc->handle[i], 0);
|
|
} else {
|
|
ems_mapping_context.pos_mapped[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Prepare a special context save block for DOS and save it to
|
|
* VM86 memory
|
|
*/
|
|
static void
|
|
save_context_to_dos(EMScontext *emp)
|
|
{
|
|
int i, end;
|
|
EMScontext context;
|
|
u_short *sp;
|
|
u_short sum;
|
|
|
|
context.ems_saved_context = ems_mapping_context;
|
|
context.magic = EMS_SAVEMAGIC;
|
|
context.checksum = 0;
|
|
sp = (u_short *)&context;
|
|
end = sizeof(EMScontext) / sizeof(short);
|
|
/* Generate checksum */
|
|
for (i = 0, sum = 0; i < end; i++) {
|
|
sum += *sp++;
|
|
sum &= 0xffff;
|
|
}
|
|
context.checksum = 0x10000L - sum;
|
|
/* Save it to VM86 memory */
|
|
*emp = context;
|
|
}
|
|
|
|
/* Check a context returned from VM86 app for validity, return 0, if
|
|
* not valid, else return 1
|
|
*/
|
|
static int
|
|
check_saved_context(EMScontext *emp)
|
|
{
|
|
int i, end;
|
|
u_short *sp;
|
|
u_short sum;
|
|
|
|
if (emp->magic != EMS_SAVEMAGIC)
|
|
return 0;
|
|
|
|
sp = (u_short *)emp;
|
|
end = sizeof(EMScontext) / sizeof(short);
|
|
/* Generate checksum */
|
|
for (i = 0, sum = 0; i < end; i++) {
|
|
sum += *sp++;
|
|
sum &= 0xffff;
|
|
}
|
|
if (sum != 0)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
/* Helper routine for the move routines below: Check if length bytes
|
|
* can be moved from/to handle pages (i.e are there enough pages)
|
|
*/
|
|
static int
|
|
check_alloc_pages(u_short handle, u_short firstpage, u_short offset,
|
|
u_long length)
|
|
{
|
|
u_long nbytes;
|
|
|
|
if (firstpage > ems_handle[handle]->npages)
|
|
return (0);
|
|
nbytes = (ems_handle[handle]->npages - firstpage) * EMS_PAGESIZE - offset;
|
|
return (ems_handle[handle]->npages >= nbytes);
|
|
}
|
|
|
|
/* Copy a block of memory up to the next 16kB boundary in the source
|
|
* to the destination in upward direction (i.e. with ascending addresses)
|
|
* XXX Could be an inline function.
|
|
*/
|
|
static void
|
|
copy_block_up(struct copydesc *cdp)
|
|
{
|
|
size_t size;
|
|
void *srcp;
|
|
void *dstp;
|
|
|
|
/* If source or both memory types are EMS, source determines the
|
|
* block lenght, else destination determines the block lenght
|
|
*/
|
|
if (cdp->copytype & SRC_EMS)
|
|
size = EMS_PAGESIZE - cdp->EMS_OFFS(src_addr);
|
|
else
|
|
size = EMS_PAGESIZE - cdp->EMS_OFFS(dst_addr);
|
|
|
|
if (size > cdp->rest_len)
|
|
size = cdp->rest_len;
|
|
|
|
/* If src is EMS memory, it is mapped into position 0 */
|
|
if (cdp->copytype & SRC_EMS)
|
|
srcp = (void *)(ems_frame_addr + cdp->EMS_OFFS(src_addr));
|
|
else
|
|
srcp = (void *)(cdp->EMS_PTR(src_addr));
|
|
|
|
/* If dest is EMS memory, it is mapped into position 1,2 */
|
|
if (cdp->copytype & DST_EMS)
|
|
dstp = (void *)(ems_frame_addr + EMS_PAGESIZE +
|
|
cdp->EMS_OFFS(dst_addr));
|
|
else
|
|
dstp = (void *)(cdp->EMS_PTR(dst_addr));
|
|
|
|
/* Move this block */
|
|
memmove(dstp, srcp, size);
|
|
|
|
/* Update the copy descriptor: This updates the address of both
|
|
* conventional and EMS memory
|
|
*/
|
|
cdp->EMS_PTR(src_addr) += size;
|
|
cdp->EMS_PTR(dst_addr) += size;
|
|
|
|
cdp->rest_len -= size;
|
|
}
|
|
|
|
|
|
/* Move EMS memory starting with handle page src_seg and offset src_offset
|
|
* to conventional memory dst_addr for length bytes
|
|
* dst_addr is checked, handle is valid
|
|
*/
|
|
static u_long
|
|
move_ems_to_conv(short src_handle, u_short src_seg,
|
|
u_short src_offset, u_long dst_addr, u_long length)
|
|
{
|
|
EMS_mapping_context ems_saved_context;
|
|
EMS_handle *ehp;
|
|
int pageindx = src_seg;
|
|
struct copydesc cd;
|
|
|
|
if (check_alloc_pages(src_handle, src_seg, src_offset, length) == 0)
|
|
return EMS_MOVE_OVERFLOW;
|
|
|
|
ehp = ems_handle[src_handle];
|
|
|
|
/* Prepare the move: Save the mapping context */
|
|
ems_saved_context = ems_mapping_context;
|
|
|
|
/* Setup the copy descriptor struct */
|
|
|
|
cd.copytype = SRC_EMS;
|
|
cd.EMS_PAGE(src_addr) = ehp->pagenum[pageindx];
|
|
cd.EMS_OFFS(src_addr) = src_offset;
|
|
cd.EMS_PTR(dst_addr) = dst_addr;
|
|
cd.rest_len = length;
|
|
|
|
do {
|
|
/* Map for the first block copy, source is mapped to position zero */
|
|
map_page(cd.EMS_PAGE(src_addr), 0, src_handle, 0);
|
|
copy_block_up(&cd);
|
|
} while(cd.rest_len > 0);
|
|
|
|
/* Restore the original mapping */
|
|
restore_context(&ems_saved_context);
|
|
return EMS_SUCCESS;
|
|
}
|
|
|
|
/* Move conventional memory starting with src_addr
|
|
* to EMS memory starting with handle page src_seg and offset src_offset
|
|
* for length bytes
|
|
* dst_addr is checked, handle is valid
|
|
*/
|
|
static u_long
|
|
move_conv_to_ems(u_long src_addr, u_short dst_handle, u_short dst_seg,
|
|
u_short dst_offset, u_long length)
|
|
{
|
|
EMS_mapping_context ems_saved_context;
|
|
EMS_handle *ehp;
|
|
int pageindx = dst_seg;
|
|
struct copydesc cd;
|
|
|
|
if (check_alloc_pages(dst_handle, dst_seg, dst_offset, length) == 0)
|
|
return EMS_MOVE_OVERFLOW;
|
|
|
|
ehp = ems_handle[dst_handle];
|
|
|
|
/* Prepare the move: Save the mapping context */
|
|
ems_saved_context = ems_mapping_context;
|
|
|
|
/* Setup the copy descriptor struct */
|
|
|
|
cd.copytype = DST_EMS;
|
|
cd.EMS_PAGE(dst_addr) = ehp->pagenum[pageindx];
|
|
cd.EMS_OFFS(dst_addr) = dst_offset;
|
|
cd.EMS_PTR(src_addr) = src_addr;
|
|
cd.rest_len = length;
|
|
|
|
do {
|
|
map_page(cd.EMS_PAGE(dst_addr), 1, dst_handle, 0);
|
|
copy_block_up(&cd);
|
|
} while(cd.rest_len > 0);
|
|
|
|
/* Restore the original mapping */
|
|
restore_context(&ems_saved_context);
|
|
return EMS_SUCCESS;
|
|
}
|
|
|
|
static u_long
|
|
move_ems_to_ems(u_short src_handle, u_short src_seg, u_short src_offset,
|
|
u_short dst_handle, u_short dst_seg, u_short dst_offset,
|
|
u_long length)
|
|
{
|
|
EMS_mapping_context ems_saved_context;
|
|
EMS_handle *src_hp, *dst_hp;
|
|
struct copydesc cd;
|
|
|
|
if (check_alloc_pages(src_handle, src_seg, src_offset, length) == 0)
|
|
return EMS_MOVE_OVERFLOW;
|
|
if (check_alloc_pages(dst_handle, dst_seg, dst_offset, length) == 0)
|
|
return EMS_MOVE_OVERFLOW;
|
|
|
|
src_hp = ems_handle[src_handle];
|
|
dst_hp = ems_handle[dst_handle];
|
|
|
|
/* Prepare the move: Save the mapping context */
|
|
ems_saved_context = ems_mapping_context;
|
|
|
|
/* Setup the copy descriptor struct */
|
|
|
|
cd.copytype = SRC_EMS | DST_EMS;
|
|
cd.EMS_PAGE(src_addr) = src_hp->pagenum[src_seg];
|
|
cd.EMS_OFFS(src_addr) = src_offset;
|
|
cd.EMS_PAGE(dst_addr) = dst_hp->pagenum[dst_seg];
|
|
cd.EMS_OFFS(dst_addr) = dst_offset;
|
|
cd.rest_len = length;
|
|
|
|
/* Copy */
|
|
do {
|
|
map_page(cd.EMS_PAGE(src_addr), 0, src_handle, 0);
|
|
map_page(cd.EMS_PAGE(dst_addr), 1, dst_handle, 0);
|
|
/* If there are more pages, map the next destination page to
|
|
* position 2. This removes a compare between source and dest
|
|
* offsets.
|
|
*/
|
|
if (cd.EMS_PAGE(dst_addr) < dst_hp->npages)
|
|
map_page((cd.EMS_PAGE(dst_addr) + 1), 2, dst_handle, 0);
|
|
copy_block_up(&cd);
|
|
} while(cd.rest_len > 0);
|
|
|
|
/* Restore the original mapping */
|
|
restore_context(&ems_saved_context);
|
|
return EMS_SUCCESS;
|
|
}
|