fix(agc): patch indirect-buffer continuation links (#1012)
This commit is contained in:
parent
6cebf7ec56
commit
6cac2b5f47
|
|
@ -10,6 +10,36 @@ public static partial class AgcExports
|
|||
{
|
||||
// This partial patches previously constructed AGC command packets.
|
||||
|
||||
// Uncatalogued name; the NID and import library identify this AGC export.
|
||||
// Observed ABI: packet, cache policy, next-buffer address, length in dwords.
|
||||
// The caller patches the previous DcbJump after allocating its continuation.
|
||||
#pragma warning disable SHEM006
|
||||
[SysAbiExport(Nid = "Ikfdt-rIqCE", ExportName = "sceAgcUnknownIkfdt",
|
||||
Target = Generation.Gen5, LibraryName = "libSceAgc")]
|
||||
public static int UnknownIkfdt(CpuContext ctx)
|
||||
{
|
||||
var packet = ctx[CpuRegister.Rdi];
|
||||
var cachePolicy = (uint)ctx[CpuRegister.Rsi];
|
||||
var target = ctx[CpuRegister.Rdx];
|
||||
var dwords = (uint)ctx[CpuRegister.Rcx];
|
||||
if (cachePolicy > 3 || dwords > 0xFFFFFu ||
|
||||
!TryGetPacketIdentity(ctx, packet, out var opcode, out _) ||
|
||||
opcode != ItIndirectBuffer ||
|
||||
!TryReadUInt32(ctx, packet, out var header) ||
|
||||
((header >> 16) & 0x3FFFu) != 2 ||
|
||||
!TryReadUInt32(ctx, packet + 12, out var control))
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
|
||||
// Preserve call/chain mode and every other control bit.
|
||||
control = (control & ~0x300F_FFFFu) | (cachePolicy << 28) | dwords;
|
||||
return TryWriteUInt32(ctx, packet + 4, (uint)target) &&
|
||||
TryWriteUInt32(ctx, packet + 8, (uint)(target >> 32) & 0xFFFFu) &&
|
||||
TryWriteUInt32(ctx, packet + 12, control)
|
||||
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
|
||||
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
#pragma warning restore SHEM006
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "vcmNN+AAXnY",
|
||||
ExportName = "sceAgcSetCxRegIndirectPatchSetAddress",
|
||||
|
|
|
|||
|
|
@ -214,12 +214,4 @@ public static class GameServiceStubs
|
|||
Target = Generation.Gen5, LibraryName = "libSceVideoRecording")]
|
||||
public static int VideoRecordingSetInfo(CpuContext ctx) => Ok(ctx);
|
||||
|
||||
// Captured from GTA V Enhanced (PPSA04264); not in the public NID catalog.
|
||||
// Side-effect-free success — same as unresolved stub behavior that kept boot
|
||||
// moving; reverse the ABI before writing guest memory.
|
||||
#pragma warning disable SHEM006
|
||||
[SysAbiExport(Nid = "Ikfdt-rIqCE", ExportName = "sceUnknownIkfdt",
|
||||
Target = Generation.Gen5, LibraryName = "libKernel")]
|
||||
public static int UnknownIkfdt(CpuContext ctx) => Ok(ctx);
|
||||
#pragma warning restore SHEM006
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,6 +197,57 @@ public sealed class AgcCommandBufferChainTests
|
|||
uint targetDwords)
|
||||
=> WriteJump(ctx, memory, linkAddress, 1, target, targetDwords);
|
||||
|
||||
[Fact]
|
||||
public void PatchedPlaceholderChain_ReachesContinuation()
|
||||
{
|
||||
var memory = new FakeCpuMemory(BaseAddress, MemorySize);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
var nextDwords = WriteUnsatisfiedWait(ctx, memory, SecondLinkAddress);
|
||||
var rootDwords = WriteChain(ctx, memory, FirstLinkAddress, 0, 0);
|
||||
ctx[CpuRegister.Rdi] = FirstLinkAddress;
|
||||
ctx[CpuRegister.Rsi] = 2;
|
||||
ctx[CpuRegister.Rdx] = SecondLinkAddress;
|
||||
ctx[CpuRegister.Rcx] = nextDwords;
|
||||
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_OK, AgcExports.UnknownIkfdt(ctx));
|
||||
Assert.Equal(0x2F30_0000u | nextDwords, ReadUInt32(memory, FirstLinkAddress + 12));
|
||||
SubmitDcb(ctx, memory, FirstLinkAddress, rootDwords);
|
||||
Assert.Equal(1, StreamOf(memory).BlockedQueueCount);
|
||||
Assert.Equal(SecondLinkAddress, StreamOf(memory).SnapshotBlocked().SampleWaitAddress);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0u)]
|
||||
[InlineData(1u)]
|
||||
public void JumpPatch_PreservesModeAndEncodesAddress(uint mode)
|
||||
{
|
||||
var memory = new FakeCpuMemory(BaseAddress, MemorySize);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
WriteJump(ctx, memory, FirstLinkAddress, mode, 0, 0);
|
||||
ctx[CpuRegister.Rdi] = FirstLinkAddress;
|
||||
ctx[CpuRegister.Rsi] = 3;
|
||||
ctx[CpuRegister.Rdx] = 0xABCD_1234_5678;
|
||||
ctx[CpuRegister.Rcx] = 0x123;
|
||||
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_OK, AgcExports.UnknownIkfdt(ctx));
|
||||
Assert.Equal(0x1234_5678u, ReadUInt32(memory, FirstLinkAddress + 4));
|
||||
Assert.Equal(0xABCDu, ReadUInt32(memory, FirstLinkAddress + 8));
|
||||
Assert.Equal(0x3F20_0123u | (mode << 20), ReadUInt32(memory, FirstLinkAddress + 12));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JumpPatch_RejectsOtherPacketWithoutChangingIt()
|
||||
{
|
||||
var memory = new FakeCpuMemory(BaseAddress, MemorySize);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
WriteUInt32(memory, FirstLinkAddress, 0xC002_1000);
|
||||
WriteUInt64(memory, FirstLinkAddress + 4, 0xDEAD_BEEF);
|
||||
ctx[CpuRegister.Rdi] = FirstLinkAddress;
|
||||
ctx[CpuRegister.Rsi] = 2;
|
||||
ctx[CpuRegister.Rdx] = SecondLinkAddress;
|
||||
ctx[CpuRegister.Rcx] = 7;
|
||||
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT, AgcExports.UnknownIkfdt(ctx));
|
||||
Assert.Equal(0xDEAD_BEEFul, ReadUInt64(memory, FirstLinkAddress + 4));
|
||||
}
|
||||
|
||||
private static uint WriteJump(
|
||||
CpuContext ctx,
|
||||
FakeCpuMemory memory,
|
||||
|
|
|
|||
Loading…
Reference in New Issue