unit pm4_ring; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses errno, vmparam, vm_mmap, md_map, systm, si_ci_vi_merged_enum, pm4defs, bittype; const GC_RING_SIZE=$80000; GC_RING_PADD=64*1024; type p_pm4_ring=^t_pm4_ring; t_pm4_ring=packed record buff:Pointer; size:DWORD; rptr:DWORD; wptr:DWORD; aptr:DWORD; end; p_gc_hqd=^t_gc_hqd; t_gc_hqd=record base_guest_addr:Pointer; base_dmem_addr :Pointer; read_guest_addr:PDWORD; read_mirr_addr :PDWORD; next_dmem_addr :PDWORD; enable :DWORD; lenLog2 :DWORD; g_queueId :DWORD; pipePriority :DWORD; ringSizeDw :DWORD; ReadOffsetDw :DWORD; end; function gc_ring_create(ring:p_pm4_ring;size:ptruint):Integer; function gc_ring_free (ring:p_pm4_ring):Integer; function gc_ring_pm4_alloc (ring:p_pm4_ring;size:DWORD;buff:PPointer):Boolean; procedure gc_ring_pm4_submit (ring:p_pm4_ring); procedure gc_ring_pm4_release(ring:p_pm4_ring); function gc_ring_pm4_peek (ring:p_pm4_ring;size:PDWORD;buff:PPointer):Boolean; function gc_ring_pm4_drain(ring:p_pm4_ring;size:DWORD):Boolean; function gc_submit_internal (ring:p_pm4_ring;count:DWORD;cmds:Pointer):Integer; function gc_switch_buffer_internal(ring:p_pm4_ring):Integer; function gc_pm4_event_write_eop (ring:p_pm4_ring;addr:Pointer;data:QWORD;intSel,wait:Integer):Integer; Function gc_map_hqd(ringBaseAddress:Pointer; readPtrAddress :Pointer; nextPtrAddress :Pointer; lenLog2 :DWORD; g_queueId :DWORD; pipePriority :DWORD; hqd:p_gc_hqd):Integer; Function gc_unmap_hqd(hqd:p_gc_hqd):Integer; Function gc_map_hdq_ding_dong(hqd:p_gc_hqd;NextOffsetDw:DWORD):Integer; function gc_map_hdq_peek (hqd:p_gc_hqd;size:PDWORD;buff:PPointer):Boolean; function gc_map_hdq_drain (hqd:p_gc_hqd;size:DWORD):Boolean; implementation uses kern_dmem; function gc_ring_create(ring:p_pm4_ring;size:ptruint):Integer; var hMem:THandle; begin Result:=0; if (ring=nil) then Exit(-1); size:=QWORD(1) shl BsfQWORD(size); if (size0) then Exit; Result:=md_split(ring^.buff,size); if (Result<>0) then Exit; hMem:=0; Result:=md_memfd_create(hMem,size,VM_RW); if (Result<>0) then Exit; Result:=md_file_mmap_ex(hMem,ring^.buff,0,size,VM_RW); if (Result<>0) then begin md_memfd_close(hMem); Exit; end; Result:=md_file_mmap_ex(hMem,ring^.buff+size,0,size,VM_RW); md_memfd_close(hMem); end; function gc_ring_free(ring:p_pm4_ring):Integer; begin Result:=0; if (ring=nil) then Exit; if (ring^.buff=nil) then Exit; if (ring^.size=0) then Exit; Result:=md_unmap_ex(ring^.buff,ring^.size shl 1); end; function block_id(val,size:DWORD):DWORD; inline; begin Result:=val and (not (size-1)); end; function block_ofs(val,size:DWORD):DWORD; inline; begin Result:=val and (size-1); end; //need lock function gc_ring_pm4_alloc(ring:p_pm4_ring;size:DWORD;buff:PPointer):Boolean; var rsiz:DWORD; rptr:DWORD; next:DWORD; begin Result:=False; rsiz:=ring^.size; if (size>rsiz) then Exit; rptr:=ring^.rptr; next:=ring^.aptr+size; if (block_id(next,rsiz)=block_id(rptr,rsiz)) then begin if (block_ofs(next,rsiz)block_ofs(rptr,rsiz)) then Exit; end; buff^:=ring^.buff+block_ofs(ring^.aptr,rsiz); ring^.aptr:=next; Result:=True; end; procedure gc_ring_pm4_submit(ring:p_pm4_ring); begin System.InterlockedExchange(ring^.wptr,ring^.aptr); end; procedure gc_ring_pm4_release(ring:p_pm4_ring); begin ring^.aptr:=ring^.wptr; end; //single consumer function gc_ring_pm4_peek(ring:p_pm4_ring;size:PDWORD;buff:PPointer):Boolean; var rsiz:DWORD; rptr:DWORD; wptr:DWORD; s :DWORD; begin Result:=False; rsiz:=ring^.size; rptr:=ring^.rptr; wptr:=ring^.wptr; if (block_id(rptr,rsiz)=block_id(wptr,rsiz)) then begin s:=block_ofs(wptr,rsiz)-block_ofs(rptr,rsiz); end else begin s:=(rsiz-block_ofs(rptr,rsiz))+block_ofs(wptr,rsiz); end; if (s<>0) then begin size^:=s; buff^:=ring^.buff+block_ofs(rptr,rsiz); Result:=True; end; end; //single consumer function gc_ring_pm4_drain(ring:p_pm4_ring;size:DWORD):Boolean; var rsiz:DWORD; rptr:DWORD; wptr:DWORD; s :DWORD; begin Result:=False; rsiz:=ring^.size; rptr:=ring^.rptr; wptr:=ring^.wptr; if (block_id(rptr,rsiz)=block_id(wptr,rsiz)) then begin s:=block_ofs(wptr,rsiz)-block_ofs(rptr,rsiz); end else begin s:=(rsiz-block_ofs(rptr,rsiz))+block_ofs(wptr,rsiz); end; if (size>s) then Exit; rptr:=rptr+size; System.InterlockedExchange(ring^.rptr,rptr); Result:=True; end; function gc_submit_internal(ring:p_pm4_ring;count:DWORD;cmds:Pointer):Integer; var size:QWORD; buf:PPM4CMDINDIRECTBUFFER; op:DWORD; begin Result:=0; if (count=0) then Exit; if (count>=$1000) then Exit(-2142502897); size:=(count*16); buf:=nil; if not gc_ring_pm4_alloc(ring,size,@buf) then begin Writeln(stderr,'### gc_submit_common : Cannot allocate a space in ring buffer.'); Exit(EBUSY); end; Result:=copyin(cmds,buf,size); if (Result<>0) then begin gc_ring_pm4_release(ring); Exit(-2142502898); end; while (count<>0) do begin op:=DWORD(buf^.header); if ((op<>$c0023300) and (op<>$c0023f00)) then begin Writeln(stderr,'## gc_insert_indirect_buffer: invalid opcode = 0x',HexStr(op,8)); gc_ring_pm4_release(ring); Exit(-2142502896); end; if (buf^.ibSize=0) then begin Writeln(stderr,'## gc_insert_indirect_buffer: invalid ib_size = 0x',HexStr(buf^.ibSize,5)); gc_ring_pm4_release(ring); Exit(-2142502895); end; Inc(buf); Dec(count); end; gc_ring_pm4_submit(ring); end; function gc_switch_buffer_internal(ring:p_pm4_ring):Integer; var buf:PPM4CMDSWITCHBUFFER; begin Result:=0; buf:=nil; if not gc_ring_pm4_alloc(ring,sizeof(PM4CMDSWITCHBUFFER),@buf) then begin Writeln(stderr,'### gc_switch_buffer_internal : Cannot allocate a space in ring buffer.'); Exit(EBUSY); end; //IT_SWITCH_BUFFER = $0000008b; buf^.header:=$c0008b00; buf^.data :=0; gc_ring_pm4_submit(ring); end; function gc_pm4_event_write_eop(ring:p_pm4_ring;addr:Pointer;data:QWORD;intSel,wait:Integer):Integer; var buf:PPM4CMDEVENTWRITEEOP; begin Result:=0; buf:=nil; if not gc_ring_pm4_alloc(ring,sizeof(PM4CMDEVENTWRITEEOP),@buf) then begin Writeln(stderr,'### gc_pm4_event_write_eop : Cannot allocate a space in ring buffer.'); Exit(EBUSY); end; buf^:=Default(PM4CMDEVENTWRITEEOP); // IT_EVENT_WRITE_EOP DWORD(buf^.header):=$C0044700; if (wait=0) then begin buf^.eventType:=BOTTOM_OF_PIPE_TS; //CbDbReadsDone end else begin buf^.eventType:=CACHE_FLUSH_TS; //FlushCbDbCaches end; buf^.eventIndex :=5; buf^.invalidateL2:=1; buf^.address :=QWORD(addr); buf^.intSel :=ord(intSel<>0)*EVENTWRITEEOP_INT_SEL_SEND_INT_ON_CONFIRM; buf^.dataSel :=EVENTWRITEEOP_DATA_SEL_SEND_DATA64; buf^.DATA :=data; gc_ring_pm4_submit(ring); end; Function gc_map_hqd(ringBaseAddress:Pointer; readPtrAddress :Pointer; nextPtrAddress :Pointer; lenLog2 :DWORD; g_queueId :DWORD; pipePriority :DWORD; hqd:p_gc_hqd):Integer; var base_dmem_addr:PDWORD; read_mirr_addr:PDWORD; base:Pointer; offset:Integer; begin Result:=0; if ((lenLog2 - 8) >= 23) then begin Exit(Integer($804c000c)); end; if ((g_queueId - 1) >= 511) then begin Exit(Integer($804c000d)); end; base_dmem_addr:=get_dmem_ptr(ringBaseAddress); offset:=QWORD(readPtrAddress) and PAGE_MASK; base :=Pointer(QWORD(readPtrAddress) and (not PAGE_MASK)); read_mirr_addr:=mirror_map(base,PAGE_SIZE); read_mirr_addr:=Pointer(QWORD(read_mirr_addr)+offset); //TODO: ring bound check hqd^.base_guest_addr:=ringBaseAddress; hqd^.base_dmem_addr :=base_dmem_addr; hqd^.read_guest_addr:=readPtrAddress; hqd^.read_mirr_addr :=read_mirr_addr; hqd^.next_dmem_addr :=nextPtrAddress; hqd^.lenLog2 :=lenLog2; hqd^.g_queueId :=g_queueId; hqd^.pipePriority :=pipePriority; hqd^.ringSizeDw :=1 shl (lenLog2-2); hqd^.ReadOffsetDw :=0; hqd^.enable :=1; end; Function gc_unmap_hqd(hqd:p_gc_hqd):Integer; var base:Pointer; begin hqd^:=Default(t_gc_hqd); base:=Pointer(QWORD(hqd^.read_mirr_addr) and (not PAGE_MASK)); mirror_unmap(base,PAGE_SIZE); Result:=0; end; //single producer Function gc_map_hdq_ding_dong(hqd:p_gc_hqd;NextOffsetDw:DWORD):Integer; begin if (hqd^.enable=0) then Exit(EINVAL); if (NextOffsetDw <= hqd^.ringSizeDw) then begin hqd^.next_dmem_addr^:=NextOffsetDw; //hqd^.read_dmem_addr^:=NextOffsetDw; Result:=0; end else begin Result:=EINVAL; end; end; //single consumer function gc_map_hdq_peek(hqd:p_gc_hqd;size:PDWORD;buff:PPointer):Boolean; var ReadOffsetDw:DWORD; NextOffsetDw:DWORD; begin Result:=False; if (hqd^.enable=0) then Exit; ReadOffsetDw:=hqd^.ReadOffsetDw and (hqd^.ringSizeDw-1); NextOffsetDw:=hqd^.next_dmem_addr^ and (hqd^.ringSizeDw-1); if (ReadOffsetDw=NextOffsetDw) then Exit; if (NextOffsetDw>ReadOffsetDw) then begin size^:=(NextOffsetDw-ReadOffsetDw) shl 2; end else begin size^:=(hqd^.ringSizeDw-ReadOffsetDw) shl 2; end; buff^:=hqd^.base_dmem_addr + (ReadOffsetDw*4); Result:=True; end; //single consumer function gc_map_hdq_drain(hqd:p_gc_hqd;size:DWORD):Boolean; var ReadOffsetDw:DWORD; NextOffsetDw:DWORD; s :DWORD; begin Result:=False; if (hqd^.enable=0) then Exit; ReadOffsetDw:=hqd^.ReadOffsetDw and (hqd^.ringSizeDw-1); NextOffsetDw:=hqd^.next_dmem_addr^ and (hqd^.ringSizeDw-1); if (ReadOffsetDw=NextOffsetDw) then Exit; if (NextOffsetDw>ReadOffsetDw) then begin s:=(NextOffsetDw-ReadOffsetDw) shl 2; end else begin s:=(hqd^.ringSizeDw-ReadOffsetDw+NextOffsetDw) shl 2; end; if (size>s) then Exit; ReadOffsetDw:=(ReadOffsetDw + (size shr 2)) and (hqd^.ringSizeDw-1); hqd^.ReadOffsetDw :=ReadOffsetDw; hqd^.read_mirr_addr^:=ReadOffsetDw; Result:=True; end; end.