kern: support 2-pool layout on 2.x-4.x instead of modern 4-pool layout

This commit is contained in:
Michael Scire 2020-08-25 18:10:58 -07:00 committed by SciresM
parent ce95af89ef
commit e8ffbe630f
7 changed files with 204 additions and 92 deletions

View file

@ -34,5 +34,6 @@ namespace ams::kern {
u64 GetInitialProcessIdMin();
u64 GetInitialProcessIdMax();
size_t GetInitialProcessesSecureMemorySize();
}

View file

@ -469,7 +469,7 @@ namespace ams::kern::board::nintendo::nx {
KSleepManager::Initialize();
/* Reserve secure applet memory. */
{
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address == Null<KVirtualAddress>);
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, SecureAppletMemorySize));
@ -486,7 +486,7 @@ namespace ams::kern::board::nintendo::nx {
}
u32 KSystemControl::GetInitialProcessBinaryPool() {
return KMemoryManager::Pool_Application;
return KMemoryManager::Pool_Unsafe;
}
/* Privileged Access. */

View file

@ -38,6 +38,30 @@ namespace ams::kern {
MESOSPHERE_ABORT_UNLESS(header->num_processes <= init::GetSlabResourceCounts().num_KProcess);
}
size_t GetProcessesSecureMemorySize(KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) {
u8 *current = GetPointer<u8>(binary_address + sizeof(InitialProcessBinaryHeader));
const u8 * const end = GetPointer<u8>(binary_address + header.size - sizeof(KInitialProcessHeader));
size_t size = 0;
const size_t num_processes = header.num_processes;
for (size_t i = 0; i < num_processes; i++) {
/* Validate that we can read the current KIP. */
MESOSPHERE_ABORT_UNLESS(current <= end);
KInitialProcessReader reader;
MESOSPHERE_ABORT_UNLESS(reader.Attach(current));
/* If the process uses secure memory, account for that. */
if (reader.UsesSecureMemory()) {
size += util::AlignUp(reader.GetSize(), PageSize);
}
/* Advance the reader. */
current += reader.GetBinarySize();
}
return size;
}
void CreateProcesses(InitialProcessInfo *infos, KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) {
u8 *current = GetPointer<u8>(binary_address + sizeof(InitialProcessBinaryHeader));
const u8 * const end = GetPointer<u8>(binary_address + header.size - sizeof(KInitialProcessHeader));
@ -108,10 +132,10 @@ namespace ams::kern {
}
}
KVirtualAddress g_initial_process_binary_address;
InitialProcessBinaryHeader g_initial_process_binary_header;
u64 g_initial_process_id_min = std::numeric_limits<u64>::max();
u64 g_initial_process_id_max = std::numeric_limits<u64>::min();
constinit KVirtualAddress g_initial_process_binary_address = Null<KVirtualAddress>;
constinit InitialProcessBinaryHeader g_initial_process_binary_header = {};
constinit u64 g_initial_process_id_min = std::numeric_limits<u64>::max();
constinit u64 g_initial_process_id_max = std::numeric_limits<u64>::min();
}
@ -123,6 +147,12 @@ namespace ams::kern {
return g_initial_process_id_max;
}
size_t GetInitialProcessesSecureMemorySize() {
LoadInitialProcessBinaryHeader(&g_initial_process_binary_header);
return GetProcessesSecureMemorySize(g_initial_process_binary_address != Null<KVirtualAddress> ? g_initial_process_binary_address : GetInitialProcessBinaryAddress(), g_initial_process_binary_header);
}
void CopyInitialProcessBinaryToKernelMemory() {
LoadInitialProcessBinaryHeader(&g_initial_process_binary_header);

View file

@ -59,19 +59,37 @@ namespace ams::kern {
namespace init {
namespace {
void SetupDevicePhysicalMemoryRegions() {
/* TODO: Give these constexpr defines somewhere? */
MESOSPHERE_INIT_ABORT_UNLESS(SetupUartPhysicalMemoryRegion());
MESOSPHERE_INIT_ABORT_UNLESS(SetupPowerManagementControllerMemoryRegion());
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50041000, 0x1000, KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50042000, 0x1000, KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
}
void SetupPoolPartitionMemoryRegionsImpl() {
void SetupDramPhysicalMemoryRegions() {
const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
const KPhysicalAddress physical_memory_base_address = KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress);
/* Insert blocks into the tree. */
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), intended_memory_size, KMemoryRegionType_Dram));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly));
}
void SetupPoolPartitionMemoryRegions() {
/* Start by identifying the extents of the DRAM memory region. */
const auto dram_extents = KMemoryLayout::GetMainMemoryPhysicalExtents();
/* Determine the end of the pool region. */
const uintptr_t pool_end = dram_extents.GetEndAddress() - KTraceBufferSize;
/* Get Application and Applet pool sizes. */
const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize();
const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize();
const size_t unsafe_system_pool_min_size = KSystemControl::Init::GetMinimumNonSecureSystemPoolSize();
/* Find the start of the kernel DRAM region. */
const KMemoryRegion *kernel_dram_region = KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramKernelBase);
MESOSPHERE_INIT_ABORT_UNLESS(kernel_dram_region != nullptr);
@ -84,6 +102,15 @@ namespace ams::kern {
MESOSPHERE_INIT_ABORT_UNLESS(pool_partitions_region != nullptr);
const uintptr_t pool_partitions_start = pool_partitions_region->GetAddress();
/* Setup the pool partition layouts. */
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
/* On 5.0.0+, setup modern 4-pool-partition layout. */
/* Get Application and Applet pool sizes. */
const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize();
const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize();
const size_t unsafe_system_pool_min_size = KSystemControl::Init::GetMinimumNonSecureSystemPoolSize();
/* Decide on starting addresses for our pools. */
const uintptr_t application_pool_start = pool_end - application_pool_size;
const uintptr_t applet_pool_start = application_pool_start - applet_pool_size;
@ -124,46 +151,67 @@ namespace ams::kern {
/* Insert the system pool. */
const uintptr_t system_pool_size = pool_management_start - pool_partitions_start;
InsertPoolPartitionRegionIntoBothTrees(pool_partitions_start, system_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
} else if (GetTargetFirmware() >= TargetFirmware_2_0_0) {
/* On < 5.0.0, setup a legacy 2-pool layout for backwards compatibility. */
static_assert(KMemoryManager::Pool_Count == 4);
static_assert(KMemoryManager::Pool_Unsafe == KMemoryManager::Pool_Application);
static_assert(KMemoryManager::Pool_Secure == KMemoryManager::Pool_System);
/* Get Secure pool size. */
constexpr size_t LegacySecureKernelSize = 6_MB; /* KPageBuffer pages, other small kernel allocations. */
constexpr size_t LegacySecureMiscSize = 1_MB; /* Miscellaneous pages for secure process mapping. */
constexpr size_t LegacySecureHeapSize = 24_MB; /* Heap pages for secure process mapping (fs). */
constexpr size_t LegacySecureEsSize = 1_MB + 232_KB; /* Size for additional secure process (es, 4.0.0+). */
const size_t secure_pool_size = GetInitialProcessesSecureMemorySize() + LegacySecureKernelSize + LegacySecureHeapSize + LegacySecureMiscSize + (GetTargetFirmware() >= TargetFirmware_4_0_0 ? LegacySecureEsSize : 0);
/* Calculate the overhead for the secure and (defunct) applet/non-secure-system pools. */
size_t total_overhead_size = KMemoryManager::CalculateManagementOverheadSize(secure_pool_size);
/* Calculate the overhead for (an amount larger than) the unsafe pool. */
const size_t approximate_total_overhead_size = total_overhead_size + KMemoryManager::CalculateManagementOverheadSize((pool_end - pool_partitions_start) - secure_pool_size - total_overhead_size) + 2 * PageSize;
/* Determine the start of the unsafe region. */
const uintptr_t unsafe_memory_start = util::AlignUp(pool_partitions_start + secure_pool_size + approximate_total_overhead_size, CarveoutAlignment);
/* Determine the start of the pool regions. */
const uintptr_t application_pool_start = unsafe_memory_start;
/* Determine the pool sizes. */
const size_t application_pool_size = pool_end - application_pool_start;
/* We want to arrange application pool depending on where the middle of dram is. */
const uintptr_t dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2;
u32 cur_pool_attr = 0;
if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) {
InsertPoolPartitionRegionIntoBothTrees(application_pool_start, application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(application_pool_size);
} else {
const size_t first_application_pool_size = dram_midpoint - application_pool_start;
const size_t second_application_pool_size = application_pool_start + application_pool_size - dram_midpoint;
InsertPoolPartitionRegionIntoBothTrees(application_pool_start, first_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
InsertPoolPartitionRegionIntoBothTrees(dram_midpoint, second_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size);
}
void SetupPoolPartitionMemoryRegionsDeprecatedImpl() {
/* Insert the secure pool. */
InsertPoolPartitionRegionIntoBothTrees(pool_partitions_start, secure_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
/* Insert the pool management region. */
MESOSPHERE_INIT_ABORT_UNLESS(total_overhead_size <= approximate_total_overhead_size);
const uintptr_t pool_management_start = pool_partitions_start + secure_pool_size;
const size_t pool_management_size = unsafe_memory_start - pool_management_start;
MESOSPHERE_INIT_ABORT_UNLESS(total_overhead_size <= pool_management_size);
u32 pool_management_attr = 0;
InsertPoolPartitionRegionIntoBothTrees(pool_management_start, pool_management_size, KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement, pool_management_attr);
} else {
/* TODO: 1.0.0 single-pool layout. */
MESOSPHERE_UNIMPLEMENTED();
}
}
void SetupDevicePhysicalMemoryRegions() {
/* TODO: Give these constexpr defines somewhere? */
MESOSPHERE_INIT_ABORT_UNLESS(SetupUartPhysicalMemoryRegion());
MESOSPHERE_INIT_ABORT_UNLESS(SetupPowerManagementControllerMemoryRegion());
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50041000, 0x1000, KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50042000, 0x1000, KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
}
void SetupDramPhysicalMemoryRegions() {
const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
const KPhysicalAddress physical_memory_base_address = KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress);
/* Insert blocks into the tree. */
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), intended_memory_size, KMemoryRegionType_Dram));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly));
}
void SetupPoolPartitionMemoryRegions() {
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
/* On 5.0.0+, setup modern 4-pool-partition layout. */
SetupPoolPartitionMemoryRegionsImpl();
} else {
/* On < 5.0.0, setup a legacy 2-pool layout for backwards compatibility. */
SetupPoolPartitionMemoryRegionsDeprecatedImpl();
}
}
}

View file

@ -152,11 +152,17 @@ namespace ams::kern {
PrintMemoryRegion(" PageTableHeap", KMemoryLayout::GetKernelPageTableHeapRegionPhysicalExtents());
PrintMemoryRegion(" InitPageTable", KMemoryLayout::GetKernelInitPageTableRegionPhysicalExtents());
PrintMemoryRegion(" MemoryPoolRegion", KMemoryLayout::GetKernelPoolPartitionRegionPhysicalExtents());
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
PrintMemoryRegion(" System", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents());
PrintMemoryRegion(" Management", KMemoryLayout::GetKernelPoolManagementRegionPhysicalExtents());
PrintMemoryRegion(" SystemUnsafe", KMemoryLayout::GetKernelSystemNonSecurePoolRegionPhysicalExtents());
PrintMemoryRegion(" Applet", KMemoryLayout::GetKernelAppletPoolRegionPhysicalExtents());
PrintMemoryRegion(" Application", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents());
} else {
PrintMemoryRegion(" Secure", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents());
PrintMemoryRegion(" Management", KMemoryLayout::GetKernelPoolManagementRegionPhysicalExtents());
PrintMemoryRegion(" Unsafe", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents());
}
if constexpr (IsKTraceEnabled) {
MESOSPHERE_LOG(" Debug\n");
PrintMemoryRegion(" Trace Buffer", KMemoryLayout::GetKernelTraceBufferRegionPhysicalExtents());

View file

@ -131,7 +131,7 @@ namespace ams::kern::svc {
}
/* Validate the pool partition. */
/* TODO: 4.0.0 UseSecureMemory flag, pre-4.0.0 behavior. */
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
switch (params.flags & ams::svc::CreateProcessFlag_PoolPartitionMask) {
case ams::svc::CreateProcessFlag_PoolPartitionApplication:
case ams::svc::CreateProcessFlag_PoolPartitionApplet:
@ -141,6 +141,7 @@ namespace ams::kern::svc {
default:
return svc::ResultInvalidEnumValue();
}
}
/* Check that the code address is aligned. */
R_UNLESS(util::IsAligned(params.code_address, KProcess::AslrAlignment), svc::ResultInvalidAddress());
@ -201,23 +202,30 @@ namespace ams::kern::svc {
KResourceLimit *process_resource_limit = resource_limit.IsNotNull() ? resource_limit.GetPointerUnsafe() : std::addressof(Kernel::GetSystemResourceLimit());
/* Get the pool for the process. */
/* TODO: 4.0.0 UseSecureMemory flag, pre-4.0.0 behavior. */
KMemoryManager::Pool pool;
switch (params.flags & ams::svc::CreateProcessFlag_PoolPartitionMask) {
const auto pool = [] ALWAYS_INLINE_LAMBDA (u32 flags) -> KMemoryManager::Pool {
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
switch (flags & ams::svc::CreateProcessFlag_PoolPartitionMask) {
case ams::svc::CreateProcessFlag_PoolPartitionApplication:
pool = KMemoryManager::Pool_Application;
break;
return KMemoryManager::Pool_Application;
case ams::svc::CreateProcessFlag_PoolPartitionApplet:
pool = KMemoryManager::Pool_Applet;
break;
return KMemoryManager::Pool_Applet;
case ams::svc::CreateProcessFlag_PoolPartitionSystem:
pool = KMemoryManager::Pool_System;
break;
return KMemoryManager::Pool_System;
case ams::svc::CreateProcessFlag_PoolPartitionSystemNonSecure:
default:
pool = KMemoryManager::Pool_SystemNonSecure;
break;
return KMemoryManager::Pool_SystemNonSecure;
}
} else if (GetTargetFirmware() >= TargetFirmware_4_0_0) {
if ((flags & ams::svc::CreateProcessFlag_DeprecatedUseSecureMemory) != 0) {
return KMemoryManager::Pool_Secure;
} else {
return KMemoryManager::Pool_Unsafe;
}
} else {
return KMemoryManager::Pool_Unsafe;
}
}(params.flags);
/* Initialize the process. */
R_TRY(process->Initialize(params, user_caps, num_caps, process_resource_limit, pool));

View file

@ -168,6 +168,17 @@ namespace ams::pm::resource {
}
}
bool IsKTraceEnabled() {
if (!svc::IsKernelMesosphere()) {
return false;
}
u64 value = 0;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_MesosphereMeta, INVALID_HANDLE, svc::MesosphereMetaInfo_IsKTraceEnabled));
return value != 0;
}
}
/* Resource API. */
@ -265,6 +276,14 @@ namespace ams::pm::resource {
g_memory_resource_limits[i][ResourceLimitGroup_System] += extra_memory_size;
g_memory_resource_limits[i][src_group] -= extra_memory_size;
}
/* If KTrace is enabled, account for that by subtracting the memory from the applet pool. */
if (IsKTraceEnabled()) {
constexpr size_t KTraceBufferSize = 16_MB;
for (size_t i = 0; i < spl::MemoryArrangement_Count; i++) {
g_memory_resource_limits[i][ResourceLimitGroup_Applet] -= KTraceBufferSize;
}
}
}
/* Actually set resource limits. */