fpPS4/kernel/ps4_map_mm.pas
red-prig 57b2c9fa13 +
2022-10-02 00:46:07 +03:00

2245 lines
52 KiB
ObjectPascal

unit ps4_map_mm;
{$mode objfpc}{$H+}
interface
uses
Windows,
g23tree,
RWLock,
sys_types,
mmap,
mm_adr_direct,
mm_adr_virtual,
mm_adr_pool,
Classes,
SysUtils;
{
Flexible memory: elf sections, stack, calloc, cpu only
Direct memory: phisical mapped, cpu/gpu mem
Pooled memory: section of direct memory, 64 KiB blocks,
The application program can use a total of 5184 MiB (5824 MiB in NEO mode) physical memory.
If there is no specification, 448 MiB will be assigned as flexible memory.
Physical Address Space and Direct Memory Areas is guaranteed to be aligned to a 2 MiB boundary.
Unmapped area : 0x0000 0000 0000 - 0x0000 0040 0000 Size: 0x0000 0040 0000 (4MB)
System managed area : 0x0000 0040 0000 - 0x0007 FFFF C000 Size: 0x0007 FFBF C000 (31GB)
System reserved area: 0x0007 FFFF C000 - 0x0010 0000 0000 Size: 0x0008 0000 4000 (32GB)
User area : 0x0010 0000 0000 - 0x00FC 0000 0000 Size: 0x00EC 0000 0000 (944GB)
System reserved area: 0x00FC 0000 0000 - 0x00FF FFFF FFFF Size: 0x0003 FFFF FFFF (15GB)
}
var
DirectManager:TDirectManager;
VirtualManager:TVirtualManager;
Const
SCE_KERNEL_MAIN_DMEM_SIZE=$180000000; //6GB
type
pSceKernelDirectMemoryQueryInfo=^SceKernelDirectMemoryQueryInfo;
SceKernelDirectMemoryQueryInfo=packed record
start:QWORD;
__end:QWORD;
mType:Integer;
align:Integer;
end;
const
SCE_KERNEL_VIRTUAL_RANGE_NAME_SIZE=32;
SCE_KERNEL_DMQ_FIND_NEXT=1;
SCE_KERNEL_VQ_FIND_NEXT=1;
type
pSceKernelVirtualQueryInfo=^SceKernelVirtualQueryInfo;
SceKernelVirtualQueryInfo=packed record
pstart:Pointer;
pend :Pointer;
offset:QWORD;
protection:Integer;
memoryType:Integer;
bits:bitpacked record
isFlexibleMemory:0..1;
isDirectMemory :0..1;
isStack :0..1;
isPooledMemory :0..1;
isCommitted :0..1;
end;
name:array[0..SCE_KERNEL_VIRTUAL_RANGE_NAME_SIZE-1] of AnsiChar;
end;
function ps4_sceKernelGetDirectMemorySize:Int64; SysV_ABI_CDecl;
function ps4_getpagesize:Integer; SysV_ABI_CDecl;
function ps4_sceKernelAllocateDirectMemory(
searchStart:QWORD;
searchEnd:QWORD;
length:QWORD;
alignment:QWORD;
memoryType:Integer;
physicalAddrDest:PQWORD):Integer; SysV_ABI_CDecl;
function ps4_sceKernelAllocateMainDirectMemory(
length:QWORD;
alignment:QWORD;
memoryType:Integer;
physicalAddrDest:PQWORD):Integer; SysV_ABI_CDecl;
function ps4_sceKernelAvailableDirectMemorySize(
searchStart:QWORD;
searchEnd:QWORD;
alignment:QWORD;
physAddrOut:PQWORD;
sizeOut:PQWORD):Integer; SysV_ABI_CDecl;
function ps4_sceKernelDirectMemoryQuery(
offset:QWORD;
flags:Integer;
info:pSceKernelDirectMemoryQueryInfo;
infoSize:QWORD):Integer; SysV_ABI_CDecl;
function ps4_sceKernelGetDirectMemoryType(
start:QWORD;
memoryTypeOut:PInteger;
regionStartOut:PQWORD;
regionEndOut:PQWORD):Integer; SysV_ABI_CDecl;
function ps4_sceKernelCheckedReleaseDirectMemory(start,len:QWORD):Integer; SysV_ABI_CDecl;
function ps4_sceKernelReleaseDirectMemory(start,len:QWORD):Integer; SysV_ABI_CDecl;
//mapping
function ps4_sceKernelMapDirectMemory(
virtualAddrDest:PPointer;
length:QWORD;
protections:Integer;
flags:Integer;
physicalAddr:QWORD;
alignment:QWORD):Integer; SysV_ABI_CDecl;
function ps4_sceKernelMapNamedFlexibleMemory(
virtualAddrDest:PPointer;
length:QWORD;
protections:Integer;
flags:Integer;
name:PChar):Integer; SysV_ABI_CDecl;
function ps4_sceKernelMapFlexibleMemory(
virtualAddrDest:PPointer;
length:QWORD;
protections:Integer;
flags:Integer):Integer; SysV_ABI_CDecl;
function ps4_sceKernelMunmap(addr:Pointer;len:size_t):Integer; SysV_ABI_CDecl;
function ps4_sceKernelQueryMemoryProtection(addr:Pointer;pStart,pEnd:PPointer;pProt:PInteger):Integer; SysV_ABI_CDecl;
function ps4_sceKernelVirtualQuery(addr:Pointer;
flags:Integer;
info:pSceKernelVirtualQueryInfo;
infoSize:QWORD):Integer; SysV_ABI_CDecl;
function ps4_sceKernelMprotect(addr:Pointer;len:QWORD;prot:Integer):Integer; SysV_ABI_CDecl;
function ps4_sceKernelSetVirtualRangeName(addr:Pointer;len:QWORD;name:Pchar):Integer; SysV_ABI_CDecl;
function ps4_mmap(addr:Pointer;len:size_t;prot,flags:Integer;fd:Integer;offset:size_t):Pointer; SysV_ABI_CDecl;
function ps4_munmap(addr:Pointer;len:size_t):Integer; SysV_ABI_CDecl;
function ps4_msync(addr:Pointer;len:size_t;flags:Integer):Integer; SysV_ABI_CDecl;
function ps4_mprotect(addr:Pointer;len:size_t;prot:Integer):Integer; SysV_ABI_CDecl;
type
TGpuMemAlloc=function(addr:Pointer;len:size_t):Pointer;
TGpuMemFree =procedure(h:Pointer);
TGpuMemBlock=record
pAddr:Pointer;
nSize:Int64;
Handle:Pointer;
end;
TGpuMemCb=record
Alloc:TGpuMemAlloc;
Free :TGpuMemFree;
end;
var
GpuMemCb:TGpuMemCb;
Function TryGetGpuMemBlockByAddr(addr:Pointer;var block:TGpuMemBlock):Boolean;
Procedure RegistredStack;
Procedure UnRegistredStack;
implementation
uses
sys_kernel,
sys_signal;
const
INVALID_DIRECT=QWORD(-1);
BT_STACK =0;
BT_DIRECT_BIG=1;
BT_DIRECT_64K=2;
BT_PHYSIC_BIG=3;
BT_PHYSIC_64K=4;
BS_FREE =0;
BS_RESERVE=1;
BS_COMMIT =2;
type
PBlock=^TBlock;
TBlock=object
pAddr:Pointer;
nSize:Int64;
bType:SizeUint;
end;
pdlist=^Tdlist;
Pdnode=^Tdnode;
Tdnode=object
pPrev,pNext:Pdnode;
end;
Tdlist=object
pHead,pTail:Pdnode;
function REMOVE_HEAD:Pdnode; inline;
procedure INSERT_TAIL(e:Pdnode); inline;
procedure REMOVE(e:Pdnode); inline;
end;
TnodeInfo=bitpacked record
id:Byte;
prot:Byte;
state:Byte;
len:Byte;
align2:DWORD;
end;
PdnodeAdr=^TdnodeAdr;
TdnodeAdr=object(Tdnode)
direct:QWORD;
info:TnodeInfo;
end;
PBlockBig=^TBlockBig;
TBlockBig=object(TBlock)
direct:QWORD;
Handle:Pointer;
prot:Byte;
end;
PBlock64k=^TBlock64k;
TBlock64k=object(TBlock)
nodes:array[0..3] of TdnodeAdr;
end;
function IsPowerOfTwo(x:QWORD):Boolean; inline;
begin
Result:=(x and (x - 1))=0;
end;
function fastIntLog2(i:QWORD):QWORD; inline;
begin
Result:=BsfQWORD(i);
end;
function str_mem_type(memoryType:Integer):RawByteString;
begin
Result:='';
Case memoryType of
SCE_KERNEL_WB_ONION :Result:='WB_ONION';
SCE_KERNEL_WC_GARLIC:Result:='WC_GARLIC';
SCE_KERNEL_WB_GARLIC:Result:='WB_GARLIC';
else
Result:=IntToStr(memoryType);
end;
end;
function test_KP_flags(flags:Integer):RawByteString;
begin
Result:='';
if (flags and SCE_KERNEL_PROT_CPU_READ) <>0 then Result:=Result+' CPU_READ';
if (flags and SCE_KERNEL_PROT_CPU_WRITE)<>0 then Result:=Result+' CPU_WRIT';
if (flags and SCE_KERNEL_PROT_CPU_EXEC) <>0 then Result:=Result+' CPU_EXEC';
if (flags and SCE_KERNEL_PROT_GPU_READ) <>0 then Result:=Result+' GPU_READ';
if (flags and SCE_KERNEL_PROT_GPU_WRITE)<>0 then Result:=Result+' GPU_WRIT';
end;
//
function Get16kBlockCount(len:PTRUINT):PTRUINT; inline;
begin
Result:=len div LOGICAL_PAGE_SIZE;
end;
function Get4kBlockCount(len:PTRUINT):PTRUINT; inline;
begin
Result:=len div PHYSICAL_PAGE_SIZE;
end;
//
function VirtualAllocAlign(Addr:Pointer;dwSize,alignment:PTRUINT;flAllocationType,flProtect:DWORD):Pointer;
begin
Result:=nil;
if (alignment<=GRANULAR_PAGE_SIZE) or (Addr<>nil) then
begin
Result:=VirtualAlloc(Addr,dwSize,flAllocationType,flProtect);
Exit;
end;
Addr:=Pointer($5400000);
Addr:=AlignUp(Addr,alignment);
repeat
Result:=VirtualAlloc(Addr,dwSize,flAllocationType,flProtect);
if (Result<>nil) then Exit;
Case GetLastError of
ERROR_INVALID_ADDRESS:;
else
Exit;
end;
Addr:=Addr+alignment;
until false;
end;
function VirtualQueryBase(Addr:Pointer):TBlock;
var
Info:TMemoryBasicInformation;
begin
Result:=Default(TBlock);
Info:=Default(TMemoryBasicInformation);
if (VirtualQuery(addr,Info,SizeOf(TMemoryBasicInformation))<>0) then
begin
Case Info.State of
MEM_FREE :Result.bType:=BS_FREE;
MEM_COMMIT :Result.bType:=BS_COMMIT;
MEM_RESERVE:Result.bType:=BS_RESERVE;
end;
Result.pAddr:=Info.AllocationBase;
Result.nSize:=Info.RegionSize+(ptruint(Info.BaseAddress)-ptruint(Info.AllocationBase));
end;
end;
function VirtualIsFullReserve(Addr:Pointer;dwSize:PTRUINT):Boolean;
var
curr:Pointer;
q:TBlock;
begin
Result:=True;
curr:=Addr;
While (curr<Addr+dwSize) do
begin
q:=VirtualQueryBase(curr);
Case q.bType of
BS_FREE:
begin
curr:=q.pAddr+q.nSize;
end;
BS_RESERVE:curr:=curr+PHYSICAL_PAGE_SIZE;
BS_COMMIT:Exit(False);
end;
end;
end;
///
function Tdlist.REMOVE_HEAD:Pdnode; inline;
begin
Result:=pHead;
if (pHead<>nil) then
begin
pHead:=pHead^.pNext;
if (pHead<>nil) then
begin
pHead^.pPrev:=nil;
end else
begin
pTail:=nil;
end;
Result^.pPrev:=nil;
Result^.pNext:=nil;
end;
end;
procedure Tdlist.INSERT_TAIL(e:Pdnode); inline;
begin
if (e=nil) then Exit;
if (e^.pPrev<>nil) or (e^.pNext<>nil) then Exit;
if (pHead=nil) then
begin
pHead:=e;
end else
begin
pTail^.pNext:=e;
e^.pPrev:=pTail;
end;
pTail:=e;
end;
procedure Tdlist.REMOVE(e:Pdnode); inline;
var
p,n:Pdnode;
begin
if (e=nil) then Exit;
if (pHead=e) then
begin
pHead:=pHead^.pNext;
if (pHead=nil) then pTail:=nil;
end else
if (pTail=e) then
begin
pTail:=pTail^.pPrev;
if (pTail=nil) then pHead:=nil;
end else
begin
p:=e^.pPrev;
n:=e^.pNext;
if (p<>nil) then p^.pNext:=n;
if (n<>nil) then n^.pPrev:=p;
end;
e^.pPrev:=nil;
e^.pNext:=nil;
end;
type
TBlockCompare=object
function c(const a,b:PBlock):Integer; static;
end;
function TBlockCompare.c(const a,b:PBlock):Integer; static;
begin
if (a^.pAddr=b^.pAddr) then
Result:=0
else
if (a^.pAddr<b^.pAddr) then
Result:=-1
else
Result:=1;
end;
type
TMemDirectAdrCompare=object
function c(const a,b:TBlock):Integer; static;
end;
function TMemDirectAdrCompare.c(const a,b:TBlock):Integer; static;
begin
if (a.pAddr=b.pAddr) then
Result:=0
else
if (a.pAddr<b.pAddr) then
Result:=-1
else
Result:=1;
end;
type
TDirectAdrSet=specialize T23treeSet<TBlock,TMemDirectAdrCompare>;
type
TBlockSet=specialize T23treeSet<PBlock,TBlockCompare>;
TPageMM=object
var
FLock:TRWLock;
FDirectSize:QWORD;
//FDirectAdrSet:TDirectAdrSet;
FMapBlockSet:TBlockSet;
//direct,num ,len
Falign:array[Boolean,0..3,1..3] of Tdlist;
Procedure Init;
function _new_big_block_d(addr:Pointer;len,alignment,direct:size_t;prot:Byte):Pointer;
function _new_64k_block_d(addr:Pointer;len,alignment,direct:size_t;prot:Byte):Pointer;
function _isfree_64k_block_d(block:PBlock64k):Boolean;
procedure _unmap_64k_block_d(block:PBlock64k);
procedure _map_64k_block_d(block:PBlock64k);
function _alloc_part_d(len,alignment,direct:size_t;prot:Byte):Pointer;
function _TryGetMapBlockByAddr(addr:Pointer;var _pblock:PBlock):Boolean;
procedure _DeleteBlockByAddr(addr:Pointer);
function _check_fixed(addr:Pointer;len:size_t;overwrite:Boolean):Boolean;
function _free_fixed(addr:Pointer;len:size_t):Boolean;
function _commit_fixed_d(addr:Pointer;len,direct:size_t;prot:Byte):Boolean;
function mmap_d(addr:Pointer;len,alignment,direct:size_t;prot:Byte;overwrite:Boolean):Pointer;
function unmap(addr:Pointer;len:size_t):Boolean;
function QueryProt(addr:Pointer;pStart,pEnd:PPointer;pProt:PInteger):Boolean;
function ChangeProt(addr:Pointer;len:QWORD;prot:Integer):Boolean;
end;
Procedure TPageMM.Init;
begin
FillChar(Self,SizeOf(TPageMM),0);
rwlock_init(FLock);
end;
function TPageMM._new_big_block_d(addr:Pointer;len,alignment,direct:size_t;prot:Byte):Pointer;
var
base:Pointer;
block:PBlockBig;
begin
Result:=nil;
base:=VirtualAllocAlign(addr,len,alignment,MEM_COMMIT or MEM_RESERVE,__map_prot_page(prot));
if (base=nil) then Exit;
block:=AllocMem(SizeOf(TBlockBig));
if (block=nil) then
begin
VirtualFree(base,0,MEM_RELEASE);
Exit;
end;
block^.pAddr:=base;
block^.nSize:=len;
block^.bType:=BT_DIRECT_BIG;
block^.direct:=direct;
block^.prot:=prot;
if _isgpu(prot) and (GpuMemCb.Alloc<>nil) then
begin
block^.Handle:=GpuMemCb.Alloc(base,len);
end;
Assert(block<>nil);
FMapBlockSet.Insert(block);
Result:=base;
end;
function TPageMM._new_64k_block_d(addr:Pointer;len,alignment,direct:size_t;prot:Byte):Pointer;
var
base:Pointer;
block:PBlock64k;
i,c:Byte;
begin
Result:=nil;
base:=VirtualAllocAlign(addr,GRANULAR_PAGE_SIZE,alignment,MEM_COMMIT or MEM_RESERVE,__map_prot_page(prot));
if (len<>GRANULAR_PAGE_SIZE) then
begin
VirtualFree(base+len,GRANULAR_PAGE_SIZE-len,MEM_DECOMMIT);
end;
if (base=nil) then Exit;
block:=AllocMem(SizeOf(TBlock64k));
if (block=nil) then
begin
VirtualFree(base,0,MEM_RELEASE);
Exit;
end;
block^.pAddr:=base;
block^.nSize:=GRANULAR_PAGE_SIZE;
block^.bType:=BT_DIRECT_64K;
For i:=0 to 3 do
begin
if (direct=INVALID_DIRECT) then
begin
block^.nodes[i].direct:=INVALID_DIRECT;
end else
begin
block^.nodes[i].direct:=direct+(i*LOGICAL_PAGE_SIZE);
end;
block^.nodes[i].info.id:=i;
block^.nodes[i].info.prot:=prot;
block^.nodes[i].info.state:=BS_COMMIT;
end;
c:=Get16kBlockCount(len);
if (c<4) then
begin
For i:=c to 3 do
begin
block^.nodes[i].direct:=0;
block^.nodes[i].info.prot:=0;
block^.nodes[i].info.state:=BS_FREE;
end;
_map_64k_block_d(block);
end;
Assert(block<>nil);
FMapBlockSet.Insert(block);
Result:=base;
end;
function TPageMM._isfree_64k_block_d(block:PBlock64k):Boolean;
var
i:Byte;
begin
Result:=True;
For i:=0 to 3 do
begin
if (block^.nodes[i].info.state=BS_COMMIT) then Exit(false);
end;
end;
procedure TPageMM._unmap_64k_block_d(block:PBlock64k);
var
i:Byte;
begin
For i:=0 to 3 do
begin
Falign[block^.nodes[i].direct<>INVALID_DIRECT,
i,
block^.nodes[i].info.len].
REMOVE(@block^.nodes[i]);
end;
end;
procedure TPageMM._map_64k_block_d(block:PBlock64k);
var
ip,ic:Byte;
begin
ip:=0;
While (ip<=3) do
begin
if (block^.nodes[ip].info.state<>BS_COMMIT) then
begin
ic:=1;
While (ip+ic<=3) do
begin
if (block^.nodes[ip+ic].info.state<>BS_COMMIT) then
Inc(ic)
else
Break;
end;
block^.nodes[ip].info.len:=ic;
Falign[block^.nodes[ip].direct<>INVALID_DIRECT,ip,ic].INSERT_TAIL(@block^.nodes[ip]);
end;
Inc(ip);
end;
end;
// //num ,len
// Falign_d:array[0..3,1..3] of Tdlist;
function TPageMM._alloc_part_d(len,alignment,direct:size_t;prot:Byte):Pointer;
var
block:PBlock64k;
node:PdnodeAdr;
i,b,n,count:Byte;
function _find_by_len_16(len:Byte):PdnodeAdr;
var
i:Byte;
begin
Result:=nil;
For i:=0 to 3 do
begin
Result:=PdnodeAdr(Falign[direct<>INVALID_DIRECT,i,len].REMOVE_HEAD);
if (Result<>nil) then Break;
end;
end;
function _find_by_len_32(len:Byte):PdnodeAdr;
begin
Result:=PdnodeAdr(Falign[direct<>INVALID_DIRECT,0,len].REMOVE_HEAD);
if (Result<>nil) then Exit;
Result:=PdnodeAdr(Falign[direct<>INVALID_DIRECT,2,len].REMOVE_HEAD);
end;
begin
count:=Get16kBlockCount(len);
node:=nil;
if (alignment<=16*1024) then
begin //16k
node:=_find_by_len_16(count);
end else
if (alignment<=32*1024) then
begin //32k
node:=_find_by_len_32(count);
if (node=nil) and (count<=2) then
begin
node:=PdnodeAdr(Falign[direct<>INVALID_DIRECT,1,count].REMOVE_HEAD);
if (node<>nil) then Inc(node);
end;
end else
begin //64k
node:=PdnodeAdr(Falign[direct<>INVALID_DIRECT,0,count].REMOVE_HEAD);
end;
if (node=nil) then //not found
begin
Result:=_new_64k_block_d(nil,len,alignment,direct,prot);
end else
begin
b:=node^.info.id;
block:=Pointer(PtrUint(@node[-b])-PtrUint(@PBlock64k(nil)^.nodes));
n:=b+count-1;
For i:=b to n do
begin
if (direct=INVALID_DIRECT) then
begin
block^.nodes[i].direct:=INVALID_DIRECT;
end else
begin
block^.nodes[i].direct:=direct+((i-b)*LOGICAL_PAGE_SIZE);
end;
block^.nodes[i].info.prot:=prot;
block^.nodes[i].info.state:=BS_COMMIT;
block^.nodes[i].info.len:=0;
end;
_map_64k_block_d(block);
Result:=block^.pAddr+(b*LOGICAL_PAGE_SIZE);
Result:=VirtualAlloc(Result,len,MEM_COMMIT,__map_prot_page(prot));
end;
end;
function TPageMM._TryGetMapBlockByAddr(addr:Pointer;var _pblock:PBlock):Boolean;
var
It:TBlockSet.Iterator;
i:Integer;
begin
Result:=False;
It:=FMapBlockSet.find_le(@addr);
//if (It.Item=nil) then Exit;
if (It.Item=nil) then
begin
Writeln('Memory dump:',HexStr(addr));
It:=FMapBlockSet.cbegin;
While (It.Item<>nil) do
begin
_pblock:=It.Item^;
if (_pblock<>nil) then
begin
Case _pblock^.bType of
BT_STACK:
begin
Writeln('[BT_STACK]');
Writeln(' pAddr:',HexStr(_pblock^.pAddr));
Writeln(' nSize:',HexStr(_pblock^.nSize,16));
end;
BT_DIRECT_BIG:
begin
Writeln('[BT_DIRECT_BIG]');
Writeln(' pAddr:',HexStr(_pblock^.pAddr));
Writeln(' nSize:',HexStr(_pblock^.nSize,16));
Writeln(' direct:',HexStr(PBlockBig(_pblock)^.direct,16));
Writeln(' Handle:',HexStr(PBlockBig(_pblock)^.Handle));
Writeln(' prot:',test_KP_flags(PBlockBig(_pblock)^.prot));
end;
BT_DIRECT_64K:
begin
Writeln('[BT_DIRECT_64K]');
Writeln(' pAddr:',HexStr(_pblock^.pAddr));
Writeln(' nSize:',HexStr(_pblock^.nSize,16));
For i:=0 to 3 do
begin
Writeln(' [node]:',i);
Writeln(' direct:' ,HexStr(PBlock64k(_pblock)^.nodes[i].direct,16));
Writeln(' info.id:' ,HexStr(PBlock64k(_pblock)^.nodes[i].info.id,2));
Writeln(' info.prot:' ,test_KP_flags(PBlock64k(_pblock)^.nodes[i].info.prot));
Writeln(' info.state:',PBlock64k(_pblock)^.nodes[i].info.state);
Writeln(' info.len:' ,PBlock64k(_pblock)^.nodes[i].info.len);
end;
end;
BT_PHYSIC_BIG:
begin
Writeln('[BT_PHYSIC_BIG]');
Writeln(' pAddr:',HexStr(_pblock^.pAddr));
Writeln(' nSize:',HexStr(_pblock^.nSize,16));
end;
BT_PHYSIC_64K:
begin
Writeln('[BT_PHYSIC_64K]');
Writeln(' pAddr:',HexStr(_pblock^.pAddr));
Writeln(' nSize:',HexStr(_pblock^.nSize,16));
end;
else;
end;
end;
It.Next;
end;
Writeln('------------');
Assert(false);
Exit;
end;
_pblock:=It.Item^;
if (_pblock=nil) then Exit;
if (_pblock^.pAddr>addr) or (_pblock^.pAddr+_pblock^.nSize<=addr) then
begin
_pblock:=nil;
Exit;
end;
Result:=True;
end;
procedure TPageMM._DeleteBlockByAddr(addr:Pointer);
var
It:TBlockSet.Iterator;
_pblock:PBlock;
begin
It:=FMapBlockSet.find(@addr);
if (It.Item=nil) then Exit;
_pblock:=It.Item^;
FMapBlockSet.erase(It);
if (_pblock<>nil) then FreeMem(_pblock);
end;
function TPageMM._check_fixed(addr:Pointer;len:size_t;overwrite:Boolean):Boolean;
var
curr:Pointer;
q:TBlock;
_pblock:PBlock;
begin
Result:=true;
curr:=addr;
repeat
q:=VirtualQueryBase(curr);
Case q.bType of
BS_FREE:
begin
curr:=q.pAddr+q.nSize;
end;
BS_RESERVE,
BS_COMMIT:
begin
if (q.bType=BS_COMMIT) and (not overwrite) then Exit(False);
if not _TryGetMapBlockByAddr(curr,_pblock) then Exit;
Case _pblock^.bType of
BT_DIRECT_BIG,
BT_DIRECT_64K:
begin
curr:=curr+LOGICAL_PAGE_SIZE;
end;
BT_PHYSIC_BIG,
BT_PHYSIC_64K:
begin
curr:=curr+PHYSICAL_PAGE_SIZE;
end;
else
Exit(False);
end;
end;
end;
until (curr>=addr+len);
end;
function TPageMM._free_fixed(addr:Pointer;len:size_t):Boolean;
var
curr:Pointer;
q:TBlock;
_pblock:PBlock;
i:Byte;
begin
Result:=true;
curr:=addr;
repeat
q:=VirtualQueryBase(curr);
Case q.bType of
BS_FREE:
begin
curr:=q.pAddr+q.nSize;
end;
BS_RESERVE,
BS_COMMIT:
begin
if not _TryGetMapBlockByAddr(curr,_pblock) then Exit;
Case _pblock^.bType of
BT_DIRECT_BIG:
begin
if (curr=q.pAddr) and (_pblock^.pAddr=q.pAddr) and (_pblock^.nSize=q.nSize) then
begin
if _isgpu(PBlockBig(_pblock)^.prot) and (GpuMemCb.Free<>nil) then
begin
GpuMemCb.Free(PBlockBig(_pblock)^.Handle);
end;
if not VirtualFree(q.pAddr,0,MEM_RELEASE) then Exit(False);
_DeleteBlockByAddr(q.pAddr);
curr:=q.pAddr+q.nSize;
end else
begin
VirtualFree(curr,LOGICAL_PAGE_SIZE,MEM_DECOMMIT);
curr:=curr+LOGICAL_PAGE_SIZE;
if VirtualIsFullReserve(q.pAddr,q.nSize) then
begin
if _isgpu(PBlockBig(_pblock)^.prot) and (GpuMemCb.Free<>nil) then
begin
GpuMemCb.Free(PBlockBig(_pblock)^.Handle);
end;
if not VirtualFree(q.pAddr,0,MEM_RELEASE) then Exit(False);
_DeleteBlockByAddr(q.pAddr);
curr:=q.pAddr+q.nSize;
end;
end;
end;
BT_PHYSIC_BIG:
begin
if (curr=q.pAddr) and (_pblock^.pAddr=q.pAddr) and (_pblock^.nSize=q.nSize) then
begin
if not VirtualFree(q.pAddr,0,MEM_RELEASE) then Exit(False);
_DeleteBlockByAddr(q.pAddr);
curr:=q.pAddr+q.nSize;
end else
begin
VirtualFree(curr,PHYSICAL_PAGE_SIZE,MEM_DECOMMIT);
curr:=curr+PHYSICAL_PAGE_SIZE;
if VirtualIsFullReserve(q.pAddr,q.nSize) then
begin
if not VirtualFree(q.pAddr,0,MEM_RELEASE) then Exit(False);
_DeleteBlockByAddr(q.pAddr);
curr:=q.pAddr+q.nSize;
end;
end;
end;
BT_DIRECT_64K:
if (q.bType=BS_COMMIT) then
begin
VirtualFree(curr,LOGICAL_PAGE_SIZE,MEM_DECOMMIT);
i:=Get16kBlockCount(curr-_pblock^.pAddr);
_unmap_64k_block_d(PBlock64k(_pblock));
if PBlock64k(_pblock)^.nodes[i].direct<>INVALID_DIRECT then
begin
PBlock64k(_pblock)^.nodes[i].direct:=0;
end;
PBlock64k(_pblock)^.nodes[i].info.prot:=0;
PBlock64k(_pblock)^.nodes[i].info.state:=BS_FREE;
if _isfree_64k_block_d(PBlock64k(_pblock)) then
begin
if not VirtualFree(_pblock^.pAddr,0,MEM_RELEASE) then Exit(False);
q.pAddr:=_pblock^.pAddr;
_DeleteBlockByAddr(q.pAddr);
end else
begin
_map_64k_block_d(PBlock64k(_pblock));
end;
curr:=curr+LOGICAL_PAGE_SIZE;
end else
begin
curr:=curr+LOGICAL_PAGE_SIZE;
end;
BT_PHYSIC_64K:Assert(False);
else
Exit(False);
end;
end;
end;
until (curr>=addr+len);
end;
function TPageMM._commit_fixed_d(addr:Pointer;len,direct:size_t;prot:Byte):Boolean;
var
base:Pointer;
curr:Pointer;
q:TBlock;
_pblock:PBlock;
i:Byte;
begin
Result:=true;
curr:=addr;
repeat
q:=VirtualQueryBase(curr);
Case q.bType of
BS_FREE:
begin
base:=curr;
curr:=q.pAddr+q.nSize;
if (curr>addr+len) then curr:=addr+len;
if _new_big_block_d(base,curr-base,0,direct,prot)=nil then Exit(False);
if (direct<>INVALID_DIRECT) then
begin
direct:=direct+(curr-base);
end;
end;
BS_RESERVE:
begin
if not _TryGetMapBlockByAddr(curr,_pblock) then Exit;
Case _pblock^.bType of
BT_DIRECT_BIG:
begin
if VirtualAlloc(curr,LOGICAL_PAGE_SIZE,MEM_COMMIT,__map_prot_page(prot))=nil then Exit(False);
curr:=curr+LOGICAL_PAGE_SIZE;
if (direct<>INVALID_DIRECT) then
begin
direct:=direct+LOGICAL_PAGE_SIZE;
end;
end;
BT_PHYSIC_BIG:
begin
if VirtualAlloc(curr,PHYSICAL_PAGE_SIZE,MEM_COMMIT,__map_prot_page(prot))=nil then Exit(False);
curr:=curr+PHYSICAL_PAGE_SIZE;
if (direct<>INVALID_DIRECT) then
begin
direct:=direct+PHYSICAL_PAGE_SIZE;
end;
end;
BT_DIRECT_64K:
begin
if VirtualAlloc(curr,LOGICAL_PAGE_SIZE,MEM_COMMIT,__map_prot_page(prot))=nil then Exit(False);
i:=Get16kBlockCount(curr-_pblock^.pAddr);
_unmap_64k_block_d(PBlock64k(_pblock));
PBlock64k(_pblock)^.nodes[i].direct:=direct;
PBlock64k(_pblock)^.nodes[i].info.prot:=prot;
PBlock64k(_pblock)^.nodes[i].info.state:=BS_COMMIT;
_map_64k_block_d(PBlock64k(_pblock));
curr:=curr+LOGICAL_PAGE_SIZE;
if (direct<>INVALID_DIRECT) then
begin
direct:=direct+LOGICAL_PAGE_SIZE;
end;
end;
BT_PHYSIC_64K:Assert(False);
else
Exit(False);
end;
end;
BS_COMMIT:Exit(False);
end;
until (curr>=addr+len);
end;
function TPageMM.mmap_d(addr:Pointer;len,alignment,direct:size_t;prot:Byte;overwrite:Boolean):Pointer;
begin
Result:=nil;
rwlock_wrlock(FLock);
if _isgpu(prot) then
begin
Result:=_new_big_block_d(addr,len,alignment,direct,prot);
end else
if (addr<>nil) then //fixed adr
begin
if _check_fixed(addr,len,overwrite) then
begin
if _free_fixed(addr,len) then
begin
if _commit_fixed_d(addr,len,direct,prot) then
Result:=addr;
end;
end;
end else
begin //any addr
if (alignment<=GRANULAR_PAGE_SIZE) and (len<=GRANULAR_PAGE_SIZE) then //64k block
begin
if (len=GRANULAR_PAGE_SIZE) then //full
begin
Result:=_new_64k_block_d(addr,len,alignment,direct,prot);
end else
begin
Result:=_alloc_part_d(len,alignment,direct,prot);
end;
end else
if (len<=GRANULAR_PAGE_SIZE) then //64k but big aligned
begin
Result:=_new_64k_block_d(addr,len,alignment,direct,prot);
end else
begin //big block
Result:=_new_big_block_d(addr,len,alignment,direct,prot);
end;
end;
rwlock_unlock(FLock);
end;
function TPageMM.unmap(addr:Pointer;len:size_t):Boolean;
begin
Result:=false;
rwlock_wrlock(FLock);
Result:=_check_fixed(addr,len,true);
if Result then
begin
Result:=_free_fixed(addr,len);
end;
rwlock_unlock(FLock);
end;
function TPageMM.QueryProt(addr:Pointer;pStart,pEnd:PPointer;pProt:PInteger):Boolean;
var
_pblock:PBlock;
i,b,e:Byte;
begin
Result:=False;
rwlock_rdlock(FLock);
if _TryGetMapBlockByAddr(addr,_pblock) then
begin
Result:=True;
Case _pblock^.bType of
BT_DIRECT_BIG,
BT_PHYSIC_BIG:
begin
if (pStart<>nil) then pStart^:=_pblock^.pAddr;
if (pEnd<>nil) then pEnd^ :=_pblock^.pAddr+_pblock^.nSize-1;
if (pProt<>nil) then pProt^ :=PBlockBig(_pblock)^.prot;
end;
BT_DIRECT_64K:
begin
i:=Get16kBlockCount(addr-_pblock^.pAddr);
b:=i;
repeat
if (b=0) then Break;
if (PBlock64k(_pblock)^.nodes[b-1].info.state<>PBlock64k(_pblock)^.nodes[i].info.state) then Break;
if (PBlock64k(_pblock)^.nodes[b-1].info.prot<>PBlock64k(_pblock)^.nodes[i].info.prot) then Break;
Dec(b);
until false;
e:=i;
repeat
if (e=3) then Break;
if (PBlock64k(_pblock)^.nodes[e+1].info.state<>PBlock64k(_pblock)^.nodes[i].info.state) then Break;
if (PBlock64k(_pblock)^.nodes[e+1].info.prot<>PBlock64k(_pblock)^.nodes[i].info.prot) then Break;
Inc(e);
until false;
if (pStart<>nil) then pStart^:=_pblock^.pAddr+(b*LOGICAL_PAGE_SIZE);
if (pEnd<>nil) then pEnd^ :=_pblock^.pAddr+(e*LOGICAL_PAGE_SIZE)-1;
if (pProt<>nil) then pProt^ := PBlock64k(_pblock)^.nodes[i].info.prot;
end;
BT_PHYSIC_64K:Assert(False);
end;
end;
rwlock_unlock(FLock);
end;
function __mprotect(addr:Pointer;len:size_t;prot:Integer):Integer;
Var
newprotect,oldprotect:DWORD;
begin
newprotect:=__map_prot_page(prot);
oldprotect:=0;
if not VirtualProtect(addr,len,newprotect,oldprotect) then
begin
Exit(-1);
end;
Result:=0;
end;
function TPageMM.ChangeProt(addr:Pointer;len:QWORD;prot:Integer):Boolean;
var
_pblock:PBlock;
begin
Result:=False;
rwlock_rdlock(FLock);
repeat
if _TryGetMapBlockByAddr(addr,_pblock) then
begin
if (_pblock^.nSize>len) then
begin
Result:=(__mprotect(addr,len,prot)=0);
Break;
end else
begin
Result:=(__mprotect(addr,_pblock^.nSize,prot)=0);
end;
if (len>=_pblock^.nSize) then
begin
len:=len-_pblock^.nSize;
addr:=addr+_pblock^.nSize;
end else
begin
Break;
end;
end else
begin
if (len>=PHYSICAL_PAGE_SIZE) then
begin
len:=len-PHYSICAL_PAGE_SIZE;
addr:=addr+PHYSICAL_PAGE_SIZE;
end else
begin
Break;
end;
end;
until (len=0);
rwlock_unlock(FLock);
end;
///////
Var
PageMM:TPageMM;
Function TryGetGpuMemBlockByAddr(addr:Pointer;var block:TGpuMemBlock):Boolean;
var
_pblock:PBlock;
begin
Result:=False;
rwlock_rdlock(PageMM.FLock);
if PageMM._TryGetMapBlockByAddr(addr,_pblock) then
begin
Case _pblock^.bType of
BT_DIRECT_BIG:
if _isgpu(PBlockBig(_pblock)^.prot) then
begin
block.pAddr :=_pblock^.pAddr;
block.nSize :=_pblock^.nSize;
block.Handle:=PBlockBig(_pblock)^.Handle;
Result:=true;
end;
end;
end;
rwlock_unlock(PageMM.FLock);
end;
Procedure RegistredStack;
var
block:PBlock;
begin
rwlock_wrlock(PageMM.FLock);
block:=AllocMem(SizeOf(TBlock));
if (block=nil) then Exit;
block^.pAddr:=StackBottom;
block^.nSize:=StackLength;
block^.bType:=BT_STACK;
PageMM.FMapBlockSet.Insert(block);
rwlock_unlock(PageMM.FLock);
end;
Procedure UnRegistredStack;
begin
rwlock_wrlock(PageMM.FLock);
PageMM._DeleteBlockByAddr(StackBottom);
rwlock_unlock(PageMM.FLock);
end;
function ps4_sceKernelGetDirectMemorySize:Int64; SysV_ABI_CDecl;
begin
Result:=SCE_KERNEL_MAIN_DMEM_SIZE;
end;
function ps4_getpagesize:Integer; SysV_ABI_CDecl;
begin
Result:=PHYSICAL_PAGE_SIZE;
end;
function _test_mtype(mtype:Integer):Boolean; inline;
begin
Case mtype of
SCE_KERNEL_WB_ONION ,
SCE_KERNEL_WC_GARLIC,
SCE_KERNEL_WB_GARLIC:Result:=True;
else
Result:=False;
end;
end;
//direct
function _sceKernelAllocateDirectMemory(
searchStart:QWORD;
searchEnd:QWORD;
length:QWORD;
alignment:QWORD;
memoryType:Integer;
physicalAddrDest:PQWORD):Integer;
begin
Result:=EINVAL;
Writeln('srchd:',HexStr(searchStart,10),'..',HexStr(searchEnd,10),' len:',HexStr(length,10));
Writeln('align:',HexStr(alignment,10),' ','mType:',str_mem_type(memoryType));
if (physicalAddrDest=nil) or (length=0) or (searchEnd<=searchStart) then Exit;
if (searchEnd>SCE_KERNEL_MAIN_DMEM_SIZE) then Exit;
if (alignment=0) then alignment:=LOGICAL_PAGE_SIZE;
if not IsAlign(length ,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(alignment,LOGICAL_PAGE_SIZE) then Exit;
if not IsPowerOfTwo(alignment) then Exit;
if (fastIntLog2(alignment)>31) then Exit;
if not _test_mtype(memoryType) then Exit;
_sig_lock;
rwlock_wrlock(PageMM.FLock); //rw
Result:=DirectManager.Alloc(searchStart,searchEnd,length,alignment,Byte(memoryType),physicalAddrDest^);
rwlock_unlock(PageMM.FLock);
_sig_unlock;
end;
function _sceKernelAllocateMainDirectMemory(
length:QWORD;
alignment:QWORD;
memoryType:Integer;
physicalAddrDest:PQWORD):Integer;
begin
Result:=EINVAL;
Writeln('srchm: len:',HexStr(length,10));
Writeln('align:',HexStr(alignment,10),' ','mType:',str_mem_type(memoryType));
if (physicalAddrDest=nil) or (length=0) then Exit;
if (alignment=0) then alignment:=LOGICAL_PAGE_SIZE;
if not IsAlign(length ,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(alignment,LOGICAL_PAGE_SIZE) then Exit;
if not IsPowerOfTwo(alignment) then Exit;
if (fastIntLog2(alignment)>31) then Exit;
if not _test_mtype(memoryType) then Exit;
_sig_lock;
rwlock_wrlock(PageMM.FLock); //rw
Result:=DirectManager.Alloc(length,alignment,Byte(memoryType),physicalAddrDest^);
rwlock_unlock(PageMM.FLock);
_sig_unlock;
end;
function _sceKernelAvailableDirectMemorySize(
searchStart:QWORD;
searchEnd:QWORD;
alignment:QWORD;
physAddrOut:PQWORD;
sizeOut:PQWORD):Integer;
var
FAdrOut,FSizeOut:QWORD;
begin
Result:=EINVAL;
if (physAddrOut=nil) or (sizeOut=nil) or (searchEnd<=searchStart) then Exit;
if (searchEnd>SCE_KERNEL_MAIN_DMEM_SIZE) then Exit;
if (alignment=0) then alignment:=LOGICAL_PAGE_SIZE;
if not IsAlign(searchStart,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(searchEnd ,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(alignment ,LOGICAL_PAGE_SIZE) then Exit;
if not IsPowerOfTwo(alignment) then Exit;
if (fastIntLog2(alignment)>31) then Exit;
FAdrOut :=0;
FSizeOut:=0;
_sig_lock;
rwlock_rdlock(PageMM.FLock); //r
Result:=DirectManager.CheckedAvailable(searchStart,searchEnd,alignment,FAdrOut,FSizeOut);
rwlock_unlock(PageMM.FLock);
_sig_unlock;
if (Result=0) then
begin
if (physAddrOut<>nil) then
begin
physAddrOut^:=FAdrOut;
end;
if (sizeOut<>nil) then
begin
sizeOut^:=FSizeOut;
end;
end;
end;
function _sceKernelDirectMemoryQuery(
offset:QWORD;
flags:Integer;
info:pSceKernelDirectMemoryQueryInfo;
infoSize:QWORD):Integer;
var
ROut:TDirectAdrNode;
begin
Result:=EINVAL;
if (info=nil) or (infoSize<>SizeOf(SceKernelDirectMemoryQueryInfo)) then Exit;
if not IsAlign(offset,LOGICAL_PAGE_SIZE) then Exit;
ROut:=Default(TDirectAdrNode);
_sig_lock;
rwlock_rdlock(PageMM.FLock); //r
Result:=DirectManager.Query(offset,(flags=SCE_KERNEL_DMQ_FIND_NEXT),ROut);
rwlock_unlock(PageMM.FLock);
_sig_unlock;
if (Result<>0) then
begin
info^:=Default(SceKernelDirectMemoryQueryInfo);
info^.start:=ROut.Offset;
info^.__end:=ROut.Offset+ROut.Size;
info^.mType:=ROut.F.mtype;
end;
end;
function _sceKernelGetDirectMemoryType(
start:QWORD;
memoryTypeOut:PInteger;
regionStartOut:PQWORD;
regionEndOut:PQWORD):Integer;
var
ROut:TDirectAdrNode;
begin
Result:=EINVAL;
if (memoryTypeOut=nil) then Exit;
if (regionStartOut=nil) then Exit;
if (regionEndOut=nil) then Exit;
start:=AlignDw(start,PHYSICAL_PAGE_SIZE);
ROut:=Default(TDirectAdrNode);
_sig_lock;
rwlock_rdlock(PageMM.FLock); //r
Result:=DirectManager.QueryMType(start,ROut);
rwlock_unlock(PageMM.FLock);
_sig_unlock;
if (Result<>0) then
begin
memoryTypeOut ^:=ROut.F.mtype;
regionStartOut^:=ROut.Offset;
regionEndOut ^:=ROut.Offset+ROut.Size;
end;
end;
function _sceKernelCheckedReleaseDirectMemory(start,len:QWORD):Integer;
begin
Result:=EINVAL;
if not IsAlign(start,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(len ,LOGICAL_PAGE_SIZE) then Exit;
_sig_lock;
rwlock_rdlock(PageMM.FLock); //r
Result:=DirectManager.CheckedRelease(start,len);
rwlock_unlock(PageMM.FLock);
_sig_unlock;
end;
function _sceKernelReleaseDirectMemory(start,len:QWORD):Integer;
begin
Result:=EINVAL;
if not IsAlign(start,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(len ,LOGICAL_PAGE_SIZE) then Exit;
_sig_lock;
rwlock_wrlock(PageMM.FLock); //rw
Result:=DirectManager.Release(start,len);
rwlock_unlock(PageMM.FLock);
_sig_unlock;
end;
//
function ps4_sceKernelAllocateDirectMemory(
searchStart:QWORD;
searchEnd:QWORD;
length:QWORD;
alignment:QWORD;
memoryType:Integer;
physicalAddrDest:PQWORD):Integer; SysV_ABI_CDecl;
begin
Result:=_sceKernelAllocateDirectMemory(
searchStart,
searchEnd,
length,
alignment,
memoryType,
physicalAddrDest);
if (Result<>0) then
begin
Writeln(StdErr,'[WARN]:sceKernelAllocateDirectMemory:',Result);
end;
_set_errno(Result);
Result:=px2sce(Result);
end;
function ps4_sceKernelAllocateMainDirectMemory(
length:QWORD;
alignment:QWORD;
memoryType:Integer;
physicalAddrDest:PQWORD):Integer; SysV_ABI_CDecl;
begin
Result:=_sceKernelAllocateMainDirectMemory(
length,
alignment,
memoryType,
physicalAddrDest);
if (Result<>0) then
begin
Writeln(StdErr,'[WARN]:sceKernelAllocateMainDirectMemory:',Result);
end;
_set_errno(Result);
Result:=px2sce(Result);
end;
function ps4_sceKernelAvailableDirectMemorySize(
searchStart:QWORD;
searchEnd:QWORD;
alignment:QWORD;
physAddrOut:PQWORD;
sizeOut:PQWORD):Integer; SysV_ABI_CDecl;
begin
Result:=_sceKernelAvailableDirectMemorySize(
searchStart,
searchEnd,
alignment,
physAddrOut,
sizeOut);
if (Result<>0) then
begin
Writeln(StdErr,'[WARN]:sceKernelAvailableDirectMemorySize:',Result);
end;
_set_errno(Result);
Result:=px2sce(Result);
end;
function ps4_sceKernelDirectMemoryQuery(
offset:QWORD;
flags:Integer;
info:pSceKernelDirectMemoryQueryInfo;
infoSize:QWORD):Integer; SysV_ABI_CDecl;
begin
Result:=_sceKernelDirectMemoryQuery(
offset,
flags,
info,
infoSize);
if (Result<>0) then
begin
Writeln(StdErr,'[WARN]:sceKernelDirectMemoryQuery:',Result);
end;
_set_errno(Result);
Result:=px2sce(Result);
end;
function ps4_sceKernelGetDirectMemoryType(
start:QWORD;
memoryTypeOut:PInteger;
regionStartOut:PQWORD;
regionEndOut:PQWORD):Integer; SysV_ABI_CDecl;
begin
Result:=_sceKernelGetDirectMemoryType(
start,
memoryTypeOut,
regionStartOut,
regionEndOut);
if (Result<>0) then
begin
Writeln(StdErr,'[WARN]:sceKernelGetDirectMemoryType:',Result);
end;
_set_errno(Result);
Result:=px2sce(Result);
end;
function ps4_sceKernelCheckedReleaseDirectMemory(start,len:QWORD):Integer; SysV_ABI_CDecl;
begin
Result:=_sceKernelCheckedReleaseDirectMemory(start,len);
if (Result<>0) then
begin
Writeln(StdErr,'[WARN]:sceKernelCheckedReleaseDirectMemory:',Result);
end;
_set_errno(Result);
Result:=px2sce(Result);
end;
function ps4_sceKernelReleaseDirectMemory(start,len:QWORD):Integer; SysV_ABI_CDecl;
begin
Result:=_sceKernelReleaseDirectMemory(start,len);
if (Result<>0) then
begin
Writeln(StdErr,'[WARN]:sceKernelReleaseDirectMemory:',Result);
end;
_set_errno(Result);
Result:=px2sce(Result);
end;
//mapping
//flag:MAP_VOID fd=-1 //reserve
//flag:MAP_ANON fd=-1 //flex
//flag:MAP_SHARED fd=/dev/dmem%d offset=physicalAddr //direct
function __mmap(addr:Pointer;len,align:size_t;prot,flags,fd:Integer;offset:size_t;var res:Pointer):Integer;
begin
Result:=EINVAL;
if not IsAlign(addr ,PHYSICAL_PAGE_SIZE) then Exit;
if not IsAlign(len ,PHYSICAL_PAGE_SIZE) then Exit;
if not IsAlign(offset,PHYSICAL_PAGE_SIZE) then Exit;
if (align<PHYSICAL_PAGE_SIZE) then align:=PHYSICAL_PAGE_SIZE;
_sig_lock;
rwlock_wrlock(PageMM.FLock); //rw
if (flags and MAP_VOID)<>0 then //reserved
begin
flags:=flags and (not MAP_SHARED);
Result:=VirtualManager.mmap(addr,len,align,prot,flags,fd,offset,res);
end else
if (flags and MAP_ANON)<>0 then //flex
begin
Result:=VirtualManager.mmap(addr,len,align,prot,flags,fd,offset,res);
end else
if (flags and MAP_SHARED)<>0 then
begin
if (fd>=0) then
begin
if (fd=0) then //direct (psevdo dmem fd=0)
begin
Result:=DirectManager.CheckedMMap(offset,len);
if (Result=0) then
begin
Result:=VirtualManager.mmap(addr,len,align,prot,flags,fd,offset,res);
if (Result=0) then
begin
Result:=DirectManager.mmap_addr(offset,len,addr);
end;
end;
end else
begin //map file
Result:=VirtualManager.mmap(addr,len,align,prot,flags,fd,offset,res);
end;
end;
end;
rwlock_unlock(PageMM.FLock);
_sig_unlock;
end;
function __sys_mmap_dmem(
addr:Pointer;
length:QWORD;
alignment:QWORD;
mtype,prots,flags:Integer;
physicalAddr:QWORD;
var res:Pointer):Integer;
begin
Result:=0;
_sig_lock;
rwlock_wrlock(PageMM.FLock); //rw
Result:=DirectManager.CheckedMMap(physicalAddr,length);
if (Result=0) then
begin
flags:=flags or MAP_SHARED;
Result:=VirtualManager.mmap(addr,length,alignment,prots,flags,0,physicalAddr,res);
if (Result=0) then
begin
Result:=DirectManager.mmap_addr(physicalAddr,length,addr,mtype);
end;
end;
rwlock_unlock(PageMM.FLock);
_sig_unlock;
end;
function __munmap(addr:Pointer;len:size_t):Integer;
begin
Result:=VirtualManager.Release(addr,len);
end;
function _munmap(addr:Pointer;len:size_t):Integer;
begin
_sig_lock;
rwlock_wrlock(PageMM.FLock); //rw
Result:=VirtualManager.Release(addr,len);
rwlock_unlock(PageMM.FLock);
_sig_unlock;
end;
function __release_direct(Offset,Size:QWORD):Integer;
begin
Result:=DirectManager.Release(Offset,Size);
end;
function _sceKernelMapFlexibleMemory(
virtualAddrDest:PPointer;
length:QWORD;
prots,flags:Integer):Integer;
var
addr:Pointer;
begin
Result:=SCE_KERNEL_ERROR_EINVAL;
if ((flags and $ffbfff6f)<>0) then Exit;
if ((prots and $ffffffc8)<>0) then Exit;
if (length<LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(length,LOGICAL_PAGE_SIZE) then Exit;
addr:=virtualAddrDest^;
if not IsAlign(addr,LOGICAL_PAGE_SIZE) then Exit;
if (((flags and MAP_FIXED) <> 0) and (addr=nil)) then
begin
if (SDK_VERSION > $16fffff) then
begin
Exit(SCE_KERNEL_ERROR_EINVAL);
end;
flags:=flags and $ffffffef;
Writeln('[WARNING] map(addr=0, flags=MAP_FIXED)');
end;
if (addr=nil) then
begin
addr:=Pointer($880000000);
end;
Result:=__mmap(addr,length,0,prots,flags or MAP_ANON,-1,0,addr);
if (Result=0) then
begin
virtualAddrDest^:=addr;
Result:=0;
end;
end;
function _sceKernelReserveVirtualRange(
virtualAddrDest:PPointer;
length:QWORD;
flags:Integer;
alignment:QWORD):Integer;
var
addr:Pointer;
begin
Result:=SCE_KERNEL_ERROR_EINVAL;
if ((flags and $ffbfff6f)<>0) then Exit;
if (length<LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(length ,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(alignment,LOGICAL_PAGE_SIZE) then Exit;
if not IsPowerOfTwo(alignment) then Exit;
if (alignment<LOGICAL_PAGE_SIZE) then alignment:=LOGICAL_PAGE_SIZE;
if (fastIntLog2(alignment)>31) then Exit;
addr:=virtualAddrDest^;
if not IsAlign(addr,LOGICAL_PAGE_SIZE) then Exit;
if (((flags and MAP_FIXED) <> 0) and (addr=nil)) then
begin
if (SDK_VERSION > $16fffff) then
begin
Exit(SCE_KERNEL_ERROR_EINVAL);
end;
flags:=flags and $ffffffef;
Writeln('[WARNING] map(addr=0, flags=MAP_FIXED)');
end;
Result:=__mmap(addr,length,alignment,0,flags or MAP_VOID or MAP_SHARED,-1,0,addr);
if (Result=0) then
begin
virtualAddrDest^:=addr;
Result:=0;
end;
end;
function _sceKernelMapDirectMemory2(
virtualAddrDest:PPointer;
length:QWORD;
mtype,prots,flags:Integer;
physicalAddr:QWORD;
alignment:QWORD):Integer;
var
addr:Pointer;
begin
Result:=SCE_KERNEL_ERROR_EINVAL;
if ((flags and $1f000000)<>0) then Exit;
if ((prots and $ffffffc8)<>0) then Exit;
if (length<LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(length ,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(physicalAddr,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(alignment ,LOGICAL_PAGE_SIZE) then Exit;
if not IsPowerOfTwo(alignment) then Exit;
if (alignment<LOGICAL_PAGE_SIZE) then alignment:=LOGICAL_PAGE_SIZE;
if (fastIntLog2(alignment)>31) then Exit;
addr:=virtualAddrDest^;
if not IsAlign(addr,LOGICAL_PAGE_SIZE) then Exit;
Result:=__sys_mmap_dmem(addr,length,alignment,mtype,prots,flags,physicalAddr,addr);
if (Result=0) then
begin
virtualAddrDest^:=addr;
Result:=0;
end;
end;
function _sceKernelMapDirectMemory(
virtualAddrDest:PPointer;
length:QWORD;
prots,flags:Integer;
physicalAddr:QWORD;
alignment:QWORD):Integer;
var
addr:Pointer;
_flags:Integer;
begin
Result:=SCE_KERNEL_ERROR_EINVAL;
if ((physicalAddr < $3000000000) or (physicalAddr > $301fffffff)) and
((flags and SCE_KERNEL_MAP_DMEM_COMPAT)=0) and (SDK_VERSION > $24fffff) then
begin
Result:=_sceKernelMapDirectMemory2(virtualAddrDest,length,-1,prots,flags,physicalAddr,alignment);
Exit;
end;
if ((flags and $1f000000)<>0) then Exit;
if ((prots and $ffffffc8)<>0) then Exit;
if (length<LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(length ,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(physicalAddr,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(alignment ,LOGICAL_PAGE_SIZE) then Exit;
if not IsPowerOfTwo(alignment) then Exit;
if (alignment<LOGICAL_PAGE_SIZE) then alignment:=LOGICAL_PAGE_SIZE;
if (fastIntLog2(alignment)>31) then Exit;
addr:=virtualAddrDest^;
if not IsAlign(addr,LOGICAL_PAGE_SIZE) then Exit;
_flags:=flags and $fffffbff;
if (((flags and MAP_FIXED) <> 0) and (addr=nil)) then
begin
if (SDK_VERSION > $16fffff) then
begin
Exit(SCE_KERNEL_ERROR_EINVAL);
end;
_flags:=flags and $fffffbef;
Writeln('[WARNING] map(addr=0, flags=MAP_FIXED)');
end;
if (addr=nil) then
begin
addr:=Pointer($880000000);
end;
Result:=__mmap(addr,length,alignment,prots,_flags or MAP_SHARED,0,physicalAddr,addr);
if (Result=0) then
begin
virtualAddrDest^:=addr;
Result:=0;
end;
end;
////
////
function ps4_sceKernelMapDirectMemory(
virtualAddrDest:PPointer;
length:QWORD;
protections:Integer;
flags:Integer;
physicalAddr:QWORD;
alignment:QWORD):Integer; SysV_ABI_CDecl;
var
//flProtect:DWORD;
R:Pointer;
begin
if (virtualAddrDest=nil) or (length=0) then Exit(SCE_KERNEL_ERROR_EINVAL);
//Assert(flags=0);
//Writeln('AddrSrc:',HexStr(virtualAddrDest^));
Writeln('length:',HexStr(length,16),' ',
test_KP_flags(protections),' ',
'flags:',flags);
Writeln('length:',HexStr(length,16),' ',
'physicalAddr:',HexStr(physicalAddr,16),' ',
'alignment:',HexStr(alignment,16));
if (alignment=0) then alignment:=LOGICAL_PAGE_SIZE;
if not IsAlign(virtualAddrDest^,LOGICAL_PAGE_SIZE) then Exit(SCE_KERNEL_ERROR_EINVAL);
if not IsAlign(length ,LOGICAL_PAGE_SIZE) then Exit(SCE_KERNEL_ERROR_EINVAL);
if not IsAlign(alignment,LOGICAL_PAGE_SIZE) then Exit(SCE_KERNEL_ERROR_EINVAL);
if not IsPowerOfTwo(alignment) then Exit(SCE_KERNEL_ERROR_EINVAL);
if (flags and SCE_KERNEL_MAP_FIXED)<>0 then
begin
R:=virtualAddrDest^;
end else
begin
R:=nil;
end;
_sig_lock;
R:=PageMM.mmap_d(R,length,alignment,physicalAddr,protections,(flags and SCE_KERNEL_MAP_NO_OVERWRITE)=0);
_sig_unlock;
//Writeln('alloc:',HexStr(R),'..',HexStr(R+length));
virtualAddrDest^:=R;
if (R=nil) then
begin
Exit(SCE_KERNEL_ERROR_ENOMEM);
end;
if _isgpu(protections) then
begin
Writeln('GPU:',HexStr(R),'..',HexStr(R+length));
end;
Assert(IsAlign(R,alignment),'sceKernelMapDirectMemory not aligned!');
Result:=0;
end;
function ps4_sceKernelMapNamedFlexibleMemory(
virtualAddrDest:PPointer;
length:QWORD;
protections:Integer;
flags:Integer;
name:PChar):Integer; SysV_ABI_CDecl;
var
//flProtect:DWORD;
R:Pointer;
begin
Result:=SCE_KERNEL_ERROR_EINVAL;
if (virtualAddrDest=nil) or (length=0) then Exit;
//Assert(flags=0);
//Writeln('AddrSrc:',HexStr(virtualAddrDest^));
Writeln('length:',HexStr(length,16),' ',
test_KP_flags(protections),' ',
'flags:',flags);
Writeln('length:',HexStr(length,16),' ',
'name:',name);
if not IsAlign(virtualAddrDest^,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(length,LOGICAL_PAGE_SIZE) then Exit;
if (flags and SCE_KERNEL_MAP_FIXED)<>0 then
begin
R:=virtualAddrDest^;
end else
begin
R:=nil;
end;
_sig_lock;
R:=PageMM.mmap_d(R,length,0,INVALID_DIRECT,protections,(flags and SCE_KERNEL_MAP_NO_OVERWRITE)=0);
_sig_unlock;
Writeln('alloc:',HexStr(R),'..',HexStr(R+length));
virtualAddrDest^:=R;
if (R=nil) then
begin
Exit(SCE_KERNEL_ERROR_ENOMEM);
end;
if _isgpu(protections) then
begin
Writeln('GPU:',HexStr(R),'..',HexStr(R+length));
end;
Result:=0;
end;
function ps4_sceKernelMapFlexibleMemory(
virtualAddrDest:PPointer;
length:QWORD;
protections:Integer;
flags:Integer):Integer; SysV_ABI_CDecl;
var
//flProtect:DWORD;
R:Pointer;
begin
Result:=SCE_KERNEL_ERROR_EINVAL;
if (virtualAddrDest=nil) or (length=0) then Exit;
//Assert(flags=0);
//Writeln('AddrSrc:',HexStr(virtualAddrDest^));
Writeln('length:',HexStr(length,16),' ',
test_KP_flags(protections),' ',
'flags:',flags);
Writeln('length:',HexStr(length,16));
if not IsAlign(virtualAddrDest^,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(length,LOGICAL_PAGE_SIZE) then Exit;
if (flags and SCE_KERNEL_MAP_FIXED)<>0 then
begin
R:=virtualAddrDest^;
end else
begin
R:=nil;
end;
_sig_lock;
R:=PageMM.mmap_d(R,length,0,INVALID_DIRECT,protections,(flags and SCE_KERNEL_MAP_NO_OVERWRITE)=0);
_sig_unlock;
//Writeln('alloc:',HexStr(R),'..',HexStr(R+length));
virtualAddrDest^:=R;
if (R=nil) then
begin
Exit(SCE_KERNEL_ERROR_ENOMEM);
end;
if _isgpu(protections) then
begin
Writeln('GPU:',HexStr(R),'..',HexStr(R+length));
end;
Result:=0;
end;
//MEMORY_BASIC_INFORMATION = record
// BaseAddress : PVOID; addr to check
// AllocationBase : PVOID; addr to begin region
// AllocationProtect : DWORD; R/W/E
// RegionSize : PTRUINT; Full size-( (addr to begin)-(addr to check) )
// State : DWORD; MEM_COMMIT or MEM_RESERVE
// Protect : DWORD; R/W/E
// _Type : DWORD; MEM_PRIVATE
//end;
//flex
function ps4_sceKernelMunmap(addr:Pointer;len:size_t):Integer; SysV_ABI_CDecl;
begin
Result:=SCE_KERNEL_ERROR_EINVAL;
if (addr=nil) or (len=0) then Exit;
if not IsAlign(addr,LOGICAL_PAGE_SIZE) then Exit;
if not IsAlign(len,LOGICAL_PAGE_SIZE) then Exit;
_sig_lock;
if PageMM.unmap(addr,len) then Result:=0;
_sig_unlock;
end;
//flex
function ps4_sceKernelQueryMemoryProtection(addr:Pointer;pStart,pEnd:PPointer;pProt:PInteger):Integer; SysV_ABI_CDecl;
begin
Result:=SCE_KERNEL_ERROR_EACCES;
//Writeln(HexStr(addr));
//addr:=AlignDw(addr,LOGICAL_PAGE_SIZE);
_sig_lock;
if PageMM.QueryProt(addr,pStart,pEnd,pProt) then Result:=0;
_sig_unlock;
end;
function ps4_sceKernelVirtualQuery(addr:Pointer;
flags:Integer;
info:pSceKernelVirtualQueryInfo;
infoSize:QWORD):Integer; SysV_ABI_CDecl;
begin
Result:=0;
Assert(false,'TODO');
end;
function ps4_sceKernelMprotect(addr:Pointer;len:QWORD;prot:Integer):Integer; SysV_ABI_CDecl;
begin
Result:=SCE_KERNEL_ERROR_EINVAL;
_sig_lock;
if PageMM.ChangeProt(addr,len,prot) then Result:=0;
_sig_unlock;
end;
function ps4_sceKernelSetVirtualRangeName(addr:Pointer;len:QWORD;name:Pchar):Integer; SysV_ABI_CDecl;
begin
Writeln('sceKernelSetVirtualRangeName:',HexStr(addr),':',len,':',name);
Result:=0;
end;
function ps4_mmap(addr:Pointer;len:size_t;prot,flags:Integer;fd:Integer;offset:size_t):Pointer; SysV_ABI_CDecl;
Var
map:Pointer;
protect:DWORD;
begin
map:=MAP_FAILED;
if (fd<>-1) {or ((flags and MAP_ANONYMOUS)=0)} then
begin
SetLastError(EBADF);
Result:=MAP_FAILED;
Exit;
end;
if (not IsAlign(addr,PHYSICAL_PAGE_SIZE)) or
(not IsAlign(len,PHYSICAL_PAGE_SIZE)) or
(not IsAlign(offset,PHYSICAL_PAGE_SIZE)) then
begin
SetLastError(EINVAL);
Result:=MAP_FAILED;
Exit;
end;
protect:=__map_prot_page(prot);
SetLastError(0);
if (len=0) or
// Unsupported flag combinations
((flags and MAP_FIXED)<>0) then
// Usupported protection combinations
//(prot=PROT_EXEC) then
begin
SetLastError(EINVAL);
Result:=MAP_FAILED;
Exit;
end;
_sig_lock;
map:=VirtualAlloc(addr,len,MEM_COMMIT or MEM_RESERVE,Protect);
_sig_unlock;
if (map=nil) then
begin
Result:=MAP_FAILED;
Exit;
end;
Result:=map;
end;
function ps4_munmap(addr:Pointer;len:size_t):Integer; SysV_ABI_CDecl;
var
Info:TMemoryBasicInformation;
begin
Result:=-1;
if (addr=nil) or (len=0) then Exit;
if not IsAlign(len,PHYSICAL_PAGE_SIZE) then Exit;
Info:=Default(TMemoryBasicInformation);
_sig_lock;
if (VirtualQuery(addr,Info,len)=0) then
begin
_sig_unlock;
Writeln('GetLastError:',GetLastError);
Exit;
end;
_sig_unlock;
if (Info._Type=MEM_FREE) then
begin
Writeln('GetLastError:',GetLastError);
Exit;
end;
Assert((Info.BaseAddress=Info.AllocationBase) and (Info.RegionSize=len),'partial unmap not impliment!');
_sig_lock;
if not VirtualFree(addr,0,MEM_RELEASE) then
begin
_sig_unlock;
Writeln('GetLastError:',GetLastError);
Exit;
end;
_sig_unlock;
Result:=0;
end;
function ps4_msync(addr:Pointer;len:size_t;flags:Integer):Integer; SysV_ABI_CDecl;
begin
//Writeln('msync:',HexStr(addr));
System.ReadWriteBarrier;
Result:=0;
end;
function ps4_mprotect(addr:Pointer;len:size_t;prot:Integer):Integer; SysV_ABI_CDecl;
begin
_sig_lock;
Result:=__mprotect(addr,len,prot);
_sig_unlock;
end;
initialization
DirectManager :=TDirectManager .Create;
DirectManager .OnMemoryUnmapCb:=@__munmap;
VirtualManager:=TVirtualManager.Create($400000,$3FFFFFFFF);
VirtualManager.OnDirectUnmapCb:=@__release_direct;
PageMM.init;
end.