diff options
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs')
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs new file mode 100644 index 00000000..0e0fe7f2 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs @@ -0,0 +1,401 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Gpu.Memory; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; +using Ryujinx.Memory; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu +{ + class NvHostAsGpuDeviceFile : NvDeviceFile + { + private const uint SmallPageSize = 0x1000; + private const uint BigPageSize = 0x10000; + + private static readonly uint[] _pageSizes = new uint[] { SmallPageSize, BigPageSize }; + + private const ulong SmallRegionLimit = 0x400000000UL; // 16 GiB + private const ulong DefaultUserSize = 1UL << 37; + + private readonly struct VmRegion + { + public ulong Start { get; } + public ulong Limit { get; } + + public VmRegion(ulong start, ulong limit) + { + Start = start; + Limit = limit; + } + } + + private static readonly VmRegion[] _vmRegions = new VmRegion[] + { + new VmRegion((ulong)BigPageSize << 16, SmallRegionLimit), + new VmRegion(SmallRegionLimit, DefaultUserSize) + }; + + private readonly AddressSpaceContext _asContext; + private readonly NvMemoryAllocator _memoryAllocator; + + public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner) + { + _asContext = new AddressSpaceContext(context.Device.Gpu.CreateMemoryManager(owner)); + _memoryAllocator = new NvMemoryAllocator(); + } + + public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments) + { + NvInternalResult result = NvInternalResult.NotImplemented; + + if (command.Type == NvIoctl.NvGpuAsMagic) + { + switch (command.Number) + { + case 0x01: + result = CallIoctlMethod<BindChannelArguments>(BindChannel, arguments); + break; + case 0x02: + result = CallIoctlMethod<AllocSpaceArguments>(AllocSpace, arguments); + break; + case 0x03: + result = CallIoctlMethod<FreeSpaceArguments>(FreeSpace, arguments); + break; + case 0x05: + result = CallIoctlMethod<UnmapBufferArguments>(UnmapBuffer, arguments); + break; + case 0x06: + result = CallIoctlMethod<MapBufferExArguments>(MapBufferEx, arguments); + break; + case 0x08: + result = CallIoctlMethod<GetVaRegionsArguments>(GetVaRegions, arguments); + break; + case 0x09: + result = CallIoctlMethod<InitializeExArguments>(InitializeEx, arguments); + break; + case 0x14: + result = CallIoctlMethod<RemapArguments>(Remap, arguments); + break; + } + } + + return result; + } + + public override NvInternalResult Ioctl3(NvIoctl command, Span<byte> arguments, Span<byte> inlineOutBuffer) + { + NvInternalResult result = NvInternalResult.NotImplemented; + + if (command.Type == NvIoctl.NvGpuAsMagic) + { + switch (command.Number) + { + case 0x08: + // This is the same as the one in ioctl as inlineOutBuffer is empty. + result = CallIoctlMethod<GetVaRegionsArguments>(GetVaRegions, arguments); + break; + } + } + + return result; + } + + private NvInternalResult BindChannel(ref BindChannelArguments arguments) + { + var channelDeviceFile = INvDrvServices.DeviceFileIdRegistry.GetData<NvHostChannelDeviceFile>(arguments.Fd); + if (channelDeviceFile == null) + { + // TODO: Return invalid Fd error. + } + + channelDeviceFile.Channel.BindMemory(_asContext.Gmm); + + return NvInternalResult.Success; + } + + private NvInternalResult AllocSpace(ref AllocSpaceArguments arguments) + { + ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize; + + NvInternalResult result = NvInternalResult.Success; + + lock (_asContext) + { + // Note: When the fixed offset flag is not set, + // the Offset field holds the alignment size instead. + if ((arguments.Flags & AddressSpaceFlags.FixedOffset) != 0) + { + bool regionInUse = _memoryAllocator.IsRegionInUse(arguments.Offset, size, out ulong freeAddressStartPosition); + ulong address; + + if (!regionInUse) + { + _memoryAllocator.AllocateRange(arguments.Offset, size, freeAddressStartPosition); + address = freeAddressStartPosition; + } + else + { + address = NvMemoryAllocator.PteUnmapped; + } + + arguments.Offset = address; + } + else + { + ulong address = _memoryAllocator.GetFreeAddress(size, out ulong freeAddressStartPosition, arguments.Offset); + if (address != NvMemoryAllocator.PteUnmapped) + { + _memoryAllocator.AllocateRange(address, size, freeAddressStartPosition); + } + + arguments.Offset = address; + } + + if (arguments.Offset == NvMemoryAllocator.PteUnmapped) + { + arguments.Offset = 0; + + Logger.Warning?.Print(LogClass.ServiceNv, $"Failed to allocate size {size:x16}!"); + + result = NvInternalResult.OutOfMemory; + } + else + { + _asContext.AddReservation(arguments.Offset, size); + } + } + + return result; + } + + private NvInternalResult FreeSpace(ref FreeSpaceArguments arguments) + { + ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize; + + NvInternalResult result = NvInternalResult.Success; + + lock (_asContext) + { + if (_asContext.RemoveReservation(arguments.Offset)) + { + _memoryAllocator.DeallocateRange(arguments.Offset, size); + _asContext.Gmm.Unmap(arguments.Offset, size); + } + else + { + Logger.Warning?.Print(LogClass.ServiceNv, + $"Failed to free offset 0x{arguments.Offset:x16} size 0x{size:x16}!"); + + result = NvInternalResult.InvalidInput; + } + } + + return result; + } + + private NvInternalResult UnmapBuffer(ref UnmapBufferArguments arguments) + { + lock (_asContext) + { + if (_asContext.RemoveMap(arguments.Offset, out ulong size)) + { + if (size != 0) + { + _memoryAllocator.DeallocateRange(arguments.Offset, size); + _asContext.Gmm.Unmap(arguments.Offset, size); + } + } + else + { + Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid buffer offset {arguments.Offset:x16}!"); + } + } + + return NvInternalResult.Success; + } + + private NvInternalResult MapBufferEx(ref MapBufferExArguments arguments) + { + const string MapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16}, size 0x{1:x16} and alignment 0x{2:x16}!"; + + ulong physicalAddress; + + if ((arguments.Flags & AddressSpaceFlags.RemapSubRange) != 0) + { + lock (_asContext) + { + if (_asContext.TryGetMapPhysicalAddress(arguments.Offset, out physicalAddress)) + { + ulong virtualAddress = arguments.Offset + arguments.BufferOffset; + + physicalAddress += arguments.BufferOffset; + _asContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize, (PteKind)arguments.Kind); + + return NvInternalResult.Success; + } + else + { + Logger.Warning?.Print(LogClass.ServiceNv, $"Address 0x{arguments.Offset:x16} not mapped!"); + + return NvInternalResult.InvalidInput; + } + } + } + + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments.NvMapHandle); + + if (map == null) + { + Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments.NvMapHandle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + ulong pageSize = (ulong)arguments.PageSize; + + if (pageSize == 0) + { + pageSize = (ulong)map.Align; + } + + physicalAddress = map.Address + arguments.BufferOffset; + + ulong size = arguments.MappingSize; + + if (size == 0) + { + size = (uint)map.Size; + } + + NvInternalResult result = NvInternalResult.Success; + + lock (_asContext) + { + // Note: When the fixed offset flag is not set, + // the Offset field holds the alignment size instead. + bool virtualAddressAllocated = (arguments.Flags & AddressSpaceFlags.FixedOffset) == 0; + + if (!virtualAddressAllocated) + { + if (_asContext.ValidateFixedBuffer(arguments.Offset, size, pageSize)) + { + _asContext.Gmm.Map(physicalAddress, arguments.Offset, size, (PteKind)arguments.Kind); + } + else + { + string message = string.Format(MapErrorMsg, arguments.Offset, size, pageSize); + + Logger.Warning?.Print(LogClass.ServiceNv, message); + + result = NvInternalResult.InvalidInput; + } + } + else + { + ulong va = _memoryAllocator.GetFreeAddress(size, out ulong freeAddressStartPosition, pageSize); + if (va != NvMemoryAllocator.PteUnmapped) + { + _memoryAllocator.AllocateRange(va, size, freeAddressStartPosition); + } + + _asContext.Gmm.Map(physicalAddress, va, size, (PteKind)arguments.Kind); + arguments.Offset = va; + } + + if (arguments.Offset == NvMemoryAllocator.PteUnmapped) + { + arguments.Offset = 0; + + Logger.Warning?.Print(LogClass.ServiceNv, $"Failed to map size 0x{size:x16}!"); + + result = NvInternalResult.InvalidInput; + } + else + { + _asContext.AddMap(arguments.Offset, size, physicalAddress, virtualAddressAllocated); + } + } + + return result; + } + + private NvInternalResult GetVaRegions(ref GetVaRegionsArguments arguments) + { + int vaRegionStructSize = Unsafe.SizeOf<VaRegion>(); + + Debug.Assert(vaRegionStructSize == 0x18); + Debug.Assert(_pageSizes.Length == 2); + + uint writeEntries = (uint)(arguments.BufferSize / vaRegionStructSize); + if (writeEntries > _pageSizes.Length) + { + writeEntries = (uint)_pageSizes.Length; + } + + for (uint i = 0; i < writeEntries; i++) + { + ref var region = ref arguments.Regions[(int)i]; + + var vmRegion = _vmRegions[i]; + uint pageSize = _pageSizes[i]; + + region.PageSize = pageSize; + region.Offset = vmRegion.Start; + region.Pages = (vmRegion.Limit - vmRegion.Start) / pageSize; + region.Padding = 0; + } + + arguments.BufferSize = (uint)(_pageSizes.Length * vaRegionStructSize); + + return NvInternalResult.Success; + } + + private NvInternalResult InitializeEx(ref InitializeExArguments arguments) + { + Logger.Stub?.PrintStub(LogClass.ServiceNv); + + return NvInternalResult.Success; + } + + private NvInternalResult Remap(Span<RemapArguments> arguments) + { + MemoryManager gmm = _asContext.Gmm; + + for (int index = 0; index < arguments.Length; index++) + { + ref RemapArguments argument = ref arguments[index]; + ulong gpuVa = (ulong)argument.GpuOffset << 16; + ulong size = (ulong)argument.Pages << 16; + int nvmapHandle = argument.NvMapHandle; + + if (nvmapHandle == 0) + { + gmm.Unmap(gpuVa, size); + } + else + { + ulong mapOffs = (ulong)argument.MapOffset << 16; + PteKind kind = (PteKind)argument.Kind; + + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, nvmapHandle); + + if (map == null) + { + Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{nvmapHandle:x8}!"); + + return NvInternalResult.InvalidInput; + } + + gmm.Map(mapOffs + map.Address, gpuVa, size, kind); + } + } + + return NvInternalResult.Success; + } + + public override void Close() { } + } +} |
