summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/config_mem.cpp4
-rw-r--r--src/core/hle/hle.cpp2
-rw-r--r--src/core/hle/kernel/kernel.cpp19
-rw-r--r--src/core/hle/kernel/memory.cpp72
-rw-r--r--src/core/hle/kernel/memory.h17
-rw-r--r--src/core/hle/kernel/process.cpp44
-rw-r--r--src/core/hle/kernel/process.h6
-rw-r--r--src/core/hle/svc.cpp2
-rw-r--r--src/core/memory.cpp5
-rw-r--r--src/core/memory.h5
10 files changed, 148 insertions, 28 deletions
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp
index 94bca0378..b1a72dc0c 100644
--- a/src/core/hle/config_mem.cpp
+++ b/src/core/hle/config_mem.cpp
@@ -25,10 +25,6 @@ void Init() {
config_mem.sys_core_ver = 0x2;
config_mem.unit_info = 0x1; // Bit 0 set for Retail
config_mem.prev_firm = 0;
- config_mem.app_mem_type = 0x2; // Default app mem type is 0
- config_mem.app_mem_alloc = 0x06000000; // Set to 96MB, since some games use more than the default (64MB)
- config_mem.base_mem_alloc = 0x01400000; // Default base memory is 20MB
- config_mem.sys_mem_alloc = Memory::FCRAM_SIZE - (config_mem.app_mem_alloc + config_mem.base_mem_alloc);
config_mem.firm_unk = 0;
config_mem.firm_version_rev = 0;
config_mem.firm_version_min = 0x40;
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index 98dc8dd58..331b1b22a 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -34,8 +34,6 @@ void Reschedule(const char *reason) {
void Init() {
Service::Init();
- ConfigMem::Init();
- SharedPage::Init();
g_reschedule = false;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 5711c0405..7a401a965 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -7,11 +7,14 @@
#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/hle/config_mem.h"
#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
+#include "core/hle/shared_page.h"
namespace Kernel {
@@ -119,6 +122,13 @@ void HandleTable::Clear() {
/// Initialize the kernel
void Init() {
+ ConfigMem::Init();
+ SharedPage::Init();
+
+ // TODO(yuriks): The memory type parameter needs to be determined by the ExHeader field instead
+ // For now it defaults to the one with a largest allocation to the app
+ Kernel::MemoryInit(2); // Allocates 96MB to the application
+
Kernel::ResourceLimitsInit();
Kernel::ThreadingInit();
Kernel::TimersInit();
@@ -131,11 +141,14 @@ void Init() {
/// Shutdown the kernel
void Shutdown() {
+ g_handle_table.Clear(); // Free all kernel objects
+
Kernel::ThreadingShutdown();
+ g_current_process = nullptr;
+
Kernel::TimersShutdown();
Kernel::ResourceLimitsShutdown();
- g_handle_table.Clear(); // Free all kernel objects
- g_current_process = nullptr;
+ Kernel::MemoryShutdown();
}
} // namespace
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 57e1912d3..e69b121eb 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -11,6 +11,7 @@
#include "common/logging/log.h"
#include "core/hle/config_mem.h"
+#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/result.h"
#include "core/hle/shared_page.h"
@@ -19,6 +20,77 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace Kernel {
+
+static MemoryRegionInfo memory_regions[3];
+
+/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each sytem
+/// memory configuration type.
+static const u32 memory_region_sizes[8][3] = {
+ // Old 3DS layouts
+ {0x04000000, 0x02C00000, 0x01400000}, // 0
+ { /* This appears to be unused. */ }, // 1
+ {0x06000000, 0x00C00000, 0x01400000}, // 2
+ {0x05000000, 0x01C00000, 0x01400000}, // 3
+ {0x04800000, 0x02400000, 0x01400000}, // 4
+ {0x02000000, 0x04C00000, 0x01400000}, // 5
+
+ // New 3DS layouts
+ {0x07C00000, 0x06400000, 0x02000000}, // 6
+ {0x0B200000, 0x02E00000, 0x02000000}, // 7
+};
+
+void MemoryInit(u32 mem_type) {
+ // TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead.
+ ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!");
+ ASSERT(mem_type != 1);
+
+ // The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with
+ // the sizes specified in the memory_region_sizes table.
+ VAddr base = 0;
+ for (int i = 0; i < 3; ++i) {
+ memory_regions[i].base = base;
+ memory_regions[i].size = memory_region_sizes[mem_type][i];
+ memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>();
+
+ base += memory_regions[i].size;
+ }
+
+ // We must've allocated the entire FCRAM by the end
+ ASSERT(base == Memory::FCRAM_SIZE);
+
+ using ConfigMem::config_mem;
+ config_mem.app_mem_type = mem_type;
+ // app_mem_malloc does not always match the configured size for memory_region[0]: in case the
+ // n3DS type override is in effect it reports the size the game expects, not the real one.
+ config_mem.app_mem_alloc = memory_region_sizes[mem_type][0];
+ config_mem.sys_mem_alloc = memory_regions[1].size;
+ config_mem.base_mem_alloc = memory_regions[2].size;
+}
+
+void MemoryShutdown() {
+ for (auto& region : memory_regions) {
+ region.base = 0;
+ region.size = 0;
+ region.linear_heap_memory = nullptr;
+ }
+}
+
+MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) {
+ switch (region) {
+ case MemoryRegion::APPLICATION:
+ return &memory_regions[0];
+ case MemoryRegion::SYSTEM:
+ return &memory_regions[1];
+ case MemoryRegion::BASE:
+ return &memory_regions[2];
+ default:
+ UNREACHABLE();
+ }
+}
+
+}
+
namespace Memory {
namespace {
diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h
index cba8a0714..2e2cae17d 100644
--- a/src/core/hle/kernel/memory.h
+++ b/src/core/hle/kernel/memory.h
@@ -4,10 +4,27 @@
#pragma once
+#include <memory>
+
#include "common/common_types.h"
+#include "core/hle/kernel/process.h"
+
namespace Kernel {
+
class VMManager;
+
+struct MemoryRegionInfo {
+ u32 base; // Not an address, but offset from start of FCRAM
+ u32 size;
+
+ std::shared_ptr<std::vector<u8>> linear_heap_memory;
+};
+
+void MemoryInit(u32 mem_type);
+void MemoryShutdown();
+MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
+
}
namespace Memory {
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 2cd1cfc14..1f45e6cf8 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -96,7 +96,7 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
int minor = kernel_version & 0xFF;
int major = (kernel_version >> 8) & 0xFF;
- LOG_DEBUG(Loader, "ExHeader kernel version: %d.%d", major, minor);
+ LOG_INFO(Loader, "ExHeader kernel version: %d.%d", major, minor);
} else {
LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x%08X", descriptor);
}
@@ -104,6 +104,8 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
}
void Process::Run(s32 main_thread_priority, u32 stack_size) {
+ memory_region = GetMemoryRegion(flags.memory_region);
+
auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) {
auto vma = vm_manager.MapMemoryBlock(segment.addr, codeset->memory,
segment.offset, segment.size, memory_state).Unwrap();
@@ -124,6 +126,15 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority);
}
+VAddr Process::GetLinearHeapBase() const {
+ return (kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_SIZE)
+ + memory_region->base;
+}
+
+VAddr Process::GetLinearHeapLimit() const {
+ return GetLinearHeapBase() + memory_region->size;
+}
+
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u32 size, VMAPermission perms) {
if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || target + size < target) {
return ERR_INVALID_ADDRESS;
@@ -166,19 +177,16 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
}
ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) {
- if (linear_heap_memory == nullptr) {
- // Initialize heap
- linear_heap_memory = std::make_shared<std::vector<u8>>();
- }
+ auto& linheap_memory = memory_region->linear_heap_memory;
- VAddr heap_end = Memory::LINEAR_HEAP_VADDR + (u32)linear_heap_memory->size();
+ VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size();
// Games and homebrew only ever seem to pass 0 here (which lets the kernel decide the address),
// but explicit addresses are also accepted and respected.
if (target == 0) {
target = heap_end;
}
- if (target < Memory::LINEAR_HEAP_VADDR || target + size > Memory::LINEAR_HEAP_VADDR_END ||
+ if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() ||
target > heap_end || target + size < target) {
return ERR_INVALID_ADDRESS;
@@ -188,25 +196,29 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p
// end. It's possible to free gaps in the middle of the heap and then reallocate them later,
// but expansions are only allowed at the end.
if (target == heap_end) {
- linear_heap_memory->insert(linear_heap_memory->end(), size, 0);
- vm_manager.RefreshMemoryBlockMappings(linear_heap_memory.get());
+ linheap_memory->insert(linheap_memory->end(), size, 0);
+ vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
}
- size_t offset = target - Memory::LINEAR_HEAP_VADDR;
- CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, linear_heap_memory, offset, size, MemoryState::Continuous));
+ // TODO(yuriks): As is, this lets processes map memory allocated by other processes from the
+ // same region. It is unknown if or how the 3DS kernel checks against this.
+ size_t offset = target - GetLinearHeapBase();
+ CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, linheap_memory, offset, size, MemoryState::Continuous));
vm_manager.Reprotect(vma, perms);
return MakeResult<VAddr>(target);
}
ResultCode Process::LinearFree(VAddr target, u32 size) {
- if (linear_heap_memory == nullptr || target < Memory::LINEAR_HEAP_VADDR ||
- target + size > Memory::LINEAR_HEAP_VADDR_END || target + size < target) {
+ auto& linheap_memory = memory_region->linear_heap_memory;
+
+ if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() ||
+ target + size < target) {
return ERR_INVALID_ADDRESS;
}
- VAddr heap_end = Memory::LINEAR_HEAP_VADDR + (u32)linear_heap_memory->size();
+ VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size();
if (target + size > heap_end) {
return ERR_INVALID_ADDRESS_STATE;
}
@@ -221,8 +233,8 @@ ResultCode Process::LinearFree(VAddr target, u32 size) {
ASSERT(vma != vm_manager.vma_map.end());
ASSERT(vma->second.type == VMAType::Free);
VAddr new_end = vma->second.base;
- if (new_end >= Memory::LINEAR_HEAP_VADDR) {
- linear_heap_memory->resize(new_end - Memory::LINEAR_HEAP_VADDR);
+ if (new_end >= GetLinearHeapBase()) {
+ linheap_memory->resize(new_end - GetLinearHeapBase());
}
}
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 5c7de9044..7c3a78b9e 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -49,6 +49,7 @@ union ProcessFlags {
};
class ResourceLimit;
+struct MemoryRegionInfo;
struct CodeSet final : public Object {
static SharedPtr<CodeSet> Create(std::string name, u64 program_id);
@@ -135,11 +136,14 @@ public:
// The left/right bounds of the address space covered by heap_memory.
VAddr heap_start = 0, heap_end = 0;
- std::shared_ptr<std::vector<u8>> linear_heap_memory;
+ MemoryRegionInfo* memory_region = nullptr;
/// Bitmask of the used TLS slots
std::bitset<300> used_tls_slots;
+ VAddr GetLinearHeapBase() const;
+ VAddr GetLinearHeapLimit() const;
+
ResultVal<VAddr> HeapAllocate(VAddr target, u32 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index b944f4af0..e1a416def 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -102,7 +102,7 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add
if (addr0 >= Memory::HEAP_VADDR && addr0 < Memory::HEAP_VADDR_END) {
ResultCode result = process.HeapFree(addr0, size);
if (result.IsError()) return result;
- } else if (addr0 >= Memory::LINEAR_HEAP_VADDR && addr0 < Memory::LINEAR_HEAP_VADDR_END) {
+ } else if (addr0 >= process.GetLinearHeapBase() && addr0 < process.GetLinearHeapLimit()) {
ResultCode result = process.LinearFree(addr0, size);
if (result.IsError()) return result;
} else {
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 935dac90f..cde390b8a 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "common/swap.h"
+#include "core/hle/kernel/process.h"
#include "core/memory.h"
#include "core/memory_setup.h"
@@ -208,6 +209,8 @@ PAddr VirtualToPhysicalAddress(const VAddr addr) {
return addr - DSP_RAM_VADDR + DSP_RAM_PADDR;
} else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) {
return addr - IO_AREA_VADDR + IO_AREA_PADDR;
+ } else if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) {
+ return addr - NEW_LINEAR_HEAP_VADDR + FCRAM_PADDR;
}
LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08X", addr);
@@ -221,7 +224,7 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) {
} else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) {
return addr - VRAM_PADDR + VRAM_VADDR;
} else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) {
- return addr - FCRAM_PADDR + LINEAR_HEAP_VADDR;
+ return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapBase();
} else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) {
return addr - DSP_RAM_PADDR + DSP_RAM_VADDR;
} else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) {
diff --git a/src/core/memory.h b/src/core/memory.h
index e6da3e2a5..d1d32f0dd 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -107,6 +107,11 @@ enum : VAddr {
TLS_AREA_VADDR = 0x1FF82000,
TLS_AREA_SIZE = 0x00030000, // Each TLS buffer is 0x200 bytes, allows for 300 threads
TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
+
+ /// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS.
+ NEW_LINEAR_HEAP_VADDR = 0x30000000,
+ NEW_LINEAR_HEAP_SIZE = 0x10000000,
+ NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
};
u8 Read8(VAddr addr);