core: hle: kernel: k_page_table: Add implementations of MapPages, UnmapPages, and FindFreeArea for TLS.
This commit is contained in:
parent
15d9b0418f
commit
3210bc2767
|
@ -424,6 +424,68 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
|
||||||
|
std::size_t num_pages, std::size_t alignment, std::size_t offset,
|
||||||
|
std::size_t guard_pages) {
|
||||||
|
VAddr address = 0;
|
||||||
|
|
||||||
|
if (num_pages <= region_num_pages) {
|
||||||
|
if (this->IsAslrEnabled()) {
|
||||||
|
// Try to directly find a free area up to 8 times.
|
||||||
|
for (std::size_t i = 0; i < 8; i++) {
|
||||||
|
const std::size_t random_offset =
|
||||||
|
KSystemControl::GenerateRandomRange(
|
||||||
|
0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) *
|
||||||
|
alignment;
|
||||||
|
const VAddr candidate =
|
||||||
|
Common::AlignDown((region_start + random_offset), alignment) + offset;
|
||||||
|
|
||||||
|
KMemoryInfo info = this->QueryInfoImpl(candidate);
|
||||||
|
|
||||||
|
if (info.state != KMemoryState::Free) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (region_start > candidate) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (info.GetAddress() + guard_pages * PageSize > candidate) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1;
|
||||||
|
if (candidate_end > info.GetLastAddress()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (candidate_end > region_start + region_num_pages * PageSize - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
address = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Fall back to finding the first free area with a random offset.
|
||||||
|
if (address == 0) {
|
||||||
|
// NOTE: Nintendo does not account for guard pages here.
|
||||||
|
// This may theoretically cause an offset to be chosen that cannot be mapped. We
|
||||||
|
// will account for guard pages.
|
||||||
|
const std::size_t offset_pages = KSystemControl::GenerateRandomRange(
|
||||||
|
0, region_num_pages - num_pages - guard_pages);
|
||||||
|
address = block_manager->FindFreeArea(region_start + offset_pages * PageSize,
|
||||||
|
region_num_pages - offset_pages, num_pages,
|
||||||
|
alignment, offset, guard_pages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the first free area.
|
||||||
|
if (address == 0) {
|
||||||
|
address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages,
|
||||||
|
alignment, offset, guard_pages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
|
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
|
||||||
KPageTable& src_page_table, VAddr src_addr) {
|
KPageTable& src_page_table, VAddr src_addr) {
|
||||||
KScopedLightLock lk(general_lock);
|
KScopedLightLock lk(general_lock);
|
||||||
|
@ -1055,6 +1117,46 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultCode KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
|
||||||
|
PAddr phys_addr, bool is_pa_valid, VAddr region_start,
|
||||||
|
std::size_t region_num_pages, KMemoryState state,
|
||||||
|
KMemoryPermission perm) {
|
||||||
|
ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
|
||||||
|
|
||||||
|
// Ensure this is a valid map request.
|
||||||
|
R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
|
||||||
|
ResultInvalidCurrentMemory);
|
||||||
|
R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
|
||||||
|
|
||||||
|
// Lock the table.
|
||||||
|
KScopedLightLock lk(general_lock);
|
||||||
|
|
||||||
|
// Find a random address to map at.
|
||||||
|
VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
|
||||||
|
this->GetNumGuardPages());
|
||||||
|
R_UNLESS(addr != 0, ResultOutOfMemory);
|
||||||
|
ASSERT(Common::IsAligned(addr, alignment));
|
||||||
|
ASSERT(this->CanContain(addr, num_pages * PageSize, state));
|
||||||
|
ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
|
||||||
|
KMemoryPermission::None, KMemoryPermission::None,
|
||||||
|
KMemoryAttribute::None, KMemoryAttribute::None)
|
||||||
|
.IsSuccess());
|
||||||
|
|
||||||
|
// Perform mapping operation.
|
||||||
|
if (is_pa_valid) {
|
||||||
|
R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr));
|
||||||
|
} else {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the blocks.
|
||||||
|
block_manager->Update(addr, num_pages, state, perm);
|
||||||
|
|
||||||
|
// We successfully mapped the pages.
|
||||||
|
*out_addr = addr;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
|
ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
|
||||||
ASSERT(this->IsLockedByCurrentThread());
|
ASSERT(this->IsLockedByCurrentThread());
|
||||||
|
|
||||||
|
@ -1097,6 +1199,30 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) {
|
||||||
|
// Check that the unmap is in range.
|
||||||
|
const std::size_t size = num_pages * PageSize;
|
||||||
|
R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
|
||||||
|
|
||||||
|
// Lock the table.
|
||||||
|
KScopedLightLock lk(general_lock);
|
||||||
|
|
||||||
|
// Check the memory state.
|
||||||
|
std::size_t num_allocator_blocks{};
|
||||||
|
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
|
||||||
|
KMemoryState::All, state, KMemoryPermission::None,
|
||||||
|
KMemoryPermission::None, KMemoryAttribute::All,
|
||||||
|
KMemoryAttribute::None));
|
||||||
|
|
||||||
|
// Perform the unmap.
|
||||||
|
R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap));
|
||||||
|
|
||||||
|
// Update the blocks.
|
||||||
|
block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None);
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
|
ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
|
||||||
Svc::MemoryPermission svc_perm) {
|
Svc::MemoryPermission svc_perm) {
|
||||||
const size_t num_pages = size / PageSize;
|
const size_t num_pages = size / PageSize;
|
||||||
|
|
|
@ -46,7 +46,14 @@ public:
|
||||||
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||||
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
|
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
|
||||||
KMemoryPermission perm);
|
KMemoryPermission perm);
|
||||||
|
ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
|
||||||
|
PAddr phys_addr, KMemoryState state, KMemoryPermission perm) {
|
||||||
|
return this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
|
||||||
|
this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize,
|
||||||
|
state, perm);
|
||||||
|
}
|
||||||
ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
|
ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
|
||||||
|
ResultCode UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state);
|
||||||
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
|
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
|
||||||
Svc::MemoryPermission svc_perm);
|
Svc::MemoryPermission svc_perm);
|
||||||
KMemoryInfo QueryInfo(VAddr addr);
|
KMemoryInfo QueryInfo(VAddr addr);
|
||||||
|
@ -91,6 +98,9 @@ private:
|
||||||
ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
|
ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
|
||||||
ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
|
ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
|
||||||
KMemoryPermission perm);
|
KMemoryPermission perm);
|
||||||
|
ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
|
||||||
|
PAddr phys_addr, bool is_pa_valid, VAddr region_start,
|
||||||
|
std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
|
||||||
ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
|
ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
|
||||||
bool IsRegionMapped(VAddr address, u64 size);
|
bool IsRegionMapped(VAddr address, u64 size);
|
||||||
bool IsRegionContiguous(VAddr addr, u64 size) const;
|
bool IsRegionContiguous(VAddr addr, u64 size) const;
|
||||||
|
@ -105,6 +115,9 @@ private:
|
||||||
VAddr GetRegionAddress(KMemoryState state) const;
|
VAddr GetRegionAddress(KMemoryState state) const;
|
||||||
std::size_t GetRegionSize(KMemoryState state) const;
|
std::size_t GetRegionSize(KMemoryState state) const;
|
||||||
|
|
||||||
|
VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
|
||||||
|
std::size_t alignment, std::size_t offset, std::size_t guard_pages);
|
||||||
|
|
||||||
ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
|
ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
|
||||||
std::size_t size, KMemoryState state_mask,
|
std::size_t size, KMemoryState state_mask,
|
||||||
KMemoryState state, KMemoryPermission perm_mask,
|
KMemoryState state, KMemoryPermission perm_mask,
|
||||||
|
@ -137,7 +150,7 @@ private:
|
||||||
return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
|
return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
|
||||||
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
|
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
|
||||||
}
|
}
|
||||||
ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask,
|
ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
|
||||||
KMemoryState state, KMemoryPermission perm_mask,
|
KMemoryState state, KMemoryPermission perm_mask,
|
||||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||||
KMemoryAttribute attr,
|
KMemoryAttribute attr,
|
||||||
|
@ -210,7 +223,7 @@ public:
|
||||||
constexpr VAddr GetAliasCodeRegionSize() const {
|
constexpr VAddr GetAliasCodeRegionSize() const {
|
||||||
return alias_code_region_end - alias_code_region_start;
|
return alias_code_region_end - alias_code_region_start;
|
||||||
}
|
}
|
||||||
size_t GetNormalMemorySize() {
|
std::size_t GetNormalMemorySize() {
|
||||||
KScopedLightLock lk(general_lock);
|
KScopedLightLock lk(general_lock);
|
||||||
return GetHeapSize() + mapped_physical_memory_size;
|
return GetHeapSize() + mapped_physical_memory_size;
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue