FPPS4/sys/vm/rmem_map.pas

946 lines
18 KiB
Plaintext

unit rmem_map;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
mqueue,
vm,
vmparam,
kern_mtx;
type
p_rmem_vaddr_instance=^t_rmem_vaddr_instance;
t_rmem_vaddr_instance=record
entry:TAILQ_ENTRY; //p_rmem_map_entry->vaddr
vaddr:QWORD;
end;
pp_rmem_map_entry=^p_rmem_map_entry;
p_rmem_map_entry=^t_rmem_map_entry;
t_rmem_map_entry=packed record
prev :p_rmem_map_entry; // previous entry
next :p_rmem_map_entry; // next entry
left :p_rmem_map_entry; // left child in binary search tree
right:p_rmem_map_entry; // right child in binary search tree
start:QWORD; // start address
__end:QWORD; // end address
vlist:TAILQ_HEAD; // virtual addr mapping
count:QWORD;
end;
p_rmem_map=^t_rmem_map;
t_rmem_map=object
header :t_rmem_map_entry; // List of entries
lock :mtx; // Lock for map data
root :p_rmem_map_entry; // Root of a binary search tree
nentries:QWORD; // Number of entries
tmap :Pointer; // p_vm_track_map
property min_offset:QWORD read header.start write header.start;
property max_offset:QWORD read header.__end write header.__end;
end;
procedure rmem_map_process_deferred;
procedure rmem_map_lock(map:p_rmem_map);
procedure rmem_map_unlock(map:p_rmem_map;def:Boolean=True);
function rmem_map_locked(map:p_rmem_map):Boolean; inline;
procedure rmem_map_init(map:p_rmem_map;min,max:QWORD);
function rmem_map_lookup_entry(
map :p_rmem_map;
address:QWORD;
entry :pp_rmem_map_entry):Boolean;
function rmem_map_test(map:p_rmem_map;
start,__end:QWORD;
mode:Integer):Boolean;
function rmem_map_insert(map:p_rmem_map;
vaddr:QWORD;
start,__end:QWORD):Integer;
function rmem_map_delete(map:p_rmem_map;
vaddr:QWORD;
start,__end:QWORD):Integer;
procedure rmem_map_track(map:p_rmem_map;
start,__end,source:QWORD;
tobj:Pointer);
implementation
uses
errno,
kern_thr,
vm_tracking_map;
function AlignUp(addr:PtrUInt;alignment:PtrUInt):PtrUInt; inline;
var
tmp:PtrUInt;
begin
if (alignment=0) then Exit(addr);
tmp:=addr+PtrUInt(alignment-1);
Result:=tmp-(tmp mod alignment)
end;
function AlignDw(addr:PtrUInt;alignment:PtrUInt):PtrUInt; inline;
begin
Result:=addr-(addr mod alignment);
end;
//
procedure _rmem_entry_add_vaddr(entry:p_rmem_map_entry;vaddr:QWORD);
var
node:p_rmem_vaddr_instance;
begin
node:=AllocMem(SizeOf(t_rmem_vaddr_instance));
node^.vaddr:=vaddr;
TAILQ_INSERT_TAIL(@entry^.vlist,node,@node^.entry);
Inc(entry^.count);
end;
function rmem_entry_add_vaddr(entry:p_rmem_map_entry;vaddr:QWORD):Boolean;
var
node:p_rmem_vaddr_instance;
begin
node:=TAILQ_FIRST(@entry^.vlist);
while (node<>nil) do
begin
if (node^.vaddr=vaddr) then
begin
Exit(False);
end;
node:=TAILQ_NEXT(node,@node^.entry);
end;
//if not one vaddr
Result:=(TAILQ_FIRST(@entry^.vlist)<>nil);
_rmem_entry_add_vaddr(entry,vaddr);
end;
procedure rmem_entry_add_track(tmap:Pointer;entry:p_rmem_map_entry;dst:QWORD);
var
node:p_rmem_vaddr_instance;
size:vm_offset_t;
vaddr:QWORD;
begin
//try add mirror track
size:=(entry^.__end-entry^.start);
vm_track_map_lock(tmap);
node:=TAILQ_FIRST(@entry^.vlist);
while (node<>nil) do
begin
vaddr:=node^.vaddr;
if (vaddr<>dst) then
begin
_vm_track_map_insert_mirror(tmap,vaddr,vaddr+size,dst);
end;
node:=TAILQ_NEXT(node,@node^.entry);
end;
vm_track_map_unlock(tmap);
end;
function _rmem_entry_del_node(entry:p_rmem_map_entry;node:p_rmem_vaddr_instance):Boolean;
begin
Dec(entry^.count);
TAILQ_REMOVE(@entry^.vlist,node,@node^.entry);
FreeMem(node);
Result:=(TAILQ_FIRST(@entry^.vlist)=nil);
end;
function rmem_entry_del_vaddr(entry:p_rmem_map_entry;vaddr:QWORD):Boolean;
var
node:p_rmem_vaddr_instance;
begin
node:=TAILQ_FIRST(@entry^.vlist);
while (node<>nil) do
begin
if (node^.vaddr=vaddr) then
begin
Result:=_rmem_entry_del_node(entry,node);
Exit;
end;
node:=TAILQ_NEXT(node,@node^.entry);
end;
Result:=False;
end;
procedure rmem_entry_del_vaddr_all(entry:p_rmem_map_entry);
var
node,next:p_rmem_vaddr_instance;
begin
node:=TAILQ_FIRST(@entry^.vlist);
while (node<>nil) do
begin
next:=TAILQ_NEXT(node,@node^.entry);
_rmem_entry_del_node(entry,node);
node:=next;
end;
end;
//
function in_vaddr_list(const b:TAILQ_HEAD;vaddr:QWORD):Boolean;
var
node:p_rmem_vaddr_instance;
begin
Result:=False;
node:=TAILQ_FIRST(@b);
while (node<>nil) do
begin
if (node^.vaddr=vaddr) then
begin
Exit(True);
end;
node:=TAILQ_NEXT(node,@node^.entry);
end;
end;
function compare_vaddr_list(a:p_rmem_map_entry;offset:QWORD;b:p_rmem_map_entry):Boolean;
var
node:p_rmem_vaddr_instance;
begin
if (a^.count<>b^.count) then
begin
Exit(False);
end;
node:=TAILQ_FIRST(@a^.vlist);
while (node<>nil) do
begin
if not in_vaddr_list(b^.vlist,node^.vaddr + offset) then
begin
Exit(False);
end;
node:=TAILQ_NEXT(node,@node^.entry);
end;
Result:=True;
end;
procedure inc_vaddr_list(src:p_rmem_map_entry;offset:QWORD);
var
node:p_rmem_vaddr_instance;
begin
node:=TAILQ_FIRST(@src^.vlist);
while (node<>nil) do
begin
node^.vaddr:=node^.vaddr + offset;
node:=TAILQ_NEXT(node,@node^.entry);
end;
end;
procedure copy_vaddr_list(src,dst:p_rmem_map_entry;offset:QWORD);
var
node:p_rmem_vaddr_instance;
begin
TAILQ_INIT(@dst^.vlist); //init
dst^.count:=0;
node:=TAILQ_FIRST(@src^.vlist);
while (node<>nil) do
begin
_rmem_entry_add_vaddr(dst,node^.vaddr + offset);
node:=TAILQ_NEXT(node,@node^.entry);
end;
end;
//
procedure rmem_entry_deallocate(entry:p_rmem_map_entry);
begin
rmem_entry_del_vaddr_all(entry);
//
Freemem(entry);
end;
procedure rmem_map_RANGE_CHECK(map:p_rmem_map;var start,__end:QWORD);
begin
if (start<map^.min_offset) then
begin
start:=map^.min_offset;
end;
if (__end>map^.max_offset) then
begin
__end:=map^.max_offset;
end;
if (start>__end) then
begin
start:=__end;
end;
end;
procedure rmem_map_lock(map:p_rmem_map);
begin
mtx_lock(map^.lock);
end;
procedure rmem_map_process_deferred;
var
td:p_kthread;
entry,next:p_rmem_map_entry;
begin
td:=curkthread;
if (td=nil) then Exit;
entry:=td^.td_rmap_def_user;
td^.td_rmap_def_user:=nil;
while (entry<>nil) do
begin
next:=entry^.next;
rmem_entry_deallocate(entry);
entry:=next;
end;
end;
procedure rmem_map_unlock(map:p_rmem_map;def:Boolean=True);
begin
mtx_unlock(map^.lock);
if def then
begin
rmem_map_process_deferred;
end;
end;
function rmem_map_locked(map:p_rmem_map):Boolean; inline;
begin
Result:=mtx_owned(map^.lock);
end;
procedure RMEM_MAP_ASSERT_LOCKED(map:p_rmem_map); inline;
begin
Assert(rmem_map_locked(map));
end;
procedure _rmem_map_init(map:p_rmem_map;min,max:QWORD); inline;
begin
map^.header.next:=@map^.header;
map^.header.prev:=@map^.header;
map^.min_offset :=min;
map^.max_offset :=max;
map^.nentries:=0;
map^.root:=nil;
end;
procedure rmem_map_init(map:p_rmem_map;min,max:QWORD);
begin
_rmem_map_init(map, min, max);
mtx_init(map^.lock,'rmap');
end;
function rmem_entry_create(map:p_rmem_map):p_rmem_map_entry;
var
new_entry:p_rmem_map_entry;
begin
new_entry:=AllocMem(SizeOf(t_rmem_map_entry));
Assert((new_entry<>nil),'rmem_map_entry_create: kernel resources exhausted');
TAILQ_INIT(@new_entry^.vlist);
Result:=new_entry;
end;
function rmem_entry_splay(addr:QWORD;root:p_rmem_map_entry):p_rmem_map_entry;
var
llist,rlist:p_rmem_map_entry;
ltree,rtree:p_rmem_map_entry;
y :p_rmem_map_entry;
begin
{ Special case of empty tree. }
if (root=nil) then Exit(root);
llist:=nil;
rlist:=nil;
repeat
{ root is never nil in here. }
if (addr<root^.start) then
begin
y:=root^.left;
if (y=nil) then break;
if (addr<y^.start) and (y^.left<>nil) then
begin
{ Rotate right and put y on rlist. }
root^.left:=y^.right;
y^.right:=root;
root:=y^.left;
y^.left:=rlist;
rlist:=y;
end else
begin
{ Put root on rlist. }
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
{ Rotate left and put y on llist. }
root^.right:=y^.left;
y^.left:=root;
root:=y^.right;
y^.right:=llist;
llist:=y;
end else
begin
{ Put root on llist. }
root^.right:=llist;
llist:=root;
root:=y;
end;
end else
begin
break;
end;
until false;
{
* Pass Two: Walk back up the two spines, flip the pointers
* and set max_free. The subtrees of the root go at the
* bottom of llist and rlist.
}
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;
{
* Final assembly: add ltree and rtree as subtrees of root.
}
root^.left:=ltree;
root^.right:=rtree;
Result:=(root);
end;
procedure rmem_entry_link(
map :p_rmem_map;
after_where:p_rmem_map_entry;
entry :p_rmem_map_entry);
begin
RMEM_MAP_ASSERT_LOCKED(map);
Inc(map^.nentries);
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
rmem_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 rmem_entry_unlink(
map :p_rmem_map;
entry :p_rmem_map_entry);
var
next,prev,root:p_rmem_map_entry;
begin
RMEM_MAP_ASSERT_LOCKED(map);
if (entry<>map^.root) then
begin
rmem_entry_splay(entry^.start, map^.root);
end;
if (entry^.left=nil) then
begin
root:=entry^.right;
end else
begin
root:=rmem_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;
Dec(map^.nentries);
end;
function rmem_map_lookup_entry(
map :p_rmem_map;
address:QWORD;
entry :pp_rmem_map_entry):Boolean;
var
cur:p_rmem_map_entry;
begin
RMEM_MAP_ASSERT_LOCKED(map);
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:=rmem_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 rmem_map_test(map:p_rmem_map;
start,__end:QWORD;
mode:Integer):Boolean;
var
entry:p_rmem_map_entry;
prev:QWORD;
begin
if not rmem_map_lookup_entry(map,start,@entry) then
begin
Exit(False);
end;
prev:=entry^.start;
while (entry<>@map^.header) and (entry^.start<__end) do
begin
if (mode=0) then
begin
if (__end>entry^.start) and (start<entry^.__end) then
begin
Exit(False);
end;
end else
begin
if (prev<>entry^.start) then
begin
Exit(False);
end;
prev:=entry^.__end;
end;
entry:=entry^.next;
end;
Result:=True;
end;
function rmem_map_insert_internal(
map :p_rmem_map;
after:p_rmem_map_entry;
start,__end:QWORD):p_rmem_map_entry;
var
new_entry:p_rmem_map_entry;
begin
RMEM_MAP_ASSERT_LOCKED(map);
if (after<>@map^.header) and
(start<after^.__end) then
begin
start:=after^.__end;
end;
if (after^.next<>@map^.header) and
(after^.next^.start<__end) then
begin
__end:=after^.next^.start;
end;
if (start>=__end) then
begin
Exit(after);
end;
{
* Create a new after
}
new_entry:=rmem_entry_create(map);
new_entry^.start:=start;
new_entry^.__end:=__end;
{
* Insert the new after into the list
}
rmem_entry_link(map, after, new_entry);
//rmem_map_simplify_entry(map, new_entry);
Result:=new_entry;
end;
procedure rmem_map_simplify_entry(map:p_rmem_map;entry:p_rmem_map_entry);
var
next,prev:p_rmem_map_entry;
prevsize,esize:QWORD;
begin
prev:=entry^.prev;
if (prev<>@map^.header) then
begin
prevsize:=prev^.__end - prev^.start;
if (prev^.__end=entry^.start) and
compare_vaddr_list(prev,prevsize,entry) then
begin
rmem_entry_unlink(map, prev);
entry^.start:=prev^.start;
//Move prev->entry
rmem_entry_del_vaddr_all(entry);
entry^.vlist:=prev^.vlist;
TAILQ_INIT(@prev^.vlist);
rmem_entry_deallocate(prev);
end;
end;
next:=entry^.next;
if (next<>@map^.header) then
begin
esize:=entry^.__end - entry^.start;
if (entry^.__end=next^.start) and
compare_vaddr_list(entry,esize,next) then
begin
rmem_entry_unlink(map, next);
entry^.__end:=next^.__end;
rmem_entry_deallocate(next);
end;
end;
end;
procedure _rmem_map_clip_start(map:p_rmem_map;entry:p_rmem_map_entry;start:QWORD);
var
new_entry:p_rmem_map_entry;
begin
RMEM_MAP_ASSERT_LOCKED(map);
rmem_map_simplify_entry(map, entry);
new_entry:=rmem_entry_create(map);
new_entry^:=entry^;
new_entry^.__end:=start;
copy_vaddr_list(entry,new_entry,0);
inc_vaddr_list(entry,(start - entry^.start));
entry^.start:=start;
rmem_entry_link(map, entry^.prev, new_entry);
end;
procedure rmem_map_clip_start(map:p_rmem_map;entry:p_rmem_map_entry;start:QWORD); inline;
begin
if (start>entry^.start) then
begin
_rmem_map_clip_start(map,entry,start);
end;
end;
procedure _rmem_map_clip_end(map:p_rmem_map;entry:p_rmem_map_entry;__end:QWORD);
var
new_entry:p_rmem_map_entry;
begin
RMEM_MAP_ASSERT_LOCKED(map);
new_entry:=rmem_entry_create(map);
new_entry^:=entry^;
new_entry^.start:=__end;
entry^.__end:=__end;
copy_vaddr_list(entry,new_entry,(__end - entry^.start));
rmem_entry_link(map, entry, new_entry);
end;
procedure rmem_map_clip_end(map:p_rmem_map;entry:p_rmem_map_entry;__end:QWORD); inline;
begin
if (__end<entry^.__end) then
begin
_rmem_map_clip_end(map,entry,__end);
end;
end;
function rmem_map_insert(map:p_rmem_map;
vaddr:QWORD;
start,__end:QWORD):Integer;
var
entry:p_rmem_map_entry;
begin
if (start=__end) then
begin
Exit(KERN_SUCCESS);
end;
if (map=nil) then
begin
Exit(KERN_INVALID_ARGUMENT);
end;
rmem_map_lock(map);
rmem_map_RANGE_CHECK(map, start, __end);
if (rmem_map_lookup_entry(map, start, @entry)) then
begin
rmem_map_clip_start(map, entry, start);
end else
begin
entry:=rmem_map_insert_internal(map,entry,start,__end);
end;
while (entry<>@map^.header) and (entry^.start<__end) do
begin
rmem_map_clip_end(map, entry, __end);
entry:=rmem_map_insert_internal(map,entry,entry^.__end,__end);
if rmem_entry_add_vaddr(entry,vaddr) then
begin
rmem_entry_add_track(map^.tmap,entry,vaddr);
end;
entry:=entry^.next;
end;
rmem_map_unlock(map);
Result:=(KERN_SUCCESS);
end;
procedure rmem_entry_delete(map:p_rmem_map;entry:p_rmem_map_entry);
begin
rmem_entry_unlink(map, entry);
begin
entry^.next:=curkthread^.td_rmap_def_user;
curkthread^.td_rmap_def_user:=entry;
end;
end;
function rmem_map_delete(map:p_rmem_map;
vaddr:QWORD;
start,__end:QWORD):Integer;
var
entry :p_rmem_map_entry;
first_entry:p_rmem_map_entry;
next :p_rmem_map_entry;
begin
RMEM_MAP_ASSERT_LOCKED(map);
if (start=__end) then
begin
Exit(KERN_SUCCESS);
end;
if (not rmem_map_lookup_entry(map, start, @first_entry)) then
begin
entry:=first_entry^.next;
end else
begin
entry:=first_entry;
rmem_map_clip_start(map, entry, start);
end;
while (entry<>@map^.header) and (entry^.start<__end) do
begin
rmem_map_clip_end(map, entry, __end);
next:=entry^.next;
if (vaddr=0) then
begin
//all
rmem_entry_delete(map, entry);
end else
if rmem_entry_del_vaddr(entry,vaddr) then
begin
//zero
rmem_entry_delete(map, entry);
end else
begin
//one
rmem_map_simplify_entry(map,entry);
next:=entry^.next;
end;
entry:=next;
end;
Result:=(KERN_SUCCESS);
end;
procedure rmem_entry_track(tmap:Pointer;entry:p_rmem_map_entry;diff,size,source:QWORD;tobj:Pointer);
var
node:p_rmem_vaddr_instance;
start:vm_offset_t;
__end:vm_offset_t;
begin
node:=TAILQ_FIRST(@entry^.vlist);
while (node<>nil) do
begin
start:=node^.vaddr+diff;
__end:=start+size;
//Writeln('rmem_entry_track:',HexStr(start,16),'..',HexStr(__end,16),'..',HexStr(source,16));
_vm_track_map_insert_deferred(tmap,start,__end,source,tobj);
node:=TAILQ_NEXT(node,@node^.entry);
end;
end;
procedure rmem_map_track(map:p_rmem_map;
start,__end,source:QWORD;
tobj:Pointer);
var
entry:p_rmem_map_entry;
e_start:QWORD;
e___end:QWORD;
diff:QWORD;
size:QWORD;
begin
rmem_map_lock(map);
vm_track_map_lock(map^.tmap);
if (rmem_map_lookup_entry(map, start, @entry)) then
begin
//
end else
begin
entry:=entry^.next;
end;
while (entry<>@map^.header) and (entry^.start<__end) do
begin
e_start:=entry^.start;
e___end:=entry^.__end;
if (start>e_start) then
begin
e_start:=start;
end;
if (__end<e___end) then
begin
e___end:=__end;
end;
if (e___end>e_start) then
begin
diff:=(e_start-entry^.start);
size:=(e___end-e_start);
rmem_entry_track(map^.tmap,
entry,
diff,
size,
source+(e_start-start),
tobj);
end;
entry:=entry^.next;
end;
vm_track_map_unlock(map^.tmap);
rmem_map_unlock(map);
end;
end.