FreeDOS/kernel/inithma.c
2001-11-17 23:26:45 +00:00

521 lines
14 KiB
C

/****************************************************************/
/* */
/* 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 _HMATextAvailable, /* first byte of available CODE area */
FAR _HMATextStart[], /* first byte of HMAable CODE area */
FAR _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 BYTE FAR * DOSTEXTFAR ASMCFUNC XMSDriverAddress;
extern FAR ASMCFUNC _EnableA20(VOID);
extern FAR ASMCFUNC _DisableA20(VOID);
extern void FAR *ASMCFUNC DetectXMSDriver(VOID);
extern int ASMCFUNC init_call_XMScall( void FAR * driverAddress, UWORD ax, UWORD dx);
#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 */
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;
}
/* report the fact we are running high thorugh int 21, ax=3306 */
version_flags |= 0x10;
return TRUE;
}
/* not necessary anymore : BO */
/*
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
*/
#if 0
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;
if (HMAclaimed) return;
fmemcpy(MK_FP(0xffff,0x0010), &VDISK_BOOT_SEKTOR, sizeof(VDISK_BOOT_SEKTOR));
setvec(0x19, MK_FP(0xffff,0x0010)); /* let INT 19 point to VDISK */
*(WORD FAR *)MK_FP(0xffff,0x002e) = 1024+64;
}
#endif
/*
this should be called, after each device driver
has been loaded with FALSE
and on finished CONFIG processing with TRUE.
will try to grab HMA;
on finalize, will install a VDISK
*/
#if 0 /* not necessary anymore */
void HMAconfig(int finalize)
{
ClaimHMA();
if (finalize)
InstallVDISK();
}
#endif
/*
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;
int3();
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; /* first free byte after HMA_TEXT */
{
/* 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 _HMARelocationTableStart[],
DOSTEXTFAR _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;
}
}
{
struct initRelocationTable {
UBYTE callNear;
UWORD callOffset;
UBYTE jmpFar;
UWORD jmpOffset;
UWORD jmpSegment;
};
extern struct initRelocationTable
_HMAinitRelocationTableStart[],
_HMAinitRelocationTableEnd[];
struct initRelocationTable *rp;
/* verify, that all entries are valid */
for (rp = _HMAinitRelocationTableStart; rp < _HMAinitRelocationTableEnd; rp++)
{
if (
rp->callNear != 0xe8 || /* call NEAR */
rp->jmpFar != 0xea || /* jmp FAR */
rp->jmpSegment != CurrentKernelSegment || /* will only relocate HMA_TEXT */
0)
{
printf("illegal init relocation entry # %d\n",
rp - _HMAinitRelocationTableStart);
goto errorReturn;
}
}
/* OK, all valid, go to relocate*/
for (rp = _HMAinitRelocationTableStart; rp < _HMAinitRelocationTableEnd; rp++)
{
rp->jmpSegment = NewKernelSegment;
}
}
CurrentKernelSegment = NewKernelSegment;
return;
errorReturn:
for (;;);
}
/*
* Log: inithma.c,v - for newer entries do "cvs log inithma.c"
*
* Revision 0.1 2001/03/16 12:00:00 tom ehlert
* initial creation
*/