FPPS4/sys/vm/vm_nt_map.pas

1615 lines
33 KiB
Plaintext

unit vm_nt_map;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
sysutils,
vm,
kern_mtx,
systm,
vm_pmap_prot,
vm_nt_sub_map;
const
NT_FILE_FREE=1;
NT_MOBJ_FREE=2;
NT_UNION_OBJ=4;
MAX_UNION_SIZE=256*1024*1024;
type
bit29=0..536870911;
bit35=0..34359738367;
t_danger_range=bitpacked record
start:bit35; //47-12=35
_size:bit29; //64-35=29
end;
t_danger_zone=object
Flock :mtx;
Frange:t_danger_range;
procedure Init;
procedure Done;
function in_range(addr,size:vm_offset_t):Boolean;
procedure d_wait(addr,size:vm_offset_t);
procedure lock(start,__end:vm_offset_t);
procedure unlock;
end;
pp_vm_nt_file_obj=^p_vm_nt_file_obj;
p_vm_nt_file_obj=^vm_nt_file_obj;
t_nt_obj_free_cb=procedure(obj:p_vm_nt_file_obj);
vm_nt_file_obj=packed record
hfile:THandle;
free :t_nt_obj_free_cb;
refs :QWORD;
flags:Byte;
maxp :Byte;
end;
///
pp_vm_nt_entry=^p_vm_nt_entry;
p_vm_nt_entry=^vm_nt_entry;
vm_nt_entry=packed object
prev :p_vm_nt_entry; // previous entry
next :p_vm_nt_entry; // next entry
left :p_vm_nt_entry; // left child in binary search tree
right :p_vm_nt_entry; // right child in binary search tree
usize :vm_offset_t; // unaligned size
offset:vm_ooffset_t; // offset into object
obj :p_vm_nt_file_obj; // object I point to
sub :t_vm_nt_sub_map;
property start:vm_offset_t read sub.header.start write sub.header.start; // start address
property __end:vm_offset_t read sub.header.__end write sub.header.__end; // end address
end;
p_vm_nt_map=^t_vm_nt_map;
t_vm_nt_map=object
header :vm_nt_entry; // List of entries
lock :mtx; // Lock for map data
root :p_vm_nt_entry; // Root of a binary search tree
danger_zone:t_danger_zone;
property min_offset:vm_offset_t read header.sub.header.start write header.sub.header.start;
property max_offset:vm_offset_t read header.sub.header.__end write header.sub.header.__end;
end;
function vm_nt_file_obj_allocate (hfile:THandle;maxp:Byte):p_vm_nt_file_obj;
procedure vm_nt_file_obj_destroy (obj:p_vm_nt_file_obj);
procedure vm_nt_file_obj_reference (obj:p_vm_nt_file_obj);
procedure vm_nt_file_obj_deallocate(obj:p_vm_nt_file_obj);
function vm_nt_map_max(map:p_vm_nt_map):vm_offset_t;
function vm_nt_map_min(map:p_vm_nt_map):vm_offset_t;
procedure vm_nt_map_init(map:p_vm_nt_map;min,max:vm_offset_t);
function vm_nt_map_insert(
map :p_vm_nt_map;
obj :p_vm_nt_file_obj;
offset:vm_ooffset_t;
start :vm_offset_t;
__end :vm_offset_t;
usize :vm_offset_t;
prot :Integer):Integer;
function vm_nt_map_delete(map:p_vm_nt_map;start:vm_offset_t;__end:vm_offset_t):Integer;
//Change protection taking into account tracking
procedure vm_nt_map_protect(map :p_vm_nt_map;
start:vm_offset_t;
__end:vm_offset_t;
prot :Integer);
//Change protection tracking
procedure vm_nt_map_tracking(map :p_vm_nt_map;
start:vm_offset_t;
__end:vm_offset_t;
prot :Integer);
//Update page protection by mode
procedure vm_nt_map_prot_fix(map :p_vm_nt_map;
start:vm_offset_t;
__end:vm_offset_t;
mode :Integer);
procedure vm_nt_map_madvise(map :p_vm_nt_map;
start :vm_offset_t;
__end :vm_offset_t;
advise:Integer);
//Create a memory mirror of the current state of the pages
function vm_nt_map_mirror(map :p_vm_nt_map;
start:vm_offset_t;
__end:vm_offset_t):Pointer;
function vm_nt_map_fetch(map :p_vm_nt_map;
start :vm_offset_t;
__end :vm_offset_t;
var p__start:vm_offset_t;
var p____end:vm_offset_t;
var p_offset:vm_offset_t;
var p____obj:p_vm_nt_file_obj
):Boolean;
implementation
uses
time,
kern_param,
md_map;
type
p_range=^t_range;
t_range=record
start:vm_offset_t;
__end:vm_offset_t;
end;
t_range_stat=record
obj:p_vm_nt_file_obj;
//
case Byte of
0:(
rprev:t_range;
rcurr:t_range;
rnext:t_range;
);
1:(
range:array[0..2] of t_range;
);
end;
function vm_nt_file_obj_allocate(hfile:THandle;maxp:Byte):p_vm_nt_file_obj;
begin
Assert(maxp<>0);
Result:=AllocMem(SizeOf(vm_nt_file_obj));
Result^.hfile:=hfile;
Result^.refs :=1;
Result^.flags:=NT_FILE_FREE or NT_MOBJ_FREE or NT_UNION_OBJ;
Result^.maxp :=maxp;
end;
procedure vm_nt_file_obj_destroy(obj:p_vm_nt_file_obj);
var
r:Integer;
free :t_nt_obj_free_cb;
begin
if ((obj^.flags and NT_FILE_FREE)<>0) then
if (obj^.hfile<>0) then
begin
r:=md_memfd_close(obj^.hfile);
if (r<>0) then
begin
Writeln('failed md_memfd_close(',obj^.hfile,'):0x',HexStr(r,8));
Assert(false,'vm_nt_file_obj_destroy');
end;
obj^.hfile:=0;
end;
free:=obj^.free;
if ((obj^.flags and NT_MOBJ_FREE)<>0) then
begin
FreeMem(obj);
end;
if (free<>nil) then
begin
free(obj);
end;
end;
procedure vm_nt_file_obj_reference(obj:p_vm_nt_file_obj);
begin
if (obj=nil) then Exit;
System.InterlockedIncrement64(obj^.refs);
end;
procedure vm_nt_file_obj_deallocate(obj:p_vm_nt_file_obj);
begin
if (obj=nil) then Exit;
if (System.InterlockedDecrement64(obj^.refs)=0) then
begin
vm_nt_file_obj_destroy(obj);
end;
end;
//
procedure vm_init_stat(var stat:t_range_stat;entry:p_vm_nt_entry); inline;
begin
stat:=Default(t_range_stat);
stat.obj :=entry^.obj;
stat.rcurr.start:=entry^.start;
stat.rcurr.__end:=entry^.__end;
end;
procedure vm_get_space(map:p_vm_nt_map;entry:p_vm_nt_entry;var start,__end:vm_offset_t);
var
prev:p_vm_nt_entry;
next:p_vm_nt_entry;
begin
prev:=entry^.prev;
next:=entry^.next;
if (prev=@map^.header) then
begin
start:=map^.min_offset;
end else
begin
start:=prev^.__end;
end;
if (next=@map^.header) then
begin
__end:=map^.max_offset;
end else
begin
__end:=next^.start;
end;
end;
procedure vm_map(map :p_vm_nt_map;
entry:p_vm_nt_entry;
prot :Integer);
var
start:vm_offset_t;
__end:vm_offset_t;
size:vm_size_t;
max:Integer;
r:Integer;
begin
if (entry^.obj<>nil) then
begin
size:=entry^.__end-entry^.start;
vm_get_space(map,entry,start,__end);
if (start<>__end) then
if (entry^.start<>start) or
(entry^.__end<>__end) then
begin
r:=md_placeholder_split(Pointer(entry^.start),size);
if (r<>0) then
begin
Writeln('failed md_placeholder_split(',HexStr(entry^.start,11),',',HexStr(entry^.start+size,11),'):0x',HexStr(r,8));
Assert(false,'vm_map');
end;
end;
max:=entry^.obj^.maxp;
if (entry^.obj^.hfile<>0) then
begin
r:=md_placeholder_commit(Pointer(entry^.start),
entry^.usize, //unaligned size
(max and VM_RW),
entry^.obj^.hfile,
entry^.offset);
if (r<>0) then
begin
Writeln('failed md_placeholder_commit(',HexStr(entry^.start,11),',',HexStr(entry^.start+size,11),'):0x',HexStr(r,8));
Assert(false,'vm_map');
end;
end;
if ((prot and VM_RW)<>(max and VM_RW)) then
begin
r:=md_protect(Pointer(entry^.start),size,(prot and VM_RW));
if (r<>0) then
begin
Writeln('failed md_protect(',HexStr(entry^.start,11),',',HexStr(entry^.start+size,11),'):0x',HexStr(r,8));
Assert(false,'vm_map');
end;
end;
//Writeln('md_file_mmap(',HexStr(entry^.start,11),',',HexStr(entry^.start+size,11),'):0x',HexStr(r,8));
end;
end;
//
function MD_IDX_TO_OFF(x:QWORD):QWORD; inline;
begin
Result:=QWORD(x) shl MD_PAGE_SHIFT;
end;
function MD_OFF_TO_IDX(x:QWORD):QWORD; inline;
begin
Result:=QWORD(x) shr MD_PAGE_SHIFT;
end;
//
procedure t_danger_zone.Init;
begin
mtx_init(Flock,'danger_zone');
end;
procedure t_danger_zone.Done;
begin
mtx_destroy(Flock);
end;
function t_danger_zone.in_range(addr,size:vm_offset_t):Boolean;
var
range:t_danger_range;
begin
QWORD(range):=System.InterlockedExchangeAdd64(QWORD(Frange),0);
Result:=(addr<MD_IDX_TO_OFF(range.start+range._size)) and ((addr+size)>MD_IDX_TO_OFF(range.start));
end;
procedure t_danger_zone.d_wait(addr,size:vm_offset_t);
begin
mtx_lock(Flock);
if in_range(addr,size) then
begin
msleep(@Self,@Flock,PCATCH,'danger_zone',hz);
end;
mtx_unlock(Flock);
end;
procedure t_danger_zone.lock(start,__end:vm_offset_t);
var
range:t_danger_range;
begin
range.start:=MD_OFF_TO_IDX(start);
range._size:=MD_OFF_TO_IDX(__end-start);
System.InterlockedExchange64(QWORD(Frange),QWORD(range));
mtx_lock(Flock);
end;
procedure t_danger_zone.unlock;
begin
System.InterlockedExchange64(QWORD(Frange),0);
mtx_unlock(Flock);
wakeup(@Self);
end;
//
function vm_remap(map:p_vm_nt_map;
entry1:p_vm_nt_entry;
entry2:p_vm_nt_entry;
entry3:p_vm_nt_entry;
var stat:t_range_stat):Boolean;
var
ets:array[0..2] of p_vm_nt_entry;
first:p_vm_nt_entry;
curr :p_vm_nt_entry;
e_count:Integer;
r_count:Integer;
start:vm_offset_t;
__end:vm_offset_t;
size:vm_size_t;
max:Integer;
p:p_range;
i,r:Integer;
begin
Result:=False;
if (stat.obj=nil) then Exit(False);
ets[0]:=entry1;
ets[1]:=entry2;
ets[2]:=entry3;
//get first entry
first:=nil;
For i:=Low(ets) to High(ets) do
begin
curr:=ets[i];
if (curr<>nil) then
begin
first:=curr;
Break;
end;
end;
if (first=nil) then Exit(False);
if (stat.rcurr.start=first^.start) and
(stat.rcurr.__end=first^.__end) then Exit(False);
start:=0;
__end:=0;
e_count:=0;
r_count:=0;
//get range
For i:=Low(ets) to High(ets) do
begin
curr:=ets[i];
if (curr<>nil) then
begin
if (start=0) or (start>curr^.start) then
begin
start:=curr^.start;
end;
if (__end=0) or (__end<curr^.__end) then
begin
__end:=curr^.__end;
end;
Inc(e_count);
end;
end;
//danger zone
map^.danger_zone.lock(start,__end);
//unmap all
For i:=Low(stat.range) to High(stat.range) do
begin
p:=@stat.range[i];
//
if (p^.start<>0) and
(p^.start<>p^.__end) then
begin
//
if (stat.obj^.hfile<>0) then
begin
size:=p^.__end-p^.start;
r:=md_placeholder_decommit(Pointer(p^.start),size);
if (r<>0) then
begin
Writeln('failed md_placeholder_decommit(',HexStr(p^.start,11),',',HexStr(p^.__end,11),'):0x',HexStr(r,8));
Assert(false,'vm_remap');
end;
end;
//
//Writeln('md_file_unmap_ex(',HexStr(p^.start,11),',',HexStr(p^.__end,11),'):0x',HexStr(r,8));
//
Inc(r_count);
end;
end;
//union parts
if (r_count>1) then
begin
size:=__end-start;
r:=md_placeholder_union(Pointer(start),size);
if (r<>0) then
begin
Writeln('failed md_placeholder_union(',HexStr(start,11),',',HexStr(__end,11),'):0x',HexStr(r,8));
Assert(false,'vm_remap');
end;
//Writeln('md_union(',HexStr(start,11),',',HexStr(__end,11),'):0x',HexStr(r,8));
end;
//split to parts
if (e_count>1) then
For i:=Low(ets) to High(ets) do
begin
curr:=ets[i];
if (curr<>nil) and (curr<>first) then
begin
size:=curr^.__end-curr^.start;
r:=md_placeholder_split(Pointer(curr^.start),size);
if (r<>0) then
begin
Writeln('failed md_placeholder_split(',HexStr(curr^.start,11),',',HexStr(curr^.__end,11),'):0x',HexStr(r,8));
Writeln('(',HexStr(start,11),',',HexStr(__end,11),')');
Assert(false,'vm_remap');
end;
//Writeln('md_split(',HexStr(ets[i]^.start,11),',',HexStr(ets[i]^.__end,11),'):0x',HexStr(r,8));
Break; //middle or last splt
end;
end;
max:=stat.obj^.maxp;
//map new parts
For i:=Low(ets) to High(ets) do
begin
curr:=ets[i];
if (curr<>nil) then
begin
//map new
if (curr^.obj<>nil) then
if (stat.obj^.hfile<>0) then
begin
r:=md_placeholder_commit(Pointer(curr^.start),
curr^.usize, //unaligned size
(max and VM_RW),
stat.obj^.hfile,
curr^.offset);
if (r<>0) then
begin
Writeln('failed md_placeholder_commit(',HexStr(curr^.start,11),',',HexStr(curr^.__end,11),'):0x',HexStr(r,8));
Assert(false,'vm_remap');
end;
//Writeln('md_file_mmap_ex(',HexStr(ets[i]^.start,11),',',HexStr(ets[i]^.__end,11),'):0x',HexStr(r,8));
end;
end;
end;
//fix prot
For i:=Low(ets) to High(ets) do
begin
curr:=ets[i];
if (curr<>nil) then
begin
if (curr^.obj<>nil) then
if (stat.obj^.hfile<>0) then
begin
vm_nt_sub_map_prot_fixup(@curr^.sub,
curr^.start,
curr^.__end,
TRACK_PROT or REMAP_PROT //untrack trigger or restore track?
);
end;
end;
end;
//danger zone
map^.danger_zone.unlock;
Result:=True;
end;
procedure vm_unmap(map:p_vm_nt_map;entry:p_vm_nt_entry);
var
start:vm_offset_t;
__end:vm_offset_t;
r:Integer;
begin
if (entry^.obj<>nil) then
if (entry^.obj^.hfile<>0) then
begin
r:=md_placeholder_decommit(Pointer(entry^.start),(entry^.__end-entry^.start));
if (r<>0) then
begin
Writeln('failed md_placeholder_decommit(',HexStr(entry^.start,11),',',HexStr(entry^.__end,11),'):0x',HexStr(r,8));
Assert(false,'vm_unmap');
end;
//Writeln('md_placeholder_decommit(',HexStr(entry^.start,11),',',HexStr(entry^.__end,11),'):0x',HexStr(r,8));
end;
vm_get_space(map,entry,start,__end);
if (start<>__end) then
if (entry^.start<>start) or
(entry^.__end<>__end) then
begin
r:=md_placeholder_union(Pointer(start),__end-start);
if (r<>0) then
begin
Writeln('failed md_placeholder_union(',HexStr(start,11),',',HexStr(__end,11),'):0x',HexStr(r,8));
Writeln('(',HexStr(entry^.start,11),',',HexStr(entry^.__end,11),'):0x',HexStr(r,8));
Assert(false,'vm_unmap');
end;
end;
end;
//
function vm_nt_map_max(map:p_vm_nt_map):vm_offset_t; inline;
begin
Result:=map^.max_offset;
end;
function vm_nt_map_min(map:p_vm_nt_map):vm_offset_t; inline;
begin
Result:=map^.min_offset;
end;
procedure VM_NT_MAP_RANGE_CHECK(map:p_vm_nt_map;var start,__end:vm_offset_t);
begin
if (start<vm_nt_map_min(map)) then
begin
start:=vm_nt_map_min(map);
end;
if (__end>vm_nt_map_max(map)) then
begin
__end:=vm_nt_map_max(map);
end;
if (start>__end) then
begin
start:=__end;
end;
end;
procedure vm_nt_map_init(map:p_vm_nt_map;min,max:vm_offset_t);
begin
map^.header.next:=@map^.header;
map^.header.prev:=@map^.header;
map^.min_offset:=min;
map^.max_offset:=max;
map^.root:=nil;
map^.danger_zone.Init;
mtx_init(map^.lock,'vm_nt_map');
end;
procedure vm_nt_map_lock(map:p_vm_nt_map); inline;
begin
mtx_lock(map^.lock);
end;
procedure vm_nt_map_unlock(map:p_vm_nt_map); inline;
begin
mtx_unlock(map^.lock);
end;
procedure vm_nt_entry_dispose(map:p_vm_nt_map;entry:p_vm_nt_entry); inline;
begin
FreeMem(entry);
end;
procedure vm_nt_entry_deallocate(map:p_vm_nt_map;entry:p_vm_nt_entry); inline;
begin
vm_nt_sub_map_free (@entry^.sub);
vm_nt_file_obj_deallocate(entry^.obj);
vm_nt_entry_dispose (map,entry);
end;
function vm_nt_entry_create(map:p_vm_nt_map):p_vm_nt_entry;
var
new_entry:p_vm_nt_entry;
begin
new_entry:=AllocMem(SizeOf(vm_nt_entry));
Assert((new_entry<>nil),'vm_nt_entry_create: kernel resources exhausted');
Result:=new_entry;
end;
function vm_nt_entry_splay(addr:vm_offset_t;root:p_vm_nt_entry):p_vm_nt_entry;
var
llist,rlist:p_vm_nt_entry;
ltree,rtree:p_vm_nt_entry;
y :p_vm_nt_entry;
begin
if (root=nil) then Exit(root);
llist:=nil;
rlist:=nil;
repeat
if (addr<root^.start) then
begin
y:=root^.left;
if (y=nil) then break;
if (addr<y^.start) and (y^.left<>nil) then
begin
root^.left:=y^.right;
y^.right:=root;
root:=y^.left;
y^.left:=rlist;
rlist:=y;
end else
begin
root^.left:=rlist;
rlist:=root;
root:=y;
end;
end else
if (addr>=root^.__end) then
begin
y:=root^.right;
if (y=nil) then break;
if (addr>=y^.__end) and (y^.right<>nil) then
begin
root^.right:=y^.left;
y^.left:=root;
root:=y^.right;
y^.right:=llist;
llist:=y;
end else
begin
root^.right:=llist;
llist:=root;
root:=y;
end;
end else
begin
break;
end;
until false;
ltree:=root^.left;
while (llist<>nil) do
begin
y:=llist^.right;
llist^.right:=ltree;
ltree:=llist;
llist:=y;
end;
rtree:=root^.right;
while (rlist<>nil) do
begin
y:=rlist^.left;
rlist^.left:=rtree;
rtree:=rlist;
rlist:=y;
end;
root^.left:=ltree;
root^.right:=rtree;
Result:=(root);
end;
procedure vm_nt_entry_link(
map :p_vm_nt_map;
after_where:p_vm_nt_entry;
entry :p_vm_nt_entry);
begin
entry^.prev:=after_where;
entry^.next:=after_where^.next;
entry^.next^.prev:=entry;
after_where^.next:=entry;
if (after_where<>@map^.header) then
begin
if (after_where<>map^.root) then
begin
vm_nt_entry_splay(after_where^.start, map^.root);
end;
entry^.right:=after_where^.right;
entry^.left:=after_where;
after_where^.right:=nil;
end else
begin
entry^.right:=map^.root;
entry^.left:=nil;
end;
map^.root:=entry;
end;
procedure vm_nt_entry_unlink(
map :p_vm_nt_map;
entry :p_vm_nt_entry);
var
next,prev,root:p_vm_nt_entry;
begin
if (entry<>map^.root) then
begin
vm_nt_entry_splay(entry^.start, map^.root);
end;
if (entry^.left=nil) then
begin
root:=entry^.right;
end else
begin
root:=vm_nt_entry_splay(entry^.start, entry^.left);
root^.right:=entry^.right;
end;
map^.root:=root;
prev:=entry^.prev;
next:=entry^.next;
next^.prev:=prev;
prev^.next:=next;
end;
function vm_nt_map_lookup_entry(
map :p_vm_nt_map;
address :vm_offset_t;
entry :pp_vm_nt_entry):Boolean;
var
cur:p_vm_nt_entry;
begin
cur:=map^.root;
if (cur=nil) then
begin
entry^:=@map^.header;
end else
if (address>=cur^.start) and (cur^.__end>address) then
begin
entry^:=cur;
Exit(TRUE);
end else
begin
cur:=vm_nt_entry_splay(address,cur);
map^.root:=cur;
if (address>=cur^.start) then
begin
entry^:=cur;
if (cur^.__end>address) then
begin
Exit(TRUE);
end;
end else
begin
entry^:=cur^.prev;
end;
end;
Result:=(FALSE);
end;
function vm_nt_map_simplify_entry(map:p_vm_nt_map;entry:p_vm_nt_entry;var sb:t_range_stat):Boolean; forward;
function _vm_nt_map_insert(
map :p_vm_nt_map;
obj :p_vm_nt_file_obj;
offset:vm_ooffset_t;
start :vm_offset_t;
__end :vm_offset_t;
usize :vm_offset_t; //unaligned size
prot :Integer):Integer;
var
new_entry :p_vm_nt_entry;
prev_entry:p_vm_nt_entry;
temp_entry:p_vm_nt_entry;
stat :t_range_stat;
begin
if (start<map^.min_offset) or (__end>map^.max_offset) or (start>=__end) then
begin
Exit(KERN_INVALID_ADDRESS);
end;
if vm_nt_map_lookup_entry(map,start,@temp_entry) then
begin
Exit(KERN_NO_SPACE);
end;
prev_entry:=temp_entry;
if (prev_entry^.next<>@map^.header) and
(prev_entry^.next^.start<__end) then
begin
Exit(KERN_NO_SPACE);
end;
new_entry:=vm_nt_entry_create(map);
new_entry^.start :=start;
new_entry^.__end :=__end;
new_entry^.usize :=usize; //unaligned size
new_entry^.obj :=obj;
new_entry^.offset:=offset;
vm_nt_sub_map_init (@new_entry^.sub,start,__end);
//fill full map
vm_nt_sub_map_insert(@new_entry^.sub,start,__end,prot);
vm_nt_entry_link(map, prev_entry, new_entry);
vm_map(map,new_entry,prot);
vm_nt_map_simplify_entry(map,new_entry,stat);
vm_remap(map,new_entry,nil,nil,stat);
Result:=KERN_SUCCESS;
end;
function vm_nt_map_insert(
map :p_vm_nt_map;
obj :p_vm_nt_file_obj;
offset:vm_ooffset_t;
start :vm_offset_t;
__end :vm_offset_t;
usize :vm_offset_t; //unaligned size
prot :Integer):Integer;
begin
if (start=__end) then
begin
Exit(KERN_SUCCESS);
end;
vm_nt_map_lock(map);
Result:=_vm_nt_map_insert(
map ,
obj ,
offset,
start ,
__end ,
usize ,
prot );
vm_nt_map_unlock(map);
end;
function vm_nt_map_simplify_entry(map:p_vm_nt_map;entry:p_vm_nt_entry;var sb:t_range_stat):Boolean;
var
next,prev:p_vm_nt_entry;
prevsize,esize:vm_size_t;
stat:t_range_stat;
begin
vm_init_stat(stat,entry);
if (stat.obj<>nil) then
if ((stat.obj^.flags and NT_UNION_OBJ)=0) then
begin
sb:=stat;
Exit(False);
end;
if ((stat.rcurr.__end-stat.rcurr.start)>MAX_UNION_SIZE) then
begin
sb:=stat;
Exit(False);
end;
prev:=entry^.prev;
if (prev<>@map^.header) then
begin
prevsize:=prev^.__end - prev^.start;
if (prev^.__end=stat.rcurr.start) and
(prevsize<=MAX_UNION_SIZE) and
(prev^.obj=stat.obj) and
((prev^.obj=nil) or (prev^.offset + prevsize=entry^.offset))
then
begin
vm_nt_entry_unlink(map, prev);
stat.rprev.start:=prev^.start;
stat.rprev.__end:=prev^.__end;
entry^.start :=prev^.start;
entry^.offset:=prev^.offset;
entry^.usize :=entry^.usize+prev^.usize; //unaligned size
//update (implicitly)
//entry^.sub.min_offset:=entry^.start;
//move
vm_nt_sub_map_move(@entry^.sub,@prev^.sub);
vm_nt_entry_deallocate(map,prev);
end;
end;
next:=entry^.next;
if (next<>@map^.header) then
begin
esize:=stat.rcurr.__end - entry^.start;
if (stat.rcurr.__end=next^.start) and
(esize<=MAX_UNION_SIZE) and
(next^.obj=stat.obj) and
((stat.obj=nil) or (entry^.offset + esize=next^.offset))
then
begin
vm_nt_entry_unlink(map, next);
stat.rnext.start:=next^.start;
stat.rnext.__end:=next^.__end;
entry^.__end:=next^.__end;
entry^.usize:=entry^.usize+next^.usize; //unaligned size
//update (implicitly)
//entry^.sub.max_offset:=entry^.__end;
//move
vm_nt_sub_map_move(@entry^.sub,@next^.sub);
vm_nt_entry_deallocate(map,next);
end;
end;
sb:=stat;
Result:=True;
end;
procedure vm_nt_map_simplify_entry(map:p_vm_nt_map;entry:p_vm_nt_entry);
var
stat:t_range_stat;
begin
vm_nt_map_simplify_entry(map,entry,stat);
vm_remap(map,entry,nil,nil,stat);
end;
procedure vm_nt_map_clip_start_end(map:p_vm_nt_map;entry:p_vm_nt_entry;start,__end:vm_offset_t);
var
prev:p_vm_nt_entry;
next:p_vm_nt_entry;
stat:t_range_stat;
begin
prev:=nil;
next:=nil;
vm_nt_map_simplify_entry(map,entry,stat);
//entry[start ,end]
//prev [start | entry[end]
if (start>entry^.start) then
begin
prev:=vm_nt_entry_create(map);
prev^:=entry^;
prev^.__end:=start;
prev^.usize:=(prev^.__end-prev^.start); //unaligned size
//new map
vm_nt_sub_map_init(@prev^.sub,prev^.start,prev^.__end);
//move
vm_nt_sub_map_clip(@prev^.sub,@entry^.sub);
entry^.offset:=entry^.offset + (start - entry^.start);
entry^.start :=start;
entry^.usize :=entry^.usize-prev^.usize; //unaligned size
//update (implicitly)
//prev ^.sub.max_offset:=start;
//entry^.sub.min_offset:=start;
vm_nt_entry_link(map, entry^.prev, prev);
vm_nt_file_obj_reference(prev^.obj);
end;
//entry[start ,end]
//entry[start | next[end]
if (__end<entry^.__end) then
begin
next:=vm_nt_entry_create(map);
next^:=entry^;
next^.start :=__end;
//new map
vm_nt_sub_map_init(@next^.sub,next^.start,next^.__end);
//move
vm_nt_sub_map_clip(@next^.sub,@entry^.sub);
entry^.__end:=__end;
entry^.usize:=(entry^.__end-entry^.start); //unaligned size
next^.offset:=next^.offset + (__end - entry^.start);
next^.usize :=next^.usize-entry^.usize; //unaligned size
//update (implicitly)
//next ^.sub.min_offset:=__end;
//entry^.sub.max_offset:=__end;
vm_nt_entry_link(map, entry, next);
vm_nt_file_obj_reference(next^.obj);
end;
//
if (prev<>nil) or (next<>nil) then
begin
//exclude entry
vm_nt_file_obj_deallocate(entry^.obj);
entry^.obj:=nil;
end;
vm_remap(map,prev,entry,next,stat);
end;
procedure vm_nt_map_clip_end(map:p_vm_nt_map;entry:p_vm_nt_entry;__end:vm_offset_t);
var
next:p_vm_nt_entry;
stat:t_range_stat;
begin
next:=nil;
vm_init_stat(stat,entry);
//
//entry[start ,end]
//entry[start | next[end]
if (__end<entry^.__end) then
begin
next:=vm_nt_entry_create(map);
next^:=entry^;
next^.start :=__end;
//new map
vm_nt_sub_map_init(@next^.sub,next^.start,next^.__end);
//move
vm_nt_sub_map_clip(@next^.sub,@entry^.sub);
entry^.__end:=__end;
entry^.usize:=(entry^.__end-entry^.start); //unaligned size
next^.offset:=next^.offset + (__end - entry^.start);
next^.usize :=next^.usize-entry^.usize; //unaligned size
//update (implicitly)
//next ^.sub.min_offset:=__end;
//entry^.sub.max_offset:=__end;
vm_nt_entry_link(map, entry, next);
vm_nt_file_obj_reference(next^.obj);
end;
//
if (next<>nil) then
begin
//exclude entry
vm_nt_file_obj_deallocate(entry^.obj);
entry^.obj:=nil;
end;
vm_remap(map,nil,entry,next,stat);
end;
procedure vm_nt_entry_delete(map:p_vm_nt_map;entry:p_vm_nt_entry); inline;
begin
vm_nt_entry_unlink (map, entry);
vm_nt_entry_deallocate(map, entry);
end;
function vm_nt_map_delete(map:p_vm_nt_map;start:vm_offset_t;__end:vm_offset_t):Integer;
var
entry :p_vm_nt_entry;
first_entry:p_vm_nt_entry;
next :p_vm_nt_entry;
begin
if (start=__end) then
begin
Exit(KERN_SUCCESS);
end;
vm_nt_map_lock(map);
if (not vm_nt_map_lookup_entry(map, start, @first_entry)) then
begin
entry:=first_entry^.next;
end else
begin
entry:=first_entry;
vm_nt_map_clip_start_end(map, entry, start, __end);
end;
while (entry<>@map^.header) and (entry^.start<__end) do
begin
vm_nt_map_clip_end(map, entry, __end);
next:=entry^.next;
vm_unmap(map,entry);
vm_nt_entry_delete(map, entry);
entry:=next;
end;
vm_nt_map_unlock(map);
Result:=(KERN_SUCCESS);
end;
procedure vm_nt_map_protect(map :p_vm_nt_map;
start:vm_offset_t;
__end:vm_offset_t;
prot :Integer);
var
entry:p_vm_nt_entry;
e_start:vm_offset_t;
e___end:vm_offset_t;
max:Integer;
begin
if (start=__end) then Exit;
vm_nt_map_lock(map);
if (not vm_nt_map_lookup_entry(map, start, @entry)) then
begin
entry:=entry^.next;
end else
begin
//
end;
while (entry<>@map^.header) and (entry^.start<__end) do
begin
e_start:=entry^.start;
e___end:=entry^.__end;
if (e_start<start) then
begin
e_start:=start;
end;
if (e___end>__end) then
begin
e___end:=__end;
end;
if (e___end>e_start) then
begin
if (entry^.obj<>nil) then
begin
max:=entry^.obj^.maxp;
end else
begin
max:=0;
end;
vm_nt_sub_map_protect(@entry^.sub,e_start,e___end,(prot and max and VM_RW));
end;
entry:=entry^.next;
end;
vm_nt_map_unlock(map);
end;
procedure vm_nt_map_tracking(map :p_vm_nt_map;
start:vm_offset_t;
__end:vm_offset_t;
prot :Integer);
var
entry:p_vm_nt_entry;
e_start:vm_offset_t;
e___end:vm_offset_t;
begin
if (start=__end) then Exit;
vm_nt_map_lock(map);
if (not vm_nt_map_lookup_entry(map, start, @entry)) then
begin
entry:=entry^.next;
end else
begin
//
end;
while (entry<>@map^.header) and (entry^.start<__end) do
begin
e_start:=entry^.start;
e___end:=entry^.__end;
if (e_start<start) then
begin
e_start:=start;
end;
if (e___end>__end) then
begin
e___end:=__end;
end;
if (e___end>e_start) then
begin
vm_nt_sub_map_tracking(@entry^.sub,e_start,e___end,prot);
end;
entry:=entry^.next;
end;
vm_nt_map_unlock(map);
end;
procedure vm_nt_map_prot_fix(map :p_vm_nt_map;
start:vm_offset_t;
__end:vm_offset_t;
mode :Integer);
var
entry:p_vm_nt_entry;
e_start:vm_offset_t;
e___end:vm_offset_t;
begin
if (start=__end) then Exit;
vm_nt_map_lock(map);
if (not vm_nt_map_lookup_entry(map, start, @entry)) then
begin
entry:=entry^.next;
end else
begin
entry:=entry;
end;
while (entry<>@map^.header) and (entry^.start<__end) do
begin
if (entry^.obj<>nil) then
begin
e_start:=entry^.start;
e___end:=entry^.__end;
if (e_start<start) then
begin
e_start:=start;
end;
if (e___end>__end) then
begin
e___end:=__end;
end;
if (e___end>e_start) then
begin
vm_nt_sub_map_prot_fixup(@entry^.sub,e_start,e___end,mode);
end;
end;
entry:=entry^.next;
end;
vm_nt_map_unlock(map);
end;
//rdi, rsi
procedure ZeroPages(addr:Pointer;size:Ptruint); assembler nostackframe SysV_ABI_CDecl;
label
_exit,
_rep;
asm
shr $5, %rsi // div 32
jz _exit
vpxor %ymm0, %ymm0, %ymm0 //zero
_rep:
vmovaps %ymm0, (%rdi)
lea 32(%rdi),%rdi
dec %rsi
jnz _rep
_exit:
end;
procedure vm_nt_map_madvise(map :p_vm_nt_map;
start :vm_offset_t;
__end :vm_offset_t;
advise:Integer);
var
entry:p_vm_nt_entry;
base,size:vm_size_t;
mirror:Pointer;
begin
if (start=__end) then Exit;
vm_nt_map_lock(map);
if (not vm_nt_map_lookup_entry(map, start, @entry)) then
begin
entry:=entry^.next;
end else
begin
entry:=entry;
end;
while (entry<>@map^.header) and (entry^.start<__end) do
begin
base:=entry^.start;
size:=entry^.__end;
if (base<start) then
begin
base:=start;
end;
if (size>__end) then
begin
size:=__end;
end;
size:=size-base;
case advise of
MADV_WILLNEED:md_activate(Pointer(base),size);
//
MADV_DONTNEED:md_dontneed(Pointer(base),size);
MADV_FREE :md_dontneed(Pointer(base),size);
//
MADV_NORMAL: //internal only
if (md_activate(Pointer(base),size)=0) then
begin
//page is restored, zero it
mirror:=vm_nt_map_mirror(map,base,base+size);
if (mirror<>nil) then
begin
ZeroPages (mirror,size);
md_placeholder_unmap(mirror,size);
end;
end;
else;
end;
//ignore errors
entry:=entry^.next;
end;
vm_nt_map_unlock(map);
end;
function vm_nt_map_mirror(map :p_vm_nt_map;
start:vm_offset_t;
__end:vm_offset_t):Pointer;
var
entry:p_vm_nt_entry;
base,b_end,size,prev:vm_size_t;
offset:vm_ooffset_t;
curr:Pointer;
obj:p_vm_nt_file_obj;
max:Integer;
r:Integer;
begin
Result:=nil;
if (start=__end) then Exit;
r:=md_placeholder_mmap(Result,__end-start);
if (r<>0) then
begin
Writeln('failed md_placeholder_mmap(',HexStr(__end-start,11),'):0x',HexStr(r,8));
Assert(false,'vm_map');
Exit;
end;
vm_nt_map_lock(map);
if (not vm_nt_map_lookup_entry(map, start, @entry)) then
begin
entry:=entry^.next;
end;
prev:=0;
while (entry<>@map^.header) and (entry^.start<__end) do
begin
obj:=entry^.obj;
if (obj<>nil) then
if (obj^.hfile<>0) then
begin
base :=entry^.start;
b_end :=entry^.__end;
offset:=entry^.offset;
if (base<start) then
begin
offset:=offset+(start-base);
base:=start;
end;
if (b_end>__end) then
begin
b_end:=__end;
end;
size:=b_end-base;
//addr in mirror
curr:=Result+(base-start);
if ((base<>start) and (base<>prev)) or
(b_end<>__end) then
begin
r:=md_placeholder_split(curr,size);
if (r<>0) then
begin
Writeln('failed md_placeholder_split(',HexStr(curr),',',HexStr(curr+size),'):0x',HexStr(r,8));
Assert(false,'vm_map');
end;
end;
prev:=b_end;
max:=obj^.maxp;
r:=md_placeholder_commit(curr,
size,
(max and VM_RW),
obj^.hfile,
offset);
if (r<>0) then
begin
Writeln('failed md_placeholder_commit(',HexStr(curr),',',HexStr(curr+size),'):0x',HexStr(r,8));
Assert(false,'vm_map');
end;
end;
entry:=entry^.next;
end;
vm_nt_map_unlock(map);
end;
function vm_nt_map_fetch(map :p_vm_nt_map;
start :vm_offset_t;
__end :vm_offset_t;
var p__start:vm_offset_t;
var p____end:vm_offset_t;
var p_offset:vm_offset_t;
var p____obj:p_vm_nt_file_obj
):Boolean;
var
entry:p_vm_nt_entry;
obj:p_vm_nt_file_obj;
begin
Result:=False;
if (start=__end) then Exit;
vm_nt_map_lock(map);
if (not vm_nt_map_lookup_entry(map, start, @entry)) then
begin
entry:=entry^.next;
end;
if (entry<>@map^.header) and (entry^.start<__end) then
begin
p__start:=entry^.start;
p____end:=entry^.__end;
p_offset:=entry^.offset;
p____obj:=nil;
obj:=entry^.obj;
if (obj<>nil) then
if (obj^.hfile<>0) then
begin
p____obj:=obj;
end;
Result:=True;
end;
vm_nt_map_unlock(map);
end;
end.