/****************************************************************/ /* */ /* initHMA.c */ /* DOS-C */ /* */ /* move kernel to HMA area */ /* */ /* Copyright (c) 2001 */ /* tom ehlert */ /* All Rights Reserved */ /* */ /* This file is part of DOS-C. */ /* */ /* DOS-C is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version */ /* 2, or (at your option) any later version. */ /* */ /* DOS-C is distributed in the hope that it will be useful, but */ /* WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ /* the GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public */ /* License along with DOS-C; see the file COPYING. If not, */ /* write to the Free Software Foundation, 675 Mass Ave, */ /* Cambridge, MA 02139, USA. */ /****************************************************************/ /* current status: load FreeDOS high, if DOS=HIGH detected suppress High Loading, if any SHIFT status detected (for debugging) if no XMS driver (HIMEM,FDXMS,...) loaded, should work cooperation with XMS drivers as follows: copy HMA_TEXT segment up. after each loaded DEVICE=SOMETHING.SYS, try to request the HMA (XMS function 0x01). if no XMS driver detected, during ONFIG.SYS processing, create a dummy VDISK entry in high memory this works with FD FDXMS - no problems detected MS HIMEM.SYS (from DOS 6.2, 9-30-93) works if and only if /TESTMEM:OFF is given otherwise HIMEM will TEST AND ZERO THE HIGH MEMORY+HMA. so, in CONFIG.C, if "HIMEM.SYS" is detected, a "/TESTMEM:OFF" parameter is forced. */ #include "portab.h" #include "init-mod.h" #include "init-dat.h" extern BYTE FAR ASM _HMATextAvailable, /* first byte of available CODE area */ FAR ASM _HMATextStart[], /* first byte of HMAable CODE area */ FAR ASM _HMATextEnd[]; /* and the last byte of it */ #ifdef VERSION_STRINGS static BYTE *RcsId = "$Id$"; #endif BYTE DosLoadedInHMA = FALSE; /* set to TRUE if loaded HIGH */ BYTE HMAclaimed = FALSE; /* set to TRUE if claimed from HIMEM */ WORD HMAFree = 0; /* first byte in HMA not yet used */ extern void FAR *DOSTEXTFAR ASM XMSDriverAddress; VOID ASMCFUNC FAR _EnableA20(VOID); VOID ASMCFUNC FAR _DisableA20(VOID); void FAR * ASMCFUNC DetectXMSDriver(VOID); int ASMCFUNC init_call_XMScall(void FAR * driverAddress, UWORD ax, UWORD dx); STATIC void InstallVDISK(void); #ifdef DEBUG #ifdef __TURBOC__ #define int3() __int__(3); #else void int3() { __asm int 3; } #endif #else #define int3() #endif #ifdef DEBUG #define HMAInitPrintf(x) printf x #else #define HMAInitPrintf(x) #endif void MoveKernel(unsigned NewKernelSegment); #ifdef DEBUG VOID hdump(BYTE FAR * p) { int loop; HMAInitPrintf(("%p", p)); for (loop = 0; loop < 16; loop++) HMAInitPrintf(("%02x ", p[loop])); printf("\n"); } #else #define hdump(ptr) #endif #define KeyboardShiftState() (*(BYTE FAR *)(MK_FP(0x40,0x17))) /* of course, this should go to ASMSUPT */ int fmemcmp(BYTE far * s1, BYTE FAR * s2, unsigned len) { for (; len; s1++, s2++, --len) { if (*s1 - *s2) return *s1 - *s2; } return 0; } /* this tests, if the HMA area can be enabled. if so, it simply leaves it on */ int EnableHMA(VOID) { _EnableA20(); if (fmemcmp(MK_FP(0x0000, 0x0000), MK_FP(0xffff, 0x0010), 128) == 0) { printf("HMA can't be enabled\n"); return FALSE; } _DisableA20(); if (fmemcmp(MK_FP(0x0000, 0x0000), MK_FP(0xffff, 0x0010), 128) != 0) { printf("HMA can't be disabled - no problem for us\n"); } _EnableA20(); if (fmemcmp(MK_FP(0x0000, 0x0000), MK_FP(0xffff, 0x0010), 128) == 0) { printf("HMA can't be enabled second time\n"); return FALSE; } HMAInitPrintf(("HMA success - leaving enabled\n")); return TRUE; } /* move the kernel up to high memory this is very unportable if we thin we succeeded, we return TRUE, else FALSE */ #define HMAOFFSET 0x20 #define HMASEGMENT 0xffff int MoveKernelToHMA() { if (DosLoadedInHMA) { return TRUE; } if ((XMSDriverAddress = DetectXMSDriver()) == NULL) return FALSE; #ifdef DEBUG /* A) for debugging purpose, suppress this, if any shift key is pressed */ if (KeyboardShiftState() & 0x0f) { printf("Keyboard state is %0x, NOT moving to HMA\n", KeyboardShiftState()); return FALSE; } #endif /* B) check out, if we can have HMA */ if (!EnableHMA()) { printf("Can't enable HMA area (the famous A20), NOT moving to HMA\n"); return FALSE; } /* allocate HMA through XMS driver */ if (HMAclaimed == 0 && (HMAclaimed = init_call_XMScall(XMSDriverAddress, 0x0100, 0xffff)) == 0) { printf("Can't reserve HMA area ??\n"); return FALSE; } MoveKernel(0xffff); { /* E) up to now, nothing really bad was done. but now, we reuse the HMA area. bad things will happen to find bugs early, cause INT 3 on all accesses to this area */ DosLoadedInHMA = TRUE; } /* on finalize, will install a VDISK */ InstallVDISK(); /* report the fact we are running high through int 21, ax=3306 */ version_flags |= 0x10; return TRUE; } /* now protect against HIMEM/FDXMS/... by simulating a VDISK FDXMS should detect us and not give HMA access to ohers unfortunately this also disables HIMEM completely so: we install this after all drivers have been loaded */ STATIC void InstallVDISK(void) { static struct { /* Boot-Sektor of a RAM-Disk */ UBYTE dummy1[3]; /* HIMEM.SYS uses 3, but FDXMS uses 2 */ char Name[5]; BYTE dummy2[3]; WORD BpS; BYTE dummy3[6]; WORD Sektoren; BYTE dummy4; } VDISK_BOOT_SEKTOR = { { 0xcf, ' ', ' '}, { 'V', 'D', 'I', 'S', 'K'}, { ' ', ' ', ' '}, 512, { 'F', 'D', 'O', 'S', ' ', ' '}, 128, /* 128*512 = 64K */ ' '}; if (!DosLoadedInHMA) return; fmemcpy(MK_FP(0xffff, 0x0010), &VDISK_BOOT_SEKTOR, sizeof(VDISK_BOOT_SEKTOR)); *(WORD FAR *) MK_FP(0xffff, 0x002e) = 1024 + 64; } /* this allocates some bytes from the HMA area only available if DOS=HIGH was successful */ VOID FAR * HMAalloc(COUNT bytesToAllocate) { VOID FAR *HMAptr; if (!DosLoadedInHMA) return NULL; if (HMAFree >= 0xfff0 - bytesToAllocate) return NULL; HMAptr = MK_FP(0xffff, HMAFree); /* align on 16 byte boundary */ HMAFree = (HMAFree + bytesToAllocate + 0xf) & 0xfff0; /*printf("HMA allocated %d byte at %x\n", bytesToAllocate, HMAptr); */ fmemset(HMAptr, 0, bytesToAllocate); return HMAptr; } unsigned CurrentKernelSegment = 0; void MoveKernel(unsigned NewKernelSegment) { UBYTE FAR *HMADest; UBYTE FAR *HMASource; unsigned len; if (CurrentKernelSegment == 0) CurrentKernelSegment = FP_SEG(_HMATextEnd); if (CurrentKernelSegment == 0xffff) return; HMASource = MK_FP(CurrentKernelSegment, (FP_OFF(_HMATextStart) & 0xfff0)); HMADest = MK_FP(NewKernelSegment, 0x0000); len = (FP_OFF(_HMATextEnd) | 0x000f) - (FP_OFF(_HMATextStart) & 0xfff0); if (NewKernelSegment == 0xffff) { HMASource += HMAOFFSET; HMADest += HMAOFFSET; len -= HMAOFFSET; } HMAInitPrintf(("HMA moving %p up to %p for %04x bytes\n", HMASource, HMADest, len)); if (NewKernelSegment < CurrentKernelSegment || NewKernelSegment == 0xffff) { unsigned i; UBYTE FAR *s, FAR * d; for (i = 0, s = HMASource, d = HMADest; i < len; i++) d[i] = s[i]; } else { /* might overlap */ unsigned i; UBYTE FAR *s, FAR * d; for (i = len, s = HMASource, d = HMADest; i != 0; i--) d[i] = s[i]; } HMAFree = (FP_OFF(HMADest) + len + 0xf) & 0xfff0; /* first free byte after HMA_TEXT on 16 byte boundary */ { /* D) but it only makes sense, if we can relocate all our entries to make use of HMA */ /* this is for a call near enableA20 jmp far kernelentry style table */ struct RelocationTable { UBYTE jmpFar; UWORD jmpOffset; UWORD jmpSegment; UBYTE callNear; UWORD callOffset; }; struct RelocatedEntry { UBYTE callNear; UWORD callOffset; UBYTE jmpFar; UWORD jmpOffset; UWORD jmpSegment; }; extern struct RelocationTable DOSTEXTFAR ASM _HMARelocationTableStart[], DOSTEXTFAR ASM _HMARelocationTableEnd[]; struct RelocationTable FAR *rp, rtemp; /* verify, that all entries are valid */ for (rp = _HMARelocationTableStart; rp < _HMARelocationTableEnd; rp++) { if (rp->jmpFar != 0xea || /* jmp FAR */ rp->jmpSegment != CurrentKernelSegment || /* will only relocate HMA_TEXT */ rp->callNear != 0xe8 || /* call NEAR */ 0) { printf("illegal relocation entry # %d\n", (FP_OFF(rp) - FP_OFF(_HMARelocationTableStart)) / sizeof(struct RelocationTable)); int3(); goto errorReturn; } } /* OK, all valid, go to relocate */ for (rp = _HMARelocationTableStart; rp < _HMARelocationTableEnd; rp++) { if (NewKernelSegment == 0xffff) { struct RelocatedEntry FAR *rel = (struct RelocatedEntry FAR *)rp; fmemcpy(&rtemp, rp, sizeof(rtemp)); rel->jmpFar = rtemp.jmpFar; rel->jmpSegment = NewKernelSegment; rel->jmpOffset = rtemp.jmpOffset; rel->callNear = rtemp.callNear; rel->callOffset = rtemp.callOffset + 5; /* near calls are relative */ } else rp->jmpSegment = NewKernelSegment; } } CurrentKernelSegment = NewKernelSegment; return; errorReturn: for (;;) ; }