unit systm;

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

interface

uses
 kern_mtx;

function copystr(from,_to:pchar;maxlen:ptruint;lencopied:pptruint):Integer;
function copyin(udaddr,kaddr:Pointer;len:ptruint):Integer;
function copyin_nofault(udaddr,kaddr:Pointer;len:ptruint):Integer;
function copyinstr(udaddr,kaddr:Pointer;len:ptruint;lencopied:pptruint):Integer;
function copyout(kaddr,udaddr:Pointer;len:ptruint):Integer;
function copyout_nofault(kaddr,udaddr:Pointer;len:ptruint):Integer;
function fubyte(var base:Byte):Byte;
function fuword32(var base:DWORD):DWORD;
function fuword64(var base:QWORD):QWORD;
function fuword(var base:Pointer):Pointer; external name 'fuword64';
function casuword32(var base:DWORD;oldval,newval:DWORD):DWORD;
function casuword64(var base:QWORD;oldval,newval:QWORD):QWORD;
function suword32(var base:DWORD;word:DWORD):DWORD;
function suword64(var base:QWORD;word:QWORD):QWORD;
function suword(var base:Pointer;word:Pointer):Pointer; external name 'suword64';

function fuptr(var base:Pointer):Pointer;
function fuptr(var base:QWORD):QWORD;

///

function  msleep(ident   :Pointer;
                 lock    :p_mtx;
                 priority:Integer;
                 wmesg   :PChar;
                 timo    :Int64):Integer; external;

function  tsleep(ident   :Pointer;
                 priority:Integer;
                 wmesg   :PChar;
                 timo    :Int64):Integer; external;

function  pause(wmesg:PChar;timo:Int64):Integer; external;

procedure wakeup(ident:Pointer); external;
procedure wakeup_one(ident:Pointer); external;

implementation

uses
 errno,
 md_systm,
 vmparam,
 kern_thr;

{
 * copystr(from, to, maxlen, int *lencopied) - MP SAFE
 *         %rdi, %rsi, %rdx, %rcx
}
function copystr(from,_to:pchar;maxlen:ptruint;lencopied:pptruint):Integer; assembler; nostackframe;
label
 v1b,
 v4f,
 v6f,
 v7f;
asm
        movq        %rdx,%r8                        { %r8 = maxlen }

        xchgq       %rdi,%rsi
        incq        %rdx
        cld
v1b:
        decq        %rdx
        jz          v4f
        lodsb
        stosb
        orb         %al,%al
        jnz         v1b

        { Success -- 0 byte reached }
        decq        %rdx
        xorl        %eax,%eax
        jmp         v6f
v4f:
        { rdx is zero -- return ENAMETOOLONG }
        movq        $ENAMETOOLONG,%rax

v6f:

        testq       %rcx,%rcx
        jz          v7f
        { set *lencopied and return %rax }
        subq        %rdx,%r8
        movq        %r8,(%rcx)
v7f:
        //ret
end;

{
 * copyin(from_user, to_kernel, len) - MP SAFE
 *        %rdi,      %rsi,      %rdx
}
function copyin(udaddr,kaddr:Pointer;len:ptruint):Integer; assembler; nostackframe;
label
 copyin_fault,
 done_copyin;
asm
        cmpq        $0x4000,%rdi //rdi <= 0x4000
        jle         copyin_fault

        movqq       %gs:teb.thread,%rax
        leaq        copyin_fault(%rip),%rcx
        movqq       %rcx,kthread.pcb_onfault(%rax)
        testq       %rdx,%rdx                        { anything to do? }
        jz          done_copyin

        {
         * make sure address is valid
        }
        movq        %rdi,%rax
        addq        %rdx,%rax
        jc          copyin_fault
        movq        $VM_MAXUSER_ADDRESS,%rcx
        cmpq        %rcx,%rax
        ja          copyin_fault

        xchgq       %rdi,%rsi
        movq        %rdx,%rcx
        movb        %cl,%al
        shrq        $3,%rcx                               { copy longword-wise }
        cld
        rep
        movsq
        movb        %al,%cl
        andb        $7,%cl                                { copy remaining bytes }
        rep
        movsb

done_copyin:
        xorl        %eax,%eax
        movq        %gs:teb.thread,%rdx
        movq        %rax,kthread.pcb_onfault(%rdx)
        ret

        //ALIGN_TEXT
        nop; nop; nop; nop;
        nop; nop; nop; nop;

copyin_fault:
        movq        %gs:teb.thread,%rdx
        movq        $0,kthread.pcb_onfault(%rdx)
        movq        $EFAULT,%rax
        //ret
end;

function copyin_nofault(udaddr,kaddr:Pointer;len:ptruint):Integer;
begin
 Result:=md_copyin(udaddr,kaddr,len,nil);
end;

{
 * copyinstr(from, to, maxlen, int *lencopied) - MP SAFE
 *           %rdi, %rsi, %rdx, %rcx
 *
 *        copy a string from from to to, stop when a 0 character is reached.
 *        return ENAMETOOLONG if string is longer than maxlen, and
 *        EFAULT on protection violations. If lencopied is non-zero,
 *        return the actual length in *lencopied.
}
function copyinstr(udaddr,kaddr:Pointer;len:ptruint;lencopied:pptruint):Integer; assembler; nostackframe;
label
 cpystrflt,
 cpystrflt_x,
 v1f,
 v2b,
 v3f,
 v4f;
asm
        cmpq        $0x4000,%rdi //rdi <= 0x4000
        jle         cpystrflt

        movq        %rdx,%r8                        { %r8 = maxlen }
        movq        %rcx,%r9                        { %r9 = *len }
        xchgq       %rdi,%rsi                       { %rdi = from, %rsi = to }
        movq        %gs:teb.thread,%rcx
        leaq        cpystrflt(%rip),%rax
        movq        %rax,kthread.pcb_onfault(%rcx)

        movq        $VM_MAXUSER_ADDRESS,%rax

        { make sure 'from' is within bounds }
        subq        %rsi,%rax
        jbe         cpystrflt

        { restrict maxlen to <= VM_MAXUSER_ADDRESS-from }
        cmpq        %rdx,%rax
        jae         v1f
        movq        %rax,%rdx
        movq        %rax,%r8
v1f:
        incq        %rdx
        cld

v2b:
        decq        %rdx
        jz          v3f

        lodsb
        stosb
        orb         %al,%al
        jnz         v2b

        { Success -- 0 byte reached }
        decq        %rdx
        xorl        %eax,%eax
        jmp         cpystrflt_x
v3f:
        { rdx is zero - return ENAMETOOLONG or EFAULT }
        movq        $VM_MAXUSER_ADDRESS,%rax
        cmpq        %rax,%rsi
        jae         cpystrflt

        movq        $ENAMETOOLONG,%rax
        jmp         cpystrflt_x

cpystrflt:
        movq        $EFAULT,%rax

cpystrflt_x:
        { set *lencopied and return %eax }
        movq        %gs:teb.thread,%rcx
        movq        $0,kthread.pcb_onfault(%rcx)

        testq       %r9,%r9
        jz          v4f
        subq        %rdx,%r8
        movq        %r8,(%r9)
v4f:
        //ret
end;

{
 * copyout(from_kernel, to_user, len)  - MP SAFE
 *         %rdi,        %rsi,    %rdx
}
function copyout(kaddr,udaddr:Pointer;len:ptruint):Integer; assembler; nostackframe;
label
 copyout_fault,
 done_copyout;
asm
        cmpq        $0x4000,%rsi //rsi <= 0x4000
        jle         copyout_fault

        movqq       %gs:teb.thread,%rax
        leaq        copyout_fault(%rip),%rcx
        movqq       %rcx,kthread.pcb_onfault(%rax)
        testq       %rdx,%rdx                        { anything to do? }
        jz          done_copyout

        {
         * First, prevent address wrapping.
        }
        movq        %rsi,%rax
        addq        %rdx,%rax
        jc          copyout_fault

        movq        $VM_MAXUSER_ADDRESS,%rcx
        cmpq        %rcx,%rax
        ja          copyout_fault

        xchgq        %rdi,%rsi
        { bcopy(%rsi, %rdi, %rdx) }
        movq        %rdx,%rcx

        shrq        $3,%rcx
        cld
        rep
        movsq
        movb        %dl,%cl
        andb        $7,%cl
        rep
        movsb

done_copyout:
        xorl        %eax,%eax
        movq        %gs:teb.thread,%rdx
        movq        %rax,kthread.pcb_onfault(%rdx)
        ret

        //ALIGN_TEXT
        nop; nop; nop; nop;
        nop; nop; nop; nop;
        nop; nop;

copyout_fault:
        movqq       %gs:teb.thread,%rdx
        movq        $0,kthread.pcb_onfault(%rdx)
        movq        $EFAULT,%rax
        //ret
end;

function copyout_nofault(kaddr,udaddr:Pointer;len:ptruint):Integer;
begin
 Result:=md_copyout(kaddr,udaddr,len,nil);
end;

function fusufault:QWORD; assembler; nostackframe;
asm
        movq        %gs:teb.thread,%rcx
        xorl        %eax,%eax
        movq        %rax,kthread.pcb_onfault(%rcx)
        decq        %rax
        //ret
end;

function fubyte(var base:Byte):Byte; assembler; nostackframe;
asm
        cmpq        $0x4000,%rdi //rdi <= 0x4000
        jle         fusufault

        movq        %gs:teb.thread,%rcx
        leaq        fusufault(%rip),%rax
        movq        %rax,kthread.pcb_onfault(%rcx)

        movq        $VM_MAXUSER_ADDRESS-1,%rax
        cmpq        %rax,%rdi
        ja          fusufault

        movzbl      (%rdi),%eax
        movq        $0,kthread.pcb_onfault(%rcx)
        //ret
end;

function fuword32(var base:DWORD):DWORD; assembler; nostackframe;
asm
        cmpq        $0x4000,%rdi //rdi <= 0x4000
        jle         fusufault

        movq        %gs:teb.thread,%rcx
        leaq        fusufault(%rip),%rax
        movq        %rax,kthread.pcb_onfault(%rcx)

        movq        $VM_MAXUSER_ADDRESS-4,%rax
        cmpq        %rax,%rdi                        { verify address is valid }
        ja          fusufault

        movl        (%rdi),%eax
        movq        $0,kthread.pcb_onfault(%rcx)
        //ret
end;


function fuword64(var base:QWORD):QWORD; assembler; nostackframe; public;
asm
        cmpq        $0x4000,%rdi //rdi <= 0x4000
        jle         fusufault

        movq        %gs:teb.thread,%rcx
        leaq        fusufault(%rip),%rax
        movq        %rax,kthread.pcb_onfault(%rcx)

        movq        $VM_MAXUSER_ADDRESS-8,%rax
        cmpq        %rax,%rdi                        { verify address is valid }
        ja          fusufault

        movq        (%rdi),%rax
        movq        $0,kthread.pcb_onfault(%rcx)
        //ret
end;

{
 * casuword32.  Compare and set user integer.  Returns -1 or the current value.
 *        dst = %rdi, old = %rsi, new = %rdx
}
function casuword32(var base:DWORD;oldval,newval:DWORD):DWORD; assembler; nostackframe;
asm
        cmpq        $0x4000,%rdi //rdi <= 0x4000
        jle         fusufault

        movq        %gs:teb.thread,%rcx
        leaq        fusufault(%rip),%rax
        movq        %rax,kthread.pcb_onfault(%rcx)

        movq        $VM_MAXUSER_ADDRESS-4,%rax
        cmpq        %rax,%rdi                        { verify address is valid }
        ja          fusufault

        movl        %esi,%eax                        { old }
        lock
        cmpxchgl    %edx,(%rdi)                      { new = %edx }

        {
         * The old value is in %eax.  If the store succeeded it will be the
         * value we expected (old) from before the store, otherwise it will
         * be the current value.
        }

        movq        %gs:teb.thread,%rcx
        movq        $0,kthread.pcb_onfault(%rcx)
        //ret
end;

{
 * casuword.  Compare and set user word.  Returns -1 or the current value.
 *        dst = %rdi, old = %rsi, new = %rdx
}
function casuword64(var base:QWORD;oldval,newval:QWORD):QWORD; assembler; nostackframe;
asm
        cmpq        $0x4000,%rdi //rdi <= 0x4000
        jle         fusufault

        movq        %gs:teb.thread,%rcx
        leaq        fusufault(%rip),%rax
        movq        %rax,kthread.pcb_onfault(%rcx)

        movq        $VM_MAXUSER_ADDRESS-4,%rax
        cmpq        %rax,%rdi                        { verify address is valid }
        ja          fusufault

        movq        %rsi,%rax                        { old }
        lock
        cmpxchgq    %rdx,(%rdi)                      { new = %rdx }

        {
         * The old value is in %eax.  If the store succeeded it will be the
         * value we expected (old) from before the store, otherwise it will
         * be the current value.
        }

        movq        %gs:teb.thread,%rcx
        movq        $0,kthread.pcb_onfault(%rcx)
        //ret
end;

{
 * Store a 32-bit word to
 * user memory.  All these functions are MPSAFE.
 * addr = %rdi, value = %rsi
}
function suword32(var base:DWORD;word:DWORD):DWORD; assembler; nostackframe;
asm
        cmpq        $0x4000,%rdi //rdi <= 0x4000
        jle         fusufault

        movq        %gs:teb.thread,%rcx
        leaq        fusufault(%rip),%rax
        movq        %rax,kthread.pcb_onfault(%rcx)

        movq        $VM_MAXUSER_ADDRESS-4,%rax
        cmpq        %rax,%rdi                        { verify address validity }
        ja          fusufault

        movl        %esi,(%rdi)
        xorl        %eax,%eax
        movq        %gs:teb.thread,%rcx
        movq        %rax,kthread.pcb_onfault(%rcx)
        //ret
end;

{
 * Store a 64-bit word to
 * user memory.  All these functions are MPSAFE.
 * addr = %rdi, value = %rsi
}
function suword64(var base:QWORD;word:QWORD):QWORD; assembler; nostackframe; public;
asm
        cmpq        $0x4000,%rdi //rdi <= 0x4000
        jle         fusufault

        movq        %gs:teb.thread,%rcx
        leaq        fusufault(%rip),%rax
        movq        %rax,kthread.pcb_onfault(%rcx)

        movq        $VM_MAXUSER_ADDRESS-8,%rax
        cmpq        %rax,%rdi                        { verify address validity }
        ja          fusufault

        movq        %rsi,(%rdi)
        xorl        %eax,%eax
        movq        %gs:teb.thread,%rcx
        movq        %rax,kthread.pcb_onfault(%rcx)
        //ret
end;

function fuptr(var base:Pointer):Pointer;
begin
 Result:=nil;
 md_copyin(@base,@Result,SizeOf(Pointer),nil);
end;

function fuptr(var base:QWORD):QWORD;
begin
 Result:=0;
 md_copyin(@base,@Result,SizeOf(QWORD),nil);
end;

end.



