Make PPTC state non-static (#4157)

* Make PPTC state non-static

* DiskCacheLoadState can be null
This commit is contained in:
gdkchan 2023-01-04 20:01:44 -03:00 committed by GitHub
parent 08831eecf7
commit fc4b7cba2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 434 additions and 230 deletions

View file

@ -1,4 +1,3 @@
using ARMeilleure.Translation.PTC;
using LibHac;
using LibHac.Account;
using LibHac.Common;
@ -14,8 +13,8 @@ using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.Memory;
using System;
@ -67,6 +66,8 @@ namespace Ryujinx.HLE.HOS
public string TitleIdText => TitleId.ToString("x16");
public IDiskCacheLoadState DiskCacheLoadState { get; private set; }
public ApplicationLoader(Switch device)
{
_device = device;
@ -94,7 +95,7 @@ namespace Ryujinx.HLE.HOS
EnsureSaveData(new ApplicationId(TitleId));
}
LoadExeFs(codeFs, metaData);
LoadExeFs(codeFs, string.Empty, metaData);
}
public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex)
@ -302,12 +303,6 @@ namespace Ryujinx.HLE.HOS
public void LoadServiceNca(string ncaFile)
{
// Disable PPTC here as it does not support multiple processes running.
// TODO: This should be eventually removed and it should stop using global state and
// instead manage the cache per process.
Ptc.Close();
PtcProfiler.Stop();
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
@ -369,16 +364,12 @@ namespace Ryujinx.HLE.HOS
// Collect the nsos, ignoring ones that aren't used.
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode;
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
{
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
}
string displayVersion = _device.System.ContentManager.GetCurrentFirmwareVersion().VersionString;
bool usePtc = _device.System.EnablePtc;
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: false);
ProgramLoader.LoadNsos(_device.System.KernelContext, out _, metaData, programInfo, executables: programs);
ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit: false);
ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs);
string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16");
bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
@ -477,9 +468,11 @@ namespace Ryujinx.HLE.HOS
_device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(),
_device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
string displayVersion = string.Empty;
if (controlNca != null)
{
ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref _displayVersion);
ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref displayVersion);
}
else
{
@ -493,9 +486,11 @@ namespace Ryujinx.HLE.HOS
string dummyTitleName = "";
BlitStruct<ApplicationControlProperty> dummyControl = new BlitStruct<ApplicationControlProperty>(1);
ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref _displayVersion);
ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref displayVersion);
}
_displayVersion = displayVersion;
if (dataStorage == null)
{
Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA");
@ -515,7 +510,7 @@ namespace Ryujinx.HLE.HOS
EnsureSaveData(new ApplicationId(TitleId & ~0xFul));
}
LoadExeFs(codeFs, metaData);
LoadExeFs(codeFs, displayVersion, metaData);
Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]");
}
@ -584,7 +579,7 @@ namespace Ryujinx.HLE.HOS
}
}
private void LoadExeFs(IFileSystem codeFs, MetaLoader metaData = null, bool isHomebrew = false)
private void LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false)
{
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
{
@ -649,23 +644,23 @@ namespace Ryujinx.HLE.HOS
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
}
Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode);
// We allow it for nx-hbloader because it can be used to launch homebrew.
bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew;
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit);
ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: programs);
ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit);
ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs);
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine);
DiskCacheLoadState = result.DiskCacheLoadState;
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine);
}
public void LoadProgram(string filePath)
{
MetaLoader metaData = GetDefaultNpdm();
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: true);
ProgramInfo programInfo = new ProgramInfo(in npdm, string.Empty, diskCacheEnabled: false, allowCodeMemoryForJit: true);
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
@ -761,9 +756,11 @@ namespace Ryujinx.HLE.HOS
Graphics.Gpu.GraphicsConfig.TitleId = null;
_device.Gpu.HostInitalized.Set();
ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: executable);
ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable);
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine);
DiskCacheLoadState = result.DiskCacheLoadState;
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine);
}
private MetaLoader GetDefaultNpdm()

View file

@ -6,7 +6,17 @@ using Ryujinx.Memory;
namespace Ryujinx.HLE.HOS
{
class ArmProcessContext<T> : IProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
interface IArmProcessContext : IProcessContext
{
IDiskCacheLoadState Initialize(
string titleIdText,
string displayVersion,
bool diskCacheEnabled,
ulong codeAddress,
ulong codeSize);
}
class ArmProcessContext<T> : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
{
private readonly ulong _pid;
private readonly GpuContext _gpuContext;
@ -40,6 +50,17 @@ namespace Ryujinx.HLE.HOS
_cpuContext.Execute(context, codeAddress);
}
public IDiskCacheLoadState Initialize(
string titleIdText,
string displayVersion,
bool diskCacheEnabled,
ulong codeAddress,
ulong codeSize)
{
_cpuContext.PrepareCodeRange(codeAddress, codeSize);
return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled);
}
public void InvalidateCacheRegion(ulong address, ulong size)
{
_cpuContext.InvalidateCacheRegion(address, size);

View file

@ -13,11 +13,30 @@ namespace Ryujinx.HLE.HOS
{
private readonly ICpuEngine _cpuEngine;
private readonly GpuContext _gpu;
private readonly string _titleIdText;
private readonly string _displayVersion;
private readonly bool _diskCacheEnabled;
private readonly ulong _codeAddress;
private readonly ulong _codeSize;
public ArmProcessContextFactory(ICpuEngine cpuEngine, GpuContext gpu)
public IDiskCacheLoadState DiskCacheLoadState { get; private set; }
public ArmProcessContextFactory(
ICpuEngine cpuEngine,
GpuContext gpu,
string titleIdText,
string displayVersion,
bool diskCacheEnabled,
ulong codeAddress,
ulong codeSize)
{
_cpuEngine = cpuEngine;
_gpu = gpu;
_titleIdText = titleIdText;
_displayVersion = displayVersion;
_diskCacheEnabled = diskCacheEnabled;
_codeAddress = codeAddress;
_codeSize = codeSize;
}
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
@ -29,21 +48,29 @@ namespace Ryujinx.HLE.HOS
mode = MemoryManagerMode.SoftwarePageTable;
}
IArmProcessContext processContext;
switch (mode)
{
case MemoryManagerMode.SoftwarePageTable:
var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
return new ArmProcessContext<MemoryManager>(pid, _cpuEngine, _gpu, memoryManager, for64Bit);
processContext = new ArmProcessContext<MemoryManager>(pid, _cpuEngine, _gpu, memoryManager, for64Bit);
break;
case MemoryManagerMode.HostMapped:
case MemoryManagerMode.HostMappedUnsafe:
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler);
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit);
processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit);
break;
default:
throw new ArgumentOutOfRangeException();
}
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize);
return processContext;
}
}
}

View file

@ -1,9 +1,9 @@
using ARMeilleure.Translation.PTC;
using LibHac.Loader;
using LibHac.Ncm;
using LibHac.Util;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
@ -21,16 +21,40 @@ namespace Ryujinx.HLE.HOS
{
public string Name;
public ulong ProgramId;
public bool AllowCodeMemoryForJit;
public readonly string TitleIdText;
public readonly string DisplayVersion;
public readonly bool DiskCacheEnabled;
public readonly bool AllowCodeMemoryForJit;
public ProgramInfo(in Npdm npdm, bool allowCodeMemoryForJit)
public ProgramInfo(in Npdm npdm, string displayVersion, bool diskCacheEnabled, bool allowCodeMemoryForJit)
{
ulong programId = npdm.Aci.Value.ProgramId.Value;
Name = StringUtils.Utf8ZToString(npdm.Meta.Value.ProgramName);
ProgramId = npdm.Aci.Value.ProgramId.Value;
ProgramId = programId;
TitleIdText = programId.ToString("x16");
DisplayVersion = displayVersion;
DiskCacheEnabled = diskCacheEnabled;
AllowCodeMemoryForJit = allowCodeMemoryForJit;
}
}
struct ProgramLoadResult
{
public static ProgramLoadResult Failed => new ProgramLoadResult(false, null, null);
public readonly bool Success;
public readonly ProcessTamperInfo TamperInfo;
public readonly IDiskCacheLoadState DiskCacheLoadState;
public ProgramLoadResult(bool success, ProcessTamperInfo tamperInfo, IDiskCacheLoadState diskCacheLoadState)
{
Success = success;
TamperInfo = tamperInfo;
DiskCacheLoadState = diskCacheLoadState;
}
}
static class ProgramLoader
{
private const bool AslrEnabled = true;
@ -102,7 +126,14 @@ namespace Ryujinx.HLE.HOS
KProcess process = new KProcess(context);
var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu);
var processContextFactory = new ArmProcessContextFactory(
context.Device.System.CpuEngine,
context.Device.Gpu,
string.Empty,
string.Empty,
false,
codeAddress,
codeSize);
result = process.InitializeKip(
creationInfo,
@ -144,9 +175,8 @@ namespace Ryujinx.HLE.HOS
return true;
}
public static bool LoadNsos(
public static ProgramLoadResult LoadNsos(
KernelContext context,
out ProcessTamperInfo tamperInfo,
MetaLoader metaData,
ProgramInfo programInfo,
byte[] arguments = null,
@ -156,8 +186,7 @@ namespace Ryujinx.HLE.HOS
if (rc.IsFailure())
{
tamperInfo = null;
return false;
return ProgramLoadResult.Failed;
}
ref readonly var meta = ref npdm.Meta.Value;
@ -212,9 +241,6 @@ namespace Ryujinx.HLE.HOS
}
}
PtcProfiler.StaticCodeStart = codeStart;
PtcProfiler.StaticCodeSize = (ulong)codeSize;
int codePagesCount = (int)(codeSize / KPageTableBase.PageSize);
int personalMmHeapPagesCount = (int)(meta.SystemResourceSize / KPageTableBase.PageSize);
@ -263,9 +289,7 @@ namespace Ryujinx.HLE.HOS
{
Logger.Error?.Print(LogClass.Loader, $"Process initialization failed setting resource limit values.");
tamperInfo = null;
return false;
return ProgramLoadResult.Failed;
}
KProcess process = new KProcess(context, programInfo.AllowCodeMemoryForJit);
@ -276,12 +300,17 @@ namespace Ryujinx.HLE.HOS
{
Logger.Error?.Print(LogClass.Loader, $"Process initialization failed due to invalid ACID flags.");
tamperInfo = null;
return false;
return ProgramLoadResult.Failed;
}
var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu);
var processContextFactory = new ArmProcessContextFactory(
context.Device.System.CpuEngine,
context.Device.Gpu,
programInfo.TitleIdText,
programInfo.DisplayVersion,
programInfo.DiskCacheEnabled,
codeStart,
codeSize);
result = process.Initialize(
creationInfo,
@ -294,9 +323,7 @@ namespace Ryujinx.HLE.HOS
{
Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\".");
tamperInfo = null;
return false;
return ProgramLoadResult.Failed;
}
for (int index = 0; index < executables.Length; index++)
@ -309,9 +336,7 @@ namespace Ryujinx.HLE.HOS
{
Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\".");
tamperInfo = null;
return false;
return ProgramLoadResult.Failed;
}
}
@ -323,9 +348,7 @@ namespace Ryujinx.HLE.HOS
{
Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\".");
tamperInfo = null;
return false;
return ProgramLoadResult.Failed;
}
context.Processes.TryAdd(process.Pid, process);
@ -333,10 +356,15 @@ namespace Ryujinx.HLE.HOS
// Keep the build ids because the tamper machine uses them to know which process to associate a
// tamper to and also keep the starting address of each executable inside a process because some
// memory modifications are relative to this address.
tamperInfo = new ProcessTamperInfo(process, buildIds, nsoBase, process.MemoryManager.HeapRegionStart,
process.MemoryManager.AliasRegionStart, process.MemoryManager.CodeRegionStart);
ProcessTamperInfo tamperInfo = new ProcessTamperInfo(
process,
buildIds,
nsoBase,
process.MemoryManager.HeapRegionStart,
process.MemoryManager.AliasRegionStart,
process.MemoryManager.CodeRegionStart);
return true;
return new ProgramLoadResult(true, tamperInfo, processContextFactory.DiskCacheLoadState);
}
private static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress)