54acbe410bf02e36d6441fadc39ac0ef2a03aefa
[dragonfly.git] / usr.bin / doscmd / ems.c
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.2 2003/06/17 04:29:26 dillon 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 */
68 u_long ems_max_size   = EMS_MAXSIZE * 1024;
69 u_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"
78 static 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
85 EMS_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 */
90 static EMS_handle *ems_handle[EMS_NUM_HANDLES];
91 static u_long ems_alloc_handles;
92 /* The active handle, if any */
93 static short active_handle;
94
95 /* The page array. It is malloced at runtime, depending on the total
96  * allocation size
97  */
98
99 static EMS_page *ems_page = NULL;
100 static u_long ems_total_pages;
101 static u_long ems_alloc_pages;
102 static u_long ems_free_pages;
103
104 /* Local structure used for region copy and move operations */
105
106 struct 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 */
117 static int      init_mapfile(void);
118 static void     map_page(u_long, u_char, short, int);
119 static EMS_handle       *get_new_handle(long);
120 static void     context_to_handle(short);
121 static long     find_next_free_handle(void);
122 static short    lookup_handle(Hname *hp);
123 static void     allocate_pages_to_handle(u_short, long);
124 static void     allocate_handle(short, long);
125 static void     reallocate_pages_to_handle(u_short, long);
126 static void     free_handle(short);
127 static void     free_pages_of_handle(short);
128 static void     restore_context(EMS_mapping_context *);
129 static void     save_context_to_dos(EMScontext *);
130 static int      check_saved_context(EMScontext *);
131 static void     *get_valid_pointer(u_short, u_short, u_long);
132 static u_long   move_ems_to_conv(short, u_short, u_short, u_long, u_long);
133 static u_long   move_conv_to_ems(u_long, u_short, u_short, u_short, u_long);
134 static 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
142 int
143 ems_init()
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
178 void
179 ems_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, dst_addr;
793             u_short src_handle, dst_handle;
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
1085 static int
1086 init_mapfile()
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
1133 fail:
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
1142 static void
1143 map_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  */
1196 static void 
1197 *get_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 */
1209 static EMS_handle
1210 *get_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 */
1221 static void
1222 context_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 */
1236 static long
1237 find_next_free_handle()
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 */
1254 static short
1255 lookup_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 */
1270 static void
1271 allocate_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  */
1282 static void
1283 free_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  */
1299 static void
1300 allocate_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  */
1332 static void
1333 reallocate_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 */
1407 static void
1408 free_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 */
1437 static void
1438 restore_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  */
1456 static void
1457 save_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  */
1482 static int
1483 check_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  */
1508 static int
1509 check_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  */
1524 static void 
1525 copy_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  */
1572 static u_long 
1573 move_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  */
1613 static u_long
1614 move_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     
1648 static u_long
1649 move_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 }