unit dmem_map; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses mqueue, vm, vmparam, sys_vm_object, kern_mtx; Const SCE_KERNEL_MAIN_DMEM_SIZE=$180000000; //6GB //$120000000; 4GB (normal/pro?) SCE_KERNEL_WB_ONION = 0; SCE_KERNEL_WC_GARLIC = 3; SCE_KERNEL_WB_GARLIC =10; //deprecated SCE_KERNEL_WB_ONION_NONVOLATILE = 1; SCE_KERNEL_WC_GARLIC_VOLATILE = 2; SCE_KERNEL_WC_GARLIC_NONVOLATILE = 3; SCE_KERNEL_WT_ONION_VOLATILE = 4; SCE_KERNEL_WT_ONION_NONVOLATILE = 5; SCE_KERNEL_WP_ONION_VOLATILE = 6; SCE_KERNEL_WP_ONION_NONVOLATILE = 7; SCE_KERNEL_UC_GARLIC_VOLATILE = 8; SCE_KERNEL_UC_GARLIC_NONVOLATILE = 9; max_valid_dmem=QWORD($5000000000); type pp_dmem_map_entry=^p_dmem_map_entry; p_dmem_map_entry=^t_dmem_map_entry; t_dmem_map_entry=packed record prev :p_dmem_map_entry; // previous entry next :p_dmem_map_entry; // next entry left :p_dmem_map_entry; // left child in binary search tree right :p_dmem_map_entry; // right child in binary search tree start :DWORD; // start address __end :DWORD; // end address adj_free:DWORD; // amount of adjacent free space max_free:DWORD; // max free space in subtree m_type :DWORD; // memory type end; p_dmem_map=^t_dmem_map; t_dmem_map=object header :t_dmem_map_entry; // List of entries lock :mtx; // Lock for map data nentries:DWORD; // Number of entries size :DWORD; // size root :p_dmem_map_entry; // Root of a binary search tree vmap :Pointer; rmap :Pointer; function get_max_offset:DWORD; property min_offset:DWORD read header.start write header.start; property max_offset:DWORD read get_max_offset write header.__end; end; procedure dmem_map_entry_deallocate(entry:p_dmem_map_entry); procedure dmem_map_lock(map:p_dmem_map); function dmem_map_trylock(map:p_dmem_map):Boolean; procedure dmem_map_unlock(map:p_dmem_map); function dmem_map_locked(map:p_dmem_map):Boolean; inline; procedure dmem_map_init(map:p_dmem_map;min,max:QWORD); procedure dmem_map_entry_dispose(map:p_dmem_map;entry:p_dmem_map_entry); inline; function dmem_map_entry_create(map:p_dmem_map):p_dmem_map_entry; function dmem_map_lookup_entry( map :p_dmem_map; address :DWORD; entry :pp_dmem_map_entry):Boolean; function dmem_map_insert( map :p_dmem_map; start :DWORD; __end :DWORD; m_type:DWORD):Integer; Function dmem_map_query_available(map:p_dmem_map;start,__end,align:QWORD;var oaddr,osize:QWORD):Integer; Function dmem_map_query(map:p_dmem_map;offset:QWORD;flags,id:Integer;info:Pointer;size:QWORD):Integer; Function dmem_map_get_memory_type(map:p_dmem_map;info:Pointer):Integer; Function dmem_map_alloc(map:p_dmem_map;start,__end,len,align:QWORD;mtype:DWORD;var oaddr:QWORD):Integer; Function dmem_map_release(map:p_dmem_map;start,len:QWORD;check:Boolean):Integer; function dmem_map_findspace(map :p_dmem_map; start :DWORD; length:DWORD; addr :PDWORD):Integer; function dmem_map_fixed(map :p_dmem_map; start :DWORD; length :DWORD; m_type :DWORD; overwr :Integer):Integer; procedure dmem_map_simplify_entry(map:p_dmem_map;entry:p_dmem_map_entry); procedure dmem_map_entry_delete(map:p_dmem_map;entry:p_dmem_map_entry); function dmem_map_delete(map:p_dmem_map;start:DWORD;__end:DWORD):Integer; function dmem_map_set_mtype(map :p_dmem_map; start:DWORD; __end:DWORD; mtype:Integer; prot :Integer; flags:Integer):Integer; function dmem_map_get_mtype(map :p_dmem_map; obj :vm_object_t; offset:QWORD; pstart:PQWORD; p__end:PQWORD; pmtype:PInteger):Integer; implementation uses errno, kern_thr, systm, vm_map, rmem_map, kern_budget; function IDX_TO_OFF(x:QWORD):QWORD; inline; begin Result:=QWORD(x) shl PAGE_SHIFT; end; function OFF_TO_IDX(x:QWORD):QWORD; inline; begin Result:=QWORD(x) shr PAGE_SHIFT; end; function IsPowerOfTwo(x:QWORD):Boolean; inline; begin Result:=(x and (x - 1))=0; end; function fastIntLog2(i:QWORD):QWORD; inline; begin Result:=BsfQWORD(i); end; 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; function t_dmem_map.get_max_offset:DWORD; begin if (header.__end > OFF_TO_IDX(kern_budget.DMEM_LIMIT)) then begin Result:=OFF_TO_IDX(kern_budget.DMEM_LIMIT); end else begin Result:=header.__end; end; end; procedure dmem_map_entry_deallocate(entry:p_dmem_map_entry); begin Freemem(entry); end; procedure DMEM_MAP_RANGE_CHECK(map:p_dmem_map;var start,__end:DWORD); begin if (startmap^.max_offset) then begin __end:=map^.max_offset; end; if (start>__end) then begin start:=__end; end; end; procedure dmem_map_lock(map:p_dmem_map); begin mtx_lock(map^.lock); end; function dmem_map_trylock(map:p_dmem_map):Boolean; begin Result:=mtx_trylock(map^.lock); end; procedure dmem_map_unlock(map:p_dmem_map); begin mtx_unlock(map^.lock); end; function dmem_map_locked(map:p_dmem_map):Boolean; inline; begin Result:=mtx_owned(map^.lock); end; procedure DMEM_MAP_ASSERT_LOCKED(map:p_dmem_map); inline; begin Assert(dmem_map_locked(map)); end; procedure _dmem_map_init(map:p_dmem_map;min,max:DWORD); begin map^.header.next:=@map^.header; map^.header.prev:=@map^.header; map^.min_offset :=min; map^.max_offset :=max; map^.header.adj_free:=(max-min); map^.header.max_free:=(max-min); map^.nentries:=0; map^.size :=0; map^.root:=nil; end; procedure dmem_map_init(map:p_dmem_map;min,max:QWORD); begin _dmem_map_init(map, OFF_TO_IDX(min), OFF_TO_IDX(max)); mtx_init(map^.lock,'dmem'); end; procedure dmem_map_entry_dispose(map:p_dmem_map;entry:p_dmem_map_entry); inline; begin FreeMem(entry); end; function dmem_map_entry_create(map:p_dmem_map):p_dmem_map_entry; var new_entry:p_dmem_map_entry; begin new_entry:=AllocMem(SizeOf(t_dmem_map_entry)); Assert((new_entry<>nil),'dmem_map_entry_create: kernel resources exhausted'); Result:=new_entry; end; procedure dmem_map_entry_set_max_free(entry:p_dmem_map_entry); begin entry^.max_free:=entry^.adj_free; if (entry^.left<>nil) then if (entry^.left^.max_free>entry^.max_free) then begin entry^.max_free:=entry^.left^.max_free; end; if (entry^.right<>nil) then if (entry^.right^.max_free>entry^.max_free) then begin entry^.max_free:=entry^.right^.max_free; end; end; function dmem_map_entry_splay(addr:DWORD;root:p_dmem_map_entry):p_dmem_map_entry; var llist,rlist:p_dmem_map_entry; ltree,rtree:p_dmem_map_entry; y :p_dmem_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; dmem_map_entry_set_max_free(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; dmem_map_entry_set_max_free(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; dmem_map_entry_set_max_free(llist); ltree:=llist; llist:=y; end; rtree:=root^.right; while (rlist<>nil) do begin y:=rlist^.left; rlist^.left:=rtree; dmem_map_entry_set_max_free(rlist); rtree:=rlist; rlist:=y; end; { * Final assembly: add ltree and rtree as subtrees of root. } root^.left:=ltree; root^.right:=rtree; dmem_map_entry_set_max_free(root); Result:=(root); end; procedure dmem_map_entry_link( map :p_dmem_map; after_where:p_dmem_map_entry; entry :p_dmem_map_entry); var i:DWORD; begin DMEM_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 dmem_map_entry_splay(after_where^.start, map^.root); end; entry^.right:=after_where^.right; entry^.left:=after_where; after_where^.right:=nil; after_where^.adj_free:=entry^.start - after_where^.__end; dmem_map_entry_set_max_free(after_where); end else begin entry^.right:=map^.root; entry^.left:=nil; end; if (entry^.next=@map^.header) then begin i:=map^.max_offset; end else begin i:=entry^.next^.start; end; entry^.adj_free:=i-entry^.__end; dmem_map_entry_set_max_free(entry); map^.root:=entry; end; procedure dmem_map_entry_unlink( map :p_dmem_map; entry :p_dmem_map_entry); var next,prev,root:p_dmem_map_entry; i:DWORD; begin DMEM_MAP_ASSERT_LOCKED(map); if (entry<>map^.root) then begin dmem_map_entry_splay(entry^.start, map^.root); end; if (entry^.left=nil) then begin root:=entry^.right; end else begin root:=dmem_map_entry_splay(entry^.start, entry^.left); root^.right:=entry^.right; if (entry^.next=@map^.header) then begin i:=map^.max_offset; end else begin i:=entry^.next^.start; end; root^.adj_free:=i-root^.__end; dmem_map_entry_set_max_free(root); end; map^.root:=root; prev:=entry^.prev; next:=entry^.next; next^.prev:=prev; prev^.next:=next; Dec(map^.nentries); end; procedure dmem_map_entry_resize_free(map:p_dmem_map;entry:p_dmem_map_entry); begin if (entry<>map^.root) then begin map^.root:=dmem_map_entry_splay(entry^.start, map^.root); end; if (entry^.next=@map^.header) then begin entry^.adj_free:=map^.max_offset-entry^.__end; end else begin entry^.adj_free:=entry^.next^.start-entry^.__end; end; dmem_map_entry_set_max_free(entry); end; function dmem_map_lookup_entry( map :p_dmem_map; address :DWORD; entry :pp_dmem_map_entry):Boolean; var cur:p_dmem_map_entry; begin DMEM_MAP_ASSERT_LOCKED(map); { * If the map is empty, then the map entry immediately preceding * "address" is the map's header. } 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 { * Splay requires a write lock on the map. However, it only * restructures the binary search tree; it does not otherwise * change the map. Thus, the map's timestamp need not change * on a temporary upgrade. } cur:=dmem_map_entry_splay(address,cur); map^.root:=cur; { * If "address" is contained within a map entry, the new root * is that map entry. Otherwise, the new root is a map entry * immediately before or after "address". } 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 dmem_map_insert( map :p_dmem_map; start :DWORD; __end :DWORD; m_type:DWORD):Integer; var new_entry :p_dmem_map_entry; prev_entry:p_dmem_map_entry; temp_entry:p_dmem_map_entry; begin DMEM_MAP_ASSERT_LOCKED(map); { * Check that the start and end points are not bogus. } if (startmap^.max_offset) or (start>=__end) then begin Exit(EINVAL); end; { * Find the entry prior to the proposed starting address; if it's part * of an existing entry, this range is bogus. } if dmem_map_lookup_entry(map,start,@temp_entry) then begin Exit(EAGAIN); end; prev_entry:=temp_entry; { * Assert that the next entry doesn't overlap the end point. } if (prev_entry^.next<>@map^.header) and (prev_entry^.next^.start<__end) then begin Exit(EAGAIN); end; if (prev_entry<>@map^.header) and (prev_entry^.__end=start) then begin { * We were able to extend the object. Determine if we * can extend the previous map entry to include the * new range as well. } if (prev_entry^.m_type=m_type) then begin map^.size:=map^.size+(__end - prev_entry^.__end); prev_entry^.__end:=__end; //change size dmem_map_entry_resize_free(map, prev_entry); dmem_map_simplify_entry(map, prev_entry); Exit(0); end; end; { * Create a new entry } new_entry:=dmem_map_entry_create(map); new_entry^.start:=start; new_entry^.__end:=__end; new_entry^.m_type:=m_type; { * Insert the new entry into the list } dmem_map_entry_link(map, prev_entry, new_entry); map^.size:=map^.size+(new_entry^.__end - new_entry^.start); dmem_map_simplify_entry(map, new_entry); Result:=0; end; Function dmem_map_query_available(map:p_dmem_map;start,__end,align:QWORD;var oaddr,osize:QWORD):Integer; var entry:p_dmem_map_entry; r_addr,r_size:QWORD; t_aligned:QWORD; t_sizeof :QWORD; t_startof:QWORD; t_endof :QWORD; t_freeof :QWORD; begin Result:=0; if not IsPowerOfTwo(align) then begin Exit(EINVAL); end; if (alignmax_valid_dmem) then start:=max_valid_dmem; __end:=(not (__end shr 63)) and __end; if (__end>max_valid_dmem) then __end:=max_valid_dmem; start:=AlignUp(start,align); dmem_map_lock(map); if (map^.root=nil) then begin if (start>=IDX_TO_OFF(map^.max_offset)) then begin r_addr:=0; r_size:=0; Result:=ENOMEM; end else begin r_addr:=start; r_size:=IDX_TO_OFF(map^.max_offset)-start; Result:=0; end; end else begin r_addr:=0; r_size:=0; Result:=ENOMEM; map^.root:=dmem_map_entry_splay(OFF_TO_IDX(start), map^.root); entry:=map^.root; while (entry<>nil) and (entry<>@map^.header) do begin if (entry^.adj_free<>0) then begin t_startof:=IDX_TO_OFF(entry^.__end); //start of free space if (t_startof__end) then //clamp begin t_endof:=__end; end; if (t_endof>t_aligned) then begin t_sizeof:=t_endof-t_aligned; //size of aligned free space if (t_sizeof>r_size) then begin //save the larger space r_addr:=t_aligned; r_size:=t_sizeof; end; Result:=0; //mark something found end; end; entry:=entry^.next; end; end; dmem_map_unlock(map); oaddr:=r_addr; osize:=r_size; end; type pSceKernelDirectMemoryQueryInfo=^TSceKernelDirectMemoryQueryInfo; TSceKernelDirectMemoryQueryInfo=packed record start:QWORD; __end:QWORD; mtype:Integer; align:Integer; end; Function dmem_map_query(map:p_dmem_map;offset:QWORD;flags,id:Integer;info:Pointer;size:QWORD):Integer; var data:TSceKernelDirectMemoryQueryInfo; entry:p_dmem_map_entry; index:DWORD; begin Result:=0; if (flags>1) then begin Exit(EINVAL); end; Assert(id=0,'dmem_map_query (id<>0)'); data:=Default(TSceKernelDirectMemoryQueryInfo); Result:=EACCES; dmem_map_lock(map); if (map^.root<>nil) then begin index:=OFF_TO_IDX(offset); map^.root:=dmem_map_entry_splay(index, map^.root); entry:=map^.root; if ((flags and 1)=0) then begin if (entry<>nil) then if (entry^.start<=index) and (entry^.__end>index) then begin Result:=0; end; end else begin while (entry<>nil) and (entry<>@map^.header) do begin if (entry^.m_type<>DWORD(-1)) and (entry^.__end>index) then begin Result:=0; Break; end; entry:=entry^.next; end; end; if (Result=0) then begin data.start:=IDX_TO_OFF(entry^.start); data.__end:=IDX_TO_OFF(entry^.__end); data.mtype:=entry^.m_type; end; end; dmem_map_unlock(map); if (Result<>0) then Exit; if (size>sizeof(TSceKernelDirectMemoryQueryInfo)) then begin size:=sizeof(TSceKernelDirectMemoryQueryInfo); end; Result:=copyout(@data,info,size); end; type PGetDirectMemoryType=^TGetDirectMemoryType; TGetDirectMemoryType=packed record start :QWORD; //in start_out:QWORD; //out __end_out:QWORD; //out mtype_out:DWORD; //out align :Integer; end; Function dmem_map_get_memory_type(map:p_dmem_map;info:Pointer):Integer; var data:PGetDirectMemoryType; entry:p_dmem_map_entry; index:DWORD; begin data:=info; Result:=ENOENT; dmem_map_lock(map); if (map^.root<>nil) then begin index:=OFF_TO_IDX(data^.start); map^.root:=dmem_map_entry_splay(index, map^.root); entry:=map^.root; if (entry<>nil) then if (entry^.start<=index) and (entry^.__end>index) then begin Result:=0; end; if (Result=0) then begin data^.start_out:=IDX_TO_OFF(entry^.start); data^.__end_out:=IDX_TO_OFF(entry^.__end); data^.mtype_out:=entry^.m_type; end; end; dmem_map_unlock(map); end; Function dmem_map_alloc(map:p_dmem_map;start,__end,len,align:QWORD;mtype:DWORD;var oaddr:QWORD):Integer; var adr_dw:DWORD; begin Result:=0; if (Int64(__end or start) < 0) then begin Exit(EINVAL); end; if (Int64(len) < 1) then begin Exit(EINVAL); end; if (mtype>10) then begin Exit(EINVAL); end; if not IsPowerOfTwo(align) then begin Exit(EINVAL); end; if (( (align and QWORD($8000000000003fff)) or QWORD(len and QWORD(PAGE_MASK)) )<>0) then begin Exit(EINVAL); end; if (align=0) then align:=1; start:=(not (start shr 63)) and start; if (__end>max_valid_dmem) then __end:=max_valid_dmem; if (__end <= start) then begin Exit(EAGAIN); end; if (__end < len) then begin Exit(EAGAIN); end; if ((__end - len) < start) then begin Exit(EAGAIN); end; if (((align - 1) + __end) < __end) then begin Exit(EAGAIN); end; dmem_map_lock(map); repeat adr_dw:=0; if (dmem_map_findspace(map, OFF_TO_IDX(start), OFF_TO_IDX(len), @adr_dw)<>0) then begin dmem_map_unlock(map); Exit(EAGAIN); end; start:=IDX_TO_OFF(adr_dw); start:=AlignUp(start,align); if (start>=__end) then begin dmem_map_unlock(map); Exit(EAGAIN); end; if ((start+len)>__end) then begin dmem_map_unlock(map); Exit(EAGAIN); end; Result:=dmem_map_insert(map,OFF_TO_IDX(start),OFF_TO_IDX(start+len),mtype); until (Result<>EAGAIN); dmem_map_unlock(map); if (Result=0) then begin oaddr:=start; end; end; function _dmem_map_test(map :p_dmem_map; start:DWORD; __end:DWORD):Boolean; var curr,next,entry:p_dmem_map_entry; begin Result:=True; if dmem_map_lookup_entry(map,start,@entry) then begin // end else begin entry:=entry^.next; end; //EACCES/ENOENT //one entry? multi entry? if (entry^.start>start) then begin Exit(False); end; curr:=entry; while (curr<>@map^.header) and (curr^.start<__end) do begin next:=curr^.next; if (next<>@map^.header) then if (curr^.__end<__end) and (curr^.__end<>next^.start) then begin Exit(False); end; curr:=next; end; if (curr^.__end<__end) then begin Exit(False); end; end; procedure rmem_entry_del_vmap(vmap:vm_map_t;entry:p_rmem_map_entry); var node:p_rmem_vaddr_instance; vaddr,size:QWORD; begin size:=(entry^.__end-entry^.start); node:=TAILQ_FIRST(@entry^.vlist); while (node<>nil) do begin vaddr:=node^.vaddr; vm_map_delete(vmap, vaddr, vaddr + size, False); node:=TAILQ_NEXT(node,@node^.entry); end; end; Function dmem_map_release(map:p_dmem_map;start,len:QWORD;check:Boolean):Integer; var offset:QWORD; rmap:p_rmem_map; vmap:vm_map_t; td:p_kthread; entry:p_rmem_map_entry; begin if (((len or start) and QWORD($8000000000003fff))<>0) then begin Exit(EINVAL); end; if (Int64(start) >= max_valid_dmem) then begin Exit(0); end; offset:=max_valid_dmem - start; if (Int64(len) < Int64(offset)) then begin offset:=len; end; if (offset=0) then begin Exit(0); end; dmem_map_lock(map); if check then begin if not _dmem_map_test(map,OFF_TO_IDX(start),OFF_TO_IDX(start+len)) then begin dmem_map_unlock(map); Exit(ENOENT); end; end; Result:=dmem_map_delete(map,OFF_TO_IDX(start),OFF_TO_IDX(start+len)); dmem_map_unlock(map); if (Result=0) then begin rmap:=map^.rmap; rmem_map_process_deferred; //flush rmem_map_lock(rmap); Result:=rmem_map_delete(rmap,0,start,start+len); //dont call this rmem_map_process_deferred rmem_map_unlock(rmap,False); if (Result=0) then begin td:=curkthread; if (td<>nil) then if (td^.td_rmap_def_user<>nil) then begin vmap:=map^.vmap; //iterate rmem_map_process_deferred entry:=td^.td_rmap_def_user; vm_map_lock(vmap); while (entry<>nil) do begin rmem_entry_del_vmap(vmap,entry); entry:=entry^.next; end; vm_map_unlock(vmap); //free all rmem_map_process_deferred; end; end; end; end; function dmem_map_findspace(map :p_dmem_map; start :DWORD; length:DWORD; addr :PDWORD):Integer; label _nxt; var entry:p_dmem_map_entry; st:DWORD; begin { * Request must fit within min/max VM address and must avoid * address wrap. } if (startmap^.max_offset) or (start + lengthmap^.root^.__end) then begin st:=start; end else begin st:=map^.root^.__end; end; if (length<=map^.root^.__end + map^.root^.adj_free - st) then begin addr^:=st; Exit(0); end; { With max_free, can immediately tell if no solution. } entry:=map^.root^.right; if (entry=nil) then begin Exit(1); end; if (length>entry^.max_free) then begin Exit(1); end; { * Search the right subtree in the order: left subtree, root, * right subtree (first fit). The previous splay implies that * all regions in the right subtree have addresses>start. } while (entry<>nil) do begin if (entry^.left<>nil) then begin if not (entry^.left^.max_free>=length) then goto _nxt; entry:=entry^.left; end else begin _nxt: if (entry^.adj_free>=length) then begin addr^:=entry^.__end; Exit(0); end else begin entry:=entry^.right; end; end; end; { Can't get here, so panic if we do. } Assert(false,'dmem_map_findspace: max_free corrupt'); end; function dmem_map_fixed(map :p_dmem_map; start :DWORD; length :DWORD; m_type :DWORD; overwr :Integer):Integer; var __end:DWORD; begin __end:=start + length; dmem_map_lock(map); DMEM_MAP_RANGE_CHECK(map, start, __end); if (overwr<>0) then begin dmem_map_delete(map, start, __end); end; Result:=dmem_map_insert(map, start, __end, m_type); dmem_map_unlock(map); end; procedure dmem_map_simplify_entry(map:p_dmem_map;entry:p_dmem_map_entry); var next,prev:p_dmem_map_entry; begin prev:=entry^.prev; if (prev<>@map^.header) then begin if (prev^.__end=entry^.start) and (prev^.m_type=entry^.m_type) then begin dmem_map_entry_unlink(map, prev); entry^.start:=prev^.start; //change if (entry^.prev<>@map^.header) then begin dmem_map_entry_resize_free(map, entry^.prev); end; dmem_map_entry_dispose(map, prev); end; end; next:=entry^.next; if (next<>@map^.header) then begin if (entry^.__end=next^.start) and (next^.m_type=entry^.m_type) then begin dmem_map_entry_unlink(map, next); entry^.__end:=next^.__end; //change dmem_map_entry_resize_free(map, entry); dmem_map_entry_dispose(map, next); end; end; end; procedure _dmem_map_clip_start(map:p_dmem_map;entry:p_dmem_map_entry;start:DWORD); var new_entry:p_dmem_map_entry; begin DMEM_MAP_ASSERT_LOCKED(map); dmem_map_simplify_entry(map, entry); new_entry:=dmem_map_entry_create(map); new_entry^:=entry^; new_entry^.__end:=start; entry^.start:=start; dmem_map_entry_link(map, entry^.prev, new_entry); end; procedure dmem_map_clip_start(map:p_dmem_map;entry:p_dmem_map_entry;start:DWORD); begin if (start>entry^.start) then begin _dmem_map_clip_start(map,entry,start); end; end; procedure _dmem_map_clip_end(map:p_dmem_map;entry:p_dmem_map_entry;__end:DWORD); var new_entry:p_dmem_map_entry; begin DMEM_MAP_ASSERT_LOCKED(map); { * Create a new entry and insert it AFTER the specified entry } new_entry:=dmem_map_entry_create(map); new_entry^:=entry^; new_entry^.start:=__end; entry^.__end:=__end; dmem_map_entry_link(map, entry, new_entry); end; procedure dmem_map_clip_end(map:p_dmem_map;entry:p_dmem_map_entry;__end:DWORD); begin if (__end@map^.header) and (entry^.start<__end) do begin dmem_map_clip_end(map, entry, __end); next:=entry^.next; dmem_map_entry_delete(map, entry); entry:=next; end; Result:=(0); end; function dmem_map_set_mtype(map :p_dmem_map; start:DWORD; __end:DWORD; mtype:Integer; prot :Integer; flags:Integer):Integer; public; var current,next,entry:p_dmem_map_entry; old:DWORD; begin if (start=__end) then begin Exit(0); end; if (mtype=SCE_KERNEL_WB_GARLIC) and ((prot and (VM_PROT_WRITE or VM_PROT_GPU_WRITE))<>0) then begin if ((flags and MAP_WRITABLE_WB_GARLIC)=0) then begin Exit(EACCES); end; end; dmem_map_lock(map); DMEM_MAP_RANGE_CHECK(map, start, __end); if (dmem_map_lookup_entry(map, start, @entry)) then begin // end else begin entry:=entry^.next; end; if (entry^.start>start) then begin dmem_map_unlock(map); Exit(EACCES); end; current:=entry; while (current<>@map^.header) and (current^.start<__end) do begin next:=current^.next; if (next<>@map^.header) then if (current^.__end<__end) and (current^.__end<>next^.start) then begin dmem_map_unlock(map); Exit(EACCES); end; current:=next; end; if (current^.__end<__end) then begin dmem_map_unlock(map); Exit(EACCES); end; if (mtype=-1) then begin dmem_map_unlock(map); Exit(0); end; dmem_map_clip_start(map, entry, start); current:=entry; while (current<>@map^.header) and (current^.start<__end) do begin dmem_map_clip_end(map, current, __end); current:=current^.next; end; { * Go back and fix up protections. [Note that clipping is not * necessary the second time.] } current:=entry; while ((current<>@map^.header) and (current^.start<__end)) do begin old:=current^.m_type; current^.m_type:=mtype; if (old<>current^.m_type) then begin // end; dmem_map_simplify_entry(map, current); current:=current^.next; end; dmem_map_unlock(map); Result:=0; end; function dmem_map_get_mtype(map :p_dmem_map; obj :vm_object_t; offset:QWORD; pstart:PQWORD; p__end:PQWORD; pmtype:PInteger):Integer; var entry:p_dmem_map_entry; begin if ((obj^.flags and OBJ_DMEM_EXT)=0) then begin Exit(ENODEV); end; if (Int64(offset)<0) then begin Exit(EINVAL); end; dmem_map_lock(map); if (dmem_map_lookup_entry(map, OFF_TO_IDX(offset), @entry)) then begin pstart^:=IDX_TO_OFF(entry^.start); p__end^:=IDX_TO_OFF(entry^.__end); pmtype^:=entry^.m_type; Result:=0; end else begin Result:=ENOENT; end; dmem_map_unlock(map); end; end.