[Kernel] Return largest available direct-memory span (#334)

This commit is contained in:
Zaid Yousef
2026-07-18 02:38:39 +03:00
committed by GitHub
parent ff3ac0bba1
commit cc290f860b
2 changed files with 132 additions and 7 deletions
@@ -2459,7 +2459,20 @@ public static partial class KernelMemoryCompatExports
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT; return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
} }
if (!TryFindAvailableDirectMemorySpanLocked(searchStart, searchEnd, alignment, out var candidate, out var rangeAvailable)) bool foundSpan;
ulong candidate;
ulong rangeAvailable;
lock (_memoryGate)
{
foundSpan = TryFindAvailableDirectMemorySpanLocked(
searchStart,
searchEnd,
alignment,
out candidate,
out rangeAvailable);
}
if (!foundSpan)
{ {
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND; return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
} }
@@ -5909,9 +5922,12 @@ public static partial class KernelMemoryCompatExports
var gapEnd = Math.Min(allocation.Start, effectiveEnd); var gapEnd = Math.Min(allocation.Start, effectiveEnd);
if (candidate < gapEnd) if (candidate < gapEnd)
{ {
spanStart = candidate; var candidateLength = gapEnd - candidate;
spanLength = gapEnd - candidate; if (candidateLength > spanLength)
return true; {
spanStart = candidate;
spanLength = candidateLength;
}
} }
if (allocation.Start >= effectiveEnd) if (allocation.Start >= effectiveEnd)
@@ -5922,12 +5938,20 @@ public static partial class KernelMemoryCompatExports
candidate = AlignUp(Math.Max(candidate, allocationEnd), alignment); candidate = AlignUp(Math.Max(candidate, allocationEnd), alignment);
if (candidate >= effectiveEnd) if (candidate >= effectiveEnd)
{ {
return false; break;
}
}
if (candidate < effectiveEnd)
{
var candidateLength = effectiveEnd - candidate;
if (candidateLength > spanLength)
{
spanStart = candidate;
spanLength = candidateLength;
} }
} }
spanStart = candidate;
spanLength = effectiveEnd - candidate;
return spanLength != 0; return spanLength != 0;
} }
@@ -9,8 +9,20 @@ using Xunit;
namespace SharpEmu.Libs.Tests.Kernel; namespace SharpEmu.Libs.Tests.Kernel;
[CollectionDefinition(KernelMemoryCompatStateCollection.Name, DisableParallelization = true)]
public sealed class KernelMemoryCompatStateCollection
{
public const string Name = "KernelMemoryCompatState";
}
[Collection(KernelMemoryCompatStateCollection.Name)]
public sealed class KernelMemoryCompatExportsTests public sealed class KernelMemoryCompatExportsTests
{ {
private const ulong GuestMemoryBase = 0x1_0000_0000;
private const ulong AllocationOutAddress = GuestMemoryBase + 0x100;
private const ulong SpanStartOutAddress = GuestMemoryBase + 0x108;
private const ulong SpanSizeOutAddress = GuestMemoryBase + 0x110;
[Fact] [Fact]
public void PosixStat_MissingFileReturnsMinusOne() public void PosixStat_MissingFileReturnsMinusOne()
{ {
@@ -63,4 +75,93 @@ public sealed class KernelMemoryCompatExportsTests
CultureInfo.CurrentCulture = previousCulture; CultureInfo.CurrentCulture = previousCulture;
} }
} }
[Fact]
public void AvailableDirectMemorySize_FragmentedRangeReturnsLargestAlignedSpan()
{
const ulong firstAllocationStart = 0x0020_0000;
const ulong firstAllocationLength = 0x0020_0000;
const ulong secondAllocationStart = 0x00C0_0000;
const ulong secondAllocationLength = 0x0040_0000;
var context = new CpuContext(new FakeCpuMemory(GuestMemoryBase, 0x1000), Generation.Gen5);
try
{
AllocateDirectMemory(context, firstAllocationStart, firstAllocationLength);
AllocateDirectMemory(context, secondAllocationStart, secondAllocationLength);
QueryAvailableDirectMemory(context, 0, 0x0100_0000, 0x4000);
Assert.True(context.TryReadUInt64(SpanStartOutAddress, out var spanStart));
Assert.True(context.TryReadUInt64(SpanSizeOutAddress, out var spanSize));
Assert.Equal(0x0040_0000UL, spanStart);
Assert.Equal(0x0080_0000UL, spanSize);
}
finally
{
ReleaseDirectMemory(context, firstAllocationStart, firstAllocationLength);
ReleaseDirectMemory(context, secondAllocationStart, secondAllocationLength);
}
}
[Fact]
public void AvailableDirectMemorySize_AppliesAlignmentBeforeComparingSpans()
{
const ulong allocationStart = 0x0070_0000;
const ulong allocationLength = 0x0010_0000;
var context = new CpuContext(new FakeCpuMemory(GuestMemoryBase, 0x1000), Generation.Gen5);
try
{
AllocateDirectMemory(context, allocationStart, allocationLength);
QueryAvailableDirectMemory(context, 0x0010_0000, 0x00C0_0000, 0x0040_0000);
Assert.True(context.TryReadUInt64(SpanStartOutAddress, out var spanStart));
Assert.True(context.TryReadUInt64(SpanSizeOutAddress, out var spanSize));
Assert.Equal(0x0080_0000UL, spanStart);
Assert.Equal(0x0040_0000UL, spanSize);
}
finally
{
ReleaseDirectMemory(context, allocationStart, allocationLength);
}
}
private static void AllocateDirectMemory(CpuContext context, ulong start, ulong length)
{
context[CpuRegister.Rdi] = start;
context[CpuRegister.Rsi] = start + length;
context[CpuRegister.Rdx] = length;
context[CpuRegister.Rcx] = 0x4000;
context[CpuRegister.R8] = 0;
context[CpuRegister.R9] = AllocationOutAddress;
Assert.Equal(0, KernelMemoryCompatExports.KernelAllocateDirectMemory(context));
Assert.True(context.TryReadUInt64(AllocationOutAddress, out var allocatedAddress));
Assert.Equal(start, allocatedAddress);
}
private static void QueryAvailableDirectMemory(
CpuContext context,
ulong searchStart,
ulong searchEnd,
ulong alignment)
{
context[CpuRegister.Rdi] = searchStart;
context[CpuRegister.Rsi] = searchEnd;
context[CpuRegister.Rdx] = alignment;
context[CpuRegister.Rcx] = SpanStartOutAddress;
context[CpuRegister.R8] = SpanSizeOutAddress;
Assert.Equal(0, KernelMemoryCompatExports.KernelAvailableDirectMemorySize(context));
}
private static void ReleaseDirectMemory(CpuContext context, ulong start, ulong length)
{
context[CpuRegister.Rdi] = start;
context[CpuRegister.Rsi] = length;
Assert.Equal(0, KernelMemoryCompatExports.KernelReleaseDirectMemory(context));
}
} }