freebsd-nq/usr.bin/doscmd/ems.c

1697 lines
46 KiB
C
Raw Normal View History

/*-
* 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.
*
1999-08-28 01:08:13 +00:00
* $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();
static void map_page(u_long pagenum, u_char position, short handle,
int unmaponly);
static EMS_handle *get_new_handle(long npages);
static void context_to_handle(short handle);
static long find_next_free_handle();
static short lookup_handle(Hname *hp);
static void allocate_pages_to_handle(u_short handle, long npages);
static void allocate_handle(short handle, long npages);
static void reallocate_pages_to_handle(u_short handle, long npages);
static void free_handle(short handle);
static void free_pages_of_handle(short handle);
static void restore_context(EMS_mapping_context *emc);
static void save_context_to_dos(EMScontext *emp);
static int check_saved_context(EMScontext *emp);
static void *get_valid_pointer(u_short seg, u_short offs, u_long size);
static u_long move_ems_to_conv(short handle, u_short src_seg,
u_short src_offset, u_long dst_addr, u_long length);
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);
static u_long move_ems_to_ems(u_short src_hande, u_short src_seg,
u_short src_offset, u_short dst_handle,
u_short dst_seg, u_short dst_offset, u_long length);
/*
* EMS initialization routine: Return 1, if successful, return 0 if
* init problem or EMS disabled
*/
int
ems_init()
{
int 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;
int 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:
{
u_long addr;
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 %d to %d 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 = N_GETPTR(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 = N_GETPTR(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;
unknown:
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;
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_INHERIT | MAP_SHARED,
-1, 0) < 0) {
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_INHERIT | MAP_SHARED,
-1, 0) < 0)
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_INHERIT | MAP_SHARED,
mapfile_fd, file_offs) < 0) {
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 = N_GETPTR(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");
}
/* 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)
{
int 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)
{
int syspagenum;
int pages_to_alloc;
int allocpagenum;
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;
int 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;
}