mirror of https://github.com/red-prig/fpPS4.git
634 lines
11 KiB
Plaintext
634 lines
11 KiB
Plaintext
unit dev_sce_zlib;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
vmparam,
|
|
sys_conf,
|
|
sys_event;
|
|
|
|
Function zlib_modevent(_mod,_type:Integer):Integer;
|
|
|
|
implementation
|
|
|
|
uses
|
|
paszlib,
|
|
errno,
|
|
mqueue,
|
|
kern_mtx,
|
|
kern_condvar,
|
|
vm_map,
|
|
sys_vm_object,
|
|
kern_proc,
|
|
kern_thr,
|
|
subr_backtrace;
|
|
|
|
type
|
|
p_zlib_node=^t_zlib_node;
|
|
t_zlib_node=record
|
|
entry :TAILQ_ENTRY;
|
|
requestId:QWORD;
|
|
src :Pointer;
|
|
dst :Pointer;
|
|
src_len :DWORD;
|
|
dst_len :DWORD;
|
|
status :Integer;
|
|
end;
|
|
|
|
p_zlib_context=^t_zlib_context;
|
|
t_zlib_context=record
|
|
td:p_kthread;
|
|
|
|
free__list:TAILQ_HEAD;
|
|
queue_list:TAILQ_HEAD;
|
|
stash_list:TAILQ_HEAD;
|
|
|
|
queue_cv:t_cv;
|
|
|
|
nodes:array[0..63] of t_zlib_node;
|
|
|
|
mark_free :Byte;
|
|
free__count:Byte;
|
|
queue_count:Byte;
|
|
stash_count:Byte;
|
|
end;
|
|
|
|
var
|
|
zlib_init :Integer=0;
|
|
zlib_dev :p_cdev=nil;
|
|
zlib_sc_mtx :mtx;
|
|
|
|
zlib_knl_mtx:mtx;
|
|
zlib_knlist :t_knlist;
|
|
|
|
procedure zlib_proc(context:p_zlib_context); forward; SysV_ABI_CDecl;
|
|
|
|
Function zlib_open(dev:p_cdev;oflags,devtype:Integer):Integer;
|
|
var
|
|
context:p_zlib_context;
|
|
i:Integer;
|
|
begin
|
|
mtx_lock(zlib_sc_mtx);
|
|
|
|
if (zlib_init=0) then
|
|
begin
|
|
//max queue = 64
|
|
|
|
context:=AllocMem(SizeOf(t_zlib_context));
|
|
|
|
with context^ do
|
|
begin
|
|
TAILQ_INIT(@free__list);
|
|
TAILQ_INIT(@queue_list);
|
|
TAILQ_INIT(@stash_list);
|
|
|
|
cv_init(@queue_cv,'zlib_queue_cv');
|
|
|
|
for i:=0 to High(nodes) do
|
|
begin
|
|
TAILQ_INSERT_TAIL(@free__list,@nodes[i],@nodes[i].entry);
|
|
end;
|
|
|
|
free__count:=Length(nodes);
|
|
end;
|
|
|
|
Result:=kthread_add(@zlib_proc,context,@context^.td,(64*1024) div (16*1024),'[ZLIB]');
|
|
if (Result=0) then
|
|
begin
|
|
dev^.si_drv1:=context;
|
|
zlib_init :=1;
|
|
end else
|
|
begin
|
|
FreeMem(context);
|
|
end;
|
|
|
|
end else
|
|
begin
|
|
Result:=EBUSY;
|
|
end;
|
|
|
|
mtx_unlock(zlib_sc_mtx);
|
|
end;
|
|
|
|
Function zlib_close(dev:p_cdev;fflag,devtype:Integer):Integer;
|
|
var
|
|
context:p_zlib_context;
|
|
begin
|
|
mtx_lock(zlib_sc_mtx);
|
|
|
|
if (zlib_init<>0) then
|
|
begin
|
|
//flush queue
|
|
|
|
context:=dev^.si_drv1;
|
|
|
|
with context^ do
|
|
begin
|
|
mark_free:=1; //mark free
|
|
cv_signal(@queue_cv); //wakeup
|
|
end;
|
|
//free in thread
|
|
|
|
zlib_init:=0;
|
|
end;
|
|
|
|
mtx_unlock(zlib_sc_mtx);
|
|
|
|
Result:=0;
|
|
end;
|
|
|
|
type
|
|
p_data_zlib_init=^t_data_zlib_init;
|
|
t_data_zlib_init=packed record
|
|
buffer:Pointer;
|
|
length:DWORD;
|
|
_align:Integer;
|
|
unused:Integer;
|
|
end;
|
|
|
|
p_data_zlib_inflate=^t_data_zlib_inflate;
|
|
t_data_zlib_inflate=packed record
|
|
requestId:QWORD;
|
|
cmd:Integer;
|
|
_align1:Integer;
|
|
source:Pointer;
|
|
destination:Pointer;
|
|
sourceLength:Integer;
|
|
destinationLength:Integer;
|
|
status:Integer;
|
|
_align2:Integer;
|
|
end;
|
|
|
|
Function zlib_ioctl_init(data:p_data_zlib_init):Integer;
|
|
var
|
|
buffer_lo,buffer_hi:QWORD;
|
|
map :vm_map_t;
|
|
entry:vm_map_entry_t;
|
|
begin
|
|
if (data^.length=0) or
|
|
(data^.buffer=nil) then Exit(EINVAL);
|
|
|
|
buffer_lo:=QWORD(data^.buffer) and QWORD(not PAGE_MASK);
|
|
buffer_hi:=(QWORD(data^.buffer) + data^.length + PAGE_MASK) and QWORD(not PAGE_MASK);
|
|
|
|
map:=p_proc.p_vmspace;
|
|
|
|
if (buffer_hi < buffer_lo) or
|
|
(buffer_lo < map^.header.start) or
|
|
(map^.header.__end < buffer_hi) then Exit(EACCES);
|
|
|
|
if (buffer_hi <= buffer_lo) then Exit(EINVAL);
|
|
|
|
vm_map_lock(map,False);
|
|
|
|
if not vm_map_lookup_entry(map,buffer_lo,@entry) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(EACCES);
|
|
end;
|
|
|
|
vm_map_unlock(map);
|
|
|
|
Result:=0;
|
|
end;
|
|
|
|
Function zlib_ioctl_fini(data:p_data_zlib_init):Integer;
|
|
begin
|
|
Result:=0;
|
|
end;
|
|
|
|
var
|
|
id_gen:QWORD=0;
|
|
|
|
function gen_requestId:QWORD; inline;
|
|
var
|
|
tmp:QWORD;
|
|
begin
|
|
tmp:=(id_gen + 1) div ($ffffffffff);
|
|
id_gen:=id_gen + 1 + (tmp - (tmp << 40));
|
|
Result:=id_gen * $10000;
|
|
end;
|
|
|
|
function zlib_alloc_node(context:p_zlib_context):p_zlib_node; inline;
|
|
begin
|
|
with context^ do
|
|
begin
|
|
Result:=TAILQ_FIRST(@free__list);
|
|
if (Result<>nil) then
|
|
begin
|
|
Dec(free__count);
|
|
TAILQ_REMOVE(@free__list,Result,@Result^.entry);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure zlib_free_node(context:p_zlib_context;node:p_zlib_node); inline;
|
|
begin
|
|
with context^ do
|
|
begin
|
|
Inc(free__count);
|
|
TAILQ_INSERT_HEAD(@free__list,node,@node^.entry);
|
|
end;
|
|
end;
|
|
|
|
procedure zlib_restore_node(context:p_zlib_context;node:p_zlib_node); inline;
|
|
begin
|
|
with context^ do
|
|
begin
|
|
Dec(stash_count);
|
|
Inc(free__count);
|
|
TAILQ_REMOVE (@stash_list,node,@node^.entry);
|
|
TAILQ_INSERT_TAIL(@free__list,node,@node^.entry);
|
|
end;
|
|
end;
|
|
|
|
procedure zlib_stash_node(context:p_zlib_context;node:p_zlib_node); inline;
|
|
begin
|
|
with context^ do
|
|
begin
|
|
Inc(stash_count);
|
|
TAILQ_INSERT_HEAD(@stash_list,node,@node^.entry);
|
|
end;
|
|
end;
|
|
|
|
procedure zlib_send_node(context:p_zlib_context;node:p_zlib_node); inline;
|
|
begin
|
|
with context^ do
|
|
begin
|
|
Inc(queue_count);
|
|
TAILQ_INSERT_TAIL(@queue_list,node,@node^.entry);
|
|
cv_signal(@queue_cv);
|
|
end;
|
|
end;
|
|
|
|
function zlib_recv_node(context:p_zlib_context):p_zlib_node; inline;
|
|
begin
|
|
with context^ do
|
|
begin
|
|
Result:=TAILQ_LAST(@queue_list);
|
|
if (Result<>nil) then
|
|
begin
|
|
Dec(queue_count);
|
|
TAILQ_REMOVE(@queue_list,Result,@Result^.entry);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure zlib_proc(context:p_zlib_context); SysV_ABI_CDecl;
|
|
var
|
|
node:p_zlib_node;
|
|
begin
|
|
repeat
|
|
|
|
mtx_lock(zlib_sc_mtx);
|
|
|
|
repeat
|
|
|
|
node:=zlib_recv_node(context);
|
|
if (node<>nil) then Break;
|
|
|
|
_cv_wait_sig(@context^.queue_cv,@zlib_sc_mtx);
|
|
|
|
until (context^.mark_free<>0);
|
|
|
|
mtx_unlock(zlib_sc_mtx);
|
|
|
|
if (node<>nil) then
|
|
begin
|
|
|
|
with node^ do
|
|
begin
|
|
status:=uncompress(dst,dst_len,src,src_len);
|
|
//TODO: error codes?
|
|
end;
|
|
|
|
mtx_lock(zlib_sc_mtx);
|
|
zlib_stash_node(context,node);
|
|
mtx_unlock(zlib_sc_mtx);
|
|
|
|
knote(@zlib_knlist,0,0);
|
|
end;
|
|
|
|
until (context^.mark_free<>0);
|
|
|
|
mtx_lock(zlib_sc_mtx);
|
|
|
|
with context^ do
|
|
begin
|
|
thread_dec_ref(td);
|
|
cv_destroy(@queue_cv);
|
|
end;
|
|
FreeMem(context);
|
|
|
|
mtx_unlock(zlib_sc_mtx);
|
|
end;
|
|
|
|
Function zlib_buffer_test(buffer:Pointer;length:DWORD):Integer;
|
|
label
|
|
_EACCES;
|
|
var
|
|
buffer_lo,buffer_hi:QWORD;
|
|
map :vm_map_t;
|
|
entry:vm_map_entry_t;
|
|
obj :vm_map_object;
|
|
|
|
offset_next:QWORD;
|
|
offset :QWORD;
|
|
addr :QWORD;
|
|
addr_next :QWORD;
|
|
begin
|
|
if (buffer=nil) or
|
|
(length=0) then Exit(EINVAL);
|
|
|
|
buffer_lo:=QWORD(buffer) and QWORD($ffffffffffffc000);
|
|
buffer_hi:=(QWORD(buffer) + length + $3fff) and QWORD($ffffffffffffc000);
|
|
|
|
map:=p_proc.p_vmspace;
|
|
|
|
if (buffer_hi < buffer_lo) or
|
|
(buffer_lo < map^.header.start) or
|
|
(map^.header.__end < buffer_hi) then Exit(EINVAL);
|
|
|
|
if (buffer_hi <= buffer_lo) then Exit(EINVAL);
|
|
|
|
vm_map_lock(map,False);
|
|
|
|
offset_next:=QWORD(-1);
|
|
offset :=0;
|
|
addr :=buffer_lo;
|
|
|
|
repeat
|
|
|
|
if not vm_map_lookup_entry(map,addr,@entry) then
|
|
begin
|
|
_EACCES:
|
|
vm_map_unlock(map);
|
|
Exit(EACCES);
|
|
end;
|
|
|
|
obj:=entry^.vm_obj;
|
|
if (obj=nil) then
|
|
begin
|
|
goto _EACCES;
|
|
end;
|
|
|
|
if ((obj^.flags and OBJ_DMEM_EXT)=0) then
|
|
begin
|
|
goto _EACCES;
|
|
end;
|
|
|
|
if (offset <> 0) and
|
|
((entry^.offset - entry^.start + addr) <> (offset_next + $4000)) then
|
|
begin
|
|
goto _EACCES;
|
|
end;
|
|
|
|
addr_next :=addr + $4000;
|
|
offset :=offset - $4000;
|
|
offset_next:=addr - entry^.start + entry^.offset;
|
|
addr :=addr_next;
|
|
|
|
until (addr_next >= buffer_hi);
|
|
|
|
vm_map_unlock(map);
|
|
|
|
Result:=0;
|
|
end;
|
|
|
|
Function zlib_ioctl_inflate(dev:p_cdev;data:p_data_zlib_inflate):Integer;
|
|
label
|
|
_unlock;
|
|
var
|
|
context:p_zlib_context;
|
|
node:p_zlib_node;
|
|
requestId:QWORD;
|
|
begin
|
|
Result:=0;
|
|
|
|
if (data^.cmd <> 1) then Exit(EINVAL);
|
|
|
|
context:=dev^.si_drv1;
|
|
|
|
mtx_lock(zlib_knl_mtx);
|
|
|
|
node:=zlib_alloc_node(context);
|
|
|
|
if (node=nil) then
|
|
begin
|
|
Result:=EBUSY;
|
|
goto _unlock;
|
|
end;
|
|
|
|
Result:=zlib_buffer_test(data^.source,data^.sourceLength);
|
|
if (Result<>0) then
|
|
begin
|
|
zlib_free_node(context,node);
|
|
goto _unlock;
|
|
end;
|
|
|
|
Result:=zlib_buffer_test(data^.destination,data^.destinationLength);
|
|
if (Result<>0) then
|
|
begin
|
|
zlib_free_node(context,node);
|
|
goto _unlock;
|
|
end;
|
|
|
|
requestId:=gen_requestId;
|
|
|
|
node^.requestId:=requestId;
|
|
node^.src :=data^.source;
|
|
node^.dst :=data^.destination;
|
|
node^.src_len :=data^.sourceLength;
|
|
node^.dst_len :=data^.destinationLength;
|
|
node^.status :=0;
|
|
|
|
data^.requestId:=requestId;
|
|
|
|
zlib_send_node(context,node);
|
|
|
|
_unlock:
|
|
mtx_unlock(zlib_knl_mtx);
|
|
end;
|
|
|
|
Function zlib_ioctl_get_result(dev:p_cdev;data:p_data_zlib_inflate):Integer;
|
|
var
|
|
context:p_zlib_context;
|
|
node:p_zlib_node;
|
|
begin
|
|
Result:=EINVAL;
|
|
|
|
context:=dev^.si_drv1;
|
|
|
|
mtx_lock(zlib_knl_mtx);
|
|
|
|
node:=TAILQ_FIRST(@context^.stash_list);
|
|
|
|
while (node<>nil) do
|
|
begin
|
|
//
|
|
if (node^.requestId=data^.requestId) then
|
|
begin
|
|
Result:=0;
|
|
data^.destinationLength:=node^.dst_len;
|
|
data^.status :=node^.status;
|
|
|
|
zlib_restore_node(context,node);
|
|
|
|
Break;
|
|
end;
|
|
//
|
|
node:=TAILQ_NEXT(node,@node^.entry);
|
|
end;
|
|
|
|
mtx_unlock(zlib_knl_mtx);
|
|
end;
|
|
|
|
Function zlib_ioctl(dev:p_cdev;cmd:QWORD;data:Pointer;fflag:Integer):Integer;
|
|
begin
|
|
Result:=0;
|
|
|
|
Writeln('zlib_ioctl:0x',HexStr(cmd,8));
|
|
|
|
case cmd of
|
|
|
|
$c0185a03: //sceZlibInitialize
|
|
begin
|
|
Result:=zlib_ioctl_init(data);
|
|
end;
|
|
$80185a03: //sceZlibFinalize
|
|
begin
|
|
Result:=zlib_ioctl_fini(data);
|
|
end;
|
|
$c0305a01: //sceZlibInflate
|
|
begin
|
|
Result:=zlib_ioctl_inflate(dev,data);
|
|
end;
|
|
$c0305a02: //sceZlibGetResult
|
|
begin
|
|
Result:=zlib_ioctl_get_result(dev,data);
|
|
end;
|
|
|
|
else
|
|
begin
|
|
print_error_td('zlib_ioctl(0x'+HexStr(cmd,8)+')');
|
|
Assert(False);
|
|
Result:=EINVAL;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure zlib_detach(kn:p_knote);
|
|
begin
|
|
mtx_lock(zlib_knl_mtx);
|
|
|
|
knlist_remove(@zlib_knlist,kn,1);
|
|
|
|
mtx_unlock(zlib_knl_mtx);
|
|
end;
|
|
|
|
function zlib_event(kn:p_knote;hint:QWORD):Integer;
|
|
var
|
|
context:p_zlib_context;
|
|
node:p_zlib_node;
|
|
begin
|
|
node:=nil;
|
|
|
|
kn^.kn_kevent.data:=0;
|
|
|
|
context:=kn^.kn_hook;
|
|
|
|
mtx_lock(zlib_sc_mtx);
|
|
|
|
with context^ do
|
|
begin
|
|
node:=TAILQ_FIRST(@stash_list);
|
|
end;
|
|
|
|
if (node<>nil) then
|
|
begin
|
|
kn^.kn_kevent.data:=node^.requestId;
|
|
end;
|
|
|
|
mtx_unlock(zlib_sc_mtx);
|
|
|
|
Result:=ord(node<>nil);
|
|
end;
|
|
|
|
const
|
|
zlib_filterops:t_filterops=(
|
|
f_isfd :1;
|
|
f_detach:@zlib_detach;
|
|
f_event :@zlib_event;
|
|
);
|
|
|
|
Function zlib_kqfilter(dev:p_cdev;kn:p_knote):Integer;
|
|
begin
|
|
|
|
if (kn^.kn_kevent.filter = EVFILT_READ) then
|
|
begin
|
|
|
|
mtx_lock(zlib_knl_mtx);
|
|
|
|
kn^.kn_fop :=@zlib_filterops;
|
|
kn^.kn_hook:=dev^.si_drv1;
|
|
|
|
knlist_add(@zlib_knlist,kn,1);
|
|
|
|
mtx_unlock(zlib_knl_mtx);
|
|
|
|
Result:=0;
|
|
end else
|
|
begin
|
|
Result:=EINVAL;
|
|
end;
|
|
|
|
end;
|
|
|
|
const
|
|
zlib_cdevsw:t_cdevsw=(
|
|
d_version :D_VERSION;
|
|
d_flags :0;
|
|
d_name :'zlib';
|
|
d_open :@zlib_open;
|
|
d_close :@zlib_close;
|
|
d_ioctl :@zlib_ioctl;
|
|
d_kqfilter :@zlib_kqfilter;
|
|
);
|
|
|
|
Function zlib_modevent(_mod,_type:Integer):Integer;
|
|
begin
|
|
case (_type) of
|
|
MOD_LOAD:
|
|
begin
|
|
mtx_init (zlib_sc_mtx ,'zlib_sc_mtx');
|
|
mtx_init (zlib_knl_mtx,'zlib_knl_mtx');
|
|
knlist_init_mtx(@zlib_knlist,@zlib_knl_mtx);
|
|
zlib_dev:=make_dev_credf(0, @zlib_cdevsw, 0, UID_ROOT, GID_WHEEL, &666, 'sce_zlib',[]);
|
|
end;
|
|
|
|
MOD_UNLOAD:
|
|
begin
|
|
zlib_close (zlib_dev,0,0);
|
|
destroy_dev (zlib_dev);
|
|
mtx_destroy (zlib_sc_mtx);
|
|
knlist_clear (@zlib_knlist, 0);
|
|
knlist_destroy(@zlib_knlist);
|
|
mtx_destroy (zlib_knl_mtx);
|
|
end;
|
|
|
|
MOD_SHUTDOWN:;
|
|
|
|
else
|
|
Exit(EOPNOTSUPP);
|
|
end;
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
end.
|
|
|