kernel: transfer_memory: Properly reserve and reset memory region.

master
bunnei 2020-01-30 22:39:07 +07:00
parent 7a547b9342
commit ba53543da6
5 changed files with 116 additions and 40 deletions

@ -1863,10 +1863,14 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
} }
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms);
if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) {
return reserve_result;
}
auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
const auto result = handle_table.Create(std::move(transfer_mem_handle)); const auto result{handle_table.Create(std::move(transfer_mem_handle))};
if (result.Failed()) { if (result.Failed()) {
return result.Code(); return result.Code();
} }

@ -8,15 +8,23 @@
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/transfer_memory.h" #include "core/hle/kernel/transfer_memory.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel { namespace Kernel {
TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {} TransferMemory::TransferMemory(KernelCore& kernel, Memory::Memory& memory)
TransferMemory::~TransferMemory() = default; : Object{kernel}, memory{memory} {}
std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, TransferMemory::~TransferMemory() {
u64 size, MemoryPermission permissions) { // Release memory region when transfer memory is destroyed
std::shared_ptr<TransferMemory> transfer_memory{std::make_shared<TransferMemory>(kernel)}; Reset();
}
std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, Memory::Memory& memory,
VAddr base_address, u64 size,
MemoryPermission permissions) {
std::shared_ptr<TransferMemory> transfer_memory{
std::make_shared<TransferMemory>(kernel, memory)};
transfer_memory->base_address = base_address; transfer_memory->base_address = base_address;
transfer_memory->memory_size = size; transfer_memory->memory_size = size;
@ -27,7 +35,7 @@ std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr
} }
const u8* TransferMemory::GetPointer() const { const u8* TransferMemory::GetPointer() const {
return backing_block.get()->data(); return memory.GetPointer(base_address);
} }
u64 TransferMemory::GetSize() const { u64 TransferMemory::GetSize() const {
@ -62,6 +70,52 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
ResultCode TransferMemory::Reserve() {
auto& vm_manager{owner_process->VMManager()};
const auto check_range_result{vm_manager.CheckRangeState(
base_address, memory_size, MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated,
MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::All,
VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None,
MemoryAttribute::IpcAndDeviceMapped)};
if (check_range_result.Failed()) {
return check_range_result.Code();
}
auto [state_, permissions_, attribute] = *check_range_result;
if (const auto result{vm_manager.ReprotectRange(
base_address, memory_size, SharedMemory::ConvertPermissions(owner_permissions))};
result.IsError()) {
return result;
}
return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask,
attribute | MemoryAttribute::Locked);
}
ResultCode TransferMemory::Reset() {
auto& vm_manager{owner_process->VMManager()};
if (const auto result{vm_manager.CheckRangeState(
base_address, memory_size,
MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated,
MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::None,
VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked,
MemoryAttribute::IpcAndDeviceMapped)};
result.Failed()) {
return result.Code();
}
if (const auto result{
vm_manager.ReprotectRange(base_address, memory_size, VMAPermission::ReadWrite)};
result.IsError()) {
return result;
}
return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask,
MemoryAttribute::None);
}
ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) { ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {
if (memory_size != size) { if (memory_size != size) {
return ERR_INVALID_SIZE; return ERR_INVALID_SIZE;

@ -11,6 +11,10 @@
union ResultCode; union ResultCode;
namespace Memory {
class Memory;
}
namespace Kernel { namespace Kernel {
class KernelCore; class KernelCore;
@ -26,12 +30,13 @@ enum class MemoryPermission : u32;
/// ///
class TransferMemory final : public Object { class TransferMemory final : public Object {
public: public:
explicit TransferMemory(KernelCore& kernel); explicit TransferMemory(KernelCore& kernel, Memory::Memory& memory);
~TransferMemory() override; ~TransferMemory() override;
static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size, static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Memory::Memory& memory,
VAddr base_address, u64 size,
MemoryPermission permissions); MemoryPermission permissions);
TransferMemory(const TransferMemory&) = delete; TransferMemory(const TransferMemory&) = delete;
@ -80,6 +85,14 @@ public:
/// ///
ResultCode UnmapMemory(VAddr address, u64 size); ResultCode UnmapMemory(VAddr address, u64 size);
/// Reserves the region to be used for the transfer memory, called after the transfer memory is
/// created.
ResultCode Reserve();
/// Resets the region previously used for the transfer memory, called after the transfer memory
/// is closed.
ResultCode Reset();
private: private:
/// Memory block backing this instance. /// Memory block backing this instance.
std::shared_ptr<PhysicalMemory> backing_block; std::shared_ptr<PhysicalMemory> backing_block;
@ -98,6 +111,8 @@ private:
/// Whether or not this transfer memory instance has mapped memory. /// Whether or not this transfer memory instance has mapped memory.
bool is_mapped = false; bool is_mapped = false;
Memory::Memory& memory;
}; };
} // namespace Kernel } // namespace Kernel

@ -544,7 +544,8 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const {
ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,
MemoryAttribute attribute) { MemoryAttribute attribute) {
constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped; constexpr auto ignore_mask =
MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped | MemoryAttribute::Locked;
constexpr auto attribute_mask = ~ignore_mask; constexpr auto attribute_mask = ~ignore_mask;
const auto result = CheckRangeState( const auto result = CheckRangeState(

@ -98,6 +98,8 @@ enum class MemoryAttribute : u32 {
DeviceMapped = 4, DeviceMapped = 4,
/// Uncached memory /// Uncached memory
Uncached = 8, Uncached = 8,
IpcAndDeviceMapped = LockedForIPC | DeviceMapped,
}; };
constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) { constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) {
@ -654,6 +656,35 @@ public:
/// is scheduled. /// is scheduled.
Common::PageTable page_table{Memory::PAGE_BITS}; Common::PageTable page_table{Memory::PAGE_BITS};
using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
/// Checks if an address range adheres to the specified states provided.
///
/// @param address The starting address of the address range.
/// @param size The size of the address range.
/// @param state_mask The memory state mask.
/// @param state The state to compare the individual VMA states against,
/// which is done in the form of: (vma.state & state_mask) != state.
/// @param permission_mask The memory permissions mask.
/// @param permissions The permission to compare the individual VMA permissions against,
/// which is done in the form of:
/// (vma.permission & permission_mask) != permission.
/// @param attribute_mask The memory attribute mask.
/// @param attribute The memory attributes to compare the individual VMA attributes
/// against, which is done in the form of:
/// (vma.attributes & attribute_mask) != attribute.
/// @param ignore_mask The memory attributes to ignore during the check.
///
/// @returns If successful, returns a tuple containing the memory attributes
/// (with ignored bits specified by ignore_mask unset), memory permissions, and
/// memory state across the memory range.
/// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
///
CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
VMAPermission permission_mask, VMAPermission permissions,
MemoryAttribute attribute_mask, MemoryAttribute attribute,
MemoryAttribute ignore_mask) const;
private: private:
using VMAIter = VMAMap::iterator; using VMAIter = VMAMap::iterator;
@ -707,35 +738,6 @@ private:
/// Clears out the page table /// Clears out the page table
void ClearPageTable(); void ClearPageTable();
using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
/// Checks if an address range adheres to the specified states provided.
///
/// @param address The starting address of the address range.
/// @param size The size of the address range.
/// @param state_mask The memory state mask.
/// @param state The state to compare the individual VMA states against,
/// which is done in the form of: (vma.state & state_mask) != state.
/// @param permission_mask The memory permissions mask.
/// @param permissions The permission to compare the individual VMA permissions against,
/// which is done in the form of:
/// (vma.permission & permission_mask) != permission.
/// @param attribute_mask The memory attribute mask.
/// @param attribute The memory attributes to compare the individual VMA attributes
/// against, which is done in the form of:
/// (vma.attributes & attribute_mask) != attribute.
/// @param ignore_mask The memory attributes to ignore during the check.
///
/// @returns If successful, returns a tuple containing the memory attributes
/// (with ignored bits specified by ignore_mask unset), memory permissions, and
/// memory state across the memory range.
/// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
///
CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
VMAPermission permission_mask, VMAPermission permissions,
MemoryAttribute attribute_mask, MemoryAttribute attribute,
MemoryAttribute ignore_mask) const;
/// Gets the amount of memory currently mapped (state != Unmapped) in a range. /// Gets the amount of memory currently mapped (state != Unmapped) in a range.
ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const;