unit vm_nt_sub_map; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses sysutils, vm, vm_pmap_prot; type pp_vm_nt_sub_entry=^p_vm_nt_sub_entry; p_vm_nt_sub_entry=^vm_nt_sub_entry; vm_nt_sub_entry=packed record prev :p_vm_nt_sub_entry; // previous entry next :p_vm_nt_sub_entry; // next entry left :p_vm_nt_sub_entry; // left child in binary search tree right:p_vm_nt_sub_entry; // right child in binary search tree start:vm_offset_t; // start address __end:vm_offset_t; // end address // decl_prot:Byte; // declared protection (with tracking bits) real_prot:Byte; // real page protection align :Word; end; p_vm_nt_sub_map=^t_vm_nt_sub_map; t_vm_nt_sub_map=object header:vm_nt_sub_entry; // List of entries root :p_vm_nt_sub_entry; // Root of a binary search tree property min_offset:vm_offset_t read header.start write header.start; property max_offset:vm_offset_t read header.__end write header.__end; end; procedure vm_nt_sub_map_init(map:p_vm_nt_sub_map;min,max:vm_offset_t); function vm_nt_sub_map_insert( map :p_vm_nt_sub_map; start:vm_offset_t; __end:vm_offset_t; prot :Byte):Integer; // Move nodes from one submap to another procedure vm_nt_sub_map_move(dst,src:p_vm_nt_sub_map); procedure vm_nt_sub_map_clip(dst,src:p_vm_nt_sub_map); procedure vm_nt_sub_map_free(map:p_vm_nt_sub_map); //Change protection taking into account tracking function vm_nt_sub_map_protect(map :p_vm_nt_sub_map; start:vm_offset_t; __end:vm_offset_t; prot :Byte):Integer; //Change protection tracking function vm_nt_sub_map_tracking(map :p_vm_nt_sub_map; start:vm_offset_t; __end:vm_offset_t; prot :Byte):Integer; //Update page protection by mode procedure vm_nt_sub_map_prot_fixup(map :p_vm_nt_sub_map; start:vm_offset_t; __end:vm_offset_t; mode :Integer); implementation uses md_map; procedure vm_nt_sub_map_init(map:p_vm_nt_sub_map;min,max:vm_offset_t); inline; begin map^:=Default(t_vm_nt_sub_map); map^.header.next:=@map^.header; map^.header.prev:=@map^.header; map^.min_offset :=min; map^.max_offset :=max; end; function vm_nt_sub_entry_create(map:p_vm_nt_sub_map):p_vm_nt_sub_entry; var new_entry:p_vm_nt_sub_entry; begin new_entry:=AllocMem(SizeOf(vm_nt_sub_entry)); Assert((new_entry<>nil),'vm_nt_sub_entry_create: kernel resources exhausted'); Result:=new_entry; end; procedure vm_nt_sub_entry_dispose(map:p_vm_nt_sub_map;entry:p_vm_nt_sub_entry); inline; begin FreeMem(entry); end; function vm_nt_sub_entry_splay(addr:vm_offset_t;root:p_vm_nt_sub_entry):p_vm_nt_sub_entry; var llist,rlist:p_vm_nt_sub_entry; ltree,rtree:p_vm_nt_sub_entry; y :p_vm_nt_sub_entry; begin 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; 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_sub_entry_link( map :p_vm_nt_sub_map; after_where:p_vm_nt_sub_entry; entry :p_vm_nt_sub_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_sub_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_sub_entry_unlink( map :p_vm_nt_sub_map; entry:p_vm_nt_sub_entry); var next,prev,root:p_vm_nt_sub_entry; begin if (entry<>map^.root) then begin vm_nt_sub_entry_splay(entry^.start, map^.root); end; if (entry^.left=nil) then begin root:=entry^.right; end else begin root:=vm_nt_sub_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_sub_map_lookup_entry( map :p_vm_nt_sub_map; address:vm_offset_t; entry :pp_vm_nt_sub_entry):Boolean; var cur:p_vm_nt_sub_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_sub_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; procedure vm_nt_sub_simplify_entry(map:p_vm_nt_sub_map;entry:p_vm_nt_sub_entry); forward; function vm_nt_sub_map_insert( map :p_vm_nt_sub_map; start:vm_offset_t; __end:vm_offset_t; prot :Byte):Integer; var new_entry :p_vm_nt_sub_entry; prev_entry :p_vm_nt_sub_entry; temp_entry :p_vm_nt_sub_entry; begin if (startmap^.max_offset) or (start>=__end) then begin Exit(KERN_INVALID_ADDRESS); end; if vm_nt_sub_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; if (prev_entry<>@map^.header) and (prev_entry^.decl_prot=prot) and (prev_entry^.real_prot=0) and (prev_entry^.__end=start) then begin prev_entry^.__end:=__end; vm_nt_sub_simplify_entry(map, prev_entry); Exit(KERN_SUCCESS); end; new_entry:=vm_nt_sub_entry_create(map); new_entry^.start:=start; new_entry^.__end:=__end; new_entry^.decl_prot:=prot; new_entry^.real_prot:=0; //do fixup vm_nt_sub_entry_link(map, prev_entry, new_entry); vm_nt_sub_simplify_entry(map, new_entry); Result:=KERN_SUCCESS; end; procedure vm_nt_sub_map_entry_delete(map:p_vm_nt_sub_map;entry:p_vm_nt_sub_entry); inline; begin vm_nt_sub_entry_unlink (map, entry); vm_nt_sub_entry_dispose(map, entry); end; procedure vm_nt_sub_simplify_entry(map:p_vm_nt_sub_map;entry:p_vm_nt_sub_entry); var next,prev:p_vm_nt_sub_entry; begin prev:=entry^.prev; if (prev<>@map^.header) then begin if (prev^.__end=entry^.start) and (prev^.decl_prot=entry^.decl_prot) and (prev^.real_prot=entry^.real_prot) then begin vm_nt_sub_entry_unlink(map, prev); entry^.start:=prev^.start; vm_nt_sub_entry_dispose(map, prev); end; end; next:=entry^.next; if (next<>@map^.header) then begin if (entry^.__end=next^.start) and (next^.decl_prot=entry^.decl_prot) and (next^.real_prot=entry^.real_prot) then begin vm_nt_sub_entry_unlink(map, next); entry^.__end:=next^.__end; vm_nt_sub_entry_dispose(map, next); end; end; end; procedure _vm_nt_sub_map_clip_start(map:p_vm_nt_sub_map;entry:p_vm_nt_sub_entry;start:vm_offset_t); var new_entry:p_vm_nt_sub_entry; begin Assert(startentry^.start) then begin _vm_nt_sub_map_clip_start(map,entry,start); end; end; procedure _vm_nt_sub_map_clip_end(map:p_vm_nt_sub_map;entry:p_vm_nt_sub_entry;__end:vm_offset_t); var new_entry:p_vm_nt_sub_entry; begin Assert(__end>entry^.start); new_entry:=vm_nt_sub_entry_create(map); new_entry^:=entry^; new_entry^.start:=__end; entry^.__end :=__end; vm_nt_sub_entry_link(map, entry, new_entry); end; procedure vm_nt_sub_map_clip_end(map:p_vm_nt_sub_map;entry:p_vm_nt_sub_entry;__end:vm_offset_t); inline; begin if (__end@src^.header) do begin // if (after=nil) then begin vm_nt_sub_map_lookup_entry(dst, entry^.__end, @after); //get first position of insert end; // next:=entry^.next; // vm_nt_sub_entry_unlink (src,entry); vm_nt_sub_entry_link (dst,after,entry); vm_nt_sub_simplify_entry(dst,entry); // after:=entry; // entry:=next; end; end; procedure vm_nt_sub_map_clip(dst,src:p_vm_nt_sub_map); var entry,next,after:p_vm_nt_sub_entry; begin Assert((dst^.min_offset=src^.min_offset) or (dst^.max_offset=src^.max_offset),'vm_nt_sub_map_clip'); { Writeln('[vm_nt_sub_map_clip]'); Writeln(' dst:',HexStr(dst^.min_offset,16),':',HexStr(dst^.max_offset,16)); Writeln(' src:',HexStr(src^.min_offset,16),':',HexStr(src^.max_offset,16)); } entry:=nil; if (vm_nt_sub_map_lookup_entry(src, dst^.min_offset, @entry)) then begin vm_nt_sub_map_clip_start(src, entry, dst^.min_offset); //devide src by dst end else begin entry:=entry^.next; end; // after:=nil; // while (entry<>@src^.header) and (entry^.start@map^.header) do begin next:=entry^.next; // vm_nt_sub_map_entry_delete(map,entry); // entry:=next; end; end; // function vm_nt_sub_map_protect(map :p_vm_nt_sub_map; start:vm_offset_t; __end:vm_offset_t; prot :Byte):Integer; var entry:p_vm_nt_sub_entry; base,size:vm_size_t; mask:Integer; r:Integer; begin if (start=__end) then begin Exit(KERN_SUCCESS); end; if (vm_nt_sub_map_lookup_entry(map, start, @entry)) then begin vm_nt_sub_map_clip_start(map, entry, start); end else begin entry:=entry^.next; end; while (entry<>@map^.header) and (entry^.start<__end) do begin vm_nt_sub_map_clip_end(map, entry, __end); //save trac, change prot prot:=(prot and VM_RW) or (entry^.decl_prot and PAGE_TRACK_RWX); entry^.decl_prot:=prot; //masking mask:=not (prot shr PAGE_TRACK_SHIFT); prot:=(prot and VM_RW) and mask; if (prot<>(entry^.real_prot and VM_RW)) then begin base:=entry^.start; size:=entry^.__end-entry^.start; r:=md_protect(Pointer(base),size,prot); if (r<>0) then begin Writeln('failed md_protect(',HexStr(base,11),',',HexStr(base+size,11),'):0x',HexStr(r,8)); Assert(false,'vm_nt_sub_map_protect'); end; //save changes entry^.real_prot:=prot; end; vm_nt_sub_simplify_entry(map, entry); entry:=entry^.next; end; Result:=(KERN_SUCCESS); end; function vm_nt_sub_map_tracking(map :p_vm_nt_sub_map; start:vm_offset_t; __end:vm_offset_t; prot :Byte):Integer; var entry:p_vm_nt_sub_entry; base,size:vm_size_t; mask:Integer; r:Integer; begin if (start=__end) then begin Exit(KERN_SUCCESS); end; if (vm_nt_sub_map_lookup_entry(map, start, @entry)) then begin vm_nt_sub_map_clip_start(map, entry, start); end else begin entry:=entry^.next; end; while (entry<>@map^.header) and (entry^.start<__end) do begin vm_nt_sub_map_clip_end(map, entry, __end); //save prot, change track prot:=(prot and PAGE_TRACK_RWX) or (entry^.decl_prot and VM_RW); entry^.decl_prot:=prot; //masking mask:=not (prot shr PAGE_TRACK_SHIFT); prot:=(prot and VM_RW) and mask; if (prot<>(entry^.real_prot and VM_RW)) then begin base:=entry^.start; size:=entry^.__end-entry^.start; r:=md_protect(Pointer(base),size,prot); if (r<>0) then begin Writeln('failed md_protect(',HexStr(base,11),',',HexStr(base+size,11),'):0x',HexStr(r,8)); Assert(false,'vm_nt_sub_map_protect'); end; //save changes entry^.real_prot:=prot; end; vm_nt_sub_simplify_entry(map, entry); entry:=entry^.next; end; Result:=(KERN_SUCCESS); end; procedure vm_nt_sub_map_prot_fixup(map :p_vm_nt_sub_map; start:vm_offset_t; __end:vm_offset_t; mode :Integer); var entry :p_vm_nt_sub_entry; e_start:vm_offset_t; e___end:vm_offset_t; base,size:vm_size_t; prot:Integer; mask:Integer; r:Integer; begin if (start=__end) then Exit; if (vm_nt_sub_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 (e_start__end) then begin e___end:=__end; end; if (e_start<>e___end) then begin prot:=entry^.decl_prot; //TRACK_PROT Take tracking bits into account if ((mode and TRACK_PROT)=0) then begin prot:=(prot and VM_RW); end else begin mask:=not (prot shr PAGE_TRACK_SHIFT); prot:=(prot and VM_RW) and mask; end; //REMAP_PROT Ignore protect bit checking if ((mode and REMAP_PROT)<>0) or (prot<>(entry^.real_prot and VM_RW)) then begin base:=e_start; size:=e___end-e_start; r:=md_protect(Pointer(base),size,prot); if (r<>0) then begin Writeln('failed md_protect(',HexStr(base,11),',',HexStr(base+size,11),'):0x',HexStr(r,8)); Assert(false,'vm_nt_sub_map_prot_fixup'); end; //save changes entry^.real_prot:=prot; end; end; entry:=entry^.next; end; end; end.