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 (startmap^.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 (addrnil) 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 (startentry^.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@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@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 (__ende_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.