Merge pull request #1025 from yuriks/heap-management

Kernel: Correct(er) handling of Heap and Linear Heap allocations
This commit is contained in:
Yuri Kunde Schlesner 2015-08-22 14:01:57 -07:00
commit 3efb205a68
29 changed files with 729 additions and 316 deletions

View file

@ -91,17 +91,16 @@ void LogMessage(Class log_class, Level log_level,
} // namespace Log } // namespace Log
#define LOG_GENERIC(log_class, log_level, ...) \ #define LOG_GENERIC(log_class, log_level, ...) \
::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \ ::Log::LogMessage(log_class, log_level, __FILE__, __LINE__, __func__, __VA_ARGS__)
__FILE__, __LINE__, __func__, __VA_ARGS__)
#ifdef _DEBUG #ifdef _DEBUG
#define LOG_TRACE( log_class, ...) LOG_GENERIC(log_class, Trace, __VA_ARGS__) #define LOG_TRACE( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Trace, __VA_ARGS__)
#else #else
#define LOG_TRACE( log_class, ...) (void(0)) #define LOG_TRACE( log_class, ...) (void(0))
#endif #endif
#define LOG_DEBUG( log_class, ...) LOG_GENERIC(log_class, Debug, __VA_ARGS__) #define LOG_DEBUG( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Debug, __VA_ARGS__)
#define LOG_INFO( log_class, ...) LOG_GENERIC(log_class, Info, __VA_ARGS__) #define LOG_INFO( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Info, __VA_ARGS__)
#define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning, __VA_ARGS__) #define LOG_WARNING( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Warning, __VA_ARGS__)
#define LOG_ERROR( log_class, ...) LOG_GENERIC(log_class, Error, __VA_ARGS__) #define LOG_ERROR( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__)
#define LOG_CRITICAL(log_class, ...) LOG_GENERIC(log_class, Critical, __VA_ARGS__) #define LOG_CRITICAL(log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__)

View file

@ -29,6 +29,7 @@ set(SRCS
hle/kernel/address_arbiter.cpp hle/kernel/address_arbiter.cpp
hle/kernel/event.cpp hle/kernel/event.cpp
hle/kernel/kernel.cpp hle/kernel/kernel.cpp
hle/kernel/memory.cpp
hle/kernel/mutex.cpp hle/kernel/mutex.cpp
hle/kernel/process.cpp hle/kernel/process.cpp
hle/kernel/resource_limit.cpp hle/kernel/resource_limit.cpp
@ -115,7 +116,6 @@ set(SRCS
loader/loader.cpp loader/loader.cpp
loader/ncch.cpp loader/ncch.cpp
tracer/recorder.cpp tracer/recorder.cpp
mem_map.cpp
memory.cpp memory.cpp
settings.cpp settings.cpp
system.cpp system.cpp
@ -157,6 +157,7 @@ set(HEADERS
hle/kernel/address_arbiter.h hle/kernel/address_arbiter.h
hle/kernel/event.h hle/kernel/event.h
hle/kernel/kernel.h hle/kernel/kernel.h
hle/kernel/memory.h
hle/kernel/mutex.h hle/kernel/mutex.h
hle/kernel/process.h hle/kernel/process.h
hle/kernel/resource_limit.h hle/kernel/resource_limit.h
@ -245,7 +246,6 @@ set(HEADERS
loader/ncch.h loader/ncch.h
tracer/recorder.h tracer/recorder.h
tracer/citrace.h tracer/citrace.h
mem_map.h
memory.h memory.h
memory_setup.h memory_setup.h
settings.h settings.h

View file

@ -4,7 +4,6 @@
#include "common/swap.h" #include "common/swap.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/mem_map.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/arm/skyeye_common/armstate.h" #include "core/arm/skyeye_common/armstate.h"
#include "core/arm/skyeye_common/vfp/vfp.h" #include "core/arm/skyeye_common/vfp/vfp.h"

View file

@ -17,7 +17,6 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/mem_map.h"
#include "core/arm/skyeye_common/arm_regformat.h" #include "core/arm/skyeye_common/arm_regformat.h"
#include "core/arm/skyeye_common/armstate.h" #include "core/arm/skyeye_common/armstate.h"
#include "core/arm/skyeye_common/armsupp.h" #include "core/arm/skyeye_common/armsupp.h"

View file

@ -25,10 +25,6 @@ void Init() {
config_mem.sys_core_ver = 0x2; config_mem.sys_core_ver = 0x2;
config_mem.unit_info = 0x1; // Bit 0 set for Retail config_mem.unit_info = 0x1; // Bit 0 set for Retail
config_mem.prev_firm = 0; 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_unk = 0;
config_mem.firm_version_rev = 0; config_mem.firm_version_rev = 0;
config_mem.firm_version_min = 0x40; config_mem.firm_version_min = 0x40;
@ -36,7 +32,4 @@ void Init() {
config_mem.firm_sys_core_ver = 0x2; config_mem.firm_sys_core_ver = 0x2;
} }
void Shutdown() {
}
} // namespace } // namespace

View file

@ -52,6 +52,5 @@ static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE, "Config Memory
extern ConfigMemDef config_mem; extern ConfigMemDef config_mem;
void Init(); void Init();
void Shutdown();
} // namespace } // namespace

View file

@ -172,6 +172,14 @@ template<ResultCode func(u32, s64, s64)> void Wrap() {
FuncReturn(func(PARAM(0), param1, param2).raw); FuncReturn(func(PARAM(0), param1, param2).raw);
} }
template<ResultCode func(s64*, Handle, u32)> void Wrap() {
s64 param_1 = 0;
u32 retval = func(&param_1, PARAM(1), PARAM(2)).raw;
Core::g_app_core->SetReg(1, (u32)param_1);
Core::g_app_core->SetReg(2, (u32)(param_1 >> 32));
FuncReturn(retval);
}
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32 // Function wrappers that return type u32

View file

@ -34,8 +34,6 @@ void Reschedule(const char *reason) {
void Init() { void Init() {
Service::Init(); Service::Init();
ConfigMem::Init();
SharedPage::Init();
g_reschedule = false; g_reschedule = false;
@ -43,8 +41,6 @@ void Init() {
} }
void Shutdown() { void Shutdown() {
ConfigMem::Shutdown();
SharedPage::Shutdown();
Service::Shutdown(); Service::Shutdown();
LOG_DEBUG(Kernel, "shutdown OK"); LOG_DEBUG(Kernel, "shutdown OK");

View file

@ -7,11 +7,14 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/config_mem.h"
#include "core/hle/kernel/kernel.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/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h" #include "core/hle/kernel/timer.h"
#include "core/hle/shared_page.h"
namespace Kernel { namespace Kernel {
@ -119,6 +122,13 @@ void HandleTable::Clear() {
/// Initialize the kernel /// Initialize the kernel
void Init() { 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::ResourceLimitsInit();
Kernel::ThreadingInit(); Kernel::ThreadingInit();
Kernel::TimersInit(); Kernel::TimersInit();
@ -131,11 +141,14 @@ void Init() {
/// Shutdown the kernel /// Shutdown the kernel
void Shutdown() { void Shutdown() {
g_handle_table.Clear(); // Free all kernel objects
Kernel::ThreadingShutdown(); Kernel::ThreadingShutdown();
g_current_process = nullptr;
Kernel::TimersShutdown(); Kernel::TimersShutdown();
Kernel::ResourceLimitsShutdown(); Kernel::ResourceLimitsShutdown();
g_handle_table.Clear(); // Free all kernel objects Kernel::MemoryShutdown();
g_current_process = nullptr;
} }
} // namespace } // namespace

View file

@ -0,0 +1,136 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "common/common_types.h"
#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"
#include "core/memory.h"
#include "core/memory_setup.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
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 {
struct MemoryArea {
u32 base;
u32 size;
const char* name;
};
// We don't declare the IO regions in here since its handled by other means.
static MemoryArea memory_areas[] = {
{SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory
{VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM)
{DSP_RAM_VADDR, DSP_RAM_SIZE, "DSP RAM"}, // DSP memory
{TLS_AREA_VADDR, TLS_AREA_SIZE, "TLS Area"}, // TLS memory
};
}
void Init() {
InitMemoryMap();
LOG_DEBUG(HW_Memory, "initialized OK");
}
void InitLegacyAddressSpace(Kernel::VMManager& address_space) {
using namespace Kernel;
for (MemoryArea& area : memory_areas) {
auto block = std::make_shared<std::vector<u8>>(area.size);
address_space.MapMemoryBlock(area.base, std::move(block), 0, area.size, MemoryState::Private).Unwrap();
}
auto cfg_mem_vma = address_space.MapBackingMemory(CONFIG_MEMORY_VADDR,
(u8*)&ConfigMem::config_mem, CONFIG_MEMORY_SIZE, MemoryState::Shared).MoveFrom();
address_space.Reprotect(cfg_mem_vma, VMAPermission::Read);
auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR,
(u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom();
address_space.Reprotect(shared_page_vma, VMAPermission::Read);
}
} // namespace

View file

@ -0,0 +1,35 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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 {
void Init();
void InitLegacyAddressSpace(Kernel::VMManager& address_space);
} // namespace

View file

@ -7,11 +7,11 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/make_unique.h" #include "common/make_unique.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/vm_manager.h"
#include "core/mem_map.h"
#include "core/memory.h" #include "core/memory.h"
namespace Kernel { namespace Kernel {
@ -36,8 +36,7 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) {
process->codeset = std::move(code_set); process->codeset = std::move(code_set);
process->flags.raw = 0; process->flags.raw = 0;
process->flags.memory_region = MemoryRegion::APPLICATION; process->flags.memory_region = MemoryRegion::APPLICATION;
process->address_space = Common::make_unique<VMManager>(); Memory::InitLegacyAddressSpace(process->vm_manager);
Memory::InitLegacyAddressSpace(*process->address_space);
return process; return process;
} }
@ -93,9 +92,11 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
mapping.unk_flag = false; mapping.unk_flag = false;
} else if ((type & 0xFE0) == 0xFC0) { // 0x01FF } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF
// Kernel version // Kernel version
int minor = descriptor & 0xFF; kernel_version = descriptor & 0xFFFF;
int major = (descriptor >> 8) & 0xFF;
LOG_INFO(Loader, "ExHeader kernel version ignored: %d.%d", major, minor); int minor = kernel_version & 0xFF;
int major = (kernel_version >> 8) & 0xFF;
LOG_INFO(Loader, "ExHeader kernel version: %d.%d", major, minor);
} else { } else {
LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x%08X", descriptor); LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x%08X", descriptor);
} }
@ -103,20 +104,153 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
} }
void Process::Run(s32 main_thread_priority, u32 stack_size) { 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 MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) {
auto vma = address_space->MapMemoryBlock(segment.addr, codeset->memory, auto vma = vm_manager.MapMemoryBlock(segment.addr, codeset->memory,
segment.offset, segment.size, memory_state).Unwrap(); segment.offset, segment.size, memory_state).Unwrap();
address_space->Reprotect(vma, permissions); vm_manager.Reprotect(vma, permissions);
misc_memory_used += segment.size;
}; };
// Map CodeSet segments
MapSegment(codeset->code, VMAPermission::ReadExecute, MemoryState::Code); MapSegment(codeset->code, VMAPermission::ReadExecute, MemoryState::Code);
MapSegment(codeset->rodata, VMAPermission::Read, MemoryState::Code); MapSegment(codeset->rodata, VMAPermission::Read, MemoryState::Code);
MapSegment(codeset->data, VMAPermission::ReadWrite, MemoryState::Private); MapSegment(codeset->data, VMAPermission::ReadWrite, MemoryState::Private);
address_space->LogLayout(); // Allocate and map stack
vm_manager.MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, MemoryState::Locked
).Unwrap();
misc_memory_used += stack_size;
vm_manager.LogLayout(Log::Level::Debug);
Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); 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;
}
if (heap_memory == nullptr) {
// Initialize heap
heap_memory = std::make_shared<std::vector<u8>>();
heap_start = heap_end = target;
}
// If necessary, expand backing vector to cover new heap extents.
if (target < heap_start) {
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
heap_start = target;
vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
}
if (target + size > heap_end) {
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
heap_end = target + size;
vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
}
ASSERT(heap_end - heap_start == heap_memory->size());
CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start, size, MemoryState::Private));
vm_manager.Reprotect(vma, perms);
heap_used += size;
return MakeResult<VAddr>(heap_end - size);
}
ResultCode Process::HeapFree(VAddr target, u32 size) {
if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || target + size < target) {
return ERR_INVALID_ADDRESS;
}
ResultCode result = vm_manager.UnmapRange(target, size);
if (result.IsError()) return result;
heap_used -= size;
return RESULT_SUCCESS;
}
ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) {
auto& linheap_memory = memory_region->linear_heap_memory;
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 < GetLinearHeapBase() || target + size > GetLinearHeapLimit() ||
target > heap_end || target + size < target) {
return ERR_INVALID_ADDRESS;
}
// Expansion of the linear heap is only allowed if you do an allocation immediatelly at its
// 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) {
linheap_memory->insert(linheap_memory->end(), size, 0);
vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
}
// 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);
linear_heap_used += size;
return MakeResult<VAddr>(target);
}
ResultCode Process::LinearFree(VAddr target, u32 size) {
auto& linheap_memory = memory_region->linear_heap_memory;
if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() ||
target + size < target) {
return ERR_INVALID_ADDRESS;
}
VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size();
if (target + size > heap_end) {
return ERR_INVALID_ADDRESS_STATE;
}
ResultCode result = vm_manager.UnmapRange(target, size);
if (result.IsError()) return result;
linear_heap_used -= size;
if (target + size == heap_end) {
// End of linear heap has been freed, so check what's the last allocated block in it and
// reduce the size.
auto vma = vm_manager.FindVMA(target);
ASSERT(vma != vm_manager.vma_map.end());
ASSERT(vma->second.type == VMAType::Free);
VAddr new_end = vma->second.base;
if (new_end >= GetLinearHeapBase()) {
linheap_memory->resize(new_end - GetLinearHeapBase());
}
}
return RESULT_SUCCESS;
}
Kernel::Process::Process() {} Kernel::Process::Process() {}
Kernel::Process::~Process() {} Kernel::Process::~Process() {}

View file

@ -15,6 +15,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/vm_manager.h"
namespace Kernel { namespace Kernel {
@ -48,7 +49,7 @@ union ProcessFlags {
}; };
class ResourceLimit; class ResourceLimit;
class VMManager; struct MemoryRegionInfo;
struct CodeSet final : public Object { struct CodeSet final : public Object {
static SharedPtr<CodeSet> Create(std::string name, u64 program_id); static SharedPtr<CodeSet> Create(std::string name, u64 program_id);
@ -104,14 +105,12 @@ public:
/// processes access to specific I/O regions and device memory. /// processes access to specific I/O regions and device memory.
boost::container::static_vector<AddressMapping, 8> address_mappings; boost::container::static_vector<AddressMapping, 8> address_mappings;
ProcessFlags flags; ProcessFlags flags;
/// Kernel compatibility version for this process
u16 kernel_version = 0;
/// The id of this process /// The id of this process
u32 process_id = next_process_id++; u32 process_id = next_process_id++;
/// Bitmask of the used TLS slots
std::bitset<300> used_tls_slots;
std::unique_ptr<VMManager> address_space;
/** /**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
* to this process. * to this process.
@ -123,6 +122,36 @@ public:
*/ */
void Run(s32 main_thread_priority, u32 stack_size); void Run(s32 main_thread_priority, u32 stack_size);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management
VMManager vm_manager;
// Memory used to back the allocations in the regular heap. A single vector is used to cover
// the entire virtual address space extents that bound the allocations, including any holes.
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
std::shared_ptr<std::vector<u8>> heap_memory;
// The left/right bounds of the address space covered by heap_memory.
VAddr heap_start = 0, heap_end = 0;
u32 heap_used = 0, linear_heap_used = 0, misc_memory_used = 0;
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);
ResultVal<VAddr> LinearAllocate(VAddr target, u32 size, VMAPermission perms);
ResultCode LinearFree(VAddr target, u32 size);
private: private:
Process(); Process();
~Process() override; ~Process() override;

View file

@ -6,7 +6,6 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/mem_map.h"
#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/resource_limit.h"
namespace Kernel { namespace Kernel {

View file

@ -117,6 +117,7 @@ void Thread::Stop() {
wait_objects.clear(); wait_objects.clear();
Kernel::g_current_process->used_tls_slots[tls_index] = false; Kernel::g_current_process->used_tls_slots[tls_index] = false;
g_current_process->misc_memory_used -= Memory::TLS_ENTRY_SIZE;
HLE::Reschedule(__func__); HLE::Reschedule(__func__);
} }
@ -414,6 +415,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
} }
ASSERT_MSG(thread->tls_index != -1, "Out of TLS space"); ASSERT_MSG(thread->tls_index != -1, "Out of TLS space");
g_current_process->misc_memory_used += Memory::TLS_ENTRY_SIZE;
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context // to initialize the context
@ -504,7 +506,7 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
} }
VAddr Thread::GetTLSAddress() const { VAddr Thread::GetTLSAddress() const {
return Memory::TLS_AREA_VADDR + tls_index * 0x200; return Memory::TLS_AREA_VADDR + tls_index * Memory::TLS_ENTRY_SIZE;
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -11,6 +11,15 @@
namespace Kernel { namespace Kernel {
static const char* GetMemoryStateName(MemoryState state) {
static const char* names[] = {
"Free", "Reserved", "IO", "Static", "Code", "Private", "Shared", "Continuous", "Aliased",
"Alias", "AliasCode", "Locked",
};
return names[(int)state];
}
bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
ASSERT(base + size == next.base); ASSERT(base + size == next.base);
if (permissions != next.permissions || if (permissions != next.permissions ||
@ -51,11 +60,15 @@ void VMManager::Reset() {
} }
VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { VMManager::VMAHandle VMManager::FindVMA(VAddr target) const {
return std::prev(vma_map.upper_bound(target)); if (target >= MAX_ADDRESS) {
return vma_map.end();
} else {
return std::prev(vma_map.upper_bound(target));
}
} }
ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
std::shared_ptr<std::vector<u8>> block, u32 offset, u32 size, MemoryState state) { std::shared_ptr<std::vector<u8>> block, size_t offset, u32 size, MemoryState state) {
ASSERT(block != nullptr); ASSERT(block != nullptr);
ASSERT(offset + size <= block->size()); ASSERT(offset + size <= block->size());
@ -106,10 +119,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u3
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
} }
void VMManager::Unmap(VMAHandle vma_handle) { VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
VMAIter iter = StripIterConstness(vma_handle); VirtualMemoryArea& vma = vma_handle->second;
VirtualMemoryArea& vma = iter->second;
vma.type = VMAType::Free; vma.type = VMAType::Free;
vma.permissions = VMAPermission::None; vma.permissions = VMAPermission::None;
vma.meminfo_state = MemoryState::Free; vma.meminfo_state = MemoryState::Free;
@ -121,26 +132,67 @@ void VMManager::Unmap(VMAHandle vma_handle) {
UpdatePageTableForVMA(vma); UpdatePageTableForVMA(vma);
MergeAdjacent(iter); return MergeAdjacent(vma_handle);
} }
void VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) { ResultCode VMManager::UnmapRange(VAddr target, u32 size) {
CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size));
VAddr target_end = target + size;
VMAIter end = vma_map.end();
// The comparison against the end of the range must be done using addresses since VMAs can be
// merged during this process, causing invalidation of the iterators.
while (vma != end && vma->second.base < target_end) {
vma = std::next(Unmap(vma));
}
ASSERT(FindVMA(target)->second.size >= size);
return RESULT_SUCCESS;
}
VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) {
VMAIter iter = StripIterConstness(vma_handle); VMAIter iter = StripIterConstness(vma_handle);
VirtualMemoryArea& vma = iter->second; VirtualMemoryArea& vma = iter->second;
vma.permissions = new_perms; vma.permissions = new_perms;
UpdatePageTableForVMA(vma); UpdatePageTableForVMA(vma);
MergeAdjacent(iter); return MergeAdjacent(iter);
} }
void VMManager::LogLayout() const { ResultCode VMManager::ReprotectRange(VAddr target, u32 size, VMAPermission new_perms) {
CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size));
VAddr target_end = target + size;
VMAIter end = vma_map.end();
// The comparison against the end of the range must be done using addresses since VMAs can be
// merged during this process, causing invalidation of the iterators.
while (vma != end && vma->second.base < target_end) {
vma = std::next(StripIterConstness(Reprotect(vma, new_perms)));
}
return RESULT_SUCCESS;
}
void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
// If this ever proves to have a noticeable performance impact, allow users of the function to
// specify a specific range of addresses to limit the scan to.
for (const auto& p : vma_map) { for (const auto& p : vma_map) {
const VirtualMemoryArea& vma = p.second; const VirtualMemoryArea& vma = p.second;
LOG_DEBUG(Kernel, "%08X - %08X size: %8X %c%c%c", vma.base, vma.base + vma.size, vma.size, if (block == vma.backing_block.get()) {
UpdatePageTableForVMA(vma);
}
}
}
void VMManager::LogLayout(Log::Level log_level) const {
for (const auto& p : vma_map) {
const VirtualMemoryArea& vma = p.second;
LOG_GENERIC(Log::Class::Kernel, log_level, "%08X - %08X size: %8X %c%c%c %s",
vma.base, vma.base + vma.size, vma.size,
(u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-',
(u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-',
(u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-'); (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-', GetMemoryStateName(vma.meminfo_state));
} }
} }
@ -151,21 +203,19 @@ VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle & iter) {
} }
ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) {
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: %8X", size); ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size);
ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: %08X", base); ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", base);
VMAIter vma_handle = StripIterConstness(FindVMA(base)); VMAIter vma_handle = StripIterConstness(FindVMA(base));
if (vma_handle == vma_map.end()) { if (vma_handle == vma_map.end()) {
// Target address is outside the range managed by the kernel // Target address is outside the range managed by the kernel
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, return ERR_INVALID_ADDRESS;
ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E01BF5
} }
VirtualMemoryArea& vma = vma_handle->second; VirtualMemoryArea& vma = vma_handle->second;
if (vma.type != VMAType::Free) { if (vma.type != VMAType::Free) {
// Region is already allocated // Region is already allocated
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, return ERR_INVALID_ADDRESS_STATE;
ErrorSummary::InvalidState, ErrorLevel::Usage); // 0xE0A01BF5
} }
u32 start_in_vma = base - vma.base; u32 start_in_vma = base - vma.base;
@ -173,8 +223,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) {
if (end_in_vma > vma.size) { if (end_in_vma > vma.size) {
// Requested allocation doesn't fit inside VMA // Requested allocation doesn't fit inside VMA
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, return ERR_INVALID_ADDRESS_STATE;
ErrorSummary::InvalidState, ErrorLevel::Usage); // 0xE0A01BF5
} }
if (end_in_vma != vma.size) { if (end_in_vma != vma.size) {
@ -189,6 +238,35 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) {
return MakeResult<VMAIter>(vma_handle); return MakeResult<VMAIter>(vma_handle);
} }
ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u32 size) {
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size);
ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", target);
VAddr target_end = target + size;
ASSERT(target_end >= target);
ASSERT(target_end <= MAX_ADDRESS);
ASSERT(size > 0);
VMAIter begin_vma = StripIterConstness(FindVMA(target));
VMAIter i_end = vma_map.lower_bound(target_end);
for (auto i = begin_vma; i != i_end; ++i) {
if (i->second.type == VMAType::Free) {
return ERR_INVALID_ADDRESS_STATE;
}
}
if (target != begin_vma->second.base) {
begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base);
}
VMAIter end_vma = StripIterConstness(FindVMA(target_end));
if (end_vma != vma_map.end() && target_end != end_vma->second.base) {
end_vma = SplitVMA(end_vma, target_end - end_vma->second.base);
}
return MakeResult<VMAIter>(begin_vma);
}
VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) { VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) {
VirtualMemoryArea& old_vma = vma_handle->second; VirtualMemoryArea& old_vma = vma_handle->second;
VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA

View file

@ -14,6 +14,14 @@
namespace Kernel { namespace Kernel {
const ResultCode ERR_INVALID_ADDRESS{ // 0xE0E01BF5
ErrorDescription::InvalidAddress, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage};
const ResultCode ERR_INVALID_ADDRESS_STATE{ // 0xE0A01BF5
ErrorDescription::InvalidAddress, ErrorModule::OS,
ErrorSummary::InvalidState, ErrorLevel::Usage};
enum class VMAType : u8 { enum class VMAType : u8 {
/// VMA represents an unmapped region of the address space. /// VMA represents an unmapped region of the address space.
Free, Free,
@ -75,7 +83,7 @@ struct VirtualMemoryArea {
/// Memory block backing this VMA. /// Memory block backing this VMA.
std::shared_ptr<std::vector<u8>> backing_block = nullptr; std::shared_ptr<std::vector<u8>> backing_block = nullptr;
/// Offset into the backing_memory the mapping starts from. /// Offset into the backing_memory the mapping starts from.
u32 offset = 0; size_t offset = 0;
// Settings for type = BackingMemory // Settings for type = BackingMemory
/// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed.
@ -141,7 +149,7 @@ public:
* @param state MemoryState tag to attach to the VMA. * @param state MemoryState tag to attach to the VMA.
*/ */
ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block,
u32 offset, u32 size, MemoryState state); size_t offset, u32 size, MemoryState state);
/** /**
* Maps an unmanaged host memory pointer at a given address. * Maps an unmanaged host memory pointer at a given address.
@ -163,14 +171,23 @@ public:
*/ */
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state); ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state);
/// Unmaps the given VMA. /// Unmaps a range of addresses, splitting VMAs as necessary.
void Unmap(VMAHandle vma); ResultCode UnmapRange(VAddr target, u32 size);
/// Changes the permissions of the given VMA. /// Changes the permissions of the given VMA.
void Reprotect(VMAHandle vma, VMAPermission new_perms); VMAHandle Reprotect(VMAHandle vma, VMAPermission new_perms);
/// Changes the permissions of a range of addresses, splitting VMAs as necessary.
ResultCode ReprotectRange(VAddr target, u32 size, VMAPermission new_perms);
/**
* Scans all VMAs and updates the page table range of any that use the given vector as backing
* memory. This should be called after any operation that causes reallocation of the vector.
*/
void RefreshMemoryBlockMappings(const std::vector<u8>* block);
/// Dumps the address space layout to the log, for debugging /// Dumps the address space layout to the log, for debugging
void LogLayout() const; void LogLayout(Log::Level log_level) const;
private: private:
using VMAIter = decltype(vma_map)::iterator; using VMAIter = decltype(vma_map)::iterator;
@ -178,12 +195,21 @@ private:
/// Converts a VMAHandle to a mutable VMAIter. /// Converts a VMAHandle to a mutable VMAIter.
VMAIter StripIterConstness(const VMAHandle& iter); VMAIter StripIterConstness(const VMAHandle& iter);
/// Unmaps the given VMA.
VMAIter Unmap(VMAIter vma);
/** /**
* Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing
* the appropriate error checking. * the appropriate error checking.
*/ */
ResultVal<VMAIter> CarveVMA(VAddr base, u32 size); ResultVal<VMAIter> CarveVMA(VAddr base, u32 size);
/**
* Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each
* end of the range.
*/
ResultVal<VMAIter> CarveVMARange(VAddr base, u32 size);
/** /**
* Splits a VMA in two, at the specified offset. * Splits a VMA in two, at the specified offset.
* @returns the right side of the split, with the original iterator becoming the left side. * @returns the right side of the split, with the original iterator becoming the left side.

View file

@ -16,6 +16,7 @@
#include "core/hle/hle.h" #include "core/hle/hle.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
@ -37,7 +38,7 @@ static Kernel::SharedPtr<Kernel::Mutex> lock;
static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event
static std::vector<u8> shared_font; static std::shared_ptr<std::vector<u8>> shared_font;
static u32 cpu_percent; ///< CPU time available to the running application static u32 cpu_percent; ///< CPU time available to the running application
@ -74,11 +75,12 @@ void Initialize(Service::Interface* self) {
void GetSharedFont(Service::Interface* self) { void GetSharedFont(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer(); u32* cmd_buff = Kernel::GetCommandBuffer();
if (!shared_font.empty()) { if (shared_font != nullptr) {
// TODO(bunnei): This function shouldn't copy the shared font every time it's called. // TODO(yuriks): This is a hack to keep this working right now even with our completely
// Instead, it should probably map the shared font as RO memory. We don't currently have // broken shared memory system.
// an easy way to do this, but the copy should be sufficient for now. shared_font_mem->base_address = SHARED_FONT_VADDR;
memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size()); Kernel::g_current_process->vm_manager.MapMemoryBlock(shared_font_mem->base_address,
shared_font, 0, shared_font_mem->size, Kernel::MemoryState::Shared);
cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@ -391,7 +393,6 @@ void Init() {
// a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
// "shared_font.bin" in the Citra "sysdata" directory. // "shared_font.bin" in the Citra "sysdata" directory.
shared_font.clear();
std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
FileUtil::CreateFullPath(filepath); // Create path if not already created FileUtil::CreateFullPath(filepath); // Create path if not already created
@ -399,8 +400,8 @@ void Init() {
if (file.IsOpen()) { if (file.IsOpen()) {
// Read shared font data // Read shared font data
shared_font.resize((size_t)file.GetSize()); shared_font = std::make_shared<std::vector<u8>>((size_t)file.GetSize());
file.ReadBytes(shared_font.data(), (size_t)file.GetSize()); file.ReadBytes(shared_font->data(), shared_font->size());
// Create shared font memory object // Create shared font memory object
using Kernel::MemoryPermission; using Kernel::MemoryPermission;
@ -424,7 +425,7 @@ void Init() {
} }
void Shutdown() { void Shutdown() {
shared_font.clear(); shared_font = nullptr;
shared_font_mem = nullptr; shared_font_mem = nullptr;
lock = nullptr; lock = nullptr;
notification_event = nullptr; notification_event = nullptr;

View file

@ -4,7 +4,6 @@
#include "common/bit_field.h" #include "common/bit_field.h"
#include "core/mem_map.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"

View file

@ -10,7 +10,6 @@
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/service/y2r_u.h" #include "core/hle/service/y2r_u.h"
#include "core/hw/y2r.h" #include "core/hw/y2r.h"
#include "core/mem_map.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/utils.h" #include "video_core/utils.h"

View file

@ -18,7 +18,4 @@ void Init() {
shared_page.running_hw = 0x1; // product shared_page.running_hw = 0x1; // product
} }
void Shutdown() {
}
} // namespace } // namespace

View file

@ -54,6 +54,5 @@ static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE, "Shared page st
extern SharedPageDef shared_page; extern SharedPageDef shared_page;
void Init(); void Init();
void Shutdown();
} // namespace } // namespace

View file

@ -10,11 +10,11 @@
#include "common/symbols.h" #include "common/symbols.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/mem_map.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/resource_limit.h"
@ -41,32 +41,114 @@ const ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel,
const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E
const ResultCode ERR_MISALIGNED_ADDRESS{ // 0xE0E01BF1
ErrorDescription::MisalignedAddress, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage};
const ResultCode ERR_MISALIGNED_SIZE{ // 0xE0E01BF2
ErrorDescription::MisalignedSize, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage};
const ResultCode ERR_INVALID_COMBINATION{ // 0xE0E01BEE
ErrorDescription::InvalidCombination, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage};
enum ControlMemoryOperation { enum ControlMemoryOperation {
MEMORY_OPERATION_HEAP = 0x00000003, MEMOP_FREE = 1,
MEMORY_OPERATION_GSP_HEAP = 0x00010003, MEMOP_RESERVE = 2, // This operation seems to be unsupported in the kernel
MEMOP_COMMIT = 3,
MEMOP_MAP = 4,
MEMOP_UNMAP = 5,
MEMOP_PROTECT = 6,
MEMOP_OPERATION_MASK = 0xFF,
MEMOP_REGION_APP = 0x100,
MEMOP_REGION_SYSTEM = 0x200,
MEMOP_REGION_BASE = 0x300,
MEMOP_REGION_MASK = 0xF00,
MEMOP_LINEAR = 0x10000,
}; };
/// Map application or GSP heap memory /// Map application or GSP heap memory
static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", using namespace Kernel;
LOG_DEBUG(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=0x%X, permissions=0x%08X",
operation, addr0, addr1, size, permissions); operation, addr0, addr1, size, permissions);
switch (operation) { if ((addr0 & Memory::PAGE_MASK) != 0 || (addr1 & Memory::PAGE_MASK) != 0) {
return ERR_MISALIGNED_ADDRESS;
}
if ((size & Memory::PAGE_MASK) != 0) {
return ERR_MISALIGNED_SIZE;
}
// Map normal heap memory u32 region = operation & MEMOP_REGION_MASK;
case MEMORY_OPERATION_HEAP: operation &= ~MEMOP_REGION_MASK;
*out_addr = Memory::MapBlock_Heap(size, operation, permissions);
if (region != 0) {
LOG_WARNING(Kernel_SVC, "ControlMemory with specified region not supported, region=%X", region);
}
if ((permissions & (u32)MemoryPermission::ReadWrite) != permissions) {
return ERR_INVALID_COMBINATION;
}
VMAPermission vma_permissions = (VMAPermission)permissions;
auto& process = *g_current_process;
switch (operation & MEMOP_OPERATION_MASK) {
case MEMOP_FREE:
{
if (addr0 >= Memory::HEAP_VADDR && addr0 < Memory::HEAP_VADDR_END) {
ResultCode result = process.HeapFree(addr0, size);
if (result.IsError()) return result;
} else if (addr0 >= process.GetLinearHeapBase() && addr0 < process.GetLinearHeapLimit()) {
ResultCode result = process.LinearFree(addr0, size);
if (result.IsError()) return result;
} else {
return ERR_INVALID_ADDRESS;
}
*out_addr = addr0;
break; break;
}
// Map GSP heap memory case MEMOP_COMMIT:
case MEMORY_OPERATION_GSP_HEAP: {
*out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions); if (operation & MEMOP_LINEAR) {
CASCADE_RESULT(*out_addr, process.LinearAllocate(addr0, size, vma_permissions));
} else {
CASCADE_RESULT(*out_addr, process.HeapAllocate(addr0, size, vma_permissions));
}
break; break;
}
case MEMOP_MAP: // TODO: This is just a hack to avoid regressions until memory aliasing is implemented
{
CASCADE_RESULT(*out_addr, process.HeapAllocate(addr0, size, vma_permissions));
break;
}
case MEMOP_UNMAP: // TODO: This is just a hack to avoid regressions until memory aliasing is implemented
{
ResultCode result = process.HeapFree(addr0, size);
if (result.IsError()) return result;
break;
}
case MEMOP_PROTECT:
{
ResultCode result = process.vm_manager.ReprotectRange(addr0, size, vma_permissions);
if (result.IsError()) return result;
break;
}
// Unknown ControlMemory operation
default: default:
LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation); LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation);
return ERR_INVALID_COMBINATION;
} }
process.vm_manager.LogLayout(Log::Level::Trace);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -537,9 +619,9 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_inf
if (process == nullptr) if (process == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
auto vma = process->address_space->FindVMA(addr); auto vma = process->vm_manager.FindVMA(addr);
if (vma == process->address_space->vma_map.end()) if (vma == Kernel::g_current_process->vm_manager.vma_map.end())
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
memory_info->base_address = vma->second.base; memory_info->base_address = vma->second.base;
@ -692,6 +774,52 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) {
LOG_TRACE(Kernel_SVC, "called process=0x%08X type=%u", process_handle, type);
using Kernel::Process;
Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle);
if (process == nullptr)
return ERR_INVALID_HANDLE;
switch (type) {
case 0:
case 2:
// TODO(yuriks): Type 0 returns a slightly higher number than type 2, but I'm not sure
// what's the difference between them.
*out = process->heap_used + process->linear_heap_used + process->misc_memory_used;
break;
case 1:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
// These are valid, but not implemented yet
LOG_ERROR(Kernel_SVC, "unimplemented GetProcessInfo type=%u", type);
break;
case 20:
*out = Memory::FCRAM_PADDR - process->GetLinearHeapBase();
break;
default:
LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type=%u", type);
if (type >= 21 && type <= 23) {
return ResultCode( // 0xE0E01BF4
ErrorDescription::NotImplemented, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
} else {
return ResultCode( // 0xD8E007ED
ErrorDescription::InvalidEnumValue, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
break;
}
return RESULT_SUCCESS;
}
namespace { namespace {
struct FunctionDef { struct FunctionDef {
using Func = void(); using Func = void();
@ -746,7 +874,7 @@ static const FunctionDef SVC_Table[] = {
{0x28, HLE::Wrap<GetSystemTick>, "GetSystemTick"}, {0x28, HLE::Wrap<GetSystemTick>, "GetSystemTick"},
{0x29, nullptr, "GetHandleInfo"}, {0x29, nullptr, "GetHandleInfo"},
{0x2A, nullptr, "GetSystemInfo"}, {0x2A, nullptr, "GetSystemInfo"},
{0x2B, nullptr, "GetProcessInfo"}, {0x2B, HLE::Wrap<GetProcessInfo>, "GetProcessInfo"},
{0x2C, nullptr, "GetThreadInfo"}, {0x2C, nullptr, "GetThreadInfo"},
{0x2D, HLE::Wrap<ConnectToPort>, "ConnectToPort"}, {0x2D, HLE::Wrap<ConnectToPort>, "ConnectToPort"},
{0x2E, nullptr, "SendSyncRequest1"}, {0x2E, nullptr, "SendSyncRequest1"},

View file

@ -1,163 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/config_mem.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/result.h"
#include "core/hle/shared_page.h"
#include "core/mem_map.h"
#include "core/memory.h"
#include "core/memory_setup.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Memory {
namespace {
struct MemoryArea {
u32 base;
u32 size;
const char* name;
};
// We don't declare the IO regions in here since its handled by other means.
static MemoryArea memory_areas[] = {
{HEAP_VADDR, HEAP_SIZE, "Heap"}, // Application heap (main memory)
{SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory
{LINEAR_HEAP_VADDR, LINEAR_HEAP_SIZE, "Linear Heap"}, // Linear heap (main memory)
{VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM)
{DSP_RAM_VADDR, DSP_RAM_SIZE, "DSP RAM"}, // DSP memory
{TLS_AREA_VADDR, TLS_AREA_SIZE, "TLS Area"}, // TLS memory
};
/// Represents a block of memory mapped by ControlMemory/MapMemoryBlock
struct MemoryBlock {
MemoryBlock() : handle(0), base_address(0), address(0), size(0), operation(0), permissions(0) {
}
u32 handle;
u32 base_address;
u32 address;
u32 size;
u32 operation;
u32 permissions;
const u32 GetVirtualAddress() const{
return base_address + address;
}
};
static std::map<u32, MemoryBlock> heap_map;
static std::map<u32, MemoryBlock> heap_linear_map;
}
u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) {
MemoryBlock block;
block.base_address = HEAP_VADDR;
block.size = size;
block.operation = operation;
block.permissions = permissions;
if (heap_map.size() > 0) {
const MemoryBlock last_block = heap_map.rbegin()->second;
block.address = last_block.address + last_block.size;
}
heap_map[block.GetVirtualAddress()] = block;
return block.GetVirtualAddress();
}
u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) {
MemoryBlock block;
block.base_address = LINEAR_HEAP_VADDR;
block.size = size;
block.operation = operation;
block.permissions = permissions;
if (heap_linear_map.size() > 0) {
const MemoryBlock last_block = heap_linear_map.rbegin()->second;
block.address = last_block.address + last_block.size;
}
heap_linear_map[block.GetVirtualAddress()] = block;
return block.GetVirtualAddress();
}
PAddr VirtualToPhysicalAddress(const VAddr addr) {
if (addr == 0) {
return 0;
} else if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) {
return addr - VRAM_VADDR + VRAM_PADDR;
} else if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) {
return addr - LINEAR_HEAP_VADDR + FCRAM_PADDR;
} else if (addr >= DSP_RAM_VADDR && addr < DSP_RAM_VADDR_END) {
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;
}
LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08x", addr);
// To help with debugging, set bit on address so that it's obviously invalid.
return addr | 0x80000000;
}
VAddr PhysicalToVirtualAddress(const PAddr addr) {
if (addr == 0) {
return 0;
} 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;
} 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) {
return addr - IO_AREA_PADDR + IO_AREA_VADDR;
}
LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08x", addr);
// To help with debugging, set bit on address so that it's obviously invalid.
return addr | 0x80000000;
}
void Init() {
InitMemoryMap();
LOG_DEBUG(HW_Memory, "initialized OK");
}
void InitLegacyAddressSpace(Kernel::VMManager& address_space) {
using namespace Kernel;
for (MemoryArea& area : memory_areas) {
auto block = std::make_shared<std::vector<u8>>(area.size);
address_space.MapMemoryBlock(area.base, std::move(block), 0, area.size, MemoryState::Private).Unwrap();
}
auto cfg_mem_vma = address_space.MapBackingMemory(CONFIG_MEMORY_VADDR,
(u8*)&ConfigMem::config_mem, CONFIG_MEMORY_SIZE, MemoryState::Shared).MoveFrom();
address_space.Reprotect(cfg_mem_vma, VMAPermission::Read);
auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR,
(u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom();
address_space.Reprotect(shared_page_vma, VMAPermission::Read);
}
void Shutdown() {
heap_map.clear();
heap_linear_map.clear();
LOG_DEBUG(HW_Memory, "shutdown OK");
}
} // namespace

View file

@ -1,46 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Kernel {
class VMManager;
}
namespace Memory {
void Init();
void InitLegacyAddressSpace(Kernel::VMManager& address_space);
void Shutdown();
/**
* Maps a block of memory on the heap
* @param size Size of block in bytes
* @param operation Memory map operation type
* @param permissions Memory allocation permissions
*/
u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions);
/**
* Maps a block of memory on the GSP heap
* @param size Size of block in bytes
* @param operation Memory map operation type
* @param permissions Control memory permissions
*/
u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions);
/**
* Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical
* address. This should be used by services to translate addresses for use by the hardware.
*/
PAddr VirtualToPhysicalAddress(VAddr addr);
/**
* Undoes a mapping performed by VirtualToPhysicalAddress().
*/
VAddr PhysicalToVirtualAddress(PAddr addr);
} // namespace

View file

@ -9,7 +9,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/mem_map.h" #include "core/hle/kernel/process.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/memory_setup.h" #include "core/memory_setup.h"
@ -198,4 +198,42 @@ void WriteBlock(const VAddr addr, const u8* data, const size_t size) {
Write8(addr + offset, data[offset]); Write8(addr + offset, data[offset]);
} }
PAddr VirtualToPhysicalAddress(const VAddr addr) {
if (addr == 0) {
return 0;
} else if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) {
return addr - VRAM_VADDR + VRAM_PADDR;
} else if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) {
return addr - LINEAR_HEAP_VADDR + FCRAM_PADDR;
} else if (addr >= DSP_RAM_VADDR && addr < DSP_RAM_VADDR_END) {
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);
// To help with debugging, set bit on address so that it's obviously invalid.
return addr | 0x80000000;
}
VAddr PhysicalToVirtualAddress(const PAddr addr) {
if (addr == 0) {
return 0;
} 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 + 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) {
return addr - IO_AREA_PADDR + IO_AREA_VADDR;
}
LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08X", addr);
// To help with debugging, set bit on address so that it's obviously invalid.
return addr | 0x80000000;
}
} // namespace } // namespace

View file

@ -15,6 +15,8 @@ namespace Memory {
* be mapped. * be mapped.
*/ */
const u32 PAGE_SIZE = 0x1000; const u32 PAGE_SIZE = 0x1000;
const u32 PAGE_MASK = PAGE_SIZE - 1;
const int PAGE_BITS = 12;
/// Physical memory regions as seen from the ARM11 /// Physical memory regions as seen from the ARM11
enum : PAddr { enum : PAddr {
@ -103,8 +105,15 @@ enum : VAddr {
// hardcoded value. // hardcoded value.
/// Area where TLS (Thread-Local Storage) buffers are allocated. /// Area where TLS (Thread-Local Storage) buffers are allocated.
TLS_AREA_VADDR = 0x1FF82000, TLS_AREA_VADDR = 0x1FF82000,
TLS_AREA_SIZE = 0x00030000, // Each TLS buffer is 0x200 bytes, allows for 300 threads TLS_ENTRY_SIZE = 0x200,
TLS_AREA_SIZE = 300 * TLS_ENTRY_SIZE, // Allows for up to 300 threads
TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE, 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); u8 Read8(VAddr addr);
@ -121,6 +130,17 @@ void WriteBlock(VAddr addr, const u8* data, size_t size);
u8* GetPointer(VAddr virtual_address); u8* GetPointer(VAddr virtual_address);
/**
* Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical
* address. This should be used by services to translate addresses for use by the hardware.
*/
PAddr VirtualToPhysicalAddress(VAddr addr);
/**
* Undoes a mapping performed by VirtualToPhysicalAddress().
*/
VAddr PhysicalToVirtualAddress(PAddr addr);
/** /**
* Gets a pointer to the memory region beginning at the specified physical address. * Gets a pointer to the memory region beginning at the specified physical address.
* *

View file

@ -10,9 +10,6 @@
namespace Memory { namespace Memory {
const u32 PAGE_MASK = PAGE_SIZE - 1;
const int PAGE_BITS = 12;
void InitMemoryMap(); void InitMemoryMap();
/** /**

View file

@ -4,11 +4,11 @@
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/mem_map.h"
#include "core/system.h" #include "core/system.h"
#include "core/hw/hw.h" #include "core/hw/hw.h"
#include "core/hle/hle.h" #include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
@ -29,7 +29,6 @@ void Shutdown() {
HLE::Shutdown(); HLE::Shutdown();
Kernel::Shutdown(); Kernel::Shutdown();
HW::Shutdown(); HW::Shutdown();
Memory::Shutdown();
CoreTiming::Shutdown(); CoreTiming::Shutdown();
Core::Shutdown(); Core::Shutdown();
} }