/* * Copyright (c) 2018-2020 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 . */ #include #include "mem_impl_platform.hpp" namespace ams::mem::impl { namespace { os::Mutex g_virt_mem_enabled_lock; bool g_virt_mem_enabled_detected; bool g_virt_mem_enabled; void EnsureVirtualAddressMemoryDetected() { std::scoped_lock lk(g_virt_mem_enabled_lock); if (AMS_LIKELY(g_virt_mem_enabled_detected)) { return; } g_virt_mem_enabled = os::IsVirtualAddressMemoryEnabled(); } ALWAYS_INLINE bool IsVirtualAddressMemoryEnabled() { EnsureVirtualAddressMemoryDetected(); return g_virt_mem_enabled; } ALWAYS_INLINE errno_t ConvertResult(Result result) { /* TODO: Actually implement this in a meaningful way. */ if (R_FAILED(result)) { return EINVAL; } return 0; } ALWAYS_INLINE os::MemoryPermission ConvertToOsPermission(Prot prot) { static_assert(static_cast(Prot_read) == static_cast(os::MemoryPermission_ReadOnly)); static_assert(static_cast(Prot_write) == static_cast(os::MemoryPermission_WriteOnly)); return static_cast(prot & os::MemoryPermission_ReadWrite); } } errno_t virtual_alloc(void **ptr, size_t size) { /* Ensure size isn't too large. */ if (size > mem::impl::MaxSize) { return EINVAL; } /* Allocate virtual memory. */ uintptr_t addr; if (IsVirtualAddressMemoryEnabled()) { /* TODO: Support virtual address memory. */ AMS_ABORT("Virtual address memory not supported yet"); } else { if (auto err = ConvertResult(os::AllocateMemoryBlock(std::addressof(addr), util::AlignUp(size, os::MemoryBlockUnitSize))); err != 0) { return err; } os::SetMemoryPermission(addr, size, os::MemoryPermission_None); } return 0; } errno_t virtual_free(void *ptr, size_t size) { /* Ensure size isn't zero. */ if (size == 0) { return EINVAL; } if (IsVirtualAddressMemoryEnabled()) { /* TODO: Support virtual address memory. */ AMS_ABORT("Virtual address memory not supported yet"); } else { os::FreeMemoryBlock(reinterpret_cast(ptr), util::AlignUp(size, os::MemoryBlockUnitSize)); } return 0; } errno_t physical_alloc(void *ptr, size_t size, Prot prot) { /* Detect empty allocation. */ const uintptr_t aligned_start = util::AlignDown(reinterpret_cast(ptr), os::MemoryPageSize); const uintptr_t aligned_end = util::AlignUp(reinterpret_cast(ptr) + size, os::MemoryPageSize); const size_t aligned_size = aligned_end - aligned_start; if (aligned_end <= aligned_start) { return 0; } if (IsVirtualAddressMemoryEnabled()) { /* TODO: Support virtual address memory. */ AMS_ABORT("Virtual address memory not supported yet"); } else { os::SetMemoryPermission(aligned_start, aligned_size, ConvertToOsPermission(prot)); } return 0; } errno_t physical_free(void *ptr, size_t size) { /* Detect empty allocation. */ const uintptr_t aligned_start = util::AlignDown(reinterpret_cast(ptr), os::MemoryPageSize); const uintptr_t aligned_end = util::AlignUp(reinterpret_cast(ptr) + size, os::MemoryPageSize); const size_t aligned_size = aligned_end - aligned_start; if (aligned_end <= aligned_start) { return 0; } if (IsVirtualAddressMemoryEnabled()) { /* TODO: Support virtual address memory. */ AMS_ABORT("Virtual address memory not supported yet"); } else { os::SetMemoryPermission(aligned_start, aligned_size, os::MemoryPermission_None); } return 0; } size_t strlcpy(char *dst, const char *src, size_t size) { const size_t src_size = std::strlen(src); if (src_size >= size) { if (size) { std::memcpy(dst, src, size - 1); dst[size - 1] = 0; } } else { std::memcpy(dst, src, src_size + 1); } return src_size; } errno_t gen_random(void *dst, size_t dst_size) { os::GenerateRandomBytes(dst, dst_size); return 0; } errno_t epochtime(s64 *dst) { /* TODO: What is this calc? */ auto ts = os::ConvertToTimeSpan(os::GetSystemTick()); *dst = (ts.GetNanoSeconds() / INT64_C(100)) + INT64_C(0x8A09F909AE60000); return 0; } errno_t getcpu(s32 *out) { *out = os::GetCurrentCoreNumber(); return 0; } }