Merge pull request #1025 from yuriks/heap-management
Kernel: Correct(er) handling of Heap and Linear Heap allocations
This commit is contained in:
commit
3efb205a68
29 changed files with 729 additions and 316 deletions
|
@ -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__)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(¶m_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
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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
|
||||||
|
|
136
src/core/hle/kernel/memory.cpp
Normal file
136
src/core/hle/kernel/memory.cpp
Normal 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
|
35
src/core/hle/kernel/memory.h
Normal file
35
src/core/hle/kernel/memory.h
Normal 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
|
|
@ -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() {}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -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 {
|
||||||
|
if (target >= MAX_ADDRESS) {
|
||||||
|
return vma_map.end();
|
||||||
|
} else {
|
||||||
return std::prev(vma_map.upper_bound(target));
|
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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -18,7 +18,4 @@ void Init() {
|
||||||
shared_page.running_hw = 0x1; // product
|
shared_page.running_hw = 0x1; // product
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() {
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"},
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -10,9 +10,6 @@
|
||||||
|
|
||||||
namespace Memory {
|
namespace Memory {
|
||||||
|
|
||||||
const u32 PAGE_MASK = PAGE_SIZE - 1;
|
|
||||||
const int PAGE_BITS = 12;
|
|
||||||
|
|
||||||
void InitMemoryMap();
|
void InitMemoryMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue