[Tests] Cover random devices, POSIX open, UDS initialization, pad class data and exception contexts

Co-authored-by: Sekai <140012992+SekaixX@users.noreply.github.com>
This commit is contained in:
ParantezTech 2026-10-08 23:44:41 +03:00
parent 757567bd15
commit cde859c5e5
4 changed files with 176 additions and 0 deletions

View File

@ -0,0 +1,57 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Buffers.Binary;
using SharpEmu.Core.Cpu.Native;
using SharpEmu.HLE;
using Xunit;
namespace SharpEmu.Libs.Tests.Cpu;
public sealed class GuestExceptionContextLayoutTests
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public void ExceptionContext_UsesBareMcontextAndSonyStackAlias(bool saved)
{
const ulong address = 0x1_0000_0100;
var memory = new FakeCpuMemory(0x1_0000_0000, 0x1000);
var context = new CpuContext(memory, Generation.Gen5)
{
Rip = 0x101000,
Rflags = 0x246,
FsBase = 0x303000,
GsBase = 0x404000,
};
context[CpuRegister.Rdi] = 0x11;
context[CpuRegister.Rbx] = 0x22;
context[CpuRegister.Rsp] = 0x202000;
var continuation = saved ? new GuestCpuContinuation
{
Rip = 0x505000,
Rsp = 0x606000,
Rflags = 0x203,
Rdi = 0x33,
Rbx = 0x44,
FsBase = 0x707000,
GsBase = 0x808000,
} : default;
Assert.True(DirectExecutionBackend.TryWriteGuestExceptionContext(context, address, continuation, 0x500));
var bytes = new byte[0x500];
Assert.True(memory.TryRead(address, bytes));
Assert.Equal(saved ? continuation.Rdi : context[CpuRegister.Rdi], Read64(0x08));
Assert.Equal(saved ? continuation.Rbx : context[CpuRegister.Rbx], Read64(0x40));
Assert.Equal(saved ? continuation.Rip : context.Rip, Read64(0xA0));
Assert.Equal(saved ? continuation.Rflags : context.Rflags, Read64(0xB0));
Assert.Equal(saved ? continuation.Rsp : context[CpuRegister.Rsp], Read64(0xB8));
Assert.Equal(Read64(0xB8), Read64(0xF8));
Assert.Equal(0x480UL, Read64(0xC8));
Assert.Equal(saved ? continuation.FsBase : context.FsBase, Read64(0x440));
Assert.Equal(saved ? continuation.GsBase : context.GsBase, Read64(0x448));
ulong Read64(int offset) => BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(offset, sizeof(ulong)));
}
}

View File

@ -197,6 +197,59 @@ public sealed class KernelMemoryCompatExportsTests
Assert.Equal(ulong.MaxValue, context[CpuRegister.Rax]);
}
[Fact]
public void UnderscoreOpen_MissingFileUsesPosixFailureAbi()
{
var memory = new FakeCpuMemory(GuestMemoryBase, 0x1000);
var context = new CpuContext(memory, Generation.Gen5) { FsBase = GuestMemoryBase + 0x800 };
memory.WriteCString(GuestMemoryBase + 0x100, "/__sharpemu_test_missing__/resource_level_high.bin");
context[CpuRegister.Rdi] = GuestMemoryBase + 0x100;
Assert.Equal(-1, KernelMemoryCompatExports.KernelOpenUnderscore(context));
Assert.Equal(ulong.MaxValue, context[CpuRegister.Rax]);
Assert.True(context.TryReadUInt32(context.FsBase + 0x40, out var errno));
Assert.Equal(2u, errno);
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND, KernelExports.KernelOpen(context));
}
[Theory]
[InlineData("/dev/random")]
[InlineData("/dev/urandom")]
public void PosixRandomDevice_OpenReadFstatAndClose(string path)
{
var memory = new FakeCpuMemory(GuestMemoryBase, 0x1000);
var context = new CpuContext(memory, Generation.Gen5);
memory.WriteCString(GuestMemoryBase + 0x100, path);
context[CpuRegister.Rdi] = GuestMemoryBase + 0x100;
Assert.Equal(0, KernelMemoryCompatExports.KernelOpenUnderscore(context));
var fd = context[CpuRegister.Rax];
Assert.True(fd >= 3);
try
{
context[CpuRegister.Rdi] = fd;
context[CpuRegister.Rsi] = GuestMemoryBase + 0x200;
context[CpuRegister.Rdx] = 32;
Assert.Equal(0, KernelMemoryCompatExports.PosixRead(context));
Assert.Equal(32UL, context[CpuRegister.Rax]);
var randomBytes = new byte[32];
Assert.True(memory.TryRead(GuestMemoryBase + 0x200, randomBytes));
Assert.Contains(randomBytes, value => value != 0);
context[CpuRegister.Rsi] = GuestMemoryBase + 0x400;
Assert.Equal(0, KernelMemoryCompatExports.PosixFstat(context));
}
finally
{
context[CpuRegister.Rdi] = fd;
Assert.Equal(0, KernelMemoryCompatExports.PosixClose(context));
}
context[CpuRegister.Rsi] = GuestMemoryBase + 0x200;
context[CpuRegister.Rdx] = 1;
Assert.Equal(-1, KernelMemoryCompatExports.PosixRead(context));
}
[Fact]
public void PosixOpen_MissingFileReturnsMinusOne()
{

View File

@ -27,6 +27,29 @@ public sealed class NpUniversalDataSystemExportsTests : IDisposable
public void Dispose() => NpUniversalDataSystemState.ResetForTests();
[Fact]
public void ContextBootstrap_PreservesHandlesAcrossExplicitInitialization()
{
var context = CreateContext(userId: 7, serviceLabel: 3, options: 0x11);
var serviceHandle = CreateServiceHandle();
var before = NpUniversalDataSystemState.GetMemoryStat();
Assert.True(before.CurrentInUseSize > 0);
Initialize(poolSize: 0x20_0000);
Initialize(poolSize: 0x40_0000);
SetRegisters((ulong)(uint)context, (ulong)(uint)serviceHandle, 0x22);
AssertSuccess(NpUniversalDataSystemExports.NpUniversalDataSystemRegisterContext(_context));
var after = NpUniversalDataSystemState.GetMemoryStat();
Assert.Equal(0x40_0000UL, after.PoolSize);
Assert.True(after.CurrentInUseSize >= before.CurrentInUseSize);
Assert.True(after.MaximumInUseSize >= before.MaximumInUseSize);
AssertSuccess(NpUniversalDataSystemExports.NpUniversalDataSystemTerminate(_context));
SetRegisters((ulong)(uint)context, (ulong)(uint)serviceHandle, 0x22);
Assert.Equal(ErrorInvalidArgument, NpUniversalDataSystemExports.NpUniversalDataSystemRegisterContext(_context));
}
[Fact]
public void FullLifecycle_PreservesAttachedChildAfterItsHandleIsDestroyed()
{

View File

@ -23,6 +23,49 @@ public sealed class PadExportsTests : IDisposable
public void Dispose() => PadExports.ResetOpenedPadForTests();
[Theory]
[InlineData(0, 0)]
[InlineData(4, 4)]
[InlineData(15, 12)]
public void DeviceClassParseData_BoundsThePayloadAndPreservesAdjacentMemory(int length, int copied)
{
var padData = new byte[0x78];
padData[0x4C] = 1;
padData[0x6B] = (byte)length;
for (var index = 0; index < 12; index++)
{
padData[0x6C + index] = (byte)(index + 1);
}
Assert.True(_memory.TryWrite(Base + 0x100, padData));
Assert.True(_memory.TryWrite(Base + 0x200, Enumerable.Repeat((byte)0xA5, 32).ToArray()));
_ctx[CpuRegister.Rdi] = 0;
_ctx[CpuRegister.Rsi] = Base + 0x100;
_ctx[CpuRegister.Rdx] = Base + 0x200;
Assert.Equal(0, PadExports.PadDeviceClassParseData(_ctx));
var output = new byte[32];
Assert.True(_memory.TryRead(Base + 0x200, output));
Assert.Equal(copied == 0 ? 0 : -1, BitConverter.ToInt32(output));
Assert.Equal(1, output[4]);
Assert.Equal(copied, output[8]);
Assert.Equal(padData.AsSpan(0x6C, copied).ToArray(), output.AsSpan(12, copied).ToArray());
Assert.All(output[24..], value => Assert.Equal(0xA5, value));
}
[Fact]
public void DeviceClassGetExtendedInformation_LeavesAdjacentMemoryIntact()
{
const ulong address = Base + 0x100;
Assert.True(_memory.TryWrite(address, Enumerable.Repeat((byte)0xA5, 32).ToArray()));
_ctx[CpuRegister.Rdi] = 0;
_ctx[CpuRegister.Rsi] = address;
Assert.Equal(0, PadExports.PadDeviceClassGetExtendedInformation(_ctx));
var output = new byte[32];
Assert.True(_memory.TryRead(address, output));
Assert.All(output[..0x14], value => Assert.Equal(0, value));
Assert.All(output[0x14..], value => Assert.Equal(0xA5, value));
}
[NativeX64Theory]
[InlineData(0, 0)]
[InlineData(1, 0)]