1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-14 07:56:45 +00:00
Atmosphere/libraries/libmesosphere/source/svc/kern_svc_address_translation.cpp
Léo Lam 496adb0018
Minor header fixes to reduce parsing issues with Clang (#1700)
* Work around Clang's incomplete C++20 support for omitting typename

* vapours: fix Clang error about missing return in constexpr function

* stratosphere: fix call to non-constexpr strlen in constexpr function

strlen being constexpr is a non-compliant GCC extension; Clang
explicitly rejects it: https://reviews.llvm.org/D23692

* stratosphere: add a bunch of missing override specifiers

* stratosphere: work around Clang consteval bug

Minimal example: https://godbolt.org/z/MoM64v93M

The issue seems to be that Clang does not consider f(x) to be a
constant expression if x comes from a template argument that isn't
a non-type auto template argument (???)

We can work around this by relaxing GetMessageHeaderForCheck (by using
constexpr instead of consteval). This produces no functional changes
because the result of GetMessageHeaderForCheck() is assigned to a
constexpr variable, so the result is guaranteed to be computed
at compile-time.

* stratosphere: fix missing require clauses in definitions

GCC not requiring the require clauses to be repeated for member
definitions is actually a compiler bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96830

Clang rejects declarations with missing require clauses.

* Fix ALWAYS_INLINE_LAMBDA and parameter list relative order

While GCC doesn't seem to care about the position of the always_inline
attribute relative to the parameter list, Clang is very picky
and requires the attribute to appear after the parameter list
(and before a trailing return type)

* stratosphere: fix static constexpr member variable with incomplete type

GCC accepts this for some reason (because of the lambda?) but Clang
correctly rejects this.
2021-11-06 18:19:34 -07:00

159 lines
7.3 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));
return ResultSuccess();
}
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 QueryIoMappingFromPageTable = [&](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(pt.QueryIoMapping(std::addressof(found_address), phys_addr, size));
/* Use the size as the found size. */
found_size = size;
return ResultSuccess();
};
if (aligned) {
/* Query the input. */
R_TRY(QueryIoMappingFromPageTable(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(QueryIoMappingFromPageTable(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;
}
return ResultSuccess();
}
}
/* ============================= 64 ABI ============================= */
Result QueryPhysicalAddress64(ams::svc::lp64::PhysicalMemoryInfo *out_info, ams::svc::Address address) {
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));
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));
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),
};
return ResultSuccess();
}
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));
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));
return QueryIoMapping(reinterpret_cast<uintptr_t *>(out_address), nullptr, physical_address, size);
}
}