Merge pull request #1689 from Subv/shmem
Kernel: Implemented shared memory.
This commit is contained in:
commit
18b517e236
18 changed files with 420 additions and 131 deletions
|
@ -107,7 +107,6 @@ struct MemoryArea {
|
|||
|
||||
// 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)
|
||||
};
|
||||
|
||||
|
|
|
@ -209,7 +209,7 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p
|
|||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
// Expansion of the linear heap is only allowed if you do an allocation immediatelly at its
|
||||
// Expansion of the linear heap is only allowed if you do an allocation immediately 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) {
|
||||
|
|
|
@ -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,157 @@ 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;
|
||||
|
||||
// Increase the amount of used linear heap memory for the owner process.
|
||||
if (shared_memory->owner_process != nullptr) {
|
||||
shared_memory->owner_process->linear_heap_used += size;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// TODO(Subv): What happens if an application tries to create multiple memory blocks pointing to the same address?
|
||||
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);
|
||||
shared_memory->backing_block_offset = 0;
|
||||
// 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);
|
||||
// Reprotect the block with the new permissions
|
||||
vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions));
|
||||
}
|
||||
|
||||
shared_memory->base_address = address;
|
||||
return shared_memory;
|
||||
}
|
||||
|
||||
ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions,
|
||||
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
|
||||
MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
|
||||
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
|
||||
|
||||
shared_memory->owner_process = nullptr;
|
||||
shared_memory->name = std::move(name);
|
||||
shared_memory->size = size;
|
||||
shared_memory->permissions = permissions;
|
||||
shared_memory->other_permissions = other_permissions;
|
||||
shared_memory->backing_block = heap_block;
|
||||
shared_memory->backing_block_offset = offset;
|
||||
shared_memory->base_address = Memory::HEAP_VADDR + offset;
|
||||
|
||||
return shared_memory;
|
||||
}
|
||||
|
||||
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);
|
||||
MemoryPermission own_other_permissions = target_process == owner_process ? this->permissions : this->other_permissions;
|
||||
|
||||
// Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
|
||||
if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
|
||||
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
|
||||
}
|
||||
|
||||
// TODO(Subv): Return E0E01BEE when permissions and other_permissions don't
|
||||
// match what was specified when the memory block was created.
|
||||
// Error out if the requested permissions don't match what the creator process allows.
|
||||
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
|
||||
GetObjectId(), address, name.c_str());
|
||||
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
|
||||
}
|
||||
|
||||
// TODO(Subv): Return E0E01BEE when address should be 0.
|
||||
// Note: Find out when that's the case.
|
||||
// Heap-backed memory blocks can not be mapped with other_permissions = DontCare
|
||||
if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
|
||||
GetObjectId(), address, name.c_str());
|
||||
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);
|
||||
// Error out if the provided permissions are not compatible with what the creator process needs.
|
||||
if (other_permissions != MemoryPermission::DontCare &&
|
||||
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
|
||||
GetObjectId(), address, name.c_str());
|
||||
return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}*/
|
||||
|
||||
// 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.
|
||||
|
||||
if (address != 0) {
|
||||
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());
|
||||
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS,
|
||||
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
|
||||
}
|
||||
|
||||
// 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!",
|
||||
GetObjectId(), address, name.c_str());
|
||||
// TODO: Verify error code with hardware
|
||||
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
|
||||
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
|
||||
VAddr target_address = address;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// TODO: Test permissions
|
||||
|
||||
// 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);
|
||||
// Map the memory block into the target process
|
||||
auto result = target_process->vm_manager.MapMemoryBlock(target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
|
||||
if (result.Failed()) {
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, target_address=0x%08X name=%s, error mapping to virtual memory",
|
||||
GetObjectId(), target_address, name.c_str());
|
||||
return result.Code();
|
||||
}
|
||||
|
||||
this->base_address = address;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return target_process->vm_manager.ReprotectRange(target_address, size, ConvertPermissions(permissions));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
|
||||
u32 masked_permissions = static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute);
|
||||
return static_cast<VMAPermission>(masked_permissions);
|
||||
};
|
||||
|
||||
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
|
||||
|
|
|
@ -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 {
|
||||
|
@ -29,14 +30,29 @@ enum class MemoryPermission : u32 {
|
|||
class SharedMemory final : public Object {
|
||||
public:
|
||||
/**
|
||||
* Creates a shared memory object
|
||||
* 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(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions, VAddr address = 0, MemoryRegion region = MemoryRegion::BASE, std::string name = "Unknown");
|
||||
|
||||
/**
|
||||
* Creates a shared memory object from a block of memory managed by an HLE applet.
|
||||
* @param heap_block Heap block of the HLE applet.
|
||||
* @param offset The offset into the heap block that the SharedMemory will map.
|
||||
* @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 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> CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
|
||||
MemoryPermission permissions, MemoryPermission other_permissions, std::string name = "Unknown Applet");
|
||||
|
||||
std::string GetTypeName() const override { return "SharedMemory"; }
|
||||
std::string GetName() const override { return name; }
|
||||
|
@ -45,19 +61,27 @@ public:
|
|||
HandleType GetHandleType() const override { return HANDLE_TYPE; }
|
||||
|
||||
/**
|
||||
* Maps a shared memory block to an address in system memory
|
||||
* Converts the specified MemoryPermission into the equivalent VMAPermission.
|
||||
* @param permission The MemoryPermission to convert.
|
||||
*/
|
||||
static VMAPermission ConvertPermissions(MemoryPermission permission);
|
||||
|
||||
/**
|
||||
* 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 +90,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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue