unit dev_dmem;

{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}

interface

uses
 sys_conf;

procedure dmemdev_init();

implementation

uses
 errno,
 vuio,
 subr_uio,
 vm,
 vmparam,
 dmem_map,
 kern_dmem,
 sys_vm_object,
 vm_pager,
 kern_authinfo,
 kern_budget,
 subr_backtrace;

type
 PAvailableDirectMemorySize=^TAvailableDirectMemorySize;
 TAvailableDirectMemorySize=packed record
  start:QWORD; //in,out
  __end:QWORD; //in
  align:QWORD; //in
  osize:QWORD; //out
 end;

 PAllocateDirectMemory=^TAllocateDirectMemory;
 TAllocateDirectMemory=packed record
  start:QWORD; //in,out
  __end:QWORD; //in
  len  :QWORD; //in
  align:QWORD; //in
  mtype:DWORD;
  _alig:DWORD;
 end;

 PReleaseDirectMemory=^TReleaseDirectMemory;
 TReleaseDirectMemory=packed record
  start:QWORD; //in
  len  :QWORD; //in
 end;

 PDirectMemoryQuery=^TDirectMemoryQuery;
 TDirectMemoryQuery=packed record
  d_pool_id:Integer;
  flags    :Integer;
  id       :Integer;
  _align   :Integer;
  offset   :QWORD;
  info     :Pointer;
  size     :QWORD;
 end;

 p_set_prt_aperture=^t_set_prt_aperture;
 t_set_prt_aperture=packed record
  addr      :Pointer;
  len       :QWORD;
  apertureId:Integer;
 end;

function set_prt_aperture(data:p_set_prt_aperture):Integer;
var
 addr:QWORD;
 len :QWORD;
begin
 Result:=0;

 addr:=QWORD(data^.addr);

 if ((addr and PAGE_MASK) <> 0) then
 begin
  Exit(EINVAL);
 end;

 len:=data^.len;

 if ((QWORD($fc00000000) - addr) < len) then
 begin
  Exit(EINVAL);
 end;

 if (DWORD((addr - QWORD($1000000000)) shr 34) > 58) then
 begin
  Exit(EINVAL);
 end;

 if (Int64(len) < 0) then
 begin
  Exit(EINVAL);
 end;

 if (DWORD(data^.apertureId) > 2) then
 begin
  Exit(EINVAL);
 end;

 if ((len and PAGE_MASK) <> 0) then
 begin
  Exit(EINVAL);
 end;

 Writeln('TODO:set_prt_aperture(0x',HexStr(addr,10),',0x',HexStr(len,10),',',data^.apertureId,')');
end;

Function dmem_ioctl(dev:p_cdev;cmd:QWORD;data:Pointer;fflag:Integer):Integer;
var
 dmap:p_dmem_obj;
begin
 Result:=0;

 dmap:=dev^.si_drv1;

 Writeln('dmem_ioctl("',dev^.si_name,'",0x',HexStr(cmd,8),',0x',HexStr(fflag,8),')');

 case cmd of
  $4008800A: //sceKernelGetDirectMemorySize
            begin
             PQWORD(data)^:=kern_budget.DMEM_LIMIT;
             //PQWORD(data)^:=SCE_KERNEL_MAIN_DMEM_SIZE;
            end;

  $C0208016: //sceKernelAvailableDirectMemorySize
            begin
             with PAvailableDirectMemorySize(data)^ do
             begin
              Result:=dmem_map_query_available(dmap^.dmem,start,__end,align,start,osize);
             end;
            end;

  $C0288001: //sceKernelAllocateDirectMemory
            begin
             with PAllocateDirectMemory(data)^ do
             begin
              Result:=dmem_map_alloc(dmap^.dmem,start,__end,len,align,mtype,start);
              {
              Writeln('dmem_map_alloc(0x',HexStr(start,11),
                                    ',0x',HexStr(__end,11),
                                    ',0x',HexStr(len,11),
                                    ',0x',HexStr(align,11),
                                    ',',mtype,'):',Result);
              }
             end;
            end;

  $C0288011: //sceKernelAllocateMainDirectMemory
            begin
             with PAllocateDirectMemory(data)^ do
             begin
              Result:=dmem_map_alloc(dmap^.dmem,0,kern_budget.DMEM_LIMIT,len,align,mtype,start);
             end;
            end;

  $80108002: //sceKernelReleaseDirectMemory
            begin
             with PReleaseDirectMemory(data)^ do
             begin
              Result:=dmem_map_release(dmap^.dmem,start,len,False);
             end;
            end;

  $80108015: //sceKernelCheckedReleaseDirectMemory
            begin
             with PReleaseDirectMemory(data)^ do
             begin
              Result:=dmem_map_release(dmap^.dmem,start,len,True);
             end;
            end;

  $80288012: //sceKernelDirectMemoryQuery
            begin
             with PDirectMemoryQuery(data)^ do
             begin

              if (d_pool_id>2) then
              begin
               Exit(EINVAL);
              end;

              if (d_pool_id<>1) then
              begin
               //sceSblACMgrIsSystemUcred
               Exit(EPERM);
              end;

              Result:=dmem_map_query(dmem_maps[d_pool_id].dmem,offset,flags,id,info,size);
             end;
            end;

  $C0208004: //sceKernelGetDirectMemoryType
            begin
             Result:=dmem_map_get_memory_type(dmap^.dmem,data);
            end;

  $80188008: //sceKernelSetPrtAperture
            begin
             set_prt_aperture(data);
            end;

  else
   begin
    print_error_td('dmem_ioctl(0x'+HexStr(cmd,8)+')');
    Assert(False);
    Result:=EINVAL;
   end;
 end;

end;

function OFF_TO_IDX(x:QWORD):QWORD; inline;
begin
 Result:=QWORD(x) shr PAGE_SHIFT;
end;

Function dmem_mmap(dev:p_cdev;offset:vm_ooffset_t;paddr:p_vm_paddr_t;nprot:Integer;memattr:p_vm_memattr_t):Integer;
var
 dmap:p_dmem_obj;
 entry:p_dmem_map_entry;
 r:Boolean;
begin
 Result:=0;

 if is_sce_prog_attr_40_800000(@g_authinfo) then
 begin
  Exit(EPERM);
 end;

 if is_sce_prog_attr_40_400000(@g_authinfo) then
 begin
  Exit(EPERM);
 end;

 Writeln('dmem_mmap("',dev^.si_name,'",0x',HexStr(offset,8),',0x',HexStr(paddr),',',nprot,')');

 dmap:=dev^.si_drv1;

 entry:=nil;

 dmem_map_lock(dmap^.dmem);

  r:=dmem_map_lookup_entry(dmap^.dmem,
                           OFF_TO_IDX(offset),
                           @entry);

 dmem_map_unlock(dmap^.dmem);

 if not r then
 begin
  Exit(-1);
 end;

 paddr^:=offset {+ };
 memattr^:=0;
end;

Function dmem_mmap_single2(dev:p_cdev;offset:p_vm_ooffset_t;size:vm_size_t;obj:p_vm_object_t;nprot:Integer;maxprotp:p_vm_prot_t;flagsp:PInteger):Integer;
var
 dmap:p_dmem_obj;
 ofs:vm_ooffset_t;
 flags:Integer;
begin
 Result:=0;

 if is_sce_prog_attr_40_800000(@g_authinfo) then
 begin
  Exit(EPERM);
 end;

 if is_sce_prog_attr_40_400000(@g_authinfo) then
 begin
  Exit(EPERM);
 end;

 Writeln('dmem_mmap_single2("',dev^.si_name,'",0x',HexStr(offset^,8),',0x',HexStr(size,8),',',nprot,')');

 ofs:=offset^;

 if (ofs > -1) and (size <= max_valid_dmem - ofs) then
 begin
  //
 end else
 begin
  Exit(EACCES);
 end;

 dmap:=dev^.si_drv1;

 flags:=flagsp^;

 //with the value (mtype=-1) this is a test of memory continuousness
 Result:=dmem_map_set_mtype(dmap^.dmem,
                            OFF_TO_IDX(ofs),
                            OFF_TO_IDX(ofs+size),
                            -1,
                            nprot,
                            flags);

 if (Result<>0) then Exit;

 maxprotp^:=maxprotp^ or VM_PROT_GPU_ALL;

 obj^:=dmap^.vobj;

 if ((maxprotp^ and nprot)=nprot) then
 begin
  Assert(obj^<>nil);
  vm_object_reference(obj^);
 end else
 begin
  Exit(EACCES);
 end;

end;

Function dmem_open(dev:p_cdev;oflags,devtype:Integer):Integer;
begin
 Result:=0;

 if is_sce_prog_attr_40_800000(@g_authinfo) then
 begin
  Exit(EPERM);
 end;

 if is_sce_prog_attr_40_400000(@g_authinfo) then
 begin
  Exit(EPERM);
 end;
end;

const
 dmem_cdevsw:t_cdevsw=(
  d_version     :D_VERSION;
  d_flags       :0;
  d_name        :'dmem';
  d_open        :@dmem_open;
  d_ioctl       :@dmem_ioctl;
  d_mmap        :@dmem_mmap;
  d_mmap_single2:@dmem_mmap_single2;
 );

procedure dmemdev_init();
var
 dev:p_cdev;
 obj:vm_object_t;
 i:Integer;
begin
 For i:=0 to 2 do
 begin
  dev:=make_dev(@dmem_cdevsw,i,0,0,&777,'dmem%d',[i]);
  dev^.si_drv1:=@dmem_maps[i];
  //
  obj:=vm_pager_allocate(OBJT_DEVICE,dev,0,0,0);
  obj^.size:=$1400000;
  obj^.flags:=obj^.flags or OBJ_DMEM_EXT;
  obj^.un_pager.map_base:=Pointer(VM_MIN_GPU_ADDRESS);
  vm_object_reference(obj);
  //
  dmem_maps[i].dmem:=@kern_dmem.dmem;
  dmem_maps[i].vobj:=obj;
 end;
end;


end.

