/* * All modifications in this file to the original code are * (C) Copyright 1992, ..., 2007 the "DOSEMU-Development-Team". * * for details see file COPYING.DOSEMU in the DOSEMU distribution */ /* modified for dosemu */ /* * Copyright (c) 1991 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* * DANG_BEGIN_MODULE * * REMARK * This provides the EMM Memory Management for DOSEMU. It was originally part * of the Mach Dos Emulator. * * In contrast to some of the comments (Yes, _I_ know the adage about that...) * we appear to be supporting EMS 4.0, not 3.2. Raw page size is 16k * (instead of 4k). Other than that, EMS 4.0 support appears complete. * * /REMARK * DANG_END_MODULE * * Purpose: * Mach EMM Memory Manager * */ #include #include #include #include #include #include #include #define new_utsname utsname #include "emu.h" #include "memory.h" #include "machcompat.h" #include "mapping.h" #include "emm.h" #include "dos2linux.h" #include "utilities.h" #include "int.h" #include "hlt.h" static inline boolean_t unmap_page(int); static int get_map_registers(void *ptr, int pages, const u_short *phys); static void set_map_registers(const void *ptr, int pages); #include #include #include "priv.h" /*****************************************************************************/ #define GET_MANAGER_STATUS 0x40 /* V3.0 */ #define GET_PAGE_FRAME_SEGMENT 0x41 /* V3.0 */ #define GET_PAGE_COUNTS 0x42 /* V3.0 */ #define GET_HANDLE_AND_ALLOCATE 0x43 /* V3.0 */ #define MAP_UNMAP 0x44 /* V3.0 */ #define DEALLOCATE_HANDLE 0x45 /* V3.0 */ #define GET_EMM_VERSION 0x46 /* V3.0 */ #define SAVE_PAGE_MAP 0x47 /* V3.0 */ #define RESTORE_PAGE_MAP 0x48 /* V3.0 */ #define RESERVED_1 0x49 /* V3.0 */ #define RESERVED_2 0x4a /* V3.0 */ #define GET_HANDLE_COUNT 0x4b /* V3.0 */ #define GET_PAGES_OWNED 0x4c /* V3.0 */ #define GET_PAGES_FOR_ALL 0x4d /* V3.0 */ #define PAGE_MAP_REGISTERS 0x4e /* V3.2 */ #define GET_REGISTERS 0x0 #define SET_REGISTERS 0x1 #define GET_AND_SET_REGISTERS 0x2 #define GET_SIZE_FOR_PAGE_MAP 0x3 #define PARTIAL_MAP_REGISTERS 0x4f /* V4.0 */ #define PARTIAL_GET 0x0 #define PARTIAL_SET 0x1 #define PARTIAL_GET_SIZE 0x2 #define MAP_UNMAP_MULTIPLE 0x50 /* V4.0 */ #define MULT_LOGPHYS 0 #define MULT_LOGSEG 1 #define REALLOCATE_PAGES 0x51 /* V4.0 */ #define HANDLE_ATTRIBUTE 0x52 /* V4.0 */ #define ALTERNATE_MAP_REGISTER 0x5B /* V4.0 */ #define PREPARE_FOR_WARMBOOT 0x5C /* V4.0 */ #define OS_SET_FUNCTION 0x5D /* V4.0 */ #define GET_ATT 0 #define SET_ATT 1 #define GET_ATT_CAPABILITY 2 #define EMM_VOLATILE 0 #define EMM_NONVOLATILE 1 /* not supported */ #define HANDLE_NAME 0x53 /* V4.0 */ #define GET_NAME 0 #define SET_NAME 1 #define HANDLE_DIR 0x54 /* V4.0 */ #define GET_DIR 0 #define SEARCH_NAMED 1 #define GET_TOTAL 2 #define ALTER_MAP_AND_JUMP 0x55 /* V4.0 */ #define ALTER_MAP_AND_CALL 0x56 /* V4.0 */ #define ALTER_GET_STACK_SIZE 2 #define GET_MPA_ARRAY 0x58 /* V4.0 */ #define MOD_MEMORY_REGION 0x57 /* V4.0 */ #define GET_MPA 0 #define GET_MPA_ENTRY_COUNT 1 #define GET_EMS_HARDINFO 0x59 /* V4.0 */ #define ALLOCATE_STD_PAGES 0x5a /* V4.0 */ #define GET_ARRAY 0 #define GET_RAW_PAGECOUNT 1 /* Support EMM version 3.2 */ #define EMM_VERS 0x40 /* was 0x32 */ /* EMM errors */ #define EMM_NO_ERR 0x0 #define EMM_SOFT_MAL 0x80 #define EMM_HARD_MAL 0x81 #define EMM_INV_HAN 0x83 #define EMM_FUNC_NOSUP 0x84 #define EMM_OUT_OF_HAN 0x85 #define EMM_OUT_OF_PHYS 0x87 #define EMM_OUT_OF_LOG 0x88 #define EMM_ZERO_PAGES 0x89 #define EMM_LOG_OUT_RAN 0x8a #define EMM_ILL_PHYS 0x8b #define EMM_INVALID_SUB 0x8f #define EMM_FEAT_NOSUP 0x91 #define EMM_MOVE_OVLAP 0x92 #define EMM_MOVE_SIZE 0x93 #define EMM_MOVE_1MB_LIM 0x96 #define EMM_MOVE_OVLAPI 0x97 #define EMM_NOT_FOUND 0xa0 /* 971120 acc to R.Brown's int list */ #define EMM_ERROR -1 static u_char emm_error; #define EMM_TOTAL (MAX_EMM + config.ems_cnv_pages) static int handle_total, emm_allocated; static Bit32u EMSControl_OFF; #define saved_phys_pages min(config.ems_uma_pages, EMM_MAX_SAVED_PHYS) #define phys_pages (config.ems_cnv_pages + config.ems_uma_pages) #define cnv_start_seg (0xa000 - 0x400 * config.ems_cnv_pages) #define cnv_pages_start config.ems_uma_pages static struct emm_record { u_short handle; u_short logical_page; u_short phys_seg; } emm_map[EMM_MAX_PHYS]; static struct handle_record { u_char active; int numpages; void *object; char name[9]; u_short saved_mappings_handle[EMM_MAX_SAVED_PHYS]; u_short saved_mappings_logical[EMM_MAX_SAVED_PHYS]; } handle_info[MAX_HANDLES]; #define OS_HANDLE 0 /* For OS use of EMM */ static u_char os_inuse=0; static u_short os_key1=0xffee; static u_short os_key2=0xddcc; static u_short os_allow=1; /* FIXME -- inline function */ #define CHECK_OS_HANDLE(handle) \ if ((handle) == OS_HANDLE) \ Kdebug0((dbg_fd,"trying to use OS handle in MAP_UNMAP!\n")); /* FIXME -- inline function */ #define CLEAR_HANDLE_NAME(nameptr) \ memset((nameptr), 0, 9); #define SET_HANDLE_NAME(nameptr, name) \ { memmove((nameptr), (name), 8); nameptr[8]=0; } #define CHECK_HANDLE(handle) \ if ((handle < 0) || (handle >= MAX_HANDLES)) { \ E_printf("Invalid Handle handle=%x\n", handle); \ SETHIGH(&(state->eax), EMM_INV_HAN); \ return; \ } \ if (handle_info[handle].active == 0) { \ E_printf("Invalid Handle handle=%x, active=%d\n", \ handle, handle_info[handle].active); \ SETHIGH(&(state->eax), EMM_INV_HAN); \ return; \ } /* this will have to change...0 counts are allowed */ #define HANDLE_ALLOCATED(handle) \ (handle_info[handle].active) #define NOFUNC() \ { SETHIGH(&(state->eax),EMM_FUNC_NOSUP); \ Kdebug0((dbg_fd, "function not supported: 0x%04x\n", \ (unsigned)WORD(state->eax))); } #define PHYS_PAGE_SEGADDR(i) \ (emm_map[i].phys_seg) #define PHYS_PAGE_ADDR(i) \ (PHYS_PAGE_SEGADDR(i) << 4) #define E_Stub(arg1, s, a...) E_printf("EMS: "s, ##a) #define Kdebug0(args) E_Stub args #define Kdebug1(args) E_Stub args #define Kdebug2(args) E_Stub args void ems_helper(void) { u_char *rhptr; /* request header pointer */ switch (LWORD(ebx)) { case 0: E_printf("EMS Init called!\n"); if (!config.ems_size) { LWORD(ebx) = EMS_ERROR_DISABLED_IN_CONFIG; return; } if (HI(ax) < DOSEMU_EMS_DRIVER_VERSION) { error("EMS driver version mismatch: got %i, expected %i, disabling.\n" "Please update your ems.sys from the latest dosemu package.\n", HI(ax), DOSEMU_EMS_DRIVER_VERSION); com_printf("\nEMS driver version mismatch, disabling.\n" "Please update your ems.sys from the latest dosemu package.\n" "\nPress any key!\n"); LWORD(ebx) = EMS_ERROR_VERSION_MISMATCH; com_biosgetch(); return; } break; case 3: E_printf("EMS IOCTL called!\n"); break; case 4: E_printf("EMS READ called!\n"); break; case 8: E_printf("EMS WRITE called!\n"); break; case 10: E_printf("EMS Output Status called!\n"); break; case 12: E_printf("EMS IOCTL-WRITE called!\n"); break; case 13: E_printf("EMS OPENDEV called!\n"); break; case 14: E_printf("EMS CLOSEDEV called!\n"); break; case 0x20: E_printf("EMS INT 0x67 called!\n"); break; default: error("UNKNOWN EMS HELPER FUNCTION %d\n", LWORD(ebx)); return; } rhptr = SEG_ADR((u_char *), es, di); E_printf("EMS RHDR: len %d, command %d\n", *rhptr, *(u_short *) (rhptr + 2)); LWORD(eax) = 0; /* report success */ } static void * new_memory_object(size_t bytes) { void *addr; if (!bytes) return NULL; addr = alloc_mapping(MAPPING_EMS, bytes, -1); if (!addr) return 0; E_printf("EMS: allocating 0x%08zx bytes @ %p\n", bytes, addr); return (addr); /* allocate on a PAGE boundary */ } static inline void destroy_memory_object(unsigned char *object, int length) { if (!object) return; E_printf("EMS: destroyed EMS object @ %p\n", object); free_mapping(MAPPING_EMS, object, length); } static inline void *realloc_memory_object(void *object, size_t oldsize, size_t bytes) { void *addr = realloc_mapping(MAPPING_EMS, object, oldsize, bytes); if (addr == MAP_FAILED) return 0; return addr; } static int emm_allocate_handle(int pages_needed) { int i, j; void *obj; if (handle_total >= MAX_HANDLES) { emm_error = EMM_OUT_OF_HAN; return (EMM_ERROR); } if (pages_needed > EMM_TOTAL) { E_printf("EMS: Too many pages requested\n"); emm_error = EMM_OUT_OF_PHYS; return (EMM_ERROR); } if (pages_needed > (EMM_TOTAL - emm_allocated)) { E_printf("EMS: Out of free pages\n"); emm_error = EMM_OUT_OF_LOG; return (EMM_ERROR); } /* start at 1, as 0 is the OS handle */ for (i = 1; i < MAX_HANDLES; i++) { if (handle_info[i].active == 0) { obj = new_memory_object(pages_needed * EMM_PAGE_SIZE); if (obj==NULL && pages_needed > 0) { E_printf("EMS: Allocation failed!\n"); emm_error = EMM_OUT_OF_LOG; return (EMM_ERROR); } handle_info[i].object = obj; handle_info[i].numpages = pages_needed; CLEAR_HANDLE_NAME(handle_info[i].name); handle_total++; emm_allocated += pages_needed; for (j = 0; j < saved_phys_pages; j++) { handle_info[i].saved_mappings_logical[j] = NULL_PAGE; } handle_info[i].active = 1; return (i); } } emm_error = EMM_OUT_OF_HAN; return (EMM_ERROR); } static boolean_t emm_deallocate_handle(int handle) { int numpages, i; void *object; if ((handle < 0) || (handle >= MAX_HANDLES)) return (FALSE); if (handle_info[handle].active != 1) return (FALSE); for (i = 0; i < phys_pages; i++) { if (emm_map[i].handle == handle) { unmap_page(i); emm_map[i].handle = NULL_HANDLE; } } numpages = handle_info[handle].numpages; object = handle_info[handle].object; destroy_memory_object(object,numpages*EMM_PAGE_SIZE); handle_info[handle].numpages = 0; handle_info[handle].active = 0; handle_info[handle].object = NULL; CLEAR_HANDLE_NAME(handle_info[i].name); handle_total--; emm_allocated -= numpages; return (TRUE); } static void _do_map_page(unsigned int dst, caddr_t src, int size) { E_printf("EMS: mmap()ing from %p to %#x\n", src, dst); if (MAP_FAILED == alias_mapping(MAPPING_EMS, dst, size, PROT_READ | PROT_WRITE | PROT_EXEC, src)) { E_printf("EMS: mmap() failed: %s\n",strerror(errno)); leavedos(2); } } static void _do_unmap_page(unsigned int base, int size) { E_printf("EMS: unmmap()ing from %#x\n", base); /* don't unmap, just overmap with the LOWMEM page */ alias_mapping(MAPPING_LOWMEM, base, size, PROT_READ | PROT_WRITE | PROT_EXEC, LOWMEM(base)); } static boolean_t __map_page(int physical_page) { int handle; caddr_t logical; unsigned int base; if ((physical_page < 0) || (physical_page >= phys_pages)) return (FALSE); handle=emm_map[physical_page].handle; if (handle == NULL_HANDLE) return (FALSE); E_printf("EMS: map()ing physical page 0x%01x, handle=%d, logical page 0x%x\n", physical_page,handle,emm_map[physical_page].logical_page); base = PHYS_PAGE_ADDR(physical_page); logical = handle_info[handle].object + emm_map[physical_page].logical_page * EMM_PAGE_SIZE; _do_map_page(base, logical, EMM_PAGE_SIZE); return (TRUE); } static boolean_t __unmap_page(int physical_page) { int handle; unsigned int base; if ((physical_page < 0) || (physical_page >= phys_pages)) return (FALSE); handle=emm_map[physical_page].handle; if (handle == NULL_HANDLE) return (FALSE); E_printf("EMS: unmap()ing physical page 0x%01x, handle=%d, logical page 0x%x\n", physical_page,handle,emm_map[physical_page].logical_page); base = PHYS_PAGE_ADDR(physical_page); _do_unmap_page(base, EMM_PAGE_SIZE); return (TRUE); } static inline boolean_t unmap_page(int physical_page) { E_printf("EMS: unmap_page(%d)\n",physical_page); if (__unmap_page(physical_page)) { emm_map[physical_page].handle = NULL_HANDLE; emm_map[physical_page].logical_page = NULL_PAGE; return (TRUE); } else return (FALSE); } static inline boolean_t reunmap_page(int physical_page) { E_printf("EMS: reunmap_page(%d)\n",physical_page); return __unmap_page(physical_page); } static boolean_t map_page(int handle, int physical_page, int logical_page) { unsigned int base; caddr_t logical; E_printf("EMS: map_page(handle=%d, phy_page=%d, log_page=%d), prev handle=%d\n", handle, physical_page, logical_page, emm_map[physical_page].handle); if ((physical_page < 0) || (physical_page >= phys_pages)) return (FALSE); if (handle == NULL_HANDLE) return (FALSE); if (handle_info[handle].numpages <= logical_page) return (FALSE); #if 0 /* no need to unmap before mapping */ if (emm_map[physical_page].handle != NULL_HANDLE) unmap_page(physical_page); #endif base = PHYS_PAGE_ADDR(physical_page); logical = handle_info[handle].object + logical_page * EMM_PAGE_SIZE; _do_map_page(base, logical, EMM_PAGE_SIZE); emm_map[physical_page].handle = handle; emm_map[physical_page].logical_page = logical_page; return (TRUE); } static inline boolean_t remap_page(int physical_page) { E_printf("EMS: remapping physical page 0x%01x\n", physical_page); return __map_page(physical_page); } static inline int handle_pages(int handle) { return (handle_info[handle].numpages); } static int emm_save_handle_state(int handle) { int i; for (i = 0; i < saved_phys_pages; i++) { if (emm_map[i].handle != NULL_HANDLE) { handle_info[handle].saved_mappings_logical[i] = emm_map[i].logical_page; handle_info[handle].saved_mappings_handle[i] = emm_map[i].handle; } else handle_info[handle].saved_mappings_logical[i] = NULL_PAGE; } return 0; } static int emm_restore_handle_state(int handle) { int i; for (i = 0; i < saved_phys_pages; i++) { int saved_mapping; int saved_mapping_handle; saved_mapping = handle_info[handle].saved_mappings_logical[i]; saved_mapping_handle = handle_info[handle].saved_mappings_handle[i]; if (saved_mapping != NULL_PAGE) { E_printf("EMS: Restore PHY=%d, LOG=%x, HANDLE=%x\n", i, saved_mapping, saved_mapping_handle); map_page(saved_mapping_handle, i, saved_mapping); } else { E_printf("EMS: Restore page #%01x ==> unmapping \n",i); unmap_page(i); } } return 0; } static int do_map_unmap(int handle, int physical_page, int logical_page) { if ((physical_page < 0) || (physical_page >= phys_pages)) { E_printf("Invalid Physical Page physical_page=%x\n", physical_page); return EMM_ILL_PHYS; } if (logical_page == 0xffff) { E_printf("EMS: do_map_unmap is unmapping\n"); unmap_page(physical_page); } else { if ((handle < 0) || (handle >= MAX_HANDLES)) { E_printf("Invalid Handle handle=%x\n", handle); return EMM_INV_HAN; } if (handle_info[handle].active == 0) { E_printf("Invalid Handle handle=%x, active=%d\n", handle, handle_info[handle].active); return EMM_INV_HAN; } CHECK_OS_HANDLE(handle); if (logical_page >= handle_info[handle].numpages) { E_printf("Logical page too high logical_page=%d, numpages=%d\n", logical_page, handle_info[handle].numpages); return EMM_LOG_OUT_RAN; } E_printf("EMS: do_map_unmap is mapping\n"); map_page(handle, physical_page, logical_page); } return EMM_NO_ERR; } static inline int SEG_TO_PHYS(int segaddr) { int i; E_printf("SEG_TO_PHYS: segment: %x\n", segaddr); for (i = 0; i < phys_pages; i++) { if (segaddr >= emm_map[i].phys_seg && segaddr < emm_map[i].phys_seg + EMM_PAGE_SIZE/16) return i; } return -1; } /* EMS 4.0 functions start here */ int emm_get_partial_map_registers(void *ptr, const u_short *segs) { u_short *buf = ptr; int pages, i; u_short *phys; if (!config.ems_size) return EMM_NO_ERR; pages = *segs++; phys = malloc(pages * sizeof(*phys)); for (i = 0; i < pages; i++) { int phy = SEG_TO_PHYS(segs[i]); if (phy == -1) { free(phys); return EMM_ILL_PHYS; } phys[i] = phy; } /* store the count first for Get Partial Page Map */ *buf++ = pages; get_map_registers(buf, pages, phys); free(phys); return EMM_NO_ERR; } void emm_set_partial_map_registers(const void *ptr) { const u_short *buf = ptr; if (!config.ems_size) return; int pages = *buf++; set_map_registers(buf, pages); } int emm_get_size_for_partial_page_map(int pages) { if (!config.ems_size || pages < 0 || pages > phys_pages) return -1; return sizeof(u_short) + PAGE_MAP_SIZE(pages); } static inline void partial_map_registers(state_t * state) { int ret; Kdebug0((dbg_fd, "partial_map_registers %d called\n", (int) LOW(state->eax))); switch (LOW(state->eax)) { case PARTIAL_GET: ret = emm_get_partial_map_registers(Addr(state, es, edi), Addr(state, ds, esi)); SETHIGH(&(state->eax), ret); break; case PARTIAL_SET: emm_set_partial_map_registers(Addr(state, ds, esi)); SETHIGH(&(state->eax), EMM_NO_ERR); break; case PARTIAL_GET_SIZE: ret = emm_get_size_for_partial_page_map(WORD(state->ebx)); if (ret == -1) { SETHIGH(&(state->eax), EMM_ILL_PHYS); } else { SETLOW(&(state->eax), ret); SETHIGH(&(state->eax), EMM_NO_ERR); } break; default: Kdebug1((dbg_fd, "bios_emm: Partial Page Map Regs unknown fn\n")); SETHIGH(&(state->eax), EMM_FUNC_NOSUP); break; } } int emm_map_unmap_multi(const u_short *array, int handle, int map_len) { int ret = EMM_NO_ERR; int i, phys, log; for (i = 0; i < map_len; i++) { log = array[i * 2]; phys = array[i * 2 + 1]; Kdebug0((dbg_fd, "loop: 0x%x 0x%x \n", log, phys)); ret = do_map_unmap(handle, phys, log); if (ret != EMM_NO_ERR) break; } return ret; } static int do_map_unmap_multi(int method, unsigned array, int handle, int map_len) { int ret; u_short *array2 = malloc(PAGE_MAP_SIZE(map_len)); switch (method) { case MULT_LOGPHYS: /* page no method */ MEMCPY_2UNIX(array2, array, PAGE_MAP_SIZE(map_len)); break; case MULT_LOGSEG: { /* page segment method */ int i, phys, log, seg; for (i = 0; i < map_len; i++) { log = READ_WORD(array + PAGE_MAP_SIZE(i)); seg = READ_WORD(array + PAGE_MAP_SIZE(i) + sizeof(u_short)); phys = SEG_TO_PHYS(seg); Kdebug0((dbg_fd, "loop: log 0x%x seg 0x%x phys 0x%x\n", log, seg, phys)); if (phys == -1) { free(array2); return EMM_ILL_PHYS; } else { array2[i * 2] = log; array2[i * 2 + 1] = phys; } } break; } } ret = emm_map_unmap_multi(array2, handle, map_len); free(array2); return ret; } static void map_unmap_multiple(state_t * state) { int handle = WORD(state->edx); int map_len; unsigned int array; int method = LOW(state->eax); int ret; Kdebug0((dbg_fd, "map_unmap_multiple %d called\n", method)); CHECK_HANDLE(handle); switch (method) { case MULT_LOGPHYS: case MULT_LOGSEG: map_len = WORD(state->ecx); array = SEGOFF2LINEAR(state->ds, WORD(state->esi)); Kdebug0((dbg_fd, "...using mult_log%s method, " "handle %d, map_len %d, array @ %#x\n", method == MULT_LOGPHYS ? "phys" : "seg", handle, map_len, array)); ret = do_map_unmap_multi(method, array, handle, map_len); break; default: Kdebug0((dbg_fd, "ERROR: map_unmap_multiple subfunction %d not supported\n", method)); ret = EMM_INVALID_SUB; break; } SETHIGH(&(state->eax), ret); } static void reallocate_pages(state_t * state) { u_char i; int diff; int handle = WORD(state->edx); int newcount = WORD(state->ebx); void *obj; if ((handle < 0) || (handle >= MAX_HANDLES)) { SETHIGH(&(state->eax), EMM_INV_HAN); return; } if (!handle_info[handle].active) { /* no-handle */ Kdebug0((dbg_fd, "reallocate_pages handle %d invalid\n", handle)); SETHIGH(&(state->eax), EMM_INV_HAN); return; } if (newcount == handle_info[handle].numpages) { /* no-op */ Kdebug0((dbg_fd, "reallocate_pages handle %d no-change\n", handle)); SETHIGH(&(state->eax), EMM_NO_ERR); return; } diff = newcount-handle_info[handle].numpages; if (emm_allocated+diff > EMM_TOTAL) { Kdebug0((dbg_fd, "reallocate pages: maximum exceeded\n")); SETHIGH(&(state->eax), EMM_OUT_OF_PHYS); return; } emm_allocated += diff; /* Make sure extended pages have correct data */ for (i = 0; i < phys_pages; i++) if (emm_map[i].handle == handle) reunmap_page(i); Kdebug0((dbg_fd, "want reallocate_pages handle %d num %d called object=%p\n", handle, newcount, handle_info[handle].object)); if (newcount && handle_info[handle].object) { /* * No special handling required, we safely can realloc() or mremap() */ obj = realloc_memory_object(handle_info[handle].object, handle_info[handle].numpages*EMM_PAGE_SIZE ,newcount * EMM_PAGE_SIZE); if (obj==NULL) { Kdebug0((dbg_fd, "failed to realloc pages!\n")); SETHIGH(&(state->eax), EMM_OUT_OF_LOG); } else { handle_info[handle].object = obj; handle_info[handle].numpages = newcount; SETHIGH(&(state->eax), EMM_NO_ERR); } } else { obj = 0; if (handle_info[handle].object) { /* * Here we come, when reallocation would lead to a ZERO sized block. * This would be bad for mremap(), because this is equal to munmap() * We destroy the objekt instead */ destroy_memory_object(handle_info[handle].object,handle_info[handle].numpages*EMM_PAGE_SIZE); handle_info[handle].object = 0; handle_info[handle].numpages = 0; SETHIGH(&(state->eax), EMM_NO_ERR); } else { /* * Here we come, when a reallocation for an above destroyed object * is requested. * We simply create a new one. */ if (newcount) obj= new_memory_object(newcount*EMM_PAGE_SIZE); if (obj==NULL) { Kdebug0((dbg_fd, "failed to realloc pages!\n")); SETHIGH(&(state->eax), EMM_OUT_OF_LOG); } else { handle_info[handle].object = obj; handle_info[handle].numpages = newcount; SETHIGH(&(state->eax), EMM_NO_ERR); } } } Kdebug0((dbg_fd, "reallocate_pages handle %d num %d called object=%p\n", handle, newcount, obj)); /* remove pages no longer in range, remap others */ for (i = 0; i < phys_pages; i++) { if (emm_map[i].handle == handle) { /* * NOTE: In case of the above critical case (newcount==0) * We _always_ have emm_map[i].logical_page >= newcount * because even NULL_PAGE is > 0 * Hence we need no special treatment here */ if (emm_map[i].logical_page >= newcount) { emm_map[i].handle = NULL_HANDLE; emm_map[i].logical_page = NULL_PAGE; } else remap_page(i); } } } static int handle_attribute(state_t * state) { switch (LOW(state->eax)) { case GET_ATT:{ int handle = WORD(state->edx); Kdebug0((dbg_fd, "GET_ATT on handle %d\n", handle)); SETLOW(&(state->eax), EMM_VOLATILE); SETHIGH(&(state->eax), EMM_NO_ERR); return (TRUE); } case SET_ATT:{ int handle = WORD(state->edx); Kdebug0((dbg_fd, "SET_ATT on handle %d\n", handle)); SETHIGH(&(state->eax), EMM_FEAT_NOSUP); return (TRUE); } case GET_ATT_CAPABILITY:{ Kdebug0((dbg_fd, "GET_ATT_CAPABILITY\n")); SETHIGH(&(state->eax), EMM_NO_ERR); SETLOW(&(state->eax), EMM_VOLATILE); /* only supports volatiles */ return (TRUE); } default: NOFUNC(); return (UNCHANGED); } } static void handle_name(state_t * state) { switch (LOW(state->eax)) { case GET_NAME:{ int handle = WORD(state->edx); u_char *array = (u_char *) Addr(state, es, edi); CHECK_HANDLE(handle); handle_info[handle].name[8] = 0; Kdebug0((dbg_fd, "get handle name %d = %s\n", handle, handle_info[handle].name)); memmove(array, &handle_info[handle].name, 8); SETHIGH(&(state->eax), EMM_NO_ERR); break; } case SET_NAME:{ int handle = (u_short)WORD(state->edx); /* changed below from es:edi */ u_char *array = (u_char *) Addr(state, ds, esi); E_printf("SET_NAME of %8.8s\n", (u_char *)array); CHECK_HANDLE(handle); memmove(handle_info[handle].name, array, 8); handle_info[handle].name[8] = 0; Kdebug0((dbg_fd, "set handle %d to name %s\n", handle, handle_info[handle].name)); SETHIGH(&(state->eax), EMM_NO_ERR); break; } default: Kdebug0((dbg_fd, "bad handle_name function %d\n", (int) LOW(state->eax))); SETHIGH(&(state->eax), EMM_FUNC_NOSUP); return; } } static void handle_dir(state_t * state) { Kdebug0((dbg_fd, "handle_dir %d called\n", (int) LOW(state->eax))); switch (LOW(state->eax)) { case GET_DIR:{ int handle, count = 0; u_char *array = (u_char *) Addr(state, es, edi); Kdebug0((dbg_fd, "GET_DIR function called\n")); for (handle = 0; handle < MAX_HANDLES; handle++) { if (HANDLE_ALLOCATED(handle)) { count++; *(u_short *) array = handle; memmove(array + 2, handle_info[handle].name, 8); array += 10; Kdebug0((dbg_fd, "GET_DIR found handle %d name %s\n", handle, (char *) array)); } } SETHIGH(&(state->eax), EMM_NO_ERR); SETLOW(&(state->eax), count); return; } case SEARCH_NAMED:{ int handle; char xxx[9]; char *array = Addr(state, ds, esi); strncpy(xxx,array,8); xxx[8]=0; Kdebug0((dbg_fd, "SEARCH_NAMED '%s' function called\n",xxx)); for (handle = 0; handle < MAX_HANDLES; handle++) { if (!HANDLE_ALLOCATED(handle)) continue; if (!strncmp(handle_info[handle].name, array, 8)) { Kdebug0((dbg_fd, "name match %s!\n", array)); SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->edx), handle); return; } } /* got here, so search failed */ SETHIGH(&(state->eax), EMM_NOT_FOUND); return; } case GET_TOTAL:{ Kdebug0((dbg_fd, "GET_TOTAL %d\n", MAX_HANDLES)); SETWORD(&(state->ebx), MAX_HANDLES); SETHIGH(&(state->eax), EMM_NO_ERR); break; } default: Kdebug0((dbg_fd, "bad handle_dir function %d\n", (int) LOW(state->eax))); SETHIGH(&(state->eax), EMM_FUNC_NOSUP); return; } } /* input structure for EMS alter page map and jump API */ struct __attribute__ ((__packed__)) alter_map_struct { u_char map_len; u_int array; }; static int alter_map(int method, int handle, const struct alter_map_struct *alter_map) { /* input parameters */ int map_len; u_short seg, off; unsigned array; map_len = alter_map->map_len; seg = FP_SEG16(alter_map->array); off = FP_OFF16(alter_map->array); array = SEGOFF2LINEAR(seg, off); Kdebug0((dbg_fd, "...using alter_log%s method, " "handle %d, map_len %d, array @ %#x, ", method == MULT_LOGPHYS ? "phys" : "seg", handle, map_len, array)); /* change mapping context */ return do_map_unmap_multi(method, array, handle, map_len); } struct __attribute__ ((__packed__)) alter_map_jmp_struct { u_int jmp_addr; struct alter_map_struct alter_map; }; static void alter_map_and_jump(state_t * state) { int method = LOW(state->eax); Kdebug0((dbg_fd, "alter_map_and_jump %d called\n", method)); switch(method) { case MULT_LOGPHYS: /* page number method */ case MULT_LOGSEG: { /* input parameters */ struct alter_map_jmp_struct alter_map_jmp; int handle; u_short seg, off; int ret; MEMCPY_2UNIX(&alter_map_jmp, SEGOFF2LINEAR(state->ds, state->esi), sizeof alter_map_jmp); handle = WORD(state->edx); ret = alter_map(method, handle, &alter_map_jmp.alter_map); SETHIGH(&(state->eax), ret); if (ret != EMM_NO_ERR) break; /* mapping error */ seg = FP_SEG16(alter_map_jmp.jmp_addr); off = FP_OFF16(alter_map_jmp.jmp_addr); Kdebug0((dbg_fd, "jmp_addr @ %04hX:%04hXh\n", seg, off)); /* jump */ state->eip = off; state->cs = seg; break; } default: Kdebug0((dbg_fd, "bad alter_map_and_jump function %d\n", method)); SETHIGH(&(state->eax), EMM_FUNC_NOSUP); break; } } /* input structure for EMS alter page map and call API */ struct __attribute__ ((__packed__)) alter_map_call_struct { u_int call_addr; struct alter_map_struct new_map, old_map; u_short reserved[4]; }; #define ALTER_STACK_SIZE (2*4) /* 4 params */ static void alter_map_and_call(state_t * state) { int method = LOW(state->eax); Kdebug0((dbg_fd, "alter_map_and_call %d called\n", method)); switch(method) { case MULT_LOGPHYS: /* page number method */ case MULT_LOGSEG: { int handle = WORD(state->edx); int ret; u_short seg, off; u_int ssp, sp; struct alter_map_call_struct alter_map_call; /* find new mapping context */ MEMCPY_2UNIX(&alter_map_call, SEGOFF2LINEAR(state->ds, state->esi), sizeof alter_map_call); ret = alter_map(method, handle, &alter_map_call.new_map); SETHIGH(&(state->eax), ret); if (ret != EMM_NO_ERR) break; /* mapping error */ /* call user fn */ /* save parameters on the stack */ ssp = SEGOFF2LINEAR(LWORD(ss), 0); sp = LWORD(esp); pushw(ssp, sp, method); pushw(ssp, sp, handle); pushw(ssp, sp, state->ds); pushw(ssp, sp, state->esi + offsetof(struct alter_map_call_struct, old_map)); LWORD(esp) -= ALTER_STACK_SIZE; /* make far call * put ret addr of user fn in HLT block, * and save current cs:ip for returning * properly from HTL handler */ fake_call_to(EMSControl_SEG, EMSControl_OFF); seg = FP_SEG16(alter_map_call.call_addr); off = FP_OFF16(alter_map_call.call_addr); Kdebug0((dbg_fd, "call_addr @ %04hX:%04hXh\n", seg, off)); fake_call_to(seg, off); break; } case ALTER_GET_STACK_SIZE: /* get stack info size subfn */ SETWORD(&(state->ebx), ALTER_STACK_SIZE+8); SETHIGH(&(state->eax), EMM_NO_ERR); break; default: Kdebug0((dbg_fd, "bad alter_map_and_call function %d\n", (int) LOW(state->eax))); SETHIGH(&(state->eax), EMM_FUNC_NOSUP); break; } } /* hlt handler for EMS * Used for finishing the return path of the * EMS "alter page map and call" API fn. * Restores the EMS mapping context and returns to the user. * On entry, SS:ESP (DOS space stack) points to return address. * Pushed parameters saved by emm_alter_map_and_call() follow. */ static void emm_hlt_handler(Bit32u offs, void *arg) { struct alter_map_struct old_map; u_short method; u_short handle; u_short seg, off; u_int ssp, sp; int ret; /* restore inst. pointer */ fake_retf(0); /* pop parameters from stack */ ssp = SEGOFF2LINEAR(LWORD(ss), 0); sp = LWORD(esp); off = popw(ssp, sp); seg = popw(ssp, sp); handle = popw(ssp, sp); method = popw(ssp, sp); LWORD(esp) += ALTER_STACK_SIZE; /* alter_map_call structure */ MEMCPY_2UNIX(&old_map, SEGOFF2LINEAR(seg, off), sizeof old_map); /* restore old mapping */ ret = alter_map(method, handle, &old_map); SETHIGH(&(REGS.eax), ret); } struct __attribute__ ((__packed__)) mem_move_struct { int size; u_char source_type; u_short source_handle; u_short source_offset; u_short source_segment; u_char dest_type; u_short dest_handle; u_short dest_offset; u_short dest_segment; }; static void show_move_struct(struct mem_move_struct *mem_move) { E_printf("EMS: MOD MEMORY\n"); E_printf("EMS: size=0x%08x\n", mem_move->size); E_printf("EMS: source_type=0x%02x\n", mem_move->source_type); E_printf("EMS: source_handle=0x%04x\n", mem_move->source_handle); E_printf("EMS: source_offset=0x%04x\n", mem_move->source_offset); E_printf("EMS: source_segment=0x%04x\n", mem_move->source_segment); E_printf("EMS: dest_type=0x%02x\n", mem_move->dest_type); E_printf("EMS: dest_handle=0x%04x\n", mem_move->dest_handle); E_printf("EMS: dest_offset=0x%04x\n", mem_move->dest_offset); E_printf("EMS: dest_segment=0x%04x\n", mem_move->dest_segment); } static int move_memory_region(state_t * state) { struct mem_move_struct mem_move_struc, *mem_move = &mem_move_struc; unsigned char *dest, *source, *mem; unsigned src = 0; int overlap = 0; MEMCPY_2UNIX(mem_move, SEGOFF2LINEAR(state->ds, state->esi), sizeof mem_move_struc); show_move_struct(mem_move); if (mem_move->size > 0x100000) return EMM_MOVE_1MB_LIM; if (mem_move->source_type == 0) { source = NULL; src = SEGOFF2LINEAR(mem_move->source_segment, mem_move->source_offset); } else { if (!handle_info[mem_move->source_handle].active) { E_printf("EMS: Move memory region source handle not active\n"); return (EMM_INV_HAN); } if (handle_info[mem_move->source_handle].numpages <= mem_move->source_segment) { E_printf("EMS: Move memory region source numpages %d > numpages %d available\n", mem_move->source_segment, handle_info[mem_move->source_handle].numpages); return (EMM_LOG_OUT_RAN); } source = handle_info[mem_move->source_handle].object + mem_move->source_segment * EMM_PAGE_SIZE + mem_move->source_offset; mem = handle_info[mem_move->source_handle].object + handle_info[mem_move->source_handle].numpages * EMM_PAGE_SIZE; if ((source + mem_move->size) > mem) { E_printf("EMS: Source move of %p is outside handle allocation max of %p, size=%x\n", source, mem, mem_move->size); return (EMM_MOVE_SIZE); } } if (mem_move->dest_type == 0) { unsigned dst = SEGOFF2LINEAR(mem_move->dest_segment, mem_move->dest_offset); if (source) { E_printf("EMS: Move Memory Region from %p -> %#x\n", source, dst); memcpy_2dos(dst, source, mem_move->size); } else { E_printf("EMS: Move Memory Region from %#x -> %#x\n", src, dst); memmove_dos2dos(dst, src, mem_move->size); overlap = (src < dst && src + mem_move->size > dst) || (src >= dst && dst + mem_move->size > src); } } else { if (!handle_info[mem_move->dest_handle].active) { E_printf("EMS: Move memory region dest handle not active\n"); return (EMM_INV_HAN); } if (handle_info[mem_move->dest_handle].numpages <= mem_move->dest_segment) { E_printf("EMS: Move memory region dest numpages %d > numpages %d available\n", mem_move->dest_segment, handle_info[mem_move->dest_handle].numpages); return (EMM_LOG_OUT_RAN); } dest = handle_info[mem_move->dest_handle].object + mem_move->dest_segment * EMM_PAGE_SIZE + mem_move->dest_offset; mem = handle_info[mem_move->dest_handle].object + handle_info[mem_move->dest_handle].numpages * EMM_PAGE_SIZE; if ((dest + mem_move->size) > mem) { E_printf("EMS: Dest move of %p is outside handle allocation of %p, size=%x\n", dest, mem, mem_move->size); return (EMM_MOVE_SIZE); } if (source) { E_printf("EMS: Move Memory Region from %p -> %p\n", source, dest); memmove(dest, source, mem_move->size); overlap = (source < dest && source + mem_move->size > dest) || (source >= dest && dest + mem_move->size > source); } else { E_printf("EMS: Move Memory Region from %#x -> %p\n", src, dest); memcpy_2unix(dest, src, mem_move->size); } } if (overlap) { E_printf("EMS: Source overlaps Dest\n"); return (EMM_MOVE_OVLAP); } return (0); } static int exchange_memory_region(state_t * state) { struct mem_move_struct mem_move_struc, *mem_move = &mem_move_struc; unsigned char *dest, *source, *mem, *tmp; MEMCPY_2UNIX(mem_move, SEGOFF2LINEAR(state->ds, state->esi), sizeof mem_move_struc); show_move_struct(mem_move); if (mem_move->size > 0x100000) return EMM_MOVE_1MB_LIM; if (mem_move->source_type == 0) source = MK_FP32(mem_move->source_segment, mem_move->source_offset); else { if (!handle_info[mem_move->source_handle].active) { E_printf("EMS: Xchng memory region source handle not active\n"); return (EMM_INV_HAN); } if (handle_info[mem_move->source_handle].numpages <= mem_move->source_segment) { E_printf("EMS: Xchng memory region source numpages %d > numpages %d available\n", mem_move->source_segment, handle_info[mem_move->source_handle].numpages); return (EMM_LOG_OUT_RAN); } source = handle_info[mem_move->source_handle].object + mem_move->source_segment * EMM_PAGE_SIZE + mem_move->source_offset; mem = handle_info[mem_move->source_handle].object + handle_info[mem_move->source_handle].numpages * EMM_PAGE_SIZE; if ((source + mem_move->size) > mem) { E_printf("EMS: Source xchng of %p is outside handle allocation max of %p, size=%x\n", source, mem, mem_move->size); return (EMM_MOVE_SIZE); } } if (mem_move->dest_type == 0) dest = MK_FP32(mem_move->dest_segment, mem_move->dest_offset); else { if (!handle_info[mem_move->dest_handle].active) { E_printf("EMS: Xchng memory region dest handle not active\n"); return (EMM_INV_HAN); } if (handle_info[mem_move->dest_handle].numpages <= mem_move->dest_segment) { E_printf("EMS: Xchng memory region dest numpages %d > numpages %d available\n", mem_move->dest_segment, handle_info[mem_move->dest_handle].numpages); return (EMM_LOG_OUT_RAN); } dest = handle_info[mem_move->dest_handle].object + mem_move->dest_segment * EMM_PAGE_SIZE + mem_move->dest_offset; mem = handle_info[mem_move->dest_handle].object + handle_info[mem_move->dest_handle].numpages * EMM_PAGE_SIZE; if ((dest + mem_move->size) > mem) { E_printf("EMS: Dest xchng of %p is outside handle allocation of %p, size=%x\n", dest, mem, mem_move->size); return (EMM_MOVE_SIZE); } } if (source < dest) { if (source + mem_move->size > dest) return (EMM_MOVE_OVLAPI); } else if (dest + mem_move->size > source) return (EMM_MOVE_OVLAPI); E_printf("EMS: Exchange Memory Region from %p -> %p\n", source, dest); tmp = malloc(mem_move->size); memmove(tmp, source, mem_move->size); memmove(source, dest, mem_move->size); memmove(dest, tmp, mem_move->size); free(tmp); return (0); } static int get_mpa_array(state_t * state) { switch (LOW(state->eax)) { case GET_MPA:{ u_short *ptr = (u_short *) Addr(state, es, edi); int i; Kdebug0((dbg_fd, "GET_MPA addr %p called\n", ptr)); /* the array must be given in ascending order of segment, so give the page frame only after other pages */ for (i = cnv_pages_start; i < phys_pages; i++) { *ptr++ = PHYS_PAGE_SEGADDR(i); *ptr++ = i; Kdebug0((dbg_fd, "seg_addr 0x%x page_no %d\n", PHYS_PAGE_SEGADDR(i), i)); } for (i = 0; i < cnv_pages_start; i++) { *ptr++ = PHYS_PAGE_SEGADDR(i); *ptr++ = i; Kdebug0((dbg_fd, "seg_addr 0x%x page_no %d\n", PHYS_PAGE_SEGADDR(i), i)); } SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->ecx), phys_pages); return (TRUE); } case GET_MPA_ENTRY_COUNT: Kdebug0((dbg_fd, "GET_MPA_ENTRY_COUNT called\n")); SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->ecx), phys_pages); return (TRUE); default: NOFUNC(); return (UNCHANGED); } } static int get_ems_hardinfo(state_t * state) { if (os_allow) { switch (LOW(state->eax)) { case GET_ARRAY:{ u_short *ptr = (u_short *) Addr(state, es, edi); *ptr++ = (16 * 1024) / 16; /* raw page size in paragraphs, (16Bytes), 16K */ *ptr++ = 0; /* # of alternate register sets */ *ptr++ = PAGE_MAP_SIZE(phys_pages); /* size of context save area */ *ptr++ = 0; /* no DMA channels */ *ptr++ = 0; /* DMA channel operation */ Kdebug1((dbg_fd, "bios_emm: Get Hardware Info\n")); SETHIGH(&(state->eax), EMM_NO_ERR); return (TRUE); break; } case GET_RAW_PAGECOUNT:{ u_short left = EMM_TOTAL - emm_allocated; Kdebug1((dbg_fd, "bios_emm: Get Raw Page Counts left=0x%x, t:0x%x, a:0x%x\n", left, EMM_TOTAL, emm_allocated)); SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->ebx), left); SETWORD(&(state->edx), (u_short) EMM_TOTAL); return (TRUE); } default: NOFUNC(); return (UNCHANGED); } } else { Kdebug1((dbg_fd, "bios_emm: Get Hardware Info/Raw Pages - Denied\n")); SETHIGH(&(state->eax), 0xa4); return(TRUE); } } static int allocate_std_pages(state_t * state) { int pages_needed = WORD(state->ebx); int handle; Kdebug1((dbg_fd, "bios_emm: Get Handle and Standard Allocate pages = 0x%x\n", pages_needed)); if ((handle = emm_allocate_handle(pages_needed)) == EMM_ERROR) { SETHIGH(&(state->eax), emm_error); return (UNCHANGED); } SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->edx), handle); Kdebug1((dbg_fd, "bios_emm: handle = 0x%x\n", handle)); return 0; } static int get_map_registers(void *ptr, int pages, const u_short *phys) { u_short *buf = ptr; int phy, i; for (i = 0; i < pages; i++) { phy = phys ? phys[i] : i; buf[i * 2] = emm_map[phy].handle; buf[i * 2 + 1] = emm_map[phy].logical_page; Kdebug1((dbg_fd, "phy %d h %x lp %d\n", phy, emm_map[phy].handle, emm_map[phy].logical_page)); } return EMM_NO_ERR; } static void emm_get_map_registers(char *ptr) { if (!config.ems_size) return; get_map_registers(ptr, phys_pages, NULL); } static void set_map_registers(const void *ptr, int pages) { const u_short *buf = ptr; int i; int handle; int logical_page; for (i = 0; i < pages; i++) { handle = buf[i * 2]; if (handle == OS_HANDLE) E_printf("EMS: trying to use OS handle in ALT_SET_REGISTERS\n"); logical_page = buf[i * 2 + 1]; if ((u_short)handle != 0xffff) map_page(handle, i, logical_page); else unmap_page(i); Kdebug1((dbg_fd, "phy %d h %x lp %d\n", i, handle, logical_page)); } } static void emm_set_map_registers(char *ptr) { if (!config.ems_size) return; set_map_registers(ptr, phys_pages); } static int save_es=0; static int save_di=0; static void alternate_map_register(state_t * state) { if (!os_allow) { SETHIGH(&(state->eax), 0xa4); Kdebug1((dbg_fd, "bios_emm: Illegal alternate_map_register\n")); return; } switch (LOW(state->eax)) { case 0:{ /* Get Alternate Map Register */ if (save_es){ Kdebug1((dbg_fd, "bios_emm: Get Alternate Map Registers\n")); emm_get_map_registers(MK_FP32(save_es, save_di)); SETWORD(&(state->es),(u_short)save_es); SETWORD(&(state->edi),(u_short)save_di); SETHIGH(&(state->eax), EMM_NO_ERR); Kdebug1((dbg_fd, "bios_emm: Get Alternate Registers - Set to ES:0x%x, DI:0x%x\n", save_es, save_di)); } else { SETWORD(&(state->es), 0x0u); SETWORD(&(state->edi), 0x0); SETHIGH(&(state->eax), EMM_NO_ERR); Kdebug1((dbg_fd, "bios_emm: Get Alternate Registers - Not Active\n")); } return; } case 2: Kdebug1((dbg_fd, "bios_emm: Get Alternate Map Save Array Size = 0x%zx\n", PAGE_MAP_SIZE(phys_pages))); SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->edx), PAGE_MAP_SIZE(phys_pages)); return; case 3: /* Allocate Alternate Register */ SETHIGH(&(state->eax), EMM_NO_ERR); SETLOW(&(state->ebx), 0x0); Kdebug1((dbg_fd, "bios_emm: alternate_map_register Allocate Alternate Register\n")); return; case 5: /* Allocate DMA */ SETHIGH(&(state->eax), EMM_NO_ERR); Kdebug1((dbg_fd, "bios_emm: alternate_map_register Enable DMA bl=0x%x\n", (unsigned int)LOW(state->ebx))); return; } if (LOW(state->ebx)) { SETHIGH(&(state->eax), 0x8f); Kdebug1((dbg_fd, "bios_emm: Illegal alternate_map_register request for reg set 0x%x\n", (unsigned int)LOW(state->ebx))); return; } switch (LOW(state->eax)) { case 1:{ /* Set Alternate Map Register */ Kdebug1((dbg_fd, "bios_emm: Set Alternate Registers\n")); if (WORD(state->es)) { emm_set_map_registers((char *) Addr(state, es, edi)); } SETHIGH(&(state->eax), EMM_NO_ERR); save_es=WORD(state->es); save_di=WORD(state->edi); Kdebug1((dbg_fd, "bios_emm: Set Alternate Registers - Setup ES:0x%x, DI:0x%x\n", save_es, save_di)); break; } break; case 4: /* Deallocate Register */ SETHIGH(&(state->eax), EMM_NO_ERR); Kdebug1((dbg_fd, "bios_emm: alternate_map_register Deallocate Register\n")); break; case 6: /* Enable DMA */ if (LOW(state->ebx) > 0) { SETHIGH(&(state->eax), EMM_INVALID_SUB); Kdebug1((dbg_fd, "bios_emm: alternate_map_register Enable DMA not allowed bl=0x%x\n", (unsigned int)LOW(state->ebx))); } else { SETHIGH(&(state->eax), EMM_NO_ERR); Kdebug1((dbg_fd, "bios_emm: alternate_map_register Enable DMA\n")); } break; case 7: /* Disable DMA */ if (LOW(state->ebx) > 0) { SETHIGH(&(state->eax), EMM_INVALID_SUB); Kdebug1((dbg_fd, "bios_emm: alternate_map_register Disable DMA not allowed bl=0x%x\n", (unsigned int)LOW(state->ebx))); } else { SETHIGH(&(state->eax), EMM_NO_ERR); Kdebug1((dbg_fd, "bios_emm: alternate_map_register Disable DMA\n")); } break; case 8: /* Deallocate DMA */ if (LOW(state->ebx) > 0) { SETHIGH(&(state->eax), EMM_INVALID_SUB); Kdebug1((dbg_fd, "bios_emm: alternate_map_register Deallocate DMA not allowed bl=0x%x\n", (unsigned int)LOW(state->ebx))); } else { SETHIGH(&(state->eax), EMM_NO_ERR); Kdebug1((dbg_fd, "bios_emm: alternate_map_register Deallocate DMA\n")); } break; default: SETHIGH(&(state->eax), EMM_FUNC_NOSUP); Kdebug1((dbg_fd, "bios_emm: alternate_map_register Illegal Function\n")); } return; } static void os_set_function(state_t * state) { switch (LOW(state->eax)) { case 00: /* Open access to EMS / Get OS key */ if (os_inuse) { if (WORD(state->ebx) == os_key1 && WORD(state->ecx) == os_key2) { os_allow=1; SETHIGH(&(state->eax), EMM_NO_ERR); Kdebug1((dbg_fd, "bios_emm: OS Allowing access\n")); } else { SETHIGH(&(state->eax), 0xa4); Kdebug1((dbg_fd, "bios_emm: Illegal OS Allow access attempt\n")); } } else { os_allow=1; os_inuse=1; SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->ebx), os_key1); SETWORD(&(state->ecx), os_key2); Kdebug1((dbg_fd, "bios_emm: Initial OS Allowing access\n")); } return; case 01: /* Disable access to EMS */ if (os_inuse) { if (WORD(state->ebx) == os_key1 && WORD(state->ecx) == os_key2) { os_allow=0; SETHIGH(&(state->eax), EMM_NO_ERR); Kdebug1((dbg_fd, "bios_emm: OS Disallowing access\n")); } else { SETHIGH(&(state->eax), 0xa4); Kdebug1((dbg_fd, "bios_emm: Illegal OS Disallow access attempt\n")); } } else { os_allow=0; os_inuse=1; SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->ebx), os_key1); SETWORD(&(state->ecx), os_key2); Kdebug1((dbg_fd, "bios_emm: Initial OS Disallowing access\n")); } return; case 02: /* Return access */ if (WORD(state->ebx) == os_key1 && WORD(state->ecx) == os_key2) { os_allow=1; os_inuse=0; SETHIGH(&(state->eax), EMM_NO_ERR); Kdebug1((dbg_fd, "bios_emm: OS Returning access\n")); } else { SETHIGH(&(state->eax), 0xa4); Kdebug1((dbg_fd, "bios_emm: OS Illegal returning access attempt\n")); } return; } } /* end of EMS 4.0 functions */ boolean_t ems_fn(state) state_t *state; { switch (HIGH(state->eax)) { case GET_MANAGER_STATUS:{ /* 0x40 */ Kdebug1((dbg_fd, "bios_emm: Get Manager Status\n")); SETHIGH(&(state->eax), EMM_NO_ERR); break; } case GET_PAGE_FRAME_SEGMENT:{/* 0x41 */ Kdebug1((dbg_fd, "bios_emm: Get Page Frame Segment\n")); SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->ebx), EMM_SEGMENT); break; } case GET_PAGE_COUNTS:{ /* 0x42 */ u_short left = EMM_TOTAL - emm_allocated; Kdebug1((dbg_fd, "bios_emm: Get Page Counts left=0x%x, t:0x%x, a:0x%x\n", left, EMM_TOTAL, emm_allocated)); SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->ebx), left); SETWORD(&(state->edx), (u_short) EMM_TOTAL); break; } case GET_HANDLE_AND_ALLOCATE:{ /* 0x43 */ int pages_needed = WORD(state->ebx); int handle; Kdebug1((dbg_fd, "bios_emm: Get Handle and Allocate pages = 0x%x\n", pages_needed)); if (pages_needed == 0) { SETHIGH(&(state->eax), EMM_ZERO_PAGES); return (UNCHANGED); } if ((handle = emm_allocate_handle(pages_needed)) == EMM_ERROR) { SETHIGH(&(state->eax), emm_error); return (UNCHANGED); } SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->edx), handle); Kdebug1((dbg_fd, "bios_emm: handle = 0x%x\n", handle)); break; } case MAP_UNMAP:{ /* 0x44 */ int physical_page = LOW(state->eax); int logical_page = WORD(state->ebx); int handle = WORD(state->edx); int ret; ret = do_map_unmap(handle, physical_page, logical_page); SETHIGH(&(state->eax), ret); break; } case DEALLOCATE_HANDLE:{ /* 0x45 */ int handle = WORD(state->edx); Kdebug1((dbg_fd, "bios_emm: Deallocate Handle, han-0x%x\n", handle)); if (handle == OS_HANDLE) E_printf("EMS: trying to use OS handle in DEALLOCATE_HANDLE\n"); if ((handle < 0) || (handle >= MAX_HANDLES)) { E_printf("EMS: Invalid Handle\n"); SETHIGH(&(state->eax), EMM_INV_HAN); return (UNCHANGED); } if (handle_info[handle].active == 0) { E_printf("EMS: Invalid Handle\n"); SETHIGH(&(state->eax), EMM_INV_HAN); return (UNCHANGED); } emm_deallocate_handle(handle); SETHIGH(&(state->eax), EMM_NO_ERR); break; } case GET_EMM_VERSION:{ /* 0x46 */ Kdebug1((dbg_fd, "bios_emm: Get EMM version\n")); SETHIGH(&(state->eax), EMM_NO_ERR); SETLOW(&(state->eax), EMM_VERS); break; } case SAVE_PAGE_MAP:{ /* 0x47 */ int handle = WORD(state->edx); Kdebug1((dbg_fd, "bios_emm: Save Page Map, han-0x%x\n", handle)); if (handle == OS_HANDLE) E_printf("EMS: trying to use OS handle in SAVE_PAGE_MAP\n"); if ((handle < 0) || (handle >= MAX_HANDLES)) { SETHIGH(&(state->eax), EMM_INV_HAN); return (UNCHANGED); } if (handle_info[handle].active == 0) { SETHIGH(&(state->eax), EMM_INV_HAN); return (UNCHANGED); } emm_save_handle_state(handle); SETHIGH(&(state->eax), EMM_NO_ERR); break; } case RESTORE_PAGE_MAP:{ /* 0x48 */ int handle = WORD(state->edx); Kdebug1((dbg_fd, "bios_emm: Restore Page Map, han-0x%x\n", handle)); if (handle == OS_HANDLE) E_printf("EMS: trying to use OS handle in RESTORE_PAGE_MAP\n"); if ((handle < 0) || (handle >= MAX_HANDLES)) { SETHIGH(&(state->eax), EMM_INV_HAN); return (UNCHANGED); } if (handle_info[handle].active == 0) { SETHIGH(&(state->eax), EMM_INV_HAN); return (UNCHANGED); } emm_restore_handle_state(handle); SETHIGH(&(state->eax), EMM_NO_ERR); break; } case GET_HANDLE_COUNT:{ /* 0x4b */ Kdebug1((dbg_fd, "bios_emm: Get Handle Count\n")); SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->ebx), (unsigned short) handle_total); Kdebug1((dbg_fd, "bios_emm: handle_total = 0x%x\n", handle_total)); break; } case GET_PAGES_OWNED:{ /* 0x4c */ int handle = WORD(state->edx); u_short pages; if (handle == OS_HANDLE) E_printf("EMS: trying to use OS handle in GET_PAGES_OWNED\n"); Kdebug1((dbg_fd, "bios_emm: Get Pages Owned, han-0x%x\n", handle)); if ((handle < 0) || (handle >= MAX_HANDLES)) { SETHIGH(&(state->eax), EMM_INV_HAN); SETWORD(&(state->ebx), 0); return (UNCHANGED); } if (handle_info[handle].active == 0) { SETHIGH(&(state->eax), EMM_INV_HAN); SETWORD(&(state->ebx), 0); return (UNCHANGED); } pages = handle_pages(handle); SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->ebx), pages); Kdebug1((dbg_fd, "bios_emm: pages owned - 0x%x\n", pages)); break; } case GET_PAGES_FOR_ALL:{ /* 0x4d */ u_short i; int tot_pages = 0, tot_handles = 0; u_short *ptr = (u_short *) Addr(state, es, edi); Kdebug1((dbg_fd, "bios_emm: Get Pages For all to %p\n", ptr)); for (i = 0; i < MAX_HANDLES; i++) { if ((i == 0) || (handle_info[i].numpages > 0)) { *ptr = i & 0xff; ptr++; *ptr = handle_pages(i); tot_pages += *ptr; Kdebug0((dbg_fd, "handle %d pages %d\n", i, *ptr)); ptr++; tot_handles++; } } SETHIGH(&(state->eax), EMM_NO_ERR); SETWORD(&(state->ebx), handle_total); Kdebug1((dbg_fd, "bios_emm: total handles = 0x%x 0x%x\n", handle_total, tot_handles)); Kdebug1((dbg_fd, "bios_emm: total pages = 0x%x\n", tot_pages)); break; } case PAGE_MAP_REGISTERS:{ /* 0x4e */ Kdebug1((dbg_fd, "bios_emm: Page Map Registers Function.\n")); switch (LOW(state->eax)) { case GET_REGISTERS:{ Kdebug1((dbg_fd, "bios_emm: Get Registers\n")); emm_get_map_registers((char *) Addr(state, es, edi)); break; } case SET_REGISTERS:{ Kdebug1((dbg_fd, "bios_emm: Set Registers\n")); emm_set_map_registers((char *) Addr(state, ds, esi)); break; } case GET_AND_SET_REGISTERS:{ Kdebug1((dbg_fd, "bios_emm: Get and Set Registers\n")); emm_get_map_registers((char *) Addr(state, es, edi)); emm_set_map_registers((char *) Addr(state, ds, esi)); break; } case GET_SIZE_FOR_PAGE_MAP:{ Kdebug1((dbg_fd, "bios_emm: Get size for page map\n")); SETHIGH(&(state->eax), EMM_NO_ERR); SETLOW(&(state->eax), PAGE_MAP_SIZE(phys_pages)); return (UNCHANGED); } default:{ Kdebug1((dbg_fd, "bios_emm: Page Map Regs unknwn fn\n")); SETHIGH(&(state->eax), EMM_FUNC_NOSUP); return (UNCHANGED); } } SETHIGH(&(state->eax), EMM_NO_ERR); break; } case PARTIAL_MAP_REGISTERS: /* 0x4f */ partial_map_registers(state); break; case MAP_UNMAP_MULTIPLE: /* 0x50 */ map_unmap_multiple(state); break; case REALLOCATE_PAGES: /* 0x51 */ reallocate_pages(state); break; case HANDLE_ATTRIBUTE: /* 0x52 */ handle_attribute(state); break; case HANDLE_NAME: /* 0x53 */ handle_name(state); break; case HANDLE_DIR: /* 0x54 */ handle_dir(state); break; case ALTER_MAP_AND_JUMP: /* 0x55 */ alter_map_and_jump(state); break; case ALTER_MAP_AND_CALL: /* 0x56 */ alter_map_and_call(state); break; case MOD_MEMORY_REGION: /* 0x57 */ switch (LOW(state->eax)) { case 0x00: SETHIGH(&(state->eax), move_memory_region(state)); break; case 0x01: SETHIGH(&(state->eax), exchange_memory_region(state)); break; default: NOFUNC(); } E_printf("EMS: MOD returns %x\n", (u_short)HIGH(state->eax)); break; case GET_MPA_ARRAY: /* 0x58 */ get_mpa_array(state); break; case GET_EMS_HARDINFO: /* 0x59 */ get_ems_hardinfo(state); break; case ALLOCATE_STD_PAGES: /* 0x5a */ switch (LOW(state->eax)) { case 00: /* Allocate Standard Pages */ case 01: /* Allocate Raw Pages */ allocate_std_pages(state); } break; case ALTERNATE_MAP_REGISTER: /* 0x5B */ alternate_map_register(state); break; case PREPARE_FOR_WARMBOOT: /* 0x5C */ ems_reset(); SETHIGH(&(state->eax), EMM_NO_ERR); break; case OS_SET_FUNCTION: /* 0x5D */ os_set_function(state); break; /* This seems to be used by DV. case 0xde: SETHIGH(&(state->eax), 0x00); SETLOW(&(state->ebx), 0x00); SETHIGH(&(state->ebx), 0x01); break; */ default:{ Kdebug1((dbg_fd, "bios_emm: EMM function NOT supported 0x%04x\n", (unsigned) WORD(state->eax))); SETHIGH(&(state->eax), EMM_FUNC_NOSUP); break; } } return (UNCHANGED); } static void ems_reset2(void) { int sh_base; int j; if (!config.ems_size && !config.pm_dos_api) return; emm_allocated = config.ems_cnv_pages; for (sh_base = 0; sh_base < EMM_MAX_PHYS; sh_base++) { emm_map[sh_base].handle = NULL_HANDLE; } for (sh_base = 0; sh_base < MAX_HANDLES; sh_base++) { handle_info[sh_base].numpages = 0; handle_info[sh_base].active = 0; } /* set up OS handle here */ handle_info[OS_HANDLE].numpages = config.ems_cnv_pages; handle_info[OS_HANDLE].object = LOWMEM(cnv_start_seg << 4); handle_info[OS_HANDLE].active = 0xff; for (j = 0; j < saved_phys_pages; j++) { handle_info[OS_HANDLE].saved_mappings_logical[j] = NULL_PAGE; } for (sh_base = 0; sh_base < config.ems_cnv_pages; sh_base++) { emm_map[sh_base + cnv_pages_start].handle = OS_HANDLE; emm_map[sh_base + cnv_pages_start].logical_page = sh_base; } handle_total = 1; SET_HANDLE_NAME(handle_info[OS_HANDLE].name, "SYSTEM "); } void ems_reset(void) { int sh_base; for (sh_base = 1; sh_base < MAX_HANDLES; sh_base++) { if (handle_info[sh_base].active) emm_deallocate_handle(sh_base); } ems_reset2(); } void ems_init(void) { int i, j; emu_hlt_t hlt_hdlr; if (!config.ems_size && !config.pm_dos_api) return; if (config.ems_uma_pages > EMM_UMA_MAX_PHYS) { error("config.ems_uma_pages is too large\n"); config.exitearly = 1; return; } if (config.ems_uma_pages < 4 && config.pm_dos_api) { error("config.ems_uma_pages is too small, DPMI must be disabled\n"); config.exitearly = 1; return; } if (config.ems_cnv_pages > EMM_CNV_MAX_PHYS) { error("config.ems_cnv_pages is too large\n"); config.exitearly = 1; return; } open_mapping(MAPPING_EMS); E_printf("EMS: initializing memory\n"); memcheck_addtype('E', "EMS page frame"); /* set up standard EMS frame in UMA */ for (i = 0; i < config.ems_uma_pages; i++) { emm_map[i].phys_seg = EMM_SEGMENT + 0x400 * i; memcheck_reserve('E', PHYS_PAGE_ADDR(i), EMM_PAGE_SIZE); } /* now in conventional mem */ E_printf("EMS: Using %i pages in conventional memory, starting from 0x%x\n", config.ems_cnv_pages, cnv_start_seg); for (j = 0; j < config.ems_cnv_pages; j++, i++) { emm_map[i].phys_seg = cnv_start_seg + 0x400 * j; } E_printf("EMS: initialized %i pages\n", phys_pages); ems_reset2(); /* install HLT handler */ hlt_hdlr.name = "EMS"; hlt_hdlr.len = 1; hlt_hdlr.func = emm_hlt_handler; hlt_hdlr.arg = NULL; EMSControl_OFF = hlt_register_handler(hlt_hdlr); }