aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-11-28 20:18:09 -0200
committerGitHub <noreply@github.com>2018-11-28 20:18:09 -0200
commit00579927e43bf55ee06ae02933c1e755fb4120eb (patch)
tree0fd06db7b28e0accf87b465ec6f4dc74691febab /Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
parente7fe7d724778535f8eff390abef54274a343c0b7 (diff)
Better process implementation (#491)
* Initial implementation of KProcess * Some improvements to the memory manager, implement back guest stack trace printing * Better GetInfo implementation, improve checking in some places with information from process capabilities * Allow the cpu to read/write from the correct memory locations for accesses crossing a page boundary * Change long -> ulong for address/size on memory related methods to avoid unnecessary casts * Attempt at implementing ldr:ro with new KProcess * Allow BSS with size 0 on ldr:ro * Add checking for memory block slab heap usage, return errors if full, exit gracefully * Use KMemoryBlockSize const from KMemoryManager * Allow all methods to read from non-contiguous locations * Fix for TransactParcelAuto * Address PR feedback, additionally fix some small issues related to the KIP loader and implement SVCs GetProcessId, GetProcessList, GetSystemInfo, CreatePort and ManageNamedPort * Fix wrong check for source pages count from page list on MapPhysicalMemory * Fix some issues with UnloadNro on ldr:ro
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs2155
1 files changed, 1711 insertions, 444 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
index 4cfb2aa2..0aa21e3f 100644
--- a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
@@ -1,331 +1,858 @@
using ChocolArm64.Memory;
-using Ryujinx.HLE.Memory;
+using Ryujinx.Common;
using System;
using System.Collections.Generic;
-using static Ryujinx.HLE.HOS.ErrorCode;
-
namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryManager
{
public const int PageSize = 0x1000;
+ private const int KMemoryBlockSize = 0x40;
+
+ //We need 2 blocks for the case where a big block
+ //needs to be split in 2, plus one block that will be the new one inserted.
+ private const int MaxBlocksNeededForInsertion = 2;
+
private LinkedList<KMemoryBlock> Blocks;
private MemoryManager CpuMemory;
- private ArenaAllocator Allocator;
+ private Horizon System;
+
+ public ulong AddrSpaceStart { get; private set; }
+ public ulong AddrSpaceEnd { get; private set; }
+
+ public ulong CodeRegionStart { get; private set; }
+ public ulong CodeRegionEnd { get; private set; }
+
+ public ulong HeapRegionStart { get; private set; }
+ public ulong HeapRegionEnd { get; private set; }
+
+ private ulong CurrentHeapAddr;
- public long AddrSpaceStart { get; private set; }
- public long AddrSpaceEnd { get; private set; }
+ public ulong AliasRegionStart { get; private set; }
+ public ulong AliasRegionEnd { get; private set; }
- public long CodeRegionStart { get; private set; }
- public long CodeRegionEnd { get; private set; }
+ public ulong StackRegionStart { get; private set; }
+ public ulong StackRegionEnd { get; private set; }
- public long MapRegionStart { get; private set; }
- public long MapRegionEnd { get; private set; }
+ public ulong TlsIoRegionStart { get; private set; }
+ public ulong TlsIoRegionEnd { get; private set; }
- public long HeapRegionStart { get; private set; }
- public long HeapRegionEnd { get; private set; }
+ private ulong HeapCapacity;
- public long NewMapRegionStart { get; private set; }
- public long NewMapRegionEnd { get; private set; }
+ public ulong PhysicalMemoryUsage { get; private set; }
- public long TlsIoRegionStart { get; private set; }
- public long TlsIoRegionEnd { get; private set; }
+ private MemoryRegion MemRegion;
- public long PersonalMmHeapUsage { get; private set; }
+ private bool AslrDisabled;
- private long CurrentHeapAddr;
+ public int AddrSpaceWidth { get; private set; }
- public KMemoryManager(Process Process)
+ private bool IsKernel;
+ private bool AslrEnabled;
+
+ private KMemoryBlockAllocator BlockAllocator;
+
+ private int ContextId;
+
+ private MersenneTwister RandomNumberGenerator;
+
+ public KMemoryManager(Horizon System, MemoryManager CpuMemory)
{
- CpuMemory = Process.Memory;
- Allocator = Process.Device.Memory.Allocator;
+ this.System = System;
+ this.CpuMemory = CpuMemory;
- long CodeRegionSize;
- long MapRegionSize;
- long HeapRegionSize;
- long NewMapRegionSize;
- long TlsIoRegionSize;
- int AddrSpaceWidth;
+ Blocks = new LinkedList<KMemoryBlock>();
+ }
- AddressSpaceType AddrType = AddressSpaceType.Addr39Bits;
+ private static readonly int[] AddrSpaceSizes = new int[] { 32, 36, 32, 39 };
- if (Process.MetaData != null)
+ public KernelResult InitializeForProcess(
+ AddressSpaceType AddrSpaceType,
+ bool AslrEnabled,
+ bool AslrDisabled,
+ MemoryRegion MemRegion,
+ ulong Address,
+ ulong Size,
+ KMemoryBlockAllocator BlockAllocator)
+ {
+ if ((uint)AddrSpaceType > (uint)AddressSpaceType.Addr39Bits)
{
- AddrType = (AddressSpaceType)Process.MetaData.AddressSpaceWidth;
+ throw new ArgumentException(nameof(AddrSpaceType));
}
- switch (AddrType)
+ ContextId = System.ContextIdManager.GetId();
+
+ ulong AddrSpaceBase = 0;
+ ulong AddrSpaceSize = 1UL << AddrSpaceSizes[(int)AddrSpaceType];
+
+ KernelResult Result = CreateUserAddressSpace(
+ AddrSpaceType,
+ AslrEnabled,
+ AslrDisabled,
+ AddrSpaceBase,
+ AddrSpaceSize,
+ MemRegion,
+ Address,
+ Size,
+ BlockAllocator);
+
+ if (Result != KernelResult.Success)
+ {
+ System.ContextIdManager.PutId(ContextId);
+ }
+
+ return Result;
+ }
+
+ private class Region
+ {
+ public ulong Start;
+ public ulong End;
+ public ulong Size;
+ public ulong AslrOffset;
+ }
+
+ private KernelResult CreateUserAddressSpace(
+ AddressSpaceType AddrSpaceType,
+ bool AslrEnabled,
+ bool AslrDisabled,
+ ulong AddrSpaceStart,
+ ulong AddrSpaceEnd,
+ MemoryRegion MemRegion,
+ ulong Address,
+ ulong Size,
+ KMemoryBlockAllocator BlockAllocator)
+ {
+ ulong EndAddr = Address + Size;
+
+ Region AliasRegion = new Region();
+ Region HeapRegion = new Region();
+ Region StackRegion = new Region();
+ Region TlsIoRegion = new Region();
+
+ ulong CodeRegionSize;
+ ulong StackAndTlsIoStart;
+ ulong StackAndTlsIoEnd;
+ ulong BaseAddress;
+
+ switch (AddrSpaceType)
{
case AddressSpaceType.Addr32Bits:
- CodeRegionStart = 0x200000;
- CodeRegionSize = 0x3fe00000;
- MapRegionSize = 0x40000000;
- HeapRegionSize = 0x40000000;
- NewMapRegionSize = 0;
- TlsIoRegionSize = 0;
- AddrSpaceWidth = 32;
+ AliasRegion.Size = 0x40000000;
+ HeapRegion.Size = 0x40000000;
+ StackRegion.Size = 0;
+ TlsIoRegion.Size = 0;
+ CodeRegionStart = 0x200000;
+ CodeRegionSize = 0x3fe00000;
+ StackAndTlsIoStart = 0x200000;
+ StackAndTlsIoEnd = 0x40000000;
+ BaseAddress = 0x200000;
+ AddrSpaceWidth = 32;
break;
case AddressSpaceType.Addr36Bits:
- CodeRegionStart = 0x8000000;
- CodeRegionSize = 0x78000000;
- MapRegionSize = 0x180000000;
- HeapRegionSize = 0x180000000;
- NewMapRegionSize = 0;
- TlsIoRegionSize = 0;
- AddrSpaceWidth = 36;
+ AliasRegion.Size = 0x180000000;
+ HeapRegion.Size = 0x180000000;
+ StackRegion.Size = 0;
+ TlsIoRegion.Size = 0;
+ CodeRegionStart = 0x8000000;
+ CodeRegionSize = 0x78000000;
+ StackAndTlsIoStart = 0x8000000;
+ StackAndTlsIoEnd = 0x80000000;
+ BaseAddress = 0x8000000;
+ AddrSpaceWidth = 36;
break;
- case AddressSpaceType.Addr36BitsNoMap:
- CodeRegionStart = 0x200000;
- CodeRegionSize = 0x3fe00000;
- MapRegionSize = 0;
- HeapRegionSize = 0x80000000;
- NewMapRegionSize = 0;
- TlsIoRegionSize = 0;
- AddrSpaceWidth = 36;
+ case AddressSpaceType.Addr32BitsNoMap:
+ AliasRegion.Size = 0;
+ HeapRegion.Size = 0x80000000;
+ StackRegion.Size = 0;
+ TlsIoRegion.Size = 0;
+ CodeRegionStart = 0x200000;
+ CodeRegionSize = 0x3fe00000;
+ StackAndTlsIoStart = 0x200000;
+ StackAndTlsIoEnd = 0x40000000;
+ BaseAddress = 0x200000;
+ AddrSpaceWidth = 32;
break;
case AddressSpaceType.Addr39Bits:
- CodeRegionStart = 0x8000000;
- CodeRegionSize = 0x80000000;
- MapRegionSize = 0x1000000000;
- HeapRegionSize = 0x180000000;
- NewMapRegionSize = 0x80000000;
- TlsIoRegionSize = 0x1000000000;
- AddrSpaceWidth = 39;
+ AliasRegion.Size = 0x1000000000;
+ HeapRegion.Size = 0x180000000;
+ StackRegion.Size = 0x80000000;
+ TlsIoRegion.Size = 0x1000000000;
+ CodeRegionStart = BitUtils.AlignDown(Address, 0x200000);
+ CodeRegionSize = BitUtils.AlignUp (EndAddr, 0x200000) - CodeRegionStart;
+ StackAndTlsIoStart = 0;
+ StackAndTlsIoEnd = 0;
+ BaseAddress = 0x8000000;
+ AddrSpaceWidth = 39;
break;
- default: throw new InvalidOperationException();
+ default: throw new ArgumentException(nameof(AddrSpaceType));
+ }
+
+ CodeRegionEnd = CodeRegionStart + CodeRegionSize;
+
+ ulong MapBaseAddress;
+ ulong MapAvailableSize;
+
+ if (CodeRegionStart - BaseAddress >= AddrSpaceEnd - CodeRegionEnd)
+ {
+ //Has more space before the start of the code region.
+ MapBaseAddress = BaseAddress;
+ MapAvailableSize = CodeRegionStart - BaseAddress;
+ }
+ else
+ {
+ //Has more space after the end of the code region.
+ MapBaseAddress = CodeRegionEnd;
+ MapAvailableSize = AddrSpaceEnd - CodeRegionEnd;
}
- AddrSpaceStart = 0;
- AddrSpaceEnd = 1L << AddrSpaceWidth;
+ ulong MapTotalSize = AliasRegion.Size + HeapRegion.Size + StackRegion.Size + TlsIoRegion.Size;
+
+ ulong AslrMaxOffset = MapAvailableSize - MapTotalSize;
- CodeRegionEnd = CodeRegionStart + CodeRegionSize;
- MapRegionStart = CodeRegionEnd;
- MapRegionEnd = CodeRegionEnd + MapRegionSize;
- HeapRegionStart = MapRegionEnd;
- HeapRegionEnd = MapRegionEnd + HeapRegionSize;
- NewMapRegionStart = HeapRegionEnd;
- NewMapRegionEnd = HeapRegionEnd + NewMapRegionSize;
- TlsIoRegionStart = NewMapRegionEnd;
- TlsIoRegionEnd = NewMapRegionEnd + TlsIoRegionSize;
+ this.AslrEnabled = AslrEnabled;
- CurrentHeapAddr = HeapRegionStart;
+ this.AddrSpaceStart = AddrSpaceStart;
+ this.AddrSpaceEnd = AddrSpaceEnd;
- if (NewMapRegionSize == 0)
+ this.BlockAllocator = BlockAllocator;
+
+ if (MapAvailableSize < MapTotalSize)
{
- NewMapRegionStart = AddrSpaceStart;
- NewMapRegionEnd = AddrSpaceEnd;
+ return KernelResult.OutOfMemory;
}
- Blocks = new LinkedList<KMemoryBlock>();
+ if (AslrEnabled)
+ {
+ AliasRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ HeapRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ StackRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ TlsIoRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ }
+
+ //Regions are sorted based on ASLR offset.
+ //When ASLR is disabled, the order is Map, Heap, NewMap and TlsIo.
+ AliasRegion.Start = MapBaseAddress + AliasRegion.AslrOffset;
+ AliasRegion.End = AliasRegion.Start + AliasRegion.Size;
+ HeapRegion.Start = MapBaseAddress + HeapRegion.AslrOffset;
+ HeapRegion.End = HeapRegion.Start + HeapRegion.Size;
+ StackRegion.Start = MapBaseAddress + StackRegion.AslrOffset;
+ StackRegion.End = StackRegion.Start + StackRegion.Size;
+ TlsIoRegion.Start = MapBaseAddress + TlsIoRegion.AslrOffset;
+ TlsIoRegion.End = TlsIoRegion.Start + TlsIoRegion.Size;
- long AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
+ SortRegion(HeapRegion, AliasRegion);
+
+ if (StackRegion.Size != 0)
+ {
+ SortRegion(StackRegion, AliasRegion);
+ SortRegion(StackRegion, HeapRegion);
+ }
+ else
+ {
+ StackRegion.Start = StackAndTlsIoStart;
+ StackRegion.End = StackAndTlsIoEnd;
+ }
+
+ if (TlsIoRegion.Size != 0)
+ {
+ SortRegion(TlsIoRegion, AliasRegion);
+ SortRegion(TlsIoRegion, HeapRegion);
+ SortRegion(TlsIoRegion, StackRegion);
+ }
+ else
+ {
+ TlsIoRegion.Start = StackAndTlsIoStart;
+ TlsIoRegion.End = StackAndTlsIoEnd;
+ }
+
+ AliasRegionStart = AliasRegion.Start;
+ AliasRegionEnd = AliasRegion.End;
+ HeapRegionStart = HeapRegion.Start;
+ HeapRegionEnd = HeapRegion.End;
+ StackRegionStart = StackRegion.Start;
+ StackRegionEnd = StackRegion.End;
+ TlsIoRegionStart = TlsIoRegion.Start;
+ TlsIoRegionEnd = TlsIoRegion.End;
+
+ CurrentHeapAddr = HeapRegionStart;
+ HeapCapacity = 0;
+ PhysicalMemoryUsage = 0;
+
+ this.MemRegion = MemRegion;
+ this.AslrDisabled = AslrDisabled;
+
+ return InitializeBlocks(AddrSpaceStart, AddrSpaceEnd);
+ }
+
+ private ulong GetRandomValue(ulong Min, ulong Max)
+ {
+ return (ulong)GetRandomValue((long)Min, (long)Max);
+ }
+
+ private long GetRandomValue(long Min, long Max)
+ {
+ if (RandomNumberGenerator == null)
+ {
+ RandomNumberGenerator = new MersenneTwister(0);
+ }
+
+ return RandomNumberGenerator.GenRandomNumber(Min, Max);
+ }
+
+ private static void SortRegion(Region Lhs, Region Rhs)
+ {
+ if (Lhs.AslrOffset < Rhs.AslrOffset)
+ {
+ Rhs.Start += Lhs.Size;
+ Rhs.End += Lhs.Size;
+ }
+ else
+ {
+ Lhs.Start += Rhs.Size;
+ Lhs.End += Rhs.Size;
+ }
+ }
+
+ private KernelResult InitializeBlocks(ulong AddrSpaceStart, ulong AddrSpaceEnd)
+ {
+ //First insertion will always need only a single block,
+ //because there's nothing else to split.
+ if (!BlockAllocator.CanAllocate(1))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
InsertBlock(AddrSpaceStart, AddrSpacePagesCount, MemoryState.Unmapped);
+
+ return KernelResult.Success;
}
- public void HleMapProcessCode(long Position, long Size)
+ public KernelResult MapPages(
+ ulong Address,
+ KPageList PageList,
+ MemoryState State,
+ MemoryPermission Permission)
{
- long PagesCount = Size / PageSize;
+ ulong PagesCount = PageList.GetPagesCount();
+
+ ulong Size = PagesCount * PageSize;
- if (!Allocator.TryAllocate(Size, out long PA))
+ if (!ValidateRegionForState(Address, Size, State))
{
- throw new InvalidOperationException();
+ return KernelResult.InvalidMemState;
}
lock (Blocks)
{
- InsertBlock(Position, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
+ if (!IsUnmapped(Address, PagesCount * PageSize))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ KernelResult Result = MapPages(Address, PageList, Permission);
+
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, State, Permission);
+ }
- CpuMemory.Map(Position, PA, Size);
+ return Result;
}
}
- public long MapProcessCodeMemory(long Dst, long Src, long Size)
+ public KernelResult UnmapPages(ulong Address, KPageList PageList, MemoryState StateExpected)
{
+ ulong PagesCount = PageList.GetPagesCount();
+
+ ulong Size = PagesCount * PageSize;
+
+ ulong EndAddr = Address + Size;
+
+ ulong AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
+
+ if (AddrSpaceStart > Address)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (AddrSpacePagesCount < PagesCount)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (EndAddr - 1 > AddrSpaceEnd - 1)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
lock (Blocks)
{
- long PagesCount = Size / PageSize;
+ KPageList CurrentPageList = new KPageList();
- bool Success = IsUnmapped(Dst, Size);
+ AddVaRangeToPageList(CurrentPageList, Address, PagesCount);
- Success &= CheckRange(
- Src,
- Size,
- MemoryState.Mask,
- MemoryState.Heap,
- MemoryPermission.Mask,
- MemoryPermission.ReadAndWrite,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
+ if (!CurrentPageList.IsEqual(PageList))
+ {
+ return KernelResult.InvalidMemRange;
+ }
- if (Success)
+ if (CheckRange(
+ Address,
+ Size,
+ MemoryState.Mask,
+ StateExpected,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out MemoryState State,
+ out _,
+ out _))
{
- long PA = CpuMemory.GetPhysicalAddress(Src);
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
- InsertBlock(Dst, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
- InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.None);
+ KernelResult Result = MmuUnmap(Address, PagesCount);
- CpuMemory.Map(Dst, PA, Size);
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+ }
- return 0;
+ return Result;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
+ }
+
+ public KernelResult MapNormalMemory(long Address, long Size, MemoryPermission Permission)
+ {
+ //TODO.
+ return KernelResult.Success;
+ }
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ public KernelResult MapIoMemory(long Address, long Size, MemoryPermission Permission)
+ {
+ //TODO.
+ return KernelResult.Success;
}
- public long UnmapProcessCodeMemory(long Dst, long Src, long Size)
+ public KernelResult AllocateOrMapPa(
+ ulong NeededPagesCount,
+ int Alignment,
+ ulong SrcPa,
+ bool Map,
+ ulong RegionStart,
+ ulong RegionPagesCount,
+ MemoryState State,
+ MemoryPermission Permission,
+ out ulong Address)
{
+ Address = 0;
+
+ ulong RegionSize = RegionPagesCount * PageSize;
+
+ ulong RegionEndAddr = RegionStart + RegionSize;
+
+ if (!ValidateRegionForState(RegionStart, RegionSize, State))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (RegionPagesCount <= NeededPagesCount)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ ulong ReservedPagesCount = IsKernel ? 1UL : 4UL;
+
lock (Blocks)
{
- long PagesCount = Size / PageSize;
+ if (AslrEnabled)
+ {
+ ulong TotalNeededSize = (ReservedPagesCount + NeededPagesCount) * PageSize;
- bool Success = CheckRange(
- Dst,
- Size,
- MemoryState.Mask,
- MemoryState.CodeStatic,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
+ ulong RemainingPages = RegionPagesCount - NeededPagesCount;
- Success &= CheckRange(
- Src,
- Size,
- MemoryState.Mask,
- MemoryState.Heap,
- MemoryPermission.Mask,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
+ ulong AslrMaxOffset = ((RemainingPages + ReservedPagesCount) * PageSize) / (ulong)Alignment;
- if (Success)
+ for (int Attempt = 0; Attempt < 8; Attempt++)
+ {
+ Address = BitUtils.AlignDown(RegionStart + GetRandomValue(0, AslrMaxOffset) * (ulong)Alignment, Alignment);
+
+ ulong EndAddr = Address + TotalNeededSize;
+
+ KMemoryInfo Info = FindBlock(Address).GetInfo();
+
+ if (Info.State != MemoryState.Unmapped)
+ {
+ continue;
+ }
+
+ ulong CurrBaseAddr = Info.Address + ReservedPagesCount * PageSize;
+ ulong CurrEndAddr = Info.Address + Info.Size;
+
+ if (Address >= RegionStart &&
+ Address >= CurrBaseAddr &&
+ EndAddr - 1 <= RegionEndAddr - 1 &&
+ EndAddr - 1 <= CurrEndAddr - 1)
+ {
+ break;
+ }
+ }
+
+ if (Address == 0)
+ {
+ ulong AslrPage = GetRandomValue(0, AslrMaxOffset);
+
+ Address = FindFirstFit(
+ RegionStart + AslrPage * PageSize,
+ RegionPagesCount - AslrPage,
+ NeededPagesCount,
+ Alignment,
+ 0,
+ ReservedPagesCount);
+ }
+ }
+
+ if (Address == 0)
{
- InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
- InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+ Address = FindFirstFit(
+ RegionStart,
+ RegionPagesCount,
+ NeededPagesCount,
+ Alignment,
+ 0,
+ ReservedPagesCount);
+ }
- CpuMemory.Unmap(Dst, Size);
+ if (Address == 0)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ MemoryOperation Operation = Map
+ ? MemoryOperation.MapPa
+ : MemoryOperation.Allocate;
- return 0;
+ KernelResult Result = DoMmuOperation(
+ Address,
+ NeededPagesCount,
+ SrcPa,
+ Map,
+ Permission,
+ Operation);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
}
+
+ InsertBlock(Address, NeededPagesCount, State, Permission);
}
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ return KernelResult.Success;
}
- public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission)
+ public KernelResult MapNewProcessCode(
+ ulong Address,
+ ulong PagesCount,
+ MemoryState State,
+ MemoryPermission Permission)
{
- long PagesCount = Size / PageSize;
+ ulong Size = PagesCount * PageSize;
- if (!Allocator.TryAllocate(Size, out long PA))
+ if (!ValidateRegionForState(Address, Size, State))
{
- throw new InvalidOperationException();
+ return KernelResult.InvalidMemState;
}
lock (Blocks)
{
- InsertBlock(Position, PagesCount, State, Permission);
+ if (!IsUnmapped(Address, Size))
+ {
+ return KernelResult.InvalidMemState;
+ }
- CpuMemory.Map(Position, PA, Size);
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ KernelResult Result = DoMmuOperation(
+ Address,
+ PagesCount,
+ 0,
+ false,
+ Permission,
+ MemoryOperation.Allocate);
+
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, State, Permission);
+ }
+
+ return Result;
}
}
- public long HleMapTlsPage()
+ public KernelResult MapProcessCodeMemory(ulong Dst, ulong Src, ulong Size)
{
- bool HasTlsIoRegion = TlsIoRegionStart != TlsIoRegionEnd;
-
- long Position = HasTlsIoRegion ? TlsIoRegionStart : CodeRegionStart;
+ ulong PagesCount = Size / PageSize;
lock (Blocks)
{
- while (Position < (HasTlsIoRegion ? TlsIoRegionEnd : CodeRegionEnd))
+ bool Success = CheckRange(
+ Src,
+ Size,
+ MemoryState.Mask,
+ MemoryState.Heap,
+ MemoryPermission.Mask,
+ MemoryPermission.ReadAndWrite,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out MemoryState State,
+ out MemoryPermission Permission,
+ out _);
+
+ Success &= IsUnmapped(Dst, Size);
+
+ if (Success)
{
- if (FindBlock(Position).State == MemoryState.Unmapped)
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
{
- InsertBlock(Position, 1, MemoryState.ThreadLocal, MemoryPermission.ReadAndWrite);
+ return KernelResult.OutOfResource;
+ }
- if (!Allocator.TryAllocate(PageSize, out long PA))
- {
- throw new InvalidOperationException();
- }
+ KPageList PageList = new KPageList();
+
+ AddVaRangeToPageList(PageList, Src, PagesCount);
- CpuMemory.Map(Position, PA, PageSize);
+ KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Result = MapPages(Dst, PageList, MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ MmuChangePermission(Src, PagesCount, Permission);
- return Position;
+ return Result;
}
- Position += PageSize;
+ InsertBlock(Src, PagesCount, State, MemoryPermission.None, MemoryAttribute.Borrowed);
+ InsertBlock(Dst, PagesCount, MemoryState.ModCodeStatic);
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
+ }
+ }
+
+ public KernelResult UnmapProcessCodeMemory(ulong Dst, ulong Src, ulong Size)
+ {
+ ulong PagesCount = Size / PageSize;
+
+ lock (Blocks)
+ {
+ bool Success = CheckRange(
+ Src,
+ Size,
+ MemoryState.Mask,
+ MemoryState.Heap,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.Borrowed,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out _,
+ out _,
+ out _);
- throw new InvalidOperationException();
+ Success &= CheckRange(
+ Dst,
+ PageSize,
+ MemoryState.UnmapProcessCodeMemoryAllowed,
+ MemoryState.UnmapProcessCodeMemoryAllowed,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out MemoryState State,
+ out _,
+ out _);
+
+ Success &= CheckRange(
+ Dst,
+ Size,
+ MemoryState.Mask,
+ State,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None);
+
+ if (Success)
+ {
+ KernelResult Result = MmuUnmap(Dst, PagesCount);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ //TODO: Missing some checks here.
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
+ InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
+ }
}
}
- public long TrySetHeapSize(long Size, out long Position)
+ public KernelResult SetHeapSize(ulong Size, out ulong Address)
{
- Position = 0;
+ Address = 0;
- if ((ulong)Size > (ulong)(HeapRegionEnd - HeapRegionStart))
+ if (Size > HeapRegionEnd - HeapRegionStart)
{
- return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+ return KernelResult.OutOfMemory;
}
- bool Success = false;
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- long CurrentHeapSize = GetHeapSize();
+ ulong CurrentHeapSize = GetHeapSize();
- if ((ulong)CurrentHeapSize <= (ulong)Size)
+ if (CurrentHeapSize <= Size)
{
//Expand.
- long DiffSize = Size - CurrentHeapSize;
+ ulong DiffSize = Size - CurrentHeapSize;
lock (Blocks)
{
- if (Success = IsUnmapped(CurrentHeapAddr, DiffSize))
+ if (CurrentProcess.ResourceLimit != null && DiffSize != 0 &&
+ !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, DiffSize))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+
+ ulong PagesCount = DiffSize / PageSize;
+
+ KMemoryRegionManager Region = GetMemoryRegionManager();
+
+ KernelResult Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList);
+
+ void CleanUpForError()
{
- if (!Allocator.TryAllocate(DiffSize, out long PA))
+ if (PageList != null)
+ {
+ Region.FreePages(PageList);
+ }
+
+ if (CurrentProcess.ResourceLimit != null && DiffSize != 0)
{
- return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+ CurrentProcess.ResourceLimit.Release(LimitableResource.Memory, DiffSize);
}
+ }
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
- long PagesCount = DiffSize / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ CleanUpForError();
- InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+ return KernelResult.OutOfResource;
+ }
- CpuMemory.Map(CurrentHeapAddr, PA, DiffSize);
+ if (!IsUnmapped(CurrentHeapAddr, DiffSize))
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidMemState;
}
+
+ Result = DoMmuOperation(
+ CurrentHeapAddr,
+ PagesCount,
+ PageList,
+ MemoryPermission.ReadAndWrite,
+ MemoryOperation.MapVa);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
}
}
else
{
//Shrink.
- long FreeAddr = HeapRegionStart + Size;
- long DiffSize = CurrentHeapSize - Size;
+ ulong FreeAddr = HeapRegionStart + Size;
+ ulong DiffSize = CurrentHeapSize - Size;
lock (Blocks)
{
- Success = CheckRange(
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ if (!CheckRange(
FreeAddr,
DiffSize,
MemoryState.Mask,
@@ -337,48 +864,66 @@ namespace Ryujinx.HLE.HOS.Kernel
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
- out _);
-
- if (Success)
+ out _))
{
- long PagesCount = DiffSize / PageSize;
+ return KernelResult.InvalidMemState;
+ }
- InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped);
+ ulong PagesCount = DiffSize / PageSize;
- FreePages(FreeAddr, PagesCount);
+ KernelResult Result = MmuUnmap(FreeAddr, PagesCount);
- CpuMemory.Unmap(FreeAddr, DiffSize);
+ if (Result != KernelResult.Success)
+ {
+ return Result;
}
+
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, BitUtils.AlignDown(DiffSize, PageSize));
+
+ InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped);
}
}
CurrentHeapAddr = HeapRegionStart + Size;
- if (Success)
- {
- Position = HeapRegionStart;
+ Address = HeapRegionStart;
- return 0;
- }
+ return KernelResult.Success;
+ }
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ public ulong GetTotalHeapSize()
+ {
+ lock (Blocks)
+ {
+ return GetHeapSize() + PhysicalMemoryUsage;
+ }
}
- public long GetHeapSize()
+ private ulong GetHeapSize()
{
return CurrentHeapAddr - HeapRegionStart;
}
- public long SetMemoryAttribute(
- long Position,
- long Size,
+ public KernelResult SetHeapCapacity(ulong Capacity)
+ {
+ lock (Blocks)
+ {
+ HeapCapacity = Capacity;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult SetMemoryAttribute(
+ ulong Address,
+ ulong Size,
MemoryAttribute AttributeMask,
MemoryAttribute AttributeValue)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.AttributeChangeAllowed,
MemoryState.AttributeChangeAllowed,
@@ -391,35 +936,42 @@ namespace Ryujinx.HLE.HOS.Kernel
out MemoryPermission Permission,
out MemoryAttribute Attribute))
{
- long PagesCount = Size / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong PagesCount = Size / PageSize;
Attribute &= ~AttributeMask;
Attribute |= AttributeMask & AttributeValue;
- InsertBlock(Position, PagesCount, State, Permission, Attribute);
+ InsertBlock(Address, PagesCount, State, Permission, Attribute);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public KMemoryInfo QueryMemory(long Position)
+ public KMemoryInfo QueryMemory(ulong Address)
{
- if ((ulong)Position >= (ulong)AddrSpaceStart &&
- (ulong)Position < (ulong)AddrSpaceEnd)
+ if (Address >= AddrSpaceStart &&
+ Address < AddrSpaceEnd)
{
lock (Blocks)
{
- return FindBlock(Position).GetInfo();
+ return FindBlock(Address).GetInfo();
}
}
else
{
return new KMemoryInfo(
AddrSpaceEnd,
- -AddrSpaceEnd,
+ ~AddrSpaceEnd + 1,
MemoryState.Reserved,
MemoryPermission.None,
MemoryAttribute.None,
@@ -428,7 +980,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- public long Map(long Src, long Dst, long Size)
+ public KernelResult Map(ulong Dst, ulong Src, ulong Size)
{
bool Success;
@@ -452,22 +1004,90 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Success)
{
- long PagesCount = Size / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+ {
+ return KernelResult.OutOfResource;
+ }
- InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed);
+ ulong PagesCount = Size / PageSize;
- InsertBlock(Dst, PagesCount, MemoryState.MappedMemory, MemoryPermission.ReadAndWrite);
+ KPageList PageList = new KPageList();
- long PA = CpuMemory.GetPhysicalAddress(Src);
+ AddVaRangeToPageList(PageList, Src, PagesCount);
- CpuMemory.Map(Dst, PA, Size);
+ KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Result = MapPages(Dst, PageList, MemoryPermission.ReadAndWrite);
+
+ if (Result != KernelResult.Success)
+ {
+ if (MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure reverting memory permission.");
+ }
+
+ return Result;
+ }
+
+ InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed);
+ InsertBlock(Dst, PagesCount, MemoryState.Stack, MemoryPermission.ReadAndWrite);
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
+ }
+
+ public KernelResult UnmapForKernel(ulong Address, ulong PagesCount, MemoryState StateExpected)
+ {
+ ulong Size = PagesCount * PageSize;
+
+ lock (Blocks)
+ {
+ if (CheckRange(
+ Address,
+ Size,
+ MemoryState.Mask,
+ StateExpected,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out _,
+ out _,
+ out _))
+ {
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ KernelResult Result = MmuUnmap(Address, PagesCount);
- return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+ }
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
+ }
+ }
}
- public long Unmap(long Src, long Dst, long Size)
+ public KernelResult Unmap(ulong Dst, ulong Src, ulong Size)
{
bool Success;
@@ -491,87 +1111,70 @@ namespace Ryujinx.HLE.HOS.Kernel
Dst,
Size,
MemoryState.Mask,
- MemoryState.MappedMemory,
+ MemoryState.Stack,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
- out _,
+ out MemoryPermission DstPermission,
out _);
if (Success)
{
- long PagesCount = Size / PageSize;
-
- InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite);
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+ {
+ return KernelResult.OutOfResource;
+ }
- InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
+ ulong PagesCount = Size / PageSize;
- CpuMemory.Unmap(Dst, Size);
- }
- }
+ KPageList SrcPageList = new KPageList();
+ KPageList DstPageList = new KPageList();
- return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
- }
+ AddVaRangeToPageList(SrcPageList, Src, PagesCount);
+ AddVaRangeToPageList(DstPageList, Dst, PagesCount);
- public long MapSharedMemory(KSharedMemory SharedMemory, MemoryPermission Permission, long Position)
- {
- lock (Blocks)
- {
- if (IsUnmapped(Position, SharedMemory.Size))
- {
- long PagesCount = SharedMemory.Size / PageSize;
-
- InsertBlock(Position, PagesCount, MemoryState.SharedMemory, Permission);
+ if (!DstPageList.IsEqual(SrcPageList))
+ {
+ return KernelResult.InvalidMemRange;
+ }
- CpuMemory.Map(Position, SharedMemory.PA, SharedMemory.Size);
+ KernelResult Result = MmuUnmap(Dst, PagesCount);
- return 0;
- }
- }
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
- }
+ Result = MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite);
- public long UnmapSharedMemory(long Position, long Size)
- {
- lock (Blocks)
- {
- if (CheckRange(
- Position,
- Size,
- MemoryState.Mask,
- MemoryState.SharedMemory,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out _,
- out _))
- {
- long PagesCount = Size / PageSize;
+ if (Result != KernelResult.Success)
+ {
+ MapPages(Dst, DstPageList, DstPermission);
- InsertBlock(Position, PagesCount, MemoryState.Unmapped);
+ return Result;
+ }
- CpuMemory.Unmap(Position, Size);
+ InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite);
+ InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long ReserveTransferMemory(long Position, long Size, MemoryPermission Permission)
+ public KernelResult ReserveTransferMemory(ulong Address, ulong Size, MemoryPermission Permission)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
@@ -584,25 +1187,34 @@ namespace Ryujinx.HLE.HOS.Kernel
out _,
out MemoryAttribute Attribute))
{
- long PagesCount = Size / PageSize;
+ //TODO: Missing checks.
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong PagesCount = Size / PageSize;
Attribute |= MemoryAttribute.Borrowed;
- InsertBlock(Position, PagesCount, State, Permission, Attribute);
+ InsertBlock(Address, PagesCount, State, Permission, Attribute);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long ResetTransferMemory(long Position, long Size)
+ public KernelResult ResetTransferMemory(ulong Address, ulong Size)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
@@ -615,23 +1227,30 @@ namespace Ryujinx.HLE.HOS.Kernel
out _,
out _))
{
- long PagesCount = Size / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong PagesCount = Size / PageSize;
- InsertBlock(Position, PagesCount, State, MemoryPermission.ReadAndWrite);
+ InsertBlock(Address, PagesCount, State, MemoryPermission.ReadAndWrite);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long SetProcessMemoryPermission(long Position, long Size, MemoryPermission Permission)
+ public KernelResult SetProcessMemoryPermission(ulong Address, ulong Size, MemoryPermission Permission)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.ProcessPermissionChangeAllowed,
MemoryState.ProcessPermissionChangeAllowed,
@@ -640,47 +1259,73 @@ namespace Ryujinx.HLE.HOS.Kernel
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out _,
+ out MemoryState OldState,
+ out MemoryPermission OldPermission,
out _))
{
- if (State == MemoryState.CodeStatic)
- {
- State = MemoryState.CodeMutable;
- }
- else if (State == MemoryState.ModCodeStatic)
+ MemoryState NewState = OldState;
+
+ //If writing into the code region is allowed, then we need
+ //to change it to mutable.
+ if ((Permission & MemoryPermission.Write) != 0)
{
- State = MemoryState.ModCodeMutable;
+ if (OldState == MemoryState.CodeStatic)
+ {
+ NewState = MemoryState.CodeMutable;
+ }
+ else if (OldState == MemoryState.ModCodeStatic)
+ {
+ NewState = MemoryState.ModCodeMutable;
+ }
+ else
+ {
+ throw new InvalidOperationException($"Memory state \"{OldState}\" not valid for this operation.");
+ }
}
- else
+
+ if (NewState != OldState || Permission != OldPermission)
{
- throw new InvalidOperationException();
- }
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
- long PagesCount = Size / PageSize;
+ ulong PagesCount = Size / PageSize;
- InsertBlock(Position, PagesCount, State, Permission);
+ MemoryOperation Operation = (Permission & MemoryPermission.Execute) != 0
+ ? MemoryOperation.ChangePermsAndAttributes
+ : MemoryOperation.ChangePermRw;
- return 0;
+ KernelResult Result = DoMmuOperation(Address, PagesCount, 0, false, Permission, Operation);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ InsertBlock(Address, PagesCount, NewState, Permission);
+ }
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long MapPhysicalMemory(long Position, long Size)
+ public KernelResult MapPhysicalMemory(ulong Address, ulong Size)
{
- long End = Position + Size;
+ ulong EndAddr = Address + Size;
lock (Blocks)
{
- long MappedSize = 0;
+ ulong MappedSize = 0;
KMemoryInfo Info;
- LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Position);
-
- LinkedListNode<KMemoryBlock> Node = BaseNode;
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
do
{
@@ -688,57 +1333,66 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Info.State != MemoryState.Unmapped)
{
- MappedSize += GetSizeInRange(Info, Position, End);
+ MappedSize += GetSizeInRange(Info, Address, EndAddr);
}
Node = Node.Next;
}
- while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
+ while (Info.Address + Info.Size < EndAddr && Node != null);
if (MappedSize == Size)
{
- return 0;
+ return KernelResult.Success;
}
- long RemainingSize = Size - MappedSize;
+ ulong RemainingSize = Size - MappedSize;
+
+ ulong RemainingPages = RemainingSize / PageSize;
- if (!Allocator.TryAllocate(RemainingSize, out long PA))
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.ResourceLimit != null &&
+ !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, RemainingSize))
{
- return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+ return KernelResult.ResLimitExceeded;
}
- Node = BaseNode;
+ KMemoryRegionManager Region = GetMemoryRegionManager();
- do
- {
- Info = Node.Value.GetInfo();
+ KernelResult Result = Region.AllocatePages(RemainingPages, AslrDisabled, out KPageList PageList);
- if (Info.State == MemoryState.Unmapped)
+ void CleanUpForError()
+ {
+ if (PageList != null)
{
- long CurrSize = GetSizeInRange(Info, Position, End);
+ Region.FreePages(PageList);
+ }
- long MapPosition = Info.Position;
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, RemainingSize);
+ }
- if ((ulong)MapPosition < (ulong)Position)
- {
- MapPosition = Position;
- }
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
- CpuMemory.Map(MapPosition, PA, CurrSize);
+ return Result;
+ }
- PA += CurrSize;
- }
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ CleanUpForError();
- Node = Node.Next;
+ return KernelResult.OutOfResource;
}
- while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
- PersonalMmHeapUsage += RemainingSize;
+ MapPhysicalMemory(PageList, Address, EndAddr);
- long PagesCount = Size / PageSize;
+ PhysicalMemoryUsage += RemainingSize;
+
+ ulong PagesCount = Size / PageSize;
InsertBlock(
- Position,
+ Address,
PagesCount,
MemoryState.Unmapped,
MemoryPermission.None,
@@ -748,22 +1402,26 @@ namespace Ryujinx.HLE.HOS.Kernel
MemoryAttribute.None);
}
- return 0;
+ return KernelResult.Success;
}
- public long UnmapPhysicalMemory(long Position, long Size)
+ public KernelResult UnmapPhysicalMemory(ulong Address, ulong Size)
{
- long End = Position + Size;
+ ulong EndAddr = Address + Size;
lock (Blocks)
{
- long HeapMappedSize = 0;
+ //Scan, ensure that the region can be unmapped (all blocks are heap or
+ //already unmapped), fill pages list for freeing memory.
+ ulong HeapMappedSize = 0;
- long CurrPosition = Position;
+ KPageList PageList = new KPageList();
KMemoryInfo Info;
- LinkedListNode<KMemoryBlock> Node = FindBlockNode(CurrPosition);
+ LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Address);
+
+ LinkedListNode<KMemoryBlock> Node = BaseNode;
do
{
@@ -773,90 +1431,200 @@ namespace Ryujinx.HLE.HOS.Kernel
{
if (Info.Attribute != MemoryAttribute.None)
{
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ return KernelResult.InvalidMemState;
}
- HeapMappedSize += GetSizeInRange(Info, Position, End);
+ ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
+ ulong BlockAddress = GetAddrInRange(Info, Address);
+
+ AddVaRangeToPageList(PageList, BlockAddress, BlockSize / PageSize);
+
+ HeapMappedSize += BlockSize;
}
else if (Info.State != MemoryState.Unmapped)
{
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ return KernelResult.InvalidMemState;
}
Node = Node.Next;
}
- while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
+ while (Info.Address + Info.Size < EndAddr && Node != null);
if (HeapMappedSize == 0)
{
- return 0;
+ return KernelResult.Success;
+ }
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
}
- PersonalMmHeapUsage -= HeapMappedSize;
+ //Try to unmap all the heap mapped memory inside range.
+ KernelResult Result = KernelResult.Success;
- long PagesCount = Size / PageSize;
+ Node = BaseNode;
- InsertBlock(Position, PagesCount, MemoryState.Unmapped);
+ do
+ {
+ Info = Node.Value.GetInfo();
+
+ if (Info.State == MemoryState.Heap)
+ {
+ ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
+ ulong BlockAddress = GetAddrInRange(Info, Address);
- FreePages(Position, PagesCount);
+ ulong BlockPagesCount = BlockSize / PageSize;
- CpuMemory.Unmap(Position, Size);
+ Result = MmuUnmap(BlockAddress, BlockPagesCount);
- return 0;
+ if (Result != KernelResult.Success)
+ {
+ //If we failed to unmap, we need to remap everything back again.
+ MapPhysicalMemory(PageList, Address, BlockAddress + BlockSize);
+
+ break;
+ }
+ }
+
+ Node = Node.Next;
+ }
+ while (Info.Address + Info.Size < EndAddr && Node != null);
+
+ if (Result == KernelResult.Success)
+ {
+ GetMemoryRegionManager().FreePages(PageList);
+
+ PhysicalMemoryUsage -= HeapMappedSize;
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, HeapMappedSize);
+
+ ulong PagesCount = Size / PageSize;
+
+ InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+ }
+
+ return Result;
}
}
- private long GetSizeInRange(KMemoryInfo Info, long Start, long End)
+ private void MapPhysicalMemory(KPageList PageList, ulong Address, ulong EndAddr)
{
- long CurrEnd = Info.Size + Info.Position;
- long CurrSize = Info.Size;
+ KMemoryInfo Info;
- if ((ulong)Info.Position < (ulong)Start)
- {
- CurrSize -= Start - Info.Position;
- }
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
- if ((ulong)CurrEnd > (ulong)End)
+ LinkedListNode<KPageNode> PageListNode = PageList.Nodes.First;
+
+ KPageNode PageNode = PageListNode.Value;
+
+ ulong SrcPa = PageNode.Address;
+ ulong SrcPaPages = PageNode.PagesCount;
+
+ do
{
- CurrSize -= CurrEnd - End;
- }
+ Info = Node.Value.GetInfo();
+
+ if (Info.State == MemoryState.Unmapped)
+ {
+ ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
+
+ ulong DstVaPages = BlockSize / PageSize;
+
+ ulong DstVa = GetAddrInRange(Info, Address);
+
+ while (DstVaPages > 0)
+ {
+ if (SrcPaPages == 0)
+ {
+ PageListNode = PageListNode.Next;
+
+ PageNode = PageListNode.Value;
+
+ SrcPa = PageNode.Address;
+ SrcPaPages = PageNode.PagesCount;
+ }
+
+ ulong PagesCount = SrcPaPages;
+
+ if (PagesCount > DstVaPages)
+ {
+ PagesCount = DstVaPages;
+ }
+
+ DoMmuOperation(
+ DstVa,
+ PagesCount,
+ SrcPa,
+ true,
+ MemoryPermission.ReadAndWrite,
+ MemoryOperation.MapPa);
+
+ DstVa += PagesCount * PageSize;
+ SrcPa += PagesCount * PageSize;
+ SrcPaPages -= PagesCount;
+ DstVaPages -= PagesCount;
+ }
+ }
- return CurrSize;
+ Node = Node.Next;
+ }
+ while (Info.Address + Info.Size < EndAddr && Node != null);
}
- private void FreePages(long Position, long PagesCount)
+ private static ulong GetSizeInRange(KMemoryInfo Info, ulong Start, ulong End)
{
- for (long Page = 0; Page < PagesCount; Page++)
+ ulong EndAddr = Info.Size + Info.Address;
+ ulong Size = Info.Size;
+
+ if (Info.Address < Start)
{
- long VA = Position + Page * PageSize;
+ Size -= Start - Info.Address;
+ }
- if (!CpuMemory.IsMapped(VA))
- {
- continue;
- }
+ if (EndAddr > End)
+ {
+ Size -= EndAddr - End;
+ }
- long PA = CpuMemory.GetPhysicalAddress(VA);
+ return Size;
+ }
- Allocator.Free(PA, PageSize);
+ private static ulong GetAddrInRange(KMemoryInfo Info, ulong Start)
+ {
+ if (Info.Address < Start)
+ {
+ return Start;
}
+
+ return Info.Address;
}
- public bool HleIsUnmapped(long Position, long Size)
+ private void AddVaRangeToPageList(KPageList PageList, ulong Start, ulong PagesCount)
{
- bool Result = false;
+ ulong Address = Start;
- lock (Blocks)
+ while (Address < Start + PagesCount * PageSize)
{
- Result = IsUnmapped(Position, Size);
- }
+ KernelResult Result = ConvertVaToPa(Address, out ulong Pa);
- return Result;
+ if (Result != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure translating virtual address.");
+ }
+
+ PageList.AddRange(Pa, 1);
+
+ Address += PageSize;
+ }
}
- private bool IsUnmapped(long Position, long Size)
+ private bool IsUnmapped(ulong Address, ulong Size)
{
return CheckRange(
- Position,
+ Address,
Size,
MemoryState.Mask,
MemoryState.Unmapped,
@@ -871,8 +1639,8 @@ namespace Ryujinx.HLE.HOS.Kernel
}
private bool CheckRange(
- long Position,
- long Size,
+ ulong Address,
+ ulong Size,
MemoryState StateMask,
MemoryState StateExpected,
MemoryPermission PermissionMask,
@@ -884,24 +1652,44 @@ namespace Ryujinx.HLE.HOS.Kernel
out MemoryPermission OutPermission,
out MemoryAttribute OutAttribute)
{
- KMemoryInfo BlkInfo = FindBlock(Position).GetInfo();
+ ulong EndAddr = Address + Size - 1;
+
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
+
+ KMemoryInfo Info = Node.Value.GetInfo();
- ulong Start = (ulong)Position;
- ulong End = (ulong)Size + Start;
+ MemoryState FirstState = Info.State;
+ MemoryPermission FirstPermission = Info.Permission;
+ MemoryAttribute FirstAttribute = Info.Attribute;
- if (End <= (ulong)(BlkInfo.Position + BlkInfo.Size))
+ do
{
- if ((BlkInfo.Attribute & AttributeMask) == AttributeExpected &&
- (BlkInfo.State & StateMask) == StateExpected &&
- (BlkInfo.Permission & PermissionMask) == PermissionExpected)
+ Info = Node.Value.GetInfo();
+
+ //Check if the block state matches what we expect.
+ if ( FirstState != Info.State ||
+ FirstPermission != Info.Permission ||
+ (Info.Attribute & AttributeMask) != AttributeExpected ||
+ (FirstAttribute | AttributeIgnoreMask) != (Info.Attribute | AttributeIgnoreMask) ||
+ (FirstState & StateMask) != StateExpected ||
+ (FirstPermission & PermissionMask) != PermissionExpected)
+ {
+ break;
+ }
+
+ //Check if this is the last block on the range, if so return success.
+ if (EndAddr <= Info.Address + Info.Size - 1)
{
- OutState = BlkInfo.State;
- OutPermission = BlkInfo.Permission;
- OutAttribute = BlkInfo.Attribute & ~AttributeIgnoreMask;
+ OutState = FirstState;
+ OutPermission = FirstPermission;
+ OutAttribute = FirstAttribute & ~AttributeIgnoreMask;
return true;
}
+
+ Node = Node.Next;
}
+ while (Node != null);
OutState = MemoryState.Unmapped;
OutPermission = MemoryPermission.None;
@@ -910,9 +1698,48 @@ namespace Ryujinx.HLE.HOS.Kernel
return false;
}
+ private bool CheckRange(
+ ulong Address,
+ ulong Size,
+ MemoryState StateMask,
+ MemoryState StateExpected,
+ MemoryPermission PermissionMask,
+ MemoryPermission PermissionExpected,
+ MemoryAttribute AttributeMask,
+ MemoryAttribute AttributeExpected)
+ {
+ ulong EndAddr = Address + Size - 1;
+
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
+
+ do
+ {
+ KMemoryInfo Info = Node.Value.GetInfo();
+
+ //Check if the block state matches what we expect.
+ if ((Info.State & StateMask) != StateExpected ||
+ (Info.Permission & PermissionMask) != PermissionExpected ||
+ (Info.Attribute & AttributeMask) != AttributeExpected)
+ {
+ break;
+ }
+
+ //Check if this is the last block on the range, if so return success.
+ if (EndAddr <= Info.Address + Info.Size - 1)
+ {
+ return true;
+ }
+
+ Node = Node.Next;
+ }
+ while (Node != null);
+
+ return false;
+ }
+
private void InsertBlock(
- long BasePosition,
- long PagesCount,
+ ulong BaseAddress,
+ ulong PagesCount,
MemoryState OldState,
MemoryPermission OldPermission,
MemoryAttribute OldAttribute,
@@ -923,10 +1750,11 @@ namespace Ryujinx.HLE.HOS.Kernel
//Insert new block on the list only on areas where the state
//of the block matches the state specified on the Old* state
//arguments, otherwise leave it as is.
+ int OldCount = Blocks.Count;
+
OldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
- ulong Start = (ulong)BasePosition;
- ulong End = (ulong)PagesCount * PageSize + Start;
+ ulong EndAddr = PagesCount * PageSize + BaseAddress;
LinkedListNode<KMemoryBlock> Node = Blocks.First;
@@ -937,10 +1765,10 @@ namespace Ryujinx.HLE.HOS.Kernel
KMemoryBlock CurrBlock = Node.Value;
- ulong CurrStart = (ulong)CurrBlock.BasePosition;
- ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
+ ulong CurrBaseAddr = CurrBlock.BaseAddress;
+ ulong CurrEndAddr = CurrBlock.PagesCount * PageSize + CurrBaseAddr;
- if (Start < CurrEnd && CurrStart < End)
+ if (BaseAddress < CurrEndAddr && CurrBaseAddr < EndAddr)
{
MemoryAttribute CurrBlockAttr = CurrBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped;
@@ -953,36 +1781,36 @@ namespace Ryujinx.HLE.HOS.Kernel
continue;
}
- if (CurrStart >= Start && CurrEnd <= End)
+ if (CurrBaseAddr >= BaseAddress && CurrEndAddr <= EndAddr)
{
CurrBlock.State = NewState;
CurrBlock.Permission = NewPermission;
CurrBlock.Attribute &= ~MemoryAttribute.IpcAndDeviceMapped;
CurrBlock.Attribute |= NewAttribute;
}
- else if (CurrStart >= Start)
+ else if (CurrBaseAddr >= BaseAddress)
{
- CurrBlock.BasePosition = (long)End;
+ CurrBlock.BaseAddress = EndAddr;
- CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
+ CurrBlock.PagesCount = (CurrEndAddr - EndAddr) / PageSize;
- long NewPagesCount = (long)((End - CurrStart) / PageSize);
+ ulong NewPagesCount = (EndAddr - CurrBaseAddr) / PageSize;
NewNode = Blocks.AddBefore(Node, new KMemoryBlock(
- (long)CurrStart,
+ CurrBaseAddr,
NewPagesCount,
NewState,
NewPermission,
NewAttribute));
}
- else if (CurrEnd <= End)
+ else if (CurrEndAddr <= EndAddr)
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- long NewPagesCount = (long)((CurrEnd - Start) / PageSize);
+ ulong NewPagesCount = (CurrEndAddr - BaseAddress) / PageSize;
NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
- BasePosition,
+ BaseAddress,
NewPagesCount,
NewState,
NewPermission,
@@ -990,19 +1818,19 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- long NextPagesCount = (long)((CurrEnd - End) / PageSize);
+ ulong NextPagesCount = (CurrEndAddr - EndAddr) / PageSize;
NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
- BasePosition,
+ BaseAddress,
PagesCount,
NewState,
NewPermission,
NewAttribute));
Blocks.AddAfter(NewNode, new KMemoryBlock(
- (long)End,
+ EndAddr,
NextPagesCount,
CurrBlock.State,
CurrBlock.Permission,
@@ -1016,21 +1844,24 @@ namespace Ryujinx.HLE.HOS.Kernel
Node = NextNode;
}
+
+ BlockAllocator.Count += Blocks.Count - OldCount;
}
private void InsertBlock(
- long BasePosition,
- long PagesCount,
+ ulong BaseAddress,
+ ulong PagesCount,
MemoryState State,
MemoryPermission Permission = MemoryPermission.None,
MemoryAttribute Attribute = MemoryAttribute.None)
{
//Inserts new block at the list, replacing and spliting
//existing blocks as needed.
- KMemoryBlock Block = new KMemoryBlock(BasePosition, PagesCount, State, Permission, Attribute);
+ KMemoryBlock Block = new KMemoryBlock(BaseAddress, PagesCount, State, Permission, Attribute);
+
+ int OldCount = Blocks.Count;
- ulong Start = (ulong)BasePosition;
- ulong End = (ulong)PagesCount * PageSize + Start;
+ ulong EndAddr = PagesCount * PageSize + BaseAddress;
LinkedListNode<KMemoryBlock> NewNode = null;
@@ -1042,26 +1873,26 @@ namespace Ryujinx.HLE.HOS.Kernel
LinkedListNode<KMemoryBlock> NextNode = Node.Next;
- ulong CurrStart = (ulong)CurrBlock.BasePosition;
- ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
+ ulong CurrBaseAddr = CurrBlock.BaseAddress;
+ ulong CurrEndAddr = CurrBlock.PagesCount * PageSize + CurrBaseAddr;
- if (Start < CurrEnd && CurrStart < End)
+ if (BaseAddress < CurrEndAddr && CurrBaseAddr < EndAddr)
{
- if (Start >= CurrStart && End <= CurrEnd)
+ if (BaseAddress >= CurrBaseAddr && EndAddr <= CurrEndAddr)
{
Block.Attribute |= CurrBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped;
}
- if (Start > CurrStart && End < CurrEnd)
+ if (BaseAddress > CurrBaseAddr && EndAddr < CurrEndAddr)
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- long NextPagesCount = (long)((CurrEnd - End) / PageSize);
+ ulong NextPagesCount = (CurrEndAddr - EndAddr) / PageSize;
NewNode = Blocks.AddAfter(Node, Block);
Blocks.AddAfter(NewNode, new KMemoryBlock(
- (long)End,
+ EndAddr,
NextPagesCount,
CurrBlock.State,
CurrBlock.Permission,
@@ -1069,20 +1900,20 @@ namespace Ryujinx.HLE.HOS.Kernel
break;
}
- else if (Start <= CurrStart && End < CurrEnd)
+ else if (BaseAddress <= CurrBaseAddr && EndAddr < CurrEndAddr)
{
- CurrBlock.BasePosition = (long)End;
+ CurrBlock.BaseAddress = EndAddr;
- CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
+ CurrBlock.PagesCount = (CurrEndAddr - EndAddr) / PageSize;
if (NewNode == null)
{
NewNode = Blocks.AddBefore(Node, Block);
}
}
- else if (Start > CurrStart && End >= CurrEnd)
+ else if (BaseAddress > CurrBaseAddr && EndAddr >= CurrEndAddr)
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
if (NewNode == null)
{
@@ -1109,14 +1940,15 @@ namespace Ryujinx.HLE.HOS.Kernel
}
MergeEqualStateNeighbours(NewNode);
+
+ BlockAllocator.Count += Blocks.Count - OldCount;
}
private void MergeEqualStateNeighbours(LinkedListNode<KMemoryBlock> Node)
{
KMemoryBlock Block = Node.Value;
- ulong Start = (ulong)Block.BasePosition;
- ulong End = (ulong)Block.PagesCount * PageSize + Start;
+ ulong EndAddr = Block.PagesCount * PageSize + Block.BaseAddress;
if (Node.Previous != null)
{
@@ -1126,9 +1958,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
Blocks.Remove(Node.Previous);
- Block.BasePosition = Previous.BasePosition;
-
- Start = (ulong)Block.BasePosition;
+ Block.BaseAddress = Previous.BaseAddress;
}
}
@@ -1140,31 +1970,84 @@ namespace Ryujinx.HLE.HOS.Kernel
{
Blocks.Remove(Node.Next);
- End = (ulong)(Next.BasePosition + Next.PagesCount * PageSize);
+ EndAddr = Next.BaseAddress + Next.PagesCount * PageSize;
}
}
- Block.PagesCount = (long)((End - Start) / PageSize);
+ Block.PagesCount = (EndAddr - Block.BaseAddress) / PageSize;
}
- private static bool BlockStateEquals(KMemoryBlock LHS, KMemoryBlock RHS)
+ private static bool BlockStateEquals(KMemoryBlock Lhs, KMemoryBlock Rhs)
{
- return LHS.State == RHS.State &&
- LHS.Permission == RHS.Permission &&
- LHS.Attribute == RHS.Attribute &&
- LHS.DeviceRefCount == RHS.DeviceRefCount &&
- LHS.IpcRefCount == RHS.IpcRefCount;
+ return Lhs.State == Rhs.State &&
+ Lhs.Permission == Rhs.Permission &&
+ Lhs.Attribute == Rhs.Attribute &&
+ Lhs.DeviceRefCount == Rhs.DeviceRefCount &&
+ Lhs.IpcRefCount == Rhs.IpcRefCount;
}
- private KMemoryBlock FindBlock(long Position)
+ private ulong FindFirstFit(
+ ulong RegionStart,
+ ulong RegionPagesCount,
+ ulong NeededPagesCount,
+ int Alignment,
+ ulong ReservedStart,
+ ulong ReservedPagesCount)
{
- return FindBlockNode(Position)?.Value;
+ ulong ReservedSize = ReservedPagesCount * PageSize;
+
+ ulong TotalNeededSize = ReservedSize + NeededPagesCount * PageSize;
+
+ ulong RegionEndAddr = RegionStart + RegionPagesCount * PageSize;
+
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(RegionStart);
+
+ KMemoryInfo Info = Node.Value.GetInfo();
+
+ while (RegionEndAddr >= Info.Address)
+ {
+ if (Info.State == MemoryState.Unmapped)
+ {
+ ulong CurrBaseAddr = Info.Address + ReservedSize;
+ ulong CurrEndAddr = Info.Address + Info.Size - 1;
+
+ ulong Address = BitUtils.AlignDown(CurrBaseAddr, Alignment) + ReservedStart;
+
+ if (CurrBaseAddr > Address)
+ {
+ Address += (ulong)Alignment;
+ }
+
+ ulong AllocationEndAddr = Address + TotalNeededSize - 1;
+
+ if (AllocationEndAddr <= RegionEndAddr &&
+ AllocationEndAddr <= CurrEndAddr &&
+ Address < AllocationEndAddr)
+ {
+ return Address;
+ }
+ }
+
+ Node = Node.Next;
+
+ if (Node == null)
+ {
+ break;
+ }
+
+ Info = Node.Value.GetInfo();
+ }
+
+ return 0;
}
- private LinkedListNode<KMemoryBlock> FindBlockNode(long Position)
+ private KMemoryBlock FindBlock(ulong Address)
{
- ulong Addr = (ulong)Position;
+ return FindBlockNode(Address)?.Value;
+ }
+ private LinkedListNode<KMemoryBlock> FindBlockNode(ulong Address)
+ {
lock (Blocks)
{
LinkedListNode<KMemoryBlock> Node = Blocks.First;
@@ -1173,10 +2056,9 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KMemoryBlock Block = Node.Value;
- ulong Start = (ulong)Block.BasePosition;
- ulong End = (ulong)Block.PagesCount * PageSize + Start;
+ ulong CurrEndAddr = Block.PagesCount * PageSize + Block.BaseAddress;
- if (Start <= Addr && End - 1 >= Addr)
+ if (Block.BaseAddress <= Address && CurrEndAddr - 1 >= Address)
{
return Node;
}
@@ -1187,5 +2069,390 @@ namespace Ryujinx.HLE.HOS.Kernel
return null;
}
+
+ private bool ValidateRegionForState(ulong Address, ulong Size, MemoryState State)
+ {
+ ulong EndAddr = Address + Size;
+
+ ulong RegionBaseAddr = GetBaseAddrForState(State);
+
+ ulong RegionEndAddr = RegionBaseAddr + GetSizeForState(State);
+
+ bool InsideRegion()
+ {
+ return RegionBaseAddr <= Address &&
+ EndAddr > Address &&
+ EndAddr - 1 <= RegionEndAddr - 1;
+ }
+
+ bool OutsideHeapRegion()
+ {
+ return EndAddr <= HeapRegionStart ||
+ Address >= HeapRegionEnd;
+ }
+
+ bool OutsideMapRegion()
+ {
+ return EndAddr <= AliasRegionStart ||
+ Address >= AliasRegionEnd;
+ }
+
+ switch (State)
+ {
+ case MemoryState.Io:
+ case MemoryState.Normal:
+ case MemoryState.CodeStatic:
+ case MemoryState.CodeMutable:
+ case MemoryState.SharedMemory:
+ case MemoryState.ModCodeStatic:
+ case MemoryState.ModCodeMutable:
+ case MemoryState.Stack:
+ case MemoryState.ThreadLocal:
+ case MemoryState.TransferMemoryIsolated:
+ case MemoryState.TransferMemory:
+ case MemoryState.ProcessMemory:
+ case MemoryState.CodeReadOnly:
+ case MemoryState.CodeWritable:
+ return InsideRegion() && OutsideHeapRegion() && OutsideMapRegion();
+
+ case MemoryState.Heap:
+ return InsideRegion() && OutsideMapRegion();
+
+ case MemoryState.IpcBuffer0:
+ case MemoryState.IpcBuffer1:
+ case MemoryState.IpcBuffer3:
+ return InsideRegion() && OutsideHeapRegion();
+
+ case MemoryState.KernelStack:
+ return InsideRegion();
+ }
+
+ throw new ArgumentException($"Invalid state value \"{State}\".");
+ }
+
+ private ulong GetBaseAddrForState(MemoryState State)
+ {
+ switch (State)
+ {
+ case MemoryState.Io:
+ case MemoryState.Normal:
+ case MemoryState.ThreadLocal:
+ return TlsIoRegionStart;
+
+ case MemoryState.CodeStatic:
+ case MemoryState.CodeMutable:
+ case MemoryState.SharedMemory:
+ case MemoryState.ModCodeStatic:
+ case MemoryState.ModCodeMutable:
+ case MemoryState.TransferMemoryIsolated:
+ case MemoryState.TransferMemory:
+ case MemoryState.ProcessMemory:
+ case MemoryState.CodeReadOnly:
+ case MemoryState.CodeWritable:
+ return GetAddrSpaceBaseAddr();
+
+ case MemoryState.Heap:
+ return HeapRegionStart;
+
+ case MemoryState.IpcBuffer0:
+ case MemoryState.IpcBuffer1:
+ case MemoryState.IpcBuffer3:
+ return AliasRegionStart;
+
+ case MemoryState.Stack:
+ return StackRegionStart;
+
+ case MemoryState.KernelStack:
+ return AddrSpaceStart;
+ }
+
+ throw new ArgumentException($"Invalid state value \"{State}\".");
+ }
+
+ private ulong GetSizeForState(MemoryState State)
+ {
+ switch (State)
+ {
+ case MemoryState.Io:
+ case MemoryState.Normal:
+ case MemoryState.ThreadLocal:
+ return TlsIoRegionEnd - TlsIoRegionStart;
+
+ case MemoryState.CodeStatic:
+ case MemoryState.CodeMutable:
+ case MemoryState.SharedMemory:
+ case MemoryState.ModCodeStatic:
+ case MemoryState.ModCodeMutable:
+ case MemoryState.TransferMemoryIsolated:
+ case MemoryState.TransferMemory:
+ case MemoryState.ProcessMemory:
+ case MemoryState.CodeReadOnly:
+ case MemoryState.CodeWritable:
+ return GetAddrSpaceSize();
+
+ case MemoryState.Heap:
+ return HeapRegionEnd - HeapRegionStart;
+
+ case MemoryState.IpcBuffer0:
+ case MemoryState.IpcBuffer1:
+ case MemoryState.IpcBuffer3:
+ return AliasRegionEnd - AliasRegionStart;
+
+ case MemoryState.Stack:
+ return StackRegionEnd - StackRegionStart;
+
+ case MemoryState.KernelStack:
+ return AddrSpaceEnd - AddrSpaceStart;
+ }
+
+ throw new ArgumentException($"Invalid state value \"{State}\".");
+ }
+
+ public ulong GetAddrSpaceBaseAddr()
+ {
+ if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39)
+ {
+ return 0x8000000;
+ }
+ else if (AddrSpaceWidth == 32)
+ {
+ return 0x200000;
+ }
+ else
+ {
+ throw new InvalidOperationException("Invalid address space width!");
+ }
+ }
+
+ public ulong GetAddrSpaceSize()
+ {
+ if (AddrSpaceWidth == 36)
+ {
+ return 0xff8000000;
+ }
+ else if (AddrSpaceWidth == 39)
+ {
+ return 0x7ff8000000;
+ }
+ else if (AddrSpaceWidth == 32)
+ {
+ return 0xffe00000;
+ }
+ else
+ {
+ throw new InvalidOperationException("Invalid address space width!");
+ }
+ }
+
+ private KernelResult MapPages(ulong Address, KPageList PageList, MemoryPermission Permission)
+ {
+ ulong CurrAddr = Address;
+
+ KernelResult Result = KernelResult.Success;
+
+ foreach (KPageNode PageNode in PageList)
+ {
+ Result = DoMmuOperation(
+ CurrAddr,
+ PageNode.PagesCount,
+ PageNode.Address,
+ true,
+ Permission,
+ MemoryOperation.MapPa);
+
+ if (Result != KernelResult.Success)
+ {
+ KMemoryInfo Info = FindBlock(CurrAddr).GetInfo();
+
+ ulong PagesCount = (Address - CurrAddr) / PageSize;
+
+ Result = MmuUnmap(Address, PagesCount);
+
+ break;
+ }
+
+ CurrAddr += PageNode.PagesCount * PageSize;
+ }
+
+ return Result;
+ }
+
+ private KernelResult MmuUnmap(ulong Address, ulong PagesCount)
+ {
+ return DoMmuOperation(
+ Address,
+ PagesCount,
+ 0,
+ false,
+ MemoryPermission.None,
+ MemoryOperation.Unmap);
+ }
+
+ private KernelResult MmuChangePermission(ulong Address, ulong PagesCount, MemoryPermission Permission)
+ {
+ return DoMmuOperation(
+ Address,
+ PagesCount,
+ 0,
+ false,
+ Permission,
+ MemoryOperation.ChangePermRw);
+ }
+
+ private KernelResult DoMmuOperation(
+ ulong DstVa,
+ ulong PagesCount,
+ ulong SrcPa,
+ bool Map,
+ MemoryPermission Permission,
+ MemoryOperation Operation)
+ {
+ if (Map != (Operation == MemoryOperation.MapPa))
+ {
+ throw new ArgumentException(nameof(Map) + " value is invalid for this operation.");
+ }
+
+ KernelResult Result;
+
+ switch (Operation)
+ {
+ case MemoryOperation.MapPa:
+ {
+ ulong Size = PagesCount * PageSize;
+
+ CpuMemory.Map((long)DstVa, (long)(SrcPa - DramMemoryMap.DramBase), (long)Size);
+
+ Result = KernelResult.Success;
+
+ break;
+ }
+
+ case MemoryOperation.Allocate:
+ {
+ KMemoryRegionManager Region = GetMemoryRegionManager();
+
+ Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList);
+
+ if (Result == KernelResult.Success)
+ {
+ Result = MmuMapPages(DstVa, PageList);
+ }
+
+ break;
+ }
+
+ case MemoryOperation.Unmap:
+ {
+ ulong Size = PagesCount * PageSize;
+
+ CpuMemory.Unmap((long)DstVa, (long)Size);
+
+ Result = KernelResult.Success;
+
+ break;
+ }
+
+ case MemoryOperation.ChangePermRw: Result = KernelResult.Success; break;
+ case MemoryOperation.ChangePermsAndAttributes: Result = KernelResult.Success; break;
+
+ default: throw new ArgumentException($"Invalid operation \"{Operation}\".");
+ }
+
+ return Result;
+ }
+
+ private KernelResult DoMmuOperation(
+ ulong Address,
+ ulong PagesCount,
+ KPageList PageList,
+ MemoryPermission Permission,
+ MemoryOperation Operation)
+ {
+ if (Operation != MemoryOperation.MapVa)
+ {
+ throw new ArgumentException($"Invalid memory operation \"{Operation}\" specified.");
+ }
+
+ return MmuMapPages(Address, PageList);
+ }
+
+ private KMemoryRegionManager GetMemoryRegionManager()
+ {
+ return System.MemoryRegions[(int)MemRegion];
+ }
+
+ private KernelResult MmuMapPages(ulong Address, KPageList PageList)
+ {
+ foreach (KPageNode PageNode in PageList)
+ {
+ ulong Size = PageNode.PagesCount * PageSize;
+
+ CpuMemory.Map((long)Address, (long)(PageNode.Address - DramMemoryMap.DramBase), (long)Size);
+
+ Address += Size;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult ConvertVaToPa(ulong Va, out ulong Pa)
+ {
+ Pa = DramMemoryMap.DramBase + (ulong)CpuMemory.GetPhysicalAddress((long)Va);
+
+ return KernelResult.Success;
+ }
+
+ public long GetMmUsedPages()
+ {
+ lock (Blocks)
+ {
+ return BitUtils.DivRoundUp(GetMmUsedSize(), PageSize);
+ }
+ }
+
+ private long GetMmUsedSize()
+ {
+ return Blocks.Count * KMemoryBlockSize;
+ }
+
+ public bool IsInvalidRegion(ulong Address, ulong Size)
+ {
+ return Address + Size - 1 > GetAddrSpaceBaseAddr() + GetAddrSpaceSize() - 1;
+ }
+
+ public bool InsideAddrSpace(ulong Address, ulong Size)
+ {
+ return AddrSpaceStart <= Address && Address + Size - 1 <= AddrSpaceEnd - 1;
+ }
+
+ public bool InsideAliasRegion(ulong Address, ulong Size)
+ {
+ return Address + Size > AliasRegionStart && AliasRegionEnd > Address;
+ }
+
+ public bool InsideHeapRegion(ulong Address, ulong Size)
+ {
+ return Address + Size > HeapRegionStart && HeapRegionEnd > Address;
+ }
+
+ public bool InsideStackRegion(ulong Address, ulong Size)
+ {
+ return Address + Size > StackRegionStart && StackRegionEnd > Address;
+ }
+
+ public bool OutsideAliasRegion(ulong Address, ulong Size)
+ {
+ return AliasRegionStart > Address || Address + Size - 1 > AliasRegionEnd - 1;
+ }
+
+ public bool OutsideAddrSpace(ulong Address, ulong Size)
+ {
+ return AddrSpaceStart > Address || Address + Size - 1 > AddrSpaceEnd - 1;
+ }
+
+ public bool OutsideStackRegion(ulong Address, ulong Size)
+ {
+ return StackRegionStart > Address || Address + Size - 1 > StackRegionEnd - 1;
+ }
}
} \ No newline at end of file