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 }