kernel: Use hashdestroy() to free hash tables allocated with hashinit().
[dragonfly.git] / usr.bin / doscmd / ems.c
... / ...
CommitLineData
1/*-
2 * Copyright (c) 1997 Helmut Wirth <hfwirth@ping.at>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice immediately at the beginning of the file, witout modification,
10 * this list of conditions, and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD: src/usr.bin/doscmd/ems.c,v 1.3.2.2 2002/04/25 11:04:51 tg Exp $
29 * $DragonFly: src/usr.bin/doscmd/ems.c,v 1.4 2005/10/30 23:00:57 swildner Exp $
30 */
31
32/*
33 * EMS memory emulation
34 *
35 * To emulate Expanded Memory we use a DOS driver (emsdriv.sys) which
36 * routes calls to int 0x67 to this emulator routine. The main entry point
37 * is ems_entry(..). The emulator needs to be initialized before the first
38 * call. The first step of the initialization is done during program startup
39 * the second part is done during DOS boot, from a call of the DOS driver.
40 * The DOS driver is neccessary because DOS programs look for it to
41 * determine if EMS is available.
42 *
43 * To emulate a configurable amount of EMS memory we use a file created
44 * at startup with the size of the configured EMS memory. This file is
45 * mapped into the EMS window like any DOS memory manager would do, using
46 * mmap calls.
47 *
48 * The emulation follows the LIM EMS 4.0 standard. Not all functions of it
49 * are implemented yet. The "alter page map and jump" and "alter page map
50 * and call" functions are not implemented, because they are rather hard to
51 * do. (It would mean a call to the emulator executes a routine in EMS
52 * memory and returns to the emulator, the emulator switches the page map
53 * and then returns to the DOS program.) LINUX does not emulate this
54 * functions and I think they were very rarely used by DOS applications.
55 *
56 * Credits: To the writers of LINUX dosemu, I looked at their code
57 */
58
59#include <sys/types.h>
60#include <sys/param.h>
61#include <sys/mman.h>
62#include <unistd.h>
63
64#include "doscmd.h"
65#include "ems.h"
66
67/* Will be configurable */
68u_long ems_max_size = EMS_MAXSIZE * 1024;
69u_long ems_frame_addr = EMS_FRAME_ADDR;
70
71/*
72 * Method for EMS: Allocate a mapfile with the size of EMS memory
73 * and map the needed part into the page frame
74 */
75
76#define EMS_MAP_PATH "/var/tmp/" /* Use a big file system */
77#define EMS_MAP_FILE "doscmd.XXXXXX"
78static int mapfile_fd = -1;
79
80/* Pages are always 16 kB in size. The page frame is 64 kB, there are
81 * 4 positions (0..3) for a page to map in. The pages are numbered from 0 to
82 * the highest 16 kB page in the mapfile, depending on the EMS size
83 */
84
85EMS_mapping_context ems_mapping_context;
86
87/* Handle and page management (see ems.h) */
88
89/* The handle array. If the pointer is NULL, the handle is unallocated */
90static EMS_handle *ems_handle[EMS_NUM_HANDLES];
91static u_long ems_alloc_handles;
92/* The active handle, if any */
93static short active_handle;
94
95/* The page array. It is malloced at runtime, depending on the total
96 * allocation size
97 */
98
99static EMS_page *ems_page = NULL;
100static u_long ems_total_pages;
101static u_long ems_alloc_pages;
102static u_long ems_free_pages;
103
104/* Local structure used for region copy and move operations */
105
106struct copydesc {
107#define SRC_EMS 1
108#define DST_EMS 2
109 short copytype; /* Type of source and destination memory */
110 EMS_addr src_addr; /* Combined pointer for source */
111 EMS_addr dst_addr; /* Combined pointer for destination */
112 u_long rest_len; /* Lenght to copy */
113};
114
115
116/* Local prototypes */
117static int init_mapfile(void);
118static void map_page(u_long, u_char, short, int);
119static EMS_handle *get_new_handle(long);
120static void context_to_handle(short);
121static long find_next_free_handle(void);
122static short lookup_handle(Hname *hp);
123static void allocate_pages_to_handle(u_short, long);
124static void allocate_handle(short, long);
125static void reallocate_pages_to_handle(u_short, long);
126static void free_handle(short);
127static void free_pages_of_handle(short);
128static void restore_context(EMS_mapping_context *);
129static void save_context_to_dos(EMScontext *);
130static int check_saved_context(EMScontext *);
131static void *get_valid_pointer(u_short, u_short, u_long);
132static u_long move_ems_to_conv(short, u_short, u_short, u_long, u_long);
133static u_long move_conv_to_ems(u_long, u_short, u_short, u_short, u_long);
134static u_long move_ems_to_ems(u_short, u_short, u_short, u_short,
135 u_short, u_short, u_long);
136
137/*
138 * EMS initialization routine: Return 1, if successful, return 0 if
139 * init problem or EMS disabled
140 */
141
142int
143ems_init(void)
144{
145 unsigned i;
146
147 if (ems_max_size == 0)
148 return 0;
149 if (init_mapfile() == 0)
150 return 0;
151 /* Sanity */
152 bzero((void *)(&ems_handle[0]), sizeof(ems_handle));
153 ems_total_pages = ems_max_size / EMS_PAGESIZE;
154 ems_alloc_pages = 0;
155 ems_free_pages = ems_total_pages;
156 ems_alloc_handles = 0;
157 active_handle = 0;
158 /* Malloc the page array */
159 ems_page = (EMS_page *)malloc(sizeof(EMS_page) * ems_total_pages);
160 if (ems_page == NULL) {
161 debug(D_ALWAYS, "Could not malloc page array, EMS disabled\n");
162 ems_frame_addr = 0;
163 ems_max_size = 0;
164 ems_total_pages = 0;
165 return 0;
166 }
167 for (i = 0; i < ems_total_pages; i++) {
168 ems_page[i].handle = 0;
169 ems_page[i].status = EMS_FREE;
170 }
171 debug(D_EMS, "EMS: Emulation init OK.\n");
172 return 1;
173}
174
175
176/* Main entry point */
177
178void
179ems_entry(regcontext_t *REGS)
180{
181 /*
182 * If EMS is not enabled, the DOS ems.exe module should not have
183 * been loaded. If it is loaded anyway, report software malfunction
184 */
185 if (ems_max_size == 0) {
186 R_AH = EMS_SW_MALFUNC;
187 debug(D_EMS, "EMS emulation not enabled\n");
188 return;
189 }
190
191 switch (R_AH)
192 {
193 case GET_MANAGER_STATUS:
194 debug(D_EMS, "EMS: Get manager status\n");
195 R_AH = EMS_SUCCESS;
196 break;
197
198 case GET_PAGE_FRAME_SEGMENT:
199 debug(D_EMS, "EMS: Get page frame segment\n");
200 R_BX = ems_frame_addr >> 4;
201 R_AH = EMS_SUCCESS;
202 break;
203
204 case GET_PAGE_COUNTS:
205 R_BX = ems_total_pages - ems_alloc_pages;
206 R_DX = ems_total_pages;
207 debug(D_EMS, "EMS: Get page count: Returned total=%d, free=%d\n",
208 R_DX, R_BX);
209 R_AH = EMS_SUCCESS;
210 break;
211
212 case GET_HANDLE_AND_ALLOCATE:
213 {
214 u_short npages;
215 short handle;
216
217 npages = R_BX;
218 debug(D_EMS, "EMS: Get handle and allocate %d pages: ", npages);
219
220 /* Enough handles? */
221 if ((handle = find_next_free_handle()) < 0) {
222 debug(D_EMS,"Return error:No handles\n");
223 R_AH = EMS_OUT_OF_HANDLES;
224 break;
225 }
226 /* Enough memory for this request ? */
227 if (npages > ems_free_pages) {
228 debug(D_EMS,"Return error:Request too big\n");
229 R_AH = EMS_OUT_OF_LOG;
230 break;
231 }
232 if (npages > ems_total_pages) {
233 debug(D_EMS,"Return error:Request too big\n");
234 R_AH = EMS_OUT_OF_PHYS;
235 break;
236 }
237 /* Not allowed to allocate zero pages with this function */
238 if (npages == 0) {
239 debug(D_EMS,"Return error:Cannot allocate 0 pages\n");
240 R_AH = EMS_ZERO_PAGES;
241 break;
242 }
243 /* Allocate the handle */
244 allocate_handle(handle, npages);
245
246 /* Allocate the pages */
247 allocate_pages_to_handle(handle, npages);
248 R_DX = handle;
249 R_AH = EMS_SUCCESS;
250 debug(D_EMS,"Return success:Handle = %d\n", handle);
251 break;
252 }
253
254 case MAP_UNMAP:
255 {
256 u_char position;
257 u_short hpagenum, spagenum;
258 short handle;
259
260 debug(D_EMS, "EMS: Map/Unmap handle=%d, pos=%d, pagenum=%d ",
261 R_DX, R_AL, R_BX);
262 handle = R_DX;
263 position = R_AL;
264 if (position > 3) {
265 debug(D_EMS, "invalid position\n");
266 R_AH = EMS_ILL_PHYS;
267 break;
268 }
269 hpagenum = R_BX;
270 /* This succeeds without a valid handle ! */
271 if (hpagenum == 0xffff) {
272 /* Unmap only */
273 map_page(0, position, handle, 1);
274 debug(D_EMS, "(unmap only) success\n");
275 R_AH = EMS_SUCCESS;
276 break;
277 }
278 if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
279 R_AH = EMS_INV_HANDLE;
280 debug(D_EMS, "invalid handle\n");
281 break;
282 }
283 if (hpagenum >= ems_handle[handle]->npages) {
284 R_AH = EMS_LOGPAGE_TOOBIG;
285 debug(D_EMS, "invalid pagenumber\n");
286 break;
287 }
288 spagenum = ems_handle[handle]->pagenum[hpagenum];
289 map_page(spagenum, position, handle, 0);
290 debug(D_EMS, "success\n");
291 R_AH = EMS_SUCCESS;
292 break;
293 }
294
295 case DEALLOCATE_HANDLE:
296 {
297 short handle;
298
299 /* Handle valid ? */
300 handle = R_DX;
301 debug(D_EMS, "EMS: Deallocate handle %d\n", handle);
302 if (handle > 255 || ems_handle[handle] == NULL) {
303 R_AH = EMS_INV_HANDLE;
304 break;
305 }
306 /* Mapping context saved ? */
307 if (ems_handle[handle]->mcontext != NULL) {
308 R_AH = EMS_SAVED_MAP;
309 break;
310 }
311
312 free_pages_of_handle(handle);
313 free_handle(handle);
314 R_AH = EMS_SUCCESS;
315 break;
316 }
317
318 case GET_EMM_VERSION:
319 debug(D_EMS, "EMS: Get version\n");
320 R_AL = EMS_VERSION;
321 R_AH = EMS_SUCCESS;
322 break;
323
324 case SAVE_PAGE_MAP:
325 {
326 short handle;
327
328 debug(D_EMS, "EMS: Save page map\n");
329 handle = R_DX;
330 if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
331 R_AH = EMS_INV_HANDLE;
332 break;
333 }
334 if (ems_handle[handle]->mcontext != NULL) {
335 /* There is already a context saved */
336 if (memcmp((void *)ems_handle[handle]->mcontext,
337 (void *)&ems_mapping_context,
338 sizeof(EMS_mapping_context)) == 0)
339 R_AH = EMS_ALREADY_SAVED;
340 else
341 R_AH = EMS_NO_ROOM_TO_SAVE;
342 break;
343 }
344 context_to_handle(handle);
345 R_AH = EMS_SUCCESS;
346 break;
347 }
348
349 case RESTORE_PAGE_MAP:
350 {
351 short handle;
352
353 debug(D_EMS, "EMS: Restore page map\n");
354 handle = R_DX;
355 if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
356 R_AH = EMS_INV_HANDLE;
357 break;
358 }
359 if (ems_handle[handle]->mcontext == NULL) {
360 R_AH = EMS_NO_SAVED_CONTEXT;
361 break;
362 }
363 restore_context(ems_handle[handle]->mcontext);
364 free((void *)ems_handle[handle]->mcontext);
365 ems_handle[handle]->mcontext = NULL;
366 R_AH = EMS_SUCCESS;
367 break;
368 }
369
370 case RESERVED_1:
371 case RESERVED_2:
372 debug(D_ALWAYS, "Reserved function called: %02x\n", R_AH);
373 R_AH = EMS_FUNC_NOSUP;
374 break;
375
376 case GET_HANDLE_COUNT:
377 debug(D_EMS, "EMS: Get handle count\n");
378 R_BX = ems_alloc_handles + 1;
379 R_AH = EMS_SUCCESS;
380 break;
381
382 case GET_PAGES_OWNED:
383 {
384 short handle;
385
386 debug(D_EMS, "EMS: Get pages owned\n");
387 /* Handle valid ? */
388 handle = R_DX;
389 if (handle > 255 || ems_handle[handle] == NULL) {
390 R_AH = EMS_INV_HANDLE;
391 break;
392 }
393 if (handle == 0)
394 R_BX = 0;
395 else
396 R_BX = ems_handle[handle]->npages;
397 R_AH = EMS_SUCCESS;
398 break;
399 }
400
401 case GET_PAGES_FOR_ALL:
402 {
403 EMShandlepage *ehp;
404 unsigned safecount;
405 int i;
406
407 debug(D_EMS, "EMS: Get pages for all\n");
408 /* Get the address passed from DOS app */
409 ehp = (EMShandlepage *)get_valid_pointer(R_ES, R_DI,
410 sizeof(EMShandlepage) * ems_alloc_handles);
411 if (ehp == NULL) {
412 R_AH = EMS_SW_MALFUNC;
413 break;
414 }
415
416 R_BX = ems_alloc_handles;
417 safecount = 0;
418 for (i = 0; i < 255; i++) {
419 if (ems_handle[i] != NULL) {
420 if (safecount > (ems_alloc_handles+1))
421 fatal("EMS: ems_alloc_handles is wrong, "
422 "cannot continue\n");
423 ehp->handle = i;
424 ehp->npages = ems_handle[i]->npages;
425 ehp++;
426 safecount++;
427 }
428 }
429 R_AH = EMS_SUCCESS;
430 break;
431 }
432
433 case PAGE_MAP:
434 /* This function is a nuisance. It was invented to save time and
435 * memory, but in our case it is useless. We have to support it
436 * but we use the same save memory as for the page map function.
437 * It uses only 20 bytes anyway. We store/restore the entire mapping
438 */
439 case PAGE_MAP_PARTIAL:
440 {
441 int subfunction;
442 EMScontext *src, *dest;
443
444 debug(D_EMS, "EMS: Page map ");
445 subfunction = R_AL;
446 if (R_AH == PAGE_MAP_PARTIAL) {
447 debug(D_EMS, "partial ");
448 /* Page map partial has slightly different subfunctions
449 * GET_SET does not exist and is GET_SIZE in this case
450 */
451 if (subfunction == GET_SET)
452 subfunction = GET_SIZE;
453 }
454 switch (subfunction)
455 {
456 case GET:
457 {
458 debug(D_EMS, "get\n");
459 /* Get the address passed from DOS app */
460 dest = (EMScontext *)get_valid_pointer(R_ES, R_DI,
461 sizeof(EMScontext));
462 if (dest == NULL) {
463 R_AH = EMS_SW_MALFUNC;
464 break;
465 }
466 save_context_to_dos(dest);
467 R_AH = EMS_SUCCESS;
468 break;
469 }
470 case SET:
471 {
472 debug(D_EMS, "set\n");
473 src = (EMScontext *)get_valid_pointer(R_DS, R_SI,
474 sizeof(EMScontext));
475 if (src == NULL) {
476 R_AH = EMS_SW_MALFUNC;
477 break;
478 }
479 if (check_saved_context(src) == 0) {
480 R_AH = EMS_SAVED_CONTEXT_BAD;
481 break;
482 }
483 restore_context(&src->ems_saved_context);
484 R_AH = EMS_SUCCESS;
485 break;
486 }
487 case GET_SET:
488 {
489 debug(D_EMS, "get/set\n");
490 dest = (EMScontext *)get_valid_pointer(R_ES, R_DI,
491 sizeof(EMScontext));
492 if (dest == NULL) {
493 R_AH = EMS_SW_MALFUNC;
494 break;
495 }
496 save_context_to_dos(dest);
497 src = (EMScontext *)get_valid_pointer(R_DS, R_SI,
498 sizeof(EMScontext));
499 if (src == NULL) {
500 R_AH = EMS_SW_MALFUNC;
501 break;
502 }
503 if (check_saved_context(src) == 0) {
504 R_AH = EMS_SAVED_CONTEXT_BAD;
505 break;
506 }
507 restore_context(&src->ems_saved_context);
508 R_AH = EMS_SUCCESS;
509 break;
510 }
511 case GET_SIZE:
512 debug(D_EMS, "get size\n");
513 R_AL = (sizeof(EMScontext) + 1) & 0xfe;
514 R_AH = EMS_SUCCESS;
515 break;
516 default:
517 debug(D_EMS, "invalid subfunction\n");
518 R_AH = EMS_INVALID_SUB;
519 break;
520 }
521 break;
522 }
523
524 case MAP_UNMAP_MULTI_HANDLE:
525 {
526 u_char position;
527 u_short hpagenum, spagenum;
528 short handle;
529 EMSmapunmap *mp;
530 int n_entry, i;
531
532
533 debug(D_EMS, "EMS: Map/Unmap multiple ");
534
535 if ((n_entry = R_CX) > 3) {
536 R_AH = EMS_ILL_PHYS;
537 }
538
539 /* This is valid according to the LIM EMS 4.0 spec */
540 if (n_entry == 0) {
541 R_AH = EMS_SUCCESS;
542 break;
543 }
544
545 handle = R_DX;
546 if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
547 R_AH = EMS_INV_HANDLE;
548 break;
549 }
550
551 mp = (EMSmapunmap *)get_valid_pointer(R_DS, R_SI,
552 sizeof(EMSmapunmap) * n_entry);
553 if (mp == NULL) {
554 R_AH = EMS_SW_MALFUNC;
555 break;
556 }
557
558 R_AH = EMS_SUCCESS;
559 /* Walk through the table and map/unmap */
560 for (i = 0; i < n_entry; i++) {
561 hpagenum = mp->log;
562 /* Method is in R_AL */
563 if (R_AL == 0) {
564 debug(D_EMS, "phys page method\n");
565 if (mp->phys <= 3) {
566 position = mp->phys;
567 } else {
568 R_AH = EMS_ILL_PHYS;
569 break;
570 }
571 } else if (R_AL == 1) {
572 /* Compute position from segment address */
573 u_short p_seg;
574
575 debug(D_EMS, "segment method\n");
576 p_seg = mp->phys;
577 p_seg -= ems_frame_addr;
578 p_seg /= EMS_PAGESIZE;
579 if (p_seg <= 3) {
580 position = p_seg;
581 } else {
582 R_AH = EMS_ILL_PHYS;
583 break;
584 }
585 } else {
586 debug(D_EMS, "invalid subfunction\n");
587 R_AH = EMS_INVALID_SUB;
588 break;
589 }
590
591 mp++;
592 if (hpagenum == 0xffff) {
593 /* Unmap only */
594 map_page(0, position, handle, 1);
595 continue;
596 }
597 if (hpagenum >= ems_handle[handle]->npages) {
598 R_AH = EMS_LOGPAGE_TOOBIG;
599 break;
600 }
601 spagenum = ems_handle[handle]->pagenum[hpagenum];
602 map_page(spagenum, position, handle, 0);
603 }
604 break;
605 }
606
607 case REALLOC_PAGES:
608 {
609 short handle;
610 u_long newpages;
611
612 debug(D_EMS, "EMS: Realloc pages ");
613
614 handle = R_DX;
615 if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
616 R_AH = EMS_INV_HANDLE;
617 debug(D_EMS, "invalid handle\n");
618 break;
619 }
620 newpages = R_BX;
621 debug(D_EMS, "changed from %ld to %ld pages\n",
622 ems_handle[handle]->npages, newpages);
623
624 /* Case 1: Realloc to zero pages */
625 if (newpages == 0) {
626 free_pages_of_handle(handle);
627 R_AH = EMS_SUCCESS;
628 break;
629 }
630 /* Case 2: New allocation is equal to allocated number */
631 if (newpages == ems_handle[handle]->npages) {
632 R_AH = EMS_SUCCESS;
633 break;
634 }
635 /* Case 3: Reallocate to bigger and smaller sizes */
636 if (newpages > ems_handle[handle]->npages) {
637 if (newpages > ems_free_pages) {
638 R_AH = EMS_OUT_OF_LOG;
639 break;
640 }
641 if (newpages > ems_total_pages) {
642 R_AH = EMS_OUT_OF_PHYS;
643 break;
644 }
645 }
646 reallocate_pages_to_handle(handle, newpages);
647 R_AH = EMS_SUCCESS;
648 break;
649 }
650
651 /* We do not support nonvolatile pages */
652 case HANDLE_ATTRIBUTES:
653 debug(D_EMS, "Handle attributes called\n");
654 switch (R_AL) {
655 case GET:
656 case SET:
657 R_AH = EMS_FEAT_NOSUP;
658 break;
659 case HANDLE_CAPABILITY:
660 R_AL = 0; /* Volatile only */
661 R_AH = EMS_SUCCESS;
662 break;
663 default:
664 R_AH = EMS_FUNC_NOSUP;
665 break;
666 }
667 break;
668
669 case HANDLE_NAME:
670 {
671 short handle;
672 Hname *hp;
673
674 handle = R_DX;
675 if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
676 R_AH = EMS_INV_HANDLE;
677 debug(D_EMS, "invalid handle\n");
678 break;
679 }
680 switch (R_AL) {
681 case GET:
682 if ((hp = (Hname *)get_valid_pointer(R_ES, R_DI, 8))
683 == NULL) {
684 R_AH = EMS_SW_MALFUNC;
685 break;
686 }
687 *hp = ems_handle[handle]->hname;
688 R_AH = EMS_SUCCESS;
689 break;
690
691 case SET:
692 if ((hp = (Hname *)get_valid_pointer(R_DS, R_SI, 8))
693 == NULL) {
694 R_AH = EMS_SW_MALFUNC;
695 break;
696 }
697 /* If the handle name is not 0, it may not exist */
698 if ((hp->ul_hn[0] | hp->ul_hn[1]) != 0) {
699 if (lookup_handle(hp) == 0) {
700 ems_handle[handle]->hname = *hp;
701 R_AH = EMS_SUCCESS;
702 } else {
703 R_AH = EMS_NAME_EXISTS;
704 break;
705 }
706 } else {
707 /* Name is deleted (set to zeros) */
708 ems_handle[handle]->hname = *hp;
709 R_AH = EMS_SUCCESS;
710 }
711 break;
712
713 default:
714 R_AH = EMS_FUNC_NOSUP;
715 break;
716 }
717 break;
718 }
719
720 case HANDLE_DIRECTORY:
721 {
722 int i;
723 EMShandledir *hdp;
724 Hname *hp;
725 short handle;
726
727 switch(R_AL) {
728 case GET:
729 hdp = (EMShandledir *)get_valid_pointer(R_ES, R_DI,
730 sizeof(EMShandledir) * ems_alloc_handles);
731 if (hdp == NULL) {
732 R_AH = EMS_SW_MALFUNC;
733 break;
734 }
735 for (i = 0; i < EMS_NUM_HANDLES; i++) {
736 if (ems_handle[i] != NULL) {
737 hdp->log = i;
738 hdp->name = ems_handle[i]->hname;
739 }
740 }
741 R_AH = EMS_SUCCESS;
742 break;
743
744 case HANDLE_SEARCH:
745 hp = (Hname *)get_valid_pointer(R_DS, R_SI, 8);
746 if (hp == NULL) {
747 R_AH = EMS_SW_MALFUNC;
748 break;
749 }
750 /* Cannot search for NULL handle name */
751 if ((hp->ul_hn[0] | hp->ul_hn[1]) != 0) {
752 R_AH = EMS_NAME_EXISTS;
753 break;
754 }
755 if ((handle = lookup_handle(hp)) == 0) {
756 R_AH = EMS_HNAME_NOT_FOUND;
757 } else {
758 R_DX = handle;
759 R_AH = EMS_SUCCESS;
760 }
761 break;
762
763 case GET_TOTAL_HANDLES:
764 R_AH = EMS_SUCCESS;
765 R_BX = EMS_NUM_HANDLES; /* Includes OS handle */
766 break;
767
768 default:
769 R_AH = EMS_FUNC_NOSUP;
770 break;
771 }
772 break;
773 }
774
775
776 /* I do not know if we need this. LINUX emulation leaves it out
777 * so I leave it out too for now.
778 */
779 case ALTER_PAGEMAP_JUMP:
780 debug(D_ALWAYS, "Alter pagemap and jump used!\n");
781 R_AH = EMS_FUNC_NOSUP;
782 break;
783 case ALTER_PAGEMAP_CALL:
784 debug(D_ALWAYS, "Alter pagemap and call used!\n");
785 R_AH = EMS_FUNC_NOSUP;
786 break;
787
788
789 case MOVE_MEMORY_REGION:
790 {
791 EMSmovemem *emvp;
792 u_long src_addr = 0, dst_addr = 0;
793 u_short src_handle = 0, dst_handle = 0;
794
795 if (R_AL == EXCHANGE)
796 debug(D_EMS, "EMS: Exchange memory region ");
797 else
798 debug(D_EMS, "EMS: Move memory region ");
799
800 emvp = (EMSmovemem *)get_valid_pointer(R_DS, R_SI,
801 sizeof(EMSmovemem));
802 if (emvp == NULL) {
803 debug(D_EMS, "Invalid structure pointer\n");
804 R_AH = EMS_SW_MALFUNC;
805 break;
806 }
807 /* Zero length is not an error */
808 if (emvp->length == 0) {
809 debug(D_EMS, "Zero length\n");
810 R_AH = EMS_SUCCESS;
811 break;
812 }
813 /* Some checks */
814 if (emvp->src_type == EMS_MOVE_CONV) {
815 /* Conventional memory source */
816 src_addr = MAKEPTR(emvp->src_seg, emvp->src_offset);
817 /* May not exceed conventional memory */
818 if ((src_addr + emvp->length) > 640 * 1024) {
819 R_AH = EMS_SW_MALFUNC;
820 break;
821 }
822 } else {
823 /* Check the handle */
824 src_handle = emvp->src_handle;
825 if (src_handle > 255 || src_handle == 0 ||
826 ems_handle[src_handle] == NULL) {
827 R_AH = EMS_INV_HANDLE;
828 debug(D_EMS, "invalid source handle\n");
829 break;
830 }
831 /* Offset may not exceed page size */
832 if (emvp->src_offset >= (16 * 1024)) {
833 R_AH = EMS_PAGEOFFSET;
834 debug(D_EMS, "source page offset too big\n");
835 break;
836 }
837 }
838
839 if (emvp->dst_type == EMS_MOVE_CONV) {
840 /* Conventional memory source */
841 dst_addr = MAKEPTR(emvp->dst_seg, emvp->dst_offset);
842 /* May not exceed conventional memory */
843 if ((dst_addr + emvp->length) > 640 * 1024) {
844 R_AH = EMS_SW_MALFUNC;
845 break;
846 }
847 } else {
848 /* Check the handle */
849 dst_handle = emvp->dst_handle;
850 if (dst_handle > 255 || dst_handle == 0 ||
851 ems_handle[dst_handle] == NULL) {
852 R_AH = EMS_INV_HANDLE;
853 debug(D_EMS, "invalid destination handle\n");
854 break;
855 }
856 /* Offset may not exceed page size */
857 if (emvp->dst_offset >= (16 * 1024)) {
858 R_AH = EMS_PAGEOFFSET;
859 debug(D_EMS, "destination page offset too big\n");
860 break;
861 }
862 }
863
864 if (R_AL == MOVE) {
865 /* If it is conventional memory only, do it */
866 if (emvp->src_type == EMS_MOVE_CONV &&
867 emvp->dst_type == EMS_MOVE_CONV) {
868 memmove((void *)dst_addr, (void *)src_addr,
869 (size_t) emvp->length);
870 debug(D_EMS, "conventional to conventional memory done\n");
871 R_AH = EMS_SUCCESS;
872 break;
873 }
874 if (emvp->src_type == EMS_MOVE_EMS &&
875 emvp->dst_type == EMS_MOVE_CONV)
876 R_AH = move_ems_to_conv(src_handle, emvp->src_seg,
877 emvp->src_offset, dst_addr, emvp->length);
878 else if (emvp->src_type == EMS_MOVE_CONV &&
879 emvp->dst_type == EMS_MOVE_EMS)
880 R_AH = move_conv_to_ems(src_addr, dst_handle,
881 emvp->dst_seg, emvp->dst_offset, emvp->length);
882 else
883 R_AH = move_ems_to_ems(src_handle, emvp->src_seg,
884 emvp->src_offset, dst_handle, emvp->dst_seg,
885 emvp->dst_offset, emvp->length);
886 debug(D_EMS, " done\n");
887 break;
888 } else {
889 /* exchange memory region */
890
891 /* We need a scratch area for the exchange */
892 void *buffer;
893 if ((buffer = malloc(emvp->length)) == NULL)
894 fatal("EMS: Could not malloc scratch area for exchange");
895
896 /* If it is conventional memory only, do it */
897 if (emvp->src_type == EMS_MOVE_CONV &&
898 emvp->dst_type == EMS_MOVE_CONV) {
899 /* destination -> buffer */
900 memmove(buffer, (void *)dst_addr, (size_t) emvp->length);
901 /* Source -> destination */
902 memmove((void *)dst_addr, (void *)src_addr,
903 (size_t) emvp->length);
904 /* Buffer -> source */
905 memmove((void *)src_addr, buffer, (size_t) emvp->length);
906 free(buffer);
907 debug(D_EMS, "conventional to conventional memory done\n");
908 R_AH = EMS_SUCCESS;
909 break;
910 }
911
912 /* Exchange EMS with conventional */
913 if (emvp->src_type == EMS_MOVE_EMS &&
914 emvp->dst_type == EMS_MOVE_CONV) {
915 /* Destination -> buffer */
916 memmove(buffer, (void *)dst_addr, (size_t) emvp->length);
917 /* Source -> destination */
918 R_AH = move_ems_to_conv(src_handle, emvp->src_seg,
919 emvp->src_offset, dst_addr, emvp->length);
920 if (R_AH != EMS_SUCCESS) {
921 free(buffer);
922 break;
923 }
924 /* Buffer -> source */
925 R_AH = move_conv_to_ems((u_long)buffer, src_handle,
926 emvp->src_seg, emvp->src_offset, emvp->length);
927
928 /* Exchange conventional with EMS */
929 } else if (emvp->src_type == EMS_MOVE_CONV &&
930 emvp->dst_type == EMS_MOVE_EMS) {
931 /* Destination -> buffer */
932 R_AH = move_ems_to_conv(dst_handle, emvp->dst_seg,
933 emvp->dst_offset, (u_long)buffer, emvp->length);
934 if (R_AH != EMS_SUCCESS) {
935 free(buffer);
936 break;
937 }
938 /* Source -> destination */
939 R_AH = move_conv_to_ems((u_long)buffer, dst_handle,
940 emvp->dst_seg, emvp->dst_offset, emvp->length);
941 /* Buffer -> source */
942 memmove(buffer, (void *)src_addr, (size_t) emvp->length);
943
944 /* Exchange EMS with EMS */
945 } else {
946 /* Destination -> buffer */
947 R_AH = move_ems_to_conv(dst_handle, emvp->dst_seg,
948 emvp->dst_offset, (u_long)buffer, emvp->length);
949 if (R_AH != EMS_SUCCESS) {
950 free(buffer);
951 break;
952 }
953 /* Source -> destination */
954 R_AH = move_ems_to_ems(src_handle, emvp->src_seg,
955 emvp->src_offset, dst_handle, emvp->dst_seg,
956 emvp->dst_offset, emvp->length);
957 if (R_AH != EMS_SUCCESS) {
958 free(buffer);
959 break;
960 }
961 /* Buffer -> source */
962 R_AH = move_conv_to_ems((u_long)buffer, src_handle,
963 emvp->src_seg, emvp->src_offset, emvp->length);
964 }
965 free(buffer);
966 }
967 debug(D_EMS, " done\n");
968 break;
969 }
970
971 case GET_MAPPABLE_PHYS_ADDR:
972 {
973 switch (R_AL) {
974 case GET_ARRAY:
975 {
976 EMSaddrarray *eadp;
977 int i;
978 u_short seg;
979
980 eadp = (EMSaddrarray *)get_valid_pointer(R_ES, R_DI,
981 sizeof(EMSaddrarray) * 4);
982 if (eadp == NULL) {
983 R_AH = EMS_SW_MALFUNC;
984 break;
985 }
986 for (i = 0, seg = (ems_frame_addr >> 4); i < 4; i++) {
987 eadp->segm = seg;
988 eadp->phys = i;
989 eadp++;
990 seg += 1024;
991 }
992 R_AH = EMS_SUCCESS;
993 break;
994 }
995 case GET_ARRAY_ENTRIES:
996 /* There are always 4 positions, 4*16kB = 64kB */
997 R_CX = 4;
998 R_AH = EMS_SUCCESS;
999 break;
1000 default:
1001 R_AH = EMS_FUNC_NOSUP;
1002 break;
1003 }
1004 break;
1005 }
1006
1007 /* This is an OS function in the LIM EMS 4.0 standard: It is
1008 * usable only by an OS and its use can be disabled for all other
1009 * programs. I think we do not need to support it. It is not
1010 * implemented and it reports "disabled" to any caller.
1011 */
1012 case GET_HW_CONFIGURATION:
1013 R_AH = EMS_FUNCTION_DISABLED;
1014 break;
1015
1016 /* This function is a little different, it was defined with
1017 * LIM EMS 4.0: It is allowed to allocate zero pages and raw
1018 * page size (i.e. page size != 16kB) is supported. We have
1019 * only 16kB pages, so the second difference does not matter.
1020 */
1021 case ALLOCATE_PAGES:
1022 {
1023 u_short npages;
1024 short handle;
1025
1026 npages = R_BX;
1027 debug(D_EMS, "EMS: Get handle and allocate %d pages: ", npages);
1028
1029 /* Enough handles? */
1030 if ((handle = find_next_free_handle()) < 0) {
1031 debug(D_EMS,"Return error:No handles\n");
1032 R_AH = EMS_OUT_OF_HANDLES;
1033 break;
1034 }
1035 /* Enough memory for this request ? */
1036 if (npages > ems_free_pages) {
1037 debug(D_EMS,"Return error:Request too big\n");
1038 R_AH = EMS_OUT_OF_LOG;
1039 break;
1040 }
1041 if (npages > ems_total_pages) {
1042 debug(D_EMS,"Return error:Request too big\n");
1043 R_AH = EMS_OUT_OF_PHYS;
1044 break;
1045 }
1046
1047 /* Allocate the handle */
1048 allocate_handle(handle, npages);
1049
1050 /* Allocate the pages */
1051 allocate_pages_to_handle(handle, npages);
1052 R_DX = handle;
1053 R_AH = EMS_SUCCESS;
1054 debug(D_EMS,"Return success:Handle = %d\n", handle);
1055 break;
1056 }
1057
1058 /* This is an OS function in the LIM EMS 4.0 standard: It is
1059 * usable only by an OS and its use can be disabled for all other
1060 * programs. I think we do not need to support it. It is not
1061 * implemented and it reports "disabled" to any caller.
1062 */
1063 case ALTERNATE_MAP_REGISTER:
1064 R_AH = EMS_FUNCTION_DISABLED;
1065 break;
1066
1067 /* We cannot support that ! */
1068 case PREPARE_WARMBOOT:
1069 R_AH = EMS_FUNC_NOSUP;
1070 break;
1071
1072 case OS_FUNCTION_SET:
1073 R_AH = EMS_FUNCTION_DISABLED;
1074 break;
1075
1076 default:
1077 debug(D_ALWAYS, "EMS: Unknown function called: %02x\n", R_AH);
1078 R_AH = EMS_FUNC_NOSUP;
1079 break;
1080 }
1081}
1082
1083/* Initialize the EMS memory: Return 1 on success, 0 on failure */
1084
1085static int
1086init_mapfile(void)
1087{
1088 char path[256];
1089 int mfd;
1090
1091 /* Sanity */
1092 if (ems_max_size == 0)
1093 return 0;
1094 strcpy(path, EMS_MAP_PATH);
1095 strcat(path, EMS_MAP_FILE);
1096
1097 mfd = mkstemp(path);
1098
1099 if (mfd < 0) {
1100 debug(D_ALWAYS, "Could not create EMS mapfile, ");
1101 goto fail;
1102 }
1103 unlink(path);
1104 mapfile_fd = squirrel_fd(mfd);
1105
1106 if (lseek(mapfile_fd, (off_t)(ems_max_size - 1), 0) < 0) {
1107 debug(D_ALWAYS, "Could not seek into EMS mapfile, ");
1108 goto fail;
1109 }
1110 if (write(mapfile_fd, "", 1) < 0) {
1111 debug(D_ALWAYS, "Could not write to EMS mapfile, ");
1112 goto fail;
1113 }
1114 /* Unmap the entire page frame */
1115 if (munmap((caddr_t)ems_frame_addr, 64 * 1024) < 0) {
1116 debug(D_ALWAYS, "Could not unmap EMS page frame, ");
1117 goto fail;
1118 }
1119 /* DOS programs will access the page frame without allocating
1120 * pages first. Microsoft diagnose MSD.EXE does this, for example
1121 * We need to have memory here to avoid segmentation violation
1122 */
1123 if (mmap((caddr_t)ems_frame_addr, 64 * 1024,
1124 PROT_EXEC | PROT_READ | PROT_WRITE,
1125 MAP_ANON | MAP_FIXED | MAP_SHARED,
1126 -1, 0) == MAP_FAILED) {
1127 debug(D_ALWAYS, "Could not map EMS page frame, ");
1128 goto fail;
1129 }
1130 bzero((void *)&ems_mapping_context, sizeof(EMS_mapping_context));
1131 return (1);
1132
1133fail:
1134 debug(D_ALWAYS, "EMS disabled\n");
1135 ems_max_size = 0;
1136 ems_frame_addr = 0;
1137 return (0);
1138}
1139
1140/* Map/Unmap pages into one of four positions in the frame segment */
1141
1142static void
1143map_page(u_long pagenum, u_char position, short handle, int unmaponly)
1144{
1145 caddr_t map_addr;
1146 size_t len;
1147 off_t file_offs;
1148
1149 if (position > 3)
1150 fatal("EMS: Internal error: Mapping position\n");
1151
1152 map_addr = (caddr_t)(ems_frame_addr + (1024 * 16 * (u_long)position));
1153 len = 1024 * 16;
1154 file_offs = (off_t)(pagenum * 16 * 1024);
1155
1156 if (ems_mapping_context.pos_mapped[position]) {
1157 if (munmap(map_addr, len) < 0) {
1158 fatal("EMS unmapping error: %s\nCannot recover\n",
1159 strerror(errno));
1160 }
1161 ems_page[ems_mapping_context.pos_pagenum[position]].status
1162 &= ~EMS_MAPPED;
1163 ems_mapping_context.pos_mapped[position] = 0;
1164 ems_mapping_context.handle[position] = 0;
1165 }
1166 if (unmaponly) {
1167 /* DOS programs will access the page frame without allocating
1168 * pages first. Microsoft diagnose MSD.EXE does this, for example
1169 * We need to have memory here to avoid segmentation violation
1170 */
1171 if (mmap((caddr_t)ems_frame_addr, 64 * 1024,
1172 PROT_EXEC | PROT_READ | PROT_WRITE,
1173 MAP_ANON | MAP_FIXED | MAP_SHARED,
1174 -1, 0) == MAP_FAILED)
1175 fatal("Could not map EMS page frame during unmap only\n");
1176 return;
1177 }
1178 if (mmap(map_addr, len,
1179 PROT_EXEC | PROT_READ | PROT_WRITE,
1180 MAP_FILE | MAP_FIXED | MAP_SHARED,
1181 mapfile_fd, file_offs) == MAP_FAILED) {
1182 fatal("EMS mapping error: %s\nCannot recover\n", strerror(errno));
1183 }
1184 ems_mapping_context.pos_mapped[position] = 1;
1185 ems_mapping_context.pos_pagenum[position] = pagenum;
1186 ems_mapping_context.handle[position] = handle;
1187 ems_page[pagenum].status |= EMS_MAPPED;
1188}
1189
1190/* Get a pointer from VM86 app, check it and return it. This returns NULL
1191 * if the pointer is not valid. We can check only for very limited
1192 * criteria: The pointer and the area defined by size may not point to
1193 * memory over 1MB and it may not may to addresses under 1kB, because there
1194 * is the VM86 interrupt table.
1195 */
1196static void *
1197get_valid_pointer(u_short seg, u_short offs, u_long size)
1198{
1199 u_long addr;
1200 addr = MAKEPTR(seg, offs);
1201 /* Check bounds */
1202 if ((addr + size) >= (1024 * 1024) || addr < 1024)
1203 return NULL;
1204 else
1205 return (void *)addr;
1206}
1207
1208/* Malloc a new handle */
1209static EMS_handle *
1210get_new_handle(long npages)
1211{
1212 EMS_handle *ehp;
1213 size_t dynsize = sizeof(EMS_handle) + sizeof(short) * npages;
1214
1215 if ((ehp = calloc(1, dynsize)) == NULL)
1216 fatal("Cannot malloc EMS handle, cannot continue\n");
1217 return ehp;
1218}
1219
1220/* Allocate a mapping context to a handle */
1221static void
1222context_to_handle(short handle)
1223{
1224 EMS_mapping_context *emc;
1225
1226 if (ems_handle[handle] == NULL)
1227 fatal("EMS context_to_handle called with invalid handle\n");
1228 if ((emc = calloc(1, sizeof(EMS_mapping_context))) == NULL)
1229 fatal("EMS Cannot malloc mapping context, cannot continue\n");
1230 ems_handle[handle]->mcontext = emc;
1231 memmove((void *)emc, (void *)&ems_mapping_context,
1232 sizeof(EMS_mapping_context));
1233}
1234
1235/* Find the next free handle, returns -1 if there are no more handles */
1236static long
1237find_next_free_handle(void)
1238{
1239 int i;
1240
1241 if (ems_alloc_handles >= 255)
1242 return (-1);
1243 /* handle 0 is OS handle */
1244 for (i = 1; i < EMS_NUM_HANDLES; i++) {
1245 if (ems_handle[i] == NULL)
1246 return (i);
1247 }
1248 fatal("EMS handle count garbled, should not happen\n");
1249 /* quiet 'gcc -Wall' */
1250 return (-1);
1251}
1252
1253/* Look for a named handle, returns 0 if not found, else handle */
1254static short
1255lookup_handle(Hname *hp)
1256{
1257 int i;
1258
1259 for (i = 1; i < EMS_NUM_HANDLES; i++) {
1260 if (ems_handle[i] != NULL) {
1261 if (hp->ul_hn[0] == ems_handle[i]->hname.ul_hn[0] &&
1262 hp->ul_hn[1] == ems_handle[i]->hname.ul_hn[1])
1263 return (i);
1264 }
1265 }
1266 return (0);
1267}
1268
1269/* Malloc a new handle struct and put into array at index handle */
1270static void
1271allocate_handle(short handle, long npages)
1272{
1273 if (ems_handle[handle] != NULL)
1274 fatal("EMS allocate_handle, handle was not free\n");
1275 ems_handle[handle] = get_new_handle(npages);
1276 ems_alloc_handles++;
1277}
1278
1279/* Free a handle, return its memory. Call this *after* freeing the
1280 * allocated pages !
1281 */
1282static void
1283free_handle(short handle)
1284{
1285 if (ems_handle[handle] == NULL)
1286 fatal("EMS free_handle, handle was free\n");
1287 if (ems_handle[handle]->mcontext != NULL)
1288 free((void *)ems_handle[handle]->mcontext);
1289 free((void *)ems_handle[handle]);
1290 ems_handle[handle] = NULL;
1291 ems_alloc_handles--;
1292}
1293
1294
1295/* Allocates npages to handle. Call this routine only after you have
1296 * ensured there are enough free pages *and* the new handle is in place
1297 * in the handle array !
1298 */
1299static void
1300allocate_pages_to_handle(u_short handle, long npages)
1301{
1302 unsigned syspagenum;
1303 int pages_to_alloc = npages;
1304 int allocpagenum = 0;
1305
1306 /* sanity */
1307 if (handle > 255 || ems_handle[handle] == NULL)
1308 fatal("EMS allocate_pages_to_handle called with invalid handle\n");
1309
1310 ems_handle[handle]->npages = npages;
1311 for (syspagenum = 0; syspagenum < ems_total_pages; syspagenum++) {
1312 if (ems_page[syspagenum].status == EMS_FREE) {
1313 ems_page[syspagenum].handle = handle;
1314 ems_page[syspagenum].status = EMS_ALLOCED;
1315 ems_handle[handle]->pagenum[allocpagenum] = syspagenum;
1316 allocpagenum++;
1317 pages_to_alloc--;
1318 if (pages_to_alloc == 0)
1319 break;
1320 }
1321 }
1322 if (pages_to_alloc > 0)
1323 fatal("EMS allocate_pages_to_handle found not enough free pages\n");
1324 ems_alloc_pages += npages;
1325 ems_free_pages -= npages;
1326}
1327
1328/* Reallocates npages to handle. Call this routine only after you have
1329 * ensured there are enough free pages *and* the new handle is in place
1330 * in the handle array !
1331 */
1332static void
1333reallocate_pages_to_handle(u_short handle, long npages)
1334{
1335 unsigned allocpagenum;
1336 unsigned syspagenum;
1337 int pages_to_alloc;
1338 long delta;
1339 size_t dynsize;
1340 EMS_handle *emp;
1341
1342 /* sanity */
1343 if (handle > 255 || ems_handle[handle] == NULL)
1344 fatal("EMS allocate_pages_to_handle called with invalid handle\n");
1345
1346 delta = npages - ems_handle[handle]->npages;
1347 if (delta > 0) {
1348 /* Grow array size and allocation */
1349
1350 emp = ems_handle[handle];
1351 dynsize = sizeof(EMS_handle) + sizeof(short) * npages;
1352
1353 /* First step: Make room in the handle pagenum array */
1354 if ((emp = (EMS_handle *)realloc((void *)emp, dynsize)) == NULL)
1355 fatal("Cannot malloc EMS handle, cannot continue\n");
1356 ems_handle[handle] = emp;
1357
1358 /* Second step: Add pages to the handle */
1359 pages_to_alloc = delta;
1360 allocpagenum = ems_handle[handle]->npages;
1361 ems_handle[handle]->npages = npages;
1362 for (syspagenum = 0; syspagenum < ems_total_pages; syspagenum++) {
1363 if (ems_page[syspagenum].status == EMS_FREE) {
1364 ems_page[syspagenum].handle = handle;
1365 ems_page[syspagenum].status = EMS_ALLOCED;
1366 ems_handle[handle]->pagenum[allocpagenum] = syspagenum;
1367 allocpagenum++;
1368 pages_to_alloc--;
1369 if (pages_to_alloc == 0)
1370 break;
1371 }
1372 }
1373 if (pages_to_alloc > 0)
1374 fatal("EMS allocate_pages_to_handle found not enough free pages\n");
1375
1376 } else {
1377 /* Shrink array size and allocation */
1378
1379 /* First step: Deallocate all pages from new size to old size */
1380 for (allocpagenum = npages;
1381 allocpagenum < ems_handle[handle]->npages;
1382 allocpagenum++) {
1383 syspagenum = ems_handle[handle]->pagenum[allocpagenum];
1384
1385 /* sanity */
1386 if (syspagenum > ems_total_pages)
1387 fatal("EMS free_pages_of_handle found invalid page number\n");
1388 if (!(ems_page[syspagenum].status & EMS_ALLOCED))
1389 fatal("EMS free_pages_of_handle tried to free page already free\n");
1390 ems_page[syspagenum].handle = 0;
1391 ems_page[syspagenum].status = EMS_FREE;
1392 }
1393
1394 /* Second step: Shrink the dynamic array of the handle */
1395 dynsize = sizeof(EMS_handle) + sizeof(short) * npages;
1396 emp = ems_handle[handle];
1397 if ((emp = (EMS_handle *)realloc((void *)emp, dynsize)) == NULL)
1398 fatal("Cannot realloc EMS handle, cannot continue\n");
1399 ems_handle[handle] = emp;
1400 ems_handle[handle]->npages = npages;
1401 }
1402 ems_alloc_pages += delta;
1403 ems_free_pages -= delta;
1404}
1405
1406/* Free all pages belonging to a handle, handle must be valid */
1407static void
1408free_pages_of_handle(short handle)
1409{
1410 int allocpagenum;
1411 unsigned syspagenum;
1412 int npages;
1413
1414 /* sanity */
1415
1416 if (handle > 255 || ems_handle[handle] == NULL)
1417 fatal("EMS free_pages_of_handle called with invalid handle\n");
1418
1419 if ((npages = ems_handle[handle]->npages) == 0)
1420 return;
1421
1422 for (allocpagenum = 0; allocpagenum < npages; allocpagenum++) {
1423 syspagenum = ems_handle[handle]->pagenum[allocpagenum];
1424 /* sanity */
1425 if (syspagenum > ems_total_pages)
1426 fatal("EMS free_pages_of_handle found invalid page number\n");
1427 if (!(ems_page[syspagenum].status & EMS_ALLOCED))
1428 fatal("EMS free_pages_of_handle tried to free page already free\n");
1429 ems_page[syspagenum].handle = 0;
1430 ems_page[syspagenum].status = EMS_FREE;
1431 }
1432 ems_alloc_pages -= npages;
1433 ems_free_pages += npages;
1434}
1435
1436/* Restore a saved mapping context, overwrites current mapping context */
1437static void
1438restore_context(EMS_mapping_context *emc)
1439{
1440 int i;
1441
1442 for (i = 0; i < 4; i++) {
1443 ems_mapping_context.handle[i] = emc->handle[i];
1444 if (emc->pos_mapped[i] != 0 &&
1445 ems_mapping_context.pos_pagenum[i] != emc->pos_pagenum[i]) {
1446 map_page(emc->pos_pagenum[i], (u_char) i, emc->handle[i], 0);
1447 } else {
1448 ems_mapping_context.pos_mapped[i] = 0;
1449 }
1450 }
1451}
1452
1453/* Prepare a special context save block for DOS and save it to
1454 * VM86 memory
1455 */
1456static void
1457save_context_to_dos(EMScontext *emp)
1458{
1459 int i, end;
1460 EMScontext context;
1461 u_short *sp;
1462 u_short sum;
1463
1464 context.ems_saved_context = ems_mapping_context;
1465 context.magic = EMS_SAVEMAGIC;
1466 context.checksum = 0;
1467 sp = (u_short *)&context;
1468 end = sizeof(EMScontext) / sizeof(short);
1469 /* Generate checksum */
1470 for (i = 0, sum = 0; i < end; i++) {
1471 sum += *sp++;
1472 sum &= 0xffff;
1473 }
1474 context.checksum = 0x10000L - sum;
1475 /* Save it to VM86 memory */
1476 *emp = context;
1477}
1478
1479/* Check a context returned from VM86 app for validity, return 0, if
1480 * not valid, else return 1
1481 */
1482static int
1483check_saved_context(EMScontext *emp)
1484{
1485 int i, end;
1486 u_short *sp;
1487 u_short sum;
1488
1489 if (emp->magic != EMS_SAVEMAGIC)
1490 return 0;
1491
1492 sp = (u_short *)emp;
1493 end = sizeof(EMScontext) / sizeof(short);
1494 /* Generate checksum */
1495 for (i = 0, sum = 0; i < end; i++) {
1496 sum += *sp++;
1497 sum &= 0xffff;
1498 }
1499 if (sum != 0)
1500 return 0;
1501 else
1502 return 1;
1503}
1504
1505/* Helper routine for the move routines below: Check if length bytes
1506 * can be moved from/to handle pages (i.e are there enough pages)
1507 */
1508static int
1509check_alloc_pages(u_short handle, u_short firstpage, u_short offset,
1510 u_long length __unused)
1511{
1512 u_long nbytes;
1513
1514 if (firstpage > ems_handle[handle]->npages)
1515 return (0);
1516 nbytes = (ems_handle[handle]->npages - firstpage) * EMS_PAGESIZE - offset;
1517 return (ems_handle[handle]->npages >= nbytes);
1518}
1519
1520/* Copy a block of memory up to the next 16kB boundary in the source
1521 * to the destination in upward direction (i.e. with ascending addresses)
1522 * XXX Could be an inline function.
1523 */
1524static void
1525copy_block_up(struct copydesc *cdp)
1526{
1527 size_t size;
1528 void *srcp;
1529 void *dstp;
1530
1531 /* If source or both memory types are EMS, source determines the
1532 * block lenght, else destination determines the block lenght
1533 */
1534 if (cdp->copytype & SRC_EMS)
1535 size = EMS_PAGESIZE - cdp->EMS_OFFS(src_addr);
1536 else
1537 size = EMS_PAGESIZE - cdp->EMS_OFFS(dst_addr);
1538
1539 if (size > cdp->rest_len)
1540 size = cdp->rest_len;
1541
1542 /* If src is EMS memory, it is mapped into position 0 */
1543 if (cdp->copytype & SRC_EMS)
1544 srcp = (void *)(ems_frame_addr + cdp->EMS_OFFS(src_addr));
1545 else
1546 srcp = (void *)(cdp->EMS_PTR(src_addr));
1547
1548 /* If dest is EMS memory, it is mapped into position 1,2 */
1549 if (cdp->copytype & DST_EMS)
1550 dstp = (void *)(ems_frame_addr + EMS_PAGESIZE +
1551 cdp->EMS_OFFS(dst_addr));
1552 else
1553 dstp = (void *)(cdp->EMS_PTR(dst_addr));
1554
1555 /* Move this block */
1556 memmove(dstp, srcp, size);
1557
1558 /* Update the copy descriptor: This updates the address of both
1559 * conventional and EMS memory
1560 */
1561 cdp->EMS_PTR(src_addr) += size;
1562 cdp->EMS_PTR(dst_addr) += size;
1563
1564 cdp->rest_len -= size;
1565}
1566
1567
1568/* Move EMS memory starting with handle page src_seg and offset src_offset
1569 * to conventional memory dst_addr for length bytes
1570 * dst_addr is checked, handle is valid
1571 */
1572static u_long
1573move_ems_to_conv(short src_handle, u_short src_seg,
1574 u_short src_offset, u_long dst_addr, u_long length)
1575{
1576 EMS_mapping_context ems_saved_context;
1577 EMS_handle *ehp;
1578 int pageindx = src_seg;
1579 struct copydesc cd;
1580
1581 if (check_alloc_pages(src_handle, src_seg, src_offset, length) == 0)
1582 return EMS_MOVE_OVERFLOW;
1583
1584 ehp = ems_handle[src_handle];
1585
1586 /* Prepare the move: Save the mapping context */
1587 ems_saved_context = ems_mapping_context;
1588
1589 /* Setup the copy descriptor struct */
1590
1591 cd.copytype = SRC_EMS;
1592 cd.EMS_PAGE(src_addr) = ehp->pagenum[pageindx];
1593 cd.EMS_OFFS(src_addr) = src_offset;
1594 cd.EMS_PTR(dst_addr) = dst_addr;
1595 cd.rest_len = length;
1596
1597 do {
1598 /* Map for the first block copy, source is mapped to position zero */
1599 map_page(cd.EMS_PAGE(src_addr), 0, src_handle, 0);
1600 copy_block_up(&cd);
1601 } while(cd.rest_len > 0);
1602
1603 /* Restore the original mapping */
1604 restore_context(&ems_saved_context);
1605 return EMS_SUCCESS;
1606}
1607
1608/* Move conventional memory starting with src_addr
1609 * to EMS memory starting with handle page src_seg and offset src_offset
1610 * for length bytes
1611 * dst_addr is checked, handle is valid
1612 */
1613static u_long
1614move_conv_to_ems(u_long src_addr, u_short dst_handle, u_short dst_seg,
1615 u_short dst_offset, u_long length)
1616{
1617 EMS_mapping_context ems_saved_context;
1618 EMS_handle *ehp;
1619 int pageindx = dst_seg;
1620 struct copydesc cd;
1621
1622 if (check_alloc_pages(dst_handle, dst_seg, dst_offset, length) == 0)
1623 return EMS_MOVE_OVERFLOW;
1624
1625 ehp = ems_handle[dst_handle];
1626
1627 /* Prepare the move: Save the mapping context */
1628 ems_saved_context = ems_mapping_context;
1629
1630 /* Setup the copy descriptor struct */
1631
1632 cd.copytype = DST_EMS;
1633 cd.EMS_PAGE(dst_addr) = ehp->pagenum[pageindx];
1634 cd.EMS_OFFS(dst_addr) = dst_offset;
1635 cd.EMS_PTR(src_addr) = src_addr;
1636 cd.rest_len = length;
1637
1638 do {
1639 map_page(cd.EMS_PAGE(dst_addr), 1, dst_handle, 0);
1640 copy_block_up(&cd);
1641 } while(cd.rest_len > 0);
1642
1643 /* Restore the original mapping */
1644 restore_context(&ems_saved_context);
1645 return EMS_SUCCESS;
1646}
1647
1648static u_long
1649move_ems_to_ems(u_short src_handle, u_short src_seg, u_short src_offset,
1650 u_short dst_handle, u_short dst_seg, u_short dst_offset,
1651 u_long length)
1652{
1653 EMS_mapping_context ems_saved_context;
1654 EMS_handle *src_hp, *dst_hp;
1655 struct copydesc cd;
1656
1657 if (check_alloc_pages(src_handle, src_seg, src_offset, length) == 0)
1658 return EMS_MOVE_OVERFLOW;
1659 if (check_alloc_pages(dst_handle, dst_seg, dst_offset, length) == 0)
1660 return EMS_MOVE_OVERFLOW;
1661
1662 src_hp = ems_handle[src_handle];
1663 dst_hp = ems_handle[dst_handle];
1664
1665 /* Prepare the move: Save the mapping context */
1666 ems_saved_context = ems_mapping_context;
1667
1668 /* Setup the copy descriptor struct */
1669
1670 cd.copytype = SRC_EMS | DST_EMS;
1671 cd.EMS_PAGE(src_addr) = src_hp->pagenum[src_seg];
1672 cd.EMS_OFFS(src_addr) = src_offset;
1673 cd.EMS_PAGE(dst_addr) = dst_hp->pagenum[dst_seg];
1674 cd.EMS_OFFS(dst_addr) = dst_offset;
1675 cd.rest_len = length;
1676
1677 /* Copy */
1678 do {
1679 map_page(cd.EMS_PAGE(src_addr), 0, src_handle, 0);
1680 map_page(cd.EMS_PAGE(dst_addr), 1, dst_handle, 0);
1681 /* If there are more pages, map the next destination page to
1682 * position 2. This removes a compare between source and dest
1683 * offsets.
1684 */
1685 if (cd.EMS_PAGE(dst_addr) < dst_hp->npages)
1686 map_page((cd.EMS_PAGE(dst_addr) + 1), 2, dst_handle, 0);
1687 copy_block_up(&cd);
1688 } while(cd.rest_len > 0);
1689
1690 /* Restore the original mapping */
1691 restore_context(&ems_saved_context);
1692 return EMS_SUCCESS;
1693}