kern/test: add wip qemu-virt board support to mesosphere

This commit is contained in:
Michael Scire 2021-10-20 20:53:42 -07:00
parent 10ed579c38
commit 0a1ce6f079
39 changed files with 19796 additions and 27 deletions

View file

@ -0,0 +1,5 @@
export ATMOSPHERE_DEFINES += -DATMOSPHERE_BOARD_QEMU_VIRT -D__SWITCH__
export ATMOSPHERE_SETTINGS +=
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=
export ATMOSPHERE_ASFLAGS +=

View file

@ -52,6 +52,21 @@ export ATMOSPHERE_OS_NAME := horizon
export ATMOSPHERE_CPU_EXTENSIONS :=
endif
else ifeq ($(ATMOSPHERE_BOARD),qemu-virt)
ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57)
export ATMOSPHERE_ARCH_DIR := arm64
export ATMOSPHERE_BOARD_DIR := qemu/virt
export ATMOSPHERE_OS_DIR := horizon
export ATMOSPHERE_ARCH_NAME := arm64
export ATMOSPHERE_BOARD_NAME := qemu_virt
export ATMOSPHERE_OS_NAME := horizon
export ATMOSPHERE_CPU_EXTENSIONS := arm_crypto_extension aarch64_crypto_extension
endif
endif
ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57)

View file

@ -104,6 +104,18 @@ $(eval $(call ATMOSPHERE_ADD_TARGET, audit, $(TARGET)_audit.a, \
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_release, $(TARGET)_qemu_virt.a, \
ATMOSPHERE_BUILD_SETTINGS="" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_debug, $(TARGET)_qemu_virt_debug.a, \
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_DEBUGGING" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_audit, $(TARGET)_qemu_virt_audit.a, \
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
))
#---------------------------------------------------------------------------------
-include $(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME).mk

View file

@ -30,6 +30,8 @@ namespace ams::kern::arch::arm64::cpu {
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
constexpr inline size_t NumCores = 4;
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
constexpr inline size_t NumCores = 4;
#else
#error "Unknown Board for cpu::NumCores"
#endif

View file

@ -36,6 +36,10 @@ namespace ams::kern::arch::arm64 {
KInterruptName_SecurePhysicalTimer = 29,
KInterruptName_NonSecurePhysicalTimer = 30,
KInterruptName_LegacyNIrq = 31,
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
KInterruptName_VirtualTimer = 27,
KInterruptName_SecurePhysicalTimer = 29,
KInterruptName_NonSecurePhysicalTimer = 30,
#endif
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)

View file

@ -44,13 +44,13 @@ namespace ams::kern::board::generic {
return ams::kern::svc::ResultNotImplemented();
}
Result ALWAYS_INLINE Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) {
MESOSPHERE_UNUSED(out_mapped_size, pg, device_address, device_perm, refresh_mappings);
Result ALWAYS_INLINE Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned) {
MESOSPHERE_UNUSED(page_table, process_address, size, device_address, device_perm, is_aligned);
return ams::kern::svc::ResultNotImplemented();
}
Result ALWAYS_INLINE Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address) {
MESOSPHERE_UNUSED(pg, device_address);
Result ALWAYS_INLINE Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) {
MESOSPHERE_UNUSED(page_table, process_address, size, device_address);
return ams::kern::svc::ResultNotImplemented();
}

View file

@ -0,0 +1,33 @@
/*
* 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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
namespace ams::kern::board::qemu::virt::impl::cpu {
/* Virtual to Physical core map. */
constexpr inline const s32 VirtualToPhysicalCoreMap[BITSIZEOF(u64)] = {
0, 1, 2, 3, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 3,
};
}

View file

@ -0,0 +1,24 @@
/*
* 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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
namespace ams::kern {
constexpr inline size_t MainMemorySize = 4_GB;
constexpr inline size_t MainMemorySizeMax = 8_GB;
}

View file

@ -0,0 +1,20 @@
/*
* 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/>.
*/
/* All architectures must define NumBoardDeviceRegions. */
constexpr inline const auto NumBoardDeviceRegions = 0;
/* UNUSED: .Derive(NumBoardDeviceRegions, 0); */

View file

@ -0,0 +1,85 @@
/*
* 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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
namespace ams::kern {
struct InitialProcessBinaryLayout;
}
namespace ams::kern::board::qemu::virt {
class KSystemControl {
public:
class Init {
public:
/* Initialization. */
static size_t GetIntendedMemorySize();
static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address);
static void GetInitialProcessBinaryLayout(InitialProcessBinaryLayout *out);
static bool ShouldIncreaseThreadResourceLimit();
static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
static size_t GetApplicationPoolSize();
static size_t GetAppletPoolSize();
static size_t GetMinimumNonSecureSystemPoolSize();
static u8 GetDebugLogUartPort();
/* Randomness. */
static void GenerateRandomBytes(void *dst, size_t size);
static u64 GenerateRandomRange(u64 min, u64 max);
};
public:
/* Initialization. */
static NOINLINE void InitializePhase1();
static NOINLINE void InitializePhase2();
static NOINLINE u32 GetCreateProcessMemoryPool();
/* Randomness. */
static void GenerateRandomBytes(void *dst, size_t size);
static u64 GenerateRandomRange(u64 min, u64 max);
static u64 GenerateRandomU64();
/* Privileged Access. */
static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
static Result ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
static ALWAYS_INLINE u32 ReadRegisterPrivileged(ams::svc::PhysicalAddress address) {
u32 v;
ReadWriteRegisterPrivileged(std::addressof(v), address, 0x00000000u, 0);
return v;
}
static ALWAYS_INLINE void WriteRegisterPrivileged(ams::svc::PhysicalAddress address, u32 value) {
u32 v;
ReadWriteRegisterPrivileged(std::addressof(v), address, 0xFFFFFFFFu, value);
}
/* Power management. */
static void SleepSystem();
static NORETURN void StopSystem(void *arg = nullptr);
/* User access. */
static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
/* Secure Memory. */
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
static Result AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool);
static void FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool);
};
}

View file

@ -43,6 +43,8 @@ namespace ams::kern {
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
#define MESOSPHERE_DEBUG_LOG_USE_UART
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
#define MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING
#else
#error "Unknown board for Default Debug Log Source"
#endif

View file

@ -20,6 +20,8 @@
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include <mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp>
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
#include <mesosphere/board/qemu/virt/kern_k_memory_layout.hpp>
#else
#error "Unknown board for KMemoryLayout"
#endif

View file

@ -39,6 +39,16 @@
}
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
#include <mesosphere/board/qemu/virt/kern_cpu_map.hpp>
namespace ams::kern::cpu {
using namespace ams::kern::board::qemu::virt::impl::cpu;
}
#else
#error "Unknown board for CPU Map"
#endif

View file

@ -23,6 +23,13 @@
using ams::kern::board::nintendo::nx::KSystemControl;
}
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
#include <mesosphere/board/qemu/virt/kern_k_system_control.hpp>
namespace ams::kern {
using ams::kern::board::qemu::virt::KSystemControl;
}
#else
#error "Unknown board for KSystemControl"
#endif

View file

@ -26,6 +26,22 @@ namespace ams::kern::board::nintendo::nx::smc {
enum UserFunctionId : u32 {
UserFunctionId_SetConfig = 0xC3000401,
UserFunctionId_GetConfigUser = 0xC3000002,
UserFunctionId_GetResult = 0xC3000003,
UserFunctionId_GetResultData = 0xC3000404,
UserFunctionId_ModularExponentiate = 0xC3000E05,
UserFunctionId_GenerateRandomBytes = 0xC3000006,
UserFunctionId_GenerateAesKek = 0xC3000007,
UserFunctionId_LoadAesKey = 0xC3000008,
UserFunctionId_ComputeAes = 0xC3000009,
UserFunctionId_GenerateSpecificAesKey = 0xC300000A,
UserFunctionId_ComputeCmac = 0xC300040B,
UserFunctionId_ReencryptDeviceUniqueData = 0xC300D60C,
UserFunctionId_DecryptDeviceUniqueData = 0xC300100D,
UserFunctionId_ModularExponentiateByStorageKey = 0xC300060F,
UserFunctionId_PrepareEsDeviceUniqueKey = 0xC3000610,
UserFunctionId_LoadPreparedAesKey = 0xC3000011,
UserFunctionId_PrepareEsCommonTitleKey = 0xC3000012,
};
enum FunctionId : u32 {

View file

@ -0,0 +1,464 @@
/*
* 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>
#include "kern_secure_monitor.hpp"
namespace ams::kern::board::qemu::virt {
namespace {
constexpr uintptr_t DramPhysicalAddress = 0x40000000;
constexpr size_t SecureAlignment = 128_KB;
/* Global variables for secure memory. */
constexpr size_t SecureAppletMemorySize = 4_MB;
constinit KSpinLock g_secure_applet_lock;
constinit bool g_secure_applet_memory_used = false;
constinit KVirtualAddress g_secure_applet_memory_address = Null<KVirtualAddress>;
constinit KSpinLock g_secure_region_lock;
constinit bool g_secure_region_used = false;
constinit KPhysicalAddress g_secure_region_phys_addr = Null<KPhysicalAddress>;
constinit size_t g_secure_region_size = 0;
/* Global variables for randomness. */
constinit bool g_initialized_random_generator;
constinit util::TinyMT g_random_generator;
constinit KSpinLock g_random_lock;
ALWAYS_INLINE u64 GenerateRandomU64FromGenerator() {
return g_random_generator.GenerateRandomU64();
}
template<typename F>
ALWAYS_INLINE u64 GenerateUniformRange(u64 min, u64 max, F f) {
/* Handle the case where the difference is too large to represent. */
if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
return f();
}
/* Iterate until we get a value in range. */
const u64 range_size = ((max + 1) - min);
const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
while (true) {
if (const u64 rnd = f(); rnd < effective_max) {
return min + (rnd % range_size);
}
}
}
/* TODO */
ALWAYS_INLINE size_t GetRealMemorySizeForInit() {
return 4_GB;
}
bool SetSecureRegion(KPhysicalAddress phys_addr, size_t size) {
/* Ensure address and size are aligned. */
if (!util::IsAligned(GetInteger(phys_addr), SecureAlignment)) {
return false;
}
if (!util::IsAligned(size, SecureAlignment)) {
return false;
}
/* Disable interrupts and acquire the secure region lock. */
KScopedInterruptDisable di;
KScopedSpinLock lk(g_secure_region_lock);
/* If size is non-zero, we're allocating the secure region. Otherwise, we're freeing it. */
if (size != 0) {
/* Verify that the secure region is free. */
if (g_secure_region_used) {
return false;
}
/* Set the secure region. */
g_secure_region_used = true;
g_secure_region_phys_addr = phys_addr;
g_secure_region_size = size;
} else {
/* Verify that the secure region is in use. */
if (!g_secure_region_used) {
return false;
}
/* Verify that the address being freed is the secure region. */
if (phys_addr != g_secure_region_phys_addr) {
return false;
}
/* Clear the secure region. */
g_secure_region_used = false;
g_secure_region_phys_addr = Null<KPhysicalAddress>;
g_secure_region_size = 0;
}
// /* Configure the carveout with the secure monitor. */
// smc::ConfigureCarveout(1, GetInteger(phys_addr), size);
return true;
}
Result AllocateSecureMemoryForApplet(KVirtualAddress *out, size_t size) {
/* Verify that the size is valid. */
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
R_UNLESS(size <= SecureAppletMemorySize, svc::ResultOutOfMemory());
/* Disable interrupts and acquire the secure applet lock. */
KScopedInterruptDisable di;
KScopedSpinLock lk(g_secure_applet_lock);
/* Check that memory is reserved for secure applet use. */
MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address != Null<KVirtualAddress>);
/* Verify that the secure applet memory isn't already being used. */
R_UNLESS(!g_secure_applet_memory_used, svc::ResultOutOfMemory());
/* Return the secure applet memory. */
g_secure_applet_memory_used = true;
*out = g_secure_applet_memory_address;
return ResultSuccess();
}
void FreeSecureMemoryForApplet(KVirtualAddress address, size_t size) {
/* Disable interrupts and acquire the secure applet lock. */
KScopedInterruptDisable di;
KScopedSpinLock lk(g_secure_applet_lock);
/* Verify that the memory being freed is correct. */
MESOSPHERE_ABORT_UNLESS(address == g_secure_applet_memory_address);
MESOSPHERE_ABORT_UNLESS(size <= SecureAppletMemorySize);
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_used);
/* Release the secure applet memory. */
g_secure_applet_memory_used = false;
}
void EnsureRandomGeneratorSeeded() {
if (AMS_UNLIKELY(!g_initialized_random_generator)) {
u64 seed = UINT64_C(0xF5F5F5F5F5F5F5F5);
g_random_generator.Initialize(reinterpret_cast<u32*>(std::addressof(seed)), sizeof(seed) / sizeof(u32));
g_initialized_random_generator = true;
}
}
}
/* Initialization. */
size_t KSystemControl::Init::GetIntendedMemorySize() {
return 4_GB;
}
KPhysicalAddress KSystemControl::Init::GetKernelPhysicalBaseAddress(uintptr_t base_address) {
const size_t real_dram_size = GetRealMemorySizeForInit();
const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
if (intended_dram_size * 2 < real_dram_size) {
return base_address;
} else {
return base_address + ((real_dram_size - intended_dram_size) / 2);
}
}
void KSystemControl::Init::GetInitialProcessBinaryLayout(InitialProcessBinaryLayout *out) {
*out = {
.address = GetInteger(GetKernelPhysicalBaseAddress(DramPhysicalAddress)) + GetIntendedMemorySize() - KTraceBufferSize - InitialProcessBinarySizeMax,
._08 = 0,
};
}
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
return true;
}
size_t KSystemControl::Init::GetApplicationPoolSize() {
/* Get the base pool size. */
const size_t base_pool_size = 3285_MB;
/* Return (possibly) adjusted size. */
return base_pool_size;
}
size_t KSystemControl::Init::GetAppletPoolSize() {
/* Get the base pool size. */
const size_t base_pool_size = 507_MB;
/* Return (possibly) adjusted size. */
constexpr size_t ExtraSystemMemoryForAtmosphere = 40_MB;
return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
}
size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
return 0x29C8000;
}
void KSystemControl::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
smc::init::CpuOn(core_id, entrypoint, arg);
}
/* Randomness for Initialization. */
void KSystemControl::Init::GenerateRandomBytes(void *dst, size_t size) {
EnsureRandomGeneratorSeeded();
u8 *dst_8 = static_cast<u8 *>(dst);
while (size > 0) {
const u64 random = GenerateRandomU64FromGenerator();
std::memcpy(dst_8, std::addressof(random), std::min(size, sizeof(u64)));
size -= std::min(size, sizeof(u64));
}
}
u64 KSystemControl::Init::GenerateRandomRange(u64 min, u64 max) {
EnsureRandomGeneratorSeeded();
return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator);
}
/* System Initialization. */
void KSystemControl::InitializePhase1() {
/* Set IsDebugMode. */
{
KTargetSystem::SetIsDebugMode(true);
/* If debug mode, we want to initialize uart logging. */
KTargetSystem::EnableDebugLogging(true);
KDebugLog::Initialize();
}
/* Set Kernel Configuration. */
{
KTargetSystem::EnableDebugMemoryFill(false);
KTargetSystem::EnableUserExceptionHandlers(true);
KTargetSystem::EnableDynamicResourceLimits(true);
KTargetSystem::EnableUserPmuAccess(false);
}
/* Set Kernel Debugging. */
{
/* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */
/* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */
KTargetSystem::EnableKernelDebugging(true);
}
/* System ResourceLimit initialization. */
{
/* Construct the resource limit object. */
KResourceLimit &sys_res_limit = Kernel::GetSystemResourceLimit();
KAutoObject::Create(std::addressof(sys_res_limit));
sys_res_limit.Initialize();
/* Set the initial limits. */
const auto [total_memory_size, kernel_memory_size] = KMemoryLayout::GetTotalAndKernelMemorySizes();
const auto &slab_counts = init::GetSlabResourceCounts();
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_PhysicalMemoryMax, total_memory_size));
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_ThreadCountMax, slab_counts.num_KThread));
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_EventCountMax, slab_counts.num_KEvent));
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_TransferMemoryCountMax, slab_counts.num_KTransferMemory));
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_SessionCountMax, slab_counts.num_KSession));
/* Reserve system memory. */
MESOSPHERE_ABORT_UNLESS(sys_res_limit.Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, kernel_memory_size));
}
}
void KSystemControl::InitializePhase2() {
/* Reserve secure applet memory. */
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address == Null<KVirtualAddress>);
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, SecureAppletMemorySize));
constexpr auto SecureAppletAllocateOption = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront);
const KPhysicalAddress secure_applet_memory_phys_addr = Kernel::GetMemoryManager().AllocateAndOpenContinuous(SecureAppletMemorySize / PageSize, 1, SecureAppletAllocateOption);
MESOSPHERE_ABORT_UNLESS(secure_applet_memory_phys_addr != Null<KPhysicalAddress>);
g_secure_applet_memory_address = KMemoryLayout::GetLinearVirtualAddress(secure_applet_memory_phys_addr);
}
/* Initialize KTrace. */
if constexpr (IsKTraceEnabled) {
const auto &ktrace = KMemoryLayout::GetKernelTraceBufferRegion();
KTrace::Initialize(ktrace.GetAddress(), ktrace.GetSize());
}
}
u32 KSystemControl::GetCreateProcessMemoryPool() {
return KMemoryManager::Pool_Unsafe;
}
/* Privileged Access. */
void KSystemControl::ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
MESOSPHERE_UNUSED(out, address, mask, value);
MESOSPHERE_UNIMPLEMENTED();
}
Result KSystemControl::ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
MESOSPHERE_UNUSED(out, address, mask, value);
MESOSPHERE_UNIMPLEMENTED();
}
/* Randomness. */
void KSystemControl::GenerateRandomBytes(void *dst, size_t size) {
KScopedInterruptDisable intr_disable;
KScopedSpinLock lk(g_random_lock);
u8 *dst_8 = static_cast<u8 *>(dst);
while (size > 0) {
const u64 random = GenerateRandomU64FromGenerator();
std::memcpy(dst_8, std::addressof(random), std::min(size, sizeof(u64)));
size -= std::min(size, sizeof(u64));
}
}
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
KScopedInterruptDisable intr_disable;
KScopedSpinLock lk(g_random_lock);
return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator);
}
u64 KSystemControl::GenerateRandomU64() {
KScopedInterruptDisable intr_disable;
KScopedSpinLock lk(g_random_lock);
return GenerateRandomU64FromGenerator();
}
void KSystemControl::SleepSystem() {
MESOSPHERE_LOG("SleepSystem() was called\n");
MESOSPHERE_UNIMPLEMENTED();
}
void KSystemControl::StopSystem(void *arg) {
MESOSPHERE_UNUSED(arg);
AMS_INFINITE_LOOP();
}
/* User access. */
void KSystemControl::CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
/* Get the function id for the current call. */
u64 function_id = args->r[0];
/* We'll need to map in pages if arguments are pointers. Prepare page groups to do so. */
auto &page_table = GetCurrentProcess().GetPageTable();
auto *bim = page_table.GetBlockInfoManager();
constexpr size_t MaxMappedRegisters = 7;
std::array<KPageGroup, MaxMappedRegisters> page_groups = { KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), };
for (size_t i = 0; i < MaxMappedRegisters; i++) {
const size_t reg_id = i + 1;
if (function_id & (1ul << (8 + reg_id))) {
/* Create and open a new page group for the address. */
KVirtualAddress virt_addr = args->r[reg_id];
if (R_SUCCEEDED(page_table.MakeAndOpenPageGroup(std::addressof(page_groups[i]), util::AlignDown(GetInteger(virt_addr), PageSize), 1, KMemoryState_None, KMemoryState_None, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None))) {
/* Translate the virtual address to a physical address. */
const auto it = page_groups[i].begin();
MESOSPHERE_ASSERT(it != page_groups[i].end());
MESOSPHERE_ASSERT(it->GetNumPages() == 1);
args->r[reg_id] = GetInteger(it->GetAddress()) | (GetInteger(virt_addr) & (PageSize - 1));
} else {
/* If we couldn't map, we should clear the address. */
args->r[reg_id] = 0;
}
}
}
/* Invoke the secure monitor. */
smc::CallSecureMonitorFromUser(args);
/* Make sure that we close any pages that we opened. */
for (size_t i = 0; i < MaxMappedRegisters; i++) {
page_groups[i].Close();
}
}
/* Secure Memory. */
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
if (pool == KMemoryManager::Pool_Applet) {
return 0;
}
return size;
}
Result KSystemControl::AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool) {
/* Applet secure memory is handled separately. */
if (pool == KMemoryManager::Pool_Applet) {
return AllocateSecureMemoryForApplet(out, size);
}
/* Ensure the size is aligned. */
const size_t alignment = (pool == KMemoryManager::Pool_System ? PageSize : SecureAlignment);
R_UNLESS(util::IsAligned(size, alignment), svc::ResultInvalidSize());
/* Allocate the memory. */
const size_t num_pages = size / PageSize;
const KPhysicalAddress paddr = Kernel::GetMemoryManager().AllocateAndOpenContinuous(num_pages, alignment / PageSize, KMemoryManager::EncodeOption(static_cast<KMemoryManager::Pool>(pool), KMemoryManager::Direction_FromFront));
R_UNLESS(paddr != Null<KPhysicalAddress>, svc::ResultOutOfMemory());
/* Ensure we don't leak references to the memory on error. */
auto mem_guard = SCOPE_GUARD { Kernel::GetMemoryManager().Close(paddr, num_pages); };
/* If the memory isn't already secure, set it as secure. */
if (pool != KMemoryManager::Pool_System) {
/* Set the secure region. */
R_UNLESS(SetSecureRegion(paddr, size), svc::ResultOutOfMemory());
}
/* We succeeded. */
mem_guard.Cancel();
*out = KPageTable::GetHeapVirtualAddress(paddr);
return ResultSuccess();
}
void KSystemControl::FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool) {
/* Applet secure memory is handled separately. */
if (pool == KMemoryManager::Pool_Applet) {
return FreeSecureMemoryForApplet(address, size);
}
/* Ensure the size is aligned. */
const size_t alignment = (pool == KMemoryManager::Pool_System ? PageSize : SecureAlignment);
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(address), alignment));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, alignment));
/* If the memory isn't secure system, reset the secure region. */
if (pool != KMemoryManager::Pool_System) {
/* Check that the size being freed is the current secure region size. */
MESOSPHERE_ABORT_UNLESS(g_secure_region_size == size);
/* Get the physical address. */
const KPhysicalAddress paddr = KPageTable::GetHeapPhysicalAddress(address);
MESOSPHERE_ABORT_UNLESS(paddr != Null<KPhysicalAddress>);
/* Check that the memory being freed is the current secure region. */
MESOSPHERE_ABORT_UNLESS(paddr == g_secure_region_phys_addr);
/* Free the secure region. */
MESOSPHERE_ABORT_UNLESS(SetSecureRegion(paddr, 0));
}
/* Close the secure region's pages. */
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
}
}

View file

@ -0,0 +1,171 @@
/*
* 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>
#include "kern_secure_monitor.hpp"
namespace ams::kern::board::qemu::virt::smc {
namespace {
struct SecureMonitorArguments {
u64 x[8];
};
enum UserFunctionId : u32 {
UserFunctionId_SetConfig = 0xC3000401,
UserFunctionId_GetConfig = 0xC3000002,
UserFunctionId_GetResult = 0xC3000003,
UserFunctionId_GetResultData = 0xC3000404,
UserFunctionId_ModularExponentiate = 0xC3000E05,
UserFunctionId_GenerateRandomBytes = 0xC3000006,
UserFunctionId_GenerateAesKek = 0xC3000007,
UserFunctionId_LoadAesKey = 0xC3000008,
UserFunctionId_ComputeAes = 0xC3000009,
UserFunctionId_GenerateSpecificAesKey = 0xC300000A,
UserFunctionId_ComputeCmac = 0xC300040B,
UserFunctionId_ReencryptDeviceUniqueData = 0xC300D60C,
UserFunctionId_DecryptDeviceUniqueData = 0xC300100D,
UserFunctionId_ModularExponentiateByStorageKey = 0xC300060F,
UserFunctionId_PrepareEsDeviceUniqueKey = 0xC3000610,
UserFunctionId_LoadPreparedAesKey = 0xC3000011,
UserFunctionId_PrepareEsCommonTitleKey = 0xC3000012,
};
enum FunctionId : u32 {
FunctionId_CpuSuspend = 0xC4000001,
FunctionId_CpuOff = 0x84000002,
FunctionId_CpuOn = 0xC4000003,
};
void CallPrivilegedSecureMonitorFunction(SecureMonitorArguments &args) {
/* Load arguments into registers. */
register u64 x0 asm("x0") = args.x[0];
register u64 x1 asm("x1") = args.x[1];
register u64 x2 asm("x2") = args.x[2];
register u64 x3 asm("x3") = args.x[3];
register u64 x4 asm("x4") = args.x[4];
register u64 x5 asm("x5") = args.x[5];
register u64 x6 asm("x6") = args.x[6];
register u64 x7 asm("x7") = args.x[7];
/* Actually make the call. */
{
/* Disable interrupts while making the call. */
KScopedInterruptDisable intr_disable;
{
/* Backup the current thread pointer. */
const uintptr_t current_thread_pointer_value = cpu::GetCurrentThreadPointerValue();
__asm__ __volatile__("smc #0"
: "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7)
:
: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory"
);
/* Restore the current thread pointer into X18. */
cpu::SetCurrentThreadPointerValue(current_thread_pointer_value);
/* Store arguments to output. */
args.x[0] = x0;
args.x[1] = x1;
args.x[2] = x2;
args.x[3] = x3;
args.x[4] = x4;
args.x[5] = x5;
args.x[6] = x6;
args.x[7] = x7;
}
}
}
void CallPrivilegedSecureMonitorFunctionForInit(SecureMonitorArguments &args) {
/* Load arguments into registers. */
register u64 x0 asm("x0") = args.x[0];
register u64 x1 asm("x1") = args.x[1];
register u64 x2 asm("x2") = args.x[2];
register u64 x3 asm("x3") = args.x[3];
register u64 x4 asm("x4") = args.x[4];
register u64 x5 asm("x5") = args.x[5];
register u64 x6 asm("x6") = args.x[6];
register u64 x7 asm("x7") = args.x[7];
/* Actually make the call. */
__asm__ __volatile__("smc #0"
: "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7)
:
: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory"
);
/* Store arguments to output. */
args.x[0] = x0;
args.x[1] = x1;
args.x[2] = x2;
args.x[3] = x3;
args.x[4] = x4;
args.x[5] = x5;
args.x[6] = x6;
args.x[7] = x7;
}
/* Global lock for generate random bytes. */
KSpinLock g_generate_random_lock;
}
/* SMC functionality needed for init. */
namespace init {
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
SecureMonitorArguments args = { FunctionId_CpuOn, core_id, entrypoint, arg };
CallPrivilegedSecureMonitorFunctionForInit(args);
}
}
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
SecureMonitorArguments args = { FunctionId_CpuOn, core_id, static_cast<u64>(entrypoint), static_cast<u64>(arg) };
CallPrivilegedSecureMonitorFunction(args);
MESOSPHERE_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
}
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
MESOSPHERE_LOG("Received SMC [%p %p %p %p %p %p %p %p] from %s\n", reinterpret_cast<void *>(args->r[0]), reinterpret_cast<void *>(args->r[1]), reinterpret_cast<void *>(args->r[2]), reinterpret_cast<void *>(args->r[3]), reinterpret_cast<void *>(args->r[4]), reinterpret_cast<void *>(args->r[5]), reinterpret_cast<void *>(args->r[6]), reinterpret_cast<void *>(args->r[7]), GetCurrentProcess().GetName());
switch (args->r[0]) {
case UserFunctionId_GetConfig:
{
switch (static_cast<ConfigItem>(args->r[1])) {
case ConfigItem::ExosphereApiVersion:
args->r[1] = (static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) |
(static_cast<u64>(13) << 32) |
(static_cast<u64>(GetTargetFirmware()) << 0);
break;
default:
MESOSPHERE_PANIC("Unhandled GetConfig\n");
}
args->r[0] = static_cast<u64>(SmcResult::Success);
}
break;
default:
MESOSPHERE_PANIC("Unhandled SMC [%p %p %p %p %p %p %p %p]", reinterpret_cast<void *>(args->r[0]), reinterpret_cast<void *>(args->r[1]), reinterpret_cast<void *>(args->r[2]), reinterpret_cast<void *>(args->r[3]), reinterpret_cast<void *>(args->r[4]), reinterpret_cast<void *>(args->r[5]), reinterpret_cast<void *>(args->r[6]), reinterpret_cast<void *>(args->r[7]));
}
}
}

View file

@ -0,0 +1,76 @@
/*
* 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/>.
*/
#pragma once
#include <mesosphere.hpp>
namespace ams::kern::board::qemu::virt::smc {
enum class ConfigItem : u32 {
/* Standard config items. */
DisableProgramVerification = 1,
DramId = 2,
SecurityEngineIrqNumber = 3,
Version = 4,
HardwareType = 5,
IsRetail = 6,
IsRecoveryBoot = 7,
DeviceId = 8,
BootReason = 9,
MemoryMode = 10,
IsDebugMode = 11,
KernelConfiguration = 12,
IsChargerHiZModeEnabled = 13,
IsQuest = 14,
RegulatorType = 15,
DeviceUniqueKeyGeneration = 16,
Package2Hash = 17,
/* Extension config items for exosphere. */
ExosphereApiVersion = 65000,
ExosphereNeedsReboot = 65001,
ExosphereNeedsShutdown = 65002,
ExosphereGitCommitHash = 65003,
ExosphereHasRcmBugPatch = 65004,
ExosphereBlankProdInfo = 65005,
ExosphereAllowCalWrites = 65006,
ExosphereEmummcType = 65007,
ExospherePayloadAddress = 65008,
ExosphereLogConfiguration = 65009,
ExosphereForceEnableUsb30 = 65010,
ExosphereSupportedHosVersion = 65011,
};
enum class SmcResult {
Success = 0,
NotImplemented = 1,
InvalidArgument = 2,
InProgress = 3,
NoAsyncOperation = 4,
InvalidAsyncOperation = 5,
NotPermitted = 6,
};
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
namespace init {
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
}
}

View file

@ -32,6 +32,9 @@ namespace ams::kern {
return;
}
#if defined(MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING)
KDebugLogImpl::PutStringBySemihosting(str);
#else
while (*str) {
/* Get a character. */
const char c = *(str++);
@ -44,6 +47,7 @@ namespace ams::kern {
}
KDebugLogImpl::Flush();
#endif
}
#if defined(MESOSPHERE_ENABLE_DEBUG_PRINT)
@ -54,6 +58,11 @@ namespace ams::kern {
return ResultSuccess();
}
#if defined(MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING)
/* TODO: should we do this properly? */
KDebugLogImpl::PutStringBySemihosting(user_str.GetUnsafePointer());
MESOSPHERE_UNUSED(len);
#else
for (size_t i = 0; i < len; ++i) {
/* Get a character. */
char c;
@ -67,6 +76,7 @@ namespace ams::kern {
}
KDebugLogImpl::Flush();
#endif
return ResultSuccess();
}

View file

@ -0,0 +1,27 @@
/*
* 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/>.
*/
/* ams::kern::KDebugLogImpl::PutStringBySemihosting(const char *str) */
.section .text._ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc, "ax", %progbits
.global _ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc
.type _ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc, %function
.balign 0x10
_ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc:
mov x1, x0
mov x0, #0x4
hlt #0xF000
ret

View file

@ -0,0 +1,50 @@
/*
* 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>
#include "kern_debug_log_impl.hpp"
namespace ams::kern {
#if defined(MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING)
bool KDebugLogImpl::Initialize() {
return true;
}
void KDebugLogImpl::PutChar(char c) {
/* TODO */
AMS_UNUSED(c);
}
void KDebugLogImpl::Flush() {
/* ... */
}
void KDebugLogImpl::Save() {
/* ... */
}
void KDebugLogImpl::Restore() {
/* ... */
}
#else
#error "Unknown Debug device!"
#endif
}

View file

@ -21,6 +21,7 @@ namespace ams::kern {
class KDebugLogImpl {
public:
static NOINLINE bool Initialize();
static NOINLINE void PutStringBySemihosting(const char *s);
static NOINLINE void PutChar(char c);
static NOINLINE void Flush();

View file

@ -0,0 +1,137 @@
/*
* 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 {
namespace {
constexpr uintptr_t DramPhysicalAddress = 0x40000000;
constexpr size_t ReservedEarlyDramSize = 0x00080000;
constexpr size_t CarveoutAlignment = 0x20000;
constexpr size_t CarveoutSizeMax = 512_MB - CarveoutAlignment;
template<typename... T> requires (std::same_as<T, KMemoryRegionAttr> && ...)
constexpr ALWAYS_INLINE KMemoryRegionType GetMemoryRegionType(KMemoryRegionType base, T... attr) {
return util::FromUnderlying<KMemoryRegionType>(util::ToUnderlying(base) | (util::ToUnderlying<T>(attr) | ...));
}
void InsertPoolPartitionRegionIntoBothTrees(size_t start, size_t size, KMemoryRegionType phys_type, KMemoryRegionType virt_type, u32 &cur_attr) {
const u32 attr = cur_attr++;
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(start, size, phys_type, attr));
const KMemoryRegion *phys = KMemoryLayout::GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(phys_type, attr);
MESOSPHERE_INIT_ABORT_UNLESS(phys != nullptr);
MESOSPHERE_INIT_ABORT_UNLESS(phys->GetEndAddress() != 0);
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(phys->GetPairAddress(), size, virt_type, attr));
}
}
namespace init {
void SetupDevicePhysicalMemoryRegions() {
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x08000000, 0x10000, GetMemoryRegionType(KMemoryRegionType_InterruptDistributor, KMemoryRegionAttr_ShouldKernelMap)));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x08010000, 0x10000, GetMemoryRegionType(KMemoryRegionType_InterruptCpuInterface, KMemoryRegionAttr_ShouldKernelMap)));
}
void SetupDramPhysicalMemoryRegions() {
const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
const KPhysicalAddress physical_memory_base_address = KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress);
/* Insert blocks into the tree. */
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), intended_memory_size, KMemoryRegionType_Dram));
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly));
/* Insert the KTrace block at the end of Dram, if KTrace is enabled. */
static_assert(!IsKTraceEnabled || KTraceBufferSize > 0);
if constexpr (IsKTraceEnabled) {
const KPhysicalAddress ktrace_buffer_phys_addr = physical_memory_base_address + intended_memory_size - KTraceBufferSize;
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(ktrace_buffer_phys_addr), KTraceBufferSize, KMemoryRegionType_KernelTraceBuffer));
}
}
void SetupPoolPartitionMemoryRegions() {
/* Start by identifying the extents of the DRAM memory region. */
const auto dram_extents = KMemoryLayout::GetMainMemoryPhysicalExtents();
MESOSPHERE_INIT_ABORT_UNLESS(dram_extents.GetEndAddress() != 0);
/* Determine the end of the pool region. */
const uintptr_t pool_end = dram_extents.GetEndAddress() - KTraceBufferSize;
/* Find the start of the kernel DRAM region. */
const KMemoryRegion *kernel_dram_region = KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramKernelBase);
MESOSPHERE_INIT_ABORT_UNLESS(kernel_dram_region != nullptr);
const uintptr_t kernel_dram_start = kernel_dram_region->GetAddress();
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(kernel_dram_start, CarveoutAlignment));
/* Find the start of the pool partitions region. */
const KMemoryRegion *pool_partitions_region = KMemoryLayout::GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(KMemoryRegionType_DramPoolPartition, 0);
MESOSPHERE_INIT_ABORT_UNLESS(pool_partitions_region != nullptr);
const uintptr_t pool_partitions_start = pool_partitions_region->GetAddress();
/* Setup the pool partition layouts. */
/* Get Application and Applet pool sizes. */
const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize();
const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize();
const size_t unsafe_system_pool_min_size = KSystemControl::Init::GetMinimumNonSecureSystemPoolSize();
/* Decide on starting addresses for our pools. */
const uintptr_t application_pool_start = pool_end - application_pool_size;
const uintptr_t applet_pool_start = application_pool_start - applet_pool_size;
const uintptr_t unsafe_system_pool_start = std::min(kernel_dram_start + CarveoutSizeMax, util::AlignDown(applet_pool_start - unsafe_system_pool_min_size, CarveoutAlignment));
const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start;
/* We want to arrange application pool depending on where the middle of dram is. */
const uintptr_t dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2;
u32 cur_pool_attr = 0;
size_t total_overhead_size = 0;
if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) {
InsertPoolPartitionRegionIntoBothTrees(application_pool_start, application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(application_pool_size);
} else {
const size_t first_application_pool_size = dram_midpoint - application_pool_start;
const size_t second_application_pool_size = application_pool_start + application_pool_size - dram_midpoint;
InsertPoolPartitionRegionIntoBothTrees(application_pool_start, first_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
InsertPoolPartitionRegionIntoBothTrees(dram_midpoint, second_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size);
}
/* Insert the applet pool. */
InsertPoolPartitionRegionIntoBothTrees(applet_pool_start, applet_pool_size, KMemoryRegionType_DramAppletPool, KMemoryRegionType_VirtualDramAppletPool, cur_pool_attr);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(applet_pool_size);
/* Insert the nonsecure system pool. */
InsertPoolPartitionRegionIntoBothTrees(unsafe_system_pool_start, unsafe_system_pool_size, KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool, cur_pool_attr);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size);
/* Insert the pool management region. */
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize((unsafe_system_pool_start - pool_partitions_start) - total_overhead_size);
const uintptr_t pool_management_start = unsafe_system_pool_start - total_overhead_size;
const size_t pool_management_size = total_overhead_size;
u32 pool_management_attr = 0;
InsertPoolPartitionRegionIntoBothTrees(pool_management_start, pool_management_size, KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement, pool_management_attr);
/* Insert the system pool. */
const uintptr_t system_pool_size = pool_management_start - pool_partitions_start;
InsertPoolPartitionRegionIntoBothTrees(pool_partitions_start, system_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
}
}
}

View file

@ -26,6 +26,11 @@
return ::svcSetHeapSize(reinterpret_cast<void **>(out_address), size);
}
ALWAYS_INLINE Result SetHeapSize(uintptr_t *out_address, ::ams::svc::Size size) {
static_assert(sizeof(::ams::svc::Address) == sizeof(uintptr_t));
return ::svcSetHeapSize(reinterpret_cast<void **>(out_address), size);
}
ALWAYS_INLINE Result SetMemoryPermission(::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission perm) {
return ::svcSetMemoryPermission(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size, static_cast<u32>(perm));
}

View file

@ -38,6 +38,8 @@ namespace ams {
namespace init {
void InitializeSystemModuleBeforeConstructors();
void InitializeSystemModule();
void FinalizeSystemModule();
@ -66,6 +68,9 @@ extern "C" void __libnx_initheap(void) {
extern "C" void __appInit(void) {
/* The very first thing all stratosphere code must do is initialize the os library. */
::ams::hos::InitializeForStratosphere();
/* Perform pre-C++ constructor init. */
::ams::init::InitializeSystemModuleBeforeConstructors();
}
extern "C" void __appExit(void) {

View file

@ -17,6 +17,10 @@
namespace ams::init {
WEAK_SYMBOL void InitializeSystemModuleBeforeConstructors() {
/* This should only be used in exceptional circumstances. */
}
WEAK_SYMBOL void InitializeSystemModule() {
/* TODO: What should we do here, if anything? */
/* Nintendo does nndiagStartup(); nn::diag::InitializeSystemProcessAbortObserver(); */

View file

@ -0,0 +1,23 @@
/*
* 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/>.
*/
#pragma once
#include <vapours/svc/svc_types_common.hpp>
namespace ams::svc::board::qemu::virt {
constexpr inline const s64 TicksPerSecond = 19'200'000;
}

View file

@ -24,8 +24,15 @@
using namespace ams::svc::board::nintendo::nx;
}
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
#include <vapours/svc/board/qemu/virt/svc_hardware_constants.hpp>
namespace ams::svc {
using namespace ams::svc::board::qemu::virt;
}
#else
#error "Unknown board for svc::DeviceName"
#error "Unknown board for svc Hardware Constants"
#endif

View file

@ -8,29 +8,47 @@ ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
$(strip $1): mesosphere$(strip $2).bin
mesosphere$(strip $2).bin: kernel/kernel$(strip $2).bin kernel_ldr/kernel_ldr$(strip $2).bin
@python build_mesosphere.py kernel_ldr/kernel_ldr$(strip $2).bin kernel/kernel$(strip $2).bin mesosphere$(strip $2).bin
@python build_mesosphere.py kernel_ldr/kernel_ldr$(strip $2).bin kernel/kernel$(strip $2).bin mesosphere$(strip $2).bin $(4)
@echo "Built mesosphere$(strip $2).bin..."
kernel/kernel$(strip $2).bin: check_libmeso$(strip $1)
@$$(MAKE) -C kernel $(strip $1)
@$$(MAKE) -C kernel $(strip $1) $(3)
kernel_ldr/kernel_ldr$(strip $2).bin: check_libmeso$(strip $1)
@$$(MAKE) -C kernel_ldr $(strip $1)
@$$(MAKE) -C kernel_ldr $(strip $1) $(3)
check_libmeso$(strip $1):
@$$(MAKE) -C ../libraries/libmesosphere $(strip $1)
@$$(MAKE) -C ../libraries/libmesosphere $(strip $1) $(3)
clean-$(strip $1):
@$$(MAKE) -C ../libraries/libmesosphere clean-$(strip $1)
@$$(MAKE) -C kernel clean-$(strip $1)
@$$(MAKE) -C kernel_ldr clean-$(strip $1)
@$$(MAKE) -C ../libraries/libmesosphere clean-$(strip $1) $(3)
@$$(MAKE) -C kernel clean-$(strip $1) $(3)
@$$(MAKE) -C kernel_ldr clean-$(strip $1) $(3)
@rm -f mesosphere$(strip $2).bin
endef
$(eval $(call ATMOSPHERE_ADD_TARGET, release, ,))
$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug,))
$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit,))
$(eval $(call ATMOSPHERE_ADD_TARGET, release, ,,))
$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug,,))
$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit,,))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_release, _qemu_virt, \
ATMOSPHERE_BOARD="qemu-virt" \
ATMOSPHERE_CPU="arm-cortex-a57" \
, ../tests/TestSvc/TestSvc.kip \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_debug, _qemu_virt_debug, \
ATMOSPHERE_BOARD="qemu-virt" \
ATMOSPHERE_CPU="arm-cortex-a57" \
, ../tests/TestSvc/TestSvc.kip \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_audit, _qemu_virt_audit, \
ATMOSPHERE_BOARD="qemu-virt" \
ATMOSPHERE_CPU="arm-cortex-a57" \
, ../tests/TestSvc/TestSvc.kip \
))
clean:
@$(MAKE) -C ../libraries/libmesosphere clean

View file

@ -10,8 +10,8 @@ def align_up(val, algn):
return val - (val % algn)
def main(argc, argv):
if argc != 4:
print('Usage: %s kernel_ldr.bin kernel.bin output.bin' % argv[0])
if argc < 4:
print('Usage: %s kernel_ldr.bin kernel.bin output.bin [initial_process.kip ...]' % argv[0])
return 1
with open(argv[1], 'rb') as f:
kernel_ldr = f.read()
@ -30,16 +30,25 @@ def main(argc, argv):
kernel += b'\x00' * (kernel_end - len(kernel))
assert (kernel_end == len(kernel))
embedded_ini = b''
embedded_kips = b''
num_kips = 0
for kip_file in argv[4:]:
try:
with open('ini.bin', 'rb') as f:
embedded_ini = f.read()
with open(kip_file, 'rb') as f:
data = f.read()
if data.startswith(b'KIP1'):
embedded_kips += data
num_kips += 1
except:
pass
if num_kips > 0:
embedded_ini_header = pk('<4sIII', b'INI1', len(embedded_kips) + 0x10, num_kips, 0)
else:
embedded_ini_header = b''
embedded_ini_offset = align_up(kernel_end, 0x1000)
embedded_ini_end = embedded_ini_offset + len(embedded_ini) # TODO: Create and embed an INI, eventually.
embedded_ini_end = embedded_ini_offset + len(embedded_ini_header) + len(embedded_kips)
kernel_ldr_offset = align_up(embedded_ini_end, 0x1000) + (0x1000 if len(embedded_ini) == 0 else 0)
kernel_ldr_offset = align_up(embedded_ini_end, 0x1000) + (0x1000 if len(embedded_ini_header) == 0 else 0)
kernel_ldr_end = kernel_ldr_offset + len(kernel_ldr)
mesosphere_end = align_up(kernel_ldr_end, 0x1000)
@ -48,7 +57,8 @@ def main(argc, argv):
f.write(pk('<QQI', embedded_ini_offset, kernel_ldr_offset, atmosphere_target_firmware(13, 0, 0)))
f.write(kernel[kernel_metadata_offset + 0x18:])
f.seek(embedded_ini_offset)
f.write(embedded_ini)
f.write(embedded_ini_header)
f.write(embedded_kips)
f.seek(embedded_ini_end)
f.seek(kernel_ldr_offset)
f.write(kernel_ldr)

View file

@ -85,6 +85,18 @@ $(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_release, _qemu_virt, \
ATMOSPHERE_BUILD_SETTINGS="" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_debug, _qemu_virt_debug, \
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_DEBUGGING" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_audit, _qemu_virt_audit, \
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
))
$(ATMOSPHERE_BUILD_DIR)/%:
@[ -d $@ ] || mkdir -p $@

View file

@ -85,6 +85,18 @@ $(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_release, _qemu_virt, \
ATMOSPHERE_BUILD_SETTINGS="" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_debug, _qemu_virt_debug, \
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_DEBUGGING" \
))
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_audit, _qemu_virt_audit, \
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
))
$(ATMOSPHERE_BUILD_DIR)/%:
@[ -d $@ ] || mkdir -p $@

View file

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

107
tests/TestSvc/Makefile Normal file
View file

@ -0,0 +1,107 @@
#---------------------------------------------------------------------------------
# pull in common stratosphere sysmodule configuration
#---------------------------------------------------------------------------------
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).kip $(TARGET).elf
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).kip
$(OUTPUT).kip : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

128
tests/TestSvc/TestSvc.json Normal file
View file

@ -0,0 +1,128 @@
{
"name": "TestSvc",
"title_id": "0x5555555555555555",
"main_thread_stack_size": "0x8000",
"main_thread_priority": 28,
"default_cpu_id": 3,
"process_category": 1,
"use_secure_memory": true,
"immortal": true,
"kernel_capabilities": [
{
"type": "handle_table_size",
"value": 0
},
{
"type": "syscalls",
"value": {
"svcUnknown00": "0x00",
"svcSetHeapSize": "0x01",
"svcSetMemoryPermission": "0x02",
"svcSetMemoryAttribute": "0x03",
"svcMapMemory": "0x04",
"svcUnmapMemory": "0x05",
"svcQueryMemory": "0x06",
"svcExitProcess": "0x07",
"svcCreateThread": "0x08",
"svcStartThread": "0x09",
"svcExitThread": "0x0A",
"svcSleepThread": "0x0B",
"svcGetThreadPriority": "0x0C",
"svcSetThreadPriority": "0x0D",
"svcGetThreadCoreMask": "0x0E",
"svcSetThreadCoreMask": "0x0F",
"svcGetCurrentProcessorNumber": "0x10",
"svcSignalEvent": "0x11",
"svcClearEvent": "0x12",
"svcMapSharedMemory": "0x13",
"svcUnmapSharedMemory": "0x14",
"svcCreateTransferMemory": "0x15",
"svcCloseHandle": "0x16",
"svcResetSignal": "0x17",
"svcWaitSynchronization": "0x18",
"svcCancelSynchronization": "0x19",
"svcArbitrateLock": "0x1A",
"svcArbitrateUnlock": "0x1B",
"svcWaitProcessWideKeyAtomic": "0x1C",
"svcSignalProcessWideKey": "0x1D",
"svcGetSystemTick": "0x1E",
"svcConnectToNamedPort": "0x1F",
"svcSendSyncRequestLight": "0x20",
"svcSendSyncRequest": "0x21",
"svcSendSyncRequestWithUserBuffer": "0x22",
"svcSendAsyncRequestWithUserBuffer": "0x23",
"svcGetProcessId": "0x24",
"svcGetThreadId": "0x25",
"svcBreak": "0x26",
"svcOutputDebugString": "0x27",
"svcReturnFromException": "0x28",
"svcGetInfo": "0x29",
"svcFlushEntireDataCache": "0x2A",
"svcFlushDataCache": "0x2B",
"svcMapPhysicalMemory": "0x2C",
"svcUnmapPhysicalMemory": "0x2D",
"svcGetDebugFutureThreadInfo": "0x2E",
"svcGetLastThreadInfo": "0x2F",
"svcGetResourceLimitLimitValue": "0x30",
"svcGetResourceLimitCurrentValue": "0x31",
"svcSetThreadActivity": "0x32",
"svcGetThreadContext3": "0x33",
"svcWaitForAddress": "0x34",
"svcSignalToAddress": "0x35",
"svcSynchronizePreemptionState": "0x36",
"svcGetResourceLimitPeakValue": "0x37",
"svcUnknown38": "0x38",
"svcUnknown39": "0x39",
"svcUnknown3a": "0x3A",
"svcUnknown3b": "0x3B",
"svcKernelDebug": "0x3C",
"svcChangeKernelTraceState": "0x3D",
"svcUnknown3e": "0x3E",
"svcUnknown3f": "0x3F",
"svcCreateSession": "0x40",
"svcAcceptSession": "0x41",
"svcReplyAndReceiveLight": "0x42",
"svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45",
"svcUnknown46": "0x46",
"svcUnknown47": "0x47",
"svcMapPhysicalMemoryUnsafe": "0x48",
"svcUnmapPhysicalMemoryUnsafe": "0x49",
"svcSetUnsafeLimit": "0x4A",
"svcCreateCodeMemory": "0x4B",
"svcControlCodeMemory": "0x4C",
"svcSleepSystem": "0x4D",
"svcReadWriteRegister": "0x4E",
"svcSetProcessActivity": "0x4F",
"svcCreateSharedMemory": "0x50",
"svcMapTransferMemory": "0x51",
"svcUnmapTransferMemory": "0x52",
"svcQueryIoMapping": "0x55",
"svcDebugActiveProcess": "0x60",
"svcBreakDebugProcess": "0x61",
"svcTerminateDebugProcess": "0x62",
"svcGetDebugEvent": "0x63",
"svcContinueDebugEvent": "0x64",
"svcGetProcessList": "0x65",
"svcGetThreadList": "0x66",
"svcGetDebugThreadContext": "0x67",
"svcSetDebugThreadContext": "0x68",
"svcQueryDebugProcessMemory": "0x69",
"svcReadDebugProcessMemory": "0x6A",
"svcWriteDebugProcessMemory": "0x6B",
"svcSetHardwareBreakPoint": "0x6C",
"svcGetDebugThreadParam": "0x6D",
"svcGetSystemInfo": "0x6F",
"svcConnectToPort": "0x72",
"svcSetProcessMemoryPermission": "0x73",
"svcMapProcessMemory": "0x74",
"svcUnmapProcessMemory": "0x75",
"svcQueryProcessMemory": "0x76",
"svcMapProcessCodeMemory": "0x77",
"svcUnmapProcessCodeMemory": "0x78",
"svcCallSecureMonitor": "0x7F"
}
}
]
}

17959
tests/TestSvc/source/catch.hpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,93 @@
/*
* 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 <stratosphere.hpp>
#define CATCH_CONFIG_NOSTDOUT
#define CATCH_CONFIG_RUNNER
#define CATCH_CONFIG_PREFIX_ALL
#define CATCH_CONFIG_DISABLE_EXCEPTIONS
#define CATCH_CONFIG_NO_POSIX_SIGNALS
#include "catch.hpp"
namespace ams {
namespace {
constexpr size_t MallocBufferSize = 16_MB;
alignas(os::MemoryPageSize) constinit u8 g_malloc_buffer[MallocBufferSize];
}
namespace init {
void InitializeSystemModuleBeforeConstructors() {
/* Catch has global-ctors which allocate, so we need to do this earlier than normal. */
init::InitializeAllocator(g_malloc_buffer, sizeof(g_malloc_buffer));
}
void InitializeSystemModule() { /* ... */ }
void FinalizeSystemModule() { /* ... */ }
void Startup() { /* ... */ }
}
void NORETURN Exit(int rc) {
AMS_UNUSED(rc);
AMS_ABORT("Exit called by immortal process");
}
void Main() {
/* Run tests. */
Catch::Session().run(os::GetHostArgc(), os::GetHostArgv());
AMS_INFINITE_LOOP();
/* This can never be reached. */
AMS_ASSUME(false);
}
}
namespace Catch {
namespace {
class OutputDebugStringStream : public std::stringbuf {
public:
OutputDebugStringStream() = default;
~OutputDebugStringStream() { pubsync(); }
int sync() override {
const auto message = str();
return R_SUCCEEDED(ams::svc::OutputDebugString(message.c_str(), message.length())) ? 0 : -1;
}
};
}
std::ostream& cout() {
static std::ostream ret(new OutputDebugStringStream);
return ret;
}
std::ostream& clog() {
return cout();
}
std::ostream& cerr() {
return clog();
}
}

View file

@ -0,0 +1,153 @@
/*
* 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 <stratosphere.hpp>
#define CATCH_CONFIG_NOSTDOUT
#define CATCH_CONFIG_PREFIX_ALL
#define CATCH_CONFIG_DISABLE_EXCEPTIONS
#define CATCH_CONFIG_NO_POSIX_SIGNALS
#include "catch.hpp"
namespace ams::test {
namespace {
size_t GetPhysicalMemorySizeMax() {
u64 v;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(v), svc::InfoType_ResourceLimit, svc::InvalidHandle, 0));
const svc::Handle resource_limit = v;
ON_SCOPE_EXIT { svc::CloseHandle(resource_limit); };
s64 size;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(size), resource_limit, svc::LimitableResource_PhysicalMemoryMax));
return static_cast<size_t>(size);
}
size_t GetPhysicalMemorySizeAvailable() {
u64 v;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(v), svc::InfoType_ResourceLimit, svc::InvalidHandle, 0));
const svc::Handle resource_limit = v;
ON_SCOPE_EXIT { svc::CloseHandle(resource_limit); };
s64 total;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total), resource_limit, svc::LimitableResource_PhysicalMemoryMax));
s64 current;
R_ABORT_UNLESS(svc::GetResourceLimitCurrentValue(std::addressof(current), resource_limit, svc::LimitableResource_PhysicalMemoryMax));
return static_cast<size_t>(total - current);
}
}
CATCH_TEST_CASE("svc::SetHeapSize") {
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
uintptr_t dummy;
/* Reset the heap. */
uintptr_t addr;
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
/* Ensure that we don't leak memory. */
const size_t initial_memory = GetPhysicalMemorySizeAvailable();
ON_SCOPE_EXIT { CATCH_REQUIRE(initial_memory == GetPhysicalMemorySizeAvailable()); };
CATCH_SECTION("Unaligned and too big sizes fail") {
CATCH_REQUIRE(svc::ResultInvalidSize::Includes(svc::SetHeapSize(std::addressof(dummy), 5)));
CATCH_REQUIRE(svc::ResultInvalidSize::Includes(svc::SetHeapSize(std::addressof(dummy), 64_GB)));
}
CATCH_SECTION("Larger size than address space fails") {
CATCH_REQUIRE(svc::ResultOutOfMemory::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(svc::AddressMemoryRegionHeap39Size + 1, svc::HeapSizeAlignment))));
}
CATCH_SECTION("Bounded by resource limit") {
CATCH_REQUIRE(svc::ResultLimitReached::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(GetPhysicalMemorySizeMax() + 1, svc::HeapSizeAlignment))));
CATCH_REQUIRE(svc::ResultLimitReached::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(GetPhysicalMemorySizeAvailable() + 1, svc::HeapSizeAlignment))));
}
CATCH_SECTION("SetHeapSize gives heap memory") {
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
CATCH_REQUIRE(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
CATCH_REQUIRE(mem_info.base_address == addr);
CATCH_REQUIRE(mem_info.size == svc::HeapSizeAlignment);
CATCH_REQUIRE(mem_info.permission == svc::MemoryPermission_ReadWrite);
CATCH_REQUIRE(mem_info.state == svc::MemoryState_Normal);
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
}
CATCH_SECTION("SetHeapSize cannot remove read-only heap") {
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
CATCH_REQUIRE(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
CATCH_REQUIRE(mem_info.base_address == addr);
CATCH_REQUIRE(mem_info.size == svc::HeapSizeAlignment);
CATCH_REQUIRE(mem_info.permission == svc::MemoryPermission_ReadWrite);
CATCH_REQUIRE(mem_info.state == svc::MemoryState_Normal);
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(addr, svc::HeapSizeAlignment, svc::MemoryPermission_Read)));
CATCH_REQUIRE(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
CATCH_REQUIRE(mem_info.base_address == addr);
CATCH_REQUIRE(mem_info.size == svc::HeapSizeAlignment);
CATCH_REQUIRE(mem_info.permission == svc::MemoryPermission_Read);
CATCH_REQUIRE(mem_info.state == svc::MemoryState_Normal);
CATCH_REQUIRE(svc::ResultInvalidCurrentMemory::Includes(svc::SetHeapSize(std::addressof(dummy), 0)));
CATCH_REQUIRE(R_SUCCEEDED(svc::SetMemoryPermission(addr, svc::HeapSizeAlignment, svc::MemoryPermission_ReadWrite)));
CATCH_REQUIRE(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
CATCH_REQUIRE(mem_info.base_address == addr);
CATCH_REQUIRE(mem_info.size == svc::HeapSizeAlignment);
CATCH_REQUIRE(mem_info.permission == svc::MemoryPermission_ReadWrite);
CATCH_REQUIRE(mem_info.state == svc::MemoryState_Normal);
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
}
CATCH_SECTION("Heap memory does not survive unmap/re-map") {
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 2 * svc::HeapSizeAlignment)));
u8 * const heap = reinterpret_cast<u8 *>(addr);
std::memset(heap, 0xAA, svc::HeapSizeAlignment);
std::memset(heap + svc::HeapSizeAlignment, 0xBB, svc::HeapSizeAlignment);
CATCH_REQUIRE(heap[svc::HeapSizeAlignment] == 0xBB);
CATCH_REQUIRE(std::memcmp(heap + svc::HeapSizeAlignment, heap + svc::HeapSizeAlignment + 1, svc::HeapSizeAlignment - 1) == 0);
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
CATCH_REQUIRE(heap[0] == 0xAA);
CATCH_REQUIRE(std::memcmp(heap, heap + 1, svc::HeapSizeAlignment - 1) == 0);
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 2 * svc::HeapSizeAlignment)));
CATCH_REQUIRE(heap[svc::HeapSizeAlignment] == 0x00);
CATCH_REQUIRE(std::memcmp(heap + svc::HeapSizeAlignment, heap + svc::HeapSizeAlignment + 1, svc::HeapSizeAlignment - 1) == 0);
CATCH_REQUIRE(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
}
}
}

View file

@ -0,0 +1,37 @@
/*
* 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 <stratosphere.hpp>
#define CATCH_CONFIG_NOSTDOUT
#define CATCH_CONFIG_PREFIX_ALL
#define CATCH_CONFIG_DISABLE_EXCEPTIONS
#define CATCH_CONFIG_NO_POSIX_SIGNALS
#include "catch.hpp"
namespace ams::test {
CATCH_TEST_CASE( "svc::SleepThread: Thread sleeps for time specified" ) {
for (s64 ns = 1; ns < TimeSpan::FromSeconds(1).GetNanoSeconds(); ns *= 2) {
const auto start = os::GetSystemTickOrdered();
svc::SleepThread(ns);
const auto end = os::GetSystemTickOrdered();
const s64 taken_ns = (end - start).ToTimeSpan().GetNanoSeconds();
CATCH_REQUIRE( taken_ns >= ns );
}
}
}