--- /dev/null
+//--------------------------------------------------------------------------
+//
+// XMSARRAY.CPP: body of XMSarray interface library.
+// Copyright (c) J.English 1993.
+// Author's address: je@unix.brighton.ac.uk
+//
+// Permission is granted to use copy and distribute the
+// information contained in this file provided that this
+// copyright notice is retained intact and that any software
+// or other document incorporating this file or parts thereof
+// makes the source code for the library of which this file
+// is a part freely available.
+//
+//--------------------------------------------------------------------------
+//
+// Revision history:
+// 1.0 Jun 1993 Initial coding
+// 1.01 Sep 1993 Filenames changed from XMS.* to XMSARRAY.*
+// 2.0 Nov 1993 Revised to use general purpose XMS class
+//
+//--------------------------------------------------------------------------
+
+#include "xms.h"
+#include "xmsarray.h"
+#include <dos.h>
+#include <stdlib.h>
+\f
+//--------------------------------------------------------------------------
+//
+// Constants.
+//
+const int USED = 1, // cache line is in use
+ DIRTY = 2; // cache line has been written to
+
+
+//--------------------------------------------------------------------------
+//
+// Data structures.
+//
+struct XMSbuffer // cache structure
+{
+ int mask; // ... mask to extract item number in buffer
+ long where; // ... offset to start of XMS block
+ unsigned width; // ... unit for XMS transfers in bytes
+ char* cache; // ... buffer for XMS transfers
+ long tag; // ... current index held in buffer
+ char flags; // ... buffer state (in use, dirty)
+};
+
+struct XMSheader // header for XMS block
+{
+ long size; // ... block size in bytes
+ int free; // ... is it a free block?
+};
+
+struct XMSmove // descriptor for XMS moves
+{
+ long count; // ... number of bytes to move
+ int srchandle; // ... source handle (0 for real memory)
+ long srcoffset; // ... source offset (or far pointer)
+ int dsthandle; // ... destination handle (0 for real memory)
+ long dstoffset; // ... destination offset (or far pointer)
+};
+
+
+//--------------------------------------------------------------------------
+//
+// Globals (statics).
+//
+static XMS* arrayBlock = 0; // block used to hold XMS arrays
+static int arrayCount = 0; // number of allocated XMS arrays
+static long XMSsize = 0; // current allocation size, internal reckoning
+ // (XMS driver rounds up to nearest 4K or 16K)
+
+//--------------------------------------------------------------------------
+//
+// Function to return the number of bits required to represent "n",
+// used for rounding up to powers of 2 and generating masks.
+//
+static int bits (unsigned n)
+{
+ int b = 0;
+ for (long i = 1; i > 0 && i < n; i <<= 1)
+ b++;
+ return b;
+}
+\f
+//--------------------------------------------------------------------------
+//
+// XMSlocate: locate a free block of XMS.
+//
+// This is used by XMSalloc to check if there is a block of suitable
+// size which has been returned to the free list. If not, it returns
+// 0 (which is never a legitimate offset) and if so, it divides the
+// block as necessary, marks the allocated block as in use and returns
+// its offset. XMS blocks are prefaced by an XMSheader giving their
+// size and status (free/in use). Offsets refer to the first byte
+// of the allocation (i.e. the byte after the header); the size then
+// gives the address of the next block. Adjacent free blocks are merged
+// while scanning the list. If the last block is free, it can't be
+// merged with anything and so XMSsize is adjusted to remove it from
+// the internal record of the allocation size.
+//
+static long XMSlocate (long size)
+{
+ long p = 0, oldp = 0;
+ XMSheader h, oldh = {0,0};
+
+ //--- scan block list from offset 0
+ while (p < XMSsize)
+ {
+ //--- get header of next block
+ XMS::copy (&h, (*arrayBlock)[p], sizeof(h));
+
+ //--- merge adjacent free blocks by changing size of previous block
+ if (oldh.free && h.free)
+ { oldh.size += h.size + sizeof(h);
+ h = oldh, p = oldp;
+ XMS::copy ((*arrayBlock)[p], &h, sizeof(h));
+ }
+
+ //--- check if block (after possible merging) is big enough
+ if (h.free && h.size >= size + sizeof(XMSheader))
+ break;
+
+ //--- keep value and offset for block and get offset of next header
+ oldh = h, oldp = p;
+ p += h.size + sizeof(h);
+ }
+
+ //--- no suitable block available: remove last block if it is free
+ if (p >= XMSsize)
+ { if (oldh.free)
+ XMSsize = oldp;
+ return 0;
+ }
+
+ //--- divide block if necessary
+ if (h.free && h.size >= size + sizeof(XMSheader))
+ {
+ XMSheader t = {h.size - size - sizeof(t), 1};
+ XMS::copy (arrayBlock->at(p+sizeof(h)+size), &t, sizeof(t));
+ h.size = size;
+ }
+
+ //--- write new block's header and return offset of first usable byte
+ h.free = 0;
+ XMS::copy (arrayBlock->at(p), &h, sizeof(h));
+ return p + sizeof(h);
+}
+\f
+//--------------------------------------------------------------------------
+//
+// XMSalloc: allocate a block of XMS.
+//
+// This function tries to allocate "size" bytes of XMS and returns 0
+// if it fails.
+//
+static long XMSalloc (long size)
+{
+ //--- check XMS initialised before proceeding
+ if (arrayCount++ == 0)
+ { arrayBlock = new XMS (sizeof(XMSheader) + size);
+ if (arrayBlock == 0 || !arrayBlock->valid())
+ { delete arrayBlock;
+ arrayCount--;
+ arrayBlock = 0;
+ }
+ else
+ { XMSheader h = {0,0};
+ XMS::copy (arrayBlock->at(0), &h, sizeof(XMSheader));
+ }
+ }
+ if (arrayBlock == 0)
+ return 0;
+
+ //--- try to find a suitable free block in the current allocation
+ long pos = XMSlocate (size);
+ if (pos != 0)
+ return pos;
+
+ //--- try to extend current allocation if none found
+ if (arrayBlock->resize (XMSsize + size + sizeof(XMSheader)) != XMS::SUCCESS)
+ return 0;
+
+ //--- write the header for the new block at the end of current allocation
+ XMSheader h = {size,0};
+ XMS::copy (arrayBlock->at(XMSsize), &h, sizeof(h));
+
+ //--- get offset of first usable byte of new block
+ pos = XMSsize + sizeof(h);
+
+ //--- increase allocation size to accomodate it and return its offset
+ XMSsize += size + sizeof(h);
+ return pos;
+}
+
+
+//--------------------------------------------------------------------------
+//
+// XMSfree: mark a block of XMS as free.
+//
+// This returns a block to the free list by rewriting its header.
+//
+static void XMSfree (long offset)
+{
+ if (--arrayCount == 0)
+ { delete arrayBlock;
+ return;
+ }
+ XMSheader h;
+ XMS::copy (&h, arrayBlock->at(offset-sizeof(h)), sizeof(h));
+ h.free = 1;
+ XMS::copy (arrayBlock->at(offset-sizeof(h)), &h, sizeof(h));
+}
+\f
+//--------------------------------------------------------------------------
+//
+// XMSarrayBase::XMSarrayBase.
+//
+// Constructor for base class XMS, called by constructor for XMSarray.
+// Constructs an XMS array containing the specified number of items of
+// the specified size and also a one-line cache of the specified size.
+//
+XMSarrayBase::XMSarrayBase (long items, unsigned size, unsigned cachesize)
+{
+ //--- initial state is "unallocated"
+ state = 0;
+
+ //--- create the cache
+ buffer = new XMSbuffer;
+ if (buffer == 0)
+ return;
+
+ //--- set up the item offset mask (items in line - 1)
+ buffer->mask = (size > cachesize) ? 0 :
+ (1 << (bits(cachesize) - bits(size))) - 1;
+
+ //--- calculate number of items needed (1 extra rounded up to nearest line)
+ if (items < 0)
+ items = 0;
+ items += buffer->mask;
+ items &= ~buffer->mask;
+
+ //--- calculate cache size in bytes and allocate it
+ buffer->width = (buffer->mask + 1) * size;
+ buffer->cache = new char [buffer->width];
+ if (buffer->cache == 0)
+ return;
+
+ //--- try to allocate the XMS block
+ buffer->where = XMSalloc (items * size);
+
+ //--- mark cache line as unused
+ buffer->tag = 0;
+ buffer->flags = 0;
+
+ //--- record if allocation succeeded
+ state = (buffer->where != 0);
+}
+
+
+//--------------------------------------------------------------------------
+//
+// XMSarrayBase::XMSarrayBase.
+//
+// Copy constructor used by XMSitem to clone a reference to the
+// base array.
+//
+XMSarrayBase::XMSarrayBase (const XMSarrayBase& base)
+{
+ state = base.state;
+ buffer = base.buffer;
+}
+\f
+//--------------------------------------------------------------------------
+//
+// XMSarrayBase::free.
+//
+// Try to free an array. Check that it's there before doing anything,
+// then free the XMS block and delete the cache. This is not a written
+// as a destructor so that XMSitem doesn't call it. Unfortunately
+// Borland C++ won't let you nominate all possible instantiations of
+// a template as friends of a non-template class, i.e.
+// class X { template<class T> friend class Y<T>; ... };
+// gives a compilation error.
+//
+void XMSarrayBase::free ()
+{
+ if (buffer == 0)
+ return;
+ if (buffer->where != 0)
+ XMSfree (buffer->where);
+ delete buffer->cache;
+ delete buffer;
+}
+
+
+//--------------------------------------------------------------------------
+//
+// XMSarrayBase::mask.
+//
+// Return the item number within the cache line for a given subscript.
+//
+int XMSarrayBase::mask (int n)
+{
+ if (!state)
+ return 0;
+ return n & buffer->mask;
+}
+\f
+//--------------------------------------------------------------------------
+//
+// XMSarrayBase::get.
+//
+// Get a block from XMS into conventional memory. If it's not in the
+// cache, load the relevant line (writing back the old line if it was
+// dirty). Return a pointer to the cached copy of the item. "Base"
+// is the base address of the line to load and "offset" is the offset
+// into the cache buffer.
+//
+void* XMSarrayBase::get (long base, int offset)
+{
+ if (!state)
+ return 0;
+ if (buffer->tag != base || !(buffer->flags & USED))
+ { if (buffer->flags & DIRTY)
+ XMS::copy (arrayBlock->at (buffer->where + buffer->tag),
+ buffer->cache, buffer->width);
+ XMS::copy (buffer->cache, arrayBlock->at (buffer->where + base),
+ buffer->width);
+ buffer->flags = USED;
+ buffer->tag = base;
+ }
+ return buffer->cache + offset;
+}
+
+
+//--------------------------------------------------------------------------
+//
+// XMSarrayBase::put.
+//
+// Gets a copy of the relevant cache line into memory and then marks
+// the line as dirty (about to be written to).
+//
+void* XMSarrayBase::put (long base, int offset)
+{
+ if (!state)
+ return 0;
+ void* addr = get (base, offset);
+ buffer->flags |= DIRTY;
+ return addr;
+}
--- /dev/null
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/doscmd/ems.c,v 1.3.2.2 2002/04/25 11:04:51 tg Exp $");
+
+/*
+ * 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 __unused)
+{
+ 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;
+}