📄 pm.c
字号:
{ PMSREGS sregs; ulong linAddr; ulong DSBaseAddr; /* Get the base address for the default DS selector */ PM_segread(&sregs); DSBaseAddr = DPMI_getSelectorBase(sregs.ds); if ((base < 0x100000) && (DSBaseAddr == 0)) { /* DS is zero based, so we can directly access the first 1Mb of * system memory (like under DOS4GW). */ return (void*)base; } /* Map the memory to a linear address using DPMI function 0x800 */ if ((linAddr = DPMI_mapPhysicalToLinear(base,limit)) == 0xFFFFFFFF) { if (base >= 0x100000) return NULL; /* If the linear address mapping fails but we are trying to * map an area in the first 1Mb of system memory, then we must * be running under a Windows or OS/2 DOS box. Under these * environments we can use the segment wrap around as a fallback * measure, as this does work properly. */ linAddr = base; } /* Now expand the default DS selector to 4Gb so we can access it */ if (!DPMI_setSelectorLimit(sregs.ds,0xFFFFFFFFUL)) return NULL; /* Finally enable caching for the page tables that we just mapped in, * since DOS4GW and PMODE/W create the page table entries without * caching enabled which hurts the performance of the linear framebuffer * as it disables write combining on Pentium Pro and above processors. * * For those processors cache disabling is better handled through the * MTRR registers anyway (we can write combine a region but disable * caching) so that MMIO register regions do not screw up. */ if (DSBaseAddr == 0) PM_adjustPageTables(linAddr,limit,isCached); /* Now return the base address of the memory into the default DS */ return (void*)(linAddr - DSBaseAddr);}#if defined(PM386)/* Some DOS extender implementations do not directly support calling a * real mode procedure from protected mode. However we can simulate what * we need temporarily hooking the INT 6Ah vector with a small real mode * stub that will call our real mode code for us. */static uchar int6AHandler[] = { 0x00,0x00,0x00,0x00, /* __PMODE_callReal variable */ 0xFB, /* sti */ 0x2E,0xFF,0x1E,0x00,0x00, /* call [cs:__PMODE_callReal] */ 0xCF, /* iretf */ };static uchar *crPtr = NULL; /* Pointer to of int 6A handler */static uint crRSeg,crROff; /* Real mode seg:offset of handler */void PMAPI PM_callRealMode(uint seg,uint off, RMREGS *in, RMSREGS *sregs){ uchar *p; uint oldSeg,oldOff; if (!crPtr) { /* Allocate and copy the memory block only once */ crPtr = PM_allocRealSeg(sizeof(int6AHandler), &crRSeg, &crROff); memcpy(crPtr,int6AHandler,sizeof(int6AHandler)); } PM_setWord(crPtr,off); /* Plug in address to call */ PM_setWord(crPtr+2,seg); p = PM_mapRealPointer(0,0x6A * 4); oldOff = PM_getWord(p); /* Save old handler address */ oldSeg = PM_getWord(p+2); PM_setWord(p,crROff+4); /* Hook 6A handler */ PM_setWord(p+2,crRSeg); PM_int86x(0x6A, in, in, sregs); /* Call real mode code */ PM_setWord(p,oldOff); /* Restore old handler */ PM_setWord(p+2,oldSeg);}#endif /* PM386 */#endif /* !REALMODE *//****************************************************************************REMARKS:Allocates a block of locked, physically contiguous memory. The memorymay be required to be below the 16Meg boundary.****************************************************************************/void * PMAPI PM_allocLockedMem( uint size, ulong *physAddr, ibool contiguous, ibool below16Meg){ uchar *p,*roundedP; uint r_seg,r_off; uint roundedSize = (size + 4 + 0xFFF) & ~0xFFF; PM_lockHandle lh; /* Unused in DOS */#ifndef REALMODE VXD_regs regs; /* If we have connected to our helper VxD in a Windows DOS box, use the * helper VxD services to allocate the memory that we need. */ if (VXD_version) { memset(®s,0,sizeof(regs)); regs.eax = API_NUM(PMHELP_ALLOCLOCKED); regs.ebx = size; regs.ecx = (ulong)physAddr; regs.edx = contiguous | (below16Meg << 8); _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel); return (void*)regs.eax; } /* If the memory is not contiguous, we simply need to allocate it * using regular memory allocation services, and lock it down * in memory. * * For contiguous memory blocks, the only way to guarantee contiguous physical * memory addresses under DOS is to allocate the memory below the * 1Meg boundary as real mode memory. * * Note that we must page align the memory block, and we also must * keep track of the non-aligned pointer so we can properly free * it later. Hence we actually allocate 4 bytes more than the * size rounded up to the next 4K boundary. */ if (!contiguous) p = PM_malloc(roundedSize); else#endif p = PM_allocRealSeg(roundedSize,&r_seg,&r_off); if (p == NULL) return NULL; roundedP = (void*)(((ulong)p + 0xFFF) & ~0xFFF); *((ulong*)(roundedP + size)) = (ulong)p; PM_lockDataPages(roundedP,size,&lh); if ((*physAddr = PM_getPhysicalAddr(roundedP)) == 0xFFFFFFFF) { PM_freeLockedMem(roundedP,size,contiguous); return NULL; } /* Disable caching for the memory since it is probably a DMA buffer */#ifndef REALMODE PM_adjustPageTables((ulong)roundedP,size-1,false);#endif return roundedP;}/****************************************************************************REMARKS:Free a block of locked memory.****************************************************************************/void PMAPI PM_freeLockedMem(void *p,uint size,ibool contiguous){#ifndef REALMODE VXD_regs regs; PM_lockHandle lh; /* Unused in DOS */ if (!p) return; if (VXD_version) { memset(®s,0,sizeof(regs)); regs.eax = API_NUM(PMHELP_FREELOCKED); regs.ebx = (ulong)p; regs.ecx = size; regs.edx = contiguous; _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel); return; } PM_unlockDataPages(p,size,&lh); if (!contiguous) free(*((void**)((uchar*)p + size))); else#endif PM_freeRealSeg(*((void**)((char*)p + size)));}#ifndef REALMODE/****************************************************************************REMARKS:Allocates a new block of pages for the page block manager.****************************************************************************/static pageblock *PM_addNewPageBlock(void){ int i,size; pageblock *newBlock; char *p,*next; /* Allocate memory for the new page block, and add to head of list */ size = PAGES_PER_BLOCK * PM_PAGE_SIZE + (PM_PAGE_SIZE-1) + sizeof(pageblock); if ((newBlock = PM_malloc(size)) == NULL) return NULL; newBlock->prev = NULL; newBlock->next = pageBlocks; if (pageBlocks) pageBlocks->prev = newBlock; pageBlocks = newBlock; /* Initialise the page aligned free list for the page block */ newBlock->freeCount = PAGES_PER_BLOCK; newBlock->freeList = p = (char*)(((ulong)(newBlock + 1) + (PM_PAGE_SIZE-1)) & ~(PM_PAGE_SIZE-1)); newBlock->freeListStart = newBlock->freeList; newBlock->freeListEnd = p + (PAGES_PER_BLOCK-1) * PM_PAGE_SIZE; for (i = 0; i < PAGES_PER_BLOCK; i++,p = next) FREELIST_NEXT(p) = next = p + PM_PAGE_SIZE; FREELIST_NEXT(p - PM_PAGE_SIZE) = NULL; return newBlock;}#endif/****************************************************************************REMARKS:Allocates a page aligned and page sized block of memory****************************************************************************/void * PMAPI PM_allocPage( ibool locked){#ifndef REALMODE VXD_regs regs; pageblock *block; void *p; PM_lockHandle lh; /* Unused in DOS */ /* Call the helper VxD for this service if we are running in a DOS box */ if (VXD_version) { memset(®s,0,sizeof(regs)); regs.eax = API_NUM(PMHELP_ALLOCPAGE); regs.ebx = locked; _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel); return (void*)regs.eax; } /* Scan the block list looking for any free blocks. Allocate a new * page block if no free blocks are found. */ for (block = pageBlocks; block != NULL; block = block->next) { if (block->freeCount) break; } if (block == NULL && (block = PM_addNewPageBlock()) == NULL) return NULL; block->freeCount--; p = block->freeList; block->freeList = FREELIST_NEXT(p); if (locked) PM_lockDataPages(p,PM_PAGE_SIZE,&lh); return p;#else return NULL;#endif}/****************************************************************************REMARKS:Free a page aligned and page sized block of memory****************************************************************************/void PMAPI PM_freePage( void *p){#ifndef REALMODE VXD_regs regs; pageblock *block; /* Call the helper VxD for this service if we are running in a DOS box */ if (VXD_version) { memset(®s,0,sizeof(regs)); regs.eax = API_NUM(PMHELP_FREEPAGE); regs.ebx = (ulong)p; _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel); return; } /* First find the page block that this page belongs to */ for (block = pageBlocks; block != NULL; block = block->next) { if (p >= block->freeListStart && p <= block->freeListEnd) break; } CHECK(block != NULL); /* Now free the block by adding it to the free list */ FREELIST_NEXT(p) = block->freeList; block->freeList = p; if (++block->freeCount == PAGES_PER_BLOCK) { /* If all pages in the page block are now free, free the entire * page block itself. */ if (block == pageBlocks) { /* Delete from head */ pageBlocks = block->next; if (block->next) block->next->prev = NULL; } else { /* Delete from middle of list */ CHECK(block->prev != NULL); block->prev->next = block->next; if (block->next) block->next->prev = block->prev; } PM_free(block); }#else (void)p;#endif}/*-------------------------------------------------------------------------*//* DOS Real Mode support. *//*-------------------------------------------------------------------------*/#ifdef REALMODE#ifndef MK_FP#define MK_FP(s,o) ( (void far *)( ((ulong)(s) << 16) + \ (ulong)(o) ))#endifvoid * PMAPI PM_mapRealPointer(uint r_seg,uint r_off){ return MK_FP(r_seg,r_off); }void * PMAPI PM_getBIOSPointer(void){ return MK_FP(0x40,0);}void * PMAPI PM_getA0000Pointer(void){ return MK_FP(0xA000,0);}void * PMAPI PM_mapPhysicalAddr(ulong base,ulong limit,ibool isCached){ uint sel = base >> 4; uint off = base & 0xF; limit = limit; return MK_FP(sel,off);}void PMAPI PM_freePhysicalAddr(void *ptr,ulong limit){ ptr = ptr; }ulong PMAPI PM_getPhysicalAddr(void *p){ return ((((ulong)p >> 16) << 4) + (ushort)p);}ibool PMAPI PM_getPhysicalAddrRange(void *p,ulong length,ulong *physAddress){ return false; }void * PMAPI PM_mapToProcess(void *base,ulong limit){ return (void*)base; }void * PMAPI PM_allocRealSeg(uint size,uint *r_seg,uint *r_off){ /* Call malloc() to allocate the memory for us */ void *p = PM_malloc(size); *r_seg = FP_SEG(p); *r_off = FP_OFF(p); return p;}void PMAPI PM_freeRealSeg(void *mem){ if (mem) PM_free(mem);}int PMAPI PM_int86(int intno, RMREGS *in, RMREGS *out){ return PM_int386(intno,in,out);}int PMAPI PM_int86x(int intno, RMREGS *in, RMREGS *out, RMSREGS *sregs){ return PM_int386x(intno,in,out,sregs);}void PMAPI PM_availableMemory(ulong *physical,ulong *total){ PMREGS regs; regs.h.ah = 0x48; regs.x.bx = 0xFFFF; PM_int86(0x21,®s,®s); *physical = *total = regs.x.bx * 16UL;}#endif/*-------------------------------------------------------------------------*//* Phar Lap TNT DOS Extender support. *//*-------------------------------------------------------------------------*/#ifdef TNT#include <pldos32.h>#include <pharlap.h>#include <hw386.h>static uchar *zeroPtr = NULL;void * PMAPI PM_getBIOSPointer(void){ if (!zeroPtr) zeroPtr = PM_mapPhysicalAddr(0,0xFFFFF,true); return (void*)(zeroPtr + 0x400);}void * PMAPI PM_getA0000Pointer(void){ static void *bankPtr; if (!bankPtr) bankPtr = PM_mapPhysicalAddr(0xA0000,0xFFFF,true); return bankPtr;}void * PMAPI PM_mapPhysicalAddr(ulong base,ulong limit,ibool isCached){ CONFIG_INF config; ULONG offset; int err; ulong baseAddr,baseOfs,newLimit; VXD_regs regs; /* If we have connected to our helper VxD in a Windows DOS box, use * the helper VxD services to map memory instead of the DPMI services. * We do this because the helper VxD can properly disable caching * where necessary, which we can only do directly here if we are * running at ring 0 (ie: under real DOS). */ if (VXD_version == -1) PM_init(); if (VXD_version) { memset(®s,0,sizeof(regs)); regs.eax = API_NUM(PMHELP_MAPPHYS); regs.ebx = base; regs.ecx = limit; regs.edx = isCached; _PM_VxDCall(®s,_PM_VXD_off,_PM_VXD_sel); return (void*)regs.eax; } /* Round the physical address to a 4Kb boundary and the limit to a * 4Kb-1 boundary before passing the values to TNT. If we round the * physical address, then we also add an extra offset into the address * that we return. */ baseOfs = base & 4095; baseAddr = base & ~4095; newLimit = ((limit+baseOfs+1+4095) & ~4095)-1; _dx_config_inf(&config, (UCHAR*)&config); err = _dx_map_phys(config.c_ds_sel,baseAddr,(newLimit + 4095) / 4096,&offset); if (err == 130) { /* If the TNT function failed, we are running in a DPMI environment * and this function does not work. However we know how to handle * DPMI properly, so we use our generic DPMI functions to do * what the TNT runtime libraries can't. */ return DPMI_mapPhysicalAddr(base,limit,isCached); } if (err == 0) return (void*)(offset + baseOfs); return NULL;}void PMAPI PM_freePhysicalAddr(void *ptr,ulong limit){}ulong PMAPI PM_getPhysicalAddr(void *p){ return 0xFFFFFFFFUL; }ibool PMAPI PM_getPhysicalAddrRange(void *p,ulong length,ulong *physAddress){ return false; }void * PMAPI PM_mapToProcess(void *base,ulong limit){ return (void*)base; }void * PMAPI PM_mapRealPointer(uint r_seg,uint r_off){ if (!zeroPtr) zeroPtr = PM_mapPhysicalAddr(0,0xFFFFF); return (void*)(zeroPtr + MK_PHYS(r_seg,r_off));}void * PMAPI PM_allocRealSeg(uint size,uint *r_seg,uint *r_off){ USHORT addr,t; void *p; if (_dx_real_alloc((size + 0xF) >> 4,&addr,&t) != 0) return 0; *r_seg = addr; /* Real mode segment address */ *r_off = 0; /* Real mode segment offset */ p = PM_mapRealPointer(*r_seg,*r_off); _PM_addRealModeBlock(p,addr); return p;}void PMAPI PM_freeRealSeg(void *mem){ if (mem) _dx_real_free(_PM_findRealModeBlock(mem));}#define INDPMI(reg) rmregs.reg = regs->reg#define OUTDPMI(reg) regs->reg = rmregs.regvoid PMAPI DPMI_int86(int intno, DPMI_regs *regs){ SWI_REGS rmregs; memset(&rmregs, 0, sizeof(rmregs)); INDPMI(eax); INDPMI(ebx); INDPMI(ecx); INDPMI(edx); INDPMI(esi); INDPMI(edi); _dx_real_int(intno,&rmregs); OUTDPMI(eax); OUTDPMI(ebx); OUTDPMI(ecx); OUTDPMI(edx); OUTDPMI(esi); OUTDPMI(edi); regs->flags = rmregs.flags;}#define IN(reg) rmregs.reg = in->e.reg#define OUT(reg) out->e.reg = rmregs.regint PMAPI PM_int86(int intno, RMREGS *in, RMREGS *out){ SWI_REGS rmregs; memset(&rmregs, 0, sizeof(rmregs)); IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi); _dx_real_int(intno,&rmregs); OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi); out->x.cflag = rmregs.flags & 0x1; return out->x.ax;}int PMAPI PM_int86x(int intno, RMREGS *in, RMREGS *out, RMSREGS *sregs){ SWI_REGS rmregs; memset(&rmregs, 0, sizeof(rmregs)); IN(eax); IN(ebx); IN(ecx); IN(edx); IN(esi); IN(edi); rmregs.es = sregs->es; rmregs.ds = sregs->ds; _dx_real_int(intno,&rmregs); OUT(eax); OUT(ebx); OUT(ecx); OUT(edx); OUT(esi); OUT(edi); sregs->es = rmregs.es;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -