From 874540bb5c1c5737bc9b0bfdc96fe1cf12ff164d Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sun, 4 Apr 2021 09:06:59 -0300
Subject: [PATCH] Allow DRAM size to be increased from 4GB to 6GB (#2174)

* Allow DRAM size to be increased from 4GB to 6GB

* Add option on the UI
---
 .../Configuration/ConfigurationFileFormat.cs  |   7 +-
 .../Configuration/ConfigurationState.cs       |   9 ++
 Ryujinx.HLE/HOS/Horizon.cs                    |   8 +-
 .../HOS/Kernel/Common/KSystemControl.cs       |  72 ++++++++++
 Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs   | 131 ++++++------------
 .../HOS/Kernel/Common/MemoryArrange.cs        |  12 ++
 Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs   |   9 ++
 Ryujinx.HLE/HOS/Kernel/KernelContext.cs       |  10 +-
 .../HOS/Kernel/Memory/DramMemoryMap.cs        |   2 -
 .../HOS/Kernel/Memory/KMemoryArrange.cs       |  22 ---
 .../HOS/Kernel/Memory/KMemoryArrangeRegion.cs |  16 ---
 Ryujinx.HLE/MemoryConfiguration.cs            |  62 +++++++++
 Ryujinx.HLE/Switch.cs                         |  17 ++-
 Ryujinx/Ui/MainWindow.cs                      |  16 ++-
 Ryujinx/Ui/Windows/SettingsWindow.cs          |   9 +-
 Ryujinx/Ui/Windows/SettingsWindow.glade       |  20 ++-
 16 files changed, 278 insertions(+), 144 deletions(-)
 create mode 100644 Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs
 create mode 100644 Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs
 create mode 100644 Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs
 delete mode 100644 Ryujinx.HLE/HOS/Kernel/Memory/KMemoryArrange.cs
 delete mode 100644 Ryujinx.HLE/HOS/Kernel/Memory/KMemoryArrangeRegion.cs
 create mode 100644 Ryujinx.HLE/MemoryConfiguration.cs

diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
index 901c823e5a..32e76375d0 100644
--- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.Configuration
         /// <summary>
         /// The current version of the file format
         /// </summary>
-        public const int CurrentVersion = 22;
+        public const int CurrentVersion = 23;
 
         public int Version { get; set; }
 
@@ -173,6 +173,11 @@ namespace Ryujinx.Configuration
         /// </summary>
         public AudioBackend AudioBackend { get; set; }
 
+        /// <summary>
+        /// Expands the RAM amount on the emulated system from 4GB to 6GB
+        /// </summary>
+        public bool ExpandRam { get; set; }
+
         /// <summary>
         /// Enable or disable ignoring missing services
         /// </summary>
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs
index d51ee9efc6..e257fa232b 100644
--- a/Ryujinx.Common/Configuration/ConfigurationState.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationState.cs
@@ -218,6 +218,11 @@ namespace Ryujinx.Configuration
             /// </summary>
             public ReactiveObject<AudioBackend> AudioBackend { get; private set; }
 
+            /// <summary>
+            /// Defines the amount of RAM available on the emulated system, and how it is distributed
+            /// </summary>
+            public ReactiveObject<bool> ExpandRam { get; private set; }
+
             /// <summary>
             /// Enable or disable ignoring missing services
             /// </summary>
@@ -234,6 +239,7 @@ namespace Ryujinx.Configuration
                 EnableFsIntegrityChecks = new ReactiveObject<bool>();
                 FsGlobalAccessLogMode   = new ReactiveObject<int>();
                 AudioBackend            = new ReactiveObject<AudioBackend>();
+                ExpandRam               = new ReactiveObject<bool>();
                 IgnoreMissingServices   = new ReactiveObject<bool>();
             }
         }
@@ -433,6 +439,7 @@ namespace Ryujinx.Configuration
                 EnableFsIntegrityChecks   = System.EnableFsIntegrityChecks,
                 FsGlobalAccessLogMode     = System.FsGlobalAccessLogMode,
                 AudioBackend              = System.AudioBackend,
+                ExpandRam                 = System.ExpandRam,
                 IgnoreMissingServices     = System.IgnoreMissingServices,
                 GuiColumns                = new GuiColumns
                 {
@@ -497,6 +504,7 @@ namespace Ryujinx.Configuration
             System.EnableFsIntegrityChecks.Value   = true;
             System.FsGlobalAccessLogMode.Value     = 0;
             System.AudioBackend.Value              = AudioBackend.OpenAl;
+            System.ExpandRam.Value                 = false;
             System.IgnoreMissingServices.Value     = false;
             Ui.GuiColumns.FavColumn.Value          = true;
             Ui.GuiColumns.IconColumn.Value         = true;
@@ -838,6 +846,7 @@ namespace Ryujinx.Configuration
             System.EnableFsIntegrityChecks.Value   = configurationFileFormat.EnableFsIntegrityChecks;
             System.FsGlobalAccessLogMode.Value     = configurationFileFormat.FsGlobalAccessLogMode;
             System.AudioBackend.Value              = configurationFileFormat.AudioBackend;
+            System.ExpandRam.Value                 = configurationFileFormat.ExpandRam;
             System.IgnoreMissingServices.Value     = configurationFileFormat.IgnoreMissingServices;
             Ui.GuiColumns.FavColumn.Value          = configurationFileFormat.GuiColumns.FavColumn;
             Ui.GuiColumns.IconColumn.Value         = configurationFileFormat.GuiColumns.IconColumn;
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index daa8dcc3fb..80f097f415 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -110,9 +110,13 @@ namespace Ryujinx.HLE.HOS
         internal LibHac.Horizon LibHacHorizonServer { get; private set; }
         internal HorizonClient LibHacHorizonClient { get; private set; }
 
-        public Horizon(Switch device, ContentManager contentManager)
+        public Horizon(Switch device, ContentManager contentManager, MemoryConfiguration memoryConfiguration)
         {
-            KernelContext = new KernelContext(device, device.Memory);
+            KernelContext = new KernelContext(
+                device,
+                device.Memory,
+                memoryConfiguration.ToKernelMemorySize(),
+                memoryConfiguration.ToKernelMemoryArrange());
 
             Device = device;
 
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs b/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs
new file mode 100644
index 0000000000..630baacfc2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs
@@ -0,0 +1,72 @@
+using Ryujinx.HLE.HOS.Kernel.Memory;
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+    static class KSystemControl
+    {
+        private const ulong Kb = 1024;
+        private const ulong Mb = 1024 * Kb;
+        private const ulong Gb = 1024 * Mb;
+
+        private const ulong PageSize = 4 * Kb;
+
+        private const ulong RequiredNonSecureSystemPoolSizeVi = 0x2238 * PageSize;
+        private const ulong RequiredNonSecureSystemPoolSizeNvservices = 0x710 * PageSize;
+        private const ulong RequiredNonSecureSystemPoolSizeOther = 0x80 * PageSize;
+
+        private const ulong RequiredNonSecureSystemPoolSize =
+            RequiredNonSecureSystemPoolSizeVi +
+            RequiredNonSecureSystemPoolSizeNvservices +
+            RequiredNonSecureSystemPoolSizeOther;
+
+        public static ulong GetApplicationPoolSize(MemoryArrange arrange)
+        {
+            return arrange switch
+            {
+                MemoryArrange.MemoryArrange4GB or
+                MemoryArrange.MemoryArrange4GBSystemDev or
+                MemoryArrange.MemoryArrange6GBAppletDev => 3285 * Mb,
+                MemoryArrange.MemoryArrange4GBAppletDev => 2048 * Mb,
+                MemoryArrange.MemoryArrange6GB or
+                MemoryArrange.MemoryArrange8GB => 4916 * Mb,
+                _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\".")
+            };
+        }
+
+        public static ulong GetAppletPoolSize(MemoryArrange arrange)
+        {
+            return arrange switch
+            {
+                MemoryArrange.MemoryArrange4GB => 507 * Mb,
+                MemoryArrange.MemoryArrange4GBAppletDev => 1554 * Mb,
+                MemoryArrange.MemoryArrange4GBSystemDev => 448 * Mb,
+                MemoryArrange.MemoryArrange6GB => 562 * Mb,
+                MemoryArrange.MemoryArrange6GBAppletDev or
+                MemoryArrange.MemoryArrange8GB => 2193 * Mb,
+                _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\".")
+            };
+        }
+
+        public static ulong GetMinimumNonSecureSystemPoolSize()
+        {
+            return RequiredNonSecureSystemPoolSize;
+        }
+
+        public static ulong GetDramEndAddress(MemorySize size)
+        {
+            return DramMemoryMap.DramBase + GetDramSize(size);
+        }
+
+        public static ulong GetDramSize(MemorySize size)
+        {
+            return size switch
+            {
+                MemorySize.MemorySize4GB => 4 * Gb,
+                MemorySize.MemorySize6GB => 6 * Gb,
+                MemorySize.MemorySize8GB => 8 * Gb,
+                _ => throw new ArgumentException($"Invalid memory size \"{size}\".")
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs b/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
index bbb75f182d..1949df311c 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
@@ -5,7 +5,21 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
 {
     static class KernelInit
     {
-        public static void InitializeResourceLimit(KResourceLimit resourceLimit)
+        private struct MemoryRegion
+        {
+            public ulong Address { get; }
+            public ulong Size    { get; }
+
+            public ulong EndAddress => Address + Size;
+
+            public MemoryRegion(ulong address, ulong size)
+            {
+                Address = address;
+                Size    = size;
+            }
+        }
+
+        public static void InitializeResourceLimit(KResourceLimit resourceLimit, MemorySize size)
         {
             void EnsureSuccess(KernelResult result)
             {
@@ -15,11 +29,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
                 }
             }
 
-            int kernelMemoryCfg = 0;
+            ulong ramSize = KSystemControl.GetDramSize(size);
 
-            long ramSize = GetRamSize(kernelMemoryCfg);
-
-            EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Memory,         ramSize));
+            EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Memory,         (long)ramSize));
             EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Thread,         800));
             EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Event,          700));
             EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.TransferMemory, 200));
@@ -32,106 +44,45 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
             }
         }
 
-        public static KMemoryRegionManager[] GetMemoryRegions()
+        public static KMemoryRegionManager[] GetMemoryRegions(MemorySize size, MemoryArrange arrange)
         {
-            KMemoryArrange arrange = GetMemoryArrange();
+            ulong poolEnd             = KSystemControl.GetDramEndAddress(size);
+            ulong applicationPoolSize = KSystemControl.GetApplicationPoolSize(arrange);
+            ulong appletPoolSize      = KSystemControl.GetAppletPoolSize(arrange);
 
-            return new KMemoryRegionManager[]
-            {
-                GetMemoryRegion(arrange.Application),
-                GetMemoryRegion(arrange.Applet),
-                GetMemoryRegion(arrange.Service),
-                GetMemoryRegion(arrange.NvServices)
-            };
-        }
+            MemoryRegion servicePool;
+            MemoryRegion nvServicesPool;
+            MemoryRegion appletPool;
+            MemoryRegion applicationPool;
 
-        private static KMemoryRegionManager GetMemoryRegion(KMemoryArrangeRegion region)
-        {
-            return new KMemoryRegionManager(region.Address, region.Size, region.EndAddr);
-        }
+            ulong nvServicesPoolSize = KSystemControl.GetMinimumNonSecureSystemPoolSize();
 
-        private static KMemoryArrange GetMemoryArrange()
-        {
-            int mcEmemCfg = 0x1000;
+            applicationPool = new MemoryRegion(poolEnd - applicationPoolSize, applicationPoolSize);
 
-            ulong ememApertureSize = (ulong)(mcEmemCfg & 0x3fff) << 20;
+            ulong nvServicesPoolEnd = applicationPool.Address - appletPoolSize;
 
-            int kernelMemoryCfg = 0;
-
-            ulong ramSize = (ulong)GetRamSize(kernelMemoryCfg);
-
-            ulong ramPart0;
-            ulong ramPart1;
-
-            if (ramSize * 2 > ememApertureSize)
-            {
-                ramPart0 = ememApertureSize / 2;
-                ramPart1 = ememApertureSize / 2;
-            }
-            else
-            {
-                ramPart0 = ememApertureSize;
-                ramPart1 = 0;
-            }
-
-            int memoryArrange = 1;
-
-            ulong applicationRgSize;
-
-            switch (memoryArrange)
-            {
-                case 2:    applicationRgSize = 0x80000000;  break;
-                case 0x11:
-                case 0x21: applicationRgSize = 0x133400000; break;
-                default:   applicationRgSize = 0xcd500000;  break;
-            }
-
-            ulong appletRgSize;
-
-            switch (memoryArrange)
-            {
-                case 2:    appletRgSize = 0x61200000; break;
-                case 3:    appletRgSize = 0x1c000000; break;
-                case 0x11: appletRgSize = 0x23200000; break;
-                case 0x12:
-                case 0x21: appletRgSize = 0x89100000; break;
-                default:   appletRgSize = 0x1fb00000; break;
-            }
-
-            KMemoryArrangeRegion serviceRg;
-            KMemoryArrangeRegion nvServicesRg;
-            KMemoryArrangeRegion appletRg;
-            KMemoryArrangeRegion applicationRg;
-
-            const ulong nvServicesRgSize = 0x29ba000;
-
-            ulong applicationRgEnd = DramMemoryMap.DramEnd; //- RamPart0;
-
-            applicationRg = new KMemoryArrangeRegion(applicationRgEnd - applicationRgSize, applicationRgSize);
-
-            ulong nvServicesRgEnd = applicationRg.Address - appletRgSize;
-
-            nvServicesRg = new KMemoryArrangeRegion(nvServicesRgEnd - nvServicesRgSize, nvServicesRgSize);
-            appletRg     = new KMemoryArrangeRegion(nvServicesRgEnd, appletRgSize);
+            nvServicesPool = new MemoryRegion(nvServicesPoolEnd - nvServicesPoolSize, nvServicesPoolSize);
+            appletPool     = new MemoryRegion(nvServicesPoolEnd, appletPoolSize);
 
             // Note: There is an extra region used by the kernel, however
             // since we are doing HLE we are not going to use that memory, so give all
             // the remaining memory space to services.
-            ulong serviceRgSize = nvServicesRg.Address - DramMemoryMap.SlabHeapEnd;
+            ulong servicePoolSize = nvServicesPool.Address - DramMemoryMap.SlabHeapEnd;
 
-            serviceRg = new KMemoryArrangeRegion(DramMemoryMap.SlabHeapEnd, serviceRgSize);
+            servicePool = new MemoryRegion(DramMemoryMap.SlabHeapEnd, servicePoolSize);
 
-            return new KMemoryArrange(serviceRg, nvServicesRg, appletRg, applicationRg);
+            return new KMemoryRegionManager[]
+            {
+                GetMemoryRegion(applicationPool),
+                GetMemoryRegion(appletPool),
+                GetMemoryRegion(servicePool),
+                GetMemoryRegion(nvServicesPool)
+            };
         }
 
-        private static long GetRamSize(int kernelMemoryCfg)
+        private static KMemoryRegionManager GetMemoryRegion(MemoryRegion region)
         {
-            switch ((kernelMemoryCfg >> 16) & 3)
-            {
-                case 1:  return 0x180000000;
-                case 2:  return 0x200000000;
-                default: return 0x100000000;
-            }
+            return new KMemoryRegionManager(region.Address, region.Size, region.EndAddress);
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs b/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs
new file mode 100644
index 0000000000..136b0240b2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+    enum MemoryArrange : byte
+    {
+        MemoryArrange4GB,
+        MemoryArrange4GBAppletDev,
+        MemoryArrange4GBSystemDev,
+        MemoryArrange6GB,
+        MemoryArrange6GBAppletDev,
+        MemoryArrange8GB
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs b/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs
new file mode 100644
index 0000000000..1148b0f404
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+    enum MemorySize : byte
+    {
+        MemorySize4GB = 0,
+        MemorySize6GB = 1,
+        MemorySize8GB = 2
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs b/Ryujinx.HLE/HOS/Kernel/KernelContext.cs
index cacb7fb3b6..b57b950473 100644
--- a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KernelContext.cs
@@ -51,7 +51,11 @@ namespace Ryujinx.HLE.HOS.Kernel
         private long _processId;
         private long _threadUid;
 
-        public KernelContext(Switch device, MemoryBlock memory)
+        public KernelContext(
+            Switch device,
+            MemoryBlock memory,
+            MemorySize memorySize,
+            MemoryArrange memoryArrange)
         {
             Device = device;
             Memory = memory;
@@ -64,9 +68,9 @@ namespace Ryujinx.HLE.HOS.Kernel
 
             ResourceLimit = new KResourceLimit(this);
 
-            KernelInit.InitializeResourceLimit(ResourceLimit);
+            KernelInit.InitializeResourceLimit(ResourceLimit, memorySize);
 
-            MemoryRegions = KernelInit.GetMemoryRegions();
+            MemoryRegions = KernelInit.GetMemoryRegions(memorySize, memoryArrange);
 
             LargeMemoryBlockAllocator = new KMemoryBlockAllocator(KernelConstants.MemoryBlockAllocatorSize * 2);
             SmallMemoryBlockAllocator = new KMemoryBlockAllocator(KernelConstants.MemoryBlockAllocatorSize);
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs b/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs
index 261c2972b3..dea2a4efea 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs
@@ -3,8 +3,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
     static class DramMemoryMap
     {
         public const ulong DramBase = 0x80000000;
-        public const ulong DramSize = 0x100000000;
-        public const ulong DramEnd  = DramBase + DramSize;
 
         public const ulong KernelReserveBase = DramBase + 0x60000;
 
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryArrange.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryArrange.cs
deleted file mode 100644
index 7dfc2b7780..0000000000
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryArrange.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace Ryujinx.HLE.HOS.Kernel.Memory
-{
-    class KMemoryArrange
-    {
-        public KMemoryArrangeRegion Service     { get; private set; }
-        public KMemoryArrangeRegion NvServices  { get; private set; }
-        public KMemoryArrangeRegion Applet      { get; private set; }
-        public KMemoryArrangeRegion Application { get; private set; }
-
-        public KMemoryArrange(
-            KMemoryArrangeRegion service,
-            KMemoryArrangeRegion nvServices,
-            KMemoryArrangeRegion applet,
-            KMemoryArrangeRegion application)
-        {
-            Service     = service;
-            NvServices  = nvServices;
-            Applet      = applet;
-            Application = application;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryArrangeRegion.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryArrangeRegion.cs
deleted file mode 100644
index eaf0fe5fb8..0000000000
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryArrangeRegion.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace Ryujinx.HLE.HOS.Kernel.Memory
-{
-    struct KMemoryArrangeRegion
-    {
-        public ulong Address { get; private set; }
-        public ulong Size    { get; private set; }
-
-        public ulong EndAddr => Address + Size;
-
-        public KMemoryArrangeRegion(ulong address, ulong size)
-        {
-            Address = address;
-            Size    = size;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/MemoryConfiguration.cs b/Ryujinx.HLE/MemoryConfiguration.cs
new file mode 100644
index 0000000000..2a59e04f22
--- /dev/null
+++ b/Ryujinx.HLE/MemoryConfiguration.cs
@@ -0,0 +1,62 @@
+using Ryujinx.HLE.HOS.Kernel.Common;
+using System;
+
+namespace Ryujinx.HLE
+{
+    public enum MemoryConfiguration
+    {
+        MemoryConfiguration4GB = 0,
+        MemoryConfiguration4GBAppletDev = 1,
+        MemoryConfiguration4GBSystemDev = 2,
+        MemoryConfiguration6GB = 3,
+        MemoryConfiguration6GBAppletDev = 4,
+        MemoryConfiguration8GB = 5
+    }
+
+    static class MemoryConfigurationExtensions
+    {
+        private const ulong Gb = 1024 * 1024 * 1024;
+
+        public static MemoryArrange ToKernelMemoryArrange(this MemoryConfiguration configuration)
+        {
+            return configuration switch
+            {
+                MemoryConfiguration.MemoryConfiguration4GB => MemoryArrange.MemoryArrange4GB,
+                MemoryConfiguration.MemoryConfiguration4GBAppletDev => MemoryArrange.MemoryArrange4GBAppletDev,
+                MemoryConfiguration.MemoryConfiguration4GBSystemDev => MemoryArrange.MemoryArrange4GBSystemDev,
+                MemoryConfiguration.MemoryConfiguration6GB => MemoryArrange.MemoryArrange6GB,
+                MemoryConfiguration.MemoryConfiguration6GBAppletDev => MemoryArrange.MemoryArrange6GBAppletDev,
+                MemoryConfiguration.MemoryConfiguration8GB => MemoryArrange.MemoryArrange8GB,
+                _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\".")
+            };
+        }
+
+        public static MemorySize ToKernelMemorySize(this MemoryConfiguration configuration)
+        {
+            return configuration switch
+            {
+                MemoryConfiguration.MemoryConfiguration4GB or
+                MemoryConfiguration.MemoryConfiguration4GBAppletDev or
+                MemoryConfiguration.MemoryConfiguration4GBSystemDev => MemorySize.MemorySize4GB,
+                MemoryConfiguration.MemoryConfiguration6GB or
+                MemoryConfiguration.MemoryConfiguration6GBAppletDev => MemorySize.MemorySize6GB,
+                MemoryConfiguration.MemoryConfiguration8GB => MemorySize.MemorySize8GB,
+                _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\".")
+            };
+        }
+
+        public static ulong ToDramSize(this MemoryConfiguration configuration)
+        {
+            return configuration switch
+            {
+                MemoryConfiguration.MemoryConfiguration4GB or
+                MemoryConfiguration.MemoryConfiguration4GBAppletDev or
+                MemoryConfiguration.MemoryConfiguration4GBSystemDev => 4 * Gb,
+                MemoryConfiguration.MemoryConfiguration6GB or
+                MemoryConfiguration.MemoryConfiguration6GBAppletDev => 6 * Gb,
+                MemoryConfiguration.MemoryConfiguration8GB => 8 * Gb,
+                _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\".")
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 61c0776957..6770b25e51 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -24,6 +24,8 @@ namespace Ryujinx.HLE
 {
     public class Switch : IDisposable
     {
+        private MemoryConfiguration _memoryConfiguration;
+
         public IHardwareDeviceDriver AudioDeviceDriver { get; private set; }
 
         internal MemoryBlock Memory { get; private set; }
@@ -52,7 +54,13 @@ namespace Ryujinx.HLE
 
         public bool EnableDeviceVsync { get; set; } = true;
 
-        public Switch(VirtualFileSystem fileSystem, ContentManager contentManager, UserChannelPersistence userChannelPersistence, IRenderer renderer, IHardwareDeviceDriver audioDeviceDriver)
+        public Switch(
+            VirtualFileSystem fileSystem,
+            ContentManager contentManager,
+            UserChannelPersistence userChannelPersistence,
+            IRenderer renderer,
+            IHardwareDeviceDriver audioDeviceDriver,
+            MemoryConfiguration memoryConfiguration)
         {
             if (renderer == null)
             {
@@ -71,9 +79,11 @@ namespace Ryujinx.HLE
 
             UserChannelPersistence = userChannelPersistence;
 
+            _memoryConfiguration = memoryConfiguration;
+
             AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(audioDeviceDriver);
 
-            Memory = new MemoryBlock(1UL << 32);
+            Memory = new MemoryBlock(memoryConfiguration.ToDramSize());
 
             Gpu = new GpuContext(renderer);
 
@@ -102,7 +112,7 @@ namespace Ryujinx.HLE
 
             FileSystem = fileSystem;
 
-            System = new Horizon(this, contentManager);
+            System = new Horizon(this, contentManager, memoryConfiguration);
             System.InitializeServices();
 
             Statistics = new PerformanceStatistics();
@@ -146,6 +156,7 @@ namespace Ryujinx.HLE
             Logger.Info?.Print(LogClass.Application, $"AudioBackend: {ConfigurationState.Instance.System.AudioBackend.Value}");
             Logger.Info?.Print(LogClass.Application, $"IsDocked: {ConfigurationState.Instance.System.EnableDockedMode.Value}");
             Logger.Info?.Print(LogClass.Application, $"Vsync: {ConfigurationState.Instance.Graphics.EnableVsync.Value}");
+            Logger.Info?.Print(LogClass.Application, $"MemoryConfiguration: {_memoryConfiguration}");
         }
 
         public static IntegrityCheckLevel GetIntegrityCheckLevel()
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index 1fa52d323f..369c6238d0 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -337,7 +337,17 @@ namespace Ryujinx.Ui
                 }
             }
 
-            _emulationContext = new HLE.Switch(_virtualFileSystem, _contentManager, _userChannelPersistence, renderer, deviceDriver)
+            var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value
+                ? HLE.MemoryConfiguration.MemoryConfiguration6GB
+                : HLE.MemoryConfiguration.MemoryConfiguration4GB;
+
+            _emulationContext = new HLE.Switch(
+                _virtualFileSystem,
+                _contentManager,
+                _userChannelPersistence,
+                renderer,
+                deviceDriver,
+                memoryConfiguration)
             {
                 UiHandler = _uiHandler
             };
@@ -664,7 +674,7 @@ namespace Ryujinx.Ui
 
                 GlRendererWidget.Exit();
 
-                if(GlRendererWidget.Window != Window && GlRendererWidget.Window != null)
+                if (GlRendererWidget.Window != Window && GlRendererWidget.Window != null)
                 {
                     GlRendererWidget.Window.Dispose();
                 }
@@ -1061,7 +1071,7 @@ namespace Ryujinx.Ui
                     if (responseInstallDialog == ResponseType.Yes)
                     {
                         Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}");
-                        
+
                         Thread thread = new Thread(() =>
                         {
                             Application.Invoke(delegate
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs
index 17d4c961f0..d4d680909d 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.cs
+++ b/Ryujinx/Ui/Windows/SettingsWindow.cs
@@ -51,6 +51,7 @@ namespace Ryujinx.Ui.Windows
         [GUI] CheckButton     _shaderCacheToggle;
         [GUI] CheckButton     _ptcToggle;
         [GUI] CheckButton     _fsicToggle;
+        [GUI] CheckButton     _expandRamToggle;
         [GUI] CheckButton     _ignoreToggle;
         [GUI] CheckButton     _directKeyboardAccess;
         [GUI] ComboBoxText    _systemLanguageSelect;
@@ -214,6 +215,11 @@ namespace Ryujinx.Ui.Windows
                 _fsicToggle.Click();
             }
 
+            if (ConfigurationState.Instance.System.ExpandRam)
+            {
+                _expandRamToggle.Click();
+            }
+
             if (ConfigurationState.Instance.System.IgnoreMissingServices)
             {
                 _ignoreToggle.Click();
@@ -417,6 +423,7 @@ namespace Ryujinx.Ui.Windows
             ConfigurationState.Instance.Graphics.EnableShaderCache.Value       = _shaderCacheToggle.Active;
             ConfigurationState.Instance.System.EnablePtc.Value                 = _ptcToggle.Active;
             ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value   = _fsicToggle.Active;
+            ConfigurationState.Instance.System.ExpandRam.Value                 = _expandRamToggle.Active;
             ConfigurationState.Instance.System.IgnoreMissingServices.Value     = _ignoreToggle.Active;
             ConfigurationState.Instance.Hid.EnableKeyboard.Value               = _directKeyboardAccess.Active;
             ConfigurationState.Instance.Ui.EnableCustomTheme.Value             = _custThemeToggle.Active;
@@ -436,7 +443,7 @@ namespace Ryujinx.Ui.Windows
             {
                 AudioBackend audioBackend = (AudioBackend)_audioBackendStore.GetValue(activeIter, 1);
                 if (audioBackend != ConfigurationState.Instance.System.AudioBackend.Value)
-                { 
+                {
                     ConfigurationState.Instance.System.AudioBackend.Value = audioBackend;
 
                     Logger.Info?.Print(LogClass.Application, $"AudioBackend toggled to: {audioBackend}");
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade
index e9d241f80b..3dc0bdce8a 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.glade
+++ b/Ryujinx/Ui/Windows/SettingsWindow.glade
@@ -1625,6 +1625,24 @@
                                 <property name="margin-left">10</property>
                                 <property name="margin-right">10</property>
                                 <property name="orientation">vertical</property>
+                                <child>
+                                  <object class="GtkCheckButton" id="_expandRamToggle">
+                                    <property name="label" translatable="yes">Expand DRAM size to 6GB</property>
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">True</property>
+                                    <property name="receives-default">False</property>
+                                    <property name="tooltip-text" translatable="yes">Expands the amount of memory on the emulated system from 4GB to 6GB</property>
+                                    <property name="halign">start</property>
+                                    <property name="margin-top">5</property>
+                                    <property name="margin-bottom">5</property>
+                                    <property name="draw-indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
                                 <child>
                                   <object class="GtkCheckButton" id="_ignoreToggle">
                                     <property name="label" translatable="yes">Ignore Missing Services</property>
@@ -1640,7 +1658,7 @@
                                   <packing>
                                     <property name="expand">False</property>
                                     <property name="fill">True</property>
-                                    <property name="position">0</property>
+                                    <property name="position">1</property>
                                   </packing>
                                 </child>
                               </object>