From cde859c5e5bccb7c3aa8bbc016b5d67e51c89f28 Mon Sep 17 00:00:00 2001 From: ParantezTech <12572227+par274@users.noreply.github.com> Date: Thu, 8 Oct 2026 23:44:41 +0300 Subject: [PATCH] [Tests] Cover random devices, POSIX open, UDS initialization, pad class data and exception contexts Co-authored-by: Sekai <140012992+SekaixX@users.noreply.github.com> --- .../Cpu/GuestExceptionContextLayoutTests.cs | 57 +++++++++++++++++++ .../Kernel/KernelMemoryCompatExportsTests.cs | 53 +++++++++++++++++ .../Np/NpUniversalDataSystemExportsTests.cs | 23 ++++++++ .../Pad/PadExportsTests.cs | 43 ++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 tests/SharpEmu.Libs.Tests/Cpu/GuestExceptionContextLayoutTests.cs diff --git a/tests/SharpEmu.Libs.Tests/Cpu/GuestExceptionContextLayoutTests.cs b/tests/SharpEmu.Libs.Tests/Cpu/GuestExceptionContextLayoutTests.cs new file mode 100644 index 0000000..9734140 --- /dev/null +++ b/tests/SharpEmu.Libs.Tests/Cpu/GuestExceptionContextLayoutTests.cs @@ -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))); + } +} diff --git a/tests/SharpEmu.Libs.Tests/Kernel/KernelMemoryCompatExportsTests.cs b/tests/SharpEmu.Libs.Tests/Kernel/KernelMemoryCompatExportsTests.cs index 077927d..df2817c 100644 --- a/tests/SharpEmu.Libs.Tests/Kernel/KernelMemoryCompatExportsTests.cs +++ b/tests/SharpEmu.Libs.Tests/Kernel/KernelMemoryCompatExportsTests.cs @@ -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() { diff --git a/tests/SharpEmu.Libs.Tests/Np/NpUniversalDataSystemExportsTests.cs b/tests/SharpEmu.Libs.Tests/Np/NpUniversalDataSystemExportsTests.cs index cc23067..0d200f4 100644 --- a/tests/SharpEmu.Libs.Tests/Np/NpUniversalDataSystemExportsTests.cs +++ b/tests/SharpEmu.Libs.Tests/Np/NpUniversalDataSystemExportsTests.cs @@ -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() { diff --git a/tests/SharpEmu.Libs.Tests/Pad/PadExportsTests.cs b/tests/SharpEmu.Libs.Tests/Pad/PadExportsTests.cs index 2d3efa6..60181bb 100644 --- a/tests/SharpEmu.Libs.Tests/Pad/PadExportsTests.cs +++ b/tests/SharpEmu.Libs.Tests/Pad/PadExportsTests.cs @@ -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)]