diff --git a/config/board/qemu/virt/board.mk b/config/board/qemu/virt/board.mk new file mode 100644 index 00000000..752aad41 --- /dev/null +++ b/config/board/qemu/virt/board.mk @@ -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 += \ No newline at end of file diff --git a/config/common.mk b/config/common.mk index 3fd7c1ca..460f7d9e 100644 --- a/config/common.mk +++ b/config/common.mk @@ -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) diff --git a/libmesosphere/Makefile b/libmesosphere/Makefile index c60cc865..6a996f15 100644 --- a/libmesosphere/Makefile +++ b/libmesosphere/Makefile @@ -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 diff --git a/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp b/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp index c06c4e30..35382437 100644 --- a/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp +++ b/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp @@ -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 diff --git a/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp b/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp index cbe6e1ea..6503b4e1 100644 --- a/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp +++ b/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp @@ -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) diff --git a/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp b/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp index cc9404a4..0fb09b4e 100644 --- a/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp +++ b/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp @@ -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(); } diff --git a/libmesosphere/include/mesosphere/board/qemu/virt/kern_cpu_map.hpp b/libmesosphere/include/mesosphere/board/qemu/virt/kern_cpu_map.hpp new file mode 100644 index 00000000..ec333546 --- /dev/null +++ b/libmesosphere/include/mesosphere/board/qemu/virt/kern_cpu_map.hpp @@ -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 . + */ +#pragma once +#include + +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, + }; + +} diff --git a/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_layout.hpp b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_layout.hpp new file mode 100644 index 00000000..003f7457 --- /dev/null +++ b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_layout.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::kern { + + constexpr inline size_t MainMemorySize = 4_GB; + constexpr inline size_t MainMemorySizeMax = 8_GB; + +} diff --git a/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_region_device_types.inc b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_region_device_types.inc new file mode 100644 index 00000000..3d64f040 --- /dev/null +++ b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_region_device_types.inc @@ -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 . + */ + +/* All architectures must define NumBoardDeviceRegions. */ +constexpr inline const auto NumBoardDeviceRegions = 0; + /* UNUSED: .Derive(NumBoardDeviceRegions, 0); */ + diff --git a/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_system_control.hpp b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_system_control.hpp new file mode 100644 index 00000000..d1d1fa21 --- /dev/null +++ b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_system_control.hpp @@ -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 . + */ +#pragma once +#include + +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); + }; + +} \ No newline at end of file diff --git a/libmesosphere/include/mesosphere/kern_debug_log.hpp b/libmesosphere/include/mesosphere/kern_debug_log.hpp index 8253b1a2..51b83596 100644 --- a/libmesosphere/include/mesosphere/kern_debug_log.hpp +++ b/libmesosphere/include/mesosphere/kern_debug_log.hpp @@ -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 diff --git a/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp b/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp index 66ee75ab..5ac2a590 100644 --- a/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp +++ b/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp @@ -20,6 +20,8 @@ #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) #include +#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT) + #include #else #error "Unknown board for KMemoryLayout" #endif diff --git a/libmesosphere/include/mesosphere/kern_select_cpu.hpp b/libmesosphere/include/mesosphere/kern_select_cpu.hpp index 8cf08533..799a2fb3 100644 --- a/libmesosphere/include/mesosphere/kern_select_cpu.hpp +++ b/libmesosphere/include/mesosphere/kern_select_cpu.hpp @@ -39,6 +39,16 @@ } +#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT) + + #include + + namespace ams::kern::cpu { + + using namespace ams::kern::board::qemu::virt::impl::cpu; + + } + #else #error "Unknown board for CPU Map" #endif diff --git a/libmesosphere/include/mesosphere/kern_select_system_control.hpp b/libmesosphere/include/mesosphere/kern_select_system_control.hpp index cb1bb4d9..0ea19592 100644 --- a/libmesosphere/include/mesosphere/kern_select_system_control.hpp +++ b/libmesosphere/include/mesosphere/kern_select_system_control.hpp @@ -23,6 +23,13 @@ using ams::kern::board::nintendo::nx::KSystemControl; } +#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT) + #include + + namespace ams::kern { + using ams::kern::board::qemu::virt::KSystemControl; + } + #else #error "Unknown board for KSystemControl" #endif diff --git a/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp b/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp index 112524fa..2ec4cbe2 100644 --- a/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp +++ b/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp @@ -25,7 +25,23 @@ namespace ams::kern::board::nintendo::nx::smc { }; enum UserFunctionId : u32 { - UserFunctionId_SetConfig = 0xC3000401, + 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 { diff --git a/libmesosphere/source/board/qemu/virt/kern_k_system_control.cpp b/libmesosphere/source/board/qemu/virt/kern_k_system_control.cpp new file mode 100644 index 00000000..53526eec --- /dev/null +++ b/libmesosphere/source/board/qemu/virt/kern_k_system_control.cpp @@ -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 . + */ +#include +#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; + + constinit KSpinLock g_secure_region_lock; + constinit bool g_secure_region_used = false; + constinit KPhysicalAddress g_secure_region_phys_addr = Null; + 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 + 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::max() && min == std::numeric_limits::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::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; + 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); + + /* 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(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(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); + 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); + + 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(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 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(pool), KMemoryManager::Direction_FromFront)); + R_UNLESS(paddr != Null, 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); + + /* 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); + } + +} \ No newline at end of file diff --git a/libmesosphere/source/board/qemu/virt/kern_secure_monitor.cpp b/libmesosphere/source/board/qemu/virt/kern_secure_monitor.cpp new file mode 100644 index 00000000..c4fa1c38 --- /dev/null +++ b/libmesosphere/source/board/qemu/virt/kern_secure_monitor.cpp @@ -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 . + */ +#include +#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(entrypoint), static_cast(arg) }; + CallPrivilegedSecureMonitorFunction(args); + MESOSPHERE_ABORT_UNLESS((static_cast(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(args->r[0]), reinterpret_cast(args->r[1]), reinterpret_cast(args->r[2]), reinterpret_cast(args->r[3]), reinterpret_cast(args->r[4]), reinterpret_cast(args->r[5]), reinterpret_cast(args->r[6]), reinterpret_cast(args->r[7]), GetCurrentProcess().GetName()); + + switch (args->r[0]) { + case UserFunctionId_GetConfig: + { + switch (static_cast(args->r[1])) { + case ConfigItem::ExosphereApiVersion: + args->r[1] = (static_cast(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) | + (static_cast(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) | + (static_cast(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) | + (static_cast(13) << 32) | + (static_cast(GetTargetFirmware()) << 0); + break; + default: + MESOSPHERE_PANIC("Unhandled GetConfig\n"); + } + + args->r[0] = static_cast(SmcResult::Success); + } + break; + default: + MESOSPHERE_PANIC("Unhandled SMC [%p %p %p %p %p %p %p %p]", reinterpret_cast(args->r[0]), reinterpret_cast(args->r[1]), reinterpret_cast(args->r[2]), reinterpret_cast(args->r[3]), reinterpret_cast(args->r[4]), reinterpret_cast(args->r[5]), reinterpret_cast(args->r[6]), reinterpret_cast(args->r[7])); + } + } + +} \ No newline at end of file diff --git a/libmesosphere/source/board/qemu/virt/kern_secure_monitor.hpp b/libmesosphere/source/board/qemu/virt/kern_secure_monitor.hpp new file mode 100644 index 00000000..86962400 --- /dev/null +++ b/libmesosphere/source/board/qemu/virt/kern_secure_monitor.hpp @@ -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 . + */ +#pragma once +#include + +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); + + } + +} \ No newline at end of file diff --git a/libmesosphere/source/kern_debug_log.cpp b/libmesosphere/source/kern_debug_log.cpp index 6ff3df94..5f296b65 100644 --- a/libmesosphere/source/kern_debug_log.cpp +++ b/libmesosphere/source/kern_debug_log.cpp @@ -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(); } diff --git a/libmesosphere/source/kern_debug_log_impl.arch.arm64.s b/libmesosphere/source/kern_debug_log_impl.arch.arm64.s new file mode 100644 index 00000000..e7d4423e --- /dev/null +++ b/libmesosphere/source/kern_debug_log_impl.arch.arm64.s @@ -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 . + */ + +/* 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 + diff --git a/libmesosphere/source/kern_debug_log_impl.board.qemu_virt.cpp b/libmesosphere/source/kern_debug_log_impl.board.qemu_virt.cpp new file mode 100644 index 00000000..a71b904d --- /dev/null +++ b/libmesosphere/source/kern_debug_log_impl.board.qemu_virt.cpp @@ -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 . + */ +#include +#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 + +} diff --git a/libmesosphere/source/kern_debug_log_impl.hpp b/libmesosphere/source/kern_debug_log_impl.hpp index fb065c53..a34591db 100644 --- a/libmesosphere/source/kern_debug_log_impl.hpp +++ b/libmesosphere/source/kern_debug_log_impl.hpp @@ -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(); diff --git a/libmesosphere/source/kern_k_memory_layout.board.qemu_virt.cpp b/libmesosphere/source/kern_k_memory_layout.board.qemu_virt.cpp new file mode 100644 index 00000000..578761d6 --- /dev/null +++ b/libmesosphere/source/kern_k_memory_layout.board.qemu_virt.cpp @@ -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 . + */ +#include + +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 requires (std::same_as && ...) + constexpr ALWAYS_INLINE KMemoryRegionType GetMemoryRegionType(KMemoryRegionType base, T... attr) { + return util::FromUnderlying(util::ToUnderlying(base) | (util::ToUnderlying(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); + } + + } + +} \ No newline at end of file diff --git a/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp b/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp index 50bc3dce..1ffb3cda 100644 --- a/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp +++ b/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp @@ -26,6 +26,11 @@ return ::svcSetHeapSize(reinterpret_cast(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(out_address), size); + } + ALWAYS_INLINE Result SetMemoryPermission(::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission perm) { return ::svcSetMemoryPermission(reinterpret_cast(static_cast(address)), size, static_cast(perm)); } diff --git a/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp b/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp index 5f2d2128..4a36bb94 100644 --- a/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp +++ b/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp @@ -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) { diff --git a/libstratosphere/source/init/init_system_module.cpp b/libstratosphere/source/init/init_system_module.cpp index 75ccce4a..0f64a107 100644 --- a/libstratosphere/source/init/init_system_module.cpp +++ b/libstratosphere/source/init/init_system_module.cpp @@ -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(); */ diff --git a/libvapours/include/vapours/svc/board/qemu/virt/svc_hardware_constants.hpp b/libvapours/include/vapours/svc/board/qemu/virt/svc_hardware_constants.hpp new file mode 100644 index 00000000..5160803a --- /dev/null +++ b/libvapours/include/vapours/svc/board/qemu/virt/svc_hardware_constants.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::svc::board::qemu::virt { + + constexpr inline const s64 TicksPerSecond = 19'200'000; + +} diff --git a/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp b/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp index e02784a8..ccd6db31 100644 --- a/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp +++ b/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp @@ -24,8 +24,15 @@ using namespace ams::svc::board::nintendo::nx; } +#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT) + + #include + 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