diff options
Diffstat (limited to 'Ryujinx.Cpu/AppleHv/HvAddressSpace.cs')
-rw-r--r-- | Ryujinx.Cpu/AppleHv/HvAddressSpace.cs | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs b/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs new file mode 100644 index 00000000..78f4c464 --- /dev/null +++ b/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs @@ -0,0 +1,129 @@ +using Ryujinx.Cpu.AppleHv.Arm; +using Ryujinx.Memory; +using System; + +namespace Ryujinx.Cpu.AppleHv +{ + class HvAddressSpace : IDisposable + { + private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39)); + private const ulong KernelRegionCodeOffset = 0UL; + private const ulong KernelRegionCodeSize = 0x2000UL; + private const ulong KernelRegionTlbiEretOffset = KernelRegionCodeOffset + 0x1000UL; + private const ulong KernelRegionEretOffset = KernelRegionTlbiEretOffset + 4UL; + + public const ulong KernelRegionEretAddress = KernelRegionBase + KernelRegionEretOffset; + public const ulong KernelRegionTlbiEretAddress = KernelRegionBase + KernelRegionTlbiEretOffset; + + private const ulong AllocationGranule = 1UL << 14; + + private readonly ulong _asBase; + private readonly ulong _asSize; + private readonly ulong _backingSize; + + private readonly HvAddressSpaceRange _userRange; + private readonly HvAddressSpaceRange _kernelRange; + + private MemoryBlock _kernelCodeBlock; + + public HvAddressSpace(MemoryBlock backingMemory, ulong asSize) + { + (_asBase, var ipaAllocator) = HvVm.CreateAddressSpace(backingMemory); + _asSize = asSize; + _backingSize = backingMemory.Size; + + _userRange = new HvAddressSpaceRange(ipaAllocator); + _kernelRange = new HvAddressSpaceRange(ipaAllocator); + + _kernelCodeBlock = new MemoryBlock(AllocationGranule); + + InitializeKernelCode(ipaAllocator); + } + + private void InitializeKernelCode(HvIpaAllocator ipaAllocator) + { + // Write exception handlers. + for (ulong offset = 0; offset < 0x800; offset += 0x80) + { + // Offsets: + // 0x0: Synchronous + // 0x80: IRQ + // 0x100: FIQ + // 0x180: SError + _kernelCodeBlock.Write(KernelRegionCodeOffset + offset, 0xD41FFFE2u); // HVC #0xFFFF + _kernelCodeBlock.Write(KernelRegionCodeOffset + offset + 4, 0xD69F03E0u); // ERET + } + + _kernelCodeBlock.Write(KernelRegionTlbiEretOffset, 0xD508831Fu); // TLBI VMALLE1IS + _kernelCodeBlock.Write(KernelRegionEretOffset, 0xD69F03E0u); // ERET + + ulong kernelCodePa = ipaAllocator.Allocate(AllocationGranule); + HvApi.hv_vm_map((ulong)_kernelCodeBlock.Pointer, kernelCodePa, AllocationGranule, hv_memory_flags_t.HV_MEMORY_READ | hv_memory_flags_t.HV_MEMORY_EXEC).ThrowOnError(); + + _kernelRange.Map(KernelRegionCodeOffset, kernelCodePa, KernelRegionCodeSize, ApFlags.UserNoneKernelReadExecute); + } + + public void InitializeMmu(ulong vcpu) + { + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_VBAR_EL1, KernelRegionBase + KernelRegionCodeOffset); + + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_TTBR0_EL1, _userRange.GetIpaBase()); + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_TTBR1_EL1, _kernelRange.GetIpaBase()); + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_MAIR_EL1, 0xffUL); + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_TCR_EL1, 0x00000011B5193519UL); + HvApi.hv_vcpu_set_sys_reg(vcpu, hv_sys_reg_t.HV_SYS_REG_SCTLR_EL1, 0x0000000034D5D925UL); + } + + public bool GetAndClearUserTlbInvalidationPending() + { + return _userRange.GetAndClearTlbInvalidationPending(); + } + + public void MapUser(ulong va, ulong pa, ulong size, MemoryPermission permission) + { + pa += _asBase; + + lock (_userRange) + { + _userRange.Map(va, pa, size, GetApFlags(permission)); + } + } + + public void UnmapUser(ulong va, ulong size) + { + lock (_userRange) + { + _userRange.Unmap(va, size); + } + } + + public void ReprotectUser(ulong va, ulong size, MemoryPermission permission) + { + lock (_userRange) + { + _userRange.Reprotect(va, size, GetApFlags(permission)); + } + } + + private static ApFlags GetApFlags(MemoryPermission permission) + { + return permission switch + { + MemoryPermission.None => ApFlags.UserNoneKernelRead, + MemoryPermission.Execute => ApFlags.UserExecuteKernelRead, + MemoryPermission.Read => ApFlags.UserReadKernelRead, + MemoryPermission.ReadAndWrite => ApFlags.UserReadWriteKernelReadWrite, + MemoryPermission.ReadAndExecute => ApFlags.UserReadExecuteKernelRead, + MemoryPermission.ReadWriteExecute => ApFlags.UserReadWriteExecuteKernelReadWrite, + _ => throw new ArgumentException($"Permission \"{permission}\" is invalid.") + }; + } + + public void Dispose() + { + _userRange.Dispose(); + _kernelRange.Dispose(); + HvVm.DestroyAddressSpace(_asBase, _backingSize); + } + } +}
\ No newline at end of file |