Kernel/SharedMemory: Properly implemented shared memory support.

Applications can request the kernel to allocate a piece of the linear heap for them when creating a shared memory object.
Shared memory areas are now properly mapped into the target processes when calling svcMapMemoryBlock.

Removed the APT Shared Font hack as it is no longer needed.
This commit is contained in:
Subv 2016-04-17 21:07:52 -05:00
parent 42a50da76b
commit 1bd0cf542f
10 changed files with 147 additions and 118 deletions

View File

@ -36,8 +36,8 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
memcpy(&capture_info, parameter.data, sizeof(capture_info));
using Kernel::MemoryPermission;
framebuffer_memory = Kernel::SharedMemory::Create(capture_info.size, MemoryPermission::ReadWrite,
MemoryPermission::ReadWrite, "MiiSelector Memory");
framebuffer_memory = Kernel::SharedMemory::Create(nullptr, capture_info.size, MemoryPermission::ReadWrite,
MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "MiiSelector Memory");
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;

View File

@ -40,8 +40,8 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
memcpy(&capture_info, parameter.data, sizeof(capture_info));
using Kernel::MemoryPermission;
framebuffer_memory = Kernel::SharedMemory::Create(capture_info.size, MemoryPermission::ReadWrite,
MemoryPermission::ReadWrite, "SoftwareKeyboard Memory");
framebuffer_memory = Kernel::SharedMemory::Create(nullptr, capture_info.size, MemoryPermission::ReadWrite,
MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "SoftwareKeyboard Memory");
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;

View File

@ -7,6 +7,7 @@
#include "common/logging/log.h"
#include "core/memory.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h"
namespace Kernel {
@ -14,93 +15,94 @@ namespace Kernel {
SharedMemory::SharedMemory() {}
SharedMemory::~SharedMemory() {}
SharedPtr<SharedMemory> SharedMemory::Create(u32 size, MemoryPermission permissions,
MemoryPermission other_permissions, std::string name) {
SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
shared_memory->owner_process = owner_process;
shared_memory->name = std::move(name);
shared_memory->base_address = 0x0;
shared_memory->fixed_address = 0x0;
shared_memory->size = size;
shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions;
if (address == 0) {
// We need to allocate a block from the Linear Heap ourselves.
// We'll manually allocate some memory from the linear heap in the specified region.
MemoryRegionInfo* memory_region = GetMemoryRegion(region);
auto& linheap_memory = memory_region->linear_heap_memory;
ASSERT_MSG(linheap_memory->size() + size <= memory_region->size, "Not enough space in region to allocate shared memory!");
shared_memory->backing_block = linheap_memory;
shared_memory->backing_block_offset = linheap_memory->size();
// Allocate some memory from the end of the linear heap for this region.
linheap_memory->insert(linheap_memory->end(), size, 0);
memory_region->used += size;
shared_memory->linear_heap_phys_address = Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset;
// Refresh the address mappings for the current process.
if (Kernel::g_current_process != nullptr) {
Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
}
} else {
auto& vm_manager = shared_memory->owner_process->vm_manager;
// The memory is already available and mapped in the owner process.
auto vma = vm_manager.FindVMA(address)->second;
// Copy it over to our own storage
shared_memory->backing_block = std::make_shared<std::vector<u8>>(vma.backing_block->data() + vma.offset,
vma.backing_block->data() + vma.offset + size);
// Unmap the existing pages
vm_manager.UnmapRange(address, size);
// Map our own block into the address space
vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, MemoryState::Shared);
}
shared_memory->base_address = address;
return shared_memory;
}
ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions,
ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
MemoryPermission other_permissions) {
if (base_address != 0) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: already mapped at 0x%08X!",
GetObjectId(), address, name.c_str(), base_address);
// TODO: Verify error code with hardware
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
// TODO(Subv): Return E0E01BEE when permissions and other_permissions don't
// match what was specified when the memory block was created.
// TODO(Subv): Return E0E01BEE when address should be 0.
// Note: Find out when that's the case.
// TODO(Subv): Check for the Shared Device Mem flag in the creator process.
/*if (was_created_with_shared_device_mem && address != 0) {
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}*/
if (fixed_address != 0) {
if (address != 0 && address != fixed_address) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: fixed_addres is 0x%08X!",
GetObjectId(), address, name.c_str(), fixed_address);
// TODO: Verify error code with hardware
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
// TODO(Subv): The same process that created a SharedMemory object
// can not map it in its own address space unless it was created with addr=0, result 0xD900182C.
// HACK(yuriks): This is only here to support the APT shared font mapping right now.
// Later, this should actually map the memory block onto the address space.
return RESULT_SUCCESS;
}
if (address < Memory::SHARED_MEMORY_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s outside of shared mem bounds!",
if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, invalid address",
GetObjectId(), address, name.c_str());
// TODO: Verify error code with hardware
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
// TODO: Test permissions
VAddr target_address = address;
// HACK: Since there's no way to write to the memory block without mapping it onto the game
// process yet, at least initialize memory the first time it's mapped.
if (address != this->base_address) {
std::memset(Memory::GetPointer(address), 0, size);
if (base_address == 0 && target_address == 0) {
// Calculate the address at which to map the memory block.
target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address);
}
this->base_address = address;
// Map the memory block into the target process
target_process->vm_manager.MapMemoryBlock(target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
return RESULT_SUCCESS;
}
ResultCode SharedMemory::Unmap(VAddr address) {
if (base_address == 0) {
// TODO(Subv): Verify what actually happens when you want to unmap a memory block that
// was originally mapped with address = 0
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
if (base_address != address)
return ResultCode(ErrorDescription::WrongAddress, ErrorModule::OS, ErrorSummary::InvalidState, ErrorLevel::Usage);
base_address = 0;
return RESULT_SUCCESS;
ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) {
// TODO(Subv): Verify what happens if the application tries to unmap an address that is not mapped to a SharedMemory.
return target_process->vm_manager.UnmapRange(address, size);
}
u8* SharedMemory::GetPointer(u32 offset) {
if (base_address != 0)
return Memory::GetPointer(base_address + offset);
LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId());
return nullptr;
return backing_block->data() + backing_block_offset + offset;
}
} // namespace

View File

@ -9,6 +9,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
namespace Kernel {
@ -30,13 +31,16 @@ class SharedMemory final : public Object {
public:
/**
* Creates a shared memory object
* @param owner_process Process that created this shared memory object.
* @param size Size of the memory block. Must be page-aligned.
* @param permissions Permission restrictions applied to the process which created the block.
* @param other_permissions Permission restrictions applied to other processes mapping the block.
* @param address The address from which to map the Shared Memory.
* @param region If the address is 0, the shared memory will be allocated in this region of the linear heap.
* @param name Optional object name, used for debugging purposes.
*/
static SharedPtr<SharedMemory> Create(u32 size, MemoryPermission permissions,
MemoryPermission other_permissions, std::string name = "Unknown");
static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address = 0, MemoryRegion region = MemoryRegion::BASE, std::string name = "Unknown");
std::string GetTypeName() const override { return "SharedMemory"; }
std::string GetName() const override { return name; }
@ -45,19 +49,21 @@ public:
HandleType GetHandleType() const override { return HANDLE_TYPE; }
/**
* Maps a shared memory block to an address in system memory
* Maps a shared memory block to an address in the target process' address space
* @param target_process Process on which to map the memory block.
* @param address Address in system memory to map shared memory block to
* @param permissions Memory block map permissions (specified by SVC field)
* @param other_permissions Memory block map other permissions (specified by SVC field)
*/
ResultCode Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions);
ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions, MemoryPermission other_permissions);
/**
* Unmaps a shared memory block from the specified address in system memory
* @param target_process Process from which to umap the memory block.
* @param address Address in system memory where the shared memory block is mapped
* @return Result code of the unmap operation
*/
ResultCode Unmap(VAddr address);
ResultCode Unmap(Process* target_process, VAddr address);
/**
* Gets a pointer to the shared memory block
@ -66,10 +72,16 @@ public:
*/
u8* GetPointer(u32 offset = 0);
/// Address of shared memory block in the process.
/// Process that created this shared memory block.
SharedPtr<Process> owner_process;
/// Address of shared memory block in the owner process if specified.
VAddr base_address;
/// Fixed address to allow mapping to. Used for blocks created from the linear heap.
VAddr fixed_address;
/// Physical address of the shared memory block in the linear heap if no address was specified during creation.
PAddr linear_heap_phys_address;
/// Backing memory for this shared memory block.
std::shared_ptr<std::vector<u8>> backing_block;
/// Offset into the backing block for this shared memory.
u32 backing_block_offset;
/// Size of the memory block. Page-aligned.
u32 size;
/// Permission restrictions applied to the process which created the block.

View File

@ -37,8 +37,6 @@ static Kernel::SharedPtr<Kernel::Mutex> lock;
static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event
static std::shared_ptr<std::vector<u8>> shared_font;
static u32 cpu_percent; ///< CPU time available to the running application
/// Parameter data to be returned in the next call to Glance/ReceiveParameter
@ -74,23 +72,14 @@ void Initialize(Service::Interface* self) {
void GetSharedFont(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
if (shared_font != nullptr) {
// TODO(yuriks): This is a hack to keep this working right now even with our completely
// broken shared memory system.
shared_font_mem->fixed_address = SHARED_FONT_VADDR;
Kernel::g_current_process->vm_manager.MapMemoryBlock(shared_font_mem->fixed_address,
shared_font, 0, shared_font_mem->size, Kernel::MemoryState::Shared);
cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = SHARED_FONT_VADDR;
cmd_buff[3] = IPC::MoveHandleDesc();
cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
} else {
cmd_buff[0] = IPC::MakeHeader(0x44, 1, 0);
cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
}
cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
// Since the SharedMemory interface doesn't provide the address at which the memory was allocated,
// the APT service calculates this address by scanning the entire address space (using svcQueryMemory)
// and searches for an allocation of the same size as the Shared Font.
cmd_buff[2] = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address);
cmd_buff[3] = IPC::MoveHandleDesc();
cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
}
void NotifyToWait(Service::Interface* self) {
@ -433,14 +422,12 @@ void Init() {
FileUtil::IOFile file(filepath, "rb");
if (file.IsOpen()) {
// Read shared font data
shared_font = std::make_shared<std::vector<u8>>((size_t)file.GetSize());
file.ReadBytes(shared_font->data(), shared_font->size());
// Create shared font memory object
using Kernel::MemoryPermission;
shared_font_mem = Kernel::SharedMemory::Create(3 * 1024 * 1024, // 3MB
MemoryPermission::ReadWrite, MemoryPermission::Read, "APT_U:shared_font_mem");
shared_font_mem = Kernel::SharedMemory::Create(nullptr, 0x332000, // 3272 KB
MemoryPermission::ReadWrite, MemoryPermission::Read, 0, Kernel::MemoryRegion::SYSTEM, "APT:SharedFont");
// Read shared font data
file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
} else {
LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
shared_font_mem = nullptr;
@ -459,7 +446,6 @@ void Init() {
}
void Shutdown() {
shared_font = nullptr;
shared_font_mem = nullptr;
lock = nullptr;
notification_event = nullptr;

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <cstring>
#include "common/alignment.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/shared_memory.h"
@ -41,14 +42,16 @@ static Kernel::SharedPtr<Kernel::Mutex> mutex = nullptr;
void Initialize(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
shared_memory = Kernel::SharedMemory::Create(cmd_buff[1],
Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::ReadWrite, "CSNDSharedMem");
u32 size = Common::AlignUp(cmd_buff[1], Memory::PAGE_SIZE);
using Kernel::MemoryPermission;
shared_memory = Kernel::SharedMemory::Create(nullptr, size,
MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
0, Kernel::MemoryRegion::BASE, "CSND:SharedMemory");
mutex = Kernel::Mutex::Create(false);
cmd_buff[1] = 0;
cmd_buff[2] = 0x4000000;
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = IPC::MoveHandleDesc(2);
cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom();
cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom();
}

View File

@ -335,8 +335,9 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
g_interrupt_event->name = "GSP_GPU::interrupt_event";
using Kernel::MemoryPermission;
g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
MemoryPermission::ReadWrite, "GSPSharedMem");
g_shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000,
MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory");
Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom();

View File

@ -280,8 +280,9 @@ void Init() {
AddService(new HID_SPVR_Interface);
using Kernel::MemoryPermission;
shared_mem = SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
MemoryPermission::Read, "HID:SharedMem");
shared_mem = SharedMemory::Create(nullptr, 0x1000,
MemoryPermission::ReadWrite, MemoryPermission::Read,
0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
next_pad_index = 0;
next_touch_index = 0;

View File

@ -94,8 +94,9 @@ void Init() {
AddService(new IR_User_Interface);
using Kernel::MemoryPermission;
shared_memory = SharedMemory::Create(0x1000, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory");
shared_memory = SharedMemory::Create(nullptr, 0x1000,
Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::ReadWrite,
0, Kernel::MemoryRegion::BASE, "IR:SharedMemory");
transfer_shared_memory = nullptr;
// Create event handle(s)

View File

@ -160,8 +160,6 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
handle, addr, permissions, other_permissions);
// TODO(Subv): The same process that created a SharedMemory object can not map it in its own address space
SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
if (shared_memory == nullptr)
return ERR_INVALID_HANDLE;
@ -176,7 +174,7 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
case MemoryPermission::WriteExecute:
case MemoryPermission::ReadWriteExecute:
case MemoryPermission::DontCare:
return shared_memory->Map(addr, permissions_type,
return shared_memory->Map(Kernel::g_current_process.get(), addr, permissions_type,
static_cast<MemoryPermission>(other_permissions));
default:
LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
@ -196,7 +194,7 @@ static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) {
if (shared_memory == nullptr)
return ERR_INVALID_HANDLE;
return shared_memory->Unmap(addr);
return shared_memory->Unmap(Kernel::g_current_process.get(), addr);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@ -790,18 +788,43 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
if (size % Memory::PAGE_SIZE != 0)
return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
// TODO(Subv): Return E0A01BF5 if the address is not in the application's heap
// TODO(Subv): Implement this function properly
SharedPtr<SharedMemory> shared_memory = nullptr;
using Kernel::MemoryPermission;
SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(size,
(MemoryPermission)my_permission, (MemoryPermission)other_permission);
// Map the SharedMemory to the specified address
shared_memory->base_address = addr;
auto VerifyPermissions = [](MemoryPermission permission) {
// SharedMemory blocks can not be created with Execute permissions
switch (permission) {
case MemoryPermission::None:
case MemoryPermission::Read:
case MemoryPermission::Write:
case MemoryPermission::ReadWrite:
return true;
default:
return false;
}
};
if (!VerifyPermissions(static_cast<MemoryPermission>(my_permission)) ||
!VerifyPermissions(static_cast<MemoryPermission>(other_permission)))
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
if (addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) {
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
// When trying to create a memory block with address = 0,
// if the process has the Shared Device Memory flag in the exheader,
// then we have to allocate from the same region as the caller process instead of the BASE region.
Kernel::MemoryRegion region = Kernel::MemoryRegion::BASE;
if (addr == 0 && Kernel::g_current_process->flags.shared_device_mem)
region = Kernel::g_current_process->flags.memory_region;
shared_memory = SharedMemory::Create(Kernel::g_current_process, size,
static_cast<MemoryPermission>(my_permission), static_cast<MemoryPermission>(other_permission), addr, region);
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory)));
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr);
LOG_WARNING(Kernel_SVC, "called addr=0x%08X", addr);
return RESULT_SUCCESS;
}