/ src / Ryujinx.Cpu / AppleHv / HvAddressSpace.cs
HvAddressSpace.cs
  1  using Ryujinx.Cpu.AppleHv.Arm;
  2  using Ryujinx.Memory;
  3  using System;
  4  using System.Runtime.Versioning;
  5  
  6  namespace Ryujinx.Cpu.AppleHv
  7  {
  8      [SupportedOSPlatform("macos")]
  9      class HvAddressSpace : IDisposable
 10      {
 11          private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));
 12          private const ulong KernelRegionCodeOffset = 0UL;
 13          private const ulong KernelRegionCodeSize = 0x2000UL;
 14          private const ulong KernelRegionTlbiEretOffset = KernelRegionCodeOffset + 0x1000UL;
 15          private const ulong KernelRegionEretOffset = KernelRegionTlbiEretOffset + 4UL;
 16  
 17          public const ulong KernelRegionEretAddress = KernelRegionBase + KernelRegionEretOffset;
 18          public const ulong KernelRegionTlbiEretAddress = KernelRegionBase + KernelRegionTlbiEretOffset;
 19  
 20          private const ulong AllocationGranule = 1UL << 14;
 21  
 22          private readonly ulong _asBase;
 23          private readonly ulong _backingSize;
 24  
 25          private readonly HvAddressSpaceRange _userRange;
 26          private readonly HvAddressSpaceRange _kernelRange;
 27  
 28          private readonly MemoryBlock _kernelCodeBlock;
 29  
 30          public HvAddressSpace(MemoryBlock backingMemory, ulong asSize)
 31          {
 32              (_asBase, var ipaAllocator) = HvVm.CreateAddressSpace(backingMemory);
 33              _backingSize = backingMemory.Size;
 34  
 35              _userRange = new HvAddressSpaceRange(ipaAllocator);
 36              _kernelRange = new HvAddressSpaceRange(ipaAllocator);
 37  
 38              _kernelCodeBlock = new MemoryBlock(AllocationGranule);
 39  
 40              InitializeKernelCode(ipaAllocator);
 41          }
 42  
 43          private void InitializeKernelCode(HvIpaAllocator ipaAllocator)
 44          {
 45              // Write exception handlers.
 46              for (ulong offset = 0; offset < 0x800; offset += 0x80)
 47              {
 48                  // Offsets:
 49                  // 0x0: Synchronous
 50                  // 0x80: IRQ
 51                  // 0x100: FIQ
 52                  // 0x180: SError
 53                  _kernelCodeBlock.Write(KernelRegionCodeOffset + offset, 0xD41FFFE2u); // HVC #0xFFFF
 54                  _kernelCodeBlock.Write(KernelRegionCodeOffset + offset + 4, 0xD69F03E0u); // ERET
 55              }
 56  
 57              _kernelCodeBlock.Write(KernelRegionTlbiEretOffset, 0xD508831Fu); // TLBI VMALLE1IS
 58              _kernelCodeBlock.Write(KernelRegionEretOffset, 0xD69F03E0u); // ERET
 59  
 60              ulong kernelCodePa = ipaAllocator.Allocate(AllocationGranule);
 61              HvApi.hv_vm_map((ulong)_kernelCodeBlock.Pointer, kernelCodePa, AllocationGranule, HvMemoryFlags.Read | HvMemoryFlags.Exec).ThrowOnError();
 62  
 63              _kernelRange.Map(KernelRegionCodeOffset, kernelCodePa, KernelRegionCodeSize, ApFlags.UserNoneKernelReadExecute);
 64          }
 65  
 66          public void InitializeMmu(ulong vcpu)
 67          {
 68              HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.VBAR_EL1, KernelRegionBase + KernelRegionCodeOffset);
 69  
 70              HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.TTBR0_EL1, _userRange.GetIpaBase());
 71              HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.TTBR1_EL1, _kernelRange.GetIpaBase());
 72              HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.MAIR_EL1, 0xffUL);
 73              HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.TCR_EL1, 0x00000011B5193519UL);
 74              HvApi.hv_vcpu_set_sys_reg(vcpu, HvSysReg.SCTLR_EL1, 0x0000000034D5D925UL);
 75          }
 76  
 77          public bool GetAndClearUserTlbInvalidationPending()
 78          {
 79              return _userRange.GetAndClearTlbInvalidationPending();
 80          }
 81  
 82          public void MapUser(ulong va, ulong pa, ulong size, MemoryPermission permission)
 83          {
 84              pa += _asBase;
 85  
 86              lock (_userRange)
 87              {
 88                  _userRange.Map(va, pa, size, GetApFlags(permission));
 89              }
 90          }
 91  
 92          public void UnmapUser(ulong va, ulong size)
 93          {
 94              lock (_userRange)
 95              {
 96                  _userRange.Unmap(va, size);
 97              }
 98          }
 99  
100          public void ReprotectUser(ulong va, ulong size, MemoryPermission permission)
101          {
102              lock (_userRange)
103              {
104                  _userRange.Reprotect(va, size, GetApFlags(permission));
105              }
106          }
107  
108          private static ApFlags GetApFlags(MemoryPermission permission)
109          {
110              return permission switch
111              {
112                  MemoryPermission.None => ApFlags.UserNoneKernelRead,
113                  MemoryPermission.Execute => ApFlags.UserExecuteKernelRead,
114                  MemoryPermission.Read => ApFlags.UserReadKernelRead,
115                  MemoryPermission.ReadAndWrite => ApFlags.UserReadWriteKernelReadWrite,
116                  MemoryPermission.ReadAndExecute => ApFlags.UserReadExecuteKernelRead,
117                  MemoryPermission.ReadWriteExecute => ApFlags.UserReadWriteExecuteKernelReadWrite,
118                  _ => throw new ArgumentException($"Permission \"{permission}\" is invalid."),
119              };
120          }
121  
122          public void Dispose()
123          {
124              _userRange.Dispose();
125              _kernelRange.Dispose();
126              HvVm.DestroyAddressSpace(_asBase, _backingSize);
127          }
128      }
129  }