unit subr_uio;

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

interface

uses
 vuio,
 systm,
 kern_param,
 kern_thr;

function uiomove(cp:Pointer;n:Integer;uio:p_uio):Integer;
function uiomove_nofault(cp:Pointer;n:Integer;uio:p_uio):Integer;
function uiomove_faultflag(cp:Pointer;n:Integer;uio:p_uio;nofault:Integer):Integer;

function copyinuio(iovp:p_iovec;iovcnt:DWORD;uiop:pp_uio):Integer;

implementation

uses
 errno,
 kern_synch;

function uiomove(cp:Pointer;n:Integer;uio:p_uio):Integer;
begin
 Exit(uiomove_faultflag(cp, n, uio, 0));
end;

function uiomove_nofault(cp:Pointer;n:Integer;uio:p_uio):Integer;
begin
 Exit(uiomove_faultflag(cp, n, uio, 1));
end;

function uiomove_faultflag(cp:Pointer;n:Integer;uio:p_uio;nofault:Integer):Integer;
label
 _out;
var
 td:p_kthread;
 iov:p_iovec;
 cnt:QWORD;
 error, newflags, save:Integer;
begin
 if (n<=0) then Exit(0);

 td:=curkthread;
 error:=0;

 Assert((uio^.uio_rw=UIO_READ) or (uio^.uio_rw=UIO_WRITE),'uiomove: mode');
 Assert((uio^.uio_segflg<>UIO_USERSPACE) or (uio^.uio_td=td),'uiomove proc');

 // XXX does it make a sense to set TDP_DEADLKTREAT for UIO_SYSSPACE ?
 newflags:=TDP_DEADLKTREAT;
 if (uio^.uio_segflg=UIO_USERSPACE) and (nofault<>0) then
 begin
  {
   * Fail if a non-spurious page fault occurs.
  }
  newflags:=newflags or TDP_NOFAULTING or TDP_RESETSPUR;
 end;
 save:=curthread_pflags_set(newflags);

 while (n > 0) and (uio^.uio_resid > 0) do
 begin
  iov:=uio^.uio_iov;
  cnt:=iov^.iov_len;
  if (cnt=0) then
  begin
   Inc(uio^.uio_iov);
   Dec(uio^.uio_iovcnt);
   continue;
  end;
  if (cnt > n) then
  begin
   cnt:=n;
  end;

  case (uio^.uio_segflg) of
   UIO_USERSPACE:
    begin
     maybe_yield();

     if (uio^.uio_rw=UIO_READ) then
      error:=copyout(cp, iov^.iov_base, cnt)
     else
      error:=copyin(iov^.iov_base, cp, cnt);

     if (error<>0) then goto _out;
    end;
   UIO_SYSSPACE:
    begin
     if (uio^.uio_rw=UIO_READ) then
      Move(cp^, iov^.iov_base^, cnt)
     else
      Move(iov^.iov_base^, cp^, cnt);
    end;
   UIO_NOCOPY:;
  end;

  Inc(iov^.iov_base  ,cnt);
  Dec(iov^.iov_len   ,cnt);
  Dec(uio^.uio_resid ,cnt);
  Inc(uio^.uio_offset,cnt);
  Inc(cp,cnt);
  Dec(n,cnt);
 end;

_out:
 curthread_pflags_restore(save);
 Exit(error);
end;

function copyinuio(iovp:p_iovec;iovcnt:DWORD;uiop:pp_uio):Integer;
var
 iov:p_iovec;
 uio:p_uio;
 iovlen:DWORD;
 error,i:Integer;
begin
 uiop^:=nil;
 if (iovcnt > UIO_MAXIOV) then
 begin
  Exit(EINVAL);
 end;
 iovlen:=iovcnt * sizeof (iovec);
 uio:=AllocMem(iovlen + sizeof(t_uio));
 iov:=p_iovec(uio + 1);
 error:=copyin(iovp, iov, iovlen);
 if (error<>0) then
 begin
  FreeMem(uio);
  Exit(error);
 end;
 uio^.uio_iov   :=iov;
 uio^.uio_iovcnt:=iovcnt;
 uio^.uio_segflg:=UIO_USERSPACE;
 uio^.uio_offset:=-1;
 uio^.uio_resid :=0;

 For i:=0 to iovcnt-1 do
 begin
  if (iov^.iov_len > IOSIZE_MAX - uio^.uio_resid) then
  begin
   FreeMem(uio);
   Exit(EINVAL);
  end;
  Inc(uio^.uio_resid,iov^.iov_len);
  Inc(iov);
 end;

 uiop^:=uio;
 Exit(0);
end;

end.

