mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-11-16 17:11:18 +01:00
NOTE: This work is not yet fully complete; kernel is done, but it was taking an exceedingly long time to get through libstratosphere. Thus, I've temporarily added -Wno-error=unused-result for libstratosphere/stratosphere. All warnings should be fixed to do the same thing Nintendo does as relevant, but this is taking a phenomenally long time and is not actually the most important work to do, so it can be put off for some time to prioritize other tasks for 21.0.0 support.
215 lines
9.3 KiB
C++
215 lines
9.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 {
|
|
|
|
class CacheOperation {
|
|
public:
|
|
virtual void Operate(void *address, size_t size) const = 0;
|
|
};
|
|
|
|
Result DoProcessCacheOperation(const CacheOperation &operation, KProcessPageTable &page_table, uintptr_t address, size_t size) {
|
|
/* Determine aligned extents. */
|
|
const uintptr_t aligned_start = util::AlignDown(address, PageSize);
|
|
const uintptr_t aligned_end = util::AlignUp(address + size, PageSize);
|
|
|
|
/* Iterate over and operate on contiguous ranges. */
|
|
uintptr_t cur_address = aligned_start;
|
|
size_t remaining = size;
|
|
while (remaining > 0) {
|
|
/* Get a contiguous range to operate on. */
|
|
KPageTableBase::MemoryRange contig_range;
|
|
R_TRY(page_table.OpenMemoryRangeForProcessCacheOperation(std::addressof(contig_range), cur_address, aligned_end - cur_address));
|
|
|
|
/* Close the range when we're done operating on it. */
|
|
ON_SCOPE_EXIT { contig_range.Close(); };
|
|
|
|
/* Adjust to remain within range. */
|
|
KVirtualAddress operate_address = KMemoryLayout::GetLinearVirtualAddress(contig_range.GetAddress());
|
|
size_t operate_size = contig_range.GetSize();
|
|
if (cur_address < address) {
|
|
operate_address += (address - cur_address);
|
|
operate_size -= (address - cur_address);
|
|
}
|
|
if (operate_size > remaining) {
|
|
operate_size = remaining;
|
|
}
|
|
|
|
/* Operate. */
|
|
operation.Operate(GetVoidPointer(operate_address), operate_size);
|
|
|
|
/* Advance. */
|
|
cur_address += contig_range.GetSize();
|
|
remaining -= operate_size;
|
|
}
|
|
MESOSPHERE_ASSERT(remaining == 0);
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
void FlushEntireDataCache() {
|
|
/* Flushing cache takes up to 1ms, so determine our minimum end tick. */
|
|
const s64 timeout = KHardwareTimer::GetTick() + ams::svc::Tick(TimeSpan::FromMilliSeconds(1));
|
|
|
|
/* Flush the entire data cache. */
|
|
cpu::FlushEntireDataCache();
|
|
|
|
/* Wait for 1ms to have passed. */
|
|
while (KHardwareTimer::GetTick() < timeout) {
|
|
cpu::Yield();
|
|
}
|
|
}
|
|
|
|
Result FlushDataCache(uintptr_t address, size_t size) {
|
|
/* Succeed if there's nothing to do. */
|
|
R_SUCCEED_IF(size == 0);
|
|
|
|
/* Validate that the region is within range. */
|
|
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(address, size), svc::ResultInvalidCurrentMemory());
|
|
|
|
/* Flush the cache. */
|
|
R_TRY(cpu::FlushDataCache(reinterpret_cast<void *>(address), size));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result InvalidateProcessDataCache(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
|
/* Validate address/size. */
|
|
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
|
R_UNLESS(address == static_cast<uintptr_t>(address), svc::ResultInvalidCurrentMemory());
|
|
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
|
|
|
/* Get the process from its handle. */
|
|
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
|
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
|
|
|
/* Invalidate the cache. */
|
|
if (process.GetPointerUnsafe() == GetCurrentProcessPointer()) {
|
|
R_TRY(process->GetPageTable().InvalidateCurrentProcessDataCache(address, size));
|
|
} else {
|
|
R_TRY(process->GetPageTable().InvalidateProcessDataCache(address, size));
|
|
}
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result StoreProcessDataCache(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
|
/* Validate address/size. */
|
|
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
|
R_UNLESS(address == static_cast<uintptr_t>(address), svc::ResultInvalidCurrentMemory());
|
|
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
|
|
|
/* Get the process from its handle. */
|
|
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
|
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
|
|
|
/* Verify the region is within range. */
|
|
auto &page_table = process->GetPageTable();
|
|
R_UNLESS(page_table.Contains(address, size), svc::ResultInvalidCurrentMemory());
|
|
|
|
/* Perform the operation. */
|
|
if (process.GetPointerUnsafe() == GetCurrentProcessPointer()) {
|
|
R_RETURN(cpu::StoreDataCache(reinterpret_cast<void *>(address), size));
|
|
} else {
|
|
class StoreCacheOperation : public CacheOperation {
|
|
public:
|
|
virtual void Operate(void *address, size_t size) const override { MESOSPHERE_R_ABORT_UNLESS(cpu::StoreDataCache(address, size)); }
|
|
} operation;
|
|
|
|
R_RETURN(DoProcessCacheOperation(operation, page_table, address, size));
|
|
}
|
|
}
|
|
|
|
Result FlushProcessDataCache(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
|
/* Validate address/size. */
|
|
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
|
R_UNLESS(address == static_cast<uintptr_t>(address), svc::ResultInvalidCurrentMemory());
|
|
R_UNLESS(size == static_cast<size_t>(size), svc::ResultInvalidCurrentMemory());
|
|
|
|
/* Get the process from its handle. */
|
|
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
|
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
|
|
|
/* Verify the region is within range. */
|
|
auto &page_table = process->GetPageTable();
|
|
R_UNLESS(page_table.Contains(address, size), svc::ResultInvalidCurrentMemory());
|
|
|
|
/* Perform the operation. */
|
|
if (process.GetPointerUnsafe() == GetCurrentProcessPointer()) {
|
|
R_RETURN(cpu::FlushDataCache(reinterpret_cast<void *>(address), size));
|
|
} else {
|
|
class FlushCacheOperation : public CacheOperation {
|
|
public:
|
|
virtual void Operate(void *address, size_t size) const override { MESOSPHERE_R_ABORT_UNLESS(cpu::FlushDataCache(address, size)); }
|
|
} operation;
|
|
|
|
R_RETURN(DoProcessCacheOperation(operation, page_table, address, size));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* ============================= 64 ABI ============================= */
|
|
|
|
void FlushEntireDataCache64() {
|
|
return FlushEntireDataCache();
|
|
}
|
|
|
|
Result FlushDataCache64(ams::svc::Address address, ams::svc::Size size) {
|
|
R_RETURN(FlushDataCache(address, size));
|
|
}
|
|
|
|
Result InvalidateProcessDataCache64(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
|
R_RETURN(InvalidateProcessDataCache(process_handle, address, size));
|
|
}
|
|
|
|
Result StoreProcessDataCache64(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
|
R_RETURN(StoreProcessDataCache(process_handle, address, size));
|
|
}
|
|
|
|
Result FlushProcessDataCache64(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
|
R_RETURN(FlushProcessDataCache(process_handle, address, size));
|
|
}
|
|
|
|
/* ============================= 64From32 ABI ============================= */
|
|
|
|
void FlushEntireDataCache64From32() {
|
|
return FlushEntireDataCache();
|
|
}
|
|
|
|
Result FlushDataCache64From32(ams::svc::Address address, ams::svc::Size size) {
|
|
R_RETURN(FlushDataCache(address, size));
|
|
}
|
|
|
|
Result InvalidateProcessDataCache64From32(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
|
R_RETURN(InvalidateProcessDataCache(process_handle, address, size));
|
|
}
|
|
|
|
Result StoreProcessDataCache64From32(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
|
R_RETURN(StoreProcessDataCache(process_handle, address, size));
|
|
}
|
|
|
|
Result FlushProcessDataCache64From32(ams::svc::Handle process_handle, uint64_t address, uint64_t size) {
|
|
R_RETURN(FlushProcessDataCache(process_handle, address, size));
|
|
}
|
|
|
|
}
|