diff options
author | jlemon <jlemon@FreeBSD.org> | 1997-08-15 23:41:26 +0000 |
---|---|---|
committer | jlemon <jlemon@FreeBSD.org> | 1997-08-15 23:41:26 +0000 |
commit | d637f4f507efd469c196718fb89e0b3eb445a20d (patch) | |
tree | 33d2032c2c6a52ff1d5f64c89c5cd8bbfd3d65b2 /usr.bin/doscmd | |
parent | 3bc0e3d9d18baa98ce9fc4f6ee87c1e48d91f530 (diff) | |
download | FreeBSD-src-d637f4f507efd469c196718fb89e0b3eb445a20d.zip FreeBSD-src-d637f4f507efd469c196718fb89e0b3eb445a20d.tar.gz |
Add support for XMS memory to doscmd.
Submitted by: Helmut F. Wirth <hfwirth@ping.at>
Diffstat (limited to 'usr.bin/doscmd')
-rw-r--r-- | usr.bin/doscmd/Makefile | 12 | ||||
-rw-r--r-- | usr.bin/doscmd/bios.c | 4 | ||||
-rw-r--r-- | usr.bin/doscmd/doscmd.1 | 5 | ||||
-rw-r--r-- | usr.bin/doscmd/doscmd.c | 33 | ||||
-rw-r--r-- | usr.bin/doscmd/doscmd.h | 6 | ||||
-rw-r--r-- | usr.bin/doscmd/xms.c | 1056 | ||||
-rw-r--r-- | usr.bin/doscmd/xms.h | 145 |
7 files changed, 1193 insertions, 68 deletions
diff --git a/usr.bin/doscmd/Makefile b/usr.bin/doscmd/Makefile index 16799ea..b3d0514 100644 --- a/usr.bin/doscmd/Makefile +++ b/usr.bin/doscmd/Makefile @@ -1,15 +1,13 @@ # from BSDI Makefile,v 2.6 1996/04/08 20:06:40 bostic Exp # -# $Id: Makefile,v 1.3 1997/08/09 20:04:04 ache Exp $ +# $Id: Makefile,v 1.4 1997/08/12 16:08:02 ache Exp $ PROG= doscmd MAN1= doscmd.1 -SRCS= AsyncIO.c ParseBuffer.c \ - bios.c callback.c cpu.c dos.c cmos.c config.c cwd.c debug.c disktab.c doscmd.c \ - exe.c i386-pinsn.c int.c \ - int10.c int13.c int14.c int16.c int17.c \ - int1a.c int2f.c intff.c \ - mem.c mouse.c net.c port.c setver.c signal.c timer.c trace.c trap.c tty.c xms.c +SRCS= AsyncIO.c ParseBuffer.c bios.c callback.c cpu.c dos.c cmos.c config.c \ + cwd.c debug.c disktab.c doscmd.c exe.c i386-pinsn.c int.c int10.c \ + int13.c int14.c int16.c int17.c int1a.c int2f.c intff.c mem.c mouse.c \ + net.c port.c setver.c signal.c timer.c trace.c trap.c tty.c xms.c CLEANFILES= doscmd.kernel crt0.o doscmd_loader.o instbsdi.exe diff --git a/usr.bin/doscmd/bios.c b/usr.bin/doscmd/bios.c index ea4090e..4cad864 100644 --- a/usr.bin/doscmd/bios.c +++ b/usr.bin/doscmd/bios.c @@ -29,7 +29,7 @@ * * BSDI bios.c,v 2.3 1996/04/08 19:32:19 bostic Exp * - * $Id: bios.c,v 1.4 1996/09/22 15:42:47 miff Exp $ + * $Id: bios.c,v 1.1 1997/08/09 01:42:33 dyson Exp $ */ #include "doscmd.h" @@ -137,7 +137,7 @@ int15(regcontext_t *REGS) */ break; case 0x88: - R_AX = 0; /* memory past 1M */ + get_raw_extmemory_info(REGS); break; case 0xc0: /* get configuration */ debug (D_TRAPS|0x15, "Get configuration\n", R_DX); diff --git a/usr.bin/doscmd/doscmd.1 b/usr.bin/doscmd/doscmd.1 index baff6d5..c1e1ba3 100644 --- a/usr.bin/doscmd/doscmd.1 +++ b/usr.bin/doscmd/doscmd.1 @@ -250,6 +250,11 @@ Same as .\" .\" .\" +.It Fl X +Enable debugging of the XMS operations. +.\" +.\" +.\" .It Fl x Open an X11 window to display output. This enables a variety interrupts not available otherwise. This diff --git a/usr.bin/doscmd/doscmd.c b/usr.bin/doscmd/doscmd.c index 282aeab..295b6a4 100644 --- a/usr.bin/doscmd/doscmd.c +++ b/usr.bin/doscmd/doscmd.c @@ -29,7 +29,7 @@ * * BSDI doscmd.c,v 2.3 1996/04/08 19:32:30 bostic Exp * - * $Id: doscmd.c,v 1.10 1997/03/18 02:36:55 msmith Exp $ + * $Id: doscmd.c,v 1.1 1997/08/09 01:42:41 dyson Exp $ */ #include <sys/types.h> @@ -96,9 +96,6 @@ static char *envs[256]; static char *dos_path = 0; char cmdname[256]; /* referenced from dos.c */ -/* high memory mapfile */ -static char *memfile = "/tmp/doscmd.XXXXXX"; - static struct i386_vm86_args vm86; static struct vm86_init_args kargs; @@ -116,7 +113,6 @@ main(int argc, char **argv) int fd; int i; char buffer[4096]; - int mfd; FILE *fp; @@ -142,29 +138,7 @@ main(int argc, char **argv) setbuf (stdout, NULL); } - mfd = mkstemp(memfile); - - if (mfd < 0) { - fprintf(stderr, "memfile: %s\n", strerror(errno)); - fprintf(stderr, "High memory will not be mapped\n"); - } else { - caddr_t add; - - unlink(memfile); - - mfd = squirrel_fd(mfd); - - lseek(mfd, 64 * 1024 - 1, 0); - write(mfd, "", 1); - add = mmap((caddr_t)0x000000, 64 * 1024, - PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, - mfd, 0); - add = mmap((caddr_t)0x100000, 64 * 1024, - PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, - mfd, 0); - } + initHMA(); /* This needs to happen before the executable is loaded */ mem_init(); @@ -595,6 +569,9 @@ do_args(int argc, char *argv[]) case 'R': debug_flags |= D_REDIR; break; + case 'X': + debug_flags |= D_XMS; + break; case 'L': debug_flags |= D_PRINTER; break; diff --git a/usr.bin/doscmd/doscmd.h b/usr.bin/doscmd/doscmd.h index 43d2b30..27aa07f 100644 --- a/usr.bin/doscmd/doscmd.h +++ b/usr.bin/doscmd/doscmd.h @@ -29,7 +29,7 @@ * * BSDI doscmd.h,v 2.3 1996/04/08 19:32:32 bostic Exp * - * $Id: doscmd.h,v 1.9 1996/09/23 09:59:25 miff Exp $ + * $Id: doscmd.h,v 1.1 1997/08/09 01:43:09 dyson Exp $ */ @@ -106,6 +106,7 @@ extern int debug_flags; #define D_TRAPS3 0x0200000 #define D_DEBUGIN 0x0400000 #define D_DOSCALL 0x0800000 /* MS-DOS function results */ +#define D_XMS 0x1000000 /* XMS calls */ #define TTYF_ECHO 0x00000001 #define TTYF_ECHONL 0x00000003 @@ -230,6 +231,9 @@ extern void printer_timeout(int printer, char *time_out); /* xms.c */ extern int int2f_43(regcontext_t *REGS); +extern void get_raw_extmemory_info(regcontext_t *REGS); +extern void initHMA(void); +extern u_long xms_maxsize; /****************************** dirty below here ******************************/ diff --git a/usr.bin/doscmd/xms.c b/usr.bin/doscmd/xms.c index c1524de..f9df109 100644 --- a/usr.bin/doscmd/xms.c +++ b/usr.bin/doscmd/xms.c @@ -1,12 +1,466 @@ +/*- + * 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. + * + * $Id$ + */ + /* -** No copyright?! -** -** $Id: xms.c,v 1.3 1996/09/22 15:43:01 miff Exp $ -*/ + * XMS memory manmagement + * + * To emulate DOS extended memory (EMM) we use an implementation of + * HIMEM.SYS driver capabitlities, according to the XMS 3.0 Spec. + * The actual memory allocated via XMS calls from DOS is allocated + * via malloc by the emulator. Maximum memory allocation is configureable. + * + * Credits to: + * The original author of this file, some parts are still here + * Linux dosemu programmers. I looked into their code. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <unistd.h> + #include "doscmd.h" +#include "xms.h" + + +/* Extended memory handle management */ + +static XMS_handle xms_hand[NUM_HANDLES]; +int num_free_handle = NUM_HANDLES; +/* This is planned to be selectable from .doscmdrc */ +u_long xms_maxsize = DEFAULT_EMM_SIZE; +static u_long xms_free_mem; +static u_long xms_used_mem; +static u_char vec_grabbed; + +/* Address entry for zero size allocated handles */ +#define XMS_NULL_ALLOC 0xffffffff + +/* High memory area (HMA) management */ +static u_char HMA_allocated = 0; +static short HMA_a20 = -1; +static int HMA_fd_off, HMA_fd_on; + +/* high memory mapfiles */ +static char *memfile = "/tmp/doscmd.XXXXXX"; + +/* Upper memory block (UMB) management */ +UMB_block *UMB_freelist = NULL; +UMB_block *UMB_alloclist = NULL; + +/* Calls to emulator */ u_long xms_vector; +static u_char xms_trampoline[] = { + 0xeb, /* JMP 5 */ + 0x03, + 0x90, /* NOP */ + 0x90, /* NOP */ + 0x90, /* NOP */ + 0xf4, /* HLT */ + 0xcb, /* RETF */ +}; + +/* Local prototypes */ +static void xms_entry(regcontext_t *REGS); +static UMB_block *create_block(u_long addr, u_long size); +static void add_block(UMB_block **listp, UMB_block *blk); +static void merge_blocks(); + +/* Init the entire module */ +void +xms_init(void) +{ + int i; + + /* Initialize handle table: xms_handle.addr == 0 means free */ + bzero((void *)xms_hand, sizeof(XMS_handle) * NUM_HANDLES); + xms_free_mem = xms_maxsize; + xms_used_mem = 0; + vec_grabbed = 0; + HMA_allocated = 0; + /* Initialize UMB blocks */ + /* 0xD0000 to 0xDffff */ + add_block(&UMB_freelist, create_block(0xd0000, 64*1024)); + /*XXX check for EMS emulation, when it is done! */ + /* 0xE0000 to 0xEffff */ + add_block(&UMB_freelist, create_block(0xe0000, 64*1024)); + merge_blocks(); + + xms_vector = insert_generic_trampoline( + sizeof(xms_trampoline), xms_trampoline); + register_callback(xms_vector + 5, xms_entry, "xms"); +} + +/* + * UMB management routines: UMBs normally lie between 0xd0000 and + * 0xefff0 in VM86 memory space and are accessible for all DOS applictions. + * We could enable more space, but with the emulator we do not + * need many drivers, so I think 2 * 64kB will suffice. If EMS emulation + * exists, a 64kB segment (0xe0000 - 0xeffff for example) is needed for + * the EMS mapping, in this case we have 64kB UMB space. This is more than + * many PCs are able to do. + * This emulation does only the management for the memory, the memory + * is present and read/write/excutable for VM86 applications. + */ + +/* Add a block to a list, maintain ascending start address order */ + +static void +add_block(UMB_block **listp, UMB_block *blk) +{ + UMB_block *bp, *obp; + + /* No blocks there, attach the new block to the head */ + if (*listp == NULL) { + *listp = blk; + blk->next = NULL; + } + else { + /* Insert at the start */ + bp = obp = *listp; + if (blk->addr < bp->addr) { + blk->next = *listp; + *listp = blk; + return; + } + /* Not at the start, insert into the list */ + for (; bp != NULL; bp = bp->next) { + if (blk->addr > bp->addr) { + obp = bp; + continue; + } + else { + obp->next = blk; + blk->next = bp; + return; + } + } + /* Append to the end of the list */ + obp->next = blk; + blk->next = NULL; + } + return; +} + +/* Find a block with address addr in the alloc list */ +static UMB_block * +find_allocated_block(u_long addr) +{ + UMB_block *bp; + + if (UMB_alloclist == NULL) + return NULL; + + for (bp = UMB_alloclist; bp != NULL; bp = bp->next) + if (bp->addr == addr) + return bp; + return NULL; +} + +/* Remove a block blk from a list, the block must exist on the list */ +static void +remove_block(UMB_block **listp, UMB_block *blk) +{ + UMB_block *bp; + + if (*listp == NULL) + goto faterr; + + if (*listp == blk) { + *listp = (*listp)->next; + return; + } + bp = *listp; + do { + if (bp->next == blk) { + bp->next = bp->next->next; + return; + } + bp = bp->next; + } while(bp != NULL); +faterr: + fatal("XMS: UMB remove_block did not find block\n"); +} + +/* Try to merge neighbouring blocks in the free list */ +static void +merge_blocks() +{ + UMB_block *bp; + u_long endaddr; + + if (UMB_freelist == NULL) + return; + bp = UMB_freelist; + do { + endaddr = bp->addr + bp->size; + if (bp->next != NULL && endaddr == bp->next->addr) { + /* Merge the current and the next block */ + UMB_block *mergebp = bp->next; + bp->size += mergebp->size; + bp->next = mergebp->next; + free(mergebp); + } + else + /* Goto next block */ + bp = bp->next; + } while (bp != NULL); +} + +/* Try to find a free block of size exactly siz */ +static UMB_block * +find_exact_block(u_long siz) +{ + UMB_block *bp; + + if (UMB_freelist == NULL) + return NULL; + + for (bp = UMB_freelist; bp != NULL; bp = bp->next) + if (bp->size == siz) + return bp; + return NULL; +} + +/* Try to find a block with a size bigger than requested. If there is + * no such block, return the block with the biggest size. If there is + * no free block at all, return NULL + */ +static UMB_block * +find_block(u_long siz) +{ + UMB_block *bp; + UMB_block *biggest = NULL; + + if (UMB_freelist == NULL) + return NULL; + + for (bp = UMB_freelist; bp != NULL; bp = bp->next) { + if (bp->size > siz) + return bp; + if (biggest == NULL) { + biggest = bp; + continue; + } + if (biggest->size < bp->size) + biggest = bp; + } + return biggest; +} + +/* Create a block structure, memory is allocated. The structure lives + * until the block is merged into another block, then it is freed */ +static UMB_block * +create_block(u_long addr, u_long size) +{ + UMB_block *blk; + + if ((blk = malloc(sizeof(UMB_block))) == NULL) + fatal ("XMS: Cannot allocate UMB structure\n"); + blk->addr = addr; + blk->size = size; + blk->next = NULL; + return blk; +} + + +/* + * initHMA(): The first 64kB of memory are mapped from 1MB (0x100000) + * again to emulate the address wrap around of the 808x. The HMA area + * is a cheap trick, usable only with 80386 and higher. The 80[345..]86 + * does not have this address wrap around. If more than 1MB is installed + * the processor can address more than 1MB: load 0xFFFF to the segment + * register and using the full offset of 0xffff the resulting highest + * address is (0xffff << 4) + 0xffff = 0x10ffef. Nearly 64kB are accessible + * from real or VM86 mode. The mmap calls emulate the address wrap by + * mapping the lowest 64kB the the first 64kB after the 1MB limit. + * In hardware this is achieved by setting and resetting the a20 bit, + * an ugly compatibility hack: The hardware simlpy clamps the address 20 + * line of the processor to low and hence the wrap around is forced. + * This is switchable via the BIOS or via HIMEM.SYS and therefore the + * first 64kB over 1MB can be enabled or disabled at will by software. + * DOS uses this trick to load itself high, if the memory is present. + * We emulate this behaviour by mapping and unmapping the HMA area. + * (Linux has implemented this feature using shared memory (SHM) calls.) + * + * This routine is called from doscmd.c at startup. A20 is disabled after + * startup. + */ + +void initHMA() +{ + caddr_t add; + int mfd; + + /* + * We need two files, one for the wrap around mapping and one + * for the HMA contents + */ + + mfd = mkstemp(memfile); + + if (mfd < 0) { + fprintf(stderr, "memfile: %s\n", strerror(errno)); + fprintf(stderr, "High memory will not be mapped\n"); + + /* We need this for XMS services. If it fails, turn HMA off */ + HMA_a20 = -1; + return; + } + unlink(memfile); + HMA_fd_off = squirrel_fd(mfd); + + lseek(HMA_fd_off, 64 * 1024 - 1, 0); + write(HMA_fd_off, "", 1); + + mfd = mkstemp(memfile); + + if (mfd < 0) { + fprintf(stderr, "memfile: %s\n", strerror(errno)); + fprintf(stderr, "High memory will not be mapped\n"); + + /* We need this for XMS services. If it fails, turn HMA off */ + HMA_a20 = -1; + return; + } + unlink(memfile); + HMA_fd_on = squirrel_fd(mfd); + + lseek(HMA_fd_on, 64 * 1024 - 1, 0); + write(HMA_fd_on, "", 1); + + if (mmap((caddr_t)0x000000, 64 * 1024, + PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + HMA_fd_off, 0) < 0) { + perror("Error mapping HMA, HMA disabled: "); + HMA_a20 = -1; + close(HMA_fd_off); + close(HMA_fd_on); + return; + } + if (mmap((caddr_t)0x100000, 64 * 1024, + PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + HMA_fd_off, 0) < 0) { + perror("Error mapping HMA, HMA disabled: "); + HMA_a20 = -1; + close(HMA_fd_off); + close(HMA_fd_on); + return; + } + HMA_a20 = 0; +} + + +/* Enable the a20 "address line" by unmapping the 64kB over 1MB */ +static void enable_a20() +{ + if (HMA_a20 < 0) + return; + + /* Unmap the wrap around portion (fd = HMA_fd_off) */ + /* XXX Not sure about this: Should I unmap first, then map new or + * does it suffice to map new "over' the existing mapping ? Both + * works (define to #if 0 next line and some lines below to try. + */ +#if 1 + if (munmap((caddr_t)0x100000, 64 * 1024) < 0) { + fatal("HMA unmapping error: %s\nCannot recover\n", strerror(errno)); + } +#endif + /* Map memory for the HMA with fd = HMA_fd_on */ + if (mmap((caddr_t)0x100000, 64 * 1024, + PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + HMA_fd_on, 0) < 0) { + fatal("HMA mapping error: %s\nCannot recover\n", strerror(errno)); + } +} + +/* Disable the a20 "address line" by mapping the 64kB over 1MB again */ +static void disable_a20() +{ + if (HMA_a20 < 0) + return; +#if 1 + /* Unmap the HMA (fd = HMA_fd_on) */ + if (munmap((caddr_t)0x100000, 64 * 1024) < 0) { + fatal("HMA unmapping error: %s\nCannot recover\n", strerror(errno)); + } +#endif + /* Remap the wrap around area */ + if (mmap((caddr_t)0x100000, 64 * 1024, + PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + HMA_fd_off, 0) < 0) { + fatal("HMA mapping error: %s\nCannot recover\n", strerror(errno)); + } +} + + +/* + * This handles calls to int15 function 88: BIOS extended memory + * request. XMS spec says: "In order to maintain compatibility with existing + * device drivers, DOS XMS drivers must not hook INT 15h until the first + * non-Version Number call to the control function is made." + */ + +void +get_raw_extmemory_info(regcontext_t *REGS) +{ + if (vec_grabbed) + R_AX = 0x0; + else + R_AX = xms_maxsize / 1024; + return; +} + + +/* Handle management routine: Find next free handle */ + +static int +get_free_handle() +{ + int i; + /* Linear search, there are only a few handles */ + for (i = 0; i < NUM_HANDLES; i++) { + if (xms_hand[i].addr == 0) + return i + 1; + } + return 0; +} + +/* Installation check */ int int2f_43(regcontext_t *REGS) { @@ -26,40 +480,582 @@ int2f_43(regcontext_t *REGS) return (1); } -/* -** XXX DANGER WILL ROBINSON! -*/ +/* Main call entry point for the XMS handler from DOS */ static void xms_entry(regcontext_t *REGS) { + + if (R_AH != 0) { + if (HMA_a20 < 0) { /* This feature is disabled */ + R_AX = 0x0; + R_BL = XMS_HMA_NOT_MANAGED; + return; + } + vec_grabbed = 1; + } + switch (R_AH) { - case 0x00: /* get version number */ - R_AX = 0x0300; /* 3.0 */ - R_BX = 0x0001; /* internal revision 0.1 */ - R_DX = 0x0001; /* HMA exists */ + case XMS_GET_VERSION: + debug(D_XMS, "XMS: Get Version\n"); + R_AX = XMS_VERSION; /* 3.0 */ + R_BX = XMS_REVISION; /* internal revision 0 */ + R_DX = (HMA_a20 < 0) ? 0x0000 : 0x0001; + break; + + /* + * XXX Not exact! Spec says compare size to a HMAMIN parameter and + * refuse HMA, if space is too small. With MSDOS 5.0 and higher DOS + * itself uses the HMA (DOS=HIGH), so I think we can safely ignore + * that. + */ + case XMS_ALLOCATE_HIGH_MEMORY: + debug(D_XMS, "XMS: Allocate HMA\n"); + if (HMA_allocated) { + R_AX = 0x0; + R_BL = XMS_HMA_ALREADY_USED; + } + else { + HMA_allocated = 1; + R_AX = 0x1; + R_BL = XMS_SUCCESS; + } + break; + + case XMS_FREE_HIGH_MEMORY: + debug(D_XMS, "XMS: Free HMA\n"); + if (HMA_allocated) { + HMA_allocated = 0; + R_AX = 0x1; + R_BL = XMS_SUCCESS; + } + else { + R_AX = 0x0; + R_BL = XMS_HMA_NOT_ALLOCATED; + } + break; + + case XMS_GLOBAL_ENABLE_A20: + debug(D_XMS, "XMS: Global enable A20\n"); + if (HMA_a20 == 0) + enable_a20(); + HMA_a20 = 1; + R_AX = 0x1; + R_BL = XMS_SUCCESS; + break; + + case XMS_GLOBAL_DISABLE_A20: + debug(D_XMS, "XMS: Global disable A20\n"); + if (HMA_a20 != 0) + disable_a20(); + HMA_a20 = 0; + R_AX = 0x1; + R_BL = XMS_SUCCESS; + break; + + /* + * This is an accumulating call. Every call increments HMA_a20. + * Caller must use LOCAL_DISBALE_A20 once for each previous call + * to LOCAL_ENABLE_A20. + */ + case XMS_LOCAL_ENABLE_A20: + debug(D_XMS, "XMS: Local enable A20\n"); + HMA_a20++; + if (HMA_a20 == 1) + enable_a20(); + R_AX = 0x1; + R_BL = XMS_SUCCESS; + break; + + case XMS_LOCAL_DISABLE_A20: + debug(D_XMS, "XMS: Local disable A20\n"); + if (HMA_a20 > 0) + HMA_a20--; + if (HMA_a20 == 0) + disable_a20(); + R_AX = 0x1; + R_BL = XMS_SUCCESS; + break; + + case XMS_QUERY_A20: + /* + * Disabled because DOS permanently scans this, to avoid endless output. + */ +#if 0 + debug(D_XMS, "XMS: Query A20\n"); */ +#endif + R_AX = (HMA_a20 > 0) ? 0x1 : 0x0; + R_BL = XMS_SUCCESS; + break; + + case XMS_QUERY_FREE_EXTENDED_MEMORY: + R_AX = R_DX = xms_free_mem / 1024; + R_BL = XMS_SUCCESS; + debug(D_XMS, "XMS: Query free EMM: Returned %dkB\n", R_AX); + break; + + case XMS_ALLOCATE_EXTENDED_MEMORY: + { + size_t req_siz; + int hindx, hnum; + void *mem; + + debug(D_XMS, "XMS: Allocate EMM: "); + /* Enough handles ? */ + if ((hnum = get_free_handle()) == 0) { + R_AX = 0x00; + R_BL = XMS_OUT_OF_HANDLES; + debug(D_XMS, " Out of handles\n"); + break; + } + hindx = hnum - 1; + req_siz = R_DX * 1024; + + /* Enough memory ? */ + if (req_siz > xms_free_mem) { + R_AX = 0x00; + R_BL = XMS_FULL; + debug(D_XMS, " No memory left\n"); + break; + } + + xms_hand[hindx].size = req_siz; + xms_hand[hindx].num_locks = 0; + + /* XXX + * Not sure about that: Is it possible to reserve a handle + * but with no memory attached ? XMS specs are unclear on + * that point. Linux implementation does it this way. + */ + if (req_siz == 0) + /* This handle is reserved, but has size 0 and no address */ + xms_hand[hindx].addr = XMS_NULL_ALLOC; + else { + if ((mem = malloc(req_siz)) == NULL) + fatal("XMS: Cannot malloc !"); + xms_hand[hindx].addr = (u_long)mem; + } + xms_free_mem -= req_siz; + xms_used_mem += req_siz; + num_free_handle--; + R_AX = 0x1; + R_DX = hnum; + R_BL = XMS_SUCCESS; + debug(D_XMS, " Allocated %d kB, handle %d\n", + req_siz / 1024, hnum); + break; + } + + case XMS_FREE_EXTENDED_MEMORY: + { + int hnum, hindx; + + debug(D_XMS, "XMS: Free EMM: "); + hnum = R_DX; + if (hnum > NUM_HANDLES || hnum == 0) { + R_AX = 0x0; + R_BL = XMS_INVALID_HANDLE; + debug(D_XMS, " Invalid handle\n"); + break; + } + hindx = hnum - 1; + + if (xms_hand[hindx].addr == 0) { + R_AX = 0x0; + R_BL = XMS_INVALID_HANDLE; + debug(D_XMS, " Invalid handle\n"); + } + else if (xms_hand[hindx].num_locks > 0) { + R_AX = 0x0; + R_BL = XMS_BLOCK_IS_LOCKED; + debug(D_XMS, " Is locked\n"); + } + else { + if (xms_hand[hindx].addr != XMS_NULL_ALLOC) { + free((void *)xms_hand[hindx].addr); + xms_free_mem += xms_hand[hindx].size; + xms_used_mem -= xms_hand[hindx].size; + } + xms_hand[hindx].addr = 0; + xms_hand[hindx].size = 0; + xms_hand[hindx].num_locks = 0; + num_free_handle++; + debug(D_XMS, " Success for handle %d\n", hnum); + R_AX = 0x1; + R_BL = XMS_SUCCESS; + } + break; + } + + case XMS_MOVE_EXTENDED_MEMORY_BLOCK: + { + u_long srcptr, dstptr; + u_long srcoffs, dstoffs; + int srcidx, dstidx; + const struct EMM *eptr; + int n; + + debug(D_XMS, "XMS: Move EMM block: "); + eptr = (struct EMM *)N_GETPTR(R_DS, R_SI); + + /* Sanity check: Don't allow eptr pointing to emulator data */ + if (((u_long)eptr + sizeof(struct EMM)) >= 0x100000) { + R_AX = 0x0; + R_BL = XMS_GENERAL_ERROR; + debug(D_XMS, " Offset to EMM structure wrong\n"); + break; + } + + /* Validate handles and offsets */ + + if (eptr->src_handle > NUM_HANDLES) { + R_AX = 0x0; + R_BL = XMS_INVALID_SOURCE_HANDLE; + debug(D_XMS, " Invalid handle\n"); + break; + } + if (eptr->dst_handle > NUM_HANDLES) { + R_AX = 0x0; + R_BL = XMS_INVALID_DESTINATION_HANDLE; + debug(D_XMS, " Invalid handle\n"); + break; + } + srcidx = eptr->src_handle - 1; + dstidx = eptr->dst_handle - 1; + srcoffs = eptr->src_offset; + dstoffs = eptr->dst_offset; + n = eptr->nbytes; + /* Lenght must be even, see XMS spec */ + if (n & 1) { + R_AX = 0x0; + R_BL = XMS_INVALID_LENGTH; + debug(D_XMS, " Length not even\n"); + break; + } + if (eptr->src_handle != 0) { + srcptr = xms_hand[srcidx].addr; + if (srcptr == 0 || srcptr == XMS_NULL_ALLOC) { + R_AX = 0x0; + R_BL = XMS_INVALID_SOURCE_HANDLE; + debug(D_XMS, " Invalid source handle\n"); + break; + } + if ((srcoffs + n) > xms_hand[srcidx].size) { + R_AX = 0x0; + R_BL = XMS_INVALID_SOURCE_OFFSET; + debug(D_XMS, " Invalid source offset\n"); + break; + } + srcptr += srcoffs; + } + else { + srcptr = VECPTR(srcoffs); + /* Sanity check: Don't allow srcptr pointing to + * emulator data above 1M + */ + if ((srcptr + n) >= 0x100000) { + R_AX = 0x0; + R_BL = XMS_GENERAL_ERROR; + debug(D_XMS, " Source segment invalid\n"); + break; + } + } + + if (eptr->dst_handle != 0) { + dstptr = xms_hand[dstidx].addr; + if (dstptr == NULL || dstptr == XMS_NULL_ALLOC) { + R_AX = 0x0; + R_BL = XMS_INVALID_DESTINATION_HANDLE; + debug(D_XMS, " Invalid dest handle\n"); + break; + } + if ((dstoffs + n) > xms_hand[dstidx].size) { + R_AX = 0x0; + R_BL = XMS_INVALID_DESTINATION_OFFSET; + debug(D_XMS, " Invalid dest offset\n"); + break; + } + dstptr += dstoffs; + } + else { + dstptr = VECPTR(dstoffs); + /* Sanity check: Don't allow dstptr pointing to + * emulator data above 1M + */ + if ((dstptr + n) >= 0x100000) { + R_AX = 0x0; + R_BL = XMS_GENERAL_ERROR; + debug(D_XMS, " Dest segment invalid\n"); + break; + } + } + memmove((void *)dstptr, (void *)srcptr, n); + debug(D_XMS, "Moved from %08x to %08x, %04x bytes\n", + srcptr, dstptr, n); + R_AX = 0x1; + R_BL = XMS_SUCCESS; + break; + } + + case XMS_LOCK_EXTENDED_MEMORY_BLOCK: + { + int hnum,hindx; + + debug(D_XMS, "XMS: Lock EMM block\n"); + hnum = R_DX; + if (hnum > NUM_HANDLES || hnum == 0) { + R_AX = 0x0; + R_BL = XMS_INVALID_HANDLE; + break; + } + hindx = hnum - 1; + if (xms_hand[hindx].addr == 0) { + R_AX = 0x0; + R_BL = XMS_INVALID_HANDLE; + break; + } + if (xms_hand[hindx].num_locks == 255) { + R_AX = 0x0; + R_BL = XMS_BLOCK_LOCKCOUNT_OVERFLOW; + break; + } + xms_hand[hindx].num_locks++; + R_AX = 0x1; + /* + * The 32 bit "physical" address is returned here. I hope + * the solution to simply return the linear address of the + * malloced area is good enough. Most DOS programs won't + * need this anyway. It could be important for future DPMI. + */ + R_BX = xms_hand[hindx].addr & 0xffff; + R_DX = (xms_hand[hindx].addr & 0xffff0000) >> 16; + break; + } + + case XMS_UNLOCK_EXTENDED_MEMORY_BLOCK: + { + int hnum,hindx; + + debug(D_XMS, "XMS: Unlock EMM block\n"); + hnum = R_DX; + if (hnum > NUM_HANDLES || hnum == 0) { + R_AX = 0x0; + R_BL = XMS_INVALID_HANDLE; + break; + } + hindx = hnum - 1; + if (xms_hand[hindx].addr == 0) { + R_AX = 0x0; + R_BL = XMS_INVALID_HANDLE; + break; + } + if (xms_hand[hindx].num_locks == 0) { + R_AX = 0x0; + R_BL = XMS_BLOCK_NOT_LOCKED; + break; + } + xms_hand[hindx].num_locks--; + R_AX = 0x1; + R_BL = XMS_SUCCESS; + break; + } + + case XMS_GET_EMB_HANDLE_INFORMATION: + { + int hnum,hindx; + + debug(D_XMS, "XMS: Get handle information: DX=%04x\n, R_DX"); + hnum = R_DX; + if (hnum > NUM_HANDLES || hnum == 0) { + R_AX = 0x0; + R_BL = XMS_INVALID_HANDLE; + break; + } + hindx = hnum - 1; + if (xms_hand[hindx].addr == 0) { + R_AX = 0x0; + R_BL = XMS_INVALID_HANDLE; + break; + } + R_AX = 0x1; + R_BH = xms_hand[hindx].num_locks; + R_BL = num_free_handle; + R_DX = xms_hand[hindx].size / 1024; + break; + } + + case XMS_RESIZE_EXTENDED_MEMORY_BLOCK: + { + int hnum,hindx; + size_t req_siz; + long sizediff; + void *mem; + + debug(D_XMS, "XMS: Resize EMM block\n"); + hnum = R_DX; + req_siz = R_BX * 1024; + if (hnum > NUM_HANDLES || hnum == 0) { + R_AX = 0x0; + R_BL = XMS_INVALID_HANDLE; + break; + } + hindx = hnum - 1; + if (xms_hand[hindx].addr == 0) { + R_AX = 0x0; + R_BL = XMS_INVALID_HANDLE; + break; + } + if (xms_hand[hindx].num_locks > 0) { + R_AX = 0x0; + R_BL = XMS_BLOCK_IS_LOCKED; + break; + } + sizediff = req_siz - xms_hand[hindx].size; + + if (sizediff > 0) { + if ((sizediff + xms_used_mem) > xms_maxsize) { + R_AX = 0x0; + R_BL = XMS_FULL; + break; + } + } + + if (sizediff == 0) { /* Never trust DOS programs */ + R_AX = 0x1; + R_BL = XMS_SUCCESS; + break; + } + + xms_used_mem += sizediff; + xms_free_mem -= sizediff; + + if (xms_hand[hindx].addr == XMS_NULL_ALLOC) { + if ((mem = malloc(req_siz)) == NULL) + fatal("XMS: Cannot malloc !"); + xms_hand[hindx].addr = (u_long)mem; + xms_hand[hindx].size = req_siz; + } + else { + if ((mem = realloc((void *)xms_hand[hindx].addr,req_siz)) + == NULL) + fatal("XMS: Cannot realloc !"); + xms_hand[hindx].addr = (u_long)mem; + xms_hand[hindx].size = req_siz; + } + + R_AX = 0x1; + R_BL = XMS_SUCCESS; + break; + } + + case XMS_ALLOCATE_UMB: + { + u_long req_siz; + UMB_block *bp; + + debug(D_XMS, "XMS: Allocate UMB: DX=%04x\n", R_DX); + req_siz = R_DX * 16; + /* Some programs try to allocate 0 bytes. XMS spec says + * nothing about this. So the driver grants the request + * but it rounds up to the next paragraph size (1) and + * returns this amount of memory + */ + if (req_siz == 0) + req_siz = 0x10; + + /* First try to find an exact fit */ + if ((bp = find_exact_block(req_siz)) != NULL) { + /* Found ! Move block from free list to alloc list */ + remove_block(&UMB_freelist, bp); + add_block(&UMB_alloclist, bp); + R_AX = 0x1; + R_DX = req_siz >> 4; + R_BX = bp->addr >> 4; + break; + } + /* Try to find a block big enough */ + bp = find_block(req_siz); + if (bp == NULL) { + R_AX = 0x0; + R_BL = XMS_NO_UMBS_AVAILABLE; + R_DX = 0x0; + } + else if (bp->size < req_siz) { + R_AX = 0x0; + R_BL = XMS_REQUESTED_UMB_TOO_BIG; + R_DX = bp->size / 16; + } + else { + UMB_block *newbp; + /* Found a block large enough. Split it into the size + * we need, rest remains on the free list. New block + * goes to the alloc list + */ + newbp = create_block(bp->addr, req_siz); + bp->addr += req_siz; + bp->size -= req_siz; + add_block(&UMB_alloclist, newbp); + R_AX = 0x1; + R_BX = newbp->addr >> 4; + R_DX = req_siz / 16; + } + break; + } + + case XMS_DEALLOCATE_UMB: + { + u_long req_addr; + UMB_block *blk; + + debug(D_XMS, "XMS: Deallocate UMB: DX=%04x\n", R_DX); + req_addr = R_DX << 4; + if ((blk = find_allocated_block(req_addr)) == NULL) { + R_AX = 0x0; + R_BL = XMS_INVALID_UMB_SEGMENT; + } + else { + /* Move the block from the alloc list to the free list + * and try to do garbage collection + */ + remove_block(&UMB_alloclist, blk); + add_block(&UMB_freelist, blk); + merge_blocks(); + R_AX = 0x1; + R_BL = XMS_SUCCESS; + } + break; + } + + + /* + * If the option DOS=UMB is enabled, DOS grabs the entire UMB + * at boot time. In any other case this is used to load resident + * utilities. I don't think this function is neccesary here. + */ + case XMS_REALLOCATE_UMB: + debug(D_XMS, "XMS: Reallocate UMB\n"); + R_AX = 0x0; + R_BL = XMS_NOT_IMPLEMENTED; + break; + + /* These are the same as the above functions, but they use 32 bit + * registers (i.e. EDX instead of DX). This is for allocations of + * more than 64MB. I think this will hardly be used in the emulator + * It seems to work without them, but the functions are in the XMS 3.0 + * spec. If something breaks because they are not here, I can implement + * them + */ + case XMS_QUERY_FREE_EXTENDED_MEMORY_LARGE: + case XMS_ALLOCATE_EXTENDED_MEMORY_LARGE: + case XMS_FREE_EXTENDED_MEMORY_LARGE: + debug(D_XMS, "XMS: 8x function called, not implemented\n"); + R_AX = 0x0; + R_BL = XMS_NOT_IMPLEMENTED; break; default: - debug(D_ALWAYS, "XMS %02x\n", R_AH); + debug(D_ALWAYS, "XMS: Unimplemented function %02x, \n", R_AH); R_AX = 0; + R_BL = XMS_NOT_IMPLEMENTED; break; } } - -static u_char xms_trampoline[] = { - 0xeb, /* JMP 5 */ - 0x03, - 0x90, /* NOP */ - 0x90, /* NOP */ - 0x90, /* NOP */ - 0xf4, /* HLT */ - 0xcb, /* RETF */ -}; - -void -xms_init(void) -{ - xms_vector = insert_generic_trampoline( - sizeof(xms_trampoline), xms_trampoline); - register_callback(xms_vector + 5, xms_entry, "xms"); -} diff --git a/usr.bin/doscmd/xms.h b/usr.bin/doscmd/xms.h new file mode 100644 index 0000000..14a223f --- /dev/null +++ b/usr.bin/doscmd/xms.h @@ -0,0 +1,145 @@ +/*- + * 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. + * + * $Id$ + */ + +#ifndef XMS_H +#define XMS_H + +#define XMS_VERSION 0x0300 /* version 3.00 */ +#define XMS_REVISION 0x0100 /* driver revision 1.0 */ + +#define NUM_HANDLES 64 /* number of available handles */ +#define FIRST_HANDLE 1 /* number of firts valid handle */ +#define PARAGRAPH 16 /* bytes in a paragraph */ +#define MAX_BLOCK_LOCKS 256 /* number of locks on a block */ +#define DEFAULT_EMM_SIZE 512 * 1024 /* default EMM size */ + +/* Register AH codes for XMS functions */ +#define XMS_GET_VERSION 0x00 +#define XMS_ALLOCATE_HIGH_MEMORY 0x01 +#define XMS_FREE_HIGH_MEMORY 0x02 +#define XMS_GLOBAL_ENABLE_A20 0x03 +#define XMS_GLOBAL_DISABLE_A20 0x04 +#define XMS_LOCAL_ENABLE_A20 0x05 +#define XMS_LOCAL_DISABLE_A20 0x06 +#define XMS_QUERY_A20 0x07 +#define XMS_QUERY_FREE_EXTENDED_MEMORY 0x08 +#define XMS_ALLOCATE_EXTENDED_MEMORY 0x09 +#define XMS_FREE_EXTENDED_MEMORY 0x0a +#define XMS_MOVE_EXTENDED_MEMORY_BLOCK 0x0b +#define XMS_LOCK_EXTENDED_MEMORY_BLOCK 0x0c +#define XMS_UNLOCK_EXTENDED_MEMORY_BLOCK 0x0d +#define XMS_GET_EMB_HANDLE_INFORMATION 0x0e +#define XMS_RESIZE_EXTENDED_MEMORY_BLOCK 0x0f +#define XMS_ALLOCATE_UMB 0x10 +#define XMS_DEALLOCATE_UMB 0x11 +#define XMS_REALLOCATE_UMB 0x12 +/* New functions for values bigger than 65MB, not implented yet */ +#define XMS_QUERY_FREE_EXTENDED_MEMORY_LARGE 0x88 +#define XMS_ALLOCATE_EXTENDED_MEMORY_LARGE 0x89 +#define XMS_FREE_EXTENDED_MEMORY_LARGE 0x8a + + +/* XMS error return codes */ +#define XMS_SUCCESS 0x0 +#define XMS_NOT_IMPLEMENTED 0x80 +#define XMS_VDISK 0x81 /* If vdisk.sys is present */ +#define XMS_A20_ERROR 0x82 +#define XMS_GENERAL_ERROR 0x8e +#define XMS_HMA_NOT_MANAGED 0x90 +#define XMS_HMA_ALREADY_USED 0x91 +#define XMS_HMA_NOT_ALLOCATED 0x93 +#define XMS_A20_STILL_ENABLED 0x94 +#define XMS_FULL 0xa0 +#define XMS_OUT_OF_HANDLES 0xa1 +#define XMS_INVALID_HANDLE 0xa2 +#define XMS_INVALID_SOURCE_HANDLE 0xa3 +#define XMS_INVALID_SOURCE_OFFSET 0xa4 +#define XMS_INVALID_DESTINATION_HANDLE 0xa5 +#define XMS_INVALID_DESTINATION_OFFSET 0xa6 +#define XMS_INVALID_LENGTH 0xa7 +#define XMS_BLOCK_NOT_LOCKED 0xaa +#define XMS_BLOCK_IS_LOCKED 0xab +#define XMS_BLOCK_LOCKCOUNT_OVERFLOW 0xac +#define XMS_REQUESTED_UMB_TOO_BIG 0xb0 +#define XMS_NO_UMBS_AVAILABLE 0xb1 +#define XMS_INVALID_UMB_SEGMENT 0xb2 + + +/* + * EMM structure for data exchange with DOS caller, hence the + * packed format + */ + +struct EMM { + u_long nbytes; + u_short src_handle __attribute__ ((packed)); + u_long src_offset __attribute__ ((packed)); + u_short dst_handle __attribute__ ((packed)); + u_long dst_offset __attribute__ ((packed)); +} ; + +/* + * XMS info structure, only used to pass information to and from + * DOS + */ + +struct XMSinfo { + u_char handle; /* the handle */ + u_char num_locks __attribute__ ((packed)); /* number of locks */ + u_long size __attribute__ ((packed)); /* size of memory */ + u_long phys_addr __attribute__ ((packed)); /* "physical" address */ +}; + +/* + * Handle management inside the emulator for extendend memory pages, + * invisible to DOS + */ + +typedef struct { + u_long addr; /* address inside emulator, from malloc() */ + u_long size; /* size in bytes */ + u_char num_locks; /* lock count for this handle */ +} XMS_handle; + +/* + * Managment of UMB memory paragraphs (16 bytes). UMB blocks are + * directly accessible by VM86 applications and lie between 0xd0000 and + * 0xefff0 in VM86 memory space. + */ + +struct _UMB_block { + u_long addr; /* Start address of block */ + u_long size; /* Size in bytes */ + struct _UMB_block *next; +}; + +typedef struct _UMB_block UMB_block; + +#endif /* XMS_H */ |