mirror of https://github.com/red-prig/fpPS4.git
535 lines
11 KiB
Plaintext
535 lines
11 KiB
Plaintext
unit vm_file;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
vm,
|
|
vmparam;
|
|
|
|
type
|
|
t_protect_cb=function(start,__end:QWORD;base:Pointer;size:QWORD;prot:Integer):Integer;
|
|
t_remove_cb =function(start,__end:QWORD;base:Pointer;size:QWORD):Integer;
|
|
t_unmap_cb =function(base:Pointer;size:QWORD):Integer;
|
|
|
|
p_vm_file_obj=^vm_file_obj;
|
|
vm_file_obj=packed record
|
|
base :Pointer;
|
|
size :QWORD;
|
|
refs :QWORD;
|
|
protect:t_protect_cb;
|
|
remove :t_remove_cb;
|
|
unmap :t_unmap_cb;
|
|
end;
|
|
|
|
pp_vm_file_entry=^p_vm_file_entry;
|
|
p_vm_file_entry=^vm_file_entry;
|
|
vm_file_entry=packed record
|
|
prev :p_vm_file_entry;
|
|
next :p_vm_file_entry;
|
|
left :p_vm_file_entry;
|
|
right :p_vm_file_entry;
|
|
start :QWORD;
|
|
__end :QWORD;
|
|
offset:QWORD;
|
|
obj :p_vm_file_obj;
|
|
end;
|
|
|
|
p_vm_file_map=^vm_file_map;
|
|
vm_file_map=packed object
|
|
header:vm_file_entry;
|
|
root :p_vm_file_entry;
|
|
end;
|
|
|
|
function vm_file_obj_allocate (base:Pointer;size:QWORD):p_vm_file_obj;
|
|
procedure vm_file_obj_destroy (obj:p_vm_file_obj);
|
|
procedure vm_file_obj_reference (obj:p_vm_file_obj);
|
|
procedure vm_file_obj_deallocate(obj:p_vm_file_obj);
|
|
|
|
procedure vm_file_map_init(map:p_vm_file_map;min,max:QWORD);
|
|
procedure vm_file_map_free(map:p_vm_file_map);
|
|
|
|
function vm_file_map_lookup_entry(
|
|
map :p_vm_file_map;
|
|
address:QWORD;
|
|
entry :pp_vm_file_entry):Boolean;
|
|
|
|
function vm_file_map_insert(
|
|
map :p_vm_file_map;
|
|
obj :p_vm_file_obj;
|
|
offset:QWORD;
|
|
start :QWORD;
|
|
__end :QWORD):Integer;
|
|
|
|
function vm_file_map_protect(map :p_vm_file_map;
|
|
start:QWORD;
|
|
__end:QWORD;
|
|
prot :Integer):Integer;
|
|
|
|
function vm_file_map_delete(map :p_vm_file_map;
|
|
start:QWORD;
|
|
__end:QWORD):Integer;
|
|
|
|
implementation
|
|
|
|
function OFF_TO_IDX(x:QWORD):DWORD; inline;
|
|
begin
|
|
Result:=QWORD(x) shr PAGE_SHIFT;
|
|
end;
|
|
|
|
function vm_file_obj_allocate(base:Pointer;size:QWORD):p_vm_file_obj;
|
|
begin
|
|
Result:=AllocMem(SizeOf(vm_file_obj));
|
|
|
|
Result^.base:=base;
|
|
Result^.size:=size;
|
|
Result^.refs:=1;
|
|
end;
|
|
|
|
procedure vm_file_obj_destroy(obj:p_vm_file_obj);
|
|
begin
|
|
if (obj^.unmap<>nil) then
|
|
begin
|
|
obj^.unmap(obj^.base,obj^.size);
|
|
end;
|
|
|
|
FreeMem(obj);
|
|
end;
|
|
|
|
procedure vm_file_obj_reference(obj:p_vm_file_obj);
|
|
begin
|
|
if (obj=nil) then Exit;
|
|
|
|
System.InterlockedIncrement64(obj^.refs);
|
|
end;
|
|
|
|
procedure vm_file_obj_deallocate(obj:p_vm_file_obj);
|
|
begin
|
|
if (obj=nil) then Exit;
|
|
|
|
if (System.InterlockedDecrement64(obj^.refs)=0) then
|
|
begin
|
|
vm_file_obj_destroy(obj);
|
|
end;
|
|
end;
|
|
|
|
procedure vm_file_map_init(map:p_vm_file_map;min,max:QWORD);
|
|
begin
|
|
map^.header.next :=@map^.header;
|
|
map^.header.prev :=@map^.header;
|
|
map^.header.start:=min;
|
|
map^.header.__end:=max;
|
|
map^.root :=nil;
|
|
end;
|
|
|
|
procedure vm_file_map_free(map:p_vm_file_map);
|
|
begin
|
|
vm_file_map_delete(map,map^.header.start,map^.header.__end);
|
|
end;
|
|
|
|
procedure vm_file_entry_dispose(map:p_vm_file_map;entry:p_vm_file_entry); inline;
|
|
begin
|
|
vm_file_obj_deallocate(entry^.obj);
|
|
FreeMem(entry);
|
|
end;
|
|
|
|
function vm_file_entry_create(map:p_vm_file_map):p_vm_file_entry;
|
|
var
|
|
new_entry:p_vm_file_entry;
|
|
begin
|
|
new_entry:=AllocMem(SizeOf(vm_file_entry));
|
|
Assert((new_entry<>nil),'vm_file_entry_create: kernel resources exhausted');
|
|
Result:=new_entry;
|
|
end;
|
|
|
|
function vm_file_entry_splay(addr:QWORD;root:p_vm_file_entry):p_vm_file_entry;
|
|
var
|
|
llist,rlist:p_vm_file_entry;
|
|
ltree,rtree:p_vm_file_entry;
|
|
y :p_vm_file_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_file_map_entry_link(
|
|
map :p_vm_file_map;
|
|
after_where:p_vm_file_entry;
|
|
entry :p_vm_file_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_file_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_file_map_entry_unlink(
|
|
map :p_vm_file_map;
|
|
entry:p_vm_file_entry);
|
|
var
|
|
next,prev,root:p_vm_file_entry;
|
|
begin
|
|
if (entry<>map^.root) then
|
|
begin
|
|
vm_file_entry_splay(entry^.start, map^.root);
|
|
end;
|
|
if (entry^.left=nil) then
|
|
begin
|
|
root:=entry^.right;
|
|
end else
|
|
begin
|
|
root:=vm_file_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_file_map_lookup_entry(
|
|
map :p_vm_file_map;
|
|
address:QWORD;
|
|
entry :pp_vm_file_entry):Boolean;
|
|
var
|
|
cur:p_vm_file_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_file_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_file_map_insert(
|
|
map :p_vm_file_map;
|
|
obj :p_vm_file_obj;
|
|
offset:QWORD;
|
|
start :QWORD;
|
|
__end :QWORD):Integer;
|
|
var
|
|
new_entry :p_vm_file_entry;
|
|
prev_entry:p_vm_file_entry;
|
|
temp_entry:p_vm_file_entry;
|
|
begin
|
|
if (start>=__end) then
|
|
begin
|
|
Exit(KERN_INVALID_ADDRESS);
|
|
end;
|
|
|
|
if vm_file_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_file_entry_create(map);
|
|
new_entry^.start:=start;
|
|
new_entry^.__end:=__end;
|
|
|
|
new_entry^.offset:=offset;
|
|
new_entry^.obj :=obj;
|
|
|
|
vm_file_map_entry_link(map, prev_entry, new_entry);
|
|
end;
|
|
|
|
procedure vm_file_map_entry_delete(map:p_vm_file_map;entry:p_vm_file_entry);
|
|
begin
|
|
vm_file_map_entry_unlink(map, entry);
|
|
vm_file_entry_dispose(map,entry);
|
|
end;
|
|
|
|
procedure _vm_file_map_clip_start(map:p_vm_file_map;entry:p_vm_file_entry;start:QWORD);
|
|
var
|
|
new_entry:p_vm_file_entry;
|
|
begin
|
|
new_entry:=vm_file_entry_create(map);
|
|
new_entry^:=entry^;
|
|
|
|
new_entry^.__end:=start;
|
|
entry^.offset:=entry^.offset + (start - entry^.start);
|
|
entry^.start:=start;
|
|
|
|
vm_file_map_entry_link(map, entry^.prev, new_entry);
|
|
|
|
vm_file_obj_reference(new_entry^.obj);
|
|
end;
|
|
|
|
procedure vm_file_map_clip_start(map:p_vm_file_map;entry:p_vm_file_entry;start:QWORD);
|
|
begin
|
|
if (start>entry^.start) then
|
|
begin
|
|
_vm_file_map_clip_start(map,entry,start);
|
|
end;
|
|
end;
|
|
|
|
procedure _vm_file_map_clip_end(map:p_vm_file_map;entry:p_vm_file_entry;__end:QWORD);
|
|
var
|
|
new_entry:p_vm_file_entry;
|
|
begin
|
|
new_entry:=vm_file_entry_create(map);
|
|
new_entry^:=entry^;
|
|
|
|
new_entry^.start:=__end;
|
|
entry^.__end:=__end;
|
|
new_entry^.offset:=new_entry^.offset + (__end - entry^.start);
|
|
|
|
vm_file_map_entry_link(map, entry, new_entry);
|
|
|
|
vm_file_obj_reference(new_entry^.obj);
|
|
end;
|
|
|
|
procedure vm_file_map_clip_end(map:p_vm_file_map;entry:p_vm_file_entry;__end:QWORD);
|
|
begin
|
|
if (__end<entry^.__end) then
|
|
begin
|
|
_vm_file_map_clip_end(map,entry,__end);
|
|
end;
|
|
end;
|
|
|
|
function vm_file_map_protect(map :p_vm_file_map;
|
|
start:QWORD;
|
|
__end:QWORD;
|
|
prot :Integer):Integer;
|
|
var
|
|
current,entry:p_vm_file_entry;
|
|
obj:p_vm_file_obj;
|
|
m_start:QWORD;
|
|
m___end:QWORD;
|
|
offset :QWORD;
|
|
size :QWORD;
|
|
begin
|
|
if (start=__end) then
|
|
begin
|
|
Exit(KERN_SUCCESS);
|
|
end;
|
|
|
|
if (not vm_file_map_lookup_entry(map, start, @entry)) then
|
|
begin
|
|
entry:=entry^.next;
|
|
end;
|
|
|
|
current:=entry;
|
|
while ((current<>@map^.header) and (current^.start<__end)) do
|
|
begin
|
|
obj:=current^.obj;
|
|
|
|
if (obj^.protect<>nil) then
|
|
begin
|
|
|
|
m_start:=current^.start;
|
|
m___end:=current^.__end;
|
|
|
|
if (start>m_start) then
|
|
begin
|
|
offset :=start-m_start;
|
|
m_start:=start;
|
|
end else
|
|
begin
|
|
offset:=0;
|
|
end;
|
|
|
|
if (m___end>__end) then
|
|
begin
|
|
m___end:=__end;
|
|
end;
|
|
|
|
offset:=offset+current^.offset;
|
|
|
|
size:=(m___end-m_start)+offset;
|
|
if (size>obj^.size) then
|
|
begin
|
|
size:=obj^.size;
|
|
end;
|
|
size:=size-offset;
|
|
|
|
obj^.protect(m_start,
|
|
m___end,
|
|
obj^.base+offset,
|
|
size,
|
|
prot);
|
|
end;
|
|
|
|
current:=current^.next;
|
|
end;
|
|
|
|
Result:=(KERN_SUCCESS);
|
|
end;
|
|
|
|
function vm_file_map_delete(map :p_vm_file_map;
|
|
start:QWORD;
|
|
__end:QWORD):Integer;
|
|
var
|
|
entry :p_vm_file_entry;
|
|
first_entry:p_vm_file_entry;
|
|
next :p_vm_file_entry;
|
|
obj :p_vm_file_obj;
|
|
offset :QWORD;
|
|
size :QWORD;
|
|
begin
|
|
if (start=__end) then
|
|
begin
|
|
Exit(KERN_SUCCESS);
|
|
end;
|
|
|
|
if (not vm_file_map_lookup_entry(map, start, @first_entry)) then
|
|
begin
|
|
entry:=first_entry^.next;
|
|
end else
|
|
begin
|
|
entry:=first_entry;
|
|
|
|
vm_file_map_clip_start(map, entry, start);
|
|
end;
|
|
|
|
while (entry<>@map^.header) and (entry^.start<__end) do
|
|
begin
|
|
vm_file_map_clip_end(map, entry, __end);
|
|
next:=entry^.next;
|
|
|
|
obj:=entry^.obj;
|
|
|
|
if (obj^.remove<>nil) then
|
|
begin
|
|
offset:=entry^.offset;
|
|
|
|
size:=(entry^.__end-entry^.start)+offset;
|
|
if (size>obj^.size) then
|
|
begin
|
|
size:=obj^.size;
|
|
end;
|
|
size:=size-offset;
|
|
|
|
obj^.remove(entry^.start,
|
|
entry^.__end,
|
|
obj^.base+offset,
|
|
size
|
|
);
|
|
|
|
end;
|
|
|
|
vm_file_map_entry_delete(map, entry);
|
|
entry:=next;
|
|
end;
|
|
Result:=(KERN_SUCCESS);
|
|
end;
|
|
|
|
end.
|
|
|
|
|