/ src / Ryujinx.HLE / HOS / Kernel / Memory / KCodeMemory.cs
KCodeMemory.cs
  1  using Ryujinx.Common;
  2  using Ryujinx.HLE.HOS.Kernel.Common;
  3  using Ryujinx.HLE.HOS.Kernel.Process;
  4  using Ryujinx.Horizon.Common;
  5  using System;
  6  using System.Diagnostics;
  7  
  8  namespace Ryujinx.HLE.HOS.Kernel.Memory
  9  {
 10      class KCodeMemory : KAutoObject
 11      {
 12          public KProcess Owner { get; private set; }
 13          private readonly KPageList _pageList;
 14          private readonly object _lock;
 15          private ulong _address;
 16          private bool _isOwnerMapped;
 17          private bool _isMapped;
 18  
 19          public KCodeMemory(KernelContext context) : base(context)
 20          {
 21              _pageList = new KPageList();
 22              _lock = new object();
 23          }
 24  
 25          public Result Initialize(ulong address, ulong size)
 26          {
 27              Owner = KernelStatic.GetCurrentProcess();
 28  
 29              Result result = Owner.MemoryManager.BorrowCodeMemory(_pageList, address, size);
 30  
 31              if (result != Result.Success)
 32              {
 33                  return result;
 34              }
 35  
 36              Owner.CpuMemory.Fill(address, size, 0xff);
 37              Owner.IncrementReferenceCount();
 38  
 39              _address = address;
 40              _isMapped = false;
 41              _isOwnerMapped = false;
 42  
 43              return Result.Success;
 44          }
 45  
 46          public Result Map(ulong address, ulong size, KMemoryPermission perm)
 47          {
 48              if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
 49              {
 50                  return KernelResult.InvalidSize;
 51              }
 52  
 53              lock (_lock)
 54              {
 55                  if (_isMapped)
 56                  {
 57                      return KernelResult.InvalidState;
 58                  }
 59  
 60                  KProcess process = KernelStatic.GetCurrentProcess();
 61  
 62                  Result result = process.MemoryManager.MapPages(address, _pageList, MemoryState.CodeWritable, KMemoryPermission.ReadAndWrite);
 63  
 64                  if (result != Result.Success)
 65                  {
 66                      return result;
 67                  }
 68  
 69                  _isMapped = true;
 70              }
 71  
 72              return Result.Success;
 73          }
 74  
 75          public Result MapToOwner(ulong address, ulong size, KMemoryPermission permission)
 76          {
 77              if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
 78              {
 79                  return KernelResult.InvalidSize;
 80              }
 81  
 82              lock (_lock)
 83              {
 84                  if (_isOwnerMapped)
 85                  {
 86                      return KernelResult.InvalidState;
 87                  }
 88  
 89                  Debug.Assert(permission == KMemoryPermission.Read || permission == KMemoryPermission.ReadAndExecute);
 90  
 91                  Result result = Owner.MemoryManager.MapPages(address, _pageList, MemoryState.CodeReadOnly, permission);
 92  
 93                  if (result != Result.Success)
 94                  {
 95                      return result;
 96                  }
 97  
 98                  _isOwnerMapped = true;
 99              }
100  
101              return Result.Success;
102          }
103  
104          public Result Unmap(ulong address, ulong size)
105          {
106              if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
107              {
108                  return KernelResult.InvalidSize;
109              }
110  
111              lock (_lock)
112              {
113                  KProcess process = KernelStatic.GetCurrentProcess();
114  
115                  Result result = process.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeWritable);
116  
117                  if (result != Result.Success)
118                  {
119                      return result;
120                  }
121  
122                  Debug.Assert(_isMapped);
123  
124                  _isMapped = false;
125              }
126  
127              return Result.Success;
128          }
129  
130          public Result UnmapFromOwner(ulong address, ulong size)
131          {
132              if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
133              {
134                  return KernelResult.InvalidSize;
135              }
136  
137              lock (_lock)
138              {
139                  Result result = Owner.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeReadOnly);
140  
141                  if (result != Result.Success)
142                  {
143                      return result;
144                  }
145  
146                  Debug.Assert(_isOwnerMapped);
147  
148                  _isOwnerMapped = false;
149              }
150  
151              return Result.Success;
152          }
153  
154          protected override void Destroy()
155          {
156              if (!_isMapped && !_isOwnerMapped)
157              {
158                  ulong size = _pageList.GetPagesCount() * KPageTableBase.PageSize;
159  
160                  if (Owner.MemoryManager.UnborrowCodeMemory(_address, size, _pageList) != Result.Success)
161                  {
162                      throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes.");
163                  }
164              }
165  
166              Owner.DecrementReferenceCount();
167          }
168      }
169  }