111 lines
3.5 KiB
C#
111 lines
3.5 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.HLE;
|
|
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
namespace SharpEmu.Libs.Kernel;
|
|
|
|
internal static class KernelVirtualRangeAllocator
|
|
{
|
|
public static bool TryReserve(
|
|
CpuContext ctx,
|
|
ulong desiredAddress,
|
|
ulong length,
|
|
bool executable,
|
|
ulong alignment,
|
|
bool allowSearch,
|
|
bool allowAllocateAtAlternative,
|
|
string traceName,
|
|
out ulong mappedAddress,
|
|
bool backPartialOverlap = false)
|
|
{
|
|
mappedAddress = 0;
|
|
if (length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!TryResolveAddressSpace(ctx.Memory, out var addressSpace))
|
|
{
|
|
Console.Error.WriteLine($"[LOADER][TRACE] {traceName}: AllocateAt missing on {ctx.Memory.GetType().FullName}");
|
|
return false;
|
|
}
|
|
|
|
if (allowSearch &&
|
|
addressSpace.TryAllocateAtOrAbove(desiredAddress, length, executable, alignment, out var searchedAddress) &&
|
|
searchedAddress != 0)
|
|
{
|
|
mappedAddress = searchedAddress;
|
|
return true;
|
|
}
|
|
|
|
// Fixed mappings must cover the whole requested window even when part of
|
|
// it is already backed by another allocation. The single-call AllocateAt
|
|
// below is all-or-nothing and fails outright on partial overlap, leaving
|
|
// the untouched pages unmapped for the guest to fault into. Fill the free
|
|
// pages directly instead.
|
|
if (backPartialOverlap &&
|
|
addressSpace.TryBackFixedRange(desiredAddress, length, executable))
|
|
{
|
|
mappedAddress = desiredAddress;
|
|
return true;
|
|
}
|
|
|
|
var allocated = addressSpace.AllocateAt(desiredAddress, length, executable, allowAllocateAtAlternative);
|
|
if (allocated == 0)
|
|
{
|
|
Console.Error.WriteLine($"[LOADER][TRACE] {traceName}: AllocateAt returned {typeof(ulong).FullName} value=0");
|
|
return false;
|
|
}
|
|
|
|
mappedAddress = allocated;
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
// Expected when a fixed-address request cannot be satisfied on
|
|
// this host; the caller falls back or reports the failure.
|
|
Console.Error.WriteLine(
|
|
$"[LOADER][TRACE] {traceName}: no host mapping at 0x{desiredAddress:X16} len=0x{length:X}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the <see cref="IGuestAddressSpace"/> behind <paramref name="rootMemory"/>,
|
|
/// unwrapping decorators (bounded, like the reflection walker this replaced).
|
|
/// </summary>
|
|
public static bool TryResolveAddressSpace(ICpuMemory rootMemory, [NotNullWhen(true)] out IGuestAddressSpace? addressSpace)
|
|
{
|
|
var target = rootMemory;
|
|
for (var depth = 0; depth < 4; depth++)
|
|
{
|
|
if (target is IGuestAddressSpace resolved)
|
|
{
|
|
addressSpace = resolved;
|
|
return true;
|
|
}
|
|
|
|
if (target is not ICpuMemoryWrapper wrapper)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var inner = wrapper.Inner;
|
|
if (inner is null || ReferenceEquals(inner, target))
|
|
{
|
|
break;
|
|
}
|
|
|
|
target = inner;
|
|
}
|
|
|
|
addressSpace = null;
|
|
return false;
|
|
}
|
|
}
|