mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-18 01:46:47 +00:00
164 lines
7.6 KiB
C++
164 lines
7.6 KiB
C++
/*
|
|
* Copyright (c) Atmosphère-NX
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <mesosphere.hpp>
|
|
|
|
namespace ams::kern::svc {
|
|
|
|
/* ============================= Common ============================= */
|
|
|
|
namespace {
|
|
|
|
Result QueryPhysicalAddress(ams::svc::PhysicalMemoryInfo *out_info, uintptr_t address) {
|
|
/* NOTE: In 10.0.0, Nintendo stubbed this SVC. Should we do so? */
|
|
/* R_UNLESS(GetTargetFirmware() < TargetFirmware_10_0_0, svc::ResultInvalidCurrentMemory()); */
|
|
|
|
/* Get reference to page table. */
|
|
auto &pt = GetCurrentProcess().GetPageTable();
|
|
|
|
/* Check that the address is valid. */
|
|
R_UNLESS(pt.Contains(address, 1), svc::ResultInvalidCurrentMemory());
|
|
|
|
/* Query the physical mapping. */
|
|
R_TRY(pt.QueryPhysicalAddress(out_info, address));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result QueryIoMapping(uintptr_t *out_address, size_t *out_size, uint64_t phys_addr, size_t size) {
|
|
/* Declare variables we'll populate. */
|
|
KProcessAddress found_address = Null<KProcessAddress>;
|
|
size_t found_size = 0;
|
|
|
|
/* Get reference to page table. */
|
|
auto &pt = GetCurrentProcess().GetPageTable();
|
|
|
|
/* Check whether the address is aligned. */
|
|
const bool aligned = util::IsAligned(phys_addr, PageSize);
|
|
|
|
auto QueryMappingFromPageTable = [&](uint64_t phys_addr, size_t size) ALWAYS_INLINE_LAMBDA -> Result {
|
|
/* The size must be non-zero. */
|
|
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
|
|
|
/* The request must not overflow. */
|
|
R_UNLESS((phys_addr < phys_addr + size), svc::ResultNotFound());
|
|
|
|
/* Query the mapping. */
|
|
R_TRY_CATCH(pt.QueryIoMapping(std::addressof(found_address), phys_addr, size)) {
|
|
R_CATCH(svc::ResultNotFound) {
|
|
/* If we failed to find an io mapping, check if the address is a static mapping. */
|
|
R_TRY(pt.QueryStaticMapping(std::addressof(found_address), phys_addr, size));
|
|
}
|
|
} R_END_TRY_CATCH;
|
|
|
|
/* Use the size as the found size. */
|
|
found_size = size;
|
|
|
|
R_SUCCEED();
|
|
};
|
|
|
|
if (aligned) {
|
|
/* Query the input. */
|
|
R_TRY(QueryMappingFromPageTable(phys_addr, size));
|
|
} else {
|
|
if (kern::GetTargetFirmware() < TargetFirmware_8_0_0 && phys_addr >= PageSize) {
|
|
/* Query the aligned-down page. */
|
|
const size_t offset = phys_addr & (PageSize - 1);
|
|
R_TRY(QueryMappingFromPageTable(phys_addr - offset, size + offset));
|
|
|
|
/* Adjust the output address. */
|
|
found_address += offset;
|
|
} else {
|
|
/* Newer kernel only allows unaligned addresses when they're special enum members. */
|
|
R_UNLESS(phys_addr < PageSize, svc::ResultNotFound());
|
|
|
|
/* Try to find the memory region. */
|
|
const KMemoryRegion * const region = [] ALWAYS_INLINE_LAMBDA (ams::svc::MemoryRegionType type) -> const KMemoryRegion * {
|
|
switch (type) {
|
|
case ams::svc::MemoryRegionType_KernelTraceBuffer: return KMemoryLayout::GetPhysicalKernelTraceBufferRegion();
|
|
case ams::svc::MemoryRegionType_OnMemoryBootImage: return KMemoryLayout::GetPhysicalOnMemoryBootImageRegion();
|
|
case ams::svc::MemoryRegionType_DTB: return KMemoryLayout::GetPhysicalDTBRegion();
|
|
default: return nullptr;
|
|
}
|
|
}(static_cast<ams::svc::MemoryRegionType>(phys_addr));
|
|
|
|
/* Ensure that we found the region. */
|
|
R_UNLESS(region != nullptr, svc::ResultNotFound());
|
|
|
|
/* Chcek that the region is valid. */
|
|
MESOSPHERE_ABORT_UNLESS(region->GetEndAddress() != 0);
|
|
|
|
R_TRY(pt.QueryStaticMapping(std::addressof(found_address), region->GetAddress(), region->GetSize()));
|
|
found_size = region->GetSize();
|
|
}
|
|
}
|
|
|
|
/* We succeeded. */
|
|
MESOSPHERE_ASSERT(found_address != Null<KProcessAddress>);
|
|
MESOSPHERE_ASSERT(found_size != 0);
|
|
if (out_address != nullptr) {
|
|
*out_address = GetInteger(found_address);
|
|
}
|
|
if (out_size != nullptr) {
|
|
*out_size = found_size;
|
|
}
|
|
R_SUCCEED();
|
|
}
|
|
|
|
}
|
|
|
|
/* ============================= 64 ABI ============================= */
|
|
|
|
Result QueryPhysicalAddress64(ams::svc::lp64::PhysicalMemoryInfo *out_info, ams::svc::Address address) {
|
|
R_RETURN(QueryPhysicalAddress(out_info, address));
|
|
}
|
|
|
|
Result QueryIoMapping64(ams::svc::Address *out_address, ams::svc::Size *out_size, ams::svc::PhysicalAddress physical_address, ams::svc::Size size) {
|
|
static_assert(sizeof(*out_address) == sizeof(uintptr_t));
|
|
static_assert(sizeof(*out_size) == sizeof(size_t));
|
|
R_RETURN(QueryIoMapping(reinterpret_cast<uintptr_t *>(out_address), reinterpret_cast<size_t *>(out_size), physical_address, size));
|
|
}
|
|
|
|
Result LegacyQueryIoMapping64(ams::svc::Address *out_address, ams::svc::PhysicalAddress physical_address, ams::svc::Size size) {
|
|
static_assert(sizeof(*out_address) == sizeof(uintptr_t));
|
|
R_RETURN(QueryIoMapping(reinterpret_cast<uintptr_t *>(out_address), nullptr, physical_address, size));
|
|
}
|
|
|
|
/* ============================= 64From32 ABI ============================= */
|
|
|
|
Result QueryPhysicalAddress64From32(ams::svc::ilp32::PhysicalMemoryInfo *out_info, ams::svc::Address address) {
|
|
ams::svc::PhysicalMemoryInfo info = {};
|
|
R_TRY(QueryPhysicalAddress(std::addressof(info), address));
|
|
|
|
*out_info = {
|
|
.physical_address = info.physical_address,
|
|
.virtual_address = static_cast<u32>(info.virtual_address),
|
|
.size = static_cast<u32>(info.size),
|
|
};
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result QueryIoMapping64From32(ams::svc::Address *out_address, ams::svc::Size *out_size, ams::svc::PhysicalAddress physical_address, ams::svc::Size size) {
|
|
static_assert(sizeof(*out_address) == sizeof(uintptr_t));
|
|
static_assert(sizeof(*out_size) == sizeof(size_t));
|
|
R_RETURN(QueryIoMapping(reinterpret_cast<uintptr_t *>(out_address), reinterpret_cast<size_t *>(out_size), physical_address, size));
|
|
}
|
|
|
|
Result LegacyQueryIoMapping64From32(ams::svc::Address *out_address, ams::svc::PhysicalAddress physical_address, ams::svc::Size size) {
|
|
static_assert(sizeof(*out_address) == sizeof(uintptr_t));
|
|
R_RETURN(QueryIoMapping(reinterpret_cast<uintptr_t *>(out_address), nullptr, physical_address, size));
|
|
}
|
|
|
|
}
|