FPPS4/sys/md/md_map.pas

695 lines
15 KiB
Plaintext

unit md_map;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
ntapi,
vm,
vmparam,
windows;
const
MD_PAGE_SHIFT = 12;
MD_PAGE_SIZE = 4*1024;
MD_PAGE_MASK = MD_PAGE_SIZE-1;
MD_ALLOC_GRANULARITY= 64*1024;
MD_MAP_FIXED =$100;
MD_MAP_RESERVED =$200;
MD_MAP_ALIGN_SHIFT=24;
function MD_MAP_ALIGN(align:QWORD):DWORD; inline;
const
VM_RW =VM_PROT_READ or VM_PROT_WRITE;
VM_RX =VM_PROT_READ or VM_PROT_EXECUTE;
VM_RWX=VM_PROT_READ or VM_PROT_WRITE or VM_PROT_EXECUTE;
function md_placeholder_mmap (var base:Pointer;size:QWORD;flags:DWORD=0;hProcess:THandle=NtCurrentProcess):Integer;
function md_placeholder_unmap (base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer;
function md_placeholder_split (base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer; inline;
function md_placeholder_union (base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer; inline;
function md_placeholder_commit (base:Pointer;size:QWORD;prot:DWORD;hProcess:THandle=NtCurrentProcess):Integer;
function md_placeholder_commit (base:Pointer;size:QWORD;prot:DWORD;fd:THandle;offset:QWORD;hProcess:THandle=NtCurrentProcess):Integer;
function md_placeholder_decommit(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer;
function md_memfd_create(var hMem:THandle;size:QWORD;maxprot:Byte):Integer;
function md_memfd_open (var hMem:THandle;hFile:THandle;maxprot:Byte):Integer;
function md_memfd_close (hMem:THandle):Integer; inline;
function md_protect (base:Pointer;size:QWORD;prot:Integer;hProcess:THandle=NtCurrentProcess):Integer;
function md_dontneed(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer; inline;
function md_activate(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer; inline;
function md_mmap (var base:Pointer;size:QWORD;prot:DWORD;fd:THandle=0;offset:QWORD=0;hProcess:THandle=NtCurrentProcess):Integer;
function md_unmap(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer;
function kmem_alloc(size:QWORD;prot:DWORD):Pointer;
procedure kmem_free (base:Pointer;size:QWORD); inline;
const
ICACHE=1; //Flush the instruction cache.
DCACHE=2; //Write back to memory and invalidate the affected valid cache lines.
BCACHE=ICACHE or DCACHE;
procedure md_cacheflush(addr:Pointer;nbytes,cache:Integer);
Function md_create_swap_file(const FNAME:RawByteString;SIZE:QWORD;Var FD:THandle):DWORD;
implementation
function MD_MAP_ALIGN(align:QWORD):DWORD; inline;
begin
Result:=BsfQWORD(align) shl MD_MAP_ALIGN_SHIFT;
end;
const
MD_PROT_NONE=PAGE_NOACCESS;
MD_PROT_R =PAGE_READONLY;
MD_PROT_W =PAGE_READWRITE;
MD_PROT_RW =PAGE_READWRITE;
MD_PROT_X =PAGE_EXECUTE;
MD_PROT_RX =PAGE_EXECUTE_READ;
MD_PROT_WX =PAGE_EXECUTE_READWRITE;
MD_PROT_RWX =PAGE_EXECUTE_READWRITE;
wprots:array[0..7] of Byte=(
MD_PROT_NONE,//___
MD_PROT_R ,//__R
MD_PROT_W ,//_W_
MD_PROT_RW ,//_WR
MD_PROT_X ,//X__
MD_PROT_RX ,//X_R
MD_PROT_WX ,//XW_
MD_PROT_RWX //XWR
);
waccess:array[0..7] of Byte=(
SECTION_QUERY, //___
SECTION_QUERY or SECTION_MAP_READ, //__R
SECTION_QUERY or SECTION_MAP_WRITE, //_W_
SECTION_QUERY or SECTION_MAP_WRITE or SECTION_MAP_READ, //_WR
SECTION_QUERY or SECTION_MAP_EXECUTE, //X__
SECTION_QUERY or SECTION_MAP_EXECUTE or SECTION_MAP_READ, //X_R
SECTION_QUERY or SECTION_MAP_EXECUTE or SECTION_MAP_WRITE, //XW_
SECTION_QUERY or SECTION_MAP_EXECUTE or SECTION_MAP_WRITE or SECTION_MAP_READ //XWR
);
function md_dw_gran(x:Pointer):Pointer; inline;
begin
Result:=Pointer(QWORD(x) and (not (MD_ALLOC_GRANULARITY-1)));
end;
function md_dw_page(x:QWORD):QWORD; inline;
begin
Result:=x and (not (MD_PAGE_SIZE-1));
end;
function md_up_page(x:QWORD):QWORD; inline;
begin
Result:=(x+(MD_PAGE_SIZE-1)) and (not (MD_PAGE_SIZE-1));
end;
function md_placeholder_mmap(var base:Pointer;size:QWORD;flags:DWORD=0;hProcess:THandle=NtCurrentProcess):Integer;
var
ADDR:Pointer;
len :ULONG_PTR;
info:TMemoryBasicInformation;
EXT :TMEM_EXTENDED_PARAMETER;
REQ :TMEM_ADDRESS_REQUIREMENTS;
begin
size:=md_up_page(size);
if ((flags and MD_MAP_FIXED)<>0) then
begin
ADDR:=md_dw_gran(base);
len:=0;
Result:=NtQueryVirtualMemory(
hProcess,
ADDR,
MemoryBasicInformation,
@info,
sizeof(TMemoryBasicInformation),
@len);
if (Result=0) and (info.State=MEM_RESERVE) then
begin
if (ADDR>=info.BaseAddress) and
((ADDR+size)<=(info.BaseAddress+info.RegionSize)) then
begin
base:=info.AllocationBase;
Exit(0);
end;
end;
REQ.LowestStartingAddress:=nil;
REQ.Alignment :=0;
end else
begin
ADDR:=nil;
REQ.LowestStartingAddress:=base;
REQ.Alignment:=(flags shr MD_MAP_ALIGN_SHIFT) and $1F;
if (REQ.Alignment<=16) then
begin
REQ.Alignment:=0;
end else
begin
REQ.Alignment:=QWORD(1) shl REQ.Alignment;
end;
end;
EXT.pType :=MemExtendedParameterAddressRequirements;
EXT.Pointer:=@REQ;
REQ.HighestEndingAddress:=nil;
Result:=NtAllocateVirtualMemoryEx(
hProcess,
@ADDR,
@size,
MEM_RESERVE or MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS,
@EXT,
1
);
if (Result=0) then
begin
base:=ADDR;
end else
begin
base:=nil;
end;
end;
function md_placeholder_unmap(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer;
var
pend:Pointer;
addr,prev:Pointer;
info:TMemoryBasicInformation;
len:ULONG_PTR;
begin
if (base=nil) or (size=0) then Exit(0);
pend:=base+size;
addr:=base;
repeat
len:=0;
NtQueryVirtualMemory(
hProcess,
addr,
0,
@info,
sizeof(TMemoryBasicInformation),
@len);
if (len=0) then Break;
if (info.State<>MEM_FREE) then
begin
addr:=info.BaseAddress;
case info._Type of
MEM_MAPPED:
begin
//unmap
Result:=NtUnmapViewOfSectionEx(hProcess,addr,MEM_PRESERVE_PLACEHOLDER);
end;
MEM_PRIVATE:
begin
//unmap
len:=Info.RegionSize;
Result:=NtFreeVirtualMemory(
hProcess,
@addr,
@len,
MEM_RELEASE or MEM_PRESERVE_PLACEHOLDER
);
end;
else
Result:=-1;
end;
if (Result<>0) then Exit;
end;
prev:=addr;
addr:=addr+Info.RegionSize;
if (addr>=pend) then Break;
until (prev>=addr);
//union all
Result:=NtFreeVirtualMemory(
NtCurrentProcess,
@base,
@size,
MEM_RELEASE or MEM_COALESCE_PLACEHOLDERS
);
//ignore errors
//free
size:=0;
Result:=NtFreeVirtualMemory(
NtCurrentProcess,
@base,
@size,
MEM_RELEASE
);
end;
function md_placeholder_split(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer; inline;
begin
Result:=NtFreeVirtualMemory(
hProcess,
@base,
@size,
MEM_RELEASE or MEM_PRESERVE_PLACEHOLDER
);
end;
function md_placeholder_union(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer; inline;
begin
Result:=NtFreeVirtualMemory(
hProcess,
@base,
@size,
MEM_RELEASE or MEM_COALESCE_PLACEHOLDERS
);
end;
function md_placeholder_commit(base:Pointer;size:QWORD;prot:DWORD;hProcess:THandle=NtCurrentProcess):Integer;
begin
prot:=wprots[prot and VM_RWX];
Result:=NtAllocateVirtualMemoryEx(
hProcess,
@base,
@size,
MEM_COMMIT or MEM_RESERVE or MEM_REPLACE_PLACEHOLDER,
prot,
nil,
0
);
end;
function md_placeholder_commit(base:Pointer;size:QWORD;prot:DWORD;fd:THandle;offset:QWORD;hProcess:THandle=NtCurrentProcess):Integer;
begin
prot:=wprots[prot and VM_RWX];
Result:=NtMapViewOfSectionEx(
fd,
hProcess,
@base,
@offset,
@size,
MEM_REPLACE_PLACEHOLDER,
prot,
nil,
0
);
end;
function md_placeholder_decommit(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer;
begin
Result:=NtUnmapViewOfSectionEx(hProcess,base,MEM_PRESERVE_PLACEHOLDER);
if (DWORD(Result)=$C0000019) then //STATUS_NOT_MAPPED_VIEW
begin
Result:=NtFreeVirtualMemory(
hProcess,
@base,
@size,
MEM_RELEASE or MEM_PRESERVE_PLACEHOLDER
);
end;
end;
function md_memfd_create(var hMem:THandle;size:QWORD;maxprot:Byte):Integer;
var
Access:DWORD;
prot:DWORD;
begin
Access:=waccess[maxprot and VM_RWX];
prot :=wprots [maxprot and VM_RWX];
hMem:=0;
Result:=NtCreateSectionEx(
@hMem,
Access,
nil,
@size,
prot,
SEC_COMMIT,
THandle(0),
nil,
0
);
end;
function md_memfd_open(var hMem:THandle;hFile:THandle;maxprot:Byte):Integer;
var
Access:DWORD;
prot:DWORD;
size:QWORD;
begin
Access:=waccess[maxprot and VM_RWX];
prot :=wprots [maxprot and VM_RWX];
size:=0;
hMem:=0;
Result:=NtCreateSectionEx(
@hMem,
Access,
nil,
@size,
prot,
SEC_COMMIT,
hFile,
nil,
0
);
end;
function md_memfd_close(hMem:THandle):Integer; inline;
begin
Result:=NtClose(hMem);
end;
function md_protect(base:Pointer;size:QWORD;prot:Integer;hProcess:THandle=NtCurrentProcess):Integer;
var
old:Integer;
begin
prot:=wprots[prot and VM_RWX];
old:=0;
Result:=NtProtectVirtualMemory(
hProcess,
@base,
@size,
prot,
@old
);
end;
function md_dontneed(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer; inline;
begin
Result:=NtAllocateVirtualMemory(
hProcess,
@base,
0,
@size,
MEM_RESET,
PAGE_NOACCESS
);
end;
function md_activate(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer; inline;
begin
Result:=NtAllocateVirtualMemory(
hProcess,
@base,
0,
@size,
MEM_RESET_UNDO,
PAGE_NOACCESS
);
end;
const
atypes:array[0..3] of DWORD=(
MEM_COMMIT , //___
MEM_COMMIT or MEM_RESERVE, //__F
MEM_RESERVE , //_R_
MEM_RESERVE //_RF
);
function md_mmap(var base:Pointer;size:QWORD;prot:DWORD;fd:THandle=0;offset:QWORD=0;hProcess:THandle=NtCurrentProcess):Integer;
var
atype:DWORD;
ADDR :Pointer;
EXT :TMEM_EXTENDED_PARAMETER;
REQ :TMEM_ADDRESS_REQUIREMENTS;
begin
EXT.pType :=MemExtendedParameterAddressRequirements;
EXT.Pointer:=@REQ;
if ((prot and MD_MAP_FIXED)<>0) then
begin
ADDR:=md_dw_gran(base);
REQ.LowestStartingAddress:=nil;
REQ.Alignment :=0;
end else
begin
ADDR:=nil;
REQ.LowestStartingAddress:=base;
REQ.Alignment:=(prot shr MD_MAP_ALIGN_SHIFT) and $1F;
if (REQ.Alignment<=16) then
begin
REQ.Alignment:=0;
end else
begin
REQ.Alignment:=QWORD(1) shl REQ.Alignment;
end;
end;
REQ.HighestEndingAddress:=nil;
atype:=atypes[(prot shr 8) and 3];
if (prot and MD_MAP_RESERVED)<>0 then
begin
prot:=PAGE_NOACCESS;
fd :=0;
end else
begin
prot:=wprots[prot and VM_RWX];
end;
if (fd=THandle(0)) or (fd=THandle(-1)) then
begin
Result:=NtAllocateVirtualMemoryEx(
hProcess,
@ADDR,
@size,
atype,
prot,
@EXT,
1
);
end else
begin
Result:=NtMapViewOfSectionEx(
fd,
hProcess,
@ADDR,
@offset,
@size,
0,
prot,
@EXT,
1
);
end;
if (Result=0) then
begin
base:=ADDR;
end else
begin
base:=nil;
end;
end;
function md_unmap(base:Pointer;size:QWORD;hProcess:THandle=NtCurrentProcess):Integer;
var
pend:Pointer;
addr,prev:Pointer;
info:TMemoryBasicInformation;
len:ULONG_PTR;
begin
if (base=nil) or (size=0) then Exit(0);
pend:=base+size;
addr:=base;
repeat
len:=0;
NtQueryVirtualMemory(
hProcess,
addr,
0,
@info,
sizeof(TMemoryBasicInformation),
@len);
if (len=0) then Break;
if (info.State<>MEM_FREE) then
begin
addr:=info.AllocationBase;
case info._Type of
MEM_MAPPED:
begin
//unmap
Result:=NtUnmapViewOfSectionEx(hProcess,addr,0);
end;
MEM_PRIVATE:
begin
//unmap
len:=0;
Result:=NtFreeVirtualMemory(
hProcess,
@addr,
@len,
MEM_RELEASE
);
end;
else
Result:=-1;
end;
if (Result<>0) then Exit;
end else
begin
addr:=info.BaseAddress;
end;
prev:=addr;
addr:=info.BaseAddress+Info.RegionSize;
if (addr>=pend) then Break;
until (prev>=addr);
end;
function kmem_alloc(size:QWORD;prot:DWORD):Pointer;
var
r:Integer;
begin
Result:=Pointer(KERNEL_LOWER); //lower
r:=md_mmap(Result,size,prot);
if (r<>0) then
begin
Writeln(stderr,'kmem_alloc(0x',HexStr(size,11),',0x',HexStr(prot,3),'):0x',HexStr(r,8));
end;
end;
procedure kmem_free(base:Pointer;size:QWORD); inline;
begin
md_unmap(base,size);
end;
//
procedure md_cacheflush(addr:Pointer;nbytes,cache:Integer);
begin
if ((cache and ICACHE)<>0) then
begin
FlushInstructionCache(NtCurrentProcess,addr,nbytes);
end;
if ((cache and DCACHE)<>0) then
begin
FlushViewOfFile(addr,nbytes);
end;
end;
Function NtTruncate(FD:THandle;SIZE:QWORD):DWORD; inline;
var
BLK:IO_STATUS_BLOCK;
begin
Result:=NtSetInformationFile(
FD,
@BLK,
@SIZE,
SizeOf(Int64),
FileEndOfFileInformation);
if (Result<>0) then
begin
Result:=NtSetInformationFile(
FD,
@BLK,
@SIZE,
SizeOf(Int64),
FileAllocationInformation);
end;
end;
function StrLen(p: pwidechar): sizeint; external name 'FPC_PWIDECHAR_LENGTH'; overload;
Function md_create_swap_file(const FNAME:RawByteString;SIZE:QWORD;Var FD:THandle):DWORD;
var
W:WideString;
OBJ :OBJECT_ATTRIBUTES;
UPATH:UNICODE_STRING;
BLK :IO_STATUS_BLOCK;
begin
W:=UTF8Decode(FNAME);
W:='\??\'+W;
OBJ:=Default(OBJECT_ATTRIBUTES);
OBJ.Length :=SizeOf(OBJECT_ATTRIBUTES);
OBJ.ObjectName:=@UPATH;
UPATH:=Default(UNICODE_STRING);
UPATH.Length :=strlen(PWideChar(w))*SizeOf(WideChar);
UPATH.MaximumLength:=UPATH.Length+SizeOf(WideChar);
UPATH.Buffer :=PWideChar(w);
BLK:=Default(IO_STATUS_BLOCK);
FD:=0;
Result:=NtCreateFile(@FD,
FILE_READ_DATA or
FILE_WRITE_DATA or
FILE_APPEND_DATA or
FILE_READ_ATTRIBUTES or
FILE_WRITE_ATTRIBUTES or
FILE_CAN_DELETE or
SYNCHRONIZE,
@OBJ,
@BLK,
nil,
FILE_ATTRIBUTE_TEMPORARY,
0,
FILE_OVERWRITE_IF,
FILE_SYNCHRONOUS_IO_NONALERT or
FILE_OPEN_REPARSE_POINT or
FILE_NON_DIRECTORY_FILE or
FILE_DELETE_ON_CLOSE,
nil,
0);
if (Result<>0) then Exit;
Result:=NtTruncate(FD,SIZE);
if (Result<>0) then
begin
NtClose(FD);
FD:=0;
end;
end;
end.