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.