mirror of https://github.com/red-prig/fpPS4.git
493 lines
10 KiB
Plaintext
493 lines
10 KiB
Plaintext
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 (size<GC_RING_PADD) then size:=GC_RING_PADD;
|
|
|
|
ring^.buff:=nil;
|
|
ring^.size:=size;
|
|
ring^.rptr:=0;
|
|
ring^.wptr:=0;
|
|
ring^.aptr:=0;
|
|
|
|
Result:=md_placeholder_mmap(ring^.buff,size shl 1);
|
|
if (Result<>0) then Exit;
|
|
|
|
Result:=md_placeholder_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_placeholder_commit(ring^.buff,size,VM_RW,hMem,0);
|
|
if (Result<>0) then
|
|
begin
|
|
md_memfd_close(hMem);
|
|
Exit;
|
|
end;
|
|
|
|
Result:=md_placeholder_commit(ring^.buff+size,size,VM_RW,hMem,0);
|
|
|
|
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_placeholder_unmap(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 else
|
|
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 QWORD(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 QWORD(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.
|
|
|
|
|